]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
mmc: implement Driver Stage Register handling
authorSascha Hauer <s.hauer@pengutronix.de>
Tue, 19 Aug 2014 08:45:51 +0000 (10:45 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 9 Sep 2014 11:59:07 +0000 (13:59 +0200)
Some eMMC and SD cards implement a DSR register that allows to tune
raise/fall times and drive strength of the CMD and DATA outputs.
The values to use depend on the card in use and the host.
It might be needed to reduce the drive strength to prevent voltage peaks
above the host's specification.

Implement a 'dsr' devicetree property that allows to specify the value
to set the DSR to. For non-dt setups the new members of mmc_host can be
set by board code.

This patch was initially authored by Sascha Hauer. It contains
improvements authored by Markus Niebel and Uwe Kleine-König.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Markus Niebel <Markus.Niebel@tq-group.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Documentation/devicetree/bindings/mmc/mmc.txt
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
drivers/mmc/core/sd.c
include/linux/mmc/card.h
include/linux/mmc/host.h

index 431716e37a3964638245b2905badff16128b9495..b52628b18a537af7dac6be18150efe7a95adf78b 100644 (file)
@@ -40,6 +40,8 @@ Optional properties:
 - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
 - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
 - mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
+- dsr: Value the card's (optional) Driver Stage Register (DSR) should be
+  programmed with. Valid range: [0 .. 0xffff].
 
 *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
 polarity properties, we have to fix the meaning of the "normal" and "inverted"
index 95cceae96944c17c19dbb4e9916119a72fb2bce6..d572b2beb65a4cc5238afc011f21856facb4b395 100644 (file)
@@ -452,6 +452,14 @@ int mmc_of_parse(struct mmc_host *host)
        if (of_find_property(np, "mmc-hs400-1_2v", &len))
                host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
 
+       host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
+       if (host->dsr_req && (host->dsr & ~0xffff)) {
+               dev_err(host->parent,
+                       "device tree specified broken value for DSR: 0x%x, ignoring\n",
+                       host->dsr);
+               host->dsr_req = 0;
+       }
+
        return 0;
 
 out:
index 1eda8dd8c867228b5643e40f7655b513643eb26f..31c43165f8bcd2747e1a268c3a4637d8c421de49 100644 (file)
@@ -162,6 +162,7 @@ static int mmc_decode_csd(struct mmc_card *card)
        csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
        csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
        csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
+       csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
        csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
        csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
        csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
@@ -1271,6 +1272,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                        goto free_card;
        }
 
+       /*
+        * handling only for cards supporting DSR and hosts requesting
+        * DSR configuration
+        */
+       if (card->csd.dsr_imp && host->dsr_req)
+               mmc_set_dsr(host);
+
        /*
         * Select card, as all following commands rely on that.
         */
index f51b5ba3bbea99ce1f2b74e5b3c80a4b882b972c..ba0275e90617c95f619f51456e0dd466c0032228 100644 (file)
@@ -93,6 +93,26 @@ int mmc_deselect_cards(struct mmc_host *host)
        return _mmc_select_card(host, NULL);
 }
 
+/*
+ * Write the value specified in the device tree or board code into the optional
+ * 16 bit Driver Stage Register. This can be used to tune raise/fall times and
+ * drive strength of the DAT and CMD outputs. The actual meaning of a given
+ * value is hardware dependant.
+ * The presence of the DSR register can be determined from the CSD register,
+ * bit 76.
+ */
+int mmc_set_dsr(struct mmc_host *host)
+{
+       struct mmc_command cmd = {0};
+
+       cmd.opcode = MMC_SET_DSR;
+
+       cmd.arg = (host->dsr << 16) | 0xffff;
+       cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
+
+       return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+}
+
 int mmc_go_idle(struct mmc_host *host)
 {
        int err;
index 80ae9f4e0293a045ca8e4ebb89023e3c64e062da..390dac665b2a6246309415ca019840bee4661b07 100644 (file)
@@ -14,6 +14,7 @@
 
 int mmc_select_card(struct mmc_card *card);
 int mmc_deselect_cards(struct mmc_host *host);
+int mmc_set_dsr(struct mmc_host *host);
 int mmc_go_idle(struct mmc_host *host);
 int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
 int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
index 0c44510bf717874808a0e1d20de68aa3cf2afda5..25913889cbaa998a4aefc443a1d9677fe0ecb68a 100644 (file)
@@ -127,6 +127,7 @@ static int mmc_decode_csd(struct mmc_card *card)
                csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
                csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
                csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
+               csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
                csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
                csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
                csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
@@ -953,6 +954,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                mmc_decode_cid(card);
        }
 
+       /*
+        * handling only for cards supporting DSR and hosts requesting
+        * DSR configuration
+        */
+       if (card->csd.dsr_imp && host->dsr_req)
+               mmc_set_dsr(host);
+
        /*
         * Select card, as all following commands rely on that.
         */
index bde5147a42217b9c0991d220156aeb980d215f37..0ea795f5feb9e0b06e530472372a9b31cec5c6a4 100644 (file)
@@ -42,7 +42,8 @@ struct mmc_csd {
        unsigned int            read_partial:1,
                                read_misalign:1,
                                write_partial:1,
-                               write_misalign:1;
+                               write_misalign:1,
+                               dsr_imp:1;
 };
 
 struct mmc_ext_csd {
index 7960424d0bc0cf4f1ad65e93f9df79fa83bf9403..4cbf6147699928f9aca493437e67de33baf2de21 100644 (file)
@@ -365,6 +365,9 @@ struct mmc_host {
 
        unsigned int            slotno; /* used for sdio acpi binding */
 
+       int                     dsr_req;        /* DSR value is valid */
+       u32                     dsr;    /* optional driver stage (DSR) value */
+
        unsigned long           private[0] ____cacheline_aligned;
 };