]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'mmc/mmc-next'
authorThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:36:35 +0000 (14:36 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:36:35 +0000 (14:36 +0200)
34 files changed:
Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
arch/sh/boards/mach-ecovec24/setup.c
drivers/mmc/core/bus.c
drivers/mmc/core/core.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/bfin_sdh.c
drivers/mmc/host/dw_mmc-exynos.c
drivers/mmc/host/dw_mmc-pltfm.c
drivers/mmc/host/dw_mmc-socfpga.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/mvsdio.c
drivers/mmc/host/rtsx_pci_sdmmc.c
drivers/mmc/host/sdhci-bcm-kona.c
drivers/mmc/host/sdhci-bcm2835.c
drivers/mmc/host/sdhci-dove.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci-pxav3.c
drivers/mmc/host/sdhci-s3c.c
drivers/mmc/host/sdhci-sirf.c
drivers/mmc/host/sdhci-spear.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/wmt-sdmmc.c
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/sdhci.h
include/linux/platform_data/mmc-esdhc-imx.h

index 1dd622546d06b711bf262358b387b4bb9af3f68a..9046ba06c47ab63570f99769455dcfeeea020e10 100644 (file)
@@ -12,6 +12,11 @@ Required properties:
 Optional properties:
 - fsl,cd-controller : Indicate to use controller internal card detection
 - fsl,wp-controller : Indicate to use controller internal write protection
+- fsl,delay-line : Specify the number of delay cells for override mode.
+  This is used to set the clock delay for DLL(Delay Line) on override mode
+  to select a proper data sampling window in case the clock quality is not good
+  due to signal path is too long on the board. Please refer to eSDHC/uSDHC
+  chapter, DLL (Delay Line) section in RM for details.
 
 Examples:
 
index 066a78b034ca8589e4de4d31183075768496f81a..8f3f13315358028f20a5a79720e6a27266caf2be 100644 (file)
@@ -52,6 +52,9 @@ Optional properties:
   is specified and the ciu clock is specified then we'll try to set the ciu
   clock to this at probe time.
 
+* clock-freq-min-max: Minimum and Maximum clock frequency for card output
+  clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
+
 * num-slots: specifies the number of slots supported by the controller.
   The number of physical slots actually used could be equal or less than the
   value specified by num-slots. If this property is not specified, the value
@@ -66,6 +69,10 @@ Optional properties:
 
 * supports-highspeed: Enables support for high speed cards (up to 50MHz)
 
+* caps2-mmc-hs200-1_8v: Supports mmc HS200 SDR 1.8V mode
+
+* caps2-mmc-hs200-1_2v: Supports mmc HS200 SDR 1.2V mode
+
 * broken-cd: as documented in mmc core bindings.
 
 * vmmc-supply: The phandle to the regulator to use for vmmc.  If this is
@@ -93,8 +100,10 @@ board specific portions as listed below.
 
        dwmmc0@12200000 {
                clock-frequency = <400000000>;
+               clock-freq-min-max = <400000 200000000>;
                num-slots = <1>;
                supports-highspeed;
+               caps2-mmc-hs200-1_8v;
                broken-cd;
                fifo-depth = <0x80>;
                card-detect-delay = <200>;
index 1fa8be4097715360363c41f21043d184f68517fa..122f737a901fbad4e22de06652e6eb9b04abdfc8 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/mmc/sh_mmcif.h>
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/mtd/physmap.h>
+#include <linux/mfd/tmio.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
index 704bf66f58733a036bf79bfc4ef585f29acb7626..cdca8a70da38bfcc9cace78dde9afe563afaef24 100644 (file)
@@ -340,7 +340,7 @@ int mmc_add_card(struct mmc_card *card)
                break;
        }
 
-       if (mmc_sd_card_uhs(card) &&
+       if (mmc_card_uhs(card) &&
                (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
                uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
 
index bf18b6bfce487b2dd6a77917344c1514dc1aa7a8..006ead2fb7010a4c2ab1f8681ddc61945a54acfa 100644 (file)
@@ -917,31 +917,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
 
 EXPORT_SYMBOL(__mmc_claim_host);
 
-/**
- *     mmc_try_claim_host - try exclusively to claim a host
- *     @host: mmc host to claim
- *
- *     Returns %1 if the host is claimed, %0 otherwise.
- */
-int mmc_try_claim_host(struct mmc_host *host)
-{
-       int claimed_host = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->lock, flags);
-       if (!host->claimed || host->claimer == current) {
-               host->claimed = 1;
-               host->claimer = current;
-               host->claim_cnt += 1;
-               claimed_host = 1;
-       }
-       spin_unlock_irqrestore(&host->lock, flags);
-       if (host->ops->enable && claimed_host && host->claim_cnt == 1)
-               host->ops->enable(host);
-       return claimed_host;
-}
-EXPORT_SYMBOL(mmc_try_claim_host);
-
 /**
  *     mmc_release_host - release a host
  *     @host: mmc host to release
index ef183483d5b67934440cd8dc1a8d6910467e6a61..37f7d7059ab740f3db7d52d4129a7ddb916abfa4 100644 (file)
 
 #define MMC_OPS_TIMEOUT_MS     (10 * 60 * 1000) /* 10 minute timeout */
 
+static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
+                                   bool ignore_crc)
+{
+       int err;
+       struct mmc_command cmd = {0};
+
+       BUG_ON(!card);
+       BUG_ON(!card->host);
+
+       cmd.opcode = MMC_SEND_STATUS;
+       if (!mmc_host_is_spi(card->host))
+               cmd.arg = card->rca << 16;
+       cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+       if (ignore_crc)
+               cmd.flags &= ~MMC_RSP_CRC;
+
+       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+       if (err)
+               return err;
+
+       /* NOTE: callers are required to understand the difference
+        * between "native" and SPI format status words!
+        */
+       if (status)
+               *status = cmd.resp[0];
+
+       return 0;
+}
+
+int mmc_send_status(struct mmc_card *card, u32 *status)
+{
+       return __mmc_send_status(card, status, false);
+}
+
 static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
 {
        int err;
@@ -380,6 +414,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
        struct mmc_command cmd = {0};
        unsigned long timeout;
        u32 status;
+       bool ignore_crc = false;
 
        BUG_ON(!card);
        BUG_ON(!card->host);
@@ -408,10 +443,18 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
        if (!use_busy_signal)
                return 0;
 
-       /* Must check status to be sure of no errors */
+       /*
+        * Must check status to be sure of no errors
+        * If CMD13 is to check the busy completion of the timing change,
+        * disable the check of CRC error.
+        */
+       if (index == EXT_CSD_HS_TIMING &&
+           !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+               ignore_crc = true;
+
        timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
        do {
-               err = mmc_send_status(card, &status);
+               err = __mmc_send_status(card, &status, ignore_crc);
                if (err)
                        return err;
                if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
@@ -449,32 +492,6 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 }
 EXPORT_SYMBOL_GPL(mmc_switch);
 
-int mmc_send_status(struct mmc_card *card, u32 *status)
-{
-       int err;
-       struct mmc_command cmd = {0};
-
-       BUG_ON(!card);
-       BUG_ON(!card->host);
-
-       cmd.opcode = MMC_SEND_STATUS;
-       if (!mmc_host_is_spi(card->host))
-               cmd.arg = card->rca << 16;
-       cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
-
-       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
-       if (err)
-               return err;
-
-       /* NOTE: callers are required to understand the difference
-        * between "native" and SPI format status words!
-        */
-       if (status)
-               *status = cmd.resp[0];
-
-       return 0;
-}
-
 static int
 mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
                  u8 len)
index 69e438ee043e6f8a9c151d3447f6a9e418544231..92c18779d47e8153facc80a2450010ae199475ae 100644 (file)
@@ -589,6 +589,13 @@ static void atmci_timeout_timer(unsigned long data)
        if (host->mrq->cmd->data) {
                host->mrq->cmd->data->error = -ETIMEDOUT;
                host->data = NULL;
+               /*
+                * With some SDIO modules, sometimes DMA transfer hangs. If
+                * stop_transfer() is not called then the DMA request is not
+                * removed, following ones are queued and never computed.
+                */
+               if (host->state == STATE_DATA_XFER)
+                       host->stop_transfer(host);
        } else {
                host->mrq->cmd->error = -ETIMEDOUT;
                host->cmd = NULL;
@@ -1803,12 +1810,14 @@ static void atmci_tasklet_func(unsigned long priv)
                        if (unlikely(status)) {
                                host->stop_transfer(host);
                                host->data = NULL;
-                               if (status & ATMCI_DTOE) {
-                                       data->error = -ETIMEDOUT;
-                               } else if (status & ATMCI_DCRCE) {
-                                       data->error = -EILSEQ;
-                               } else {
-                                       data->error = -EIO;
+                               if (data) {
+                                       if (status & ATMCI_DTOE) {
+                                               data->error = -ETIMEDOUT;
+                                       } else if (status & ATMCI_DCRCE) {
+                                               data->error = -EILSEQ;
+                                       } else {
+                                               data->error = -EIO;
+                                       }
                                }
                        }
 
index 94fae2f1baaf4de39f9c0e929f35c973b8d4b0b5..e62c5bc7fa31d5d01d72438daa7df19fd2a9d06f 100644 (file)
@@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                /* Disable 4 bit SDIO */
                cfg &= ~SD4E;
        }
+       bfin_write_SDH_CFG(cfg);
 
        host->power_mode = ios->power_mode;
 #ifndef RSI_BLKSZ
@@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                cfg &= ~SD_CMD_OD;
 # endif
 
-
        if (ios->power_mode != MMC_POWER_OFF)
                cfg |= PWR_ON;
        else
@@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                clk_ctl |= CLK_E;
                host->clk_div = clk_div;
                bfin_write_SDH_CLK_CTL(clk_ctl);
-
        } else
                sdh_stop_clock(host);
 
index 6a1fa2110a057b42d2a79cfb3407bb0f8fb8f3b8..d42e664e78bcdf32bc927682961ac4ae6992d027 100644 (file)
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/mmc.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/slab.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
 #define SDMMC_CLKSEL_TIMING(x, y, z)   (SDMMC_CLKSEL_CCLK_SAMPLE(x) |  \
                                        SDMMC_CLKSEL_CCLK_DRIVE(y) |    \
                                        SDMMC_CLKSEL_CCLK_DIVIDER(z))
+#define SDMMC_CLKSEL_WAKEUP_INT                BIT(11)
 
 #define EXYNOS4210_FIXED_CIU_CLK_DIV   2
 #define EXYNOS4412_FIXED_CIU_CLK_DIV   4
 
+/* Block number in eMMC */
+#define DWMCI_BLOCK_NUM                0xFFFFFFFF
+
+#define SDMMC_EMMCP_BASE       0x1000
+#define SDMMC_MPSECURITY       (SDMMC_EMMCP_BASE + 0x0010)
+#define SDMMC_MPSBEGIN0                (SDMMC_EMMCP_BASE + 0x0200)
+#define SDMMC_MPSEND0          (SDMMC_EMMCP_BASE + 0x0204)
+#define SDMMC_MPSCTRL0         (SDMMC_EMMCP_BASE + 0x020C)
+
+/* SMU control bits */
+#define DWMCI_MPSCTRL_SECURE_READ_BIT          BIT(7)
+#define DWMCI_MPSCTRL_SECURE_WRITE_BIT         BIT(6)
+#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT      BIT(5)
+#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT     BIT(4)
+#define DWMCI_MPSCTRL_USE_FUSE_KEY             BIT(3)
+#define DWMCI_MPSCTRL_ECB_MODE                 BIT(2)
+#define DWMCI_MPSCTRL_ENCRYPTION               BIT(1)
+#define DWMCI_MPSCTRL_VALID                    BIT(0)
+
+#define EXYNOS_CCLKIN_MIN      50000000        /* unit: HZ */
+
 /* Variations in Exynos specific dw-mshc controller */
 enum dw_mci_exynos_type {
        DW_MCI_TYPE_EXYNOS4210,
        DW_MCI_TYPE_EXYNOS4412,
        DW_MCI_TYPE_EXYNOS5250,
        DW_MCI_TYPE_EXYNOS5420,
+       DW_MCI_TYPE_EXYNOS5420_SMU,
 };
 
 /* Exynos implementation specific driver private data */
@@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data {
        u8                              ciu_div;
        u32                             sdr_timing;
        u32                             ddr_timing;
+       u32                             cur_speed;
 };
 
 static struct dw_mci_exynos_compatible {
@@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible {
        }, {
                .compatible     = "samsung,exynos5420-dw-mshc",
                .ctrl_type      = DW_MCI_TYPE_EXYNOS5420,
+       }, {
+               .compatible     = "samsung,exynos5420-dw-mshc-smu",
+               .ctrl_type      = DW_MCI_TYPE_EXYNOS5420_SMU,
        },
 };
 
 static int dw_mci_exynos_priv_init(struct dw_mci *host)
 {
-       struct dw_mci_exynos_priv_data *priv;
-       int idx;
-
-       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               dev_err(host->dev, "mem alloc failed for private data\n");
-               return -ENOMEM;
-       }
+       struct dw_mci_exynos_priv_data *priv = host->priv;
 
-       for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
-               if (of_device_is_compatible(host->dev->of_node,
-                                       exynos_compat[idx].compatible))
-                       priv->ctrl_type = exynos_compat[idx].ctrl_type;
+       if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) {
+               mci_writel(host, MPSBEGIN0, 0);
+               mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
+               mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
+                          DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
+                          DWMCI_MPSCTRL_VALID |
+                          DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
        }
 
-       host->priv = priv;
        return 0;
 }
 
 static int dw_mci_exynos_setup_clock(struct dw_mci *host)
 {
        struct dw_mci_exynos_priv_data *priv = host->priv;
+       unsigned long rate = clk_get_rate(host->ciu_clk);
 
-       if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 ||
-               priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420)
-               host->bus_hz /= (priv->ciu_div + 1);
-       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
-               host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
-       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
-               host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
+       host->bus_hz = rate / (priv->ciu_div + 1);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dw_mci_exynos_suspend(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+
+       return dw_mci_suspend(host);
+}
+
+static int dw_mci_exynos_resume(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+
+       dw_mci_exynos_priv_init(host);
+       return dw_mci_resume(host);
+}
+
+/**
+ * dw_mci_exynos_resume_noirq - Exynos-specific resume code
+ *
+ * On exynos5420 there is a silicon errata that will sometimes leave the
+ * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
+ * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
+ * interrupts from going off constantly.
+ *
+ * We run this code on all exynos variants because it doesn't hurt.
+ */
+
+static int dw_mci_exynos_resume_noirq(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+       u32 clksel;
+
+       clksel = mci_readl(host, CLKSEL);
+       if (clksel & SDMMC_CLKSEL_WAKEUP_INT)
+               mci_writel(host, CLKSEL, clksel);
 
        return 0;
 }
+#else
+#define dw_mci_exynos_suspend          NULL
+#define dw_mci_exynos_resume           NULL
+#define dw_mci_exynos_resume_noirq     NULL
+#endif /* CONFIG_PM_SLEEP */
 
 static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
 {
@@ -121,40 +183,206 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 {
        struct dw_mci_exynos_priv_data *priv = host->priv;
+       unsigned int wanted = ios->clock;
+       unsigned long actual;
+       u8 div = priv->ciu_div + 1;
 
-       if (ios->timing == MMC_TIMING_UHS_DDR50)
+       if (ios->timing == MMC_TIMING_UHS_DDR50) {
                mci_writel(host, CLKSEL, priv->ddr_timing);
-       else
+               /* Should be double rate for DDR mode */
+               if (ios->bus_width == MMC_BUS_WIDTH_8)
+                       wanted <<= 1;
+       } else {
                mci_writel(host, CLKSEL, priv->sdr_timing);
+       }
+
+       /* Don't care if wanted clock is zero */
+       if (!wanted)
+               return;
+
+       /* Guaranteed minimum frequency for cclkin */
+       if (wanted < EXYNOS_CCLKIN_MIN)
+               wanted = EXYNOS_CCLKIN_MIN;
+
+       if (wanted != priv->cur_speed) {
+               int ret = clk_set_rate(host->ciu_clk, wanted * div);
+               if (ret)
+                       dev_warn(host->dev,
+                               "failed to set clk-rate %u error: %d\n",
+                                wanted * div, ret);
+               actual = clk_get_rate(host->ciu_clk);
+               host->bus_hz = actual / div;
+               priv->cur_speed = wanted;
+               host->current_speed = 0;
+       }
 }
 
 static int dw_mci_exynos_parse_dt(struct dw_mci *host)
 {
-       struct dw_mci_exynos_priv_data *priv = host->priv;
+       struct dw_mci_exynos_priv_data *priv;
        struct device_node *np = host->dev->of_node;
        u32 timing[2];
        u32 div = 0;
+       int idx;
        int ret;
 
-       of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
-       priv->ciu_div = div;
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(host->dev, "mem alloc failed for private data\n");
+               return -ENOMEM;
+       }
+
+       for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
+               if (of_device_is_compatible(np, exynos_compat[idx].compatible))
+                       priv->ctrl_type = exynos_compat[idx].ctrl_type;
+       }
+
+       if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+               priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
+       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
+               priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
+       else {
+               of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
+               priv->ciu_div = div;
+       }
 
        ret = of_property_read_u32_array(np,
                        "samsung,dw-mshc-sdr-timing", timing, 2);
        if (ret)
                return ret;
 
-       priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
-
        ret = of_property_read_u32_array(np,
                        "samsung,dw-mshc-ddr-timing", timing, 2);
        if (ret)
                return ret;
 
+       priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
        priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+       host->priv = priv;
        return 0;
 }
 
+static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
+{
+       return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
+}
+
+static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
+{
+       u32 clksel;
+       clksel = mci_readl(host, CLKSEL);
+       clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
+       mci_writel(host, CLKSEL, clksel);
+}
+
+static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
+{
+       u32 clksel;
+       u8 sample;
+
+       clksel = mci_readl(host, CLKSEL);
+       sample = (clksel + 1) & 0x7;
+       clksel = (clksel & ~0x7) | sample;
+       mci_writel(host, CLKSEL, clksel);
+       return sample;
+}
+
+static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
+{
+       const u8 iter = 8;
+       u8 __c;
+       s8 i, loc = -1;
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0xc7) == 0xc7) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0x83) == 0x83) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+out:
+       return loc;
+}
+
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+                                       struct dw_mci_tuning_data *tuning_data)
+{
+       struct dw_mci *host = slot->host;
+       struct mmc_host *mmc = slot->mmc;
+       const u8 *blk_pattern = tuning_data->blk_pattern;
+       u8 *blk_test;
+       unsigned int blksz = tuning_data->blksz;
+       u8 start_smpl, smpl, candiates = 0;
+       s8 found = -1;
+       int ret = 0;
+
+       blk_test = kmalloc(blksz, GFP_KERNEL);
+       if (!blk_test)
+               return -ENOMEM;
+
+       start_smpl = dw_mci_exynos_get_clksmpl(host);
+
+       do {
+               struct mmc_request mrq = {NULL};
+               struct mmc_command cmd = {0};
+               struct mmc_command stop = {0};
+               struct mmc_data data = {0};
+               struct scatterlist sg;
+
+               cmd.opcode = opcode;
+               cmd.arg = 0;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+               stop.opcode = MMC_STOP_TRANSMISSION;
+               stop.arg = 0;
+               stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+               data.blksz = blksz;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+               data.sg = &sg;
+               data.sg_len = 1;
+
+               sg_init_one(&sg, blk_test, blksz);
+               mrq.cmd = &cmd;
+               mrq.stop = &stop;
+               mrq.data = &data;
+               host->mrq = &mrq;
+
+               mci_writel(host, TMOUT, ~0);
+               smpl = dw_mci_exynos_move_next_clksmpl(host);
+
+               mmc_wait_for_req(mmc, &mrq);
+
+               if (!cmd.error && !data.error) {
+                       if (!memcmp(blk_pattern, blk_test, blksz))
+                               candiates |= (1 << smpl);
+               } else {
+                       dev_dbg(host->dev,
+                               "Tuning error: cmd.error:%d, data.error:%d\n",
+                               cmd.error, data.error);
+               }
+       } while (start_smpl != smpl);
+
+       found = dw_mci_exynos_get_best_clksmpl(candiates);
+       if (found >= 0)
+               dw_mci_exynos_set_clksmpl(host, found);
+       else
+               ret = -EIO;
+
+       kfree(blk_test);
+       return ret;
+}
+
 /* Common capabilities of Exynos4/Exynos5 SoC */
 static unsigned long exynos_dwmmc_caps[4] = {
        MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
@@ -171,6 +399,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
        .prepare_command        = dw_mci_exynos_prepare_command,
        .set_ios                = dw_mci_exynos_set_ios,
        .parse_dt               = dw_mci_exynos_parse_dt,
+       .execute_tuning         = dw_mci_exynos_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_exynos_match[] = {
@@ -180,6 +409,8 @@ static const struct of_device_id dw_mci_exynos_match[] = {
                        .data = &exynos_drv_data, },
        { .compatible = "samsung,exynos5420-dw-mshc",
                        .data = &exynos_drv_data, },
+       { .compatible = "samsung,exynos5420-dw-mshc-smu",
+                       .data = &exynos_drv_data, },
        {},
 };
 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
@@ -194,13 +425,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)
        return dw_mci_pltfm_register(pdev, drv_data);
 }
 
+const struct dev_pm_ops dw_mci_exynos_pmops = {
+       SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
+       .resume_noirq = dw_mci_exynos_resume_noirq,
+       .thaw_noirq = dw_mci_exynos_resume_noirq,
+       .restore_noirq = dw_mci_exynos_resume_noirq,
+};
+
 static struct platform_driver dw_mci_exynos_pltfm_driver = {
        .probe          = dw_mci_exynos_probe,
        .remove         = __exit_p(dw_mci_pltfm_remove),
        .driver         = {
                .name           = "dwmmc_exynos",
                .of_match_table = dw_mci_exynos_match,
-               .pm             = &dw_mci_pltfm_pmops,
+               .pm             = &dw_mci_exynos_pmops,
        },
 };
 
index 20897529ea5e10185a3d39864755ccdbeb8fea6b..5c496565529796f06ca4d26b280724376aa6348c 100644 (file)
@@ -39,7 +39,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
 {
        struct dw_mci *host;
        struct resource *regs;
-       int ret;
 
        host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
        if (!host)
@@ -59,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
        if (IS_ERR(host->regs))
                return PTR_ERR(host->regs);
 
-       if (drv_data && drv_data->init) {
-               ret = drv_data->init(host);
-               if (ret)
-                       return ret;
-       }
-
        platform_set_drvdata(pdev, host);
        return dw_mci_probe(host);
 }
index 14b5961a851c43ed3a78e0df34d7be79e3cc0162..3e8e53ae3302b7231c64acaf7de991b6e6861b35 100644 (file)
@@ -38,21 +38,6 @@ struct dw_mci_socfpga_priv_data {
 
 static int dw_mci_socfpga_priv_init(struct dw_mci *host)
 {
-       struct dw_mci_socfpga_priv_data *priv;
-
-       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               dev_err(host->dev, "mem alloc failed for private data\n");
-               return -ENOMEM;
-       }
-
-       priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
-       if (IS_ERR(priv->sysreg)) {
-               dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
-               return PTR_ERR(priv->sysreg);
-       }
-       host->priv = priv;
-
        return 0;
 }
 
@@ -79,12 +64,24 @@ static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
 
 static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
 {
-       struct dw_mci_socfpga_priv_data *priv = host->priv;
+       struct dw_mci_socfpga_priv_data *priv;
        struct device_node *np = host->dev->of_node;
        u32 timing[2];
        u32 div = 0;
        int ret;
 
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(host->dev, "mem alloc failed for private data\n");
+               return -ENOMEM;
+       }
+
+       priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
+       if (IS_ERR(priv->sysreg)) {
+               dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
+               return PTR_ERR(priv->sysreg);
+       }
+
        ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
        if (ret)
                dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
@@ -96,6 +93,7 @@ static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
                return ret;
 
        priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
+       host->priv = priv;
        return 0;
 }
 
@@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
 
-int dw_mci_socfpga_probe(struct platform_device *pdev)
+static int dw_mci_socfpga_probe(struct platform_device *pdev)
 {
        const struct dw_mci_drv_data *drv_data;
        const struct of_device_id *match;
@@ -128,7 +126,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = {
        .remove         = __exit_p(dw_mci_pltfm_remove),
        .driver         = {
                .name           = "dwmmc_socfpga",
-               .of_match_table = of_match_ptr(dw_mci_socfpga_match),
+               .of_match_table = dw_mci_socfpga_match,
                .pm             = &dw_mci_pltfm_pmops,
        },
 };
index 018f365e5ae46c71a6e8a0f2327e9fb8775c900d..0a6a51238b7fa07a50f563d050c66aab34eb98b3 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/irq.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
 #include <linux/mmc/dw_mmc.h>
 #include <linux/bitops.h>
 #include <linux/regulator/consumer.h>
@@ -50,6 +51,9 @@
 #define DW_MCI_RECV_STATUS     2
 #define DW_MCI_DMA_THRESHOLD   16
 
+#define DW_MCI_FREQ_MAX        200000000       /* unit: HZ */
+#define DW_MCI_FREQ_MIN        400000          /* unit: HZ */
+
 #ifdef CONFIG_MMC_DW_IDMAC
 #define IDMAC_INT_CLR          (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
                                 SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
@@ -76,42 +80,39 @@ struct idmac_desc {
 };
 #endif /* CONFIG_MMC_DW_IDMAC */
 
-/**
- * struct dw_mci_slot - MMC slot state
- * @mmc: The mmc_host representing this slot.
- * @host: The MMC controller this slot is using.
- * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
- * @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
- * @ctype: Card type for this slot.
- * @mrq: mmc_request currently being processed or waiting to be
- *     processed, or NULL when the slot is idle.
- * @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.
- * @flags: Random state bits associated with the slot.
- * @id: Number of this slot.
- * @last_detect_state: Most recently observed card detect state.
- */
-struct dw_mci_slot {
-       struct mmc_host         *mmc;
-       struct dw_mci           *host;
-
-       int                     quirks;
-       int                     wp_gpio;
-
-       u32                     ctype;
-
-       struct mmc_request      *mrq;
-       struct list_head        queue_node;
+static const u8 tuning_blk_pattern_4bit[] = {
+       0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
+       0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
+       0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
+       0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
+       0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
+       0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
+       0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
+       0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
+};
 
-       unsigned int            clock;
-       unsigned long           flags;
-#define DW_MMC_CARD_PRESENT    0
-#define DW_MMC_CARD_NEED_INIT  1
-       int                     id;
-       int                     last_detect_state;
+static const u8 tuning_blk_pattern_8bit[] = {
+       0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+       0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
+       0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
+       0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
+       0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
+       0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
+       0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
+       0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
+       0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
+       0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
+       0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
+       0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
+       0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
+       0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
+       0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
+       0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
 };
 
+static inline bool dw_mci_fifo_reset(struct dw_mci *host);
+static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host);
+
 #if defined(CONFIG_DEBUG_FS)
 static int dw_mci_req_show(struct seq_file *s, void *v)
 {
@@ -249,10 +250,15 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 
        cmdr = cmd->opcode;
 
-       if (cmdr == MMC_STOP_TRANSMISSION)
+       if (cmd->opcode == MMC_STOP_TRANSMISSION ||
+           cmd->opcode == MMC_GO_IDLE_STATE ||
+           cmd->opcode == MMC_GO_INACTIVE_STATE ||
+           (cmd->opcode == SD_IO_RW_DIRECT &&
+            ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT))
                cmdr |= SDMMC_CMD_STOP;
        else
-               cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+               if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
+                       cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
 
        if (cmd->flags & MMC_RSP_PRESENT) {
                /* We expect a response, so set this bit */
@@ -279,6 +285,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
        return cmdr;
 }
 
+static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
+{
+       struct mmc_command *stop;
+       u32 cmdr;
+
+       if (!cmd->data)
+               return 0;
+
+       stop = &host->stop_abort;
+       cmdr = cmd->opcode;
+       memset(stop, 0, sizeof(struct mmc_command));
+
+       if (cmdr == MMC_READ_SINGLE_BLOCK ||
+           cmdr == MMC_READ_MULTIPLE_BLOCK ||
+           cmdr == MMC_WRITE_BLOCK ||
+           cmdr == MMC_WRITE_MULTIPLE_BLOCK) {
+               stop->opcode = MMC_STOP_TRANSMISSION;
+               stop->arg = 0;
+               stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+       } else if (cmdr == SD_IO_RW_EXTENDED) {
+               stop->opcode = SD_IO_RW_DIRECT;
+               stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) |
+                            ((cmd->arg >> 28) & 0x7);
+               stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
+       } else {
+               return 0;
+       }
+
+       cmdr = stop->opcode | SDMMC_CMD_STOP |
+               SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP;
+
+       return cmdr;
+}
+
 static void dw_mci_start_command(struct dw_mci *host,
                                 struct mmc_command *cmd, u32 cmd_flags)
 {
@@ -293,9 +333,10 @@ static void dw_mci_start_command(struct dw_mci *host,
        mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
 }
 
-static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
 {
-       dw_mci_start_command(host, data->stop, host->stop_cmdr);
+       struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort;
+       dw_mci_start_command(host, stop, host->stop_cmdr);
 }
 
 /* DMA interface functions */
@@ -304,10 +345,10 @@ static void dw_mci_stop_dma(struct dw_mci *host)
        if (host->using_dma) {
                host->dma_ops->stop(host);
                host->dma_ops->cleanup(host);
-       } else {
-               /* Data transfer was stopped by the interrupt handler */
-               set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
        }
+
+       /* Data transfer was stopped by the interrupt handler */
+       set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
 }
 
 static int dw_mci_get_dma_dir(struct mmc_data *data)
@@ -331,6 +372,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
                                     dw_mci_get_dma_dir(data));
 }
 
+static void dw_mci_idmac_reset(struct dw_mci *host)
+{
+       u32 bmod = mci_readl(host, BMOD);
+       /* Software reset of DMA */
+       bmod |= SDMMC_IDMAC_SWRESET;
+       mci_writel(host, BMOD, bmod);
+}
+
 static void dw_mci_idmac_stop_dma(struct dw_mci *host)
 {
        u32 temp;
@@ -344,6 +393,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)
        /* Stop the IDMAC running */
        temp = mci_readl(host, BMOD);
        temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB);
+       temp |= SDMMC_IDMAC_SWRESET;
        mci_writel(host, BMOD, temp);
 }
 
@@ -435,7 +485,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
        p->des3 = host->sg_dma;
        p->des0 = IDMAC_DES0_ER;
 
-       mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
+       dw_mci_idmac_reset(host);
 
        /* Mask out interrupts - get Tx & Rx complete only */
        mci_writel(host, IDSTS, IDMAC_INT_CLR);
@@ -532,6 +582,78 @@ static void dw_mci_post_req(struct mmc_host *mmc,
        data->host_cookie = 0;
 }
 
+static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
+{
+#ifdef CONFIG_MMC_DW_IDMAC
+       unsigned int blksz = data->blksz;
+       const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
+       u32 fifo_width = 1 << host->data_shift;
+       u32 blksz_depth = blksz / fifo_width, fifoth_val;
+       u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
+       int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1;
+
+       tx_wmark = (host->fifo_depth) / 2;
+       tx_wmark_invers = host->fifo_depth - tx_wmark;
+
+       /*
+        * MSIZE is '1',
+        * if blksz is not a multiple of the FIFO width
+        */
+       if (blksz % fifo_width) {
+               msize = 0;
+               rx_wmark = 1;
+               goto done;
+       }
+
+       do {
+               if (!((blksz_depth % mszs[idx]) ||
+                    (tx_wmark_invers % mszs[idx]))) {
+                       msize = idx;
+                       rx_wmark = mszs[idx] - 1;
+                       break;
+               }
+       } while (--idx > 0);
+       /*
+        * If idx is '0', it won't be tried
+        * Thus, initial values are uesed
+        */
+done:
+       fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark);
+       mci_writel(host, FIFOTH, fifoth_val);
+#endif
+}
+
+static void dw_mci_ctrl_rd_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));
+
+       if (host->timing != MMC_TIMING_MMC_HS200 &&
+           host->timing != MMC_TIMING_UHS_SDR104)
+               goto disable;
+
+       blksz_depth = blksz / (1 << host->data_shift);
+       fifo_depth = host->fifo_depth;
+
+       if (blksz_depth > fifo_depth)
+               goto disable;
+
+       /*
+        * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz'
+        * If (blksz_depth) <  (fifo_depth >> 1), should be thld_size = blksz
+        * Currently just choose blksz.
+        */
+       thld_size = blksz;
+       mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1));
+       return;
+
+disable:
+       mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0));
+}
+
 static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 {
        int sg_len;
@@ -556,6 +678,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
                 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
                 sg_len);
 
+       /*
+        * Decide the MSIZE and RX/TX Watermark.
+        * If current block size is same with previous size,
+        * no need to update fifoth.
+        */
+       if (host->prev_blksz != data->blksz)
+               dw_mci_adjust_fifoth(host, data);
+
        /* Enable the DMA interface */
        temp = mci_readl(host, CTRL);
        temp |= SDMMC_CTRL_DMA_ENABLE;
@@ -581,10 +711,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;
-       else
+               dw_mci_ctrl_rd_thld(host, data);
+       } else {
                host->dir_status = DW_MCI_SEND_STATUS;
+       }
 
        if (dw_mci_submit_data_dma(host, data)) {
                int flags = SG_MITER_ATOMIC;
@@ -606,6 +738,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
                temp = mci_readl(host, CTRL);
                temp &= ~SDMMC_CTRL_DMA_ENABLE;
                mci_writel(host, CTRL, temp);
+
+               /*
+                * Use the initial fifoth_val for PIO mode.
+                * If next issued data may be transfered by DMA mode,
+                * prev_blksz should be invalidated.
+                */
+               mci_writel(host, FIFOTH, host->fifoth_val);
+               host->prev_blksz = 0;
+       } else {
+               /*
+                * Keep the current block size.
+                * It will be used to decide whether to update
+                * fifoth register next time.
+                */
+               host->prev_blksz = data->blksz;
        }
 }
 
@@ -632,24 +779,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
 static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
 {
        struct dw_mci *host = slot->host;
+       unsigned int clock = slot->clock;
        u32 div;
        u32 clk_en_a;
 
-       if (slot->clock != host->current_speed || force_clkinit) {
-               div = host->bus_hz / slot->clock;
-               if (host->bus_hz % slot->clock && host->bus_hz > slot->clock)
+       if (!clock) {
+               mci_writel(host, CLKENA, 0);
+               mci_send_cmd(slot,
+                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+       } else if (clock != host->current_speed || force_clkinit) {
+               div = host->bus_hz / clock;
+               if (host->bus_hz % clock && host->bus_hz > clock)
                        /*
                         * move the + 1 after the divide to prevent
                         * over-clocking the card.
                         */
                        div += 1;
 
-               div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0;
+               div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
 
-               dev_info(&slot->mmc->class_dev,
-                        "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
-                        " div = %d)\n", slot->id, host->bus_hz, slot->clock,
-                        div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+               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);
 
                /* disable clock */
                mci_writel(host, CLKENA, 0);
@@ -676,9 +830,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                mci_send_cmd(slot,
                             SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
 
-               host->current_speed = slot->clock;
+               /* keep the clock with reflecting clock dividor */
+               slot->__clk_old = clock << div;
        }
 
+       host->current_speed = clock;
+
        /* Set the current slot bus width */
        mci_writel(host, CTYPE, (slot->ctype << slot->id));
 }
@@ -700,7 +857,9 @@ static void __dw_mci_start_request(struct dw_mci *host,
 
        host->pending_events = 0;
        host->completed_events = 0;
+       host->cmd_status = 0;
        host->data_status = 0;
+       host->dir_status = 0;
 
        data = cmd->data;
        if (data) {
@@ -724,6 +883,8 @@ static void __dw_mci_start_request(struct dw_mci *host,
 
        if (mrq->stop)
                host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+       else
+               host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
 }
 
 static void dw_mci_start_request(struct dw_mci *host,
@@ -806,14 +967,13 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                regs &= ~((0x1 << slot->id) << 16);
 
        mci_writel(slot->host, UHS_REG, regs);
+       slot->host->timing = ios->timing;
 
-       if (ios->clock) {
-               /*
-                * Use mirror of ios->clock to prevent race with mmc
-                * core ios update when finding the minimum.
-                */
-               slot->clock = ios->clock;
-       }
+       /*
+        * Use mirror of ios->clock to prevent race with mmc
+        * core ios update when finding the minimum.
+        */
+       slot->clock = ios->clock;
 
        if (drv_data && drv_data->set_ios)
                drv_data->set_ios(slot->host, ios);
@@ -939,6 +1099,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
        }
 }
 
+static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct dw_mci_slot *slot = mmc_priv(mmc);
+       struct dw_mci *host = slot->host;
+       const struct dw_mci_drv_data *drv_data = host->drv_data;
+       struct dw_mci_tuning_data tuning_data;
+       int err = -ENOSYS;
+
+       if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+               if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
+                       tuning_data.blk_pattern = tuning_blk_pattern_8bit;
+                       tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
+               } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+                       tuning_data.blk_pattern = tuning_blk_pattern_4bit;
+                       tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
+               } else {
+                       return -EINVAL;
+               }
+       } else if (opcode == MMC_SEND_TUNING_BLOCK) {
+               tuning_data.blk_pattern = tuning_blk_pattern_4bit;
+               tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
+       } else {
+               dev_err(host->dev,
+                       "Undefined command(%d) for tuning\n", opcode);
+               return -EINVAL;
+       }
+
+       if (drv_data && drv_data->execute_tuning)
+               err = drv_data->execute_tuning(slot, opcode, &tuning_data);
+       return err;
+}
+
 static const struct mmc_host_ops dw_mci_ops = {
        .request                = dw_mci_request,
        .pre_req                = dw_mci_pre_req,
@@ -947,6 +1139,7 @@ static const struct mmc_host_ops dw_mci_ops = {
        .get_ro                 = dw_mci_get_ro,
        .get_cd                 = dw_mci_get_cd,
        .enable_sdio_irq        = dw_mci_enable_sdio_irq,
+       .execute_tuning         = dw_mci_execute_tuning,
 };
 
 static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@@ -978,7 +1171,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
        spin_lock(&host->lock);
 }
 
-static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
+static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
 {
        u32 status = host->cmd_status;
 
@@ -1012,12 +1205,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd
                /* newer ip versions need a delay between retries */
                if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
                        mdelay(20);
+       }
 
-               if (cmd->data) {
-                       dw_mci_stop_dma(host);
-                       host->data = NULL;
+       return cmd->error;
+}
+
+static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
+{
+       u32 status = host->data_status;
+
+       if (status & DW_MCI_DATA_ERROR_FLAGS) {
+               if (status & SDMMC_INT_DRTO) {
+                       data->error = -ETIMEDOUT;
+               } else if (status & SDMMC_INT_DCRC) {
+                       data->error = -EILSEQ;
+               } else if (status & SDMMC_INT_EBE) {
+                       if (host->dir_status ==
+                               DW_MCI_SEND_STATUS) {
+                               /*
+                                * No data CRC status was returned.
+                                * The number of bytes transferred
+                                * will be exaggerated in PIO mode.
+                                */
+                               data->bytes_xfered = 0;
+                               data->error = -ETIMEDOUT;
+                       } else if (host->dir_status ==
+                                       DW_MCI_RECV_STATUS) {
+                               data->error = -EIO;
+                       }
+               } else {
+                       /* SDMMC_INT_SBE is included */
+                       data->error = -EIO;
                }
+
+               dev_err(host->dev, "data error, status 0x%08x\n", status);
+
+               /*
+                * After an error, there may be data lingering
+                * in the FIFO
+                */
+               dw_mci_fifo_reset(host);
+       } else {
+               data->bytes_xfered = data->blocks * data->blksz;
+               data->error = 0;
        }
+
+       return data->error;
 }
 
 static void dw_mci_tasklet_func(unsigned long priv)
@@ -1025,14 +1258,16 @@ static void dw_mci_tasklet_func(unsigned long priv)
        struct dw_mci *host = (struct dw_mci *)priv;
        struct mmc_data *data;
        struct mmc_command *cmd;
+       struct mmc_request *mrq;
        enum dw_mci_state state;
        enum dw_mci_state prev_state;
-       u32 status, ctrl;
+       unsigned int err;
 
        spin_lock(&host->lock);
 
        state = host->state;
        data = host->data;
+       mrq = host->mrq;
 
        do {
                prev_state = state;
@@ -1049,16 +1284,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        cmd = host->cmd;
                        host->cmd = NULL;
                        set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
-                       dw_mci_command_complete(host, cmd);
-                       if (cmd == host->mrq->sbc && !cmd->error) {
+                       err = dw_mci_command_complete(host, cmd);
+                       if (cmd == mrq->sbc && !err) {
                                prev_state = state = STATE_SENDING_CMD;
                                __dw_mci_start_request(host, host->cur_slot,
-                                                      host->mrq->cmd);
+                                                      mrq->cmd);
                                goto unlock;
                        }
 
-                       if (!host->mrq->data || cmd->error) {
-                               dw_mci_request_end(host, host->mrq);
+                       if (cmd->data && err) {
+                               dw_mci_stop_dma(host);
+                               send_stop_abort(host, data);
+                               state = STATE_SENDING_STOP;
+                               break;
+                       }
+
+                       if (!cmd->data || err) {
+                               dw_mci_request_end(host, mrq);
                                goto unlock;
                        }
 
@@ -1069,8 +1311,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        if (test_and_clear_bit(EVENT_DATA_ERROR,
                                               &host->pending_events)) {
                                dw_mci_stop_dma(host);
-                               if (data->stop)
-                                       send_stop_cmd(host, data);
+                               send_stop_abort(host, data);
                                state = STATE_DATA_ERROR;
                                break;
                        }
@@ -1090,60 +1331,27 @@ static void dw_mci_tasklet_func(unsigned long priv)
 
                        host->data = NULL;
                        set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
-                       status = host->data_status;
-
-                       if (status & DW_MCI_DATA_ERROR_FLAGS) {
-                               if (status & SDMMC_INT_DRTO) {
-                                       data->error = -ETIMEDOUT;
-                               } else if (status & SDMMC_INT_DCRC) {
-                                       data->error = -EILSEQ;
-                               } else if (status & SDMMC_INT_EBE &&
-                                          host->dir_status ==
-                                                       DW_MCI_SEND_STATUS) {
-                                       /*
-                                        * No data CRC status was returned.
-                                        * The number of bytes transferred will
-                                        * be exaggerated in PIO mode.
-                                        */
-                                       data->bytes_xfered = 0;
-                                       data->error = -ETIMEDOUT;
-                               } else {
-                                       dev_err(host->dev,
-                                               "data FIFO error "
-                                               "(status=%08x)\n",
-                                               status);
-                                       data->error = -EIO;
-                               }
-                               /*
-                                * After an error, there may be data lingering
-                                * in the FIFO, so reset it - doing so
-                                * generates a block interrupt, hence setting
-                                * the scatter-gather pointer to NULL.
-                                */
-                               sg_miter_stop(&host->sg_miter);
-                               host->sg = NULL;
-                               ctrl = mci_readl(host, CTRL);
-                               ctrl |= SDMMC_CTRL_FIFO_RESET;
-                               mci_writel(host, CTRL, ctrl);
-                       } else {
-                               data->bytes_xfered = data->blocks * data->blksz;
-                               data->error = 0;
-                       }
+                       err = dw_mci_data_complete(host, data);
 
-                       if (!data->stop) {
-                               dw_mci_request_end(host, host->mrq);
-                               goto unlock;
-                       }
+                       if (!err) {
+                               if (!data->stop || mrq->sbc) {
+                                       if (mrq->sbc)
+                                               data->stop->error = 0;
+                                       dw_mci_request_end(host, mrq);
+                                       goto unlock;
+                               }
 
-                       if (host->mrq->sbc && !data->error) {
-                               data->stop->error = 0;
-                               dw_mci_request_end(host, host->mrq);
-                               goto unlock;
+                               /* stop command for open-ended transfer*/
+                               if (data->stop)
+                                       send_stop_abort(host, data);
                        }
 
+                       /*
+                        * If err has non-zero,
+                        * stop-abort command has been already issued.
+                        */
                        prev_state = state = STATE_SENDING_STOP;
-                       if (!data->error)
-                               send_stop_cmd(host, data);
+
                        /* fall through */
 
                case STATE_SENDING_STOP:
@@ -1151,9 +1359,19 @@ static void dw_mci_tasklet_func(unsigned long priv)
                                                &host->pending_events))
                                break;
 
+                       /* CMD error in data command */
+                       if (mrq->cmd->error && mrq->data)
+                               dw_mci_fifo_reset(host);
+
                        host->cmd = NULL;
-                       dw_mci_command_complete(host, host->mrq->stop);
-                       dw_mci_request_end(host, host->mrq);
+                       host->data = NULL;
+
+                       if (mrq->stop)
+                               dw_mci_command_complete(host, mrq->stop);
+                       else
+                               host->cmd_status = 0;
+
+                       dw_mci_request_end(host, mrq);
                        goto unlock;
 
                case STATE_DATA_ERROR:
@@ -1697,7 +1915,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                struct mmc_host *mmc = slot->mmc;
                struct mmc_request *mrq;
                int present;
-               u32 ctrl;
 
                present = dw_mci_get_cd(mmc);
                while (present != slot->last_detect_state) {
@@ -1736,11 +1953,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                                        case STATE_DATA_ERROR:
                                                if (mrq->data->error == -EINPROGRESS)
                                                        mrq->data->error = -ENOMEDIUM;
-                                               if (!mrq->stop)
-                                                       break;
                                                /* fall through */
                                        case STATE_SENDING_STOP:
-                                               mrq->stop->error = -ENOMEDIUM;
+                                               if (mrq->stop)
+                                                       mrq->stop->error = -ENOMEDIUM;
                                                break;
                                        }
 
@@ -1763,23 +1979,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                        if (present == 0) {
                                clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
 
-                               /*
-                                * Clear down the FIFO - doing so generates a
-                                * block interrupt, hence setting the
-                                * scatter-gather pointer to NULL.
-                                */
-                               sg_miter_stop(&host->sg_miter);
-                               host->sg = NULL;
-
-                               ctrl = mci_readl(host, CTRL);
-                               ctrl |= SDMMC_CTRL_FIFO_RESET;
-                               mci_writel(host, CTRL, ctrl);
-
+                               /* Clear down the FIFO */
+                               dw_mci_fifo_reset(host);
 #ifdef CONFIG_MMC_DW_IDMAC
-                               ctrl = mci_readl(host, BMOD);
-                               /* Software reset of DMA */
-                               ctrl |= SDMMC_IDMAC_SWRESET;
-                               mci_writel(host, BMOD, ctrl);
+                               dw_mci_idmac_reset(host);
 #endif
 
                        }
@@ -1901,6 +2104,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        struct dw_mci_slot *slot;
        const struct dw_mci_drv_data *drv_data = host->drv_data;
        int ctrl_id, ret;
+       u32 freq[2];
        u8 bus_width;
 
        mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
@@ -1916,8 +2120,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
 
        mmc->ops = &dw_mci_ops;
-       mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
-       mmc->f_max = host->bus_hz;
+       if (of_property_read_u32_array(host->dev->of_node,
+                                      "clock-freq-min-max", freq, 2)) {
+               mmc->f_min = DW_MCI_FREQ_MIN;
+               mmc->f_max = DW_MCI_FREQ_MAX;
+       } else {
+               mmc->f_min = freq[0];
+               mmc->f_max = freq[1];
+       }
 
        if (host->pdata->get_ocr)
                mmc->ocr_avail = host->pdata->get_ocr(id);
@@ -1964,9 +2174,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
        }
 
-       if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
-               mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
-
        if (host->pdata->blk_settings) {
                mmc->max_segs = host->pdata->blk_settings->max_segs;
                mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
@@ -2008,12 +2215,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        /* Card initially undetected */
        slot->last_detect_state = 0;
 
-       /*
-        * Card may have been plugged in prior to boot so we
-        * need to run the detect tasklet
-        */
-       queue_work(host->card_workqueue, &host->card_work);
-
        return 0;
 
 err_setup_bus:
@@ -2074,36 +2275,57 @@ no_dma:
        return;
 }
 
-static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
 {
        unsigned long timeout = jiffies + msecs_to_jiffies(500);
-       unsigned int ctrl;
+       u32 ctrl;
 
-       mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
-                               SDMMC_CTRL_DMA_RESET));
+       ctrl = mci_readl(host, CTRL);
+       ctrl |= reset;
+       mci_writel(host, CTRL, ctrl);
 
        /* wait till resets clear */
        do {
                ctrl = mci_readl(host, CTRL);
-               if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
-                             SDMMC_CTRL_DMA_RESET)))
+               if (!(ctrl & reset))
                        return true;
        } while (time_before(jiffies, timeout));
 
-       dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+       dev_err(host->dev,
+               "Timeout resetting block (ctrl reset %#x)\n",
+               ctrl & reset);
 
        return false;
 }
 
+static inline bool dw_mci_fifo_reset(struct dw_mci *host)
+{
+       /*
+        * Reseting generates a block interrupt, hence setting
+        * the scatter-gather pointer to NULL.
+        */
+       if (host->sg) {
+               sg_miter_stop(&host->sg_miter);
+               host->sg = NULL;
+       }
+
+       return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
+}
+
+static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host)
+{
+       return dw_mci_ctrl_reset(host,
+                                SDMMC_CTRL_FIFO_RESET |
+                                SDMMC_CTRL_RESET |
+                                SDMMC_CTRL_DMA_RESET);
+}
+
 #ifdef CONFIG_OF
 static struct dw_mci_of_quirks {
        char *quirk;
        int id;
 } of_quirks[] = {
        {
-               .quirk  = "supports-highspeed",
-               .id     = DW_MCI_QUIRK_HIGHSPEED,
-       }, {
                .quirk  = "broken-cd",
                .id     = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
        },
@@ -2158,6 +2380,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
        if (of_find_property(np, "enable-sdio-wakeup", NULL))
                pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
 
+       if (of_find_property(np, "supports-highspeed", NULL))
+               pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+
+       if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL))
+               pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+
+       if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
+               pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
+
        return pdata;
 }
 
@@ -2221,6 +2452,15 @@ int dw_mci_probe(struct dw_mci *host)
                host->bus_hz = clk_get_rate(host->ciu_clk);
        }
 
+       if (drv_data && drv_data->init) {
+               ret = drv_data->init(host);
+               if (ret) {
+                       dev_err(host->dev,
+                               "implementation specific init failed\n");
+                       goto err_clk_ciu;
+               }
+       }
+
        if (drv_data && drv_data->setup_clock) {
                ret = drv_data->setup_clock(host);
                if (ret) {
@@ -2287,7 +2527,7 @@ int dw_mci_probe(struct dw_mci *host)
        }
 
        /* Reset all blocks */
-       if (!mci_wait_reset(host->dev, host))
+       if (!dw_mci_ctrl_all_reset(host))
                return -ENODEV;
 
        host->dma_ops = host->pdata->dma_ops;
@@ -2317,8 +2557,8 @@ int dw_mci_probe(struct dw_mci *host)
                fifo_size = host->pdata->fifo_depth;
        }
        host->fifo_depth = fifo_size;
-       host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
-                       ((fifo_size/2) << 0));
+       host->fifoth_val =
+               SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
        mci_writel(host, FIFOTH, host->fifoth_val);
 
        /* disable clock to CIU */
@@ -2493,7 +2733,7 @@ int dw_mci_resume(struct dw_mci *host)
                }
        }
 
-       if (!mci_wait_reset(host->dev, host)) {
+       if (!dw_mci_ctrl_all_reset(host)) {
                ret = -ENODEV;
                return ret;
        }
@@ -2501,8 +2741,15 @@ int dw_mci_resume(struct dw_mci *host)
        if (host->use_dma && host->dma_ops->init)
                host->dma_ops->init(host);
 
-       /* Restore the old value at FIFOTH register */
+       /*
+        * Restore the initial value at FIFOTH register
+        * And Invalidate the prev_blksz with zero
+        */
        mci_writel(host, FIFOTH, host->fifoth_val);
+       host->prev_blksz = 0;
+
+       /* Put in max timeout */
+       mci_writel(host, TMOUT, 0xFFFFFFFF);
 
        mci_writel(host, RINTSTS, 0xFFFFFFFF);
        mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
index 81b29941c5b9b1ddaff3f313cab7bab9c57c6e46..6bf24ab917e6453a22d09689befd58e40fa96efd 100644 (file)
@@ -53,6 +53,7 @@
 #define SDMMC_IDINTEN          0x090
 #define SDMMC_DSCADDR          0x094
 #define SDMMC_BUFADDR          0x098
+#define SDMMC_CDTHRCTL         0x100
 #define SDMMC_DATA(x)          (x)
 
 /*
 #define SDMMC_CMD_INDX(n)              ((n) & 0x1F)
 /* Status register defines */
 #define SDMMC_GET_FCNT(x)              (((x)>>17) & 0x1FFF)
+/* FIFOTH register defines */
+#define SDMMC_SET_FIFOTH(m, r, t)      (((m) & 0x7) << 28 | \
+                                        ((r) & 0xFFF) << 16 | \
+                                        ((t) & 0xFFF))
 /* Internal DMAC interrupt defines */
 #define SDMMC_IDMAC_INT_AI             BIT(9)
 #define SDMMC_IDMAC_INT_NI             BIT(8)
 #define SDMMC_IDMAC_SWRESET            BIT(0)
 /* Version ID register define */
 #define SDMMC_GET_VERID(x)             ((x) & 0xFFFF)
+/* Card read threshold */
+#define SDMMC_SET_RD_THLD(v, x)                (((v) & 0x1FFF) << 16 | (x))
 
 /* Register access macros */
 #define mci_readl(dev, reg)                    \
@@ -183,6 +190,52 @@ extern int dw_mci_suspend(struct dw_mci *host);
 extern int dw_mci_resume(struct dw_mci *host);
 #endif
 
+/**
+ * struct dw_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
+ * @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
+ * @ctype: Card type for this slot.
+ * @mrq: mmc_request currently being processed or waiting to be
+ *     processed, or NULL when the slot is idle.
+ * @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.
+ * @last_detect_state: Most recently observed card detect state.
+ */
+struct dw_mci_slot {
+       struct mmc_host         *mmc;
+       struct dw_mci           *host;
+
+       int                     quirks;
+       int                     wp_gpio;
+
+       u32                     ctype;
+
+       struct mmc_request      *mrq;
+       struct list_head        queue_node;
+
+       unsigned int            clock;
+       unsigned int            __clk_old;
+
+       unsigned long           flags;
+#define DW_MMC_CARD_PRESENT    0
+#define DW_MMC_CARD_NEED_INIT  1
+       int                     id;
+       int                     last_detect_state;
+};
+
+struct dw_mci_tuning_data {
+       const u8 *blk_pattern;
+       unsigned int blksz;
+};
+
 /**
  * dw_mci driver data - dw-mshc implementation specific driver data.
  * @caps: mmc subsystem specified capabilities of the controller(s).
@@ -203,5 +256,7 @@ struct dw_mci_drv_data {
        void            (*prepare_command)(struct dw_mci *host, u32 *cmdr);
        void            (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
        int             (*parse_dt)(struct dw_mci *host);
+       int             (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode,
+                                       struct dw_mci_tuning_data *tuning_data);
 };
 #endif /* _DW_MMC_H_ */
index 06c5b0b28ebc99f547b88fb9be47a43196872ff8..a592407d2de616d85cb442d6ead34e72f5fc6649 100644 (file)
@@ -775,9 +775,9 @@ static int __init mvsd_probe(struct platform_device *pdev)
 
        spin_lock_init(&host->lock);
 
-       host->base = devm_request_and_ioremap(&pdev->dev, r);
-       if (!host->base) {
-               ret = -ENOMEM;
+       host->base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(host->base)) {
+               ret = PTR_ERR(host->base);
                goto out;
        }
 
index 375a880e0c5fb899efa5a2de9b77543fcf66d12d..e2ab12473f976cbe7d9bac3de7e71d4d229970f2 100644 (file)
@@ -364,7 +364,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
        struct mmc_host *mmc = host->mmc;
        struct mmc_card *card = mmc->card;
        struct mmc_data *data = mrq->data;
-       int uhs = mmc_sd_card_uhs(card);
+       int uhs = mmc_card_uhs(card);
        int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
        u8 cfg2, trans_mode;
        int err;
index 85472d3fd37f76b7a4b24b4a6f42033ff9dec472..9e3652c39694532be6e7236d820ad74b3c8ff0af 100644 (file)
@@ -120,7 +120,7 @@ static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
  * Software emulation of the SD card insertion/removal. Set insert=1 for insert
  * and insert=0 for removal. The card detection is done by GPIO. For Broadcom
  * IP to function properly the bit 0 of CORESTAT register needs to be set/reset
- * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
+ * to generate the CD IRQ handled in sdhci.c which schedules card_detect_work.
  */
 static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
 {
@@ -316,19 +316,7 @@ err_pltfm_free:
 
 static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
 {
-       struct sdhci_host *host = platform_get_drvdata(pdev);
-       int dead;
-       u32 scratch;
-
-       dead = 0;
-       scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
-       if (scratch == (u32)-1)
-               dead = 1;
-       sdhci_remove_host(host, dead);
-
-       sdhci_free_host(host);
-
-       return 0;
+       return sdhci_pltfm_unregister(pdev);
 }
 
 static struct platform_driver sdhci_bcm_kona_driver = {
index 36fa2df0466007f7b618aa9986ca9aa098199b32..f6d8d67c545f882678ea40bca4f2525a77bf28bf 100644 (file)
@@ -178,13 +178,7 @@ err:
 
 static int bcm2835_sdhci_remove(struct platform_device *pdev)
 {
-       struct sdhci_host *host = platform_get_drvdata(pdev);
-       int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
-
-       sdhci_remove_host(host, dead);
-       sdhci_pltfm_free(pdev);
-
-       return 0;
+       return sdhci_pltfm_unregister(pdev);
 }
 
 static const struct of_device_id bcm2835_sdhci_of_match[] = {
index 8424839660f844b748f7960a46a5e9cf5df88e56..45c7f25dac748e638018d3b0293c169b8e86c48e 100644 (file)
@@ -39,7 +39,7 @@ static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data)
 {
        struct sdhci_host *host = data;
 
-       tasklet_schedule(&host->card_tasklet);
+       schedule_schedule(&host->card_detect_work);
        return IRQ_HANDLED;
 }
 
@@ -149,8 +149,8 @@ static int sdhci_dove_probe(struct platform_device *pdev)
                goto err_sdhci_add;
 
        /*
-        * We must request the IRQ after sdhci_add_host(), as the tasklet only
-        * gets setup in sdhci_add_host() and we oops.
+        * We must request the IRQ after sdhci_add_host(), as the workqueue
+        * only gets setup in sdhci_add_host() and we oops.
         */
        if (gpio_is_valid(priv->gpio_cd)) {
                ret = request_irq(gpio_to_irq(priv->gpio_cd),
index abc8cf01e6e3317ecc3e95d32aa66236def768e9..1ce35117b8a6dab021cfa5201a385f5c5da99967 100644 (file)
 /* VENDOR SPEC register */
 #define ESDHC_VENDOR_SPEC              0xc0
 #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK  (1 << 1)
+#define  ESDHC_VENDOR_SPEC_VSELECT     (1 << 1)
+#define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON        (1 << 8)
 #define ESDHC_WTMK_LVL                 0x44
 #define ESDHC_MIX_CTRL                 0x48
+#define  ESDHC_MIX_CTRL_DDREN          (1 << 3)
 #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_FBCLK_SEL      (1 << 25)
 /* Bits 3 and 6 are not SDHCI standard definitions */
 #define  ESDHC_MIX_CTRL_SDHCI_MASK     0xb7
 
+/* dll control register */
+#define ESDHC_DLL_CTRL                 0x60
+#define ESDHC_DLL_OVERRIDE_VAL_SHIFT   9
+#define ESDHC_DLL_OVERRIDE_EN_SHIFT    8
+
+/* tune control register */
+#define ESDHC_TUNE_CTRL_STATUS         0x68
+#define  ESDHC_TUNE_CTRL_STEP          1
+#define  ESDHC_TUNE_CTRL_MIN           0
+#define  ESDHC_TUNE_CTRL_MAX           ((1 << 7) - 1)
+
+#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_BLOCK_PATTERN_LEN 64
+
+/* pinctrl state */
+#define ESDHC_PINCTRL_STATE_100MHZ     "state_100mhz"
+#define ESDHC_PINCTRL_STATE_200MHZ     "state_200mhz"
+
 /*
  * Our interpretation of the SDHCI_HOST_CONTROL register
  */
  * As a result, the TC flag is not asserted and SW  received timeout
  * exeception. Bit1 of Vendor Spec registor is used to fix it.
  */
-#define ESDHC_FLAG_MULTIBLK_NO_INT     (1 << 1)
-
-enum imx_esdhc_type {
-       IMX25_ESDHC,
-       IMX35_ESDHC,
-       IMX51_ESDHC,
-       IMX53_ESDHC,
-       IMX6Q_USDHC,
+#define ESDHC_FLAG_MULTIBLK_NO_INT     BIT(1)
+/*
+ * The flag enables the workaround for ESDHC errata ENGcm07207 which
+ * affects i.MX25 and i.MX35.
+ */
+#define ESDHC_FLAG_ENGCM07207          BIT(2)
+/*
+ * The flag tells that the ESDHC controller is an USDHC block that is
+ * integrated on the i.MX6 series.
+ */
+#define ESDHC_FLAG_USDHC               BIT(3)
+/* The IP supports manual tuning process */
+#define ESDHC_FLAG_MAN_TUNING          BIT(4)
+/* The IP supports standard tuning process */
+#define ESDHC_FLAG_STD_TUNING          BIT(5)
+/* The IP has SDHCI_CAPABILITIES_1 register */
+#define ESDHC_FLAG_HAVE_CAP1           BIT(6)
+
+struct esdhc_soc_data {
+       u32 flags;
+};
+
+static struct esdhc_soc_data esdhc_imx25_data = {
+       .flags = ESDHC_FLAG_ENGCM07207,
+};
+
+static struct esdhc_soc_data esdhc_imx35_data = {
+       .flags = ESDHC_FLAG_ENGCM07207,
+};
+
+static struct esdhc_soc_data esdhc_imx51_data = {
+       .flags = 0,
+};
+
+static struct esdhc_soc_data esdhc_imx53_data = {
+       .flags = ESDHC_FLAG_MULTIBLK_NO_INT,
+};
+
+static struct esdhc_soc_data usdhc_imx6q_data = {
+       .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING,
+};
+
+static struct esdhc_soc_data usdhc_imx6sl_data = {
+       .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+                       | ESDHC_FLAG_HAVE_CAP1,
 };
 
 struct pltfm_imx_data {
-       int flags;
        u32 scratchpad;
-       enum imx_esdhc_type devtype;
        struct pinctrl *pinctrl;
+       struct pinctrl_state *pins_default;
+       struct pinctrl_state *pins_100mhz;
+       struct pinctrl_state *pins_200mhz;
+       const struct esdhc_soc_data *socdata;
        struct esdhc_platform_data boarddata;
        struct clk *clk_ipg;
        struct clk *clk_ahb;
@@ -90,25 +157,20 @@ struct pltfm_imx_data {
                MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
                WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
        } multiblock_status;
-
+       u32 uhs_mode;
+       u32 is_ddr;
 };
 
 static struct platform_device_id imx_esdhc_devtype[] = {
        {
                .name = "sdhci-esdhc-imx25",
-               .driver_data = IMX25_ESDHC,
+               .driver_data = (kernel_ulong_t) &esdhc_imx25_data,
        }, {
                .name = "sdhci-esdhc-imx35",
-               .driver_data = IMX35_ESDHC,
+               .driver_data = (kernel_ulong_t) &esdhc_imx35_data,
        }, {
                .name = "sdhci-esdhc-imx51",
-               .driver_data = IMX51_ESDHC,
-       }, {
-               .name = "sdhci-esdhc-imx53",
-               .driver_data = IMX53_ESDHC,
-       }, {
-               .name = "sdhci-usdhc-imx6q",
-               .driver_data = IMX6Q_USDHC,
+               .driver_data = (kernel_ulong_t) &esdhc_imx51_data,
        }, {
                /* sentinel */
        }
@@ -116,38 +178,34 @@ static struct platform_device_id imx_esdhc_devtype[] = {
 MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
 
 static const struct of_device_id imx_esdhc_dt_ids[] = {
-       { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], },
-       { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
-       { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
-       { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
-       { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
+       { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
+       { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
+       { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
+       { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
+       { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
+       { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
 
 static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX25_ESDHC;
-}
-
-static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
-{
-       return data->devtype == IMX35_ESDHC;
+       return data->socdata == &esdhc_imx25_data;
 }
 
-static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
+static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX51_ESDHC;
+       return data->socdata == &esdhc_imx53_data;
 }
 
-static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
+static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX53_ESDHC;
+       return data->socdata == &usdhc_imx6q_data;
 }
 
-static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
+static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
 {
-       return data->devtype == IMX6Q_USDHC;
+       return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
 }
 
 static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
@@ -164,7 +222,21 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
        u32 val = readl(host->ioaddr + reg);
 
+       if (unlikely(reg == SDHCI_PRESENT_STATE)) {
+               u32 fsl_prss = val;
+               /* save the least 20 bits */
+               val = fsl_prss & 0x000FFFFF;
+               /* move dat[0-3] bits */
+               val |= (fsl_prss & 0x0F000000) >> 4;
+               /* move cmd line bit */
+               val |= (fsl_prss & 0x00800000) << 1;
+       }
+
        if (unlikely(reg == SDHCI_CAPABILITIES)) {
+               /* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
+               if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
+                       val &= 0xffff0000;
+
                /* In FSL esdhc IC module, only bit20 is used to indicate the
                 * ADMA2 capability of esdhc, but this bit is messed up on
                 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
@@ -178,6 +250,25 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
                }
        }
 
+       if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
+               if (esdhc_is_usdhc(imx_data)) {
+                       if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
+                               val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
+                       else
+                               /* imx6q/dl does not have cap_1 register, fake one */
+                               val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
+                                       | SDHCI_SUPPORT_SDR50
+                                       | SDHCI_USE_SDR50_TUNING;
+               }
+       }
+
+       if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
+               val = 0;
+               val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
+               val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
+               val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
+       }
+
        if (unlikely(reg == SDHCI_INT_STATUS)) {
                if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
                        val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
@@ -224,7 +315,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
                }
        }
 
-       if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
+       if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
                                && (reg == SDHCI_INT_STATUS)
                                && (val & SDHCI_INT_DATA_END))) {
                        u32 v;
@@ -256,10 +347,12 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       u16 ret = 0;
+       u32 val;
 
        if (unlikely(reg == SDHCI_HOST_VERSION)) {
                reg ^= 2;
-               if (is_imx6q_usdhc(imx_data)) {
+               if (esdhc_is_usdhc(imx_data)) {
                        /*
                         * The usdhc register returns a wrong host version.
                         * Correct it here.
@@ -268,6 +361,30 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
                }
        }
 
+       if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
+               val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               if (val & ESDHC_VENDOR_SPEC_VSELECT)
+                       ret |= SDHCI_CTRL_VDD_180;
+
+               if (esdhc_is_usdhc(imx_data)) {
+                       if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
+                               val = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
+                               /* the std tuning bits is in ACMD12_ERR for imx6sl */
+                               val = readl(host->ioaddr + SDHCI_ACMD12_ERR);
+               }
+
+               if (val & ESDHC_MIX_CTRL_EXE_TUNE)
+                       ret |= SDHCI_CTRL_EXEC_TUNING;
+               if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
+                       ret |= SDHCI_CTRL_TUNED_CLK;
+
+               ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
+               ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+               return ret;
+       }
+
        return readw(host->ioaddr + reg);
 }
 
@@ -275,10 +392,59 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       u32 new_val = 0;
 
        switch (reg) {
+       case SDHCI_CLOCK_CONTROL:
+               new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               if (val & SDHCI_CLOCK_CARD_EN)
+                       new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+               else
+                       new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+                       writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
+               return;
+       case SDHCI_HOST_CONTROL2:
+               new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               if (val & SDHCI_CTRL_VDD_180)
+                       new_val |= ESDHC_VENDOR_SPEC_VSELECT;
+               else
+                       new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
+               writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
+               imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
+               if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
+                       new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       if (val & SDHCI_CTRL_TUNED_CLK)
+                               new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       else
+                               new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       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);
+                       u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+                       if (val & SDHCI_CTRL_EXEC_TUNING) {
+                               new_val |= ESDHC_STD_TUNING_EN |
+                                               ESDHC_TUNING_START_TAP;
+                               v |= ESDHC_MIX_CTRL_EXE_TUNE;
+                               m |= ESDHC_MIX_CTRL_FBCLK_SEL;
+                       } else {
+                               new_val &= ~ESDHC_STD_TUNING_EN;
+                               v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+                               m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+                       }
+
+                       if (val & SDHCI_CTRL_TUNED_CLK)
+                               v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       else
+                               v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+
+                       writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
+                       writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
+                       writel(m, host->ioaddr + ESDHC_MIX_CTRL);
+               }
+               return;
        case SDHCI_TRANSFER_MODE:
-               if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
+               if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
                                && (host->cmd->opcode == SD_IO_RW_EXTENDED)
                                && (host->cmd->data->blocks > 1)
                                && (host->cmd->data->flags & MMC_DATA_READ)) {
@@ -288,7 +454,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                        writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
                }
 
-               if (is_imx6q_usdhc(imx_data)) {
+               if (esdhc_is_usdhc(imx_data)) {
                        u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
                        /* Swap AC23 bit */
                        if (val & SDHCI_TRNS_AUTO_CMD23) {
@@ -310,10 +476,10 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                        val |= SDHCI_CMD_ABORTCMD;
 
                if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
-                   (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
+                   (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
                        imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
 
-               if (is_imx6q_usdhc(imx_data))
+               if (esdhc_is_usdhc(imx_data))
                        writel(val << 16,
                               host->ioaddr + SDHCI_TRANSFER_MODE);
                else
@@ -379,8 +545,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
                 * The reset on usdhc fails to clear MIX_CTRL register.
                 * Do it manually here.
                 */
-               if (is_imx6q_usdhc(imx_data))
+               if (esdhc_is_usdhc(imx_data)) {
                        writel(0, host->ioaddr + ESDHC_MIX_CTRL);
+                       imx_data->is_ddr = 0;
+               }
        }
 }
 
@@ -409,8 +577,60 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
                                         unsigned int clock)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       unsigned int host_clock = clk_get_rate(pltfm_host->clk);
+       int pre_div = 2;
+       int div = 1;
+       u32 temp, val;
+
+       if (clock == 0) {
+               if (esdhc_is_usdhc(imx_data)) {
+                       val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+                       writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
+                                       host->ioaddr + ESDHC_VENDOR_SPEC);
+               }
+               goto out;
+       }
+
+       if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr)
+               pre_div = 1;
+
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | ESDHC_CLOCK_MASK);
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+
+       while (host_clock / pre_div / 16 > clock && pre_div < 256)
+               pre_div *= 2;
+
+       while (host_clock / pre_div / div > clock && div < 16)
+               div++;
 
-       esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk));
+       host->mmc->actual_clock = host_clock / pre_div / div;
+       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
+               clock, host->mmc->actual_clock);
+
+       if (imx_data->is_ddr)
+               pre_div >>= 2;
+       else
+               pre_div >>= 1;
+       div--;
+
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | (div << ESDHC_DIVIDER_SHIFT)
+               | (pre_div << ESDHC_PREDIV_SHIFT));
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+
+       if (esdhc_is_usdhc(imx_data)) {
+               val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+               writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
+               host->ioaddr + ESDHC_VENDOR_SPEC);
+       }
+
+       mdelay(1);
+out:
+       host->clock = clock;
 }
 
 static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
@@ -454,7 +674,192 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
        return 0;
 }
 
-static const struct sdhci_ops sdhci_esdhc_ops = {
+static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
+{
+       u32 reg;
+
+       /* FIXME: delay a bit for card to be ready for next tuning due to errors */
+       mdelay(1);
+
+       reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+       reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
+                       ESDHC_MIX_CTRL_FBCLK_SEL;
+       writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
+       writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+       dev_dbg(mmc_dev(host->mmc),
+               "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
+                       val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
+}
+
+static void esdhc_request_done(struct mmc_request *mrq)
+{
+       complete(&mrq->completion);
+}
+
+static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
+{
+       struct mmc_command cmd = {0};
+       struct mmc_request mrq = {0};
+       struct mmc_data data = {0};
+       struct scatterlist sg;
+       char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
+
+       cmd.opcode = opcode;
+       cmd.arg = 0;
+       cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
+
+       mrq.cmd = &cmd;
+       mrq.cmd->mrq = &mrq;
+       mrq.data = &data;
+       mrq.data->mrq = &mrq;
+       mrq.cmd->data = mrq.data;
+
+       mrq.done = esdhc_request_done;
+       init_completion(&(mrq.completion));
+
+       disable_irq(host->irq);
+       mutex_lock(&host->lock);
+       host->mrq = &mrq;
+
+       sdhci_send_command(host, mrq.cmd);
+
+       mutex_unlock(&host->lock);
+       enable_irq(host->irq);
+
+       wait_for_completion(&mrq.completion);
+
+       if (cmd.error)
+               return cmd.error;
+       if (data.error)
+               return data.error;
+
+       return 0;
+}
+
+static void esdhc_post_tuning(struct sdhci_host *host)
+{
+       u32 reg;
+
+       reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+       reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+       writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
+}
+
+static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
+{
+       int min, max, avg, ret;
+
+       /* find the mininum delay first which can pass tuning */
+       min = ESDHC_TUNE_CTRL_MIN;
+       while (min < ESDHC_TUNE_CTRL_MAX) {
+               esdhc_prepare_tuning(host, min);
+               if (!esdhc_send_tuning_cmd(host, opcode))
+                       break;
+               min += ESDHC_TUNE_CTRL_STEP;
+       }
+
+       /* find the maxinum delay which can not pass tuning */
+       max = min + ESDHC_TUNE_CTRL_STEP;
+       while (max < ESDHC_TUNE_CTRL_MAX) {
+               esdhc_prepare_tuning(host, max);
+               if (esdhc_send_tuning_cmd(host, opcode)) {
+                       max -= ESDHC_TUNE_CTRL_STEP;
+                       break;
+               }
+               max += ESDHC_TUNE_CTRL_STEP;
+       }
+
+       /* use average delay to get the best timing */
+       avg = (min + max) / 2;
+       esdhc_prepare_tuning(host, avg);
+       ret = esdhc_send_tuning_cmd(host, opcode);
+       esdhc_post_tuning(host);
+
+       dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
+               ret ? "failed" : "passed", avg, ret);
+
+       return ret;
+}
+
+static int esdhc_change_pinstate(struct sdhci_host *host,
+                                               unsigned int uhs)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct pinctrl_state *pinctrl;
+
+       dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
+
+       if (IS_ERR(imx_data->pinctrl) ||
+               IS_ERR(imx_data->pins_default) ||
+               IS_ERR(imx_data->pins_100mhz) ||
+               IS_ERR(imx_data->pins_200mhz))
+               return -EINVAL;
+
+       switch (uhs) {
+       case MMC_TIMING_UHS_SDR50:
+               pinctrl = imx_data->pins_100mhz;
+               break;
+       case MMC_TIMING_UHS_SDR104:
+               pinctrl = imx_data->pins_200mhz;
+               break;
+       default:
+               /* back to default state for other legacy timing */
+               pinctrl = imx_data->pins_default;
+       }
+
+       return pinctrl_select_state(imx_data->pinctrl, pinctrl);
+}
+
+static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       struct esdhc_platform_data *boarddata = &imx_data->boarddata;
+
+       switch (uhs) {
+       case MMC_TIMING_UHS_SDR12:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12;
+               break;
+       case MMC_TIMING_UHS_SDR25:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25;
+               break;
+       case MMC_TIMING_UHS_SDR50:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
+               break;
+       case MMC_TIMING_UHS_SDR104:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
+               break;
+       case MMC_TIMING_UHS_DDR50:
+               imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50;
+               writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
+                               ESDHC_MIX_CTRL_DDREN,
+                               host->ioaddr + ESDHC_MIX_CTRL);
+               imx_data->is_ddr = 1;
+               if (boarddata->delay_line) {
+                       u32 v;
+                       v = boarddata->delay_line <<
+                               ESDHC_DLL_OVERRIDE_VAL_SHIFT |
+                               (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
+                       if (is_imx53_esdhc(imx_data))
+                               v <<= 1;
+                       writel(v, host->ioaddr + ESDHC_DLL_CTRL);
+               }
+               break;
+       }
+
+       return esdhc_change_pinstate(host, uhs);
+}
+
+static struct sdhci_ops sdhci_esdhc_ops = {
        .read_l = esdhc_readl_le,
        .read_w = esdhc_readw_le,
        .write_l = esdhc_writel_le,
@@ -465,6 +870,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
        .get_min_clock = esdhc_pltfm_get_min_clock,
        .get_ro = esdhc_pltfm_get_ro,
        .platform_bus_width = esdhc_pltfm_bus_width,
+       .set_uhs_signaling = esdhc_set_uhs_signaling,
 };
 
 static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -506,6 +912,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
 
        of_property_read_u32(np, "max-frequency", &boarddata->f_max);
 
+       if (of_find_property(np, "no-1-8-v", NULL))
+               boarddata->support_vsel = false;
+       else
+               boarddata->support_vsel = true;
+
+       if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
+               boarddata->delay_line = 0;
+
        return 0;
 }
 #else
@@ -539,9 +953,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                goto free_sdhci;
        }
 
-       if (of_id)
-               pdev->id_entry = of_id->data;
-       imx_data->devtype = pdev->id_entry->driver_data;
+       imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
+                                                 pdev->id_entry->driver_data;
        pltfm_host->priv = imx_data;
 
        imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
@@ -568,29 +981,39 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        clk_prepare_enable(imx_data->clk_ipg);
        clk_prepare_enable(imx_data->clk_ahb);
 
-       imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
        if (IS_ERR(imx_data->pinctrl)) {
                err = PTR_ERR(imx_data->pinctrl);
                goto disable_clk;
        }
 
+       imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
+                                               PINCTRL_STATE_DEFAULT);
+       if (IS_ERR(imx_data->pins_default)) {
+               err = PTR_ERR(imx_data->pins_default);
+               dev_err(mmc_dev(host->mmc), "could not get default state\n");
+               goto disable_clk;
+       }
+
        host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
 
-       if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
+       if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)
                /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
                host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
                        | SDHCI_QUIRK_BROKEN_ADMA;
 
-       if (is_imx53_esdhc(imx_data))
-               imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
-
        /*
         * The imx6q ROM code will change the default watermark level setting
         * to something insane.  Change it back here.
         */
-       if (is_imx6q_usdhc(imx_data))
+       if (esdhc_is_usdhc(imx_data)) {
                writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
+               host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+       }
 
+       if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
+               sdhci_esdhc_ops.platform_execute_tuning =
+                                       esdhc_executing_tuning;
        boarddata = &imx_data->boarddata;
        if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
                if (!host->mmc->parent->platform_data) {
@@ -650,6 +1073,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                break;
        }
 
+       /* sdr50 and sdr104 needs work on 1.8v signal voltage */
+       if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) {
+               imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
+                                               ESDHC_PINCTRL_STATE_100MHZ);
+               imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
+                                               ESDHC_PINCTRL_STATE_200MHZ);
+               if (IS_ERR(imx_data->pins_100mhz) ||
+                               IS_ERR(imx_data->pins_200mhz)) {
+                       dev_warn(mmc_dev(host->mmc),
+                               "could not get ultra high speed state, work on normal mode\n");
+                       /* fall back to not support uhs by specify no 1.8v quirk */
+                       host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+               }
+       } else {
+               host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+       }
+
        err = sdhci_add_host(host);
        if (err)
                goto disable_clk;
index a2a06420e4635b3904c0cd73f662b21623994de7..a7d9f95a7b03dd81af540212ef8016157a595c5e 100644 (file)
 
 #define ESDHC_HOST_CONTROL_RES 0x05
 
-static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock,
-                                  unsigned int host_clock)
-{
-       int pre_div = 2;
-       int div = 1;
-       u32 temp;
-
-       if (clock == 0)
-               goto out;
-
-       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
-               | ESDHC_CLOCK_MASK);
-       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
-
-       while (host_clock / pre_div / 16 > clock && pre_div < 256)
-               pre_div *= 2;
-
-       while (host_clock / pre_div / div > clock && div < 16)
-               div++;
-
-       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
-               clock, host_clock / pre_div / div);
-
-       pre_div >>= 1;
-       div--;
-
-       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
-               | (div << ESDHC_DIVIDER_SHIFT)
-               | (pre_div << ESDHC_PREDIV_SHIFT));
-       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
-       mdelay(1);
-out:
-       host->clock = clock;
-}
-
 #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
index e328252ebf2a7f684d0756dbfe1c1b20935d05ab..0b249970b1197fcc92af6e63184c72cf7c406286 100644 (file)
@@ -199,6 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
 
 static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 {
+
+       int pre_div = 2;
+       int div = 1;
+       u32 temp;
+
+       if (clock == 0)
+               goto out;
+
        /* Workaround to reduce the clock frequency for p1010 esdhc */
        if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
                if (clock > 20000000)
@@ -207,8 +215,31 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                        clock -= 5000000;
        }
 
-       /* Set the clock */
-       esdhc_set_clock(host, clock, host->max_clk);
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | ESDHC_CLOCK_MASK);
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+
+       while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
+               pre_div *= 2;
+
+       while (host->max_clk / pre_div / div > clock && div < 16)
+               div++;
+
+       dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
+               clock, host->max_clk / pre_div / div);
+
+       pre_div >>= 1;
+       div--;
+
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
+               | (div << ESDHC_DIVIDER_SHIFT)
+               | (pre_div << ESDHC_PREDIV_SHIFT));
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+       mdelay(1);
+out:
+       host->clock = clock;
 }
 
 #ifdef CONFIG_PM
index d7d6bc8968d2422d8b2e9fb44a695b35b17927d4..06f026a537b2218b7f97c8d5640c58c7572f3639 100644 (file)
@@ -37,6 +37,7 @@
 #define PCI_DEVICE_ID_INTEL_BYT_SDIO   0x0f15
 #define PCI_DEVICE_ID_INTEL_BYT_SD     0x0f16
 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2  0x0f50
+#define PCI_DEVICE_ID_INTEL_MRFL_MMC   0x1190
 
 /*
  * PCI registers
@@ -356,6 +357,28 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
        .allow_runtime_pm = true,
 };
 
+/* Define Host controllers for Intel Merrifield platform */
+#define INTEL_MRFL_EMMC_0      0
+#define INTEL_MRFL_EMMC_1      1
+
+static int intel_mrfl_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))
+               /* SD support is not ready yet */
+               return -ENODEV;
+
+       slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
+                                MMC_CAP_1_8V_DDR;
+
+       return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
+       .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+       .probe_slot     = intel_mrfl_mmc_probe_slot,
+};
+
 /* O2Micro extra registers */
 #define O2_SD_LOCK_WP          0xD3
 #define O2_SD_MULTI_VCC3V      0xEE
@@ -939,6 +962,13 @@ static const struct pci_device_id pci_ids[] = {
                .driver_data    = (kernel_ulong_t)&sdhci_intel_byt_emmc,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_MRFL_MMC,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mrfl_mmc,
+       },
        {
                .vendor         = PCI_VENDOR_ID_O2,
                .device         = PCI_DEVICE_ID_O2_8120,
index 793dacd3b8413c5cd4eae8fc39c420c4f3ffdb5c..fb761a987ee299b66f13481863b1ba8c05c2844b 100644 (file)
@@ -378,12 +378,11 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       unsigned long flags;
 
        if (pltfm_host->clk) {
-               spin_lock_irqsave(&host->lock, flags);
+               mutex_lock(&host->lock);
                host->runtime_suspended = true;
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
 
                clk_disable_unprepare(pltfm_host->clk);
        }
@@ -395,14 +394,13 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       unsigned long flags;
 
        if (pltfm_host->clk) {
                clk_prepare_enable(pltfm_host->clk);
 
-               spin_lock_irqsave(&host->lock, flags);
+               mutex_lock(&host->lock);
                host->runtime_suspended = false;
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
        }
 
        return 0;
index 6debda9521556c530d141cb3fa898af4175a6286..0210cebb5a646ecf3b98dc1c05ac36091f70dac5 100644 (file)
@@ -376,10 +376,9 @@ static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
 #ifdef CONFIG_PM_RUNTIME
        struct sdhci_s3c *sc = sdhci_priv(host);
 #endif
-       unsigned long flags;
 
        if (host) {
-               spin_lock_irqsave(&host->lock, flags);
+               mutex_lock(&host->lock);
                if (state) {
                        dev_dbg(&dev->dev, "card inserted.\n");
 #ifdef CONFIG_PM_RUNTIME
@@ -395,8 +394,8 @@ static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
                        clk_disable_unprepare(sc->clk_io);
 #endif
                }
-               tasklet_schedule(&host->card_tasklet);
-               spin_unlock_irqrestore(&host->lock, flags);
+               schedule_work(&host->card_detect_work);
+               mutex_unlock(&host->lock);
        }
 }
 
index 696122c1b468b315968128fb5a414fee21c740bd..4ec2ea3cd217f0302fbc7369002109f6b3585291 100644 (file)
@@ -80,8 +80,8 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
                goto err_sdhci_add;
 
        /*
-        * We must request the IRQ after sdhci_add_host(), as the tasklet only
-        * gets setup in sdhci_add_host() and we oops.
+        * We must request the IRQ after sdhci_add_host(), as the workqueue
+        * only gets setup in sdhci_add_host() and we oops.
         */
        if (gpio_is_valid(priv->gpio_cd)) {
                ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd, 0);
index 2dba9f8d1760313e3c47eeca756d6a59247ed924..9b6459da7b1cd480620cb3c760c8f72ea61a7e45 100644 (file)
@@ -65,7 +65,7 @@ static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
        }
 
        /* inform sdhci driver about card insertion/removal */
-       tasklet_schedule(&host->card_tasklet);
+       schedule_work(&host->card_detect_work);
 
        return IRQ_HANDLED;
 }
index 7a7fb4f0d5a43a4829fa1e258afbf112f4dd9b82..739dcc2c6e3c475c9869e30252cb54fa66f62cbd 100644 (file)
  *     - JMicron (hardware and technical support)
  */
 
+#include <linux/workqueue.h>
 #include <linux/delay.h>
 #include <linux/highmem.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
 #include <linux/scatterlist.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
@@ -49,10 +51,9 @@ static unsigned int debug_quirks2;
 
 static void sdhci_finish_data(struct sdhci_host *);
 
-static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
 static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
-static void sdhci_tuning_timer(unsigned long data);
+static void sdhci_tuning_timeout_work(struct work_struct *wk);
 static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
 
 #ifdef CONFIG_PM_RUNTIME
@@ -208,18 +209,17 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
        }
 
        /* Wait max 100 ms */
-       timeout = 100;
+       timeout = jiffies + msecs_to_jiffies(100);
 
        /* hw clears the bit when it's done */
        while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
-               if (timeout == 0) {
+               if (time_after(jiffies, timeout)) {
                        pr_err("%s: Reset 0x%x never completed.\n",
                                mmc_hostname(host->mmc), (int)mask);
                        sdhci_dumpregs(host);
                        return;
                }
-               timeout--;
-               mdelay(1);
+               msleep(1);
        }
 
        if (host->ops->platform_reset_exit)
@@ -267,7 +267,7 @@ static void sdhci_reinit(struct sdhci_host *host)
        if (host->flags & SDHCI_USING_RETUNING_TIMER) {
                host->flags &= ~SDHCI_USING_RETUNING_TIMER;
 
-               del_timer_sync(&host->tuning_timer);
+               flush_delayed_work(&host->tuning_timeout_work);
                host->flags &= ~SDHCI_NEEDS_RETUNING;
                host->mmc->max_blk_count =
                        (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
@@ -280,8 +280,14 @@ static void sdhci_activate_led(struct sdhci_host *host)
        u8 ctrl;
 
        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
-       ctrl |= SDHCI_CTRL_LED;
-       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+#ifdef SDHCI_USE_LEDS_CLASS
+       if (host->brightness && !(ctrl & SDHCI_CTRL_LED)) {
+#else
+       if (!(ctrl & SDHCI_CTRL_LED)) {
+#endif
+               ctrl |= SDHCI_CTRL_LED;
+               sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+       }
 }
 
 static void sdhci_deactivate_led(struct sdhci_host *host)
@@ -289,8 +295,14 @@ static void sdhci_deactivate_led(struct sdhci_host *host)
        u8 ctrl;
 
        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
-       ctrl &= ~SDHCI_CTRL_LED;
-       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+#ifdef SDHCI_USE_LEDS_CLASS
+       if (!host->brightness && (ctrl & SDHCI_CTRL_LED)) {
+#else
+       if (ctrl & SDHCI_CTRL_LED) {
+#endif
+               ctrl &= ~SDHCI_CTRL_LED;
+               sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+       }
 }
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -298,19 +310,11 @@ static void sdhci_led_control(struct led_classdev *led,
        enum led_brightness brightness)
 {
        struct sdhci_host *host = container_of(led, struct sdhci_host, led);
-       unsigned long flags;
-
-       spin_lock_irqsave(&host->lock, flags);
 
        if (host->runtime_suspended)
-               goto out;
+               return;
 
-       if (brightness == LED_OFF)
-               sdhci_deactivate_led(host);
-       else
-               sdhci_activate_led(host);
-out:
-       spin_unlock_irqrestore(&host->lock, flags);
+       host->brightness = brightness;
 }
 #endif
 
@@ -978,10 +982,10 @@ static void sdhci_finish_data(struct sdhci_host *host)
 
                sdhci_send_command(host, data->stop);
        } else
-               tasklet_schedule(&host->finish_tasklet);
+               schedule_work(&host->finish_work);
 }
 
-static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
        int flags;
        u32 mask;
@@ -990,7 +994,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        WARN_ON(host->cmd);
 
        /* Wait max 10 ms */
-       timeout = 10;
+       timeout = jiffies + msecs_to_jiffies(10);
 
        mask = SDHCI_CMD_INHIBIT;
        if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
@@ -1002,19 +1006,18 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
                mask &= ~SDHCI_DATA_INHIBIT;
 
        while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
-               if (timeout == 0) {
+               if (time_after(jiffies, timeout)) {
                        pr_err("%s: Controller never released "
                                "inhibit bit(s).\n", mmc_hostname(host->mmc));
                        sdhci_dumpregs(host);
                        cmd->error = -EIO;
-                       tasklet_schedule(&host->finish_tasklet);
+                       schedule_work(&host->finish_work);
                        return;
                }
-               timeout--;
                mdelay(1);
        }
 
-       mod_timer(&host->timer, jiffies + 10 * HZ);
+       schedule_delayed_work(&host->timeout_work, 10 * HZ);
 
        host->cmd = cmd;
 
@@ -1028,7 +1031,7 @@ static 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);
+               schedule_work(&host->finish_work);
                return;
        }
 
@@ -1053,6 +1056,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 
        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 }
+EXPORT_SYMBOL_GPL(sdhci_send_command);
 
 static void sdhci_finish_command(struct sdhci_host *host)
 {
@@ -1089,7 +1093,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
                        sdhci_finish_data(host);
 
                if (!host->cmd->data)
-                       tasklet_schedule(&host->finish_tasklet);
+                       schedule_work(&host->finish_work);
 
                host->cmd = NULL;
        }
@@ -1222,17 +1226,16 @@ clock_set:
        sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
        /* Wait max 20 ms */
-       timeout = 20;
+       timeout = jiffies + msecs_to_jiffies(20);
        while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
                & SDHCI_CLOCK_INT_STABLE)) {
-               if (timeout == 0) {
+               if (time_after(jiffies, timeout)) {
                        pr_err("%s: Internal clock never "
                                "stabilised.\n", mmc_hostname(host->mmc));
                        sdhci_dumpregs(host);
                        return;
                }
-               timeout--;
-               mdelay(1);
+               msleep(1);
        }
 
        clk |= SDHCI_CLOCK_CARD_EN;
@@ -1311,7 +1314,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
         * can apply clock after applying power
         */
        if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
-               mdelay(10);
+               msleep(10);
 
        return power;
 }
@@ -1326,20 +1329,17 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct sdhci_host *host;
        int present;
-       unsigned long flags;
        u32 tuning_opcode;
 
        host = mmc_priv(mmc);
 
        sdhci_runtime_pm_get(host);
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        WARN_ON(host->mrq != NULL);
 
-#ifndef SDHCI_USE_LEDS_CLASS
        sdhci_activate_led(host);
-#endif
 
        /*
         * Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
@@ -1373,13 +1373,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        if (!present || host->flags & SDHCI_DEVICE_DEAD) {
                host->mrq->cmd->error = -ENOMEDIUM;
-               tasklet_schedule(&host->finish_tasklet);
+               schedule_work(&host->finish_work);
        } else {
                u32 present_state;
 
                present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
                /*
-                * Check if the re-tuning timer has already expired and there
+                * Check if the re-tuning timeout has already expired and there
                 * is no on-going data transfer. If so, we need to execute
                 * tuning procedure before sending command.
                 */
@@ -1391,9 +1391,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                                        mmc->card->type == MMC_TYPE_MMC ?
                                        MMC_SEND_TUNING_BLOCK_HS200 :
                                        MMC_SEND_TUNING_BLOCK;
-                               spin_unlock_irqrestore(&host->lock, flags);
+                               mutex_unlock(&host->lock);
                                sdhci_execute_tuning(mmc, tuning_opcode);
-                               spin_lock_irqsave(&host->lock, flags);
+                               mutex_lock(&host->lock);
 
                                /* Restore original mmc_request structure */
                                host->mrq = mrq;
@@ -1407,19 +1407,18 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        }
 
        mmiowb();
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 }
 
 static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
 {
-       unsigned long flags;
        int vdd_bit = -1;
        u8 ctrl;
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        if (host->flags & SDHCI_DEVICE_DEAD) {
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
                if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
                        mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
                return;
@@ -1435,7 +1434,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
        }
 
        if (host->version >= SDHCI_SPEC_300 &&
-               (ios->power_mode == MMC_POWER_UP))
+               (ios->power_mode == MMC_POWER_UP) &&
+               !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
                sdhci_enable_preset_value(host, false);
 
        sdhci_set_clock(host, ios->clock);
@@ -1446,9 +1446,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
                vdd_bit = sdhci_set_power(host, ios->vdd);
 
        if (host->vmmc && vdd_bit != -1) {
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
                mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
-               spin_lock_irqsave(&host->lock, flags);
+               mutex_lock(&host->lock);
        }
 
        if (host->ops->platform_send_init_74_clocks)
@@ -1585,7 +1585,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
                sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
 
        mmiowb();
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 }
 
 static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -1630,10 +1630,9 @@ static int sdhci_get_cd(struct mmc_host *mmc)
 
 static int sdhci_check_ro(struct sdhci_host *host)
 {
-       unsigned long flags;
        int is_readonly;
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        if (host->flags & SDHCI_DEVICE_DEAD)
                is_readonly = 0;
@@ -1643,7 +1642,7 @@ static int sdhci_check_ro(struct sdhci_host *host)
                is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
                                & SDHCI_WRITE_PROTECT);
 
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 
        /* This quirk needs to be replaced by a callback-function later */
        return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ?
@@ -1714,11 +1713,10 @@ out:
 static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
        struct sdhci_host *host = mmc_priv(mmc);
-       unsigned long flags;
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
        sdhci_enable_sdio_irq_nolock(host, enable);
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 }
 
 static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
@@ -1849,7 +1847,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 
        sdhci_runtime_pm_get(host);
        disable_irq(host->irq);
-       spin_lock(&host->lock);
+       mutex_lock(&host->lock);
 
        ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 
@@ -1869,12 +1867,20 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
            requires_tuning_nonuhs)
                ctrl |= SDHCI_CTRL_EXEC_TUNING;
        else {
-               spin_unlock(&host->lock);
+               mutex_unlock(&host->lock);
                enable_irq(host->irq);
                sdhci_runtime_pm_put(host);
                return 0;
        }
 
+       if (host->ops->platform_execute_tuning) {
+               mutex_unlock(&host->lock);
+               enable_irq(host->irq);
+               err = host->ops->platform_execute_tuning(host, opcode);
+               sdhci_runtime_pm_put(host);
+               return err;
+       }
+
        sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
 
        /*
@@ -1894,12 +1900,12 @@ 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.
         */
-       timeout = 150;
+       timeout = jiffies + msecs_to_jiffies(150);
        do {
                struct mmc_command cmd = {0};
                struct mmc_request mrq = {NULL};
 
-               if (!tuning_loop_counter && !timeout)
+               if (!tuning_loop_counter && time_after(jiffies, timeout))
                        break;
 
                cmd.opcode = opcode;
@@ -1942,7 +1948,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                host->cmd = NULL;
                host->mrq = NULL;
 
-               spin_unlock(&host->lock);
+               mutex_unlock(&host->lock);
                enable_irq(host->irq);
 
                /* Wait for Buffer Read Ready interrupt */
@@ -1950,7 +1956,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                                        (host->tuning_done == 1),
                                        msecs_to_jiffies(50));
                disable_irq(host->irq);
-               spin_lock(&host->lock);
+               mutex_lock(&host->lock);
 
                if (!host->tuning_done) {
                        pr_info(DRIVER_NAME ": Timeout waiting for "
@@ -1970,17 +1976,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 
                ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
                tuning_loop_counter--;
-               timeout--;
-               mdelay(1);
+               msleep(1);
        } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
 
        /*
         * The Host Driver has exhausted the maximum number of loops allowed,
         * so use fixed sampling frequency.
         */
-       if (!tuning_loop_counter || !timeout) {
+       if (!tuning_loop_counter || time_after(jiffies, timeout)) {
                ctrl &= ~SDHCI_CTRL_TUNED_CLK;
                sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+               err = -EIO;
        } else {
                if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
                        pr_info(DRIVER_NAME ": Tuning procedure"
@@ -1993,38 +1999,39 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 out:
        /*
         * If this is the very first time we are here, we start the retuning
-        * timer. Since only during the first time, SDHCI_NEEDS_RETUNING
-        * flag won't be set, we check this condition before actually starting
-        * the timer.
+        * timeout workqueue. Since only during the first time,
+        * SDHCI_NEEDS_RETUNING flag won't be set, we check this condition
+        * before actually starting the timeout worqueue.
         */
        if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count &&
            (host->tuning_mode == SDHCI_TUNING_MODE_1)) {
                host->flags |= SDHCI_USING_RETUNING_TIMER;
-               mod_timer(&host->tuning_timer, jiffies +
+               schedule_delayed_work(&host->tuning_timeout_work,
                        host->tuning_count * HZ);
                /* Tuning mode 1 limits the maximum data length to 4MB */
                mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
        } else {
                host->flags &= ~SDHCI_NEEDS_RETUNING;
-               /* Reload the new initial value for timer */
+               /* Reload the new initial value for timeout workqueue */
                if (host->tuning_mode == SDHCI_TUNING_MODE_1)
-                       mod_timer(&host->tuning_timer, jiffies +
+                       schedule_delayed_work(&host->tuning_timeout_work,
                                host->tuning_count * HZ);
        }
 
        /*
         * In case tuning fails, host controllers which support re-tuning can
-        * try tuning again at a later time, when the re-tuning timer expires.
+        * try tuning again at a later time, when the re-tuning timeout
+        * workqueue expires.
         * So for these controllers, we return 0. Since there might be other
         * controllers who do not have this capability, we return error for
         * them. SDHCI_USING_RETUNING_TIMER means the host is currently using
-        * a retuning timer to do the retuning for the card.
+        * a retuning timeout workqueue to do the retuning for the card.
         */
        if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
                err = 0;
 
        sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
-       spin_unlock(&host->lock);
+       mutex_unlock(&host->lock);
        enable_irq(host->irq);
        sdhci_runtime_pm_put(host);
 
@@ -2060,13 +2067,12 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
 static void sdhci_card_event(struct mmc_host *mmc)
 {
        struct sdhci_host *host = mmc_priv(mmc);
-       unsigned long flags;
 
        /* First check if client has provided their own card event */
        if (host->ops->card_event)
                host->ops->card_event(host);
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        /* Check host->mrq first in case we are runtime suspended */
        if (host->mrq && !sdhci_do_get_cd(host)) {
@@ -2079,10 +2085,10 @@ static void sdhci_card_event(struct mmc_host *mmc)
                sdhci_reset(host, SDHCI_RESET_DATA);
 
                host->mrq->cmd->error = -ENOMEDIUM;
-               tasklet_schedule(&host->finish_tasklet);
+               schedule_work(&host->finish_work);
        }
 
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 }
 
 static const struct mmc_host_ops sdhci_ops = {
@@ -2104,35 +2110,35 @@ static const struct mmc_host_ops sdhci_ops = {
  *                                                                           *
 \*****************************************************************************/
 
-static void sdhci_tasklet_card(unsigned long param)
+static void sdhci_card_detect_work(struct work_struct *wk)
 {
-       struct sdhci_host *host = (struct sdhci_host*)param;
+       struct sdhci_host *host = container_of(wk, struct sdhci_host,
+                                                  card_detect_work);
 
        sdhci_card_event(host->mmc);
 
        mmc_detect_change(host->mmc, msecs_to_jiffies(200));
 }
 
-static void sdhci_tasklet_finish(unsigned long param)
+static void sdhci_finish_work(struct work_struct *wk)
 {
        struct sdhci_host *host;
-       unsigned long flags;
        struct mmc_request *mrq;
 
-       host = (struct sdhci_host*)param;
+       host = container_of(wk, struct sdhci_host, finish_work);
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
-        /*
-         * If this tasklet gets rescheduled while running, it will
-         * be run again afterwards but without any active request.
-         */
+       /*
+        * If this work gets rescheduled while running, it will
+        * be run again afterwards but without any active request.
+        */
        if (!host->mrq) {
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
                return;
        }
 
-       del_timer(&host->timer);
+       cancel_delayed_work(&host->timeout_work);
 
        mrq = host->mrq;
 
@@ -2161,25 +2167,24 @@ static void sdhci_tasklet_finish(unsigned long param)
        host->cmd = NULL;
        host->data = NULL;
 
-#ifndef SDHCI_USE_LEDS_CLASS
-       sdhci_deactivate_led(host);
-#endif
-
        mmiowb();
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 
        mmc_request_done(host->mmc, mrq);
        sdhci_runtime_pm_put(host);
+
+       mutex_lock(&host->lock);
+       sdhci_deactivate_led(host);
+       mutex_unlock(&host->lock);
 }
 
-static void sdhci_timeout_timer(unsigned long data)
+static void sdhci_timeout_work(struct work_struct *wk)
 {
        struct sdhci_host *host;
-       unsigned long flags;
 
-       host = (struct sdhci_host*)data;
+       host = container_of(wk, struct sdhci_host, timeout_work.work);
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        if (host->mrq) {
                pr_err("%s: Timeout waiting for hardware "
@@ -2195,26 +2200,25 @@ static void sdhci_timeout_timer(unsigned long data)
                        else
                                host->mrq->cmd->error = -ETIMEDOUT;
 
-                       tasklet_schedule(&host->finish_tasklet);
+                       schedule_work(&host->finish_work);
                }
        }
 
        mmiowb();
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 }
 
-static void sdhci_tuning_timer(unsigned long data)
+static void sdhci_tuning_timeout_work(struct work_struct *wk)
 {
        struct sdhci_host *host;
-       unsigned long flags;
 
-       host = (struct sdhci_host *)data;
+       host = container_of(wk, struct sdhci_host, tuning_timeout_work.work);
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        host->flags |= SDHCI_NEEDS_RETUNING;
 
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 }
 
 /*****************************************************************************\
@@ -2242,7 +2246,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
                host->cmd->error = -EILSEQ;
 
        if (host->cmd->error) {
-               tasklet_schedule(&host->finish_tasklet);
+               schedule_work(&host->finish_work);
                return;
        }
 
@@ -2402,17 +2406,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
        }
 }
 
-static irqreturn_t sdhci_irq(int irq, void *dev_id)
+static irqreturn_t sdhci_irq_thread(int irq, void *dev_id)
 {
-       irqreturn_t result;
        struct sdhci_host *host = dev_id;
        u32 intmask, unexpected = 0;
        int cardint = 0, max_loops = 16;
 
-       spin_lock(&host->lock);
+       mutex_lock(&host->lock);
 
        if (host->runtime_suspended) {
-               spin_unlock(&host->lock);
+               mutex_unlock(&host->lock);
                pr_warning("%s: got irq while runtime suspended\n",
                       mmc_hostname(host->mmc));
                return IRQ_HANDLED;
@@ -2420,15 +2423,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 
        intmask = sdhci_readl(host, SDHCI_INT_STATUS);
 
-       if (!intmask || intmask == 0xffffffff) {
-               result = IRQ_NONE;
-               goto out;
-       }
-
 again:
-       DBG("*** %s got interrupt: 0x%08x\n",
-               mmc_hostname(host->mmc), intmask);
-
        if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
                u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
                              SDHCI_CARD_PRESENT;
@@ -2451,7 +2446,7 @@ again:
                sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
                             SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
                intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
-               tasklet_schedule(&host->card_tasklet);
+               schedule_work(&host->card_detect_work);
        }
 
        if (intmask & SDHCI_INT_CMD_MASK) {
@@ -2488,13 +2483,11 @@ again:
                sdhci_writel(host, intmask, SDHCI_INT_STATUS);
        }
 
-       result = IRQ_HANDLED;
-
        intmask = sdhci_readl(host, SDHCI_INT_STATUS);
        if (intmask && --max_loops)
                goto again;
-out:
-       spin_unlock(&host->lock);
+
+       mutex_unlock(&host->lock);
 
        if (unexpected) {
                pr_err("%s: Unexpected interrupt 0x%08x.\n",
@@ -2507,7 +2500,27 @@ out:
        if (cardint)
                mmc_signal_sdio_irq(host->mmc);
 
-       return result;
+       intmask = sdhci_readl(host, SDHCI_INT_ENABLE);
+       sdhci_writel(host, intmask, SDHCI_SIGNAL_ENABLE);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t sdhci_irq(int irq, void *dev_id)
+{
+       struct sdhci_host *host = dev_id;
+       u32 intmask = sdhci_readl(host, SDHCI_INT_STATUS);
+
+       if (!intmask || intmask == 0xffffffff)
+               return IRQ_NONE;
+
+       /* Disable interrupts */
+       sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+
+       DBG("*** %s got interrupt: 0x%08x\n",
+               mmc_hostname(host->mmc), intmask);
+
+       return IRQ_WAKE_THREAD;
 }
 
 /*****************************************************************************\
@@ -2555,7 +2568,7 @@ int sdhci_suspend_host(struct sdhci_host *host)
 
        /* Disable tuning since we are suspending */
        if (host->flags & SDHCI_USING_RETUNING_TIMER) {
-               del_timer_sync(&host->tuning_timer);
+               flush_delayed_work(&host->tuning_timeout_work);
                host->flags &= ~SDHCI_NEEDS_RETUNING;
        }
 
@@ -2563,7 +2576,7 @@ int sdhci_suspend_host(struct sdhci_host *host)
        if (ret) {
                if (host->flags & SDHCI_USING_RETUNING_TIMER) {
                        host->flags |= SDHCI_NEEDS_RETUNING;
-                       mod_timer(&host->tuning_timer, jiffies +
+                       schedule_delayed_work(&host->tuning_timeout_work,
                                        host->tuning_count * HZ);
                }
 
@@ -2594,8 +2607,9 @@ int sdhci_resume_host(struct sdhci_host *host)
        }
 
        if (!device_may_wakeup(mmc_dev(host->mmc))) {
-               ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
-                                 mmc_hostname(host->mmc), host);
+               ret = request_threaded_irq(host->irq, sdhci_irq,
+                       sdhci_irq_thread, IRQF_SHARED,
+                       mmc_hostname(host->mmc), host);
                if (ret)
                        return ret;
        } else {
@@ -2662,24 +2676,23 @@ static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
 
 int sdhci_runtime_suspend_host(struct sdhci_host *host)
 {
-       unsigned long flags;
        int ret = 0;
 
        /* Disable tuning since we are suspending */
        if (host->flags & SDHCI_USING_RETUNING_TIMER) {
-               del_timer_sync(&host->tuning_timer);
+               flush_delayed_work(&host->tuning_timeout_work);
                host->flags &= ~SDHCI_NEEDS_RETUNING;
        }
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
        sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 
        synchronize_irq(host->irq);
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
        host->runtime_suspended = true;
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 
        return ret;
 }
@@ -2687,7 +2700,6 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
 
 int sdhci_runtime_resume_host(struct sdhci_host *host)
 {
-       unsigned long flags;
        int ret = 0, host_flags = host->flags;
 
        if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -2705,16 +2717,16 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
        sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
        if ((host_flags & SDHCI_PV_ENABLED) &&
                !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
-               spin_lock_irqsave(&host->lock, flags);
+               mutex_lock(&host->lock);
                sdhci_enable_preset_value(host, true);
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
        }
 
        /* Set the re-tuning expiration flag */
        if (host->flags & SDHCI_USING_RETUNING_TIMER)
                host->flags |= SDHCI_NEEDS_RETUNING;
 
-       spin_lock_irqsave(&host->lock, flags);
+       mutex_lock(&host->lock);
 
        host->runtime_suspended = false;
 
@@ -2725,7 +2737,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
        /* Enable Card Detection */
        sdhci_enable_card_detection(host);
 
-       spin_unlock_irqrestore(&host->lock, flags);
+       mutex_unlock(&host->lock);
 
        return ret;
 }
@@ -3025,13 +3037,13 @@ int sdhci_add_host(struct sdhci_host *host)
        if (caps[1] & SDHCI_DRIVER_TYPE_D)
                mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
 
-       /* Initial value for re-tuning timer count */
+       /* Initial value for re-tuning timeout count */
        host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
                              SDHCI_RETUNING_TIMER_COUNT_SHIFT;
 
        /*
-        * In case Re-tuning Timer is not disabled, the actual value of
-        * re-tuning timer will be 2 ^ (n - 1).
+        * In case Re-tuning Timeout is not disabled, the actual value of
+        * re-tuning timeout will be 2 ^ (n - 1).
         */
        if (host->tuning_count)
                host->tuning_count = 1 << (host->tuning_count - 1);
@@ -3141,7 +3153,7 @@ int sdhci_add_host(struct sdhci_host *host)
                return -ENODEV;
        }
 
-       spin_lock_init(&host->lock);
+       mutex_init(&host->lock);
 
        /*
         * Maximum number of segments. Depends on if the hardware
@@ -3198,32 +3210,29 @@ int sdhci_add_host(struct sdhci_host *host)
        mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
 
        /*
-        * Init tasklets.
+        * Init work structs.
         */
-       tasklet_init(&host->card_tasklet,
-               sdhci_tasklet_card, (unsigned long)host);
-       tasklet_init(&host->finish_tasklet,
-               sdhci_tasklet_finish, (unsigned long)host);
+       INIT_WORK(&host->card_detect_work, sdhci_card_detect_work);
+       INIT_WORK(&host->finish_work, sdhci_finish_work);
 
-       setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+       INIT_DELAYED_WORK(&host->timeout_work, sdhci_timeout_work);
 
        if (host->version >= SDHCI_SPEC_300) {
                init_waitqueue_head(&host->buf_ready_int);
 
-               /* Initialize re-tuning timer */
-               init_timer(&host->tuning_timer);
-               host->tuning_timer.data = (unsigned long)host;
-               host->tuning_timer.function = sdhci_tuning_timer;
+               /* Initialize re-tuning timeout work */
+               INIT_DELAYED_WORK(&host->tuning_timeout_work,
+                                       sdhci_tuning_timeout_work);
        }
 
        sdhci_init(host, 0);
 
-       ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
-               mmc_hostname(mmc), host);
+       ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_irq_thread,
+                         IRQF_SHARED, mmc_hostname(host->mmc), host);
        if (ret) {
                pr_err("%s: Failed to request IRQ %d: %d\n",
                       mmc_hostname(mmc), host->irq, ret);
-               goto untasklet;
+               return ret;
        }
 
 #ifdef CONFIG_MMC_DEBUG
@@ -3265,10 +3274,6 @@ reset:
        sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
        free_irq(host->irq, host);
 #endif
-untasklet:
-       tasklet_kill(&host->card_tasklet);
-       tasklet_kill(&host->finish_tasklet);
-
        return ret;
 }
 
@@ -3276,10 +3281,8 @@ EXPORT_SYMBOL_GPL(sdhci_add_host);
 
 void sdhci_remove_host(struct sdhci_host *host, int dead)
 {
-       unsigned long flags;
-
        if (dead) {
-               spin_lock_irqsave(&host->lock, flags);
+               mutex_lock(&host->lock);
 
                host->flags |= SDHCI_DEVICE_DEAD;
 
@@ -3288,10 +3291,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
                                " transfer!\n", mmc_hostname(host->mmc));
 
                        host->mrq->cmd->error = -ENOMEDIUM;
-                       tasklet_schedule(&host->finish_tasklet);
+                       schedule_work(&host->finish_work);
                }
 
-               spin_unlock_irqrestore(&host->lock, flags);
+               mutex_unlock(&host->lock);
        }
 
        sdhci_disable_card_detection(host);
@@ -3308,10 +3311,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
        sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
        free_irq(host->irq, host);
 
-       del_timer_sync(&host->timer);
+       flush_delayed_work(&host->timeout_work);
 
-       tasklet_kill(&host->card_tasklet);
-       tasklet_kill(&host->finish_tasklet);
+       flush_work(&host->card_detect_work);
+       flush_work(&host->finish_work);
 
        if (host->vmmc) {
                regulator_disable(host->vmmc);
index b037f188fe44b26c357b77ebe985f8406aa4c1ac..0a3ed01887db824da03a66f103ef4b4948fc074f 100644 (file)
@@ -288,6 +288,7 @@ struct sdhci_ops {
        unsigned int    (*get_ro)(struct sdhci_host *host);
        void    (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
        void    (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
+       int     (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
        int     (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
        void    (*hw_reset)(struct sdhci_host *host);
        void    (*platform_suspend)(struct sdhci_host *host);
@@ -393,6 +394,8 @@ static inline void *sdhci_priv(struct sdhci_host *host)
 extern void sdhci_card_detect(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,
+                               struct mmc_command *cmd);
 
 #ifdef CONFIG_PM
 extern int sdhci_suspend_host(struct sdhci_host *host);
index 34231d5168fcf12dfe804d2a26b3cda2c6647976..57858efb3762bc38d5da77da796d6568a9aea562 100644 (file)
@@ -212,28 +212,14 @@ struct wmt_mci_priv {
 
 static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)
 {
-       u32 reg_tmp;
-       if (enable) {
-               if (priv->power_inverted) {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp | BM_SD_OFF,
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               } else {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp & (~BM_SD_OFF),
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               }
-       } else {
-               if (priv->power_inverted) {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp & (~BM_SD_OFF),
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               } else {
-                       reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
-                       writeb(reg_tmp | BM_SD_OFF,
-                              priv->sdmmc_base + SDMMC_BUSMODE);
-               }
-       }
+       u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+
+       if (enable ^ priv->power_inverted)
+               reg_tmp &= ~BM_SD_OFF;
+       else
+               reg_tmp |= BM_SD_OFF;
+
+       writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE);
 }
 
 static void wmt_mci_read_response(struct mmc_host *mmc)
index 842de3e21e70c4125f3d1574ebb0391a8d2b4cd5..f42cdbd8ac21bd0353552b00e040dfe8a811aa43 100644 (file)
@@ -420,7 +420,6 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_blockaddr(c)  ((c)->state & MMC_STATE_BLOCKADDR)
 #define mmc_card_ddr_mode(c)   ((c)->state & MMC_STATE_HIGHSPEED_DDR)
 #define mmc_card_uhs(c)                ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
-#define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
 #define mmc_card_removed(c)    ((c) && ((c)->state & MMC_CARD_REMOVED))
 #define mmc_card_doing_bkops(c)        ((c)->state & MMC_STATE_DOING_BKOPS)
@@ -432,7 +431,6 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
 #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
 #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
-#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
 #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
 #define mmc_card_set_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
index da51bec578c33043c156d6661668e8957f5cb86f..a00fc49c8434a53ced21573dfac43785f7bf2f6c 100644 (file)
@@ -188,7 +188,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
 
 extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
 extern void mmc_release_host(struct mmc_host *host);
-extern int mmc_try_claim_host(struct mmc_host *host);
 
 extern void mmc_get_card(struct mmc_card *card);
 extern void mmc_put_card(struct mmc_card *card);
index 198f0fa44e9fd5c8f2b351c38f7679f165f89f7b..6ce7d2cd3c7ad7dd5eea530192de207b3d1aef22 100644 (file)
@@ -15,6 +15,7 @@
 #define LINUX_MMC_DW_MMC_H
 
 #include <linux/scatterlist.h>
+#include <linux/mmc/core.h>
 
 #define MAX_MCI_SLOTS  2
 
@@ -129,6 +130,9 @@ struct dw_mci {
        struct mmc_request      *mrq;
        struct mmc_command      *cmd;
        struct mmc_data         *data;
+       struct mmc_command      stop_abort;
+       unsigned int            prev_blksz;
+       unsigned char           timing;
        struct workqueue_struct *card_workqueue;
 
        /* DMA interface members*/
index 3e781b8c0be74dd76e658c4ba78ecbb72516e153..2f6a97a7c1f9587a1f0cf146c98d6e2f4b3b4d8d 100644 (file)
 #define LINUX_MMC_SDHCI_H
 
 #include <linux/scatterlist.h>
+#include <linux/workqueue.h>
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include <linux/mutex.h>
 #include <linux/io.h>
 #include <linux/mmc/host.h>
 
@@ -114,9 +116,10 @@ struct sdhci_host {
 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
        struct led_classdev led;        /* LED control */
        char led_name[32];
+       enum led_brightness brightness;
 #endif
 
-       spinlock_t lock;        /* Mutex */
+       struct mutex lock;      /* Mutex */
 
        int flags;              /* Host attributes */
 #define SDHCI_USE_SDMA         (1<<0)  /* Host is SDMA capable */
@@ -160,10 +163,10 @@ struct sdhci_host {
        dma_addr_t adma_addr;   /* Mapped ADMA descr. table */
        dma_addr_t align_addr;  /* Mapped bounce buffer */
 
-       struct tasklet_struct card_tasklet;     /* Tasklet structures */
-       struct tasklet_struct finish_tasklet;
+       struct work_struct      card_detect_work;
+       struct work_struct      finish_work;
 
-       struct timer_list timer;        /* Timer for timeouts */
+       struct delayed_work     timeout_work;   /* Work for timeouts */
 
        u32 caps;               /* Alternative CAPABILITY_0 */
        u32 caps1;              /* Alternative CAPABILITY_1 */
@@ -179,7 +182,7 @@ 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
-       struct timer_list       tuning_timer;   /* Timer for tuning */
+       struct delayed_work     tuning_timeout_work;    /* Work for tuning timeouts */
 
        unsigned long private[0] ____cacheline_aligned;
 };
index d44912d81578d072b8dab544254d58682ce39ab7..75f70f6ac13778f448a03de3136d32a0ef638b65 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef __ASM_ARCH_IMX_ESDHC_H
 #define __ASM_ARCH_IMX_ESDHC_H
 
+#include <linux/types.h>
+
 enum wp_types {
        ESDHC_WP_NONE,          /* no WP, neither controller nor gpio */
        ESDHC_WP_CONTROLLER,    /* mmc controller internal WP */
@@ -32,6 +34,7 @@ enum cd_types {
  * @cd_gpio:   gpio for card_detect interrupt
  * @wp_type:   type of write_protect method (see wp_types enum above)
  * @cd_type:   type of card_detect method (see cd_types enum above)
+ * @support_vsel:  indicate it supports 1.8v switching
  */
 
 struct esdhc_platform_data {
@@ -41,5 +44,7 @@ struct esdhc_platform_data {
        enum cd_types cd_type;
        int max_bus_width;
        unsigned int f_max;
+       bool support_vsel;
+       unsigned int delay_line;
 };
 #endif /* __ASM_ARCH_IMX_ESDHC_H */