From a2a16c77a1a2c951b5930c3182e2737cb13e1a53 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 11 May 2016 14:46:00 +0200 Subject: mmc: sh_mobile_sdhi: properly document R-Car versions Replace hardcoded values with meaningful names and document what we know. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index f750f94..c3b651b 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -39,6 +39,12 @@ #define EXT_ACC 0xe4 +#define SDHI_VER_GEN2_SDR50 0x490c +/* very old datasheets said 0x490c for SDR104, too. They are wrong! */ +#define SDHI_VER_GEN2_SDR104 0xcb0d +#define SDHI_VER_GEN3_SD 0xcc10 +#define SDHI_VER_GEN3_SDMMC 0xcd10 + #define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data) struct sh_mobile_sdhi_of_data { @@ -109,14 +115,14 @@ static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width) * sh_mobile_sdhi_of_data :: dma_buswidth */ switch (sd_ctrl_read16(host, CTL_VERSION)) { - case 0x490C: + case SDHI_VER_GEN2_SDR50: val = (width == 32) ? 0x0001 : 0x0000; break; - case 0xCB0D: + case SDHI_VER_GEN2_SDR104: val = (width == 32) ? 0x0000 : 0x0001; break; - case 0xCC10: /* Gen3, SD only */ - case 0xCD10: /* Gen3, SD + MMC */ + case SDHI_VER_GEN3_SD: + case SDHI_VER_GEN3_SDMMC: if (width == 64) val = 0x0000; else if (width == 32) -- cgit v0.10.2 From 1473bdd50dce1c0abf465bd13954d602ebdfbbc7 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 13 May 2016 13:24:31 +0900 Subject: mmc: sdhci: remove comment regarding timeout during tuning Since commit 7ce45e950624 ("mmc: sdhci: SD tuning is broken for some controllers") sdhci_execute_tuning() no longer includes a timeout in its loop counter(s) so remove portion of the comment regarding this. Signed-off-by: Simon Horman Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0e3d7c0..88d75bb 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1909,7 +1909,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) /* * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number - * of loops reaches 40 times or a timeout of 150ms occurs. + * of loops reaches 40 times. */ do { struct mmc_command cmd = {0}; -- cgit v0.10.2 From e87c85617d54ec2757fae56e3bde159610a0bff6 Mon Sep 17 00:00:00 2001 From: "Yuan, Juntao" Date: Fri, 13 May 2016 07:59:24 +0000 Subject: mmc: block: correct 4KB alignment check In sectors alignment check, brq->data.blocks means sectors of the previous mqrq since data.blocks for mqrq_cur hasn't been updated yet. data.blocks will be updated later in mmc_blk_packed_hdr_wrq_prep or mmc_blk_rw_rq_prep. static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, ...... ...... struct mmc_blk_request *brq = &mq->mqrq_cur->brq; Signed-off-by: Yuan Juntao Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c5472e3..636843a 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1976,8 +1976,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) * When 4KB native sector is enabled, only 8 blocks * multiple read or write is allowed */ - if ((brq->data.blocks & 0x07) && - (card->ext_csd.data_sector_size == 4096)) { + if (mmc_large_sector(card) && + !IS_ALIGNED(blk_rq_sectors(rqc), 8)) { pr_err("%s: Transfer size is not 4KB sector size aligned\n", req->rq_disk->disk_name); mq_rq = mq->mqrq_cur; -- cgit v0.10.2 From 84d62605290f3eecfc8df7b2f259b39487d6e392 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Fri, 13 May 2016 15:16:02 +0200 Subject: mmc: sdhci: fix wakeup configuration Activating wakeup event is not enough to get a wakeup signal. The corresponding events have to be enabled in the Interrupt Status Enable Register too. It follows the specification and is needed at least by sdhci-of-at91. Signed-off-by: Ludovic Desroches Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 88d75bb..040af1b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2605,18 +2605,31 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) \*****************************************************************************/ #ifdef CONFIG_PM +/* + * To enable wakeup events, the corresponding events have to be enabled in + * the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal + * Table' in the SD Host Controller Standard Specification. + * It is useless to restore SDHCI_INT_ENABLE state in + * sdhci_disable_irq_wakeups() since it will be set by + * sdhci_enable_card_detection() or sdhci_init(). + */ void sdhci_enable_irq_wakeups(struct sdhci_host *host) { u8 val; u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE | SDHCI_WAKE_ON_INT; + u32 irq_val = SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | + SDHCI_INT_CARD_INT; val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); val |= mask ; /* Avoid fake wake up */ - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) { val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE); + irq_val &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); + } sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); + sdhci_writel(host, irq_val, SDHCI_INT_ENABLE); } EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); -- cgit v0.10.2 From 2810984b672a093ef381dd1c1284db1e99a5a6a8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 22 May 2016 11:06:08 +0200 Subject: MAINTAINERS: Add file patterns for mmc device tree bindings Submitters of device tree binding documentation may forget to CC the subsystem maintainer if this is missing. Signed-off-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson diff --git a/MAINTAINERS b/MAINTAINERS index 8c20323..705364e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7744,6 +7744,7 @@ M: Ulf Hansson L: linux-mmc@vger.kernel.org T: git git://git.linaro.org/people/ulf.hansson/mmc.git S: Maintained +F: Documentation/devicetree/bindings/mmc/ F: drivers/mmc/ F: include/linux/mmc/ F: include/uapi/linux/mmc/ -- cgit v0.10.2 From a60119ce9434b9c592bf4115fc70fa27fe9f2140 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 26 May 2016 09:56:02 +0800 Subject: Documentation: mmc: add mmc-hs400-enhanced-strobe mmc-hs400-enhanced-strobe is used to claim that the host can support hs400 mode with enhanced strobe introduced by emmc 5.1 spec. Signed-off-by: Shawn Lin Acked-by: Rob Herring Reviewed-by: Douglas Anderson Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index ed23b9b..ecc007a 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -46,6 +46,7 @@ Optional properties: - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported - mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported +- mmc-hs400-enhanced-strobe: eMMC HS400 enhanced strobe mode is supported - dsr: Value the card's (optional) Driver Stage Register (DSR) should be programmed with. Valid range: [0 .. 0xffff]. -- cgit v0.10.2 From ef29c0e273b874018f1802d12957d22008138240 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 26 May 2016 09:56:12 +0800 Subject: mmc: core: add mmc-hs400-enhanced-strobe support This patch introduce mmc-hs400-enhanced-strobe for platforms which want to enable enhanced strobe function from DT if the mmc host controller claims to support enhanced strobe. Signed-off-by: Shawn Lin Reviewed-by: Douglas Anderson Tested-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1be42fa..d7e86f9 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -313,6 +313,8 @@ int mmc_of_parse(struct mmc_host *host) host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR; if (of_property_read_bool(np, "mmc-hs400-1_2v")) host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; + if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe")) + host->caps2 |= MMC_CAP2_HS400_ES; host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr); if (host->dsr_req && (host->dsr & ~0xffff)) { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 45cde8c..b836a27 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -302,6 +302,7 @@ struct mmc_host { #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) #define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */ #define MMC_CAP2_NO_SDIO (1 << 19) /* Do not send SDIO commands during initialization */ +#define MMC_CAP2_HS400_ES (1 << 20) /* Host supports enhanced strobe */ mmc_pm_flag_t pm_caps; /* supported pm features */ -- cgit v0.10.2 From 81ac2af65793ecfbd79875d45043ff4adc0982b8 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 26 May 2016 09:56:22 +0800 Subject: mmc: core: implement enhanced strobe support Controllers use data strobe line to latch data from devices under hs400 mode, but not for cmd line. So since emmc 5.1, JEDEC introduces enhanced strobe mode for latching cmd response from emmc devices to host controllers. This new feature is optional, so it depends both on device's cap and host's cap to decide whether to use it or not. Signed-off-by: Shawn Lin Reviewed-by: Jaehoon Chung Tested-by: Douglas Anderson Tested-by: Jaehoon Chung Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 4bc48f1..c64266f 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -332,12 +332,13 @@ int mmc_add_card(struct mmc_card *card) mmc_card_ddr52(card) ? "DDR " : "", type); } else { - pr_info("%s: new %s%s%s%s%s card at address %04x\n", + pr_info("%s: new %s%s%s%s%s%s card at address %04x\n", mmc_hostname(card->host), mmc_card_uhs(card) ? "ultra high speed " : (mmc_card_hs(card) ? "high speed " : ""), mmc_card_hs400(card) ? "HS400 " : (mmc_card_hs200(card) ? "HS200 " : ""), + mmc_card_hs400es(card) ? "Enhanced strobe " : "", mmc_card_ddr52(card) ? "DDR " : "", uhs_bus_speed_mode, type, card->rca); } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8b4dfd4..e864187 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1127,6 +1127,15 @@ void mmc_set_initial_state(struct mmc_host *host) host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.timing = MMC_TIMING_LEGACY; host->ios.drv_type = 0; + host->ios.enhanced_strobe = false; + + /* + * Make sure we are in non-enhanced strobe mode before we + * actually enable it in ext_csd. + */ + if ((host->caps2 & MMC_CAP2_HS400_ES) && + host->ops->hs400_enhanced_strobe) + host->ops->hs400_enhanced_strobe(host, &host->ios); mmc_set_ios(host); } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5d438ad..97a664f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -235,6 +235,11 @@ static void mmc_select_card_type(struct mmc_card *card) avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; } + if ((caps2 & MMC_CAP2_HS400_ES) && + card->ext_csd.strobe_support && + (avail_type & EXT_CSD_CARD_TYPE_HS400)) + avail_type |= EXT_CSD_CARD_TYPE_HS400ES; + card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; @@ -386,6 +391,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) mmc_card_set_blockaddr(card); } + card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT]; card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; mmc_select_card_type(card); @@ -1223,6 +1229,78 @@ out_err: return err; } +static int mmc_select_hs400es(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + u8 val; + + if (!(host->caps & MMC_CAP_8_BIT_DATA)) { + err = -ENOTSUPP; + goto out_err; + } + + err = mmc_select_bus_width(card); + if (err < 0) + goto out_err; + + /* Switch card to HS mode */ + err = mmc_select_hs(card); + if (err) { + pr_err("%s: switch to high-speed failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + err = mmc_switch_status(card); + if (err) + goto out_err; + + /* Switch card to DDR with strobe bit */ + val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + val, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_err("%s: switch to bus width for hs400es failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + /* Switch card to HS400 */ + val = EXT_CSD_TIMING_HS400 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, val, + card->ext_csd.generic_cmd6_time, + true, false, true); + if (err) { + pr_err("%s: switch to hs400es failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + /* Set host controller to HS400 timing and frequency */ + mmc_set_timing(host, MMC_TIMING_MMC_HS400); + + /* Controller enable enhanced strobe function */ + host->ios.enhanced_strobe = true; + if (host->ops->hs400_enhanced_strobe) + host->ops->hs400_enhanced_strobe(host, &host->ios); + + err = mmc_switch_status(card); + if (err) + goto out_err; + + return 0; + +out_err: + pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), + __func__, err); + return err; +} + static void mmc_select_driver_type(struct mmc_card *card) { int card_drv_type, drive_strength, drv_type; @@ -1310,7 +1388,7 @@ err: } /* - * Activate High Speed or HS200 mode if supported. + * Activate High Speed, HS200 or HS400ES mode if supported. */ static int mmc_select_timing(struct mmc_card *card) { @@ -1319,7 +1397,9 @@ static int mmc_select_timing(struct mmc_card *card) if (!mmc_can_ext_csd(card)) goto bus_speed; - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) + err = mmc_select_hs400es(card); + else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) err = mmc_select_hs200(card); else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) err = mmc_select_hs(card); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index eb0151b..22defc2 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -95,6 +95,7 @@ struct mmc_ext_csd { u8 raw_partition_support; /* 160 */ u8 raw_rpmb_size_mult; /* 168 */ u8 raw_erased_mem_count; /* 181 */ + u8 strobe_support; /* 184 */ u8 raw_ext_csd_structure; /* 194 */ u8 raw_card_type; /* 196 */ u8 raw_driver_strength; /* 197 */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index b836a27..d72c0c3 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -19,6 +19,7 @@ #include #include +#include #include struct mmc_ios { @@ -77,6 +78,8 @@ struct mmc_ios { #define MMC_SET_DRIVER_TYPE_A 1 #define MMC_SET_DRIVER_TYPE_C 2 #define MMC_SET_DRIVER_TYPE_D 3 + + bool enhanced_strobe; /* hs400es selection */ }; struct mmc_host_ops { @@ -143,6 +146,9 @@ struct mmc_host_ops { /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); + /* Prepare enhanced strobe depending host driver */ + void (*hs400_enhanced_strobe)(struct mmc_host *host, + struct mmc_ios *ios); int (*select_drive_strength)(struct mmc_card *card, unsigned int max_dtr, int host_drv, int card_drv, int *drv_type); @@ -514,6 +520,11 @@ static inline bool mmc_card_hs400(struct mmc_card *card) return card->host->ios.timing == MMC_TIMING_MMC_HS400; } +static inline bool mmc_card_hs400es(struct mmc_card *card) +{ + return card->host->ios.enhanced_strobe; +} + void mmc_retune_timer_stop(struct mmc_host *host); static inline void mmc_retune_needed(struct mmc_host *host) diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 15f2c4a..c376209 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -297,6 +297,7 @@ struct _mmc_csd { #define EXT_CSD_PART_CONFIG 179 /* R/W */ #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ #define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_POWER_CLASS 187 /* R/W */ #define EXT_CSD_REV 192 /* RO */ @@ -380,12 +381,14 @@ struct _mmc_csd { #define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */ #define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ EXT_CSD_CARD_TYPE_HS400_1_2V) +#define EXT_CSD_CARD_TYPE_HS400ES (1<<8) /* Card can run at HS400ES */ #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_BUS_WIDTH_STROBE BIT(7) /* Enhanced strobe mode */ #define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ #define EXT_CSD_TIMING_HS 1 /* High speed */ -- cgit v0.10.2 From bc26235bbd7937a65f5beba046725911c759eca9 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 26 May 2016 09:56:30 +0800 Subject: mmc: debugfs: add HS400 enhanced strobe description We introduce HS400 with enhanced strobe function, so we need to add it for debug show. Signed-off-by: Shawn Lin Reviewed-by: Douglas Anderson Tested-by: Douglas Anderson Tested-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 9382a57..c8451ce 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -148,7 +148,8 @@ static int mmc_ios_show(struct seq_file *s, void *data) str = "mmc HS200"; break; case MMC_TIMING_MMC_HS400: - str = "mmc HS400"; + str = mmc_card_hs400es(host->card) ? + "mmc HS400 enhanced strobe" : "mmc HS400"; break; default: str = "invalid"; -- cgit v0.10.2 From a05c84651145152969a3a03d871d257dcdc843ed Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 26 May 2016 09:56:43 +0800 Subject: mmc: sdhci-of-arasan: implement enhanced strobe callback Currently sdhci-arasan 5.1 can support enhanced strobe function, and we now limit it just for "arasan,sdhci-5.1". Add mmc-hs400-enhanced-strobe in DT to enable the function if we're sure our controller can support it. Signed-off-by: Shawn Lin Acked-by: Adrian Hunter Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index b6f4c1d..533e2bc 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -25,7 +25,9 @@ #include "sdhci-pltfm.h" #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c +#define SDHCI_ARASAN_VENDOR_REGISTER 0x78 +#define VENDOR_ENHANCED_STROBE BIT(0) #define CLK_CTRL_TIMEOUT_SHIFT 16 #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) #define CLK_CTRL_TIMEOUT_MIN_EXP 13 @@ -79,6 +81,21 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) } } +static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + u32 vendor; + struct sdhci_host *host = mmc_priv(mmc); + + vendor = readl(host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); + if (ios->enhanced_strobe) + vendor |= VENDOR_ENHANCED_STROBE; + else + vendor &= ~VENDOR_ENHANCED_STROBE; + + writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); +} + static struct sdhci_ops sdhci_arasan_ops = { .set_clock = sdhci_arasan_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, @@ -245,6 +262,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev) dev_err(&pdev->dev, "phy_power_on err.\n"); goto err_phy_power; } + + host->mmc_host_ops.hs400_enhanced_strobe = + sdhci_arasan_hs400_enhanced_strobe; } ret = sdhci_add_host(host); -- cgit v0.10.2 From 7c42dbf335bc9f63a69a46489f02496029739622 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 27 May 2016 14:10:29 +0200 Subject: mmc: tmio: make a cast explicit Sparse complains about the implicit cast. Making it explicit is indeed better coding style. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 1aac2ad..7f63ec0 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -259,7 +259,7 @@ static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int addr, u32 val) { - writew(val, host->ctl + (addr << host->bus_shift)); + writew(val & 0xffff, host->ctl + (addr << host->bus_shift)); writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); } -- cgit v0.10.2 From e613cc477c777a175c89d607d1f7a8ef528d2c43 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 23 Jun 2016 14:00:58 +0300 Subject: mmc: sdhci: Fix sdhci_card_busy() host->card_busy() was introduced for SD voltage switching which checks all 4 data lines. Increasingly, host->card_busy is being used to poll the the busy signal which is only data line 0 (DAT[0]). The current logic in sdhci_card_busy() does not work in that case because it returns false if any of the data lines is high. It also ignores possibilities: - data lines 1-3 are not connected and could show at any level - data lines 1-2 can be used by SDIO for other purposes According to the SD specification, it is OK to check any of the data lines for voltage switching, so change to use DAT[0] only. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 040af1b..702a245 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1811,10 +1811,10 @@ static int sdhci_card_busy(struct mmc_host *mmc) struct sdhci_host *host = mmc_priv(mmc); u32 present_state; - /* Check whether DAT[3:0] is 0000 */ + /* Check whether DAT[0] is 0 */ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); - return !(present_state & SDHCI_DATA_LVL_MASK); + return !(present_state & SDHCI_DATA_0_LVL_MASK); } static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) -- cgit v0.10.2 From 87a18a6a565271a886d48df49e5e27ba99e26c60 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 19 May 2016 16:47:40 +0800 Subject: mmc: mmc: Use ->card_busy() to detect busy cards in __mmc_switch() Some MMC hosts do not support MMC_CAP_WAIT_WHILE_BUSY, but implements the ->card_busy() callback. In such cases, extend __mmc_switch() to use this method to check card status after switch command. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 62355bd..32de144 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -480,6 +480,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, u32 status = 0; bool use_r1b_resp = use_busy_signal; bool expired = false; + bool busy = false; mmc_retune_hold(host); @@ -535,19 +536,24 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, /* Must check status to be sure of no errors. */ timeout = jiffies + msecs_to_jiffies(timeout_ms); do { + /* + * Due to the possibility of being preempted after + * sending the status command, check the expiration + * time first. + */ + expired = time_after(jiffies, timeout); if (send_status) { - /* - * Due to the possibility of being preempted after - * sending the status command, check the expiration - * time first. - */ - expired = time_after(jiffies, timeout); err = __mmc_send_status(card, &status, ignore_crc); if (err) goto out; } if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) break; + if (host->ops->card_busy) { + if (!host->ops->card_busy(host)) + break; + busy = true; + } if (mmc_host_is_spi(host)) break; @@ -556,19 +562,20 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only * rely on waiting for the stated timeout to be sufficient. */ - if (!send_status) { + if (!send_status && !host->ops->card_busy) { mmc_delay(timeout_ms); goto out; } /* Timeout if the device never leaves the program state. */ - if (expired && R1_CURRENT_STATE(status) == R1_STATE_PRG) { + if (expired && + (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy)) { pr_err("%s: Card stuck in programming state! %s\n", mmc_hostname(host), __func__); err = -ETIMEDOUT; goto out; } - } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); + } while (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy); err = mmc_switch_status_error(host, status); out: -- cgit v0.10.2 From 08573eaf1a70104f83fdbee9b84e5be03480e9ed Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 19 May 2016 16:47:41 +0800 Subject: mmc: mmc: do not use CMD13 to get status after speed mode switch Per JEDEC spec, it is not recommended to use CMD13 to get card status after speed mode switch. below are two reason about this: 1. CMD13 cannot be guaranteed due to the asynchronous operation. Therefore it is not recommended to use CMD13 to check busy completion of the timing change indication. 2. After switch to HS200, CMD13 will get response of 0x800, and even the busy signal gets de-asserted, the response of CMD13 is aslo 0x800. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 97a664f..490229e 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -965,6 +965,19 @@ static int mmc_select_bus_width(struct mmc_card *card) return err; } +/* Caller must hold re-tuning */ +static int mmc_switch_status(struct mmc_card *card) +{ + u32 status; + int err; + + err = mmc_send_status(card, &status); + if (err) + return err; + + return mmc_switch_status_error(card->host, status); +} + /* * Switch to the high-speed mode */ @@ -975,9 +988,11 @@ static int mmc_select_hs(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, - true, true, true); - if (!err) + true, false, true); + if (!err) { mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + err = mmc_switch_status(card); + } return err; } @@ -1053,23 +1068,9 @@ static int mmc_select_hs_ddr(struct mmc_card *card) return err; } -/* Caller must hold re-tuning */ -static int mmc_switch_status(struct mmc_card *card) -{ - u32 status; - int err; - - err = mmc_send_status(card, &status); - if (err) - return err; - - return mmc_switch_status_error(card->host, status); -} - static int mmc_select_hs400(struct mmc_card *card) { struct mmc_host *host = card->host; - bool send_status = true; unsigned int max_dtr; int err = 0; u8 val; @@ -1081,9 +1082,6 @@ static int mmc_select_hs400(struct mmc_card *card) host->ios.bus_width == MMC_BUS_WIDTH_8)) return 0; - if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) - send_status = false; - /* Reduce frequency to HS frequency */ max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); @@ -1093,7 +1091,7 @@ static int mmc_select_hs400(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) { pr_err("%s: switch to high-speed from hs200 failed, err:%d\n", mmc_hostname(host), err); @@ -1103,11 +1101,9 @@ static int mmc_select_hs400(struct mmc_card *card) /* Set host controller to HS timing */ mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; /* Switch card to DDR */ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1126,7 +1122,7 @@ static int mmc_select_hs400(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) { pr_err("%s: switch to hs400 failed, err:%d\n", mmc_hostname(host), err); @@ -1137,11 +1133,9 @@ static int mmc_select_hs400(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_HS400); mmc_set_bus_speed(card); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; return 0; @@ -1159,14 +1153,10 @@ int mmc_hs200_to_hs400(struct mmc_card *card) int mmc_hs400_to_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; - bool send_status = true; unsigned int max_dtr; int err; u8 val; - if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) - send_status = false; - /* Reduce frequency to HS */ max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); @@ -1175,49 +1165,43 @@ int mmc_hs400_to_hs200(struct mmc_card *card) val = EXT_CSD_TIMING_HS; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) goto out_err; mmc_set_timing(host, MMC_TIMING_MMC_DDR52); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; /* Switch HS DDR to HS */ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) goto out_err; mmc_set_timing(host, MMC_TIMING_MMC_HS); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; /* Switch HS to HS200 */ val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - val, card->ext_csd.generic_cmd6_time, true, - send_status, true); + val, card->ext_csd.generic_cmd6_time, + true, false, true); if (err) goto out_err; mmc_set_timing(host, MMC_TIMING_MMC_HS200); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; mmc_set_bus_speed(card); @@ -1328,7 +1312,6 @@ static void mmc_select_driver_type(struct mmc_card *card) static int mmc_select_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; - bool send_status = true; unsigned int old_timing, old_signal_voltage; int err = -EINVAL; u8 val; @@ -1346,9 +1329,6 @@ static int mmc_select_hs200(struct mmc_card *card) mmc_select_driver_type(card); - if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) - send_status = false; - /* * Set the bus width(4 or 8) with host's support and * switch to HS200 mode if bus width is set successfully. @@ -1360,20 +1340,19 @@ static int mmc_select_hs200(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) goto err; old_timing = host->ios.timing; mmc_set_timing(host, MMC_TIMING_MMC_HS200); - if (!send_status) { - err = mmc_switch_status(card); - /* - * mmc_select_timing() assumes timing has not changed if - * it is a switch error. - */ - if (err == -EBADMSG) - mmc_set_timing(host, old_timing); - } + + err = mmc_switch_status(card); + /* + * mmc_select_timing() assumes timing has not changed if + * it is a switch error. + */ + if (err == -EBADMSG) + mmc_set_timing(host, old_timing); } err: if (err) { -- cgit v0.10.2 From 987aa5f8059613bf85cbb6f64ffbd34f5cb7a9d1 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 19 May 2016 16:47:42 +0800 Subject: mmc: mmc: fix switch timeout issue caused by jiffies precision with CONFIG_HZ=100, the precision of jiffies is 10ms, and the generic_cmd6_time of some card is also 10ms. then, may be current time is only 5ms, but already timed out caused by jiffies precision. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 32de144..ad6e979 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -534,7 +534,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, timeout_ms = MMC_OPS_TIMEOUT_MS; /* Must check status to be sure of no errors. */ - timeout = jiffies + msecs_to_jiffies(timeout_ms); + timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1; do { /* * Due to the possibility of being preempted after -- cgit v0.10.2 From 649c6059d2371fef886a8f967e21416204723d63 Mon Sep 17 00:00:00 2001 From: Ziyuan Xu Date: Thu, 26 May 2016 13:50:32 +0800 Subject: mmc: mmc: Fix HS switch failure in mmc_select_hs400() To slove the issue which was found on gru board for hs400. [ 4.616946] sdhci: Secure Digital Host Controller Interface driver [ 4.623135] sdhci: Copyright(c) Pierre Ossman [ 4.722575] sdhci-pltfm: SDHCI platform and OF driver helper [ 4.730962] sdhci-arasan fe330000.sdhci: No vmmc regulator found [ 4.737444] sdhci-arasan fe330000.sdhci: No vqmmc regulator found [ 4.774930] mmc0: SDHCI controller on fe330000.sdhci [fe330000.sdhci] using ADMA [ 4.980295] mmc0: switch to high-speed from hs200 failed, err:-84 [ 4.986487] mmc0: error -84 whilst initialising MMC card We should change HS400 mode selection timing to meet JEDEC specification. The JEDEC 5.1 said that change the frequency to <= 52MHZ after HS_TIMING switch. Refer to section 6.6.2.3 "HS400" timing mode selection: Set the "Timing Interface" parameter in the HS_TIMING[185] field of the Extended CSD register to 0x1 to switch to High Speed mode and then set the clock frequency to a value not greater than 52MHZ. Signed-off-by: Ziyuan Xu Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 490229e..900b9f9 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1082,10 +1082,6 @@ static int mmc_select_hs400(struct mmc_card *card) host->ios.bus_width == MMC_BUS_WIDTH_8)) return 0; - /* Reduce frequency to HS frequency */ - max_dtr = card->ext_csd.hs_max_dtr; - mmc_set_clock(host, max_dtr); - /* Switch card to HS mode */ val = EXT_CSD_TIMING_HS; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1101,6 +1097,10 @@ static int mmc_select_hs400(struct mmc_card *card) /* Set host controller to HS timing */ mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + /* Reduce frequency to HS frequency */ + max_dtr = card->ext_csd.hs_max_dtr; + mmc_set_clock(host, max_dtr); + err = mmc_switch_status(card); if (err) goto out_err; -- cgit v0.10.2 From 46cd175c9788e39a1c57698ab58f34844d216428 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Fri, 3 Jun 2016 19:47:12 +0000 Subject: DT: bindings: remove bcm2835-sdhci The sdhci-iproc also supports bcm2835. So this binding is obsolete. Signed-off-by: Stefan Wahren Acked-by: Stephen Warren Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt deleted file mode 100644 index 59476fb..0000000 --- a/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt +++ /dev/null @@ -1,18 +0,0 @@ -Broadcom BCM2835 SDHCI controller - -This file documents differences between the core properties described -by mmc.txt and the properties that represent the BCM2835 controller. - -Required properties: -- compatible : Should be "brcm,bcm2835-sdhci". -- clocks : The clock feeding the SDHCI controller. - -Example: - -sdhci: sdhci { - compatible = "brcm,bcm2835-sdhci"; - reg = <0x7e300000 0x100>; - interrupts = <2 30>; - clocks = <&clk_mmc>; - bus-width = <4>; -}; -- cgit v0.10.2 From 7eb16493ecaa41ceeb448717ee2c59690cd9047f Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Fri, 3 Jun 2016 19:47:13 +0000 Subject: mmc: sdhci-bcm2835: remove driver The sdhci-bcm2835 is no more needed since it has been replaced by sdhci-iproc. Signed-off-by: Stefan Wahren Acked-by: Eric Anholt Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 0aa484c..2cf124a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -296,17 +296,6 @@ config MMC_SDHCI_BCM_KONA If you have a controller with this interface, say Y or M here. -config MMC_SDHCI_BCM2835 - tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" - depends on ARCH_BCM2835 - depends on MMC_SDHCI_PLTFM - select MMC_SDHCI_IO_ACCESSORS - help - This selects the BCM2835 SD/MMC controller. If you have a BCM2835 - platform with SD or MMC devices, say Y or M here. - - If unsure, say N. - config MMC_SDHCI_F_SDH30 tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index af918d2..61a9345 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -71,7 +71,6 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o -obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c deleted file mode 100644 index 4a6a1d1..0000000 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * BCM2835 SDHCI - * Copyright (C) 2012 Stephen Warren - * Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me - * Portions of the code there were obviously based on the Linux kernel at: - * git://github.com/raspberrypi/linux.git rpi-3.6.y - * commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "sdhci-pltfm.h" - -/* - * 400KHz is max freq for card ID etc. Use that as min card clock. We need to - * know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY. - */ -#define MIN_FREQ 400000 - -/* - * The Arasan has a bugette whereby it may lose the content of successive - * writes to registers that are within two SD-card clock cycles of each other - * (a clock domain crossing problem). It seems, however, that the data - * register does not have this problem, which is just as well - otherwise we'd - * have to nobble the DMA engine too. - * - * This should probably be dynamically calculated based on the actual card - * frequency. However, this is the longest we'll have to wait, and doesn't - * seem to slow access down too much, so the added complexity doesn't seem - * worth it for now. - * - * 1/MIN_FREQ is (max) time per tick of eMMC clock. - * 2/MIN_FREQ is time for two ticks. - * Multiply by 1000000 to get uS per two ticks. - * *1000000 for uSecs. - * +1 for hack rounding. - */ -#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1) - -struct bcm2835_sdhci { - u32 shadow; -}; - -static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) -{ - writel(val, host->ioaddr + reg); - - udelay(BCM2835_SDHCI_WRITE_DELAY); -} - -static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) -{ - u32 val = readl(host->ioaddr + reg); - - if (reg == SDHCI_CAPABILITIES) - val |= SDHCI_CAN_VDD_330; - - return val; -} - -static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct bcm2835_sdhci *bcm2835_host = sdhci_pltfm_priv(pltfm_host); - u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow : - bcm2835_sdhci_readl(host, reg & ~3); - u32 word_num = (reg >> 1) & 1; - u32 word_shift = word_num * 16; - u32 mask = 0xffff << word_shift; - u32 newval = (oldval & ~mask) | (val << word_shift); - - if (reg == SDHCI_TRANSFER_MODE) - bcm2835_host->shadow = newval; - else - bcm2835_sdhci_writel(host, newval, reg & ~3); -} - -static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) -{ - u32 val = bcm2835_sdhci_readl(host, (reg & ~3)); - u32 word_num = (reg >> 1) & 1; - u32 word_shift = word_num * 16; - u32 word = (val >> word_shift) & 0xffff; - - return word; -} - -static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) -{ - u32 oldval = bcm2835_sdhci_readl(host, reg & ~3); - u32 byte_num = reg & 3; - u32 byte_shift = byte_num * 8; - u32 mask = 0xff << byte_shift; - u32 newval = (oldval & ~mask) | (val << byte_shift); - - bcm2835_sdhci_writel(host, newval, reg & ~3); -} - -static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) -{ - u32 val = bcm2835_sdhci_readl(host, (reg & ~3)); - u32 byte_num = reg & 3; - u32 byte_shift = byte_num * 8; - u32 byte = (val >> byte_shift) & 0xff; - - return byte; -} - -static unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host) -{ - return MIN_FREQ; -} - -static const struct sdhci_ops bcm2835_sdhci_ops = { - .write_l = bcm2835_sdhci_writel, - .write_w = bcm2835_sdhci_writew, - .write_b = bcm2835_sdhci_writeb, - .read_l = bcm2835_sdhci_readl, - .read_w = bcm2835_sdhci_readw, - .read_b = bcm2835_sdhci_readb, - .set_clock = sdhci_set_clock, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, - .get_min_clock = bcm2835_sdhci_get_min_clock, - .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, -}; - -static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { - .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, - .ops = &bcm2835_sdhci_ops, -}; - -static int bcm2835_sdhci_probe(struct platform_device *pdev) -{ - struct sdhci_host *host; - struct bcm2835_sdhci *bcm2835_host; - struct sdhci_pltfm_host *pltfm_host; - int ret; - - host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata, - sizeof(*bcm2835_host)); - if (IS_ERR(host)) - return PTR_ERR(host); - - pltfm_host = sdhci_priv(host); - - pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err; - } - ret = clk_prepare_enable(pltfm_host->clk); - if (ret) { - dev_err(&pdev->dev, "failed to enable host clk\n"); - goto err; - } - - ret = sdhci_add_host(host); - if (ret) - goto err_clk; - - return 0; -err_clk: - clk_disable_unprepare(pltfm_host->clk); -err: - sdhci_pltfm_free(pdev); - return ret; -} - -static const struct of_device_id bcm2835_sdhci_of_match[] = { - { .compatible = "brcm,bcm2835-sdhci" }, - { } -}; -MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match); - -static struct platform_driver bcm2835_sdhci_driver = { - .driver = { - .name = "sdhci-bcm2835", - .of_match_table = bcm2835_sdhci_of_match, - .pm = SDHCI_PLTFM_PMOPS, - }, - .probe = bcm2835_sdhci_probe, - .remove = sdhci_pltfm_unregister, -}; -module_platform_driver(bcm2835_sdhci_driver); - -MODULE_DESCRIPTION("BCM2835 SDHCI driver"); -MODULE_AUTHOR("Stephen Warren"); -MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From c6d8fd61c096e9f84c0bd0bc38ccf5856b76c772 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 3 Jun 2016 09:08:52 -0700 Subject: mmc: Set pref erase size based on size If available, eMMC stack uses HC_ERASE_GRP_SIZE as the preferred erase size. As some high capacity eMMC (64MB) reports this size to 512kB, the discard operations end up taking very long time. Improve the behaviour by instead calculating the preferred erase size based on the eMMC size. In this way the discard operations becomes faster. Signed-off-by: Gwendal Grignou [Ulf: Updated changelog and improved comment in code] Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e864187..1d24b3a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1934,17 +1934,15 @@ void mmc_init_erase(struct mmc_card *card) * to that size and alignment. * * For SD cards that define Allocation Unit size, limit erases to one - * Allocation Unit at a time. For MMC cards that define High Capacity - * Erase Size, whether it is switched on or not, limit to that size. - * Otherwise just have a stab at a good value. For modern cards it - * will end up being 4MiB. Note that if the value is too small, it - * can end up taking longer to erase. + * Allocation Unit at a time. + * For MMC, have a stab at ai good value and for modern cards it will + * end up being 4MiB. Note that if the value is too small, it can end + * up taking longer to erase. Also note, erase_size is already set to + * High Capacity Erase Size if available when this function is called. */ if (mmc_card_sd(card) && card->ssr.au) { card->pref_erase = card->ssr.au; card->erase_shift = ffs(card->ssr.au) - 1; - } else if (card->ext_csd.hc_erase_size) { - card->pref_erase = card->ext_csd.hc_erase_size; } else if (card->erase_size) { sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; if (sz < 128) -- cgit v0.10.2 From 8b7be8f2e7fe6c037fc7c89029a4a568ad2c60b1 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 8 Jun 2016 11:27:52 +0100 Subject: mmc: core: Only change mode if mmc_select_bus_width() is successful mmc_select_bus_width() returns bus width (4 or 8) on success or zero if unsupported. So only change mode if setting the bus width is successful. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 900b9f9..ccab9bb 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1334,7 +1334,7 @@ static int mmc_select_hs200(struct mmc_card *card) * switch to HS200 mode if bus width is set successfully. */ err = mmc_select_bus_width(card); - if (err >= 0) { + if (err > 0) { val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1642,7 +1642,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); - if (err >= 0) { + if (err > 0) { err = mmc_select_hs_ddr(card); if (err) goto free_card; -- cgit v0.10.2 From 5320226a051248b50cc2d80f2cb9599dfb56d422 Mon Sep 17 00:00:00 2001 From: Pratibhasagar V Date: Thu, 9 Jun 2016 18:09:31 -0400 Subject: mmc: core: Disable HPI for certain Hynix eMMC cards Certain Hynix eMMC 4.41 cards might get broken when HPI feature is used and hence this patch disables the HPI feature for such buggy cards. As some of the other features like BKOPs/Cache/Sanitize are dependent on HPI feature, those features would also get disabled if HPI is disabled. Signed-off-by: Pratibhasagar V Signed-off-by: Subhash Jadavani [gdavis: Forward port and cleanup] Signed-off-by: George G. Davis Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 636843a..abfe394 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2499,12 +2499,6 @@ force_ro_fail: return ret; } -#define CID_MANFID_SANDISK 0x2 -#define CID_MANFID_TOSHIBA 0x11 -#define CID_MANFID_MICRON 0x13 -#define CID_MANFID_SAMSUNG 0x15 -#define CID_MANFID_KINGSTON 0x70 - static const struct mmc_fixup blk_fixups[] = { MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk, diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ccab9bb..5485040 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -45,6 +45,17 @@ static const unsigned int tacc_mant[] = { 35, 40, 45, 50, 55, 60, 70, 80, }; +static const struct mmc_fixup mmc_ext_csd_fixups[] = { + /* + * Certain Hynix eMMC 4.41 cards might get broken when HPI feature + * is used so disable the HPI feature for such buggy cards. + */ + MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX, + 0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5), + + END_FIXUP +}; + #define UNSTUFF_BITS(resp,start,size) \ ({ \ const int __size = size; \ @@ -375,6 +386,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) */ card->ext_csd.rev = ext_csd[EXT_CSD_REV]; + /* fixup device after ext_csd revision field is updated */ + mmc_fixup_device(card, mmc_ext_csd_fixups); + card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0]; card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1]; card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2]; @@ -506,7 +520,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->cid.year += 16; /* check whether the eMMC card supports BKOPS */ - if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { + if (!mmc_card_broken_hpi(card) && + ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { card->ext_csd.bkops = 1; card->ext_csd.man_bkops_en = (ext_csd[EXT_CSD_BKOPS_EN] & @@ -519,7 +534,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) } /* check whether the eMMC card supports HPI */ - if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) { + if (!mmc_card_broken_hpi(card) && + !broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) { card->ext_csd.hpi = 1; if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2) card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION; @@ -1675,7 +1691,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * If cache size is higher than 0, this indicates * the existence of cache and it can be turned on. */ - if (card->ext_csd.cache_size > 0) { + if (!mmc_card_broken_hpi(card) && + card->ext_csd.cache_size > 0) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL, 1, card->ext_csd.generic_cmd6_time); diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index fad660b..ca9cade 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -72,6 +72,8 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) f->cis_vendor == (u16) SDIO_ANY_ID) && (f->cis_device == card->cis.device || f->cis_device == (u16) SDIO_ANY_ID) && + (f->ext_csd_rev == EXT_CSD_REV_ANY || + f->ext_csd_rev == card->ext_csd.rev) && rev >= f->rev_start && rev <= f->rev_end) { dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup); f->vendor_fixup(card, f->data); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 22defc2..d8673ca 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -280,6 +280,7 @@ struct mmc_card { #define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */ #define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */ #define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */ +#define MMC_QUIRK_BROKEN_HPI (1<<13) /* Disable broken HPI support */ unsigned int erase_size; /* erase size in sectors */ @@ -354,6 +355,9 @@ struct mmc_fixup { /* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */ u16 cis_vendor, cis_device; + /* for MMC cards */ + unsigned int ext_csd_rev; + void (*vendor_fixup)(struct mmc_card *card, int data); int data; }; @@ -362,11 +366,20 @@ struct mmc_fixup { #define CID_OEMID_ANY ((unsigned short) -1) #define CID_NAME_ANY (NULL) +#define EXT_CSD_REV_ANY (-1u) + +#define CID_MANFID_SANDISK 0x2 +#define CID_MANFID_TOSHIBA 0x11 +#define CID_MANFID_MICRON 0x13 +#define CID_MANFID_SAMSUNG 0x15 +#define CID_MANFID_KINGSTON 0x70 +#define CID_MANFID_HYNIX 0x90 + #define END_FIXUP { NULL } #define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ _cis_vendor, _cis_device, \ - _fixup, _data) \ + _fixup, _data, _ext_csd_rev) \ { \ .name = (_name), \ .manfid = (_manfid), \ @@ -377,23 +390,30 @@ struct mmc_fixup { .cis_device = (_cis_device), \ .vendor_fixup = (_fixup), \ .data = (_data), \ + .ext_csd_rev = (_ext_csd_rev), \ } #define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \ - _fixup, _data) \ + _fixup, _data, _ext_csd_rev) \ _FIXUP_EXT(_name, _manfid, \ _oemid, _rev_start, _rev_end, \ SDIO_ANY_ID, SDIO_ANY_ID, \ - _fixup, _data) \ + _fixup, _data, _ext_csd_rev) \ #define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \ - MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data) + MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \ + EXT_CSD_REV_ANY) + +#define MMC_FIXUP_EXT_CSD_REV(_name, _manfid, _oemid, _fixup, _data, \ + _ext_csd_rev) \ + MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \ + _ext_csd_rev) #define SDIO_FIXUP(_vendor, _device, _fixup, _data) \ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \ CID_OEMID_ANY, 0, -1ull, \ _vendor, _device, \ - _fixup, _data) \ + _fixup, _data, EXT_CSD_REV_ANY) \ #define cid_rev(hwrev, fwrev, year, month) \ (((u64) hwrev) << 40 | \ @@ -512,6 +532,11 @@ static inline int mmc_card_broken_irq_polling(const struct mmc_card *c) return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING; } +static inline int mmc_card_broken_hpi(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_BROKEN_HPI; +} + #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) -- cgit v0.10.2 From d806b46e5f496a6335ebd7f8432d2533507ce9a2 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 10 Jun 2016 16:22:16 +0300 Subject: mmc: block: Fix tag condition with packed writes Apparently a cut-and-paste error, 'do_data_tag' is using 'brq' for data size even though 'brq' has not been set up. Instead use blk_rq_sectors(). Signed-off-by: Adrian Hunter Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index abfe394..d180588 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1800,8 +1800,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq, do_data_tag = (card->ext_csd.data_tag_unit_size) && (prq->cmd_flags & REQ_META) && (rq_data_dir(prq) == WRITE) && - ((brq->data.blocks * brq->data.blksz) >= - card->ext_csd.data_tag_unit_size); + blk_rq_bytes(prq) >= card->ext_csd.data_tag_unit_size; /* Argument of CMD23 */ packed_cmd_hdr[(i * 2)] = cpu_to_le32( (do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) | -- cgit v0.10.2 From d3b2cd0ed05ef8807922d3ce1c21a3e7e2257165 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Thu, 16 Jun 2016 12:47:17 -0400 Subject: mmc: DT: sdhci-brcmstb: Add device tree bindings The example includes the properties required to enable UHS modes. Signed-off-by: Al Cooper Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt new file mode 100644 index 0000000..8284717 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt @@ -0,0 +1,36 @@ +* BROADCOM BRCMSTB/BMIPS SDHCI Controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the sdhci-brcmstb driver. + +NOTE: The driver disables all UHS speed modes by default and depends +on Device Tree properties to enable them for SoC/Board combinations +that support them. + +Required properties: +- compatible: "brcm,bcm7425-sdhci" + +Refer to clocks/clock-bindings.txt for generic clock consumer properties. + +Example: + + sdhci@f03e0100 { + compatible = "brcm,bcm7425-sdhci"; + reg = <0xf03e0000 0x100>; + interrupts = <0x0 0x26 0x0>; + sdhci,auto-cmd12; + clocks = <&sw_sdio>; + sd-uhs-sdr50; + sd-uhs-ddr50; + }; + + sdhci@f03e0300 { + non-removable; + bus-width = <0x8>; + compatible = "brcm,bcm7425-sdhci"; + reg = <0xf03e0200 0x100>; + interrupts = <0x0 0x27 0x0>; + sdhci,auto-cmd12; + clocks = ; + mmc-hs200-1_8v; + }; -- cgit v0.10.2 From 476bf3d62d5cc0ec003da37ba9da4f2b46c23660 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Thu, 16 Jun 2016 12:47:16 -0400 Subject: mmc: sdhci-brcmstb: Add driver for Broadcom BRCMSTB SoCs Add SDHCI driver for Broadcom BRCMSTB SoCs. This driver works with all ARM based SoCs and the 7425, 7429 and 7435 MIPS based SoCs. The driver disables all UHS speed modes by default and relies on the Device Tree node properties to enable these modes for SoC/Board combinations that support them. Signed-off-by: Al Cooper Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/MAINTAINERS b/MAINTAINERS index 705364e..6ae7139 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10209,6 +10209,13 @@ F: tools/testing/selftests/seccomp/* K: \bsecure_computing K: \bTIF_SECCOMP\b +SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) Broadcom BRCMSTB DRIVER +M: Al Cooper +L: linux-mmc@vger.kernel.org +L: bcm-kernel-feedback-list@broadcom.com +S: Maintained +F: drivers/mmc/host/sdhci-brcmstb* + SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER M: Ben Dooks M: Jaehoon Chung diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2cf124a..0b5c871 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -787,3 +787,13 @@ config MMC_SDHCI_MICROCHIP_PIC32 If you have a controller with this interface, say Y or M here. If unsure, say N. +config MMC_SDHCI_BRCMSTB + tristate "Broadcom SDIO/SD/MMC support" + depends on ARCH_BRCMSTB || BMIPS_GENERIC + depends on MMC_SDHCI_PLTFM + default y + help + This selects support for the SDIO/SD/MMC Host Controller on + Broadcom STB SoCs. + + If unsure, say Y. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 61a9345..e2bdaaf 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o +obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c new file mode 100644 index 0000000..cce10fe --- /dev/null +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -0,0 +1,143 @@ +/* + * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's + * + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include "sdhci-pltfm.h" + +#ifdef CONFIG_PM_SLEEP + +static int sdhci_brcmstb_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + int res; + + res = sdhci_suspend_host(host); + if (res) + return res; + clk_disable_unprepare(pltfm_host->clk); + return res; +} + +static int sdhci_brcmstb_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + int err; + + err = clk_prepare_enable(pltfm_host->clk); + if (err) + return err; + return sdhci_resume_host(host); +} + +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend, + sdhci_brcmstb_resume); + +static const struct sdhci_ops sdhci_brcmstb_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static struct sdhci_pltfm_data sdhci_brcmstb_pdata = { + .ops = &sdhci_brcmstb_ops, +}; + +static int sdhci_brcmstb_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct clk *clk; + int res; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Clock not found in Device Tree\n"); + clk = NULL; + } + res = clk_prepare_enable(clk); + if (res) + return res; + + host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 0); + if (IS_ERR(host)) { + res = PTR_ERR(host); + goto err_clk; + } + + /* Enable MMC_CAP2_HC_ERASE_SZ for better max discard calculations */ + host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; + + sdhci_get_of_property(pdev); + mmc_of_parse(host->mmc); + + /* + * Supply the existing CAPS, but clear the UHS modes. This + * will allow these modes to be specified by device tree + * properties through mmc_of_parse(). + */ + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | + SDHCI_SUPPORT_DDR50); + host->quirks |= SDHCI_QUIRK_MISSING_CAPS | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + + res = sdhci_add_host(host); + if (res) + goto err; + + pltfm_host = sdhci_priv(host); + pltfm_host->clk = clk; + return res; + +err: + sdhci_pltfm_free(pdev); +err_clk: + clk_disable_unprepare(clk); + return res; +} + +static const struct of_device_id sdhci_brcm_of_match[] = { + { .compatible = "brcm,bcm7425-sdhci" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); + +static struct platform_driver sdhci_brcmstb_driver = { + .driver = { + .name = "sdhci-brcmstb", + .owner = THIS_MODULE, + .pm = &sdhci_brcmstb_pmops, + .of_match_table = of_match_ptr(sdhci_brcm_of_match), + }, + .probe = sdhci_brcmstb_probe, + .remove = sdhci_pltfm_unregister, +}; + +module_platform_driver(sdhci_brcmstb_driver); + +MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs"); +MODULE_AUTHOR("Broadcom"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 4d54a25ba5df8d4fd1c1604d1526c9279237ac27 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 20 Jun 2016 10:56:40 -0700 Subject: phy: rockchip-emmc: give DLL some extra time to be ready According to the databook, 10.2us is the max time for dll to be ready to work. However in testing, some chips need 20us for dll to be ready. This patch adds some extra margin for dllrdy to be ready, fixing our -ETIMEDOUT issues. Signed-off-by: Shawn Lin Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 6ebcf3e..48cbe69 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -119,10 +119,11 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); /* - * After enable analog DLL circuits, we need extra 10.2us - * for dll to be ready for work. + * After enable analog DLL circuits, we need an extra 10.2us + * for dll to be ready for work. But according to testing, we + * find some chips need more than 25us. */ - udelay(11); + udelay(30); regmap_read(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_STATUS, &dllrdy); -- cgit v0.10.2 From d74857720d41c58f74966b5d06ebfa9111a62c69 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 20 Jun 2016 10:56:41 -0700 Subject: phy: rockchip-emmc: configure frequency range and drive impedance Signal integrity analysis has suggested we set these values. Do this in power_on(), so that they get reconfigured after suspend/resume. Signed-off-by: Shawn Lin Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 48cbe69..f2f75cf 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -56,6 +56,19 @@ #define PHYCTRL_DLLRDY_SHIFT 0x5 #define PHYCTRL_DLLRDY_DONE 0x1 #define PHYCTRL_DLLRDY_GOING 0x0 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_FREQSEL_MASK 0x3 +#define PHYCTRL_FREQSEL_SHIFT 0xc +#define PHYCTRL_DR_MASK 0x7 +#define PHYCTRL_DR_SHIFT 0x4 +#define PHYCTRL_DR_50OHM 0x0 +#define PHYCTRL_DR_33OHM 0x1 +#define PHYCTRL_DR_66OHM 0x2 +#define PHYCTRL_DR_100OHM 0x3 +#define PHYCTRL_DR_40OHM 0x4 struct rockchip_emmc_phy { unsigned int reg_offset; @@ -154,6 +167,20 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); int ret = 0; + /* DLL operation: 200 MHz */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(PHYCTRL_FREQSEL_200M, + PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Drive impedance: 50 Ohm */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_DR_50OHM, + PHYCTRL_DR_MASK, + PHYCTRL_DR_SHIFT)); + /* Power up emmc phy analog blocks */ ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); if (ret) -- cgit v0.10.2 From 36b5d460261f16563f9196c49c936b3e17d237e3 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 20 Jun 2016 10:56:42 -0700 Subject: phy: rockchip-emmc: configure default output tap delay The output tap delay controls helps maintain the hold requirements for eMMC. The exact value is dependent on the SoC and other factors, though it isn't really an exact science. But the default of 0 is not very good, as it doesn't give the eMMC much hold time, so let's bump up to 4 (approx 90 degree phase?). If we need to configure this any further (e.g., based on board or speed factors), we may need to consider a device tree representation. Suggested-by: Shawn Lin Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index f2f75cf..a0b87cc 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -69,6 +69,11 @@ #define PHYCTRL_DR_66OHM 0x2 #define PHYCTRL_DR_100OHM 0x3 #define PHYCTRL_DR_40OHM 0x4 +#define PHYCTRL_OTAPDLYENA 0x1 +#define PHYCTRL_OTAPDLYENA_MASK 0x1 +#define PHYCTRL_OTAPDLYENA_SHIFT 0xb +#define PHYCTRL_OTAPDLYSEL_MASK 0xf +#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 struct rockchip_emmc_phy { unsigned int reg_offset; @@ -181,6 +186,20 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_DR_MASK, PHYCTRL_DR_SHIFT)); + /* Output tap delay: enable */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, + PHYCTRL_OTAPDLYENA_MASK, + PHYCTRL_OTAPDLYENA_SHIFT)); + + /* Output tap delay */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(4, + PHYCTRL_OTAPDLYSEL_MASK, + PHYCTRL_OTAPDLYSEL_SHIFT)); + /* Power up emmc phy analog blocks */ ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); if (ret) -- cgit v0.10.2 From 675f65c444e85ab889d299d2a7ce62d5d478e478 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 20 Jun 2016 10:56:43 -0700 Subject: phy: rockchip-emmc: reindent the register definitions Some of the spacing was wrong (spaces instead of tabs), and due to longer entries added later, the columns weren't aligned. Let's get everything consistent. Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index a0b87cc..a69f536 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -31,44 +31,44 @@ ((val) << (shift) | (mask) << ((shift) + 16)) /* Register definition */ -#define GRF_EMMCPHY_CON0 0x0 -#define GRF_EMMCPHY_CON1 0x4 -#define GRF_EMMCPHY_CON2 0x8 -#define GRF_EMMCPHY_CON3 0xc -#define GRF_EMMCPHY_CON4 0x10 -#define GRF_EMMCPHY_CON5 0x14 -#define GRF_EMMCPHY_CON6 0x18 -#define GRF_EMMCPHY_STATUS 0x20 - -#define PHYCTRL_PDB_MASK 0x1 -#define PHYCTRL_PDB_SHIFT 0x0 -#define PHYCTRL_PDB_PWR_ON 0x1 -#define PHYCTRL_PDB_PWR_OFF 0x0 -#define PHYCTRL_ENDLL_MASK 0x1 -#define PHYCTRL_ENDLL_SHIFT 0x1 -#define PHYCTRL_ENDLL_ENABLE 0x1 -#define PHYCTRL_ENDLL_DISABLE 0x0 -#define PHYCTRL_CALDONE_MASK 0x1 -#define PHYCTRL_CALDONE_SHIFT 0x6 -#define PHYCTRL_CALDONE_DONE 0x1 -#define PHYCTRL_CALDONE_GOING 0x0 -#define PHYCTRL_DLLRDY_MASK 0x1 -#define PHYCTRL_DLLRDY_SHIFT 0x5 -#define PHYCTRL_DLLRDY_DONE 0x1 -#define PHYCTRL_DLLRDY_GOING 0x0 -#define PHYCTRL_FREQSEL_200M 0x0 -#define PHYCTRL_FREQSEL_50M 0x1 -#define PHYCTRL_FREQSEL_100M 0x2 -#define PHYCTRL_FREQSEL_150M 0x3 -#define PHYCTRL_FREQSEL_MASK 0x3 -#define PHYCTRL_FREQSEL_SHIFT 0xc -#define PHYCTRL_DR_MASK 0x7 -#define PHYCTRL_DR_SHIFT 0x4 -#define PHYCTRL_DR_50OHM 0x0 -#define PHYCTRL_DR_33OHM 0x1 -#define PHYCTRL_DR_66OHM 0x2 -#define PHYCTRL_DR_100OHM 0x3 -#define PHYCTRL_DR_40OHM 0x4 +#define GRF_EMMCPHY_CON0 0x0 +#define GRF_EMMCPHY_CON1 0x4 +#define GRF_EMMCPHY_CON2 0x8 +#define GRF_EMMCPHY_CON3 0xc +#define GRF_EMMCPHY_CON4 0x10 +#define GRF_EMMCPHY_CON5 0x14 +#define GRF_EMMCPHY_CON6 0x18 +#define GRF_EMMCPHY_STATUS 0x20 + +#define PHYCTRL_PDB_MASK 0x1 +#define PHYCTRL_PDB_SHIFT 0x0 +#define PHYCTRL_PDB_PWR_ON 0x1 +#define PHYCTRL_PDB_PWR_OFF 0x0 +#define PHYCTRL_ENDLL_MASK 0x1 +#define PHYCTRL_ENDLL_SHIFT 0x1 +#define PHYCTRL_ENDLL_ENABLE 0x1 +#define PHYCTRL_ENDLL_DISABLE 0x0 +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_CALDONE_GOING 0x0 +#define PHYCTRL_DLLRDY_MASK 0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE 0x1 +#define PHYCTRL_DLLRDY_GOING 0x0 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_FREQSEL_MASK 0x3 +#define PHYCTRL_FREQSEL_SHIFT 0xc +#define PHYCTRL_DR_MASK 0x7 +#define PHYCTRL_DR_SHIFT 0x4 +#define PHYCTRL_DR_50OHM 0x0 +#define PHYCTRL_DR_33OHM 0x1 +#define PHYCTRL_DR_66OHM 0x2 +#define PHYCTRL_DR_100OHM 0x3 +#define PHYCTRL_DR_40OHM 0x4 #define PHYCTRL_OTAPDLYENA 0x1 #define PHYCTRL_OTAPDLYENA_MASK 0x1 #define PHYCTRL_OTAPDLYENA_SHIFT 0xb -- cgit v0.10.2 From 49f9ccd8882c1b97512ece16ad68ad0594c54bbc Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:44 -0700 Subject: phy: rockchip-emmc: Increase lock time allowance Previous PHY code waited a fixed amount of time for the DLL to lock at power on time. Unfortunately, the time for the DLL to lock is actually a bit more dynamic and can be longer if the card clock is slower. Instead of waiting a fixed 30 us, let's now dynamically wait until the lock bit gets set. We'll wait up to 10 ms which should be OK even if the card clock is at the super slow 100 kHz. On its own, this change makes the PHY power on code a little more robust. Before this change the PHY was relying on the eMMC code to make sure the PHY was only powered on when the card clock was set to at least 50 MHz before, though this reliance wasn't documented anywhere. This change will be even more useful in future changes where we actually need to be able to wait for a DLL lock at slower clock speeds. Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Shawn Lin Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index a69f536..2d059c0 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -85,6 +85,7 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, { unsigned int caldone; unsigned int dllrdy; + unsigned long timeout; /* * Keep phyctrl_pdb and phyctrl_endll low to allow @@ -137,15 +138,26 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); /* - * After enable analog DLL circuits, we need an extra 10.2us - * for dll to be ready for work. But according to testing, we - * find some chips need more than 25us. + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100 kHZ) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * Hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. */ - udelay(30); - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &dllrdy); - dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + timeout = jiffies + msecs_to_jiffies(10); + do { + udelay(1); + + regmap_read(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + &dllrdy); + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (dllrdy == PHYCTRL_DLLRDY_DONE) + break; + } while (!time_after(jiffies, timeout)); + if (dllrdy != PHYCTRL_DLLRDY_DONE) { pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); return -ETIMEDOUT; -- cgit v0.10.2 From ca572f4636aa6f99f1b9fcd3901af30606f8b357 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:45 -0700 Subject: mmc: sdhci-of-arasan: Always power the PHY off/on when clock changes In commit 802ac39a5566 ("mmc: sdhci-of-arasan: fix set_clock when a phy is supported") we added code to power the PHY off and on whenever the clock was changed but we avoided doing the power cycle code when the clock was low speed. Let's now do it always. Although there may be other reasons for power cycling the PHY when the clock changes, one of the main reasons is that we need to give the DLL a chance to re-lock with the new clock. One of the things that the DLL is for is tuning the Receive Clock in HS200 mode and STRB in HS400 mode. Thus it is clear that we should make sure we power cycle the PHY (and wait for the DLL to lock) when we know we'll be in one of these two speed modes. That's what the original code did, though it used the clock rate rather than the speed mode. However, even in speed modes other than HS200,/HS400 the DLL is used for something since it can be clearly observed that the PHY doesn't function properly if you leave the DLL off. Although it appears less important to power cycle the PHY and wait for the DLL to lock when not in HS200/HS400 modes (no bugs were reported), it still seems wise to let the locking always happen nevertheless. Note: as part of this, we make sure that we never try to turn the PHY on when the clock is off (when the clock rate is 0). The PHY cannot work when the clock is off since its DLL can't lock. This change requires ("phy: rockchip-emmc: Increase lock time allowance") and will cause problems if picked without that change. Signed-off-by: Douglas Anderson Reviewed-by: Shawn Lin Tested-by: Heiko Stuebner Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 533e2bc..3ff1711 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -35,11 +35,13 @@ /** * struct sdhci_arasan_data * @clk_ahb: Pointer to the AHB clock - * @phy: Pointer to the generic phy + * @phy: Pointer to the generic phy + * @phy_on: True if the PHY is turned on. */ struct sdhci_arasan_data { struct clk *clk_ahb; struct phy *phy; + bool phy_on; }; static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) @@ -61,12 +63,10 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); - bool ctrl_phy = false; - if (clock > MMC_HIGH_52_MAX_DTR && (!IS_ERR(sdhci_arasan->phy))) - ctrl_phy = true; + if (sdhci_arasan->phy_on && !IS_ERR(sdhci_arasan->phy)) { + sdhci_arasan->phy_on = false; - if (ctrl_phy) { spin_unlock_irq(&host->lock); phy_power_off(sdhci_arasan->phy); spin_lock_irq(&host->lock); @@ -74,7 +74,9 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_set_clock(host, clock); - if (ctrl_phy) { + if (host->mmc->actual_clock && !IS_ERR(sdhci_arasan->phy)) { + sdhci_arasan->phy_on = true; + spin_unlock_irq(&host->lock); phy_power_on(sdhci_arasan->phy); spin_lock_irq(&host->lock); @@ -257,12 +259,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) goto clk_disable_all; } - ret = phy_power_on(sdhci_arasan->phy); - if (ret < 0) { - dev_err(&pdev->dev, "phy_power_on err.\n"); - goto err_phy_power; - } - host->mmc_host_ops.hs400_enhanced_strobe = sdhci_arasan_hs400_enhanced_strobe; } @@ -275,9 +271,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) err_add_host: if (!IS_ERR(sdhci_arasan->phy)) - phy_power_off(sdhci_arasan->phy); -err_phy_power: - if (!IS_ERR(sdhci_arasan->phy)) phy_exit(sdhci_arasan->phy); clk_disable_all: clk_disable_unprepare(clk_xin); -- cgit v0.10.2 From 65820199272d0fe764aa31bb2e30dc670e98135a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:46 -0700 Subject: Documentation: mmc: sdhci-of-arasan: Add soc-ctl-syscon for corecfg regs As can be seen in Arasan's datasheet [1] there are several "corecfg" settings in their SDHCI IP Block that are supposed to be controlled by software. Although the datasheet referenced is a bit vague about how to access corecfg, in Figure 5 you can see that for Arasan's PHY (a separate component than their SDHCI component) they describe the "phyctrl" registers as being "FROM SOC CTL REG", implying that it's up to the licensee of the Arasan IP block to implement these registers. It seems sane to assume that the "corecfg" registers in their SDHCI IP block works in a similar way for all licensees of the IP Block. Device tree has a model that allows a device to get a reference to random registers located elsewhere in the SoC: sysctl. Let's leverage this model and allow adding a sysctl reference to access the control registers for the Arasan SDHCI PHYs. Having a reference to the control registers doesn't do much for us on its own since the Arasan spec doesn't specify how these corecfg values are laid out in memory. In the SDHCI driver we'll need a map detailing where each corecfg can be found in each implementation. This map can be found using the primary compatible string of the SDHCI device. In that spirit, document that existing rk3399 device trees already have a specific compatible string, though up to now they've always been relying on the driver supporting the generic. Note that since existing devices seem to work fairly well as-is, we'll list the syscon reference as "optional", but it's likely that we'll run into much fewer problems if we can actually set the proper values in the syscon, so it is strongly suggested that any SoCs where we have a map to set the corecfg also include a reference to the syscon. [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf Signed-off-by: Douglas Anderson Acked-by: Rob Herring Reviewed-by: Heiko Stuebner Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 31b35c3..476604e 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -9,8 +9,12 @@ Device Tree Bindings for the Arasan SDHCI Controller [4] Documentation/devicetree/bindings/phy/phy-bindings.txt Required Properties: - - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or - 'arasan,sdhci-4.9a' or 'arasan,sdhci-5.1' + - compatible: Compatibility string. One of: + - "arasan,sdhci-8.9a": generic Arasan SDHCI 8.9a PHY + - "arasan,sdhci-4.9a": generic Arasan SDHCI 4.9a PHY + - "arasan,sdhci-5.1": generic Arasan SDHCI 5.1 PHY + - "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1": rk3399 eMMC PHY + For this device it is strongly suggested to include arasan,soc-ctl-syscon. - reg: From mmc bindings: Register location and length. - clocks: From clock bindings: Handles to clock inputs. - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb" @@ -22,6 +26,11 @@ Required Properties for "arasan,sdhci-5.1": - phys: From PHY bindings: Phandle for the Generic PHY for arasan. - phy-names: MUST be "phy_arasan". +Optional Properties: + - arasan,soc-ctl-syscon: A phandle to a syscon device (see ../mfd/syscon.txt) + used to access core corecfg registers. Offsets of registers in this + syscon are determined based on the main compatible string for the device. + Example: sdhci@e0100000 { compatible = "arasan,sdhci-8.9a"; @@ -42,3 +51,17 @@ Example: phys = <&emmc_phy>; phy-names = "phy_arasan"; } ; + + sdhci: sdhci@fe330000 { + compatible = "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1"; + reg = <0x0 0xfe330000 0x0 0x10000>; + interrupts = ; + clocks = <&cru SCLK_EMMC>, <&cru ACLK_EMMC>; + clock-names = "clk_xin", "clk_ahb"; + arasan,soc-ctl-syscon = <&grf>; + assigned-clocks = <&cru SCLK_EMMC>; + assigned-clock-rates = <200000000>; + phys = <&emmc_phy>; + phy-names = "phy_arasan"; + status = "disabled"; + }; -- cgit v0.10.2 From 3ea4666e8d429223fbb39c1dccee7599ef7657d5 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:47 -0700 Subject: mmc: sdhci-of-arasan: Properly set corecfg_baseclkfreq on rk3399 In the the earlier change in this series ("Documentation: mmc: sdhci-of-arasan: Add soc-ctl-syscon for corecfg regs") we can see the mechansim for specifying a syscon to properly set corecfg registers in sdhci-of-arasan. Now let's use this mechanism to properly set corecfg_baseclkfreq on rk3399. >From [1] the corecfg_baseclkfreq is supposed to be set to: Base Clock Frequency for SD Clock. This is the frequency of the xin_clk. This is a relatively easy thing to do. Note that we assume that xin_clk is not dynamic and we can check the clock at probe time. If any real devices have a dynamic xin_clk future patches could register for notifiers for the clock. At the moment, setting corecfg_baseclkfreq is only supported for rk3399 since we need a specific map for each implementation. The code is written in a generic way that should make this easy to extend to other SoCs. Note that a specific compatible string for rk3399 is already in use and so we add that to the table to match rk3399. [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf Signed-off-by: Douglas Anderson Reviewed-by: Heiko Stuebner Reviewed-by: Shawn Lin Tested-by: Heiko Stuebner Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 3ff1711..1286fe8 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -19,9 +19,11 @@ * your option) any later version. */ +#include #include #include #include +#include #include "sdhci-pltfm.h" #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c @@ -32,18 +34,115 @@ #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) #define CLK_CTRL_TIMEOUT_MIN_EXP 13 +/* + * On some SoCs the syscon area has a feature where the upper 16-bits of + * each 32-bit register act as a write mask for the lower 16-bits. This allows + * atomic updates of the register without locking. This macro is used on SoCs + * that have that feature. + */ +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +/** + * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map + * + * @reg: Offset within the syscon of the register containing this field + * @width: Number of bits for this field + * @shift: Bit offset within @reg of this field (or -1 if not avail) + */ +struct sdhci_arasan_soc_ctl_field { + u32 reg; + u16 width; + s16 shift; +}; + +/** + * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers + * + * It's up to the licensee of the Arsan IP block to make these available + * somewhere if needed. Presumably these will be scattered somewhere that's + * accessible via the syscon API. + * + * @baseclkfreq: Where to find corecfg_baseclkfreq + * @hiword_update: If true, use HIWORD_UPDATE to access the syscon + */ +struct sdhci_arasan_soc_ctl_map { + struct sdhci_arasan_soc_ctl_field baseclkfreq; + bool hiword_update; +}; + /** * struct sdhci_arasan_data - * @clk_ahb: Pointer to the AHB clock - * @phy: Pointer to the generic phy - * @phy_on: True if the PHY is turned on. + * @clk_ahb: Pointer to the AHB clock + * @phy: Pointer to the generic phy + * @phy_on: True if the PHY is turned on. + * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. + * @soc_ctl_map: Map to get offsets into soc_ctl registers. */ struct sdhci_arasan_data { struct clk *clk_ahb; struct phy *phy; bool phy_on; + + struct regmap *soc_ctl_base; + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; +}; + +static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { + .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 }, + .hiword_update = true, }; +/** + * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers + * + * This function allows writing to fields in sdhci_arasan_soc_ctl_map. + * Note that if a field is specified as not available (shift < 0) then + * this function will silently return an error code. It will be noisy + * and print errors for any other (unexpected) errors. + * + * @host: The sdhci_host + * @fld: The field to write to + * @val: The value to write + */ +static int sdhci_arasan_syscon_write(struct sdhci_host *host, + const struct sdhci_arasan_soc_ctl_field *fld, + u32 val) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base; + u32 reg = fld->reg; + u16 width = fld->width; + s16 shift = fld->shift; + int ret; + + /* + * Silently return errors for shift < 0 so caller doesn't have + * to check for fields which are optional. For fields that + * are required then caller needs to do something special + * anyway. + */ + if (shift < 0) + return -EINVAL; + + if (sdhci_arasan->soc_ctl_map->hiword_update) + ret = regmap_write(soc_ctl_base, reg, + HIWORD_UPDATE(val, GENMASK(width, 0), + shift)); + else + ret = regmap_update_bits(soc_ctl_base, reg, + GENMASK(shift + width, shift), + val << shift); + + /* Yell about (unexpected) regmap errors */ + if (ret) + pr_warn("%s: Regmap write fail: %d\n", + mmc_hostname(host->mmc), ret); + + return ret; +} + static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) { u32 div; @@ -191,9 +290,66 @@ static int sdhci_arasan_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, sdhci_arasan_resume); +static const struct of_device_id sdhci_arasan_of_match[] = { + /* SoC-specific compatible strings w/ soc_ctl_map */ + { + .compatible = "rockchip,rk3399-sdhci-5.1", + .data = &rk3399_soc_ctl_map, + }, + + /* Generic compatible below here */ + { .compatible = "arasan,sdhci-8.9a" }, + { .compatible = "arasan,sdhci-5.1" }, + { .compatible = "arasan,sdhci-4.9a" }, + + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); + +/** + * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq + * + * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This + * function can be used to make that happen. + * + * NOTES: + * - Many existing devices don't seem to do this and work fine. To keep + * compatibility for old hardware where the device tree doesn't provide a + * register map, this function is a noop if a soc_ctl_map hasn't been provided + * for this platform. + * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider + * to achieve lower clock rates. That means that this function is called once + * at probe time and never called again. + * + * @host: The sdhci_host + */ +static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = + sdhci_arasan->soc_ctl_map; + u32 mhz = DIV_ROUND_CLOSEST(clk_get_rate(pltfm_host->clk), 1000000); + + /* Having a map is optional */ + if (!soc_ctl_map) + return; + + /* If we have a map, we expect to have a syscon */ + if (!sdhci_arasan->soc_ctl_base) { + pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", + mmc_hostname(host->mmc)); + return; + } + + sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz); +} + static int sdhci_arasan_probe(struct platform_device *pdev) { int ret; + const struct of_device_id *match; + struct device_node *node; struct clk *clk_xin; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; @@ -207,6 +363,23 @@ static int sdhci_arasan_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node); + sdhci_arasan->soc_ctl_map = match->data; + + node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0); + if (node) { + sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); + of_node_put(node); + + if (IS_ERR(sdhci_arasan->soc_ctl_base)) { + ret = PTR_ERR(sdhci_arasan->soc_ctl_base); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get syscon: %d\n", + ret); + goto err_pltfm_free; + } + } + 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"); @@ -236,6 +409,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); pltfm_host->clk = clk_xin; + sdhci_arasan_update_baseclkfreq(host); + ret = mmc_of_parse(host->mmc); if (ret) { dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); @@ -301,14 +476,6 @@ static int sdhci_arasan_remove(struct platform_device *pdev) return ret; } -static const struct of_device_id sdhci_arasan_of_match[] = { - { .compatible = "arasan,sdhci-8.9a" }, - { .compatible = "arasan,sdhci-5.1" }, - { .compatible = "arasan,sdhci-4.9a" }, - { } -}; -MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); - static struct platform_driver sdhci_arasan_driver = { .driver = { .name = "sdhci-arasan", -- cgit v0.10.2 From 6db90c5976ff248ab6e14e0003fb232bfeee0c7a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:49 -0700 Subject: Documentation: mmc: sdhci-of-arasan: Add ability to export card clock Some SD/eMMC PHYs (like the PHY from Arasan that is designed to work with arasan,sdhci-5.1) need to know the card clock frequency in order to function properly. Physically in a SoC this clock is exported from the SDHCI IP block to the PHY IP block and the PHY needs to know the speed. Let's export the SDHCI card clock using a standard device tree mechanism so that the PHY can get access to it and query the card clock frequency. Signed-off-by: Douglas Anderson Acked-by: Rob Herring Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 476604e..3404afa 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -30,6 +30,12 @@ Optional Properties: - arasan,soc-ctl-syscon: A phandle to a syscon device (see ../mfd/syscon.txt) used to access core corecfg registers. Offsets of registers in this syscon are determined based on the main compatible string for the device. + - clock-output-names: If specified, this will be the name of the card clock + which will be exposed by this device. Required if #clock-cells is + specified. + - #clock-cells: If specified this should be the value <0>. With this property + in place we will export a clock representing the Card Clock. This clock + is expected to be consumed by our PHY. You must also specify Example: sdhci@e0100000 { @@ -61,7 +67,9 @@ Example: arasan,soc-ctl-syscon = <&grf>; assigned-clocks = <&cru SCLK_EMMC>; assigned-clock-rates = <200000000>; + clock-output-names = "emmc_cardclock"; phys = <&emmc_phy>; phy-names = "phy_arasan"; + #clock-cells = <0>; status = "disabled"; }; -- cgit v0.10.2 From c390f2110adf1c8579b7693eb241b47add4976e6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:50 -0700 Subject: mmc: sdhci-of-arasan: Add ability to export card clock Some SD/eMMC PHYs (like the PHY from Arasan that is designed to work with arasan,sdhci-5.1) need to know the card clock in order to function properly. Let's add the ability to expose this clock. Any PHY that needs to know the clock rate can add a reference and query the clock rate. At the moment we register a CLK_GET_RATE_NOCACHE clock that simply allows querying the clock. This allows us to be less intrusive with regards to the main SDHCI driver, which has complex logic for adjusting the SD clock. Right now we always fully power cycle the PHY when the clock changes and that gives the PHY a good chance to query our clock. Signed-off-by: Douglas Anderson Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 0b5c871..5274f50 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -122,6 +122,7 @@ config MMC_SDHCI_OF_ARASAN tristate "SDHCI OF support for the Arasan SDHCI controllers" depends on MMC_SDHCI_PLTFM depends on OF + depends on COMMON_CLK help This selects the Arasan Secure Digital Host Controller Interface (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 1286fe8..678f316 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -19,6 +19,7 @@ * your option) any later version. */ +#include #include #include #include @@ -73,17 +74,24 @@ struct sdhci_arasan_soc_ctl_map { /** * struct sdhci_arasan_data + * @host: Pointer to the main SDHCI host structure. * @clk_ahb: Pointer to the AHB clock * @phy: Pointer to the generic phy * @phy_on: True if the PHY is turned on. + * @sdcardclk_hw: Struct for the clock we might provide to a PHY. + * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. * @soc_ctl_map: Map to get offsets into soc_ctl registers. */ struct sdhci_arasan_data { + struct sdhci_host *host; struct clk *clk_ahb; struct phy *phy; bool phy_on; + struct clk_hw sdcardclk_hw; + struct clk *sdcardclk; + struct regmap *soc_ctl_base; const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; }; @@ -307,6 +315,31 @@ static const struct of_device_id sdhci_arasan_of_match[] = { MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); /** + * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate + * + * Return the current actual rate of the SD card clock. This can be used + * to communicate with out PHY. + * + * @hw: Pointer to the hardware clock structure. + * @parent_rate The parent rate (should be rate of clk_xin). + * Returns the card clock rate. + */ +static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) + +{ + struct sdhci_arasan_data *sdhci_arasan = + container_of(hw, struct sdhci_arasan_data, sdcardclk_hw); + struct sdhci_host *host = sdhci_arasan->host; + + return host->mmc->actual_clock; +} + +static const struct clk_ops arasan_sdcardclk_ops = { + .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, +}; + +/** * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq * * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This @@ -345,6 +378,83 @@ static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host) sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz); } +/** + * sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use + * + * Some PHY devices need to know what the actual card clock is. In order for + * them to find out, we'll provide a clock through the common clock framework + * for them to query. + * + * Note: without seriously re-architecting SDHCI's clock code and testing on + * all platforms, there's no way to create a totally beautiful clock here + * with all clock ops implemented. Instead, we'll just create a clock that can + * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock + * framework that we're doing things behind its back. This should be sufficient + * to create nice clean device tree bindings and later (if needed) we can try + * re-architecting SDHCI if we see some benefit to it. + * + * @sdhci_arasan: Our private data structure. + * @clk_xin: Pointer to the functional clock + * @dev: Pointer to our struct device. + * Returns 0 on success and error value on error + */ +static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, + struct clk *clk_xin, + struct device *dev) +{ + struct device_node *np = dev->of_node; + struct clk_init_data sdcardclk_init; + const char *parent_clk_name; + int ret; + + /* Providing a clock to the PHY is optional; no error if missing */ + if (!of_find_property(np, "#clock-cells", NULL)) + return 0; + + ret = of_property_read_string_index(np, "clock-output-names", 0, + &sdcardclk_init.name); + if (ret) { + dev_err(dev, "DT has #clock-cells but no clock-output-names\n"); + return ret; + } + + parent_clk_name = __clk_get_name(clk_xin); + sdcardclk_init.parent_names = &parent_clk_name; + sdcardclk_init.num_parents = 1; + sdcardclk_init.flags = CLK_GET_RATE_NOCACHE; + sdcardclk_init.ops = &arasan_sdcardclk_ops; + + sdhci_arasan->sdcardclk_hw.init = &sdcardclk_init; + sdhci_arasan->sdcardclk = + devm_clk_register(dev, &sdhci_arasan->sdcardclk_hw); + sdhci_arasan->sdcardclk_hw.init = NULL; + + ret = of_clk_add_provider(np, of_clk_src_simple_get, + sdhci_arasan->sdcardclk); + if (ret) + dev_err(dev, "Failed to add clock provider\n"); + + return ret; +} + +/** + * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk() + * + * Should be called any time we're exiting and sdhci_arasan_register_sdclk() + * returned success. + * + * @dev: Pointer to our struct device. + */ +static void sdhci_arasan_unregister_sdclk(struct device *dev) +{ + struct device_node *np = dev->of_node; + + if (!of_find_property(np, "#clock-cells", NULL)) + return; + + of_clk_del_provider(dev->of_node); +} + static int sdhci_arasan_probe(struct platform_device *pdev) { int ret; @@ -362,6 +472,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + sdhci_arasan->host = host; match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node); sdhci_arasan->soc_ctl_map = match->data; @@ -411,10 +522,14 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan_update_baseclkfreq(host); + ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); + if (ret) + goto clk_disable_all; + ret = mmc_of_parse(host->mmc); if (ret) { dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret); - goto clk_disable_all; + goto unreg_clk; } sdhci_arasan->phy = ERR_PTR(-ENODEV); @@ -425,13 +540,13 @@ static int sdhci_arasan_probe(struct platform_device *pdev) if (IS_ERR(sdhci_arasan->phy)) { ret = PTR_ERR(sdhci_arasan->phy); dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n"); - goto clk_disable_all; + goto unreg_clk; } ret = phy_init(sdhci_arasan->phy); if (ret < 0) { dev_err(&pdev->dev, "phy_init err.\n"); - goto clk_disable_all; + goto unreg_clk; } host->mmc_host_ops.hs400_enhanced_strobe = @@ -447,6 +562,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) err_add_host: if (!IS_ERR(sdhci_arasan->phy)) phy_exit(sdhci_arasan->phy); +unreg_clk: + sdhci_arasan_unregister_sdclk(&pdev->dev); clk_disable_all: clk_disable_unprepare(clk_xin); clk_dis_ahb: @@ -469,6 +586,8 @@ static int sdhci_arasan_remove(struct platform_device *pdev) phy_exit(sdhci_arasan->phy); } + sdhci_arasan_unregister_sdclk(&pdev->dev); + ret = sdhci_pltfm_unregister(pdev); clk_disable_unprepare(clk_ahb); -- cgit v0.10.2 From 110754563c52b9cf375d48cfbe93cc1a5e92d530 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:51 -0700 Subject: Documentation: phy: Let the rockchip eMMC PHY get an exported card clock As of an earlier change in this series ("Documentation: mmc: sdhci-of-arasan: Add ability to export card clock") the SDHCI driver used on Rockchip SoCs can now expose its clock. Let's now specify that the PHY can use it. Letting the PHY get access to this clock means it can adjust phyctrl_frqsel field appropriately. Although the Rockchip PHY appears slightly different than the reference Arasan one, you can see that the Arasan datasheet [1] had it defined as: Select the frequency range of DLL operation: 3b'000 => 200MHz to 170 MHz 3b'001 => 170MHz to 140 MHz 3b'010 => 140MHz to 110 MHz 3b'011 => 110MHz to 80MHz 3b'100 => 80MHz to 50 MHz 3b'101 => 275Mhz to 250MHz 3b'110 => 250MHz to 225MHz 3b'111 => 225MHz to 200MHz On the Rockchip version of the PHY we have less granularity but the idea is the same. [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Acked-by: Rob Herring Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt index 555cb0f..e3ea557 100644 --- a/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt +++ b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt @@ -7,6 +7,13 @@ Required properties: - reg: PHY register address offset and length in "general register files" +Optional clocks using the clock bindings (see ../clock/clock-bindings.txt), +specified by name: + - clock-names: Should contain "emmcclk". Although this is listed as optional + (because most boards can get basic functionality without having + access to it), it is strongly suggested. + - clocks: Should have a phandle to the card clock exported by the SDHCI driver. + Example: @@ -20,6 +27,8 @@ grf: syscon@ff770000 { emmcphy: phy@f780 { compatible = "rockchip,rk3399-emmc-phy"; reg = <0xf780 0x20>; + clocks = <&sdhci>; + clock-names = "emmcclk"; #phy-cells = <0>; }; }; -- cgit v0.10.2 From 352051ef1ff5efa8ba050bc3a581dbf136a1f97b Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:52 -0700 Subject: phy: rockchip-emmc: Minor code cleanup in rockchip_emmc_phy_power_on/off() There's no reason to store the return value of rockchip_emmc_phy_power() in a variable nor to check it. Just return it. Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Shawn Lin Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 2d059c0..23fe508 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -169,20 +169,14 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, static int rockchip_emmc_phy_power_off(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - int ret = 0; /* Power down emmc phy analog blocks */ - ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); - if (ret) - return ret; - - return 0; + return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); } static int rockchip_emmc_phy_power_on(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - int ret = 0; /* DLL operation: 200 MHz */ regmap_write(rk_phy->reg_base, @@ -213,11 +207,7 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_OTAPDLYSEL_SHIFT)); /* Power up emmc phy analog blocks */ - ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); - if (ret) - return ret; - - return 0; + return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); } static const struct phy_ops ops = { -- cgit v0.10.2 From 52c0624a10cce0cdc8be2d15462b9c8d76b7700d Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:53 -0700 Subject: phy: rockchip-emmc: Set phyctrl_frqsel based on card clock The "phyctrl_frqsel" is described in the Arasan datasheet [1] as "the frequency range of DLL operation". Although the Rockchip variant of this PHY has different ranges than the reference Arasan PHY it appears as if the functionality is similar. We should set this phyctrl field properly. Note: as per Rockchip engineers, apparently the "phyctrl_frqsel" is actually only useful in HS200 / HS400 modes even though the DLL itself it used for some purposes in all modes. See the discussion in the earlier change in this series: ("mmc: sdhci-of-arasan: Always power the PHY off/on when clock changes"). In any case, it shouldn't hurt to set this always. Note that this change should allow boards to run at HS200 / HS400 speed modes while running at 100 MHz or 150 MHz. In fact, running HS400 at 150 MHz (giving 300 MB/s) is the main motivation of this series, since performance is still good but signal integrity problems are less prevelant at 150 MHz. [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 23fe508..9dce958 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -78,15 +79,53 @@ struct rockchip_emmc_phy { unsigned int reg_offset; struct regmap *reg_base; + struct clk *emmcclk; }; -static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, - bool on_off) +static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) { + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); unsigned int caldone; unsigned int dllrdy; + unsigned int freqsel = PHYCTRL_FREQSEL_200M; unsigned long timeout; + if (rk_phy->emmcclk != NULL) { + unsigned long rate = clk_get_rate(rk_phy->emmcclk); + unsigned long ideal_rate; + unsigned long diff; + + switch (rate) { + case 0 ... 74999999: + ideal_rate = 50000000; + freqsel = PHYCTRL_FREQSEL_50M; + break; + case 75000000 ... 124999999: + ideal_rate = 100000000; + freqsel = PHYCTRL_FREQSEL_100M; + break; + case 125000000 ... 174999999: + ideal_rate = 150000000; + freqsel = PHYCTRL_FREQSEL_150M; + break; + default: + ideal_rate = 200000000; + break; + }; + + diff = (rate > ideal_rate) ? + rate - ideal_rate : ideal_rate - rate; + + /* + * In order for tuning delays to be accurate we need to be + * pretty spot on for the DLL range, so warn if we're too + * far off. Also warn if we're above the 200 MHz max. Don't + * warn for really slow rates since we won't be tuning then. + */ + if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + } + /* * Keep phyctrl_pdb and phyctrl_endll low to allow * initialization of CALIO state M/C DFFs @@ -132,6 +171,13 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return -ETIMEDOUT; } + /* Set the frequency of the DLL operation */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Turn on the DLL */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, @@ -166,25 +212,54 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return 0; } -static int rockchip_emmc_phy_power_off(struct phy *phy) +static int rockchip_emmc_phy_init(struct phy *phy) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + int ret = 0; + + /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to init the PHY + * + * The clock is optional, so upon any error we just set to NULL. + * + * NOTE: we don't do anything special for EPROBE_DEFER here. Given the + * above expected use case, EPROBE_DEFER isn't sensible to expect, so + * it's just like any other error. + */ + rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + if (IS_ERR(rk_phy->emmcclk)) { + dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); + rk_phy->emmcclk = NULL; + } + + return ret; +} + +static int rockchip_emmc_phy_exit(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + clk_put(rk_phy->emmcclk); + + return 0; +} + +static int rockchip_emmc_phy_power_off(struct phy *phy) +{ /* Power down emmc phy analog blocks */ - return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); } static int rockchip_emmc_phy_power_on(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - /* DLL operation: 200 MHz */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(PHYCTRL_FREQSEL_200M, - PHYCTRL_FREQSEL_MASK, - PHYCTRL_FREQSEL_SHIFT)); - /* Drive impedance: 50 Ohm */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, @@ -207,10 +282,12 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_OTAPDLYSEL_SHIFT)); /* Power up emmc phy analog blocks */ - return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); } static const struct phy_ops ops = { + .init = rockchip_emmc_phy_init, + .exit = rockchip_emmc_phy_exit, .power_on = rockchip_emmc_phy_power_on, .power_off = rockchip_emmc_phy_power_off, .owner = THIS_MODULE, -- cgit v0.10.2 From 860951c5f0f5caf595799d98b93a8304bf70f08d Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 21 Jun 2016 10:13:26 +0900 Subject: mmc: host: use the defined function to check whether card is removable In linux/mmc/host.h, mmc_card_is_removable() is already defined. It should be maintainted more easier than now. Signed-off-by: Jaehoon Chung Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 2cc6123..8012858 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1451,8 +1451,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) int gpio_cd = mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ - if ((mmc->caps & MMC_CAP_NEEDS_POLL) || - (mmc->caps & MMC_CAP_NONREMOVABLE)) + if ((mmc->caps & MMC_CAP_NEEDS_POLL) || !mmc_card_is_removable(mmc)) present = 1; else if (gpio_cd >= 0) present = gpio_cd; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 3d1ea5e..fb3ca82 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1065,7 +1065,7 @@ static int mxcmci_probe(struct platform_device *pdev) if (pdata) dat3_card_detect = pdata->dat3_card_detect; - else if (!(mmc->caps & MMC_CAP_NONREMOVABLE) + else if (mmc_card_is_removable(mmc) && !of_property_read_bool(pdev->dev.of_node, "cd-gpios")) dat3_card_detect = true; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 00a8a40..1d64712 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -264,12 +264,12 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) } dev_dbg(dev, "non-removable=%c\n", - (host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N'); + mmc_card_is_removable(host->mmc) ? 'N' : 'Y'); dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n", (mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N', (mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N'); - if (host->mmc->caps & MMC_CAP_NONREMOVABLE) + if (!mmc_card_is_removable(host->mmc)) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; dev_dbg(dev, "is_8bit=%c\n", @@ -288,7 +288,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) } /* if device is eMMC, emulate card insert right here */ - if (host->mmc->caps & MMC_CAP_NONREMOVABLE) { + if (!mmc_card_is_removable(host->mmc)) { ret = sdhci_bcm_kona_sd_card_emulate(host, 1); if (ret) { dev_err(dev, diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index d4cef71..a9b7fc0 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -288,7 +288,7 @@ static int sdhci_at91_probe(struct platform_device *pdev) * Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries * to enable polling via device tree with broken-cd property. */ - if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) && + if (mmc_card_is_removable(host->mmc) && mmc_gpio_get_cd(host->mmc) < 0) { host->mmc->caps |= MMC_CAP_NEEDS_POLL; host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 320e1c2..c95ba83 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -183,7 +183,7 @@ static void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host) writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2); - if (mhost->caps & MMC_CAP_NONREMOVABLE) + if (!mmc_card_is_removable(mhost)) cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE; else /* CARD _D ET_CTRL */ diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 702a245..cdcaba2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -117,7 +117,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) u32 present; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - (host->mmc->caps & MMC_CAP_NONREMOVABLE)) + !mmc_card_is_removable(host->mmc)) return; if (enable) { @@ -1617,7 +1617,7 @@ static int sdhci_get_cd(struct mmc_host *mmc) return 0; /* If nonremovable, assume that the card is always present. */ - if (host->mmc->caps & MMC_CAP_NONREMOVABLE) + if (!mmc_card_is_removable(host->mmc)) return 1; /* @@ -3089,7 +3089,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - !(mmc->caps & MMC_CAP_NONREMOVABLE) && + mmc_card_is_removable(mmc) && mmc_gpio_get_cd(host->mmc) < 0) mmc->caps |= MMC_CAP_NEEDS_POLL; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index f44e2ab..92467ef 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1086,7 +1086,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD || mmc->caps & MMC_CAP_NEEDS_POLL || - mmc->caps & MMC_CAP_NONREMOVABLE || + !mmc_card_is_removable(mmc) || mmc->slot.cd_irq >= 0); if (tmio_mmc_clk_enable(_host) < 0) { -- cgit v0.10.2 From a7c53671ea2612c825245143b8b591709616f95d Mon Sep 17 00:00:00 2001 From: Chuanxiao Dong Date: Wed, 22 Jun 2016 14:40:01 +0300 Subject: mmc: sdhci: use pr_err for sdhci_dumpregs sdhci_dumpregs is used to dump registers when error happens. Thus it should use pr_err instead of pr_debug to show more information about the hardware. Signed-off-by: Chuanxiao Dong [Fix whitespace and checkpatch warnings] Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cdcaba2..15bdbcb 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -52,58 +52,58 @@ static int sdhci_get_cd(struct mmc_host *mmc); static void sdhci_dumpregs(struct sdhci_host *host) { - pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", - mmc_hostname(host->mmc)); - - pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", - sdhci_readl(host, SDHCI_DMA_ADDRESS), - sdhci_readw(host, SDHCI_HOST_VERSION)); - pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", - sdhci_readw(host, SDHCI_BLOCK_SIZE), - sdhci_readw(host, SDHCI_BLOCK_COUNT)); - pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", - sdhci_readl(host, SDHCI_ARGUMENT), - sdhci_readw(host, SDHCI_TRANSFER_MODE)); - pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", - sdhci_readl(host, SDHCI_PRESENT_STATE), - sdhci_readb(host, SDHCI_HOST_CONTROL)); - pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", - sdhci_readb(host, SDHCI_POWER_CONTROL), - sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); - pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", - sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), - sdhci_readw(host, SDHCI_CLOCK_CONTROL)); - pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", - sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), - sdhci_readl(host, SDHCI_INT_STATUS)); - pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", - sdhci_readl(host, SDHCI_INT_ENABLE), - sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); - pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", - sdhci_readw(host, SDHCI_ACMD12_ERR), - sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); - pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", - sdhci_readl(host, SDHCI_CAPABILITIES), - sdhci_readl(host, SDHCI_CAPABILITIES_1)); - pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", - sdhci_readw(host, SDHCI_COMMAND), - sdhci_readl(host, SDHCI_MAX_CURRENT)); - pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", - sdhci_readw(host, SDHCI_HOST_CONTROL2)); + pr_err(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", + mmc_hostname(host->mmc)); + + pr_err(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", + sdhci_readl(host, SDHCI_DMA_ADDRESS), + sdhci_readw(host, SDHCI_HOST_VERSION)); + pr_err(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", + sdhci_readw(host, SDHCI_BLOCK_SIZE), + sdhci_readw(host, SDHCI_BLOCK_COUNT)); + pr_err(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", + sdhci_readl(host, SDHCI_ARGUMENT), + sdhci_readw(host, SDHCI_TRANSFER_MODE)); + pr_err(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", + sdhci_readl(host, SDHCI_PRESENT_STATE), + sdhci_readb(host, SDHCI_HOST_CONTROL)); + pr_err(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", + sdhci_readb(host, SDHCI_POWER_CONTROL), + sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); + pr_err(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", + sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), + sdhci_readw(host, SDHCI_CLOCK_CONTROL)); + pr_err(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", + sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), + sdhci_readl(host, SDHCI_INT_STATUS)); + pr_err(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", + sdhci_readl(host, SDHCI_INT_ENABLE), + sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); + pr_err(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", + sdhci_readw(host, SDHCI_ACMD12_ERR), + sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); + pr_err(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", + sdhci_readl(host, SDHCI_CAPABILITIES), + sdhci_readl(host, SDHCI_CAPABILITIES_1)); + pr_err(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", + sdhci_readw(host, SDHCI_COMMAND), + sdhci_readl(host, SDHCI_MAX_CURRENT)); + pr_err(DRIVER_NAME ": Host ctl2: 0x%08x\n", + sdhci_readw(host, SDHCI_HOST_CONTROL2)); if (host->flags & SDHCI_USE_ADMA) { if (host->flags & SDHCI_USE_64_BIT_DMA) - pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", - readl(host->ioaddr + SDHCI_ADMA_ERROR), - readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), - readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); else - pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", - readl(host->ioaddr + SDHCI_ADMA_ERROR), - readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); } - pr_debug(DRIVER_NAME ": ===========================================\n"); + pr_err(DRIVER_NAME ": ===========================================\n"); } /*****************************************************************************\ -- cgit v0.10.2 From 2801b95e8ff0fc24d7708a67bb98902dad197635 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 23 Jun 2016 10:52:05 +0100 Subject: mmc: sdhci-msm: fix spelling mistake: "Perpheral" -> "Peripheral" trivial fix to spelling mistake in dev_err message Signed-off-by: Colin Ian King Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 0653fe7..343e4bd 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -473,7 +473,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->pclk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(msm_host->pclk)) { ret = PTR_ERR(msm_host->pclk); - dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret); + dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret); goto bus_clk_disable; } -- cgit v0.10.2 From 1b8d79c5494484d140f2a19101412b51e2d5f6b5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 21 Jun 2016 15:12:46 +0200 Subject: mmc: core: Allow hosts to specify non-support for SD commands There are host drivers which needs to valdiate for non-supported SD commands and returnn error code for such requests. To improve and simplify the behaviour, let's invent MMC_CAP2_NO_SD which these host drivers can set to tell the mmc core to skip sending SD commands during card initialization. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1d24b3a..4c823df 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2498,15 +2498,18 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) mmc_go_idle(host); - mmc_send_if_cond(host, host->ocr_avail); + if (!(host->caps2 & MMC_CAP2_NO_SD)) + mmc_send_if_cond(host, host->ocr_avail); /* Order's important: probe SDIO, then SD, then MMC */ if (!(host->caps2 & MMC_CAP2_NO_SDIO)) if (!mmc_attach_sdio(host)) return 0; - if (!mmc_attach_sd(host)) - return 0; + if (!(host->caps2 & MMC_CAP2_NO_SD)) + if (!mmc_attach_sd(host)) + return 0; + if (!mmc_attach_mmc(host)) return 0; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index d72c0c3..c22476d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -309,6 +309,7 @@ struct mmc_host { #define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */ #define MMC_CAP2_NO_SDIO (1 << 19) /* Do not send SDIO commands during initialization */ #define MMC_CAP2_HS400_ES (1 << 20) /* Host supports enhanced strobe */ +#define MMC_CAP2_NO_SD (1 << 21) /* Do not send SD commands during initialization */ mmc_pm_flag_t pm_caps; /* supported pm features */ -- cgit v0.10.2 From dab3a28b4005ecf4b6b380a8929c36de2c07a175 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 21 Jun 2016 15:12:47 +0200 Subject: mmc: sh_mmcif: Enable MMC_CAP2_NO_SD and MMC_CAP2_NO_SDIO Enable the capabilities which tells the mmc core to prevent sending SD and SDIO commands during card initialization. In this way, we can also remove the validation of non-supported commands in the ->request() callback. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index dd64b86..4d6c59f 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1009,22 +1009,6 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) host->state = STATE_REQUEST; spin_unlock_irqrestore(&host->lock, flags); - switch (mrq->cmd->opcode) { - /* MMCIF does not support SD/SDIO command */ - case MMC_SLEEP_AWAKE: /* = SD_IO_SEND_OP_COND (5) */ - case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ - if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR) - break; - case MMC_APP_CMD: - case SD_IO_RW_DIRECT: - host->state = STATE_IDLE; - mrq->cmd->error = -ETIMEDOUT; - mmc_request_done(mmc, mrq); - return; - default: - break; - } - host->mrq = mrq; sh_mmcif_start_cmd(host, mrq); @@ -1488,6 +1472,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_init_ocr(host); mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY; + mmc->caps2 |= MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + if (pd && pd->caps) mmc->caps |= pd->caps; mmc->max_segs = 32; -- cgit v0.10.2 From 5b1c29bceea1ac2dccf5dcd9ae29a221082f1a7a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 21 Jun 2016 15:12:48 +0200 Subject: mmc: sh_mmcif: Use response type to know when to enable busy detection The sh_mmcif explicity checks for certain commands to decide when to enable HW busy detection. Instead, it should only check the response type as it tells if busy detection is needed. In this way, the mmc core also gets full control whether it thinks busy detection should be done or not. In some specific scenarios, like for ERASE and STOP commands it may decide to fall back to use a CMD13 to poll the card status instead. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 4d6c59f..96a45d8 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -819,10 +819,12 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, tmp |= CMD_SET_RTYP_NO; break; case MMC_RSP_R1: - case MMC_RSP_R1B: case MMC_RSP_R3: tmp |= CMD_SET_RTYP_6B; break; + case MMC_RSP_R1B: + tmp |= CMD_SET_RBSY | CMD_SET_RTYP_6B; + break; case MMC_RSP_R2: tmp |= CMD_SET_RTYP_17B; break; @@ -830,17 +832,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, dev_err(dev, "Unsupported response type.\n"); break; } - switch (opc) { - /* RBSY */ - case MMC_SLEEP_AWAKE: - case MMC_SWITCH: - case MMC_STOP_TRANSMISSION: - case MMC_SET_WRITE_PROT: - case MMC_CLR_WRITE_PROT: - case MMC_ERASE: - tmp |= CMD_SET_RBSY; - break; - } + /* WDAT / DATW */ if (data) { tmp |= CMD_SET_WDAT; @@ -925,23 +917,13 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, { struct mmc_command *cmd = mrq->cmd; u32 opc = cmd->opcode; - u32 mask; + u32 mask = 0; unsigned long flags; - switch (opc) { - /* response busy check */ - case MMC_SLEEP_AWAKE: - case MMC_SWITCH: - case MMC_STOP_TRANSMISSION: - case MMC_SET_WRITE_PROT: - case MMC_CLR_WRITE_PROT: - case MMC_ERASE: + if (cmd->flags & MMC_RSP_BUSY) mask = MASK_START_CMD | MASK_MRBSYE; - break; - default: + else mask = MASK_START_CMD | MASK_MCRSPE; - break; - } if (host->ccs_enable) mask |= MASK_MCCSTO; -- cgit v0.10.2 From 549646a9c79d4649b41f9e817f0588ac0cc6a235 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 21 Jun 2016 15:12:49 +0200 Subject: mmc: sh_mmcif: Inform the mmc core about the max busy timeout The sh_mmcif driver is already using a 10s request timeout. Let's also inform the mmc core about this value, as there are situations when it needs to know about it. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 96a45d8..bab971b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1455,6 +1455,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY; mmc->caps2 |= MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + mmc->max_busy_timeout = 10000; if (pd && pd->caps) mmc->caps |= pd->caps; -- cgit v0.10.2 From 52e00b84187316d7ade1e715651c07b66b5b3e17 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 21 Jun 2016 15:12:50 +0200 Subject: mmc: sh_mmcif: Use a 10s timeout in the error recovery path The current value means an mdelay(1) may execute up to 10000000 times, which translates to around ~2.8 hours. This is probably not what the orignal author had in mind. Let's instead use 10s, which is the same value sh_mmcif is using for other timeouts. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index bab971b..9007784 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -574,7 +574,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) if (state1 & STS1_CMDSEQ) { sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK); sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK); - for (timeout = 10000000; timeout; timeout--) { + for (timeout = 10000; timeout; timeout--) { if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1) & STS1_CMDSEQ)) break; -- cgit v0.10.2 From c0834a585f18d2de2dfbce600334b391c870a62d Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 27 May 2016 14:36:40 +0800 Subject: mmc: dw_mmc: remove redundant of set_bit and clear_bit dw_mci_get_cd have already dealt with these for both of internal card-detect and gpio card-detect. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8012858..8a2dd04 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2615,10 +2615,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->max_seg_size = mmc->max_req_size; } - if (dw_mci_get_cd(mmc)) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - else - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); + dw_mci_get_cd(mmc); ret = mmc_add_host(mmc); if (ret) -- cgit v0.10.2 From 56f6911cff9044c87e9c60a26a6bd619556f2d56 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 27 May 2016 14:37:05 +0800 Subject: mmc: dw_mmc: check card present before starting request The main reason to add this check is to avoid unnecessary mmc_request like the on-going cmd and the corresponding sbc if the card is removed. Although we have already checked this in dw_mci_handle_cd for runtime usage of sd card and dw_mci_init_slot for noremovable devices, but there is a timing gap before it really calls dw_mci_get_cd as mmc_detect_change needs some delay here. Another gain here is that we could save some checkings of card status after sd card been removed. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8a2dd04..46a02d1 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -105,6 +105,7 @@ struct idmac_desc { static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); static int dw_mci_card_busy(struct mmc_host *mmc); +static int dw_mci_get_cd(struct mmc_host *mmc); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -1253,15 +1254,15 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) * atomic, otherwise the card could be removed in between and the * request wouldn't fail until another card was inserted. */ - spin_lock_bh(&host->lock); - if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { - spin_unlock_bh(&host->lock); + if (!dw_mci_get_cd(mmc)) { mrq->cmd->error = -ENOMEDIUM; mmc_request_done(mmc, mrq); return; } + spin_lock_bh(&host->lock); + dw_mci_queue_request(host, slot, mrq); spin_unlock_bh(&host->lock); -- cgit v0.10.2 From af6376292285fb03bbf838747a6acf3c2266db25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Forissier?= Date: Wed, 1 Jun 2016 14:39:58 +0200 Subject: mmc: dw_mmc: k3: add MMC_CAP_CMD23 Enables RPMB support for the on-board eMMC of the HiKey board as well as for eMMC modules connected to the microSD slot. Signed-off-by: Jerome Forissier Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 63c2e2e..8e9d886 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -32,6 +32,12 @@ struct k3_priv { struct regmap *reg; }; +static unsigned long dw_mci_hi6220_caps[] = { + MMC_CAP_CMD23, + MMC_CAP_CMD23, + 0 +}; + static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) { int ret; @@ -126,6 +132,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) } static const struct dw_mci_drv_data hi6220_data = { + .caps = dw_mci_hi6220_caps, .switch_voltage = dw_mci_hi6220_switch_voltage, .set_ios = dw_mci_hi6220_set_ios, .parse_dt = dw_mci_hi6220_parse_dt, -- cgit v0.10.2 From 7a3c56773e7b15d86ae1179df87b219c864b5f68 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 10 Mar 2015 08:48:10 -0700 Subject: mmc: dw_mmc: Consider HLE errors to be data and command errors The dw_mmc driver enables HLE errors as part of DW_MCI_ERROR_FLAGS but nothing in the interrupt handler actually handles them and ACKs them. That means that if we ever get an HLE error we'll just keep getting interrupts and we'll wedge things. We really don't expect HLE errors but if we ever get them we shouldn't silently ignore them. Note that I have seen HLE errors while constantly ejecting and inserting cards (ejecting while inserting, etc). Signed-off-by: Doug Anderson Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 46a02d1..5a0e2ca 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -44,11 +44,11 @@ /* Common flag combinations */ #define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ SDMMC_INT_HTO | SDMMC_INT_SBE | \ - SDMMC_INT_EBE) + SDMMC_INT_EBE | SDMMC_INT_HLE) #define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \ - SDMMC_INT_RESP_ERR) + SDMMC_INT_RESP_ERR | SDMMC_INT_HLE) #define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ - DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_HLE) + DW_MCI_CMD_ERROR_FLAGS) #define DW_MCI_SEND_STATUS 1 #define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 -- cgit v0.10.2 From e5306c3ab28696fc112025c03c20546a6ff0040f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 7 Jun 2016 14:37:19 +0100 Subject: mmc: dw_mmc: fix 32bit little-endian access of des1 field The IDMAC_SET_BUFFER1_SIZE() macro modifies des1, but does not check if the value being passed is big or little endian desptire the des1 field being marked as __le32. Fix the issue by ensuring the values are changed from the cpu endian to the descriptor endian by using cpu_to_le32. Spotted whilst doing big endian conversion work on Exynos, and stops the mmc worker thread from stalling. Signed-off-by: Ben Dooks Reviewed-by: Shawn Lin Tested-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 5a0e2ca..b7c7a76 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -92,7 +92,7 @@ struct idmac_desc { __le32 des1; /* Buffer sizes */ #define IDMAC_SET_BUFFER1_SIZE(d, s) \ - ((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff)) + ((d)->des1 = ((d)->des1 & cpu_to_le32(0x03ffe000)) | (cpu_to_le32((s) & 0x1fff))) __le32 des2; /* buffer 1 physical address */ -- cgit v0.10.2 From 46d179525a1f6d16957dcb4624517bc04142b3e7 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 26 Apr 2016 10:03:58 +0200 Subject: mmc: dw_mmc: Wait for data transfer after response errors. According to the DesignWare state machine description, after we get a "response error" or "response CRC error" we move into data transfer mode. That means that we don't necessarily need to special case trying to deal with the failure right away. We can wait until we are notified that the data transfer is complete (with or without errors) and then we can deal with the failure. It may sound strange to defer dealing with a command that we know will fail anyway, but this appears to fix a bug. During tuning (CMD19) on a specific card on an rk3288-based system, we found that we could get a "response CRC error". Sending the stop command after the "response CRC error" would then throw the system into a confused state causing all future tuning phases to report failure. When in the confused state, the controller would show these (hex codes are interrupt status register): CMD ERR: 0x00000046 (cmd=19) CMD ERR: 0x0000004e (cmd=12) DATA ERR: 0x00000208 DATA ERR: 0x0000020c CMD ERR: 0x00000104 (cmd=19) CMD ERR: 0x00000104 (cmd=12) DATA ERR: 0x00000208 DATA ERR: 0x0000020c ... ... It is inherently difficult to deal with the complexity of trying to correctly send a stop command while a data transfer is taking place since you need to deal with different corner cases caused by the fact that the data transfer could complete (with errors or without errors) during various places in sending the stop command (dw_mci_stop_dma, send_stop_abort, etc) Instead of adding a bunch of extra complexity to deal with this, it seems much simpler to just use the more straightforward (and less error-prone) path of letting the data transfer finish. There shouldn't be any huge benefit to sending the stop command slightly earlier, anyway. Signed-off-by: Doug Anderson Signed-off-by: Enric Balletbo i Serra Cc: Alim Akhtar Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b7c7a76..7e5e8b0 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1761,6 +1761,33 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (cmd->data && err) { + /* + * During UHS tuning sequence, sending the stop + * command after the response CRC error would + * throw the system into a confused state + * causing all future tuning phases to report + * failure. + * + * In such case controller will move into a data + * transfer state after a response error or + * response CRC error. Let's let that finish + * before trying to send a stop, so we'll go to + * STATE_SENDING_DATA. + * + * Although letting the data transfer take place + * will waste a bit of time (we already know + * the command was bad), it can't cause any + * errors since it's possible it would have + * taken place anyway if this tasklet got + * delayed. Allowing the transfer to take place + * avoids races and keeps things simple. + */ + if ((err != -ETIMEDOUT) && + (cmd->opcode == MMC_SEND_TUNING_BLOCK)) { + state = STATE_SENDING_DATA; + continue; + } + dw_mci_stop_dma(host); send_stop_abort(host, data); state = STATE_SENDING_STOP; -- cgit v0.10.2 From 65257a0deed5aee66b4e3708944f0be62a64cabc Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 20 Jun 2016 13:09:45 +0900 Subject: mmc: dw_mmc: remove UBSAN warning in dw_mci_setup_bus() This patch removes following UBSAN warnings in dw_mci_setup_bus(). UBSAN: Undefined behaviour in drivers/mmc/host/dw_mmc.c:1102:14 shift exponent 250 is too large for 32-bit type 'unsigned int' Call trace: [] dump_backtrace+0x0/0x380 [] show_stack+0x14/0x20 [] dump_stack+0xe0/0x120 [] ubsan_epilogue+0x18/0x68 [] __ubsan_handle_shift_out_of_bounds+0x18c/0x1bc [] dw_mci_setup_bus+0x3a0/0x438 [...] UBSAN: Undefined behaviour in drivers/mmc/host/dw_mmc.c:1132:27 shift exponent 250 is too large for 32-bit type 'unsigned int' Call trace: [] dump_backtrace+0x0/0x380 [] show_stack+0x14/0x20 [] dump_stack+0xe0/0x120 [] ubsan_epilogue+0x18/0x68 [] __ubsan_handle_shift_out_of_bounds+0x18c/0x1bc [] dw_mci_setup_bus+0x384/0x438 [...] The warnings are caused because of bit shift which is used to filter spamming message for CONFIG_MMC_CLKGATE, but the config is already removed. So this patch just removes the shift. Signed-off-by: Seung-Woo Kim Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7e5e8b0..3e9fdc0 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1100,12 +1100,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - if ((clock << div) != slot->__clk_old || force_clkinit) - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", - slot->id, host->bus_hz, clock, - div ? ((host->bus_hz / div) >> 1) : - host->bus_hz, div); + dev_info(&slot->mmc->class_dev, + "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", + slot->id, host->bus_hz, clock, + div ? ((host->bus_hz / div) >> 1) : + host->bus_hz, div); /* disable clock */ mci_writel(host, CLKENA, 0); @@ -1128,9 +1127,6 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* inform CIU */ mci_send_cmd(slot, sdmmc_cmd_bits, 0); - - /* keep the clock with reflecting clock dividor */ - slot->__clk_old = clock << div; } host->current_speed = clock; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 1e8d838..5961037 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -245,9 +245,6 @@ extern int dw_mci_resume(struct dw_mci *host); * @queue_node: List node for placing this node in the @queue list of * &struct dw_mci. * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @__clk_old: The last updated clock with reflecting clock divider. - * Keeping track of this helps us to avoid spamming the console - * with CONFIG_MMC_CLKGATE. * @flags: Random state bits associated with the slot. * @id: Number of this slot. * @sdio_id: Number of this slot in the SDIO interrupt registers. @@ -262,7 +259,6 @@ struct dw_mci_slot { struct list_head queue_node; unsigned int clock; - unsigned int __clk_old; unsigned long flags; #define DW_MMC_CARD_PRESENT 0 -- cgit v0.10.2 From e5a613537c4816830541046af12e8d37b075949c Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 20 Jun 2016 13:16:09 +0900 Subject: mmc: dw_mmc: exynos: fix the NULL pointer dereference error "host->cur_slot" should be assigned to start the request. So it can be the NULL pointer. This patch fixed this error. Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 7e3a324..da0ef17 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -157,7 +157,7 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel)) + if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->cur_slot) set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags); } -- cgit v0.10.2 From 16a34574c6ca12bb8fd73ae034acd5b536d3cdaa Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 21 Jun 2016 14:35:37 +0900 Subject: mmc: dw_mmc: remove the quirks flags Remove the quirks flag. (DW_MCI_QUIRK_BROKEN_DTO) For removing this, enabled the dto_timer by defaults. It doesn't see any I/O performance degression. In future, dwmmc controller should not use the quirks flag. Signed-off-by: Jaehoon Chung Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 358b0dc..d3cf1f1 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -285,9 +285,6 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host->sdio_id0 = 8; - /* It needs this quirk on all Rockchip SoCs */ - host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO; - if (of_device_is_compatible(host->dev->of_node, "rockchip,rk3288-dw-mshc")) host->bus_hz /= RK3288_CLKGEN_DIV; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 3e9fdc0..e7fb052 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1824,8 +1824,7 @@ static void dw_mci_tasklet_func(unsigned long priv) * If all data-related interrupts don't come * within the given time in reading data state. */ - if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && - (host->dir_status == DW_MCI_RECV_STATUS)) + if (host->dir_status == DW_MCI_RECV_STATUS) dw_mci_set_drto(host); break; } @@ -1867,8 +1866,7 @@ static void dw_mci_tasklet_func(unsigned long priv) * interrupt doesn't come within the given time. * in reading data state. */ - if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && - (host->dir_status == DW_MCI_RECV_STATUS)) + if (host->dir_status == DW_MCI_RECV_STATUS) dw_mci_set_drto(host); break; } @@ -2434,8 +2432,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending & SDMMC_INT_DATA_OVER) { - if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) - del_timer(&host->dto_timer); + del_timer(&host->dto_timer); mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) @@ -3026,11 +3023,8 @@ int dw_mci_probe(struct dw_mci *host) setup_timer(&host->cmd11_timer, dw_mci_cmd11_timer, (unsigned long)host); - host->quirks = host->pdata->quirks; - - if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) - setup_timer(&host->dto_timer, - dw_mci_dto_timer, (unsigned long)host); + setup_timer(&host->dto_timer, + dw_mci_dto_timer, (unsigned long)host); spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index f7ed271..83b0edfc 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -112,7 +112,6 @@ struct dw_mci_dma_slave { * @part_buf: Simple buffer for partial fifo reads/writes. * @push_data: Pointer to FIFO push function. * @pull_data: Pointer to FIFO pull function. - * @quirks: Set of quirks that apply to specific versions of the IP. * @vqmmc_enabled: Status of vqmmc, should be true or false. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. @@ -218,9 +217,6 @@ struct dw_mci { void (*push_data)(struct dw_mci *host, void *buf, int cnt); void (*pull_data)(struct dw_mci *host, void *buf, int cnt); - /* Workaround flags */ - u32 quirks; - bool vqmmc_enabled; unsigned long irq_flags; /* IRQ flags */ int irq; @@ -242,17 +238,12 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); }; -/* IP Quirks/flags. */ -/* Timer for broken data transfer over scheme */ -#define DW_MCI_QUIRK_BROKEN_DTO BIT(0) - struct dma_pdata; /* Board platform data */ struct dw_mci_board { u32 num_slots; - u32 quirks; /* Workaround / Quirk flags */ unsigned int bus_hz; /* Clock speed at the cclk_in pad */ u32 caps; /* Capabilities */ -- cgit v0.10.2 From 7e4bf1bc9543a1760cd93f0e23325e7b0365b30a Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 21 Jun 2016 14:35:38 +0900 Subject: mmc: dw_mmc: add the card write threshold for HS400 mode Since v2.80a, dwmmc controller introduced the card write threshold for HS400 mode. So CardThrCtl can be supported during write operation, not only read operation. (Note: Only use the write threshold when mode is HS400.) To use more compatible, removed "_rd_" from function name. Signed-off-by: Jaehoon Chung Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e7fb052..e857beb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -899,23 +899,35 @@ done: mci_writel(host, FIFOTH, fifoth_val); } -static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) +static void dw_mci_ctrl_thld(struct dw_mci *host, struct mmc_data *data) { unsigned int blksz = data->blksz; u32 blksz_depth, fifo_depth; u16 thld_size; - - WARN_ON(!(data->flags & MMC_DATA_READ)); + u8 enable; /* * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is * in the FIFO region, so we really shouldn't access it). */ - if (host->verid < DW_MMC_240A) + if (host->verid < DW_MMC_240A || + (host->verid < DW_MMC_280A && data->flags & MMC_DATA_WRITE)) return; + /* + * Card write Threshold is introduced since 2.80a + * It's used when HS400 mode is enabled. + */ + if (data->flags & MMC_DATA_WRITE && + !(host->timing != MMC_TIMING_MMC_HS400)) + return; + + if (data->flags & MMC_DATA_WRITE) + enable = SDMMC_CARD_WR_THR_EN; + else + enable = SDMMC_CARD_RD_THR_EN; + if (host->timing != MMC_TIMING_MMC_HS200 && - host->timing != MMC_TIMING_MMC_HS400 && host->timing != MMC_TIMING_UHS_SDR104) goto disable; @@ -931,11 +943,11 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) * Currently just choose blksz. */ thld_size = blksz; - mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1)); + mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(thld_size, enable)); return; disable: - mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0)); + mci_writel(host, CDTHRCTL, 0); } static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) @@ -1006,12 +1018,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->sg = NULL; host->data = data; - if (data->flags & MMC_DATA_READ) { + if (data->flags & MMC_DATA_READ) host->dir_status = DW_MCI_RECV_STATUS; - dw_mci_ctrl_rd_thld(host, data); - } else { + else host->dir_status = DW_MCI_SEND_STATUS; - } + + dw_mci_ctrl_thld(host, data); if (dw_mci_submit_data_dma(host, data)) { if (host->data->flags & MMC_DATA_READ) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 5961037..9e740bc 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -15,6 +15,7 @@ #define _DW_MMC_H_ #define DW_MMC_240A 0x240a +#define DW_MMC_280A 0x280a #define SDMMC_CTRL 0x000 #define SDMMC_PWREN 0x004 @@ -175,7 +176,10 @@ /* Version ID register define */ #define SDMMC_GET_VERID(x) ((x) & 0xFFFF) /* Card read threshold */ -#define SDMMC_SET_RD_THLD(v, x) (((v) & 0xFFF) << 16 | (x)) +#define SDMMC_SET_THLD(v, x) (((v) & 0xFFF) << 16 | (x)) +#define SDMMC_CARD_WR_THR_EN BIT(2) +#define SDMMC_CARD_RD_THR_EN BIT(0) +/* UHS-1 register defines */ #define SDMMC_UHS_18V BIT(0) /* All ctrl reset bits */ #define SDMMC_CTRL_ALL_RESET_FLAGS \ -- cgit v0.10.2 From faecf41106123ac62c06475ef3a61bc1dabfcad2 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 24 Jun 2016 15:39:52 +0800 Subject: mmc: dw_mmc: fix unmap sg twice when finding data err DATA_OVER(the same for RI/TI of IDMAC) interrupt may come up together with data error interrupts. If so, the interrupt routine set EVENT_DATA_ERR to the pending_events and schedule the tasklet but we may still fallback to the IDMAC interrupt case as the tasklet may come up a little late, namely right after the IDMAC interrupt checking. This will casue dw_mmc unmap sg twice. We can easily see it with CONFIG_DMA_API_DEBUG enabled. WARNING: CPU: 0 PID: 0 at lib/dma-debug.c:1096 check_unmap+0x7bc/0xb38 dwmmc_exynos 12200000.mmc: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x000000006d9d2200] [size=128 bytes] Modules linked in: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.7.0-rc4 #26 Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [] (show_stack) from [] (dump_stack+0x80/0x94) [] (dump_stack) from [] (__warn+0xf8/0x110) [] (__warn) from [] (warn_slowpath_fmt+0x48/0x50) [] (warn_slowpath_fmt) from [] (check_unmap+0x7bc/0xb38) [] (check_unmap) from [] (debug_dma_unmap_sg+0x118/0x148) [] (debug_dma_unmap_sg) from [] (dw_mci_dma_cleanup+0x7c/0xb8) [] (dw_mci_dma_cleanup) from [] (dw_mci_stop_dma+0x40/0x50) [] (dw_mci_stop_dma) from [] (dw_mci_tasklet_func+0x130/0x3b4) [] (dw_mci_tasklet_func) from [] (tasklet_action+0xb4/0x150) ..[snip].. ---[ end trace 256f83eed365daf0 ]--- Reported-by: Seung-Woo Kim Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e857beb..2dfdc58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2506,7 +2506,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete((void *)host); + if (!test_bit(EVENT_DATA_ERROR, &host->pending_events)) + host->dma_ops->complete((void *)host); } } else { pending = mci_readl(host, IDSTS); @@ -2514,7 +2515,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - host->dma_ops->complete((void *)host); + if (!test_bit(EVENT_DATA_ERROR, &host->pending_events)) + host->dma_ops->complete((void *)host); } } -- cgit v0.10.2 From 5462ff39c4538b33e70a3c3fffa980c3acb27642 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 30 Jun 2016 10:00:58 +0800 Subject: mmc: mediatek: do not tune data for HS400 mode for hs400 mode, should only tune DS delay, should not tune PAD_TUNE for data path. if eMMC will run at hs400 mode, do not tune data while call ops->execute_tuning(). Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 5642f71..4d7ce65 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -326,6 +326,7 @@ struct msdc_host { unsigned char timing; bool vqmmc_enabled; u32 hs400_ds_delay; + bool hs400_mode; /* current eMMC will run at hs400 mode */ struct msdc_save_para save_para; /* used when gate HCLK */ }; @@ -1402,9 +1403,11 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode) dev_err(host->dev, "Tune response fail!\n"); return ret; } - ret = msdc_tune_data(mmc, opcode); - if (ret == -EIO) - dev_err(host->dev, "Tune data fail!\n"); + if (host->hs400_mode == false) { + ret = msdc_tune_data(mmc, opcode); + if (ret == -EIO) + dev_err(host->dev, "Tune data fail!\n"); + } return ret; } @@ -1412,6 +1415,7 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode) static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) { struct msdc_host *host = mmc_priv(mmc); + host->hs400_mode = true; writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE); return 0; -- cgit v0.10.2 From 86beac370481882550e1f3904a5af44a5f9ea395 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 30 Jun 2016 10:00:59 +0800 Subject: mmc: mediatek: fix CRC error when calling mmc_select_hs400() the tune result of hs200 mode at 200Mhz is not suitable for 50Mhz, mmc_select_hs400() will set clock frequency to 50Mhz, use defalut tune setting for 50Mhz to avoid CRC error. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 4d7ce65..4b175a6 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -287,6 +287,11 @@ struct msdc_save_para { u32 emmc50_cfg0; }; +struct msdc_tune_para { + u32 iocon; + u32 pad_tune; +}; + struct msdc_delay_phase { u8 maxlen; u8 start; @@ -328,6 +333,8 @@ struct msdc_host { u32 hs400_ds_delay; bool hs400_mode; /* current eMMC will run at hs400 mode */ struct msdc_save_para save_para; /* used when gate HCLK */ + struct msdc_tune_para def_tune_para; /* default tune setting */ + struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */ }; static void sdr_set_bits(void __iomem *reg, u32 bs) @@ -583,6 +590,18 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); sdr_set_bits(host->base + MSDC_INTEN, flags); + /* + * mmc_select_hs400() will drop to 50Mhz and High speed mode, + * tune result of hs200/200Mhz is not suitable for 50Mhz + */ + if (host->sclk <= 52000000) { + writel(host->def_tune_para.iocon, host->base + MSDC_IOCON); + writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE); + } else { + writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON); + writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE); + } + dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing); } @@ -1159,6 +1178,8 @@ static void msdc_init_hw(struct msdc_host *host) /* Configure to default data timeout */ sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3); + host->def_tune_para.iocon = readl(host->base + MSDC_IOCON); + host->def_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE); dev_dbg(host->dev, "init hardware done!"); } @@ -1409,6 +1430,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode) dev_err(host->dev, "Tune data fail!\n"); } + host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON); + host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE); return ret; } -- cgit v0.10.2 From ddc713878f445b68626d10aa190139908426eb91 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 30 Jun 2016 10:01:00 +0800 Subject: mmc: mediatek: fix CMD21/CMD19 timeout issue we did not deal with the read data of CMD21/CMD19 if there is response CRC error of CMD21/CMD19, in this case, eMMC/SD may still in send-data state. therefore, all of next commands cannot get response as device is not in transfer state. for resolving this issue, still need deal with the data receive to make device back to transfer state. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 4b175a6..91277b9 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -801,7 +801,13 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, } if (!sbc_error && !(events & MSDC_INT_CMDRDY)) { - msdc_reset_hw(host); + if (cmd->opcode != MMC_SEND_TUNING_BLOCK && + cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) + /* + * should not clear fifo/interrupt as the tune data + * may have alreay come. + */ + msdc_reset_hw(host); if (events & MSDC_INT_RSPCRCERR) { cmd->error = -EILSEQ; host->error |= REQ_CMD_EIO; @@ -885,7 +891,11 @@ static void msdc_start_command(struct msdc_host *host, static void msdc_cmd_next(struct msdc_host *host, struct mmc_request *mrq, struct mmc_command *cmd) { - if (cmd->error || (mrq->sbc && mrq->sbc->error)) + if ((cmd->error && + !(cmd->error == -EILSEQ && + (cmd->opcode == MMC_SEND_TUNING_BLOCK || + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) || + (mrq->sbc && mrq->sbc->error)) msdc_request_done(host, mrq); else if (cmd == mrq->sbc) msdc_start_command(host, mrq, mrq->cmd); -- cgit v0.10.2 From ae9c657e62ac1e565f66e9faed7376a7ff5806e2 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Thu, 30 Jun 2016 10:01:01 +0800 Subject: mmc: mediatek: perfer to use rise edge latching in our host design, rise edge latching is more stable than fall edge latching. so that if rise edge has enough margin, no need scan fall edge. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 91277b9..84e9afc 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -1328,7 +1328,7 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode) { struct msdc_host *host = mmc_priv(mmc); u32 rise_delay = 0, fall_delay = 0; - struct msdc_delay_phase final_rise_delay, final_fall_delay; + struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,}; u8 final_delay, final_maxlen; int cmd_err; int i; @@ -1341,6 +1341,11 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode) if (!cmd_err) rise_delay |= (1 << i); } + final_rise_delay = get_best_delay(host, rise_delay); + /* if rising edge has enough margin, then do not scan falling edge */ + if (final_rise_delay.maxlen >= 10 || + (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4)) + goto skip_fall; sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL); for (i = 0; i < PAD_DELAY_MAX; i++) { @@ -1350,10 +1355,9 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode) if (!cmd_err) fall_delay |= (1 << i); } - - final_rise_delay = get_best_delay(host, rise_delay); final_fall_delay = get_best_delay(host, fall_delay); +skip_fall: final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen); if (final_maxlen == final_rise_delay.maxlen) { sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL); @@ -1374,7 +1378,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode) { struct msdc_host *host = mmc_priv(mmc); u32 rise_delay = 0, fall_delay = 0; - struct msdc_delay_phase final_rise_delay, final_fall_delay; + struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,}; u8 final_delay, final_maxlen; int i, ret; @@ -1387,6 +1391,11 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode) if (!ret) rise_delay |= (1 << i); } + final_rise_delay = get_best_delay(host, rise_delay); + /* if rising edge has enough margin, then do not scan falling edge */ + if (final_rise_delay.maxlen >= 10 || + (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4)) + goto skip_fall; sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL); sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL); @@ -1397,14 +1406,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode) if (!ret) fall_delay |= (1 << i); } - - final_rise_delay = get_best_delay(host, rise_delay); final_fall_delay = get_best_delay(host, fall_delay); +skip_fall: final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen); - /* Rising edge is more stable, prefer to use it */ - if (final_rise_delay.maxlen >= 10) - final_maxlen = final_rise_delay.maxlen; if (final_maxlen == final_rise_delay.maxlen) { sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL); sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL); -- cgit v0.10.2 From d3940f2747960b2e0b82f59c447190e1e960d76f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:14 +0300 Subject: mmc: sdhci: Do not call implementations of mmc host ops directly Drivers must be able to provide their own implementations for mmc host operations. Consequently, SDHCI should call those not the default implementations. Do that by calling indirectly through the mmc host ops function pointers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 15bdbcb..7009c86 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -46,9 +46,7 @@ static unsigned int debug_quirks2; static void sdhci_finish_data(struct sdhci_host *); static void sdhci_finish_command(struct sdhci_host *); -static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); -static int sdhci_get_cd(struct mmc_host *mmc); static void sdhci_dumpregs(struct sdhci_host *host) { @@ -193,7 +191,9 @@ EXPORT_SYMBOL_GPL(sdhci_reset); static void sdhci_do_reset(struct sdhci_host *host, u8 mask) { if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!sdhci_get_cd(host->mmc)) + struct mmc_host *mmc = host->mmc; + + if (!mmc->ops->get_cd(mmc)) return; } @@ -210,10 +210,10 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) } } -static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); - static void sdhci_init(struct sdhci_host *host, int soft) { + struct mmc_host *mmc = host->mmc; + if (soft) sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); else @@ -231,7 +231,7 @@ static void sdhci_init(struct sdhci_host *host, int soft) if (soft) { /* force clock reconfiguration */ host->clock = 0; - sdhci_set_ios(host->mmc, &host->mmc->ios); + mmc->ops->set_ios(mmc, &mmc->ios); } } @@ -2096,7 +2096,7 @@ static void sdhci_card_event(struct mmc_host *mmc) if (host->ops->card_event) host->ops->card_event(host); - present = sdhci_get_cd(host->mmc); + present = mmc->ops->get_cd(mmc); spin_lock_irqsave(&host->lock, flags); @@ -2582,8 +2582,10 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) spin_unlock_irqrestore(&host->lock, flags); if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { - sdhci_card_event(host->mmc); - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + struct mmc_host *mmc = host->mmc; + + mmc->ops->card_event(mmc); + mmc_detect_change(mmc, msecs_to_jiffies(200)); } if (isr & SDHCI_INT_CARD_INT) { @@ -2667,6 +2669,7 @@ EXPORT_SYMBOL_GPL(sdhci_suspend_host); int sdhci_resume_host(struct sdhci_host *host) { + struct mmc_host *mmc = host->mmc; int ret = 0; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { @@ -2680,7 +2683,7 @@ int sdhci_resume_host(struct sdhci_host *host) sdhci_init(host, 0); host->pwr = 0; host->clock = 0; - sdhci_set_ios(host->mmc, &host->mmc->ios); + mmc->ops->set_ios(mmc, &mmc->ios); } else { sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); mmiowb(); @@ -2729,6 +2732,7 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); int sdhci_runtime_resume_host(struct sdhci_host *host) { + struct mmc_host *mmc = host->mmc; unsigned long flags; int host_flags = host->flags; @@ -2742,8 +2746,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) /* Force clock and power re-program */ host->pwr = 0; host->clock = 0; - sdhci_start_signal_voltage_switch(host->mmc, &host->mmc->ios); - sdhci_set_ios(host->mmc, &host->mmc->ios); + mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); + mmc->ops->set_ios(mmc, &mmc->ios); if ((host_flags & SDHCI_PV_ENABLED) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { -- cgit v0.10.2 From 52f5336db2d6c32a807ca37cc1c45060a88a2506 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:15 +0300 Subject: mmc: sdhci: Split sdhci_add_host() Split sdhci-add_host() in order to further our objective to make sdhci into a library. The split divides code that sets up mmc and sdhci parameters, from code that actually activates things - such as tasklet initialization, requesting the irq, and adding (and starting) the host. This gives drivers an opportunity to change various settings before committing to start the host. Drivers can continue to call sdhci_add_host() but drivers that want to take advantage of the split instead call sdhci_setup_host() followed by __sdhci_add_host(). Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7009c86..22061fa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2833,7 +2833,7 @@ static int sdhci_set_dma_mask(struct sdhci_host *host) return ret; } -int sdhci_add_host(struct sdhci_host *host) +int sdhci_setup_host(struct sdhci_host *host) { struct mmc_host *mmc; u32 caps[2] = {0, 0}; @@ -3314,6 +3314,28 @@ int sdhci_add_host(struct sdhci_host *host) */ mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; + return 0; + +unreg: + if (!IS_ERR(mmc->supply.vqmmc)) + regulator_disable(mmc->supply.vqmmc); +undma: + if (host->align_buffer) + dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz + + host->adma_table_sz, host->align_buffer, + host->align_addr); + host->adma_table = NULL; + host->align_buffer = NULL; + + return ret; +} +EXPORT_SYMBOL_GPL(sdhci_setup_host); + +int __sdhci_add_host(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + int ret; + /* * Init tasklets. */ @@ -3370,10 +3392,10 @@ unirq: free_irq(host->irq, host); untasklet: tasklet_kill(&host->finish_tasklet); -unreg: + if (!IS_ERR(mmc->supply.vqmmc)) regulator_disable(mmc->supply.vqmmc); -undma: + if (host->align_buffer) dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz + host->adma_table_sz, host->align_buffer, @@ -3383,7 +3405,18 @@ undma: return ret; } +EXPORT_SYMBOL_GPL(__sdhci_add_host); + +int sdhci_add_host(struct sdhci_host *host) +{ + int ret; + + ret = sdhci_setup_host(host); + if (ret) + return ret; + return __sdhci_add_host(host); +} EXPORT_SYMBOL_GPL(sdhci_add_host); void sdhci_remove_host(struct sdhci_host *host, int dead) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 609f87c..b16aec2 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -645,6 +645,8 @@ static inline void *sdhci_priv(struct sdhci_host *host) } extern void sdhci_card_detect(struct sdhci_host *host); +extern int sdhci_setup_host(struct sdhci_host *host); +extern int __sdhci_add_host(struct sdhci_host *host); extern int sdhci_add_host(struct sdhci_host *host); extern void sdhci_remove_host(struct sdhci_host *host, int dead); extern void sdhci_send_command(struct sdhci_host *host, -- cgit v0.10.2 From 8cb851a4da64aa838c3cb4fa76ad130ace2b5a98 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:16 +0300 Subject: mmc: sdhci: Make signal voltage support explicit Signal voltage support is not a quirk, it is a capability. According to the SDHCI specification, support for 1.8V signaling is determined by the presence of one of the capability bits SDHCI_SUPPORT_SDR50, SDHCI_SUPPORT_SDR104, or SDHCI_SUPPORT_DDR50. This is complicated by also supporting eMMC which has 1.8V modes and 1.2V modes. It would be possible to use the transfer mode to determine signal voltage support, except for eMMC DDR52 mode which uses the same capability (MMC_CAP_1_8V_DDR) for 1.8V signaling and 3V signaling. In addition, the mmc core will fail over from one signaling voltage to the next (refer mmc_power_up()) which means SDHCI really needs to validate which voltages are actually supported. Introduce SDHCI flags for signal voltage support and set them based on the supported transfer modes. In general, drivers should prefer to set the supported transfer modes correctly rather than change the signal voltage capability, except in the case where 3V DDR52 is supported but 1.8V is not. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 22061fa..8ac5a9d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1733,6 +1733,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, switch (ios->signal_voltage) { case MMC_SIGNAL_VOLTAGE_330: + if (!(host->flags & SDHCI_SIGNALING_330)) + return -EINVAL; /* Set 1.8V Signal Enable in the Host Control2 register to 0 */ ctrl &= ~SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); @@ -1759,6 +1761,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return -EAGAIN; case MMC_SIGNAL_VOLTAGE_180: + if (!(host->flags & SDHCI_SIGNALING_180)) + return -EINVAL; if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_set_voltage(mmc->supply.vqmmc, 1700000, 1950000); @@ -1790,6 +1794,8 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return -EAGAIN; case MMC_SIGNAL_VOLTAGE_120: + if (!(host->flags & SDHCI_SIGNALING_120)) + return -EINVAL; if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000, 1300000); @@ -2798,6 +2804,8 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, host->mmc_host_ops = sdhci_ops; mmc->ops = &host->mmc_host_ops; + host->flags = SDHCI_SIGNALING_330; + return host; } @@ -3257,6 +3265,15 @@ int sdhci_setup_host(struct sdhci_host *host) goto unreg; } + if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) || + (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V))) + host->flags |= SDHCI_SIGNALING_180; + + if (mmc->caps2 & MMC_CAP2_HSX00_1_2V) + host->flags |= SDHCI_SIGNALING_120; + spin_lock_init(&host->lock); /* diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b16aec2..419911f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -447,6 +447,9 @@ struct sdhci_host { #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ #define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ #define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */ +#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */ +#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */ +#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */ unsigned int version; /* SDHCI spec. version */ -- cgit v0.10.2 From 28da35899e6075ad0376f977c23dcefb815e77db Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:17 +0300 Subject: mmc: sdhci: Tidy caps variables in sdhci_setup_host() In preparation for adding a function to read the capability registers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8ac5a9d..185dbf2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2844,7 +2844,6 @@ static int sdhci_set_dma_mask(struct sdhci_host *host) int sdhci_setup_host(struct sdhci_host *host) { struct mmc_host *mmc; - u32 caps[2] = {0, 0}; u32 max_current_caps; unsigned int ocr_avail; unsigned int override_timeout_clk; @@ -2874,17 +2873,15 @@ int sdhci_setup_host(struct sdhci_host *host) mmc_hostname(mmc), host->version); } - caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps : - sdhci_readl(host, SDHCI_CAPABILITIES); - - if (host->version >= SDHCI_SPEC_300) - caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? - host->caps1 : - sdhci_readl(host, SDHCI_CAPABILITIES_1); + if (!(host->quirks & SDHCI_QUIRK_MISSING_CAPS)) { + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (host->version >= SDHCI_SPEC_300) + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + } if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; - else if (!(caps[0] & SDHCI_CAN_DO_SDMA)) + else if (!(host->caps & SDHCI_CAN_DO_SDMA)) DBG("Controller doesn't have SDMA capability\n"); else host->flags |= SDHCI_USE_SDMA; @@ -2896,7 +2893,7 @@ int sdhci_setup_host(struct sdhci_host *host) } if ((host->version >= SDHCI_SPEC_200) && - (caps[0] & SDHCI_CAN_DO_ADMA2)) + (host->caps & SDHCI_CAN_DO_ADMA2)) host->flags |= SDHCI_USE_ADMA; if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) && @@ -2912,7 +2909,7 @@ int sdhci_setup_host(struct sdhci_host *host) * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to * implement. */ - if (caps[0] & SDHCI_CAN_64BIT) + if (host->caps & SDHCI_CAN_64BIT) host->flags |= SDHCI_USE_64_BIT_DMA; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { @@ -2988,10 +2985,10 @@ int sdhci_setup_host(struct sdhci_host *host) } if (host->version >= SDHCI_SPEC_300) - host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK) + host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; else - host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK) + host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; host->max_clk *= 1000000; @@ -3010,7 +3007,7 @@ int sdhci_setup_host(struct sdhci_host *host) * In case of Host Controller v3.00, find out whether clock * multiplier is supported. */ - host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >> + host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; /* @@ -3042,7 +3039,7 @@ int sdhci_setup_host(struct sdhci_host *host) mmc->f_max = max_clk; if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { - host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> + host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; if (host->timeout_clk == 0) { if (host->ops->get_timeout_clock) { @@ -3056,7 +3053,7 @@ int sdhci_setup_host(struct sdhci_host *host) } } - if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT) + if (host->caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; if (override_timeout_clk) @@ -3097,7 +3094,7 @@ int sdhci_setup_host(struct sdhci_host *host) if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23) mmc->caps &= ~MMC_CAP_CMD23; - if (caps[0] & SDHCI_CAN_DO_HISPD) + if (host->caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && @@ -3115,9 +3112,9 @@ int sdhci_setup_host(struct sdhci_host *host) ret = regulator_enable(mmc->supply.vqmmc); if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, 1950000)) - caps[1] &= ~(SDHCI_SUPPORT_SDR104 | - SDHCI_SUPPORT_SDR50 | - SDHCI_SUPPORT_DDR50); + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | + SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50); if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); @@ -3125,28 +3122,30 @@ int sdhci_setup_host(struct sdhci_host *host) } } - if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) - caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | - SDHCI_SUPPORT_DDR50); + if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) { + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50); + } /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */ - if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | - SDHCI_SUPPORT_DDR50)) + if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50)) mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; /* SDR104 supports also implies SDR50 support */ - if (caps[1] & SDHCI_SUPPORT_SDR104) { + if (host->caps1 & SDHCI_SUPPORT_SDR104) { mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50; /* SD3.0: SDR104 is supported so (for eMMC) the caps2 * field can be promoted to support HS200. */ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) mmc->caps2 |= MMC_CAP2_HS200; - } else if (caps[1] & SDHCI_SUPPORT_SDR50) + } else if (host->caps1 & SDHCI_SUPPORT_SDR50) { mmc->caps |= MMC_CAP_UHS_SDR50; + } if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 && - (caps[1] & SDHCI_SUPPORT_HS400)) + (host->caps1 & SDHCI_SUPPORT_HS400)) mmc->caps2 |= MMC_CAP2_HS400; if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) && @@ -3155,25 +3154,25 @@ int sdhci_setup_host(struct sdhci_host *host) 1300000))) mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V; - if ((caps[1] & SDHCI_SUPPORT_DDR50) && - !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50)) + if ((host->caps1 & SDHCI_SUPPORT_DDR50) && + !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50)) mmc->caps |= MMC_CAP_UHS_DDR50; /* Does the host need tuning for SDR50? */ - if (caps[1] & SDHCI_USE_SDR50_TUNING) + if (host->caps1 & SDHCI_USE_SDR50_TUNING) host->flags |= SDHCI_SDR50_NEEDS_TUNING; /* Driver Type(s) (A, C, D) supported by the host */ - if (caps[1] & SDHCI_DRIVER_TYPE_A) + if (host->caps1 & SDHCI_DRIVER_TYPE_A) mmc->caps |= MMC_CAP_DRIVER_TYPE_A; - if (caps[1] & SDHCI_DRIVER_TYPE_C) + if (host->caps1 & SDHCI_DRIVER_TYPE_C) mmc->caps |= MMC_CAP_DRIVER_TYPE_C; - if (caps[1] & SDHCI_DRIVER_TYPE_D) + if (host->caps1 & SDHCI_DRIVER_TYPE_D) mmc->caps |= MMC_CAP_DRIVER_TYPE_D; /* Initial value for re-tuning timer count */ - host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> - SDHCI_RETUNING_TIMER_COUNT_SHIFT; + host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >> + SDHCI_RETUNING_TIMER_COUNT_SHIFT; /* * In case Re-tuning Timer is not disabled, the actual value of @@ -3183,7 +3182,7 @@ int sdhci_setup_host(struct sdhci_host *host) host->tuning_count = 1 << (host->tuning_count - 1); /* Re-tuning mode supported by the Host Controller */ - host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >> + host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >> SDHCI_RETUNING_MODE_SHIFT; ocr_avail = 0; @@ -3212,7 +3211,7 @@ int sdhci_setup_host(struct sdhci_host *host) } } - if (caps[0] & SDHCI_CAN_VDD_330) { + if (host->caps & SDHCI_CAN_VDD_330) { ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; mmc->max_current_330 = ((max_current_caps & @@ -3220,7 +3219,7 @@ int sdhci_setup_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_330_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; } - if (caps[0] & SDHCI_CAN_VDD_300) { + if (host->caps & SDHCI_CAN_VDD_300) { ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; mmc->max_current_300 = ((max_current_caps & @@ -3228,7 +3227,7 @@ int sdhci_setup_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_300_SHIFT) * SDHCI_MAX_CURRENT_MULTIPLIER; } - if (caps[0] & SDHCI_CAN_VDD_180) { + if (host->caps & SDHCI_CAN_VDD_180) { ocr_avail |= MMC_VDD_165_195; mmc->max_current_180 = ((max_current_caps & @@ -3315,7 +3314,7 @@ int sdhci_setup_host(struct sdhci_host *host) if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) { mmc->max_blk_size = 2; } else { - mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >> + mmc->max_blk_size = (host->caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; if (mmc->max_blk_size >= 3) { pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n", diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 419911f..8696c93 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -490,8 +490,8 @@ struct sdhci_host { struct timer_list timer; /* Timer for timeouts */ - u32 caps; /* Alternative CAPABILITY_0 */ - u32 caps1; /* Alternative CAPABILITY_1 */ + u32 caps; /* CAPABILITY_0 */ + u32 caps1; /* CAPABILITY_1 */ unsigned int ocr_avail_sdio; /* OCR bit masks */ unsigned int ocr_avail_sd; -- cgit v0.10.2 From 6132a3bf0711a13bcfc34cafc0a745adf868dfac Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:18 +0300 Subject: mmc: sdhci: Add sdhci_read_caps() Add sdhci_read_caps() and __sdhci_read_caps() to make it easier for drivers to fix the version and capabilities registers. Pedantically, the SDHCI specification states that the capabilities registers are valid when the host controller resets the Software Reset For All bit. That requirement has always been satisfied by performing a reset at the start of initialization, and consequently that is now part of the new functions. Although the SDHCI_QUIRK_MISSING_CAPS quirk has not yet been removed, drivers that want to provide their own caps can now use these functions instead of that quirk. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 185dbf2..44cdb6d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2841,6 +2841,38 @@ static int sdhci_set_dma_mask(struct sdhci_host *host) return ret; } +void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1) +{ + u16 v; + + if (host->read_caps) + return; + + host->read_caps = true; + + if (debug_quirks) + host->quirks = debug_quirks; + + if (debug_quirks2) + host->quirks2 = debug_quirks2; + + sdhci_do_reset(host, SDHCI_RESET_ALL); + + v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION); + host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; + + if (host->quirks & SDHCI_QUIRK_MISSING_CAPS) + return; + + host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES); + + if (host->version < SDHCI_SPEC_300) + return; + + host->caps1 = caps1 ? *caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1); +} +EXPORT_SYMBOL_GPL(__sdhci_read_caps); + int sdhci_setup_host(struct sdhci_host *host) { struct mmc_host *mmc; @@ -2856,29 +2888,15 @@ int sdhci_setup_host(struct sdhci_host *host) mmc = host->mmc; - if (debug_quirks) - host->quirks = debug_quirks; - if (debug_quirks2) - host->quirks2 = debug_quirks2; + sdhci_read_caps(host); override_timeout_clk = host->timeout_clk; - sdhci_do_reset(host, SDHCI_RESET_ALL); - - host->version = sdhci_readw(host, SDHCI_HOST_VERSION); - host->version = (host->version & SDHCI_SPEC_VER_MASK) - >> SDHCI_SPEC_VER_SHIFT; if (host->version > SDHCI_SPEC_300) { pr_err("%s: Unknown controller version (%d). You may experience problems.\n", mmc_hostname(mmc), host->version); } - if (!(host->quirks & SDHCI_QUIRK_MISSING_CAPS)) { - host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); - if (host->version >= SDHCI_SPEC_300) - host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); - } - if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_SDMA; else if (!(host->caps & SDHCI_CAN_DO_SDMA)) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 8696c93..e332e4a 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -492,6 +492,7 @@ struct sdhci_host { u32 caps; /* CAPABILITY_0 */ u32 caps1; /* CAPABILITY_1 */ + bool read_caps; /* Capability flags have been read */ unsigned int ocr_avail_sdio; /* OCR bit masks */ unsigned int ocr_avail_sd; @@ -648,6 +649,8 @@ static inline void *sdhci_priv(struct sdhci_host *host) } extern void sdhci_card_detect(struct sdhci_host *host); +extern void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, + u32 *caps1); extern int sdhci_setup_host(struct sdhci_host *host); extern int __sdhci_add_host(struct sdhci_host *host); extern int sdhci_add_host(struct sdhci_host *host); @@ -655,6 +658,11 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead); extern void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd); +static inline void sdhci_read_caps(struct sdhci_host *host) +{ + __sdhci_read_caps(host, NULL, NULL, NULL); +} + static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) { return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); -- cgit v0.10.2 From 00884b61fb3968da088f7bf26eef9b90233b69df Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:19 +0300 Subject: mmc: sdhci-pci: Do not runtime suspend at the end of sdhci_pci_probe() At the successful conclusion of sdhci_pci_probe(), if runtime pm was allowed, the device would be runtime suspended. That wastes a lot of time during initialization. Instead leave the device active until the mmc core scans for a card. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index a4dbf74..e8294a2 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1760,11 +1760,12 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) static void sdhci_pci_runtime_pm_allow(struct device *dev) { - pm_runtime_put_noidle(dev); - pm_runtime_allow(dev); + pm_suspend_ignore_children(dev, 1); pm_runtime_set_autosuspend_delay(dev, 50); pm_runtime_use_autosuspend(dev); - pm_suspend_ignore_children(dev, 1); + pm_runtime_allow(dev); + /* Stay active until mmc core scans for a card */ + pm_runtime_put_noidle(dev); } static void sdhci_pci_runtime_pm_forbid(struct device *dev) -- cgit v0.10.2 From 6bde868181dd98aef14ac3ba02a3216b40466867 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:20 +0300 Subject: mmc: sdhci: Move busy signal handling into sdhci_finish_cmd() In order to support commands during data transfer, command and data handling needs to be untangled. That means sdhci_finish_cmd() must not be called from the data IRQ handler. It is being called because of busy signal handling, which is treating the command as not finished until the busy signal is released. Instead, move busy signal handling from sdhci_cmd_irq() into sdhci_finish_cmd(). Then the data IRQ handler does not need to call sdhci_finish_cmd() and can instead finish the request. What this means in practice for a command with busy signaling, is that the command response is read from the host controller when the command complete interrupt is received, thus freeing up the command circuit for other commands. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 44cdb6d..9ca4253 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -45,7 +45,6 @@ static unsigned int debug_quirks2; static void sdhci_finish_data(struct sdhci_host *); -static void sdhci_finish_command(struct sdhci_host *); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static void sdhci_dumpregs(struct sdhci_host *host) @@ -1079,6 +1078,28 @@ static void sdhci_finish_command(struct sdhci_host *host) } } + /* + * The host can send and interrupt when the busy state has + * ended, allowing us to wait without wasting CPU cycles. + * The busy signal uses DAT0 so this is similar to waiting + * for data to complete. + * + * Note: The 1.0 specification is a bit ambiguous about this + * feature so there might be some problems with older + * controllers. + */ + if (host->cmd->flags & MMC_RSP_BUSY) { + if (host->cmd->data) { + DBG("Cannot wait for busy signal when also doing a data transfer"); + } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) && + !host->busy_handle) { + /* Mark that command complete before busy is ended */ + host->busy_handle = 1; + host->cmd = NULL; + return; + } + } + /* Finished CMD23, now send actual command. */ if (host->cmd == host->mrq->sbc) { host->cmd = NULL; @@ -2295,33 +2316,10 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) return; } - /* - * The host can send and interrupt when the busy state has - * ended, allowing us to wait without wasting CPU cycles. - * Unfortunately this is overloaded on the "data complete" - * interrupt, so we need to take some care when handling - * it. - * - * Note: The 1.0 specification is a bit ambiguous about this - * feature so there might be some problems with older - * controllers. - */ - if (host->cmd->flags & MMC_RSP_BUSY) { - if (host->cmd->data) - DBG("Cannot wait for busy signal when also doing a data transfer"); - else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) - && !host->busy_handle) { - /* Mark that command complete before busy is ended */ - host->busy_handle = 1; - return; - } - - /* The controller does not support the end-of-busy IRQ, - * fall through and take the SDHCI_INT_RESPONSE */ - } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) && - host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) { + if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) && + !(host->cmd->flags & MMC_RSP_BUSY) && !host->data && + host->cmd->opcode == MMC_STOP_TRANSMISSION) *mask &= ~SDHCI_INT_DATA_END; - } if (intmask & SDHCI_INT_RESPONSE) sdhci_finish_command(host); @@ -2395,7 +2393,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * sure we do things in the proper order. */ if (host->busy_handle) - sdhci_finish_command(host); + tasklet_schedule(&host->finish_tasklet); else host->busy_handle = 1; return; -- cgit v0.10.2 From 64ed8dd46ba69a7f03f9019758ef35604f0cdb04 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:21 +0300 Subject: mmc: sdhci: Get rid of redundant BUG_ONs BUG is never the right thing for SDHCI to do. Get rid of BUG_ON in cases it will oops anyway if the pointer is NULL, or if the condition is logically impossible. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9ca4253..f4d6c04 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -428,8 +428,6 @@ static void sdhci_transfer_pio(struct sdhci_host *host) { u32 mask; - BUG_ON(!host->data); - if (host->blocks == 0) return; @@ -929,8 +927,6 @@ static void sdhci_finish_data(struct sdhci_host *host) { struct mmc_data *data; - BUG_ON(!host->data); - data = host->data; host->data = NULL; @@ -1060,8 +1056,6 @@ static void sdhci_finish_command(struct sdhci_host *host) { int i; - BUG_ON(host->cmd == NULL); - if (host->cmd->flags & MMC_RSP_PRESENT) { if (host->cmd->flags & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ @@ -2279,8 +2273,6 @@ static void sdhci_timeout_timer(unsigned long data) static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) { - BUG_ON(intmask == 0); - if (!host->cmd) { pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); @@ -2361,7 +2353,6 @@ static void sdhci_adma_show_error(struct sdhci_host *host) { } static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { u32 command; - BUG_ON(intmask == 0); /* CMD19 generates _only_ Buffer Read Ready interrupt */ if (intmask & SDHCI_INT_DATA_AVAIL) { -- cgit v0.10.2 From e0a5640a9996bd9afef81dcdc62a82e59321a96f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:22 +0300 Subject: mmc: sdhci: Simplify sdhci_finish_command() by clearing host->cmd at the start sdhci_finish_command() is going to set host->cmd to NULL. Simplify the code by using a local variable to hold host->cmd and set host->cmd to NULL at the start. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f4d6c04..a00cdd7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1054,21 +1054,24 @@ EXPORT_SYMBOL_GPL(sdhci_send_command); static void sdhci_finish_command(struct sdhci_host *host) { + struct mmc_command *cmd = host->cmd; int i; - if (host->cmd->flags & MMC_RSP_PRESENT) { - if (host->cmd->flags & MMC_RSP_136) { + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ for (i = 0;i < 4;i++) { - host->cmd->resp[i] = sdhci_readl(host, + cmd->resp[i] = sdhci_readl(host, SDHCI_RESPONSE + (3-i)*4) << 8; if (i != 3) - host->cmd->resp[i] |= + cmd->resp[i] |= sdhci_readb(host, SDHCI_RESPONSE + (3-i)*4-1); } } else { - host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE); + cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE); } } @@ -1082,21 +1085,19 @@ static void sdhci_finish_command(struct sdhci_host *host) * feature so there might be some problems with older * controllers. */ - if (host->cmd->flags & MMC_RSP_BUSY) { - if (host->cmd->data) { + if (cmd->flags & MMC_RSP_BUSY) { + if (cmd->data) { DBG("Cannot wait for busy signal when also doing a data transfer"); } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) && !host->busy_handle) { /* Mark that command complete before busy is ended */ host->busy_handle = 1; - host->cmd = NULL; return; } } /* Finished CMD23, now send actual command. */ - if (host->cmd == host->mrq->sbc) { - host->cmd = NULL; + if (cmd == host->mrq->sbc) { sdhci_send_command(host, host->mrq->cmd); } else { @@ -1104,10 +1105,8 @@ static void sdhci_finish_command(struct sdhci_host *host) if (host->data && host->data_early) sdhci_finish_data(host); - if (!host->cmd->data) + if (!cmd->data) tasklet_schedule(&host->finish_tasklet); - - host->cmd = NULL; } } -- cgit v0.10.2 From 7c89a3d9082c316f898ec2c3c6c1689e0cad6117 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:23 +0300 Subject: mmc: sdhci: Record what command is using the data lines In order to support commands during data transfer, there must be a distinction between the command that is using the command line (and for which a command interrupt is expected) and the command that is using the data lines (for which a data interrupt is expected). There is host->cmd for the command line, but there is only host->data for the data lines, which is a different structure, does not represent the command in use, and is anyway NULL in the case of commands that use the data lines for busy signalling instead of data transfer. Introduce host->data_cmd to record what command is using the data lines, and use that instead of host->cmd when referring to the data command. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a00cdd7..c9456bd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -929,6 +929,7 @@ static void sdhci_finish_data(struct sdhci_host *host) data = host->data; host->data = NULL; + host->data_cmd = NULL; if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) == (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) @@ -1014,6 +1015,10 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) host->cmd = cmd; host->busy_handle = 0; + if (cmd->data || cmd->flags & MMC_RSP_BUSY) { + WARN_ON(host->data_cmd); + host->data_cmd = cmd; + } sdhci_prepare_data(host, cmd); @@ -2224,6 +2229,7 @@ static void sdhci_tasklet_finish(unsigned long param) host->mrq = NULL; host->cmd = NULL; host->data = NULL; + host->data_cmd = NULL; sdhci_led_deactivate(host); @@ -2365,14 +2371,19 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) } if (!host->data) { + struct mmc_command *data_cmd = host->data_cmd; + + if (data_cmd) + host->data_cmd = NULL; + /* * The "data complete" interrupt is also used to * indicate that a busy state has ended. See comment * above in sdhci_cmd_irq(). */ - if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { + if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) { if (intmask & SDHCI_INT_DATA_TIMEOUT) { - host->cmd->error = -ETIMEDOUT; + data_cmd->error = -ETIMEDOUT; tasklet_schedule(&host->finish_tasklet); return; } @@ -2447,7 +2458,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) } if (intmask & SDHCI_INT_DATA_END) { - if (host->cmd) { + if (host->cmd == host->data_cmd) { /* * Data managed to finish before the * command completed. Make sure we do diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index e332e4a..6e97170 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -466,6 +466,7 @@ struct sdhci_host { struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ + struct mmc_command *data_cmd; /* Current data command */ struct mmc_data *data; /* Current data request */ unsigned int data_early:1; /* Data finished before cmd */ unsigned int busy_handle:1; /* Handling the order of Busy-end */ -- cgit v0.10.2 From ea96802384cd062361f758f711c22010238d47f3 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:24 +0300 Subject: mmc: sdhci: Get rid of host->busy_handle Now that there is host->data_cmd to record the command for which a data interrupt is expected, it is possible to determine whether a command with busy signaling has completed without an extra flag. So host->busy_handle is not needed. Remove it. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c9456bd..1f2c4a7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1014,7 +1014,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mod_timer(&host->timer, timeout); host->cmd = cmd; - host->busy_handle = 0; if (cmd->data || cmd->flags & MMC_RSP_BUSY) { WARN_ON(host->data_cmd); host->data_cmd = cmd; @@ -1094,9 +1093,8 @@ static void sdhci_finish_command(struct sdhci_host *host) if (cmd->data) { DBG("Cannot wait for busy signal when also doing a data transfer"); } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) && - !host->busy_handle) { - /* Mark that command complete before busy is ended */ - host->busy_handle = 1; + cmd == host->data_cmd) { + /* Command complete before busy is ended */ return; } } @@ -2393,10 +2391,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * before the command completed, so make * sure we do things in the proper order. */ - if (host->busy_handle) - tasklet_schedule(&host->finish_tasklet); - else - host->busy_handle = 1; + if (host->cmd == data_cmd) + return; + + tasklet_schedule(&host->finish_tasklet); return; } } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 6e97170..13e0bd6 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -469,7 +469,6 @@ struct sdhci_host { struct mmc_command *data_cmd; /* Current data command */ struct mmc_data *data; /* Current data request */ unsigned int data_early:1; /* Data finished before cmd */ - unsigned int busy_handle:1; /* Handling the order of Busy-end */ struct sg_mapping_iter sg_miter; /* SG state for PIO */ unsigned int blocks; /* remaining PIO blocks */ -- cgit v0.10.2 From a4c73aba0463800f5e1c33c94c2a7e553e26a4eb Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:25 +0300 Subject: mmc: sdhci: Reduce the use of host->mrq In order to support commands during data transfer, there will have to be up to two active requests (mrqs) at a time, instead of just one. That means host->mrq will not be able to be used. In several places, host->mrq is used when instead the mrq can be determined from the cmd or data pointers. Reduce the use of host->mrq by doing that. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1f2c4a7..3835b20 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -906,12 +906,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, * If we are sending CMD23, CMD12 never gets sent * on successful completion (so no Auto-CMD12). */ - if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) && + if (!cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) && (cmd->opcode != SD_IO_RW_EXTENDED)) mode |= SDHCI_TRNS_AUTO_CMD12; - else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { + else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { mode |= SDHCI_TRNS_AUTO_CMD23; - sdhci_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2); + sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2); } } @@ -954,7 +954,7 @@ static void sdhci_finish_data(struct sdhci_host *host) */ if (data->stop && (data->error || - !host->mrq->sbc)) { + !data->mrq->sbc)) { /* * The controller needs a reset of internal state machines @@ -990,7 +990,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ - if (host->mrq->data && (cmd == host->mrq->data->stop)) + if (cmd->mrq->data && (cmd == cmd->mrq->data->stop)) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { @@ -1100,8 +1100,8 @@ static void sdhci_finish_command(struct sdhci_host *host) } /* Finished CMD23, now send actual command. */ - if (cmd == host->mrq->sbc) { - sdhci_send_command(host, host->mrq->cmd); + if (cmd == cmd->mrq->sbc) { + sdhci_send_command(host, cmd->mrq->cmd); } else { /* Processed actual command. */ @@ -1408,7 +1408,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; if (!present || host->flags & SDHCI_DEVICE_DEAD) { - host->mrq->cmd->error = -ENOMEDIUM; + mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } else { if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) -- cgit v0.10.2 From 43dea098bbab82ab15ac9f26aee34c8114b2e5a2 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:26 +0300 Subject: mmc: sdhci: Move host->data warning In order to support commands during data transfer, it will be possible that host->data is not NULL when preparing a new request. Move a warning that assumes otherwise. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3835b20..ee4363b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -744,14 +744,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) u8 ctrl; struct mmc_data *data = cmd->data; - WARN_ON(host->data); - if (data || (cmd->flags & MMC_RSP_BUSY)) sdhci_set_timeout(host, cmd); if (!data) return; + WARN_ON(host->data); + /* Sanity checks */ BUG_ON(data->blksz * data->blocks > 524288); BUG_ON(data->blksz > host->mmc->max_blk_size); -- cgit v0.10.2 From a6d3bdd527a9a20b41594f5f69bb32109d8501a7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:27 +0300 Subject: mmc: sdhci: Factor out sdhci_finish_mrq() In order to support commands during data transfer, there will have to be up to two active requests (mrqs) at a time, instead of just one. That means the driver must identify which one to finish. Prepare for that by factoring out sdhci_finish_mrq(). Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ee4363b..992d497 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -923,6 +923,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } +static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) +{ + tasklet_schedule(&host->finish_tasklet); +} + static void sdhci_finish_data(struct sdhci_host *host) { struct mmc_data *data; @@ -966,8 +971,9 @@ static void sdhci_finish_data(struct sdhci_host *host) } sdhci_send_command(host, data->stop); - } else - tasklet_schedule(&host->finish_tasklet); + } else { + sdhci_finish_mrq(host, data->mrq); + } } void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) @@ -999,7 +1005,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mmc_hostname(host->mmc)); sdhci_dumpregs(host); cmd->error = -EIO; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, cmd->mrq); return; } timeout--; @@ -1029,7 +1035,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) pr_err("%s: Unsupported response type!\n", mmc_hostname(host->mmc)); cmd->error = -EINVAL; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, cmd->mrq); return; } @@ -1109,7 +1115,7 @@ static void sdhci_finish_command(struct sdhci_host *host) sdhci_finish_data(host); if (!cmd->data) - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, cmd->mrq); } } @@ -1409,7 +1415,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) if (!present || host->flags & SDHCI_DEVICE_DEAD) { mrq->cmd->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, mrq); } else { if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) sdhci_send_command(host, mrq->sbc); @@ -2134,7 +2140,7 @@ static void sdhci_card_event(struct mmc_host *mmc) sdhci_do_reset(host, SDHCI_RESET_DATA); host->mrq->cmd->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, host->mrq); } spin_unlock_irqrestore(&host->lock, flags); @@ -2260,7 +2266,7 @@ static void sdhci_timeout_timer(unsigned long data) else host->mrq->cmd->error = -ETIMEDOUT; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, host->mrq); } } @@ -2307,7 +2313,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) return; } - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, host->cmd->mrq); return; } @@ -2382,7 +2388,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) { if (intmask & SDHCI_INT_DATA_TIMEOUT) { data_cmd->error = -ETIMEDOUT; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, data_cmd->mrq); return; } if (intmask & SDHCI_INT_DATA_END) { @@ -2394,7 +2400,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (host->cmd == data_cmd) return; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, data_cmd->mrq); return; } } @@ -3465,7 +3471,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) " transfer!\n", mmc_hostname(mmc)); host->mrq->cmd->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + sdhci_finish_mrq(host, host->mrq); } spin_unlock_irqrestore(&host->lock, flags); -- cgit v0.10.2 From 0cc563ce5436dca3119af86d0396412efec13a0f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:28 +0300 Subject: mmc: sdhci: Factor out sdhci_needs_reset() Factor out sdhci_needs_reset() so it can be reused. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 992d497..b0a3200 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -923,6 +923,16 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } +static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) +{ + return (!(host->flags & SDHCI_DEVICE_DEAD) && + ((mrq->cmd && mrq->cmd->error) || + (mrq->sbc && mrq->sbc->error) || + (mrq->data && ((mrq->data->error && !mrq->data->stop) || + (mrq->data->stop && mrq->data->stop->error))) || + (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))); +} + static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) { tasklet_schedule(&host->finish_tasklet); @@ -2212,13 +2222,7 @@ static void sdhci_tasklet_finish(unsigned long param) * The controller needs a reset of internal state machines * upon error conditions. */ - if (!(host->flags & SDHCI_DEVICE_DEAD) && - ((mrq->cmd && mrq->cmd->error) || - (mrq->sbc && mrq->sbc->error) || - (mrq->data && ((mrq->data->error && !mrq->data->stop) || - (mrq->data->stop && mrq->data->stop->error))) || - (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { - + if (sdhci_needs_reset(host, mrq)) { /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ -- cgit v0.10.2 From ed1563de0bc90e09d9707a85cf8d0e958c6f6261 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:29 +0300 Subject: mmc: sdhci: Track whether a reset is pending SDHCI recovers from errors by resetting the cmd and data circuits. Until that is done, there very well might be more interrupts, so ignore them in that case. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b0a3200..320c294 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -935,6 +935,9 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) { + if (sdhci_needs_reset(host, mrq)) + host->pending_reset = true; + tasklet_schedule(&host->finish_tasklet); } @@ -2232,6 +2235,8 @@ static void sdhci_tasklet_finish(unsigned long param) controllers do not like that. */ sdhci_do_reset(host, SDHCI_RESET_CMD); sdhci_do_reset(host, SDHCI_RESET_DATA); + + host->pending_reset = false; } host->mrq = NULL; @@ -2287,6 +2292,13 @@ static void sdhci_timeout_timer(unsigned long data) static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) { if (!host->cmd) { + /* + * SDHCI recovers from errors by resetting the cmd and data + * circuits. Until that is done, there very well might be more + * interrupts, so ignore them in that case. + */ + if (host->pending_reset) + return; pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); sdhci_dumpregs(host); @@ -2409,6 +2421,14 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) } } + /* + * SDHCI recovers from errors by resetting the cmd and data + * circuits. Until that is done, there very well might be more + * interrupts, so ignore them in that case. + */ + if (host->pending_reset) + return; + pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); sdhci_dumpregs(host); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 13e0bd6..7301c90 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -463,6 +463,7 @@ struct sdhci_host { bool runtime_suspended; /* Host is runtime suspended */ bool bus_on; /* Bus power prevents runtime suspend */ bool preset_enabled; /* Preset is enabled */ + bool pending_reset; /* Cmd/data reset is pending */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ -- cgit v0.10.2 From 5a8a3fef26cea4ab1fd023a20bb5b8f0bd87ce77 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:30 +0300 Subject: mmc: sdhci: Clear pointers when a request finishes Several pointers are used to identify when interrupts are expected. Namely, host->cmd, host->data_cmd and host->data. Ensure those are cleared when a request finishes. That tidies the case when a request is errored out before normal processing has completed, ensuring any interrupts that occur subsequently are not acted upon. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 320c294..dce31b7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -935,6 +935,15 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) { + if (host->cmd && host->cmd->mrq == mrq) + host->cmd = NULL; + + if (host->data_cmd && host->data_cmd->mrq == mrq) + host->data_cmd = NULL; + + if (host->data && host->data->mrq == mrq) + host->data = NULL; + if (sdhci_needs_reset(host, mrq)) host->pending_reset = true; @@ -2240,9 +2249,6 @@ static void sdhci_tasklet_finish(unsigned long param) } host->mrq = NULL; - host->cmd = NULL; - host->data = NULL; - host->data_cmd = NULL; sdhci_led_deactivate(host); -- cgit v0.10.2 From 5d0d11c51a599e6c074fdb2d55f6eba307936591 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:31 +0300 Subject: mmc: sdhci: Ensure all requests get errored out In order to support commands during data transfer, there will have to be up to two active requests (mrqs) at a time, instead of just one. That means ensuring that all requests get errored out in the cases of card or driver removal. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index dce31b7..6e3b4d0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2137,6 +2137,24 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED); } +static inline bool sdhci_has_requests(struct sdhci_host *host) +{ + return host->cmd || host->data_cmd; +} + +static void sdhci_error_out_mrqs(struct sdhci_host *host, int err) +{ + if (host->data_cmd) { + host->data_cmd->error = err; + sdhci_finish_mrq(host, host->data_cmd->mrq); + } + + if (host->cmd) { + host->cmd->error = err; + sdhci_finish_mrq(host, host->cmd->mrq); + } +} + static void sdhci_card_event(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); @@ -2151,8 +2169,8 @@ static void sdhci_card_event(struct mmc_host *mmc) spin_lock_irqsave(&host->lock, flags); - /* Check host->mrq first in case we are runtime suspended */ - if (host->mrq && !present) { + /* Check sdhci_has_requests() first in case we are runtime suspended */ + if (sdhci_has_requests(host) && !present) { pr_err("%s: Card removed during transfer!\n", mmc_hostname(host->mmc)); pr_err("%s: Resetting controller.\n", @@ -2161,8 +2179,7 @@ static void sdhci_card_event(struct mmc_host *mmc) sdhci_do_reset(host, SDHCI_RESET_CMD); sdhci_do_reset(host, SDHCI_RESET_DATA); - host->mrq->cmd->error = -ENOMEDIUM; - sdhci_finish_mrq(host, host->mrq); + sdhci_error_out_mrqs(host, -ENOMEDIUM); } spin_unlock_irqrestore(&host->lock, flags); @@ -3496,12 +3513,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) host->flags |= SDHCI_DEVICE_DEAD; - if (host->mrq) { + if (sdhci_has_requests(host)) { pr_err("%s: Controller removed during " " transfer!\n", mmc_hostname(mmc)); - - host->mrq->cmd->error = -ENOMEDIUM; - sdhci_finish_mrq(host, host->mrq); + sdhci_error_out_mrqs(host, -ENOMEDIUM); } spin_unlock_irqrestore(&host->lock, flags); -- cgit v0.10.2 From 56a590dcdc973e05230e9af3bcd4dfefd40d4127 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:32 +0300 Subject: mmc: sdhci: Factor out sdhci_data_line_cmd() Factor out sdhci_data_line_cmd() to improve readability and because it is used in multiple places. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6e3b4d0..b7b68b3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -109,6 +109,11 @@ static void sdhci_dumpregs(struct sdhci_host *host) * * \*****************************************************************************/ +static inline bool sdhci_data_line_cmd(struct mmc_command *cmd) +{ + return cmd->data || cmd->flags & MMC_RSP_BUSY; +} + static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) { u32 present; @@ -744,7 +749,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) u8 ctrl; struct mmc_data *data = cmd->data; - if (data || (cmd->flags & MMC_RSP_BUSY)) + if (sdhci_data_line_cmd(cmd)) sdhci_set_timeout(host, cmd); if (!data) @@ -1013,7 +1018,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) timeout = 10; mask = SDHCI_CMD_INHIBIT; - if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) + if (sdhci_data_line_cmd(cmd)) mask |= SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even @@ -1042,7 +1047,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mod_timer(&host->timer, timeout); host->cmd = cmd; - if (cmd->data || cmd->flags & MMC_RSP_BUSY) { + if (sdhci_data_line_cmd(cmd)) { WARN_ON(host->data_cmd); host->data_cmd = cmd; } -- cgit v0.10.2 From d7422fb489eee5587d3eecdd1151f90bf78f93a4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:33 +0300 Subject: mmc: sdhci: Separate timer timeout for command and data requests In order to support commands during data transfer, there will have to be up to two active requests (mrqs) at a time, instead of just one. Provide two timers instead of just one. One of the timers is for requests that do not use the data lines, and the other one is for requests that do. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b7b68b3..9580f76 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1003,6 +1003,23 @@ static void sdhci_finish_data(struct sdhci_host *host) } } +static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq, + unsigned long timeout) +{ + if (sdhci_data_line_cmd(mrq->cmd)) + mod_timer(&host->data_timer, timeout); + else + mod_timer(&host->timer, timeout); +} + +static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq) +{ + if (sdhci_data_line_cmd(mrq->cmd)) + del_timer(&host->data_timer); + else + del_timer(&host->timer); +} + void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; @@ -1044,7 +1061,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; else timeout += 10 * HZ; - mod_timer(&host->timer, timeout); + sdhci_mod_timer(host, cmd->mrq, timeout); host->cmd = cmd; if (sdhci_data_line_cmd(cmd)) { @@ -2232,10 +2249,10 @@ static void sdhci_tasklet_finish(unsigned long param) return; } - del_timer(&host->timer); - mrq = host->mrq; + sdhci_del_timer(host, mrq); + /* * Always unmap the data buffers if they were mapped by * sdhci_prepare_data() whenever we finish with a request. @@ -2289,7 +2306,30 @@ static void sdhci_timeout_timer(unsigned long data) spin_lock_irqsave(&host->lock, flags); - if (host->mrq) { + if (host->cmd && !sdhci_data_line_cmd(host->cmd)) { + pr_err("%s: Timeout waiting for hardware cmd interrupt.\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + + host->cmd->error = -ETIMEDOUT; + sdhci_finish_mrq(host, host->cmd->mrq); + } + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void sdhci_timeout_data_timer(unsigned long data) +{ + struct sdhci_host *host; + unsigned long flags; + + host = (struct sdhci_host *)data; + + spin_lock_irqsave(&host->lock, flags); + + if (host->data || host->data_cmd || + (host->cmd && sdhci_data_line_cmd(host->cmd))) { pr_err("%s: Timeout waiting for hardware interrupt.\n", mmc_hostname(host->mmc)); sdhci_dumpregs(host); @@ -2297,13 +2337,12 @@ static void sdhci_timeout_timer(unsigned long data) if (host->data) { host->data->error = -ETIMEDOUT; sdhci_finish_data(host); + } else if (host->data_cmd) { + host->data_cmd->error = -ETIMEDOUT; + sdhci_finish_mrq(host, host->data_cmd->mrq); } else { - if (host->cmd) - host->cmd->error = -ETIMEDOUT; - else - host->mrq->cmd->error = -ETIMEDOUT; - - sdhci_finish_mrq(host, host->mrq); + host->cmd->error = -ETIMEDOUT; + sdhci_finish_mrq(host, host->cmd->mrq); } } @@ -3432,6 +3471,8 @@ int __sdhci_add_host(struct sdhci_host *host) sdhci_tasklet_finish, (unsigned long)host); setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); + setup_timer(&host->data_timer, sdhci_timeout_data_timer, + (unsigned long)host); init_waitqueue_head(&host->buf_ready_int); @@ -3541,6 +3582,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) free_irq(host->irq, host); del_timer_sync(&host->timer); + del_timer_sync(&host->data_timer); tasklet_kill(&host->finish_tasklet); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 7301c90..a1de422 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -490,6 +490,7 @@ struct sdhci_host { struct tasklet_struct finish_tasklet; /* Tasklet structures */ struct timer_list timer; /* Timer for timeouts */ + struct timer_list data_timer; /* Timer for data timeouts */ u32 caps; /* CAPABILITY_0 */ u32 caps1; /* CAPABILITY_1 */ -- cgit v0.10.2 From 4e9f8fe5f25f0ee1361618e5f494cf5d14ee4936 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:34 +0300 Subject: mmc: sdhci: Allow for finishing multiple requests In order to support commands during data transfer, there will have to be up to two active requests (mrqs) at a time, instead of just one. That means recording which request is finished. Doing that obsoletes host->mrq which is therefore removed. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9580f76..e37d0e9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -938,6 +938,29 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))); } +static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) +{ + int i; + + for (i = 0; i < SDHCI_MAX_MRQS; i++) { + if (host->mrqs_done[i] == mrq) { + WARN_ON(1); + return; + } + } + + for (i = 0; i < SDHCI_MAX_MRQS; i++) { + if (!host->mrqs_done[i]) { + host->mrqs_done[i] = mrq; + break; + } + } + + WARN_ON(i >= SDHCI_MAX_MRQS); + + tasklet_schedule(&host->finish_tasklet); +} + static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) { if (host->cmd && host->cmd->mrq == mrq) @@ -952,7 +975,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) if (sdhci_needs_reset(host, mrq)) host->pending_reset = true; - tasklet_schedule(&host->finish_tasklet); + __sdhci_finish_mrq(host, mrq); } static void sdhci_finish_data(struct sdhci_host *host) @@ -1440,8 +1463,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_lock_irqsave(&host->lock, flags); - WARN_ON(host->mrq != NULL); - sdhci_led_activate(host); /* @@ -1455,8 +1476,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) } } - host->mrq = mrq; - if (!present || host->flags & SDHCI_DEVICE_DEAD) { mrq->cmd->error = -ENOMEDIUM; sdhci_finish_mrq(host, mrq); @@ -1993,13 +2012,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.retries = 0; cmd.data = NULL; + cmd.mrq = &mrq; cmd.error = 0; if (tuning_loop_counter-- == 0) break; mrq.cmd = &cmd; - host->mrq = &mrq; /* * In response to CMD19, the card sends 64 bytes of tuning @@ -2029,7 +2048,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) sdhci_send_command(host, &cmd); host->cmd = NULL; - host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); /* Wait for Buffer Read Ready interrupt */ @@ -2230,26 +2248,26 @@ static const struct mmc_host_ops sdhci_ops = { * * \*****************************************************************************/ -static void sdhci_tasklet_finish(unsigned long param) +static bool sdhci_request_done(struct sdhci_host *host) { - struct sdhci_host *host; unsigned long flags; struct mmc_request *mrq; - - host = (struct sdhci_host*)param; + int i; spin_lock_irqsave(&host->lock, flags); - /* - * If this tasklet gets rescheduled while running, it will - * be run again afterwards but without any active request. - */ - if (!host->mrq) { - spin_unlock_irqrestore(&host->lock, flags); - return; + for (i = 0; i < SDHCI_MAX_MRQS; i++) { + mrq = host->mrqs_done[i]; + if (mrq) { + host->mrqs_done[i] = NULL; + break; + } } - mrq = host->mrq; + if (!mrq) { + spin_unlock_irqrestore(&host->lock, flags); + return true; + } sdhci_del_timer(host, mrq); @@ -2287,14 +2305,23 @@ static void sdhci_tasklet_finish(unsigned long param) host->pending_reset = false; } - host->mrq = NULL; - - sdhci_led_deactivate(host); + if (!sdhci_has_requests(host)) + sdhci_led_deactivate(host); mmiowb(); spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); + + return false; +} + +static void sdhci_tasklet_finish(unsigned long param) +{ + struct sdhci_host *host = (struct sdhci_host *)param; + + while (!sdhci_request_done(host)) + ; } static void sdhci_timeout_timer(unsigned long data) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index a1de422..1f0413b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -314,6 +314,9 @@ struct sdhci_adma2_64_desc { */ #define SDHCI_MAX_SEGS 128 +/* Allow for a a command request and a data request at the same time */ +#define SDHCI_MAX_MRQS 2 + enum sdhci_cookie { COOKIE_UNMAPPED, COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */ @@ -465,7 +468,7 @@ struct sdhci_host { bool preset_enabled; /* Preset is enabled */ bool pending_reset; /* Cmd/data reset is pending */ - struct mmc_request *mrq; /* Current request */ + struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */ struct mmc_command *cmd; /* Current command */ struct mmc_command *data_cmd; /* Current data command */ struct mmc_data *data; /* Current data request */ -- cgit v0.10.2 From 0293d50110fbe6ccbd2fd7e0478552ec8d27087a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:35 +0300 Subject: mmc: sdhci: Factor out sdhci_auto_cmd12() Factor out sdhci_auto_cmd12() so that there is a single place that controls whether auto-CMD12 is used. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e37d0e9..229b467 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -881,6 +881,12 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); } +static inline bool sdhci_auto_cmd12(struct sdhci_host *host, + struct mmc_request *mrq) +{ + return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12); +} + static void sdhci_set_transfer_mode(struct sdhci_host *host, struct mmc_command *cmd) { @@ -911,7 +917,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, * If we are sending CMD23, CMD12 never gets sent * on successful completion (so no Auto-CMD12). */ - if (!cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) && + if (sdhci_auto_cmd12(host, cmd->mrq) && (cmd->opcode != SD_IO_RW_EXTENDED)) mode |= SDHCI_TRNS_AUTO_CMD12; else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { @@ -1469,7 +1475,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) * Ensure we don't send the STOP for non-SET_BLOCK_COUNTED * requests if Auto-CMD12 is enabled. */ - if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) { + if (sdhci_auto_cmd12(host, mrq)) { if (mrq->stop) { mrq->data->stop = NULL; mrq->stop = NULL; -- cgit v0.10.2 From 33a57adb6be38fbf008e4cdbf6c7085ed269bd3c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:36 +0300 Subject: mmc: sdhci: Do not reset cmd or data circuits that are in use In order to support commands during data transfer, it will be possible to need to reset the command circuit while the data circuit is in use, and vice versa. It is now easy to determine whether the command or data circuit is in use, and so just skip the corresponding reset if it is. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 229b467..614d776 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -986,9 +986,9 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) static void sdhci_finish_data(struct sdhci_host *host) { - struct mmc_data *data; + struct mmc_command *data_cmd = host->data_cmd; + struct mmc_data *data = host->data; - data = host->data; host->data = NULL; host->data_cmd = NULL; @@ -1022,7 +1022,8 @@ static void sdhci_finish_data(struct sdhci_host *host) * upon error conditions. */ if (data->error) { - sdhci_do_reset(host, SDHCI_RESET_CMD); + if (!host->cmd || host->cmd == data_cmd) + sdhci_do_reset(host, SDHCI_RESET_CMD); sdhci_do_reset(host, SDHCI_RESET_DATA); } @@ -2305,8 +2306,10 @@ static bool sdhci_request_done(struct sdhci_host *host) /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ - sdhci_do_reset(host, SDHCI_RESET_CMD); - sdhci_do_reset(host, SDHCI_RESET_DATA); + if (!host->cmd) + sdhci_do_reset(host, SDHCI_RESET_CMD); + if (!host->data_cmd) + sdhci_do_reset(host, SDHCI_RESET_DATA); host->pending_reset = false; } -- cgit v0.10.2 From 8842fd17b655c19fa2f50cae5924b35769411da5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:37 +0300 Subject: mmc: sdhci: Avoid STOP cmd triggering warning in sdhci_send_command() The STOP command is sent in error conditions, even when the command is not finished. Avoid triggering the warning for that in sdhci_send_command() by setting host->cmd to NULL first. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 614d776..6c8cc1a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1027,6 +1027,8 @@ static void sdhci_finish_data(struct sdhci_host *host) sdhci_do_reset(host, SDHCI_RESET_DATA); } + /* Avoid triggering warning in sdhci_send_command() */ + host->cmd = NULL; sdhci_send_command(host, data->stop); } else { sdhci_finish_mrq(host, data->mrq); -- cgit v0.10.2 From 07c161bcd7ff6d119f828402a776c9e43a50a851 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 29 Jun 2016 16:24:38 +0300 Subject: mmc: sdhci: sdhci_execute_tuning() must delete timer sdhci_send_command() starts a timer to catch cases where the host controller fails. The timer is normally deleted when the request completes, but in the case of sdhci_execute_tuning() the request is handled differently and the timer is left running. This goes unnoticed because tuning is done before another command so the timer gets reset then. That should not be relied upon, so make sdhci_execute_tuning() delete the timer. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6c8cc1a..2ee8bfa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2057,6 +2057,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) sdhci_send_command(host, &cmd); host->cmd = NULL; + sdhci_del_timer(host, &mrq); spin_unlock_irqrestore(&host->lock, flags); /* Wait for Buffer Read Ready interrupt */ -- cgit v0.10.2 From 39f80bcb0eed20e08090f8b6ed10a0709f1ae36d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Jun 2016 14:52:03 +0200 Subject: mmc: s3cmci: Register cpufreq notifier only on S3C24xx The driver registered for CPU frequency transitions to recalculate its clock when ARM clock frequency changes (ratio between frequencies of ARM's parent clock (fclk) and clock for peripherals remains fixed). This is needed only on S3C24xx platform when cpufreq driver is enabled so limit the ifdef to respective cpufreq Kconfig. Suggested-by: Marek Szyprowski Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 39814f3..c531dee 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1365,7 +1365,7 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = { .no_detect = 1, }; -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ static int s3cmci_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index cc2e46c..30c2c0d 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h @@ -74,7 +74,7 @@ struct s3cmci_host { struct dentry *debug_regs; #endif -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ struct notifier_block freq_transition; #endif }; -- cgit v0.10.2 From a0c3b68c72a355f5dab33c3ddcd257e5a718de0c Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 1 Jul 2016 15:45:28 +0800 Subject: mmc: core: Allow hosts to specify non-support for MMC commands Host drivers which needs to valdiate for non-supported MMC commands and returnn error code for such requests. To improve and simplify the behaviour, let's invent MMC_CAP2_NO_MMC which these host drivers can set to tell the mmc core to skip sending MMC commands during card initialization. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 4c823df..94cbf4e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2510,8 +2510,9 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) if (!mmc_attach_sd(host)) return 0; - if (!mmc_attach_mmc(host)) - return 0; + if (!(host->caps2 & MMC_CAP2_NO_MMC)) + if (!mmc_attach_mmc(host)) + return 0; mmc_power_off(host); return -EIO; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c22476d..aa4bfbf 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -310,6 +310,7 @@ struct mmc_host { #define MMC_CAP2_NO_SDIO (1 << 19) /* Do not send SDIO commands during initialization */ #define MMC_CAP2_HS400_ES (1 << 20) /* Host supports enhanced strobe */ #define MMC_CAP2_NO_SD (1 << 21) /* Do not send SD commands during initialization */ +#define MMC_CAP2_NO_MMC (1 << 22) /* Do not send (e)MMC commands during initialization */ mmc_pm_flag_t pm_caps; /* supported pm features */ -- cgit v0.10.2 From e71d4b816cd2d84a04101a70504c103bc8e0bc6b Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 2 Jul 2016 19:23:13 +0000 Subject: mmc: sdhci: add define for suspend/resume capability This patch adds the missing define for the suspend/resume capability (according to SD Host Controller spec). Signed-off-by: Stefan Wahren Acked-by: Scott Branden Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 1f0413b..e241e11 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -186,6 +186,7 @@ #define SDHCI_CAN_DO_ADMA1 0x00100000 #define SDHCI_CAN_DO_HISPD 0x00200000 #define SDHCI_CAN_DO_SDMA 0x00400000 +#define SDHCI_CAN_DO_SUSPEND 0x00800000 #define SDHCI_CAN_VDD_330 0x01000000 #define SDHCI_CAN_VDD_300 0x02000000 #define SDHCI_CAN_VDD_180 0x04000000 -- cgit v0.10.2 From 1883edd17d11983578ac7eb1ca63c5845bca56fe Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 2 Jul 2016 19:23:14 +0000 Subject: mmc: sdhci-iproc: Use defines instead of magic numbers The capabilities in iproc platform data are magic numbers. So replace them with the proper capability defines to make it readable. Signed-off-by: Stefan Wahren Acked-by: Scott Branden Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 1110f73..53abecc 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -164,8 +164,17 @@ static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = { static const struct sdhci_iproc_data iproc_data = { .pdata = &sdhci_iproc_pltfm_data, - .caps = 0x05E90000, - .caps1 = 0x00000064, + .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) + & SDHCI_MAX_BLOCK_MASK) | + SDHCI_CAN_VDD_330 | + SDHCI_CAN_VDD_180 | + SDHCI_CAN_DO_SUSPEND | + SDHCI_CAN_DO_HISPD | + SDHCI_CAN_DO_ADMA2 | + SDHCI_CAN_DO_SDMA, + .caps1 = SDHCI_DRIVER_TYPE_C | + SDHCI_DRIVER_TYPE_D | + SDHCI_SUPPORT_DDR50, .mmc_caps = MMC_CAP_1_8V_DDR, }; -- cgit v0.10.2 From 5fb06af7a33b4b5cedccedb611cf77568163027b Mon Sep 17 00:00:00 2001 From: Bojan Prtvar Date: Mon, 4 Jul 2016 13:56:55 +0200 Subject: mmc: core: Extend sysfs with OCR register Registers CID and CSD are already exported through sysfs so let's make this interface complete by adding missing OCR register. Signed-off-by: Bojan Prtvar Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt index caa5557..8163eca 100644 --- a/Documentation/mmc/mmc-dev-attrs.txt +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -28,6 +28,7 @@ All attributes are read-only. preferred_erase_size Preferred erase size raw_rpmb_size_mult RPMB partition size rel_sectors Reliable write sector count + ocr Operation Conditions Register Note on Erase Size and Preferred Erase Size: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5485040..403b97b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -749,6 +749,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); +MMC_DEV_ATTR(ocr, "%08x\n", card->ocr); static ssize_t mmc_fwrev_show(struct device *dev, struct device_attribute *attr, @@ -784,6 +785,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_enhanced_area_size.attr, &dev_attr_raw_rpmb_size_mult.attr, &dev_attr_rel_sectors.attr, + &dev_attr_ocr.attr, NULL, }; ATTRIBUTE_GROUPS(mmc_std); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b95bd24..3dd0861 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -675,6 +675,7 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name); MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid); MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial); +MMC_DEV_ATTR(ocr, "%08x\n", card->ocr); static struct attribute *sd_std_attrs[] = { @@ -690,6 +691,7 @@ static struct attribute *sd_std_attrs[] = { &dev_attr_name.attr, &dev_attr_oemid.attr, &dev_attr_serial.attr, + &dev_attr_ocr.attr, NULL, }; ATTRIBUTE_GROUPS(sd_std); -- cgit v0.10.2 From 5f1d1434b7a03dcf19f4a11f8632fbcd8185dfa8 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 14 Jul 2016 16:26:03 +0800 Subject: Documentation: mmc: add description for new no-sd* and no-mmc This patch adds description for no-sd, no-sdio, no-mmc. We expose these to DT as some of the controllers are unable to deal with special cmd type due to hw limitation. Signed-off-by: Shawn Lin Acked-by: Rob Herring Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index ecc007a..22d1e1f 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -49,6 +49,9 @@ Optional properties: - mmc-hs400-enhanced-strobe: eMMC HS400 enhanced strobe mode is supported - dsr: Value the card's (optional) Driver Stage Register (DSR) should be programmed with. Valid range: [0 .. 0xffff]. +- no-sdio: controller is limited to send sdio cmd during initialization +- no-sd: controller is limited to send sd cmd during initialization +- no-mmc: controller is limited to send mmc cmd during initialization *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line polarity properties, we have to fix the meaning of the "normal" and "inverted" -- cgit v0.10.2 From 6ae3e537eab9f560b516b001eb89f0cd568bdced Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 14 Jul 2016 16:26:04 +0800 Subject: mmc: core: expose MMC_CAP2_NO_* to dt The reason for why we expose these to dt is that some of the controller is unable to send special cmd type due to the hw limitation. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index d7e86f9..98f25ff 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -315,6 +315,12 @@ int mmc_of_parse(struct mmc_host *host) host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe")) host->caps2 |= MMC_CAP2_HS400_ES; + if (of_property_read_bool(np, "no-sdio")) + host->caps2 |= MMC_CAP2_NO_SDIO; + if (of_property_read_bool(np, "no-sd")) + host->caps2 |= MMC_CAP2_NO_SD; + if (of_property_read_bool(np, "no-mmc")) + host->caps2 |= MMC_CAP2_NO_MMC; host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr); if (host->dsr_req && (host->dsr & ~0xffff)) { -- cgit v0.10.2 From 6024e16654c1e1a2475e848d735963d05a12dba9 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 15 Jul 2016 10:54:50 +0900 Subject: mmc: dw_mmc: set to MMC_CAP_ERASE by default This flag needs to use the trim/discard/erase commands. dwmmc controller enables this flag by default. Signed-off-by: Jaehoon Chung Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 2dfdc58..32380d5 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2604,6 +2604,12 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps) mmc->caps = host->pdata->caps; + /* + * Support MMC_CAP_ERASE by default. + * It needs to use trim/discard/erase commands. + */ + mmc->caps |= MMC_CAP_ERASE; + if (host->pdata->pm_caps) mmc->pm_caps = host->pdata->pm_caps; -- cgit v0.10.2 From 2c4c7146c7f6cffcbd71f1df95e6bc6727324e05 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 15 Jul 2016 10:54:51 +0900 Subject: mmc: dw_mmc: rockchip: unset the MMC_CAP_ERASE flag In dw_mmc.c, it's enabled by default. It doesn't need to set MMC_CAP_ERASE in rockchip anymore. Signed-off-by: Jaehoon Chung Acked-by: Shawn Lin Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index d3cf1f1..25eae35 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -294,10 +294,10 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* Common capabilities of RK3288 SoC */ static unsigned long dw_mci_rk3288_dwmmc_caps[4] = { - MMC_CAP_ERASE | MMC_CAP_CMD23, - MMC_CAP_ERASE | MMC_CAP_CMD23, - MMC_CAP_ERASE | MMC_CAP_CMD23, - MMC_CAP_ERASE | MMC_CAP_CMD23, + MMC_CAP_CMD23, + MMC_CAP_CMD23, + MMC_CAP_CMD23, + MMC_CAP_CMD23, }; static const struct dw_mci_drv_data rk2928_drv_data = { -- cgit v0.10.2 From 6825a606584cec455b003afec7c10a07e563c994 Mon Sep 17 00:00:00 2001 From: Bojan Prtvar Date: Tue, 19 Jul 2016 11:16:38 +0200 Subject: mmc: core: Extend sysfs with DSR register Export DSR register through sysfs same as we did for the CID, CSD and OCR registers. Signed-off-by: Bojan Prtvar Signed-off-by: Ulf Hansson diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt index 8163eca..404a0e9 100644 --- a/Documentation/mmc/mmc-dev-attrs.txt +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -29,6 +29,7 @@ All attributes are read-only. raw_rpmb_size_mult RPMB partition size rel_sectors Reliable write sector count ocr Operation Conditions Register + dsr Driver Stage Register Note on Erase Size and Preferred Erase Size: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 403b97b..f2d185c 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -767,6 +767,22 @@ static ssize_t mmc_fwrev_show(struct device *dev, static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL); +static ssize_t mmc_dsr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + + if (card->csd.dsr_imp && host->dsr_req) + return sprintf(buf, "0x%x\n", host->dsr); + else + /* return default DSR value */ + return sprintf(buf, "0x%x\n", 0x404); +} + +static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL); + static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, @@ -786,6 +802,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_raw_rpmb_size_mult.attr, &dev_attr_rel_sectors.attr, &dev_attr_ocr.attr, + &dev_attr_dsr.attr, NULL, }; ATTRIBUTE_GROUPS(mmc_std); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 3dd0861..0123936 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -678,6 +678,22 @@ MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial); MMC_DEV_ATTR(ocr, "%08x\n", card->ocr); +static ssize_t mmc_dsr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + + if (card->csd.dsr_imp && host->dsr_req) + return sprintf(buf, "0x%x\n", host->dsr); + else + /* return default DSR value */ + return sprintf(buf, "0x%x\n", 0x404); +} + +static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL); + static struct attribute *sd_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, @@ -692,6 +708,7 @@ static struct attribute *sd_std_attrs[] = { &dev_attr_oemid.attr, &dev_attr_serial.attr, &dev_attr_ocr.attr, + &dev_attr_dsr.attr, NULL, }; ATTRIBUTE_GROUPS(sd_std); -- cgit v0.10.2 From 52ac7acf412b29b8a29320fde932eaa5bd3fd7c6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 9 Jul 2016 16:41:43 +0300 Subject: mmc: sdhci-pci: Convert to use managed functions pcim_* and devm_* This makes the error handling much more simpler than open-coding everything and in addition makes the probe function smaller an tidier. Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index e8294a2..0916261 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1811,15 +1811,13 @@ static int sdhci_pci_probe(struct pci_dev *pdev, return -ENODEV; } - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret; - chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL); - if (!chip) { - ret = -ENOMEM; - goto err; - } + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; chip->pdev = pdev; chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data; @@ -1835,7 +1833,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev, if (chip->fixes && chip->fixes->probe) { ret = chip->fixes->probe(chip); if (ret) - goto free; + return ret; } slots = chip->num_slots; /* Quirk may have changed this */ @@ -1845,8 +1843,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev, if (IS_ERR(slot)) { for (i--; i >= 0; i--) sdhci_pci_remove_slot(chip->slots[i]); - ret = PTR_ERR(slot); - goto free; + return PTR_ERR(slot); } chip->slots[i] = slot; @@ -1856,35 +1853,18 @@ static int sdhci_pci_probe(struct pci_dev *pdev, sdhci_pci_runtime_pm_allow(&pdev->dev); return 0; - -free: - pci_set_drvdata(pdev, NULL); - kfree(chip); - -err: - pci_disable_device(pdev); - return ret; } static void sdhci_pci_remove(struct pci_dev *pdev) { int i; - struct sdhci_pci_chip *chip; - - chip = pci_get_drvdata(pdev); - - if (chip) { - if (chip->allow_runtime_pm) - sdhci_pci_runtime_pm_forbid(&pdev->dev); + struct sdhci_pci_chip *chip = pci_get_drvdata(pdev); - for (i = 0; i < chip->num_slots; i++) - sdhci_pci_remove_slot(chip->slots[i]); - - pci_set_drvdata(pdev, NULL); - kfree(chip); - } + if (chip->allow_runtime_pm) + sdhci_pci_runtime_pm_forbid(&pdev->dev); - pci_disable_device(pdev); + for (i = 0; i < chip->num_slots; i++) + sdhci_pci_remove_slot(chip->slots[i]); } static struct pci_driver sdhci_driver = { -- cgit v0.10.2 From 761daa36e2091f3717f8cd9ebaaa0e2f81e9308e Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:10 +0800 Subject: mmc: sdhci: using common mmc_regulator_set_vqmmc() Switch to use the more robust common mmc_regulator_set_vqmmc() function in MMC core which set the target voltage as close as possible to target voltage. We did not re-factor the whole sdhci_start_signal_voltage_switch() cause we want to keep the original signal switch order between host and card to avoid potential break. Signed-off-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2ee8bfa..7894652 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1835,8 +1835,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000, - 3600000); + ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 3.3V signalling voltage failed\n", mmc_hostname(mmc)); @@ -1859,8 +1858,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, if (!(host->flags & SDHCI_SIGNALING_180)) return -EINVAL; if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_set_voltage(mmc->supply.vqmmc, - 1700000, 1950000); + ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 1.8V signalling voltage failed\n", mmc_hostname(mmc)); @@ -1892,8 +1890,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, if (!(host->flags & SDHCI_SIGNALING_120)) return -EINVAL; if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000, - 1300000); + ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 1.2V signalling voltage failed\n", mmc_hostname(mmc)); -- cgit v0.10.2 From 84d7d5539d4dad97cc436664ab703fb2bf7f07ff Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:11 +0800 Subject: mmc: sdhci-esdhc-imx: remove SDHCI_QUIRK_BROKEN_TIMEOUT_VAL The driver has already implemented the private .set_timeout() callback for common SDHCI code to do correct timeout value setting, it does not need call sdhci_calc_timeout(), so this quirk actually is not working. Remove it now. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 9d3ae1f..c005b41 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1147,8 +1147,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (IS_ERR(imx_data->pins_default)) dev_warn(mmc_dev(host->mmc), "could not get default state\n"); - host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207) /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK -- cgit v0.10.2 From 04143fbaeb579983fcc96f8e1c186e1512984b51 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:12 +0800 Subject: mmc: sdhci-esdhc-imx: add esdhc specific suspend resume callback It will be used for platform specific suspend/resume state save/restore work for some low power mode like Mega/Fast or LPSR mode. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c005b41..f94c1ef 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1254,6 +1254,16 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) } #ifdef CONFIG_PM +static int sdhci_esdhc_suspend(struct device *dev) +{ + return sdhci_pltfm_suspend(dev); +} + +static int sdhci_esdhc_resume(struct device *dev) +{ + return sdhci_pltfm_resume(dev); +} + static int sdhci_esdhc_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -1289,7 +1299,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops sdhci_esdhc_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume) + SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume) SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, sdhci_esdhc_runtime_resume, NULL) }; -- cgit v0.10.2 From cc17e1294b8aeb3596485b3b8a68419cf3f6ab69 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:13 +0800 Subject: mmc: sdhci-esdhc-imx: restore watermark level setting after resume Currently, we config the watermark_level register only in probe. This will cause the mmc write operation timeout issue after system resume back in LPSR mode. Because in LPSR mode, after system resume back, the watermark_level register(0x44) changes to 0x08000880, which set the write watermark level as 0, and set the read watermark level as 128. This value is incorrect. This patch restores the setting of watermark level register after system resume back. Signed-off-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index f94c1ef..91c6a68 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -39,6 +39,7 @@ #define ESDHC_VENDOR_SPEC_VSELECT (1 << 1) #define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) #define ESDHC_WTMK_LVL 0x44 +#define ESDHC_WTMK_DEFAULT_VAL 0x10401040 #define ESDHC_MIX_CTRL 0x48 #define ESDHC_MIX_CTRL_DDREN (1 << 3) #define ESDHC_MIX_CTRL_AC23EN (1 << 7) @@ -1157,7 +1158,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * to something insane. Change it back here. */ if (esdhc_is_usdhc(imx_data)) { - writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL); + writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL); host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -1261,6 +1262,14 @@ static int sdhci_esdhc_suspend(struct device *dev) static int sdhci_esdhc_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 = sdhci_pltfm_priv(pltfm_host); + + /* restore watermark setting in case it's lost in low power mode */ + if (esdhc_is_usdhc(imx_data)) + writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL); + return sdhci_pltfm_resume(dev); } -- cgit v0.10.2 From ca8cc0fe0e55866dee5009aff5f4740b5daf790b Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:14 +0800 Subject: mmc: sdhci-esdhci-imx: disable DLL delay line settings explicitly Disable DLL delay line settings explicitly during driver initialization in case ROM/uBoot had set an invalid delay. e.g. MX6DL ROM has set the default delay line(DLLCTRL) to 0x1000021, the uSDHC clock timing will become marginal when works on DDR mode due to default delay and will possibly see CRC errors in case the board is not perfectly designed on the eMMC chip layout. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 91c6a68..874d510 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1187,6 +1187,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) */ writel(readl(host->ioaddr + 0x6c) | BIT(7), host->ioaddr + 0x6c); + + /* disable DLL_CTRL delay line settings */ + writel(0x0, host->ioaddr + ESDHC_DLL_CTRL); } if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) -- cgit v0.10.2 From d87fc96636884430278bc3b425a5a6e2c071a377 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:15 +0800 Subject: mmc: sdhci-esdhc-imx: support setting tuning start point The delay cells of some SoCs may have less delay per one cell, for such SoCs, user could set the start delay cell point to bypass the first a few meaningless tuning commands. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 874d510..b47dad1 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -76,7 +76,8 @@ #define ESDHC_TUNING_CTRL 0xcc #define ESDHC_STD_TUNING_EN (1 << 24) /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ -#define ESDHC_TUNING_START_TAP 0x1 +#define ESDHC_TUNING_START_TAP_DEFAULT 0x1 +#define ESDHC_TUNING_START_TAP_MASK 0xff #define ESDHC_TUNING_STEP_MASK 0x00070000 #define ESDHC_TUNING_STEP_SHIFT 16 @@ -490,7 +491,12 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL); - tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP; + tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP_DEFAULT; + if (imx_data->boarddata.tuning_start_tap) { + tuning_ctrl &= ~ESDHC_TUNING_START_TAP_MASK; + tuning_ctrl |= imx_data->boarddata.tuning_start_tap; + } + if (imx_data->boarddata.tuning_step) { tuning_ctrl &= ~ESDHC_TUNING_STEP_MASK; tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT; @@ -976,6 +982,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, boarddata->wp_type = ESDHC_WP_GPIO; of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step); + of_property_read_u32(np, "fsl,tuning-start-tap", + &boarddata->tuning_start_tap); if (of_find_property(np, "no-1-8-v", NULL)) boarddata->support_vsel = false; @@ -1198,7 +1206,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) | - ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP, + ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP_DEFAULT, host->ioaddr + ESDHC_TUNING_CTRL); if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h index 95ccab3..7daa78a 100644 --- a/include/linux/platform_data/mmc-esdhc-imx.h +++ b/include/linux/platform_data/mmc-esdhc-imx.h @@ -46,5 +46,6 @@ struct esdhc_platform_data { bool support_vsel; unsigned int delay_line; unsigned int tuning_step; /* The delay cell steps in tuning procedure */ + unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */ }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ -- cgit v0.10.2 From 152f05c783438b57bb42e8171aa871c49ae5b2d5 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:16 +0800 Subject: doc: dt: fsl-imx-esdhc: add set tuning start point binding add tuning start point binding Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt index dca56d6..3e29050 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt @@ -28,6 +28,8 @@ Optional properties: transparent level shifters on the outputs of the controller. Two cells are required, first cell specifies minimum slot voltage (mV), second cell specifies maximum slot voltage (mV). Several ranges could be specified. +- fsl,tuning-start-tap: Specify the start dealy cell point when send first CMD19 + in tuning procedure. - fsl,tuning-step: Specify the increasing delay cell steps in tuning procedure. The uSDHC use one delay cell as default increasing step to do tuning process. This property allows user to change the tuning step to more than one delay -- cgit v0.10.2 From f37b20ebc4bc7c41e6fe1f4af6a62e9de70b771b Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:17 +0800 Subject: mmc: sdhci: add standard hw auto retuning support If HW supports SDHCI_TUNING_MODE_3 which is auto retuning, we won't retune during runtime suspend and resume, instead we use Re-tuning Request signaled via SDHCI_INT_RETUNE interrupt to do retuning and hw auto retuning during data transfer to guarantee the signal sample window correction. This can avoid a mass of repeatedly retuning during small file system data access and improve the performance. Signed-off-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7894652..31c14b0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -229,6 +229,10 @@ static void sdhci_init(struct sdhci_host *host, int soft) SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; + if (host->tuning_mode == SDHCI_TUNING_MODE_2 || + host->tuning_mode == SDHCI_TUNING_MODE_3) + host->ier |= SDHCI_INT_RETUNE; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); @@ -2673,6 +2677,9 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) pr_err("%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); + if (intmask & SDHCI_INT_RETUNE) + mmc_retune_needed(host->mmc); + if (intmask & SDHCI_INT_CARD_INT) { sdhci_enable_sdio_irq_nolock(host, false); host->thread_isr |= SDHCI_INT_CARD_INT; @@ -2682,7 +2689,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | - SDHCI_INT_CARD_INT); + SDHCI_INT_RETUNE | SDHCI_INT_CARD_INT); if (intmask) { unexpected |= intmask; @@ -2787,7 +2794,8 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_disable_card_detection(host); mmc_retune_timer_stop(host->mmc); - mmc_retune_needed(host->mmc); + if (host->tuning_mode != SDHCI_TUNING_MODE_3) + mmc_retune_needed(host->mmc); if (!device_may_wakeup(mmc_dev(host->mmc))) { host->ier = 0; @@ -2848,7 +2856,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) unsigned long flags; mmc_retune_timer_stop(host->mmc); - mmc_retune_needed(host->mmc); + if (host->tuning_mode != SDHCI_TUNING_MODE_3) + mmc_retune_needed(host->mmc); spin_lock_irqsave(&host->lock, flags); host->ier &= SDHCI_INT_CARD_INT; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index e241e11..0411c9f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -128,6 +128,7 @@ #define SDHCI_INT_CARD_INSERT 0x00000040 #define SDHCI_INT_CARD_REMOVE 0x00000080 #define SDHCI_INT_CARD_INT 0x00000100 +#define SDHCI_INT_RETUNE 0x00001000 #define SDHCI_INT_ERROR 0x00008000 #define SDHCI_INT_TIMEOUT 0x00010000 #define SDHCI_INT_CRC 0x00020000 @@ -518,6 +519,8 @@ struct sdhci_host { unsigned int tuning_count; /* Timer count for re-tuning */ unsigned int tuning_mode; /* Re-tuning mode supported by host */ #define SDHCI_TUNING_MODE_1 0 +#define SDHCI_TUNING_MODE_2 1 +#define SDHCI_TUNING_MODE_3 2 unsigned long private[0] ____cacheline_aligned; }; -- cgit v0.10.2 From 0b330e38589766556462c13988ebc83ef24964e5 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:18 +0800 Subject: mmc: sdhci-esdhc-imx: enable hw auto retuning for STD_TUNING Enable HW auto retuning when set SDHCI_CTRL_EXEC_TUNING and clear it when clear SDHCI_CTRL_TUNED_CLK. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index b47dad1..c90aa07 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -45,6 +45,7 @@ #define ESDHC_MIX_CTRL_AC23EN (1 << 7) #define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22) #define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23) +#define ESDHC_MIX_CTRL_AUTO_TUNE_EN (1 << 24) #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) #define ESDHC_MIX_CTRL_HS400_EN (1 << 26) /* Bits 3 and 6 are not SDHCI standard definitions */ @@ -485,11 +486,13 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } else { v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN; } if (val & SDHCI_CTRL_EXEC_TUNING) { v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; + m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL); tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP_DEFAULT; if (imx_data->boarddata.tuning_start_tap) { -- cgit v0.10.2 From da0295ff1db13765ad0da6cc7c2117f15c83b562 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:19 +0800 Subject: mmc: sdhci-esdhc-imx: enable hw auto retuning for MAN_TUNING Indicating hw auto retuning support for mx6qdl in the fake caps_1 register and enable auto retuning in post_tuning process after tuning completes. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c90aa07..ac2c83a 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -302,7 +302,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) /* imx6q/dl does not have cap_1 register, fake one */ val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 - | SDHCI_USE_SDR50_TUNING; + | SDHCI_USE_SDR50_TUNING + | (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT); if (imx_data->socdata->flags & ESDHC_FLAG_HS400) val |= SDHCI_SUPPORT_HS400; @@ -472,10 +473,13 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); - if (val & SDHCI_CTRL_TUNED_CLK) + if (val & SDHCI_CTRL_TUNED_CLK) { new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL; - else + new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; + } else { new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN; + } writel(new_val , host->ioaddr + ESDHC_MIX_CTRL); } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); @@ -761,6 +765,7 @@ static void esdhc_post_tuning(struct sdhci_host *host) reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg &= ~ESDHC_MIX_CTRL_EXE_TUNE; + reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; writel(reg, host->ioaddr + ESDHC_MIX_CTRL); } -- cgit v0.10.2 From 7ac6da2623a4d4b68c61bc289451ac630ae8ec35 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:20 +0800 Subject: mmc: sdhci-esdhc-imx: fix strobe DLL lock wrong clock issue When enable DDR, the clock factor definition is changed. e.g. original 200Mhz will become 100Mhz once MIX_CTRL_DDREN bit is set So we need to update the clock setting then the strobe dll can lock the correct clock rate. Additionally we also need disable the clock before locking strobe dll. Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ac2c83a..bbb0bd4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -853,6 +853,11 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host) u32 v; if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) { + /* disable clock before enabling strobe dll */ + writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) & + ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + /* force a reset on strobe dll */ writel(ESDHC_STROBE_DLL_CTRL_RESET, host->ioaddr + ESDHC_STROBE_DLL_CTRL); @@ -914,6 +919,8 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; writel(m, host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 1; + /* update clock after enable DDR for strobe DLL lock */ + host->ops->set_clock(host, host->clock); esdhc_set_strobe_dll(host); break; } -- cgit v0.10.2 From f3f5cf3dfc3a1bcc627cf4d7c972a4adbd9d1e25 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:21 +0800 Subject: mmc: sdhci-esdhc-imx: factor out hw related initialization into function Move all hw related static initializations into a separate function which helps concentrate the hw related initialization code. And that function could also be called by other places later as a basic hw state restore. e.g. suspend/resume where the hw state is possible to lost due to low power mode. Signed-off-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index bbb0bd4..5a0e5de 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -979,6 +979,44 @@ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { .ops = &sdhci_esdhc_ops, }; +static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + + if (esdhc_is_usdhc(imx_data)) { + /* + * The imx6q ROM code will change the default watermark + * level setting to something insane. Change it back here. + */ + writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL); + + /* + * ROM code will change the bit burst_length_enable setting + * to zero if this usdhc is choosed to boot system. Change + * it back here, otherwise it will impact the performance a + * lot. This bit is used to enable/disable the burst length + * for the external AHB2AXI bridge, it's usefully especially + * for INCR transfer because without burst length indicator, + * the AHB2AXI bridge does not know the burst length in + * advance. And without burst length indicator, AHB INCR + * transfer can only be converted to singles on the AXI side. + */ + writel(readl(host->ioaddr + SDHCI_HOST_CONTROL) + | ESDHC_BURST_LEN_EN_INCR, + host->ioaddr + SDHCI_HOST_CONTROL); + /* + * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL + * TO1.1, it's harmless for MX6SL + */ + writel(readl(host->ioaddr + 0x6c) | BIT(7), + host->ioaddr + 0x6c); + + /* disable DLL_CTRL delay line settings */ + writel(0x0, host->ioaddr + ESDHC_DLL_CTRL); + } +} + #ifdef CONFIG_OF static int sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, @@ -1176,43 +1214,11 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK | SDHCI_QUIRK_BROKEN_ADMA; - /* - * The imx6q ROM code will change the default watermark level setting - * to something insane. Change it back here. - */ if (esdhc_is_usdhc(imx_data)) { - writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL); - host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; - - /* - * ROM code will change the bit burst_length_enable setting - * to zero if this usdhc is choosed to boot system. Change - * it back here, otherwise it will impact the performance a - * lot. This bit is used to enable/disable the burst length - * for the external AHB2AXI bridge, it's usefully especially - * for INCR transfer because without burst length indicator, - * the AHB2AXI bridge does not know the burst length in - * advance. And without burst length indicator, AHB INCR - * transfer can only be converted to singles on the AXI side. - */ - writel(readl(host->ioaddr + SDHCI_HOST_CONTROL) - | ESDHC_BURST_LEN_EN_INCR, - host->ioaddr + SDHCI_HOST_CONTROL); - if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; - - /* - * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL - * TO1.1, it's harmless for MX6SL - */ - writel(readl(host->ioaddr + 0x6c) | BIT(7), - host->ioaddr + 0x6c); - - /* disable DLL_CTRL delay line settings */ - writel(0x0, host->ioaddr + ESDHC_DLL_CTRL); } if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) @@ -1237,6 +1243,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (err) goto disable_clk; + sdhci_esdhc_imx_hwinit(host); + err = sdhci_add_host(host); if (err) goto disable_clk; -- cgit v0.10.2 From 2b16cf326b70fdf6ccd2ae87807bb006487dbb85 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:22 +0800 Subject: mmc: sdhci-esdhc-imx: move tuning static configuration into hwinit function Move tuning static configuration into basic hwinit function. Tuning configuration may also be lost in low power mode, so need restore in hwinit(). Acked-by: Adrian Hunter Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 5a0e5de..c5adf4b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -484,7 +484,6 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); - u32 tuning_ctrl; if (val & SDHCI_CTRL_TUNED_CLK) { v |= ESDHC_MIX_CTRL_SMPCLK_SEL; } else { @@ -497,18 +496,6 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; - tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL); - tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP_DEFAULT; - if (imx_data->boarddata.tuning_start_tap) { - tuning_ctrl &= ~ESDHC_TUNING_START_TAP_MASK; - tuning_ctrl |= imx_data->boarddata.tuning_start_tap; - } - - if (imx_data->boarddata.tuning_step) { - tuning_ctrl &= ~ESDHC_TUNING_STEP_MASK; - tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT; - } - writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL); } else { v &= ~ESDHC_MIX_CTRL_EXE_TUNE; } @@ -983,6 +970,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + int tmp; if (esdhc_is_usdhc(imx_data)) { /* @@ -1014,6 +1002,23 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) /* disable DLL_CTRL delay line settings */ writel(0x0, host->ioaddr + ESDHC_DLL_CTRL); + + if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { + tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL); + tmp |= ESDHC_STD_TUNING_EN | + ESDHC_TUNING_START_TAP_DEFAULT; + if (imx_data->boarddata.tuning_start_tap) { + tmp &= ~ESDHC_TUNING_START_TAP_MASK; + tmp |= imx_data->boarddata.tuning_start_tap; + } + + if (imx_data->boarddata.tuning_step) { + tmp &= ~ESDHC_TUNING_STEP_MASK; + tmp |= imx_data->boarddata.tuning_step + << ESDHC_TUNING_STEP_SHIFT; + } + writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL); + } } } @@ -1225,11 +1230,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) 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_DEFAULT, - host->ioaddr + ESDHC_TUNING_CTRL); - if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; -- cgit v0.10.2 From 19dbfdd3d5152daf86cd79beb14544a2ffab8090 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:23 +0800 Subject: mmc: sdhci-esdhci-imx: re-initialize hw state after resume sdhci_esdhc_imx_hwinit() includes all basic hw intialization. Calling it after resume to re-initialize hw in case its state is already lost in low power mode. Signed-off-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c5adf4b..e5b5d1c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1297,12 +1297,9 @@ static int sdhci_esdhc_suspend(struct device *dev) static int sdhci_esdhc_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 = sdhci_pltfm_priv(pltfm_host); - /* restore watermark setting in case it's lost in low power mode */ - if (esdhc_is_usdhc(imx_data)) - writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL); + /* re-initialize hw state in case it's lost in low power mode */ + sdhci_esdhc_imx_hwinit(host); return sdhci_pltfm_resume(dev); } -- cgit v0.10.2 From a75dcbf490ac1be50a458f5417f3cd6334e80a04 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Tue, 12 Jul 2016 15:46:24 +0800 Subject: mmc: sdhci-esdhc-imx: clear tuning bits during driver probe The tuning bits like FBCLK_SEL, SMP_CLK_SEL and DLY_CELL which affects timing may have already been set by ROM if booting from SD3.0 mode like SDR104. Let's clear it first during driver probe before doing the new card enumeration to avoid working on the wrong timing. Note that tuning bits are dynamical settings which may need to be kept during MMC_PM_KEEP_POWER suspend, so we did not put them into hwinit function. Signed-off-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index e5b5d1c..2bb326b 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1224,6 +1224,11 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_1_8V_DDR; if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; + + /* clear tuning bits in case ROM has set it already */ + writel(0x0, host->ioaddr + ESDHC_MIX_CTRL); + writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR); + writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); } if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) -- cgit v0.10.2 From 1f64cec2b69544899773d88ae7039760e0a3714b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 12 Jul 2016 14:03:42 +0300 Subject: sdhci-pci: Use MRFLD as abbreviation of Merrifield Everywhere else in the code MRFLD abbreviation is used for Intel Merrifield. Do the same for sdhci-pci. Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 0916261..bdda3d0 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -419,13 +419,13 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { }; /* Define Host controllers for Intel Merrifield platform */ -#define INTEL_MRFL_EMMC_0 0 -#define INTEL_MRFL_EMMC_1 1 +#define INTEL_MRFLD_EMMC_0 0 +#define INTEL_MRFLD_EMMC_1 1 -static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) +static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) { - if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) && - (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1)) + if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_0) && + (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_1)) /* SD support is not ready yet */ return -ENODEV; @@ -435,12 +435,12 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) return 0; } -static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { +static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, - .probe_slot = intel_mrfl_mmc_probe_slot, + .probe_slot = intel_mrfld_mmc_probe_slot, }; /* O2Micro extra registers */ @@ -1104,10 +1104,10 @@ static const struct pci_device_id pci_ids[] = { { .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_MRFL_MMC, + .device = PCI_DEVICE_ID_INTEL_MRFLD_MMC, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrfld_mmc, }, { diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 89e7151..7e07887 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -14,7 +14,7 @@ #define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294 #define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295 #define PCI_DEVICE_ID_INTEL_BSW_SD 0x2296 -#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 +#define PCI_DEVICE_ID_INTEL_MRFLD_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 -- cgit v0.10.2 From efba142bcd980e08ed5e2b98b23b103697d9aa6c Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 12 Jul 2016 14:53:36 +0100 Subject: mmc: sdhci: Request regulators before reading capabilities The capabilities of the SDHCI host controller are read early during the SDHCI host initialisation in sdhci_setup_host() and before any regulators for the host have been requested. This means that if the host supports some high-speed modes (according to its capabilities register), but the board cannot because the appropriate voltage regulator is not available, then the host cannot easily override the capabilities that are supported. To allow a SDHCI host controller to determine if it can support UHS high speed modes via the presence of the MMC regulators, request the regulators before reading the capabilities of the host controller. This will allow the SDHCI host to use the 'reset' callback to take the appropriate action (set flags, configure registers, etc) before the capabilities register(s) are read. Please note that some SDHCI hosts, such as the Tegra SDHCI host, has the ability to mask bits in the capabilities register to prevent certain capabilities from being advertised. Signed-off-by: Jon Hunter Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 31c14b0..cd65d47 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3027,6 +3027,16 @@ int sdhci_setup_host(struct sdhci_host *host) mmc = host->mmc; + /* + * If there are external regulators, get them. Note this must be done + * early before resetting the host and reading the capabilities so that + * the host can take the appropriate action if regulators are not + * available. + */ + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + return ret; + sdhci_read_caps(host); override_timeout_clk = host->timeout_clk; @@ -3259,11 +3269,6 @@ int sdhci_setup_host(struct sdhci_host *host) mmc_gpio_get_cd(host->mmc) < 0) mmc->caps |= MMC_CAP_NEEDS_POLL; - /* If there are external regulators, get them */ - ret = mmc_regulator_get_supply(mmc); - if (ret == -EPROBE_DEFER) - goto undma; - /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_enable(mmc->supply.vqmmc); -- cgit v0.10.2 From 4f6aa3264af4d44caaa649dd3ff1fe98f5817251 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 12 Jul 2016 14:53:37 +0100 Subject: mmc: tegra: Only advertise UHS modes if IO regulator is present To support UHS modes for Tegra an external regulator must be present to adjust the IO voltage accordingly. Even if the regulator is not present but the host supports the UHS modes and the device supports the UHS modes, then we will attempt to switch to a high-speed mode. Without an external regulator, Tegra will fail to switch to the high-speed mode. It has been found that with some SD cards, that once it has been switch to operate at a high-speed mode, all subsequent commands issues to the card will fail and so it will not be possible to switch back to a non high-speed mode and so the SD card initialisation will fail. The SDHCI core does not require that the host have an external regulator when switching to UHS modes and therefore, the Tegra SDHCI host controller should only advertise the UHS modes as being supported if the regulator for the IO voltage is present. Fortunately, Tegra has a vendor specific register which can be used to control which modes are advertised via the SDHCI_CAPABILITIES register. Hence, if there is no IO voltage regulator available for the Tegra SDHCI host, then don't advertise the UHS modes. Note that if the regulator is not available, we also don't advertise that the SDHCI is compatible with v3.0 of the SDHCI specification because this will read the SDHCI_CAPABILITIES_1 register which will enable other UHS modes. This fixes commit 7ad2ed1dfcbe ("mmc: tegra: enable UHS-I modes") which enables UHS mode without checking if the board can support them. Fixes: 7ad2ed1dfcbe ("mmc: tegra: enable UHS-I modes") Signed-off-by: Jon Hunter Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index bcc0de4..bd11998 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -148,28 +148,37 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) return; misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); - /* Erratum: Enable SDHCI spec v3.00 support */ - if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) - misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; - /* Advertise UHS modes as supported by host */ - if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) - misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; - else - misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50; - if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) - misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; - else - misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50; - if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) - misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; - else - misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; - sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); - clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); + + misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | + SDHCI_MISC_CTRL_ENABLE_SDR50 | + SDHCI_MISC_CTRL_ENABLE_DDR50 | + SDHCI_MISC_CTRL_ENABLE_SDR104); + clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; - if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) - clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; + + /* + * If the board does not define a regulator for the SDHCI + * IO voltage, then don't advertise support for UHS modes + * even if the device supports it because the IO voltage + * cannot be configured. + */ + if (!IS_ERR(host->mmc->supply.vqmmc)) { + /* Erratum: Enable SDHCI spec v3.00 support */ + if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) + misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; + /* Advertise UHS modes as supported by host */ + if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) + misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; + if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) + misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; + if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) + misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; + if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) + clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; + } + + sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) -- cgit v0.10.2 From 9718f84b85396e090ca42fafa730410d286d61e3 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 24 Jun 2016 19:24:59 +0300 Subject: mmc: sdhci-msm: Do not reset the controller if no card in the slot The controller does not clear the "reset bit" when it is reset without a card in the slot. Because of this, the following error message is seen while booting with no plugged SD card. mmc1: Reset 0x1 never completed. Add the SDHCI_QUIRK_NO_CARD_NO_RESET quirk to avoid this. Signed-off-by: Ivan T. Ivanov Signed-off-by: Georgi Djakov Tested-by: Bjorn Andersson Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 343e4bd..cc4274a 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -427,6 +427,7 @@ static const struct sdhci_ops sdhci_msm_ops = { static const struct sdhci_pltfm_data sdhci_msm_pdata = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_NO_CARD_NO_RESET | SDHCI_QUIRK_SINGLE_POWER_WRITE, .ops = &sdhci_msm_ops, }; -- cgit v0.10.2 From ee3206748952046e90cf0dcdcffdbb94839289b4 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Tue, 19 Jul 2016 17:52:25 +0300 Subject: mmc: sdhci-msm: Add set_uhs_signaling() implementation To allow UHS mode to work properly, we need to implement a Qualcomm specific set_uhs_signaling() callback function. This function differs from the sdhci_set_uhs_signaling() in that we need check the clock rate and enable UHS timing only if the frequency is above 100MHz. This patch resolves the mmc_select_hs200 timeouts noticed after merging commit a5c1f3e55c99 ("mmc: mmc: do not use CMD13 to get status after speed mode switch") mmc0: mmc_select_hs200 failed, error -110 mmc0: error -110 whilst initialising MMC card mmc0: Reset 0x1 never completed. sdhci: =========== REGISTER DUMP (mmc0)=========== sdhci: Sys addr: 0x00000000 | Version: 0x00002e02 sdhci: Blk size: 0x00004000 | Blk cnt: 0x00000000 sdhci: Argument: 0x00000000 | Trn mode: 0x00000000 sdhci: Present: 0x01f80000 | Host ctl: 0x00000000 sdhci: Power: 0x00000000 | Blk gap: 0x00000000 sdhci: Wake-up: 0x00000000 | Clock: 0x00000003 sdhci: Timeout: 0x00000000 | Int stat: 0x00000000 sdhci: Int enab: 0x00000000 | Sig enab: 0x00000000 sdhci: AC12 err: 0x00000000 | Slot int: 0x00000000 sdhci: Caps: 0x322dc8b2 | Caps_1: 0x00008007 sdhci: Cmd: 0x00000000 | Max curr: 0x00000000 sdhci: Host ctl2: 0x00000000 sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000 sdhci: =========================================== Fixes: a5c1f3e55c99 ("mmc: mmc: do not use CMD13 to get status after...") Signed-off-by: Ritesh Harjani Signed-off-by: Georgi Djakov Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index cc4274a..1f59f00 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -410,6 +410,52 @@ retry: return rc; } +static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, + unsigned int uhs) +{ + struct mmc_host *mmc = host->mmc; + u16 ctrl_2; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + switch (uhs) { + case MMC_TIMING_UHS_SDR12: + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + break; + case MMC_TIMING_UHS_SDR25: + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + break; + case MMC_TIMING_UHS_SDR50: + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + break; + case MMC_TIMING_MMC_HS200: + case MMC_TIMING_UHS_SDR104: + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + break; + } + + /* + * When clock frequency is less than 100MHz, the feedback clock must be + * provided and DLL must not be used so that tuning can be skipped. To + * provide feedback clock, the mode selection can be any value less + * than 3'b011 in bits [2:0] of HOST CONTROL2 register. + */ + if (host->clock <= 100000000 && + (uhs == MMC_TIMING_MMC_HS400 || + uhs == MMC_TIMING_MMC_HS200 || + uhs == MMC_TIMING_UHS_SDR104)) + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + + dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n", + mmc_hostname(host->mmc), host->clock, uhs, ctrl_2); + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -422,7 +468,7 @@ static const struct sdhci_ops sdhci_msm_ops = { .reset = sdhci_reset, .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = sdhci_msm_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- cgit v0.10.2 From ad81d3871004544297fda38774f20adab3db57f8 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 24 Jun 2016 18:07:14 +0300 Subject: mmc: sdhci-msm: Add support for UHS cards Enabling support for ultra high speed mode cards requires some voltage switching and interaction with the PMIC via a special power IRQ. Add support for this. Signed-off-by: Georgi Djakov Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 1f59f00..8ef44a2a 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -32,6 +32,21 @@ #define CORE_POWER 0x0 #define CORE_SW_RST BIT(7) +#define CORE_PWRCTL_STATUS 0xdc +#define CORE_PWRCTL_MASK 0xe0 +#define CORE_PWRCTL_CLEAR 0xe4 +#define CORE_PWRCTL_CTL 0xe8 +#define CORE_PWRCTL_BUS_OFF BIT(0) +#define CORE_PWRCTL_BUS_ON BIT(1) +#define CORE_PWRCTL_IO_LOW BIT(2) +#define CORE_PWRCTL_IO_HIGH BIT(3) +#define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define REQ_BUS_OFF BIT(0) +#define REQ_BUS_ON BIT(1) +#define REQ_IO_LOW BIT(2) +#define REQ_IO_HIGH BIT(3) +#define INT_MASK 0xf #define MAX_PHASES 16 #define CORE_DLL_LOCK BIT(7) #define CORE_DLL_EN BIT(16) @@ -56,6 +71,7 @@ struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ + int pwr_irq; /* power irq */ struct clk *clk; /* main SD/MMC bus clock */ struct clk *pclk; /* SDHC peripheral bus clock */ struct clk *bus_clk; /* SDHC bus voter clock */ @@ -456,6 +472,39 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } +static void sdhci_msm_voltage_switch(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + u32 irq_status, irq_ack = 0; + + irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + irq_status &= INT_MASK; + + writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) + irq_ack |= CORE_PWRCTL_IO_SUCCESS; + + /* + * The driver has to acknowledge the interrupt, switch voltages and + * report back if it succeded or not to this register. The voltage + * switches are handled by the sdhci core, so just report success. + */ + writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); +} + +static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) +{ + struct sdhci_host *host = (struct sdhci_host *)data; + + sdhci_msm_voltage_switch(host); + + return IRQ_HANDLED; +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -469,6 +518,7 @@ static const struct sdhci_ops sdhci_msm_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, + .voltage_switch = sdhci_msm_voltage_switch, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { @@ -592,6 +642,22 @@ static int sdhci_msm_probe(struct platform_device *pdev) CORE_VENDOR_SPEC_CAPABILITIES0); } + /* Setup IRQ for handling power/voltage tasks with PMIC */ + msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); + if (msm_host->pwr_irq < 0) { + dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n", + msm_host->pwr_irq); + goto clk_disable; + } + + ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, + sdhci_msm_pwr_irq, IRQF_ONESHOT, + dev_name(&pdev->dev), host); + if (ret) { + dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret); + goto clk_disable; + } + ret = sdhci_add_host(host); if (ret) goto clk_disable; -- cgit v0.10.2 From 6fc09244d74ded1c7210fb493a17e7c29dde2010 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 27 Jun 2016 10:39:25 -0700 Subject: mmc: sdhci-of-arasan: Revert: Always power the PHY off/on when clock changes This reverts commit 4ac0d5f245e1 ("mmc: sdhci-of-arasan: Always power the PHY off/on when clock changes"), resolving conflicts with other patches that have come after. It appears that on some boards / with some eMMC devices that the patch is causing problems. Presumably turning the phy off and on again at the wrong time while initially setting up the card is confusing the card, the host, or the PHY. We have lots of power cycles while initially setting up the card because the main sdhci driver often turns off the clock by clearing SDHCI_CLOCK_CARD_EN and then calls host->ops->set_clock() to set the clock again. With all of those, we ended up with lots of power cycles. Presumably the arguments made in the original patch still hold. That is, whenever the card clock is turned off and on again (or changed) we really should wait for the DLL to lock again. However, perhaps it's really not that critical for the lower speeds. It's possible that the right answer here is: * Whenever set_clock() is called we should double-check that the DLL is locked. * Whenever set_clock() is called and we're actually changing clocks we should do a power cycle around that. * When we're doing a power cycle just because the clock changed, we probably shouldn't do quite as many things (maybe don't need to recalibarate, etc). Unfortunately the interaction between SDHCI and the PHY is extremely limited because of the limited PHY API. The PHY does have a reference to the card clock and could theoretically register for notifications, except that our clock is query only (it uses CLK_GET_RATE_NOCACHE) and so can't really be notified about updates. I believe we would need a major redesign of clock handling in SDHCI core to do better than that, or we would need to make our one fake notifications. :( Let's hope that we can eventually get more information from Arasan on how all this should be handled before doing tons more work. Until then, let's get back to a known working state. Note that the rest of the patches in the 150 MHz series should still work fine even without this one. Signed-off-by: Douglas Anderson Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 678f316..e0f193f 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -77,7 +77,6 @@ struct sdhci_arasan_soc_ctl_map { * @host: Pointer to the main SDHCI host structure. * @clk_ahb: Pointer to the AHB clock * @phy: Pointer to the generic phy - * @phy_on: True if the PHY is turned on. * @sdcardclk_hw: Struct for the clock we might provide to a PHY. * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. @@ -87,7 +86,6 @@ struct sdhci_arasan_data { struct sdhci_host *host; struct clk *clk_ahb; struct phy *phy; - bool phy_on; struct clk_hw sdcardclk_hw; struct clk *sdcardclk; @@ -170,10 +168,12 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + bool ctrl_phy = false; - if (sdhci_arasan->phy_on && !IS_ERR(sdhci_arasan->phy)) { - sdhci_arasan->phy_on = false; + if (clock > MMC_HIGH_52_MAX_DTR && (!IS_ERR(sdhci_arasan->phy))) + ctrl_phy = true; + if (ctrl_phy) { spin_unlock_irq(&host->lock); phy_power_off(sdhci_arasan->phy); spin_lock_irq(&host->lock); @@ -181,9 +181,7 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_set_clock(host, clock); - if (host->mmc->actual_clock && !IS_ERR(sdhci_arasan->phy)) { - sdhci_arasan->phy_on = true; - + if (ctrl_phy) { spin_unlock_irq(&host->lock); phy_power_on(sdhci_arasan->phy); spin_lock_irq(&host->lock); @@ -549,6 +547,12 @@ static int sdhci_arasan_probe(struct platform_device *pdev) goto unreg_clk; } + ret = phy_power_on(sdhci_arasan->phy); + if (ret < 0) { + dev_err(&pdev->dev, "phy_power_on err.\n"); + goto err_phy_power; + } + host->mmc_host_ops.hs400_enhanced_strobe = sdhci_arasan_hs400_enhanced_strobe; } @@ -561,6 +565,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev) err_add_host: if (!IS_ERR(sdhci_arasan->phy)) + phy_power_off(sdhci_arasan->phy); +err_phy_power: + if (!IS_ERR(sdhci_arasan->phy)) phy_exit(sdhci_arasan->phy); unreg_clk: sdhci_arasan_unregister_sdclk(&pdev->dev); -- cgit v0.10.2 From 4e2ea670861f2068082da310f25815c7498a41dc Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 27 Jun 2016 10:39:26 -0700 Subject: phy: rockchip-emmc: Be tolerant to card clock of 0 in power on It's possible that there are some reasons to turn the PHY on while the clock is 0. In this case we just won't wait for the DLL to lock. This is a bit of a stopgap until we figure out exactly when we're supposed to wait for the DLL to lock and when we're supposed to power cycle the PHY. Note: this patch should help with suspend/resume where the system will try to turn the PHY back on when the clock is 0. Signed-off-by: Douglas Anderson Reviewed-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 9dce958..a2aa6ac 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -88,15 +88,36 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) unsigned int caldone; unsigned int dllrdy; unsigned int freqsel = PHYCTRL_FREQSEL_200M; + unsigned long rate; unsigned long timeout; - if (rk_phy->emmcclk != NULL) { - unsigned long rate = clk_get_rate(rk_phy->emmcclk); + /* + * Keep phyctrl_pdb and phyctrl_endll low to allow + * initialization of CALIO state M/C DFFs + */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, + PHYCTRL_PDB_MASK, + PHYCTRL_PDB_SHIFT)); + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, + PHYCTRL_ENDLL_MASK, + PHYCTRL_ENDLL_SHIFT)); + + /* Already finish power_off above */ + if (on_off == PHYCTRL_PDB_PWR_OFF) + return 0; + + rate = clk_get_rate(rk_phy->emmcclk); + + if (rate != 0) { unsigned long ideal_rate; unsigned long diff; switch (rate) { - case 0 ... 74999999: + case 1 ... 74999999: ideal_rate = 50000000; freqsel = PHYCTRL_FREQSEL_50M; break; @@ -127,25 +148,6 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) } /* - * Keep phyctrl_pdb and phyctrl_endll low to allow - * initialization of CALIO state M/C DFFs - */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, - PHYCTRL_PDB_MASK, - PHYCTRL_PDB_SHIFT)); - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, - PHYCTRL_ENDLL_MASK, - PHYCTRL_ENDLL_SHIFT)); - - /* Already finish power_off above */ - if (on_off == PHYCTRL_PDB_PWR_OFF) - return 0; - - /* * According to the user manual, calpad calibration * cycle takes more than 2us without the minimal recommended * value, so we may need a little margin here @@ -183,6 +185,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); + + /* + * We turned on the DLL even though the rate was 0 because we the + * clock might be turned on later. ...but we can't wait for the DLL + * to lock when the rate is 0 because it will never lock with no + * input clock. + * + * Technically we should be checking the lock later when the clock + * is turned on, but for now we won't. + */ + if (rate == 0) + return 0; + /* * After enabling analog DLL circuits docs say that we need 10.2 us if * our source clock is at 50 MHz and that lock time scales linearly -- cgit v0.10.2 From 95cc46fee6e177668087f18549c07daec30c16c8 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 27 Jun 2016 10:39:27 -0700 Subject: phy: rockchip-emmc: Wait even longer for the DLL to lock Two times out of 2000 reboots I ran into the error message "rockchip_emmc_phy_power: dllrdy timeout". Presumably there is some corner case where the DLL just takes a little longer to timeout. Let's give it even more time to handle these corner cases. Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index a2aa6ac..fd57345 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -206,8 +206,18 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms * Hopefully we won't be running at 100 kHz, but we should still make * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. We still busy wait here because: + * - In most cases it should be super fast. + * - This is not called lots during normal operation so it shouldn't + * be a power or performance problem to busy wait. We expect it + * only at boot / resume. In both cases, eMMC is probably on the + * critical path so busy waiting a little extra time should be OK. */ - timeout = jiffies + msecs_to_jiffies(10); + timeout = jiffies + msecs_to_jiffies(50); do { udelay(1); -- cgit v0.10.2 From bb4eecf23be259929f9e7d173890a83ae5f87d4c Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 25 Jul 2016 16:48:39 +0800 Subject: mmc: Change the max discard sectors and erase response when HW busy detect When mmc host HW supports busy signalling (using R1B as response), don't use the host->max_busy_timeout as the limitation when deciding the max discard sectors, which we inform the generic BLOCK layer about. Instead, let's use at least one preferred erase size as the max discard sectors. In cases when the host controller supports HW busy signalling and the timeout for the erase operation doesn't exceed the max_busy_timeout, we keep the R1B response, otherwise we prevent the host from doing HW busy detection by converting to a R1 response. Signed-off-by: Baolin Wang Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 94cbf4e..e55cde6 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2067,7 +2067,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, unsigned int to, unsigned int arg) { struct mmc_command cmd = {0}; - unsigned int qty = 0; + unsigned int qty = 0, busy_timeout = 0; + bool use_r1b_resp = false; unsigned long timeout; int err; @@ -2135,8 +2136,22 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_ERASE; cmd.arg = arg; - cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; - cmd.busy_timeout = mmc_erase_timeout(card, arg, qty); + busy_timeout = mmc_erase_timeout(card, arg, qty); + /* + * If the host controller supports busy signalling and the timeout for + * the erase operation does not exceed the max_busy_timeout, we should + * use R1B response. Or we need to prevent the host from doing hw busy + * detection, which is done by converting to a R1 response instead. + */ + if (card->host->max_busy_timeout && + busy_timeout > card->host->max_busy_timeout) { + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + cmd.busy_timeout = busy_timeout; + use_r1b_resp = true; + } + err = mmc_wait_for_cmd(card->host, &cmd, 0); if (err) { pr_err("mmc_erase: erase error %d, status %#x\n", @@ -2148,7 +2163,14 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, if (mmc_host_is_spi(card->host)) goto out; - timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS); + /* + * In case of when R1B + MMC_CAP_WAIT_WHILE_BUSY is used, the polling + * shall be avoided. + */ + if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) + goto out; + + timeout = jiffies + msecs_to_jiffies(busy_timeout); do { memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = MMC_SEND_STATUS; @@ -2328,23 +2350,41 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card, unsigned int arg) { struct mmc_host *host = card->host; - unsigned int max_discard, x, y, qty = 0, max_qty, timeout; + unsigned int max_discard, x, y, qty = 0, max_qty, min_qty, timeout; unsigned int last_timeout = 0; - if (card->erase_shift) + if (card->erase_shift) { max_qty = UINT_MAX >> card->erase_shift; - else if (mmc_card_sd(card)) + min_qty = card->pref_erase >> card->erase_shift; + } else if (mmc_card_sd(card)) { max_qty = UINT_MAX; - else + min_qty = card->pref_erase; + } else { max_qty = UINT_MAX / card->erase_size; + min_qty = card->pref_erase / card->erase_size; + } - /* Find the largest qty with an OK timeout */ + /* + * We should not only use 'host->max_busy_timeout' as the limitation + * when deciding the max discard sectors. We should set a balance value + * to improve the erase speed, and it can not get too long timeout at + * the same time. + * + * Here we set 'card->pref_erase' as the minimal discard sectors no + * matter what size of 'host->max_busy_timeout', but if the + * 'host->max_busy_timeout' is large enough for more discard sectors, + * then we can continue to increase the max discard sectors until we + * get a balance value. + */ do { y = 0; for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) { timeout = mmc_erase_timeout(card, arg, qty + x); - if (timeout > host->max_busy_timeout) + + if (qty + x > min_qty && + timeout > host->max_busy_timeout) break; + if (timeout < last_timeout) break; last_timeout = timeout; -- cgit v0.10.2 From f9900f15b9786a476564f89a49a8b588d2e66604 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 10:31:41 +0200 Subject: mmc: sdhci-pci-core: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS Convert to define the system PM callbacks to be build for CONFIG_PM_SLEEP and use the SET_SYSTEM_SLEEP_PM_OPS. In this way the code becomes cleaner. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index bdda3d0..897cfd2 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1413,8 +1413,7 @@ static const struct sdhci_ops sdhci_pci_ops = { * * \*****************************************************************************/ -#ifdef CONFIG_PM - +#ifdef CONFIG_PM_SLEEP static int sdhci_pci_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -1496,7 +1495,9 @@ static int sdhci_pci_resume(struct device *dev) return 0; } +#endif +#ifdef CONFIG_PM static int sdhci_pci_runtime_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -1562,17 +1563,10 @@ static int sdhci_pci_runtime_resume(struct device *dev) return 0; } - -#else /* CONFIG_PM */ - -#define sdhci_pci_suspend NULL -#define sdhci_pci_resume NULL - -#endif /* CONFIG_PM */ +#endif static const struct dev_pm_ops sdhci_pci_pm_ops = { - .suspend = sdhci_pci_suspend, - .resume = sdhci_pci_resume, + SET_SYSTEM_SLEEP_PM_OPS(sdhci_pci_suspend, sdhci_pci_resume) SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend, sdhci_pci_runtime_resume, NULL) }; -- cgit v0.10.2 From dafed447eb6cce4bfb2aef649ed98e5718d6b751 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 10:39:52 +0200 Subject: mmc: sdhci-acpi: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS By using the SET_SYSTEM_SLEEP_PM_OPS when assigning the system PM callbacks, we can remove some #ifdefs so code becomes a bit cleaner. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 458ffb7..5bf4bcf 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -531,11 +531,6 @@ static int sdhci_acpi_resume(struct device *dev) return sdhci_resume_host(c->host); } -#else - -#define sdhci_acpi_suspend NULL -#define sdhci_acpi_resume NULL - #endif #ifdef CONFIG_PM @@ -559,8 +554,7 @@ static int sdhci_acpi_runtime_resume(struct device *dev) #endif static const struct dev_pm_ops sdhci_acpi_pm_ops = { - .suspend = sdhci_acpi_suspend, - .resume = sdhci_acpi_resume, + SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume) SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, sdhci_acpi_runtime_resume, NULL) }; -- cgit v0.10.2 From 9e48b33691e3466d42c8ad553ac19596a4fed44e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 11:01:48 +0200 Subject: mmc: sdhci-of-esdhc: Simplify code by using SIMPLE_DEV_PM_OPS Let's use the SIMPLE_DEV_PM_OPS macro when declaring/assigning the system PM callbacks, as the code gets simplified. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 3f34d35..239be2f 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -481,7 +481,7 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static u32 esdhc_proctl; static int esdhc_of_suspend(struct device *dev) { @@ -504,16 +504,12 @@ static int esdhc_of_resume(struct device *dev) } return ret; } - -static const struct dev_pm_ops esdhc_pmops = { - .suspend = esdhc_of_suspend, - .resume = esdhc_of_resume, -}; -#define ESDHC_PMOPS (&esdhc_pmops) -#else -#define ESDHC_PMOPS NULL #endif +static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, + esdhc_of_suspend, + esdhc_of_resume); + static const struct sdhci_ops sdhci_esdhc_be_ops = { .read_l = esdhc_be_readl, .read_w = esdhc_be_readw, @@ -657,7 +653,7 @@ static struct platform_driver sdhci_esdhc_driver = { .driver = { .name = "sdhci-esdhc", .of_match_table = sdhci_esdhc_of_match, - .pm = ESDHC_PMOPS, + .pm = &esdhc_of_dev_pm_ops, }, .probe = sdhci_esdhc_probe, .remove = sdhci_pltfm_unregister, -- cgit v0.10.2 From a81ce7723e5b338d8359003fc769d5b9285026b9 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 11:08:41 +0200 Subject: mmc: sdhci-pxav3: Remove non needed #ifdef CONFIG_PM for dev_pm_ops As the SET_SYSTEM_SLEEP_PM_OPS and the SET_RUNTIME_PM_OPS macro deals with the CONFIG_PM options when assigning the callbacks, it becomes redundant to control this when declaring the struct dev_pm_ops. Instead let's always declare it as it simplifies the code. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 3013250..dd1938d 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -583,24 +583,17 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) } #endif -#ifdef CONFIG_PM static const struct dev_pm_ops sdhci_pxav3_pmops = { SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume) SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, sdhci_pxav3_runtime_resume, NULL) }; -#define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops) - -#else -#define SDHCI_PXAV3_PMOPS NULL -#endif - static struct platform_driver sdhci_pxav3_driver = { .driver = { .name = "sdhci-pxav3", .of_match_table = of_match_ptr(sdhci_pxav3_of_match), - .pm = SDHCI_PXAV3_PMOPS, + .pm = &sdhci_pxav3_pmops, }, .probe = sdhci_pxav3_probe, .remove = sdhci_pxav3_remove, -- cgit v0.10.2 From 6b3a194b7c164eab0429c3db58612fa7d02dc5f9 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 11:23:37 +0200 Subject: mmc: sdhci-s3c: Remove non needed #ifdef CONFIG_PM for dev_pm_ops As the SET_SYSTEM_SLEEP_PM_OPS and the SET_RUNTIME_PM_OPS macro deals with the CONFIG_PM options when assigning the callbacks, it becomes redundant to control this when declaring the struct dev_pm_ops. Instead let's always declare it as it simplifies the code. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 70c724b..784c5a8 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -714,19 +714,12 @@ static int sdhci_s3c_runtime_resume(struct device *dev) } #endif -#ifdef CONFIG_PM static const struct dev_pm_ops sdhci_s3c_pmops = { SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume) SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume, NULL) }; -#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops) - -#else -#define SDHCI_S3C_PMOPS NULL -#endif - #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { .no_divider = true, @@ -765,7 +758,7 @@ static struct platform_driver sdhci_s3c_driver = { .driver = { .name = "s3c-sdhci", .of_match_table = of_match_ptr(sdhci_s3c_dt_match), - .pm = SDHCI_S3C_PMOPS, + .pm = &sdhci_s3c_pmops, }, }; -- cgit v0.10.2 From ee4cf97c3271df60bf324bd6157ee2c6604fab35 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 11:25:23 +0200 Subject: mmc: sdhci-sirf: Remove non needed #ifdef CONFIG_PM* for dev_pm_ops The SIMPLE_DEV_PM_OPS macro deals with the CONFIG_PM options when assigning the PM callbacks, thus it's not needed to control this when using the macro. By removing the non needed #ifdef, the code becomes a bit cleaner. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 34866f6..5d06863 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -260,9 +260,9 @@ static int sdhci_sirf_resume(struct device *dev) return sdhci_resume_host(host); } +#endif static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume); -#endif static const struct of_device_id sdhci_sirf_of_match[] = { { .compatible = "sirf,prima2-sdhc" }, @@ -274,9 +274,7 @@ static struct platform_driver sdhci_sirf_driver = { .driver = { .name = "sdhci-sirf", .of_match_table = sdhci_sirf_of_match, -#ifdef CONFIG_PM_SLEEP .pm = &sdhci_sirf_pm_ops, -#endif }, .probe = sdhci_sirf_probe, .remove = sdhci_pltfm_unregister, -- cgit v0.10.2 From 2788ed42cdd755c2e81bb962fed43426ee5aac64 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 11:46:25 +0200 Subject: mmc: sdhci-esdhc-imx: Assign system PM ops within #ifdef CONFIG_PM_SLEEP The system PM callbacks isn't used unless CONFIG_PM_SLEEP is set, thus it triggers a compiler warning about unused functions. Avoid this by changing from CONFIG_PM to CONFIG_PM_SLEEP. Reported-by: Arnd Bergmann Fixes: b70d0b3b5b29 ("mmc: sdhci-esdhc-imx: add esdhc specific suspend resume callback") Cc: Dong Aisheng Signed-off-by: Ulf Hansson Acked-by: Dong Aisheng diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 2bb326b..f5768a6 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1293,7 +1293,7 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sdhci_esdhc_suspend(struct device *dev) { return sdhci_pltfm_suspend(dev); @@ -1308,7 +1308,9 @@ static int sdhci_esdhc_resume(struct device *dev) return sdhci_pltfm_resume(dev); } +#endif +#ifdef CONFIG_PM static int sdhci_esdhc_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); -- cgit v0.10.2 From 3e3274ab9ff37f691bb192df164c2c295a563d3c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 12:17:14 +0200 Subject: mmc: sdhci-esdhc-imx: Use common sdhci_suspend|resume_host() To prepare to make the sdhci_pltfm_suspend|resume() static functions, move sdhci-esdhc-imx over to use the sdhci_suspend|resume_host(). Signed-off-by: Ulf Hansson Acked-by: Dong Aisheng diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index f5768a6..99e0b33 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1296,7 +1296,9 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int sdhci_esdhc_suspend(struct device *dev) { - return sdhci_pltfm_suspend(dev); + struct sdhci_host *host = dev_get_drvdata(dev); + + return sdhci_suspend_host(host); } static int sdhci_esdhc_resume(struct device *dev) @@ -1306,7 +1308,7 @@ static int sdhci_esdhc_resume(struct device *dev) /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); - return sdhci_pltfm_resume(dev); + return sdhci_resume_host(host); } #endif -- cgit v0.10.2 From 21b8fe0f070239d7a654310616f866eab0060328 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 12:25:38 +0200 Subject: mmc: sdhci-pltfm: Make sdhci_pltfm_suspend|resume() static There are no users left of these exported APIs, so let's make them static. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 64f287a..7b1aacc 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -216,21 +216,19 @@ int sdhci_pltfm_unregister(struct platform_device *pdev) EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister); #ifdef CONFIG_PM -int sdhci_pltfm_suspend(struct device *dev) +static 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); -int sdhci_pltfm_resume(struct device *dev) +static 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 d38053b..6c99210 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -110,8 +110,6 @@ 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 v0.10.2 From 2b33099996a2272cd32f34cf59de8998d4dfda1b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 12:48:04 +0200 Subject: mmc: sdhci-pltfm: Convert to use the SET_SYSTEM_SLEEP_PM_OPS Move the system PM callbacks within #ifdef CONFIG_PM_SLEEP as to avoid them being build when not used. This also allows us to use the SET_SYSTEM_SLEEP_PM_OPS macro which simplifies the code. Within this context it also makes sense to move the declaration of the struct sdhci_pltfm_pmops, outside the #ifdef CONFIG_PM as the SET_SYSTEM_SLEEP_PM_OPS deals with this. This further simplifies the code. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 7b1aacc..1d17dcf 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -215,7 +215,7 @@ int sdhci_pltfm_unregister(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sdhci_pltfm_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -229,13 +229,12 @@ static int sdhci_pltfm_resume(struct device *dev) return sdhci_resume_host(host); } +#endif const struct dev_pm_ops sdhci_pltfm_pmops = { - .suspend = sdhci_pltfm_suspend, - .resume = sdhci_pltfm_resume, + SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume) }; EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops); -#endif /* CONFIG_PM */ static int __init sdhci_pltfm_drv_init(void) { diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 6c99210..0883a2a 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -109,11 +109,7 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host) return (void *)host->private; } -#ifdef CONFIG_PM extern const struct dev_pm_ops sdhci_pltfm_pmops; #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops) -#else -#define SDHCI_PLTFM_PMOPS NULL -#endif #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ -- cgit v0.10.2 From fa243f645af7116dbc157e48e9b2c0a305e2598e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 13:07:21 +0200 Subject: mmc: sdhci-pltfm: Drop define for SDHCI_PLTFM_PMOPS Due to previous changes this define has no longer a purpose. Instead move the sdhci-pltfm drivers over to use the exported struct sdhci_pltfm_pmops. Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 1d64712..e5c634b 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -326,7 +326,7 @@ err_pltfm_free: static struct platform_driver sdhci_bcm_kona_driver = { .driver = { .name = "sdhci-kona", - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, .of_match_table = sdhci_bcm_kona_of_match, }, .probe = sdhci_bcm_kona_probe, diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 59f2923..bd286db 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -101,7 +101,7 @@ static int sdhci_cns3xxx_probe(struct platform_device *pdev) static struct platform_driver sdhci_cns3xxx_driver = { .driver = { .name = "sdhci-cns3xxx", - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_cns3xxx_probe, .remove = sdhci_pltfm_unregister, diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 407c21f..de9f960 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table); static struct platform_driver sdhci_dove_driver = { .driver = { .name = "sdhci-dove", - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, .of_match_table = sdhci_dove_of_match_table, }, .probe = sdhci_dove_probe, diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 53abecc..7262466 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -260,7 +260,7 @@ static struct platform_driver sdhci_iproc_driver = { .driver = { .name = "sdhci-iproc", .of_match_table = sdhci_iproc_of_match, - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_iproc_probe, .remove = sdhci_pltfm_unregister, diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 4079a96..ac00c5e 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -85,7 +85,7 @@ static struct platform_driver sdhci_hlwd_driver = { .driver = { .name = "sdhci-hlwd", .of_match_table = sdhci_hlwd_of_match, - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_hlwd_probe, .remove = sdhci_pltfm_unregister, diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 0883a2a..3280f20 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -110,6 +110,5 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host) } extern const struct dev_pm_ops sdhci_pltfm_pmops; -#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops) #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 1d8dd35..347eae2 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -252,7 +252,7 @@ static struct platform_driver sdhci_pxav2_driver = { .driver = { .name = "sdhci-pxav2", .of_match_table = of_match_ptr(sdhci_pxav2_of_match), - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_pxav2_probe, .remove = sdhci_pxav2_remove, diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index bd11998..1e93dc4 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -483,7 +483,7 @@ static struct platform_driver sdhci_tegra_driver = { .driver = { .name = "sdhci-tegra", .of_match_table = sdhci_tegra_dt_match, - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_tegra_probe, .remove = sdhci_pltfm_unregister, diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index 983b8b3..111b66f 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -222,7 +222,7 @@ static struct platform_driver sdhci_f_sdh30_driver = { .driver = { .name = "f_sdh30", .of_match_table = f_sdh30_dt_ids, - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_pltfm_pmops, }, .probe = sdhci_f_sdh30_probe, .remove = sdhci_f_sdh30_remove, -- cgit v0.10.2 From 27f4bf7d74547ab15474c773f7231dff44ccb2a5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 26 Jul 2016 01:16:59 +0200 Subject: mmc: rtsx_pci: Use the provided busy timeout from the mmc core The rtsx_pci driver is using a fixed 3s timeout for R1B responses, which in some cases isn't suffient. For example, erase/discard requests may require longer timeouts. Instead of always using a fixed timeout, let's use the per request calculated busy timeout from the mmc core. Cc: Micky Ching Signed-off-by: Ulf Hansson Tested-by: Mauro Santos diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 9313748..6158b96 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -244,7 +244,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, stat_idx = sd_status_index(rsp_type); if (rsp_type == SD_RSP_TYPE_R1b) - timeout = 3000; + timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000; if (cmd->opcode == SD_SWITCH_VOLTAGE) { err = rtsx_pci_write_register(pcr, SD_BUS_STAT, -- cgit v0.10.2 From 9bce7fd6f8348e889465971b61efc0245eb6449e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 26 Jul 2016 01:37:31 +0200 Subject: mmc: rtsx_pci: Enable MMC_CAP_ERASE to allow erase/discard/trim requests Cc: Micky Ching Signed-off-by: Ulf Hansson Tested-by: Mauro Santos diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 6158b96..40f957e 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1360,7 +1360,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | - MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; + MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE; mmc->max_current_330 = 400; mmc->max_current_180 = 800; -- cgit v0.10.2 From 6ea6257945188ff7f5d1670d5adc964ac78c590c Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Tue, 26 Jul 2016 22:31:06 +0530 Subject: mmc: rtsx_pci: Remove deprecated create_singlethread_workqueue The workqueue "workq" provides support for sd/mmc async request, which makes next request do dma_map_sg() while previous request transferring data. The workqueue has a single workitem(&host->work) and hence doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in rtsx_pci_sdmmc_drv_remove() to ensure that there are no pending tasks while disconnecting the driver. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 40f957e..396c9b7 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -38,7 +38,6 @@ struct realtek_pci_sdmmc { struct rtsx_pcr *pcr; struct mmc_host *mmc; struct mmc_request *mrq; - struct workqueue_struct *workq; #define SDMMC_WORKQ_NAME "rtsx_pci_sdmmc_workq" struct work_struct work; @@ -885,7 +884,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data)) host->using_cookie = sd_pre_dma_transfer(host, data, false); - queue_work(host->workq, &host->work); + schedule_work(&host->work); } static int sd_set_bus_width(struct realtek_pci_sdmmc *host, @@ -1404,11 +1403,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) return -ENOMEM; host = mmc_priv(mmc); - host->workq = create_singlethread_workqueue(SDMMC_WORKQ_NAME); - if (!host->workq) { - mmc_free_host(mmc); - return -ENOMEM; - } host->pcr = pcr; host->mmc = mmc; host->pdev = pdev; @@ -1462,9 +1456,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) mmc_remove_host(mmc); host->eject = true; - flush_workqueue(host->workq); - destroy_workqueue(host->workq); - host->workq = NULL; + flush_work(&host->work); mmc_free_host(mmc); -- cgit v0.10.2