Merge remote-tracking branch 'mmc-uh/next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 01:16:19 +0000 (11:16 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Tue, 13 Sep 2016 01:16:19 +0000 (11:16 +1000)
47 files changed:
Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt [deleted file]
Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
Documentation/devicetree/bindings/mmc/mmc.txt
Documentation/devicetree/bindings/mmc/sdhci-st.txt
Documentation/devicetree/bindings/mmc/sunxi-mmc.txt
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
Documentation/devicetree/bindings/mmc/tmio_mmc.txt
arch/arm/boot/dts/sun6i-a31.dtsi
arch/arm/boot/dts/sun7i-a20.dtsi
arch/arm/boot/dts/sun8i-a23-a33.dtsi
arch/arm/boot/dts/sun8i-h3.dtsi
drivers/mmc/card/block.c
drivers/mmc/card/mmc_test.c
drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
drivers/mmc/core/pwrseq_simple.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio_io.c
drivers/mmc/core/sdio_ops.c
drivers/mmc/host/davinci_mmc.c
drivers/mmc/host/dw_mmc-exynos.c
drivers/mmc/host/dw_mmc-k3.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/moxart-mmc.c
drivers/mmc/host/sdhci-acpi.c
drivers/mmc/host/sdhci-bcm-kona.c
drivers/mmc/host/sdhci-brcmstb.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-of-arasan.c
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-pci-core.c
drivers/mmc/host/sdhci-pci.h
drivers/mmc/host/sdhci-pltfm.c
drivers/mmc/host/sdhci-st.c
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/sh_mobile_sdhi.c
drivers/mmc/host/sunxi-mmc.c
drivers/mmc/host/tmio_mmc.h
drivers/mmc/host/tmio_mmc_pio.c
drivers/mmc/host/vub300.c
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/host.h

diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt
deleted file mode 100644 (file)
index 8284717..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-* BROADCOM BRCMSTB/BMIPS SDHCI Controller
-
-This file documents differences between the core properties in mmc.txt
-and the properties used by the sdhci-brcmstb driver.
-
-NOTE: The driver disables all UHS speed modes by default and depends
-on Device Tree properties to enable them for SoC/Board combinations
-that support them.
-
-Required properties:
-- compatible: "brcm,bcm7425-sdhci"
-
-Refer to clocks/clock-bindings.txt for generic clock consumer properties.
-
-Example:
-
-       sdhci@f03e0100 {
-               compatible = "brcm,bcm7425-sdhci";
-               reg = <0xf03e0000 0x100>;
-               interrupts = <0x0 0x26 0x0>;
-               sdhci,auto-cmd12;
-               clocks = <&sw_sdio>;
-               sd-uhs-sdr50;
-               sd-uhs-ddr50;
-       };
-
-       sdhci@f03e0300 {
-               non-removable;
-               bus-width = <0x8>;
-               compatible = "brcm,bcm7425-sdhci";
-               reg = <0xf03e0200 0x100>;
-               interrupts = <0x0 0x27 0x0>;
-               sdhci,auto-cmd12;
-               clocks = <sw_sdio>;
-               mmc-hs200-1_8v;
-       };
diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt
new file mode 100644 (file)
index 0000000..733b64a
--- /dev/null
@@ -0,0 +1,38 @@
+* BROADCOM BRCMSTB/BMIPS SDHCI Controller
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci-brcmstb driver.
+
+NOTE: The driver disables all UHS speed modes by default and depends
+on Device Tree properties to enable them for SoC/Board combinations
+that support them.
+
+Required properties:
+- compatible: should be one of the following
+  - "brcm,bcm7425-sdhci"
+  - "brcm,bcm7445-sdhci"
+
+Refer to clocks/clock-bindings.txt for generic clock consumer properties.
+
+Example:
+
+       sdhci@f03e0100 {
+               compatible = "brcm,bcm7425-sdhci";
+               reg = <0xf03e0000 0x100>;
+               interrupts = <0x0 0x26 0x0>;
+               sdhci,auto-cmd12;
+               clocks = <&sw_sdio>;
+               sd-uhs-sdr50;
+               sd-uhs-ddr50;
+       };
+
+       sdhci@f03e0300 {
+               non-removable;
+               bus-width = <0x8>;
+               compatible = "brcm,bcm7425-sdhci";
+               reg = <0xf03e0200 0x100>;
+               interrupts = <0x0 0x27 0x0>;
+               sdhci,auto-cmd12;
+               clocks = <sw_sdio>;
+               mmc-hs200-1_8v;
+       };
index ce0e7674967190898faa91bef1d7829ed36aeec8..e25436861867f28f75a2450ecbf28d1fd2df3921 100644 (file)
@@ -16,6 +16,8 @@ Optional properties:
   See ../clocks/clock-bindings.txt for details.
 - clock-names : Must include the following entry:
   "ext_clock" (External clock provided to the card).
+- post-power-on-delay-ms : Delay in ms after powering the card and
+       de-asserting the reset-gpios (if any)
 
 Example:
 
index 22d1e1f3f38bbf2bf86da4f8139a0288c7778c28..8a377827695bd2d13d169adc162b4129a043a422 100644 (file)
@@ -75,6 +75,17 @@ Optional SDIO properties:
 - wakeup-source: Enables wake up of host system on SDIO IRQ assertion
                 (Legacy property supported: "enable-sdio-wakeup")
 
+MMC power
+---------
+
+Controllers may implement power control from both the connected cards and
+the IO signaling (for example to change to high-speed 1.8V signalling). If
+the system supports this, then the following two properties should point
+to valid regulator nodes:
+
+- vqmmc-supply: supply node for IO line power
+- vmmc-supply: supply node for card's power
+
 
 MMC power sequences:
 --------------------
@@ -102,11 +113,13 @@ Required host node properties when using function subnodes:
 - #size-cells: should be zero.
 
 Required function subnode properties:
-- compatible: name of SDIO function following generic names recommended practice
 - reg: Must contain the SDIO function number of the function this subnode
        describes. A value of 0 denotes the memory SD function, values from
        1 to 7 denote the SDIO functions.
 
+Optional function subnode properties:
+- compatible: name of SDIO function following generic names recommended practice
+
 
 Examples
 --------
index 88faa91125bf5c92e76afcb6f0c9cdfc01031827..3cd4c43a32601957038eea8595c1e5d42651f94d 100644 (file)
@@ -10,7 +10,7 @@ Required properties:
                        subsystem (mmcss) inside the FlashSS (available in STiH407 SoC
                        family).
 
-- clock-names:         Should be "mmc".
+- clock-names:         Should be "mmc" and "icn".  (NB: The latter is not compulsory)
                        See: Documentation/devicetree/bindings/resource-names.txt
 - clocks:              Phandle to the clock.
                        See: Documentation/devicetree/bindings/clock/clock-bindings.txt
index 4bf41d8338046ef5aef2e47e4ee1dccf727acfe2..55cdd804cdbac0b078ea4e6ddbcd59d35813b989 100644 (file)
@@ -8,7 +8,12 @@ as the speed of SD standard 3.0.
 Absolute maximum transfer rate is 200MB/s
 
 Required properties:
- - compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc"
+ - compatible : should be one of:
+   * "allwinner,sun4i-a10-mmc"
+   * "allwinner,sun5i-a13-mmc"
+   * "allwinner,sun7i-a20-mmc"
+   * "allwinner,sun9i-a80-mmc"
+   * "allwinner,sun50i-a64-mmc"
  - reg : mmc controller base registers
  - clocks : a list with 4 phandle + clock specifier pairs
  - clock-names : must contain "ahb", "mmc", "output" and "sample"
index 8636f5ae97e5157ba868e4ddc8bf505465e7bcb3..4e00e859e885a0ce8ae59ae234b2637f2e894e70 100644 (file)
@@ -39,6 +39,10 @@ Required Properties:
 
 Optional properties:
 
+* resets: phandle + reset specifier pair, intended to represent hardware
+  reset signal present internally in some host controller IC designs.
+  See Documentation/devicetree/bindings/reset/reset.txt for details.
+
 * clocks: from common clock binding: handle to biu and ciu clocks for the
   bus interface unit clock and the card interface unit clock.
 
index 0f610d4b5b005f878c0491871fa8a0dec401d29c..13df9c2399c38988b0797ba05f30b0d16e4937f8 100644 (file)
@@ -23,6 +23,7 @@ Required properties:
                "renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC
                "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
                "renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
+               "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
 
 Optional properties:
 - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
index 6a84fe7e9ab267c2eb9a5df4280d373c850aa659..ce1960453a0bb96bda88162a50e4826a0fdfb172 100644 (file)
                };
 
                mmc0: mmc@01c0f000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c0f000 0x1000>;
                        clocks = <&ccu CLK_AHB1_MMC0>,
                                 <&ccu CLK_MMC0>,
                };
 
                mmc1: mmc@01c10000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c10000 0x1000>;
                        clocks = <&ccu CLK_AHB1_MMC1>,
                                 <&ccu CLK_MMC1>,
                };
 
                mmc2: mmc@01c11000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c11000 0x1000>;
                        clocks = <&ccu CLK_AHB1_MMC2>,
                                 <&ccu CLK_MMC2>,
                };
 
                mmc3: mmc@01c12000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c12000 0x1000>;
                        clocks = <&ccu CLK_AHB1_MMC3>,
                                 <&ccu CLK_MMC3>,
index bd0c476602438f40820d287c531d4523f5fcb675..94cf5a1c7172371b3c5691e831a238dd563d83c7 100644 (file)
                };
 
                mmc0: mmc@01c0f000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c0f000 0x1000>;
                        clocks = <&ahb_gates 8>,
                                 <&mmc0_clk 0>,
                };
 
                mmc1: mmc@01c10000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c10000 0x1000>;
                        clocks = <&ahb_gates 9>,
                                 <&mmc1_clk 0>,
                };
 
                mmc2: mmc@01c11000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c11000 0x1000>;
                        clocks = <&ahb_gates 10>,
                                 <&mmc2_clk 0>,
                };
 
                mmc3: mmc@01c12000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c12000 0x1000>;
                        clocks = <&ahb_gates 11>,
                                 <&mmc3_clk 0>,
index 01d8bbf08749e3f5ae3294fa18e0327e61e2fbfe..09204317535f9dbc63b69c339ffa00ce328da5b3 100644 (file)
                };
 
                mmc0: mmc@01c0f000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c0f000 0x1000>;
                        clocks = <&ccu CLK_BUS_MMC0>,
                                 <&ccu CLK_MMC0>,
                };
 
                mmc1: mmc@01c10000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c10000 0x1000>;
                        clocks = <&ccu CLK_BUS_MMC1>,
                                 <&ccu CLK_MMC1>,
                };
 
                mmc2: mmc@01c11000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c11000 0x1000>;
                        clocks = <&ccu CLK_BUS_MMC2>,
                                 <&ccu CLK_MMC2>,
index 6d83b8674201d9a977b22b8858d177b966407956..9ea313d9fa3a0e2f7ef433898b26d28d09da8eeb 100644 (file)
                };
 
                mmc0: mmc@01c0f000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c0f000 0x1000>;
                        clocks = <&ccu CLK_BUS_MMC0>,
                                 <&ccu CLK_MMC0>,
                };
 
                mmc1: mmc@01c10000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c10000 0x1000>;
                        clocks = <&ccu CLK_BUS_MMC1>,
                                 <&ccu CLK_MMC1>,
                };
 
                mmc2: mmc@01c11000 {
-                       compatible = "allwinner,sun5i-a13-mmc";
+                       compatible = "allwinner,sun7i-a20-mmc";
                        reg = <0x01c11000 0x1000>;
                        clocks = <&ccu CLK_BUS_MMC2>,
                                 <&ccu CLK_MMC2>,
index 2206d4477dbbdb5190906e277124580b600d71e0..03670aa5ac8e5025b107a87e82cacccc97fded32 100644 (file)
@@ -142,8 +142,6 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq)
 {
        struct mmc_packed *packed = mqrq->packed;
 
-       BUG_ON(!packed);
-
        mqrq->cmd_type = MMC_PACKED_NONE;
        packed->nr_entries = MMC_PACKED_NR_ZERO;
        packed->idx_failure = MMC_PACKED_NR_IDX;
@@ -1443,8 +1441,6 @@ static int mmc_blk_packed_err_check(struct mmc_card *card,
        int err, check, status;
        u8 *ext_csd;
 
-       BUG_ON(!packed);
-
        packed->retries--;
        check = mmc_blk_err_check(card, areq);
        err = get_card_status(card, &status, 0);
@@ -1673,6 +1669,18 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
        u8 max_packed_rw = 0;
        u8 reqs = 0;
 
+       /*
+        * We don't need to check packed for any further
+        * operation of packed stuff as we set MMC_PACKED_NONE
+        * and return zero for reqs if geting null packed. Also
+        * we clean the flag of MMC_BLK_PACKED_CMD to avoid doing
+        * it again when removing blk req.
+        */
+       if (!mqrq->packed) {
+               md->flags &= (~MMC_BLK_PACKED_CMD);
+               goto no_packed;
+       }
+
        if (!(md->flags & MMC_BLK_PACKED_CMD))
                goto no_packed;
 
@@ -1782,8 +1790,6 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
        u8 hdr_blocks;
        u8 i = 1;
 
-       BUG_ON(!packed);
-
        mqrq->cmd_type = MMC_PACKED_WRITE;
        packed->blocks = 0;
        packed->idx_failure = MMC_PACKED_NR_IDX;
@@ -1887,8 +1893,6 @@ static int mmc_blk_end_packed_req(struct mmc_queue_req *mq_rq)
        int idx = packed->idx_failure, i = 0;
        int ret = 0;
 
-       BUG_ON(!packed);
-
        while (!list_empty(&packed->list)) {
                prq = list_entry_rq(packed->list.next);
                if (idx == i) {
@@ -1917,8 +1921,6 @@ static void mmc_blk_abort_packed_req(struct mmc_queue_req *mq_rq)
        struct request *prq;
        struct mmc_packed *packed = mq_rq->packed;
 
-       BUG_ON(!packed);
-
        while (!list_empty(&packed->list)) {
                prq = list_entry_rq(packed->list.next);
                list_del_init(&prq->queuelist);
@@ -1935,8 +1937,6 @@ static void mmc_blk_revert_packed_req(struct mmc_queue *mq,
        struct request_queue *q = mq->queue;
        struct mmc_packed *packed = mq_rq->packed;
 
-       BUG_ON(!packed);
-
        while (!list_empty(&packed->list)) {
                prq = list_entry_rq(packed->list.prev);
                if (prq->queuelist.prev != &packed->list) {
@@ -2303,7 +2303,8 @@ again:
        set_capacity(md->disk, size);
 
        if (mmc_host_cmd23(card->host)) {
-               if (mmc_card_mmc(card) ||
+               if ((mmc_card_mmc(card) &&
+                    card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
                    (mmc_card_sd(card) &&
                     card->scr.cmds & SD_SCR_CMD23_SUPPORT))
                        md->flags |= MMC_BLK_CMD23;
index c032eef45762c1dfb2ef810add07e1d3f980eee5..5a8dc5a76e0dffae3fef30d3eb93591994d733c8 100644 (file)
@@ -184,6 +184,29 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
        return mmc_set_blocklen(test->card, size);
 }
 
+static bool mmc_test_card_cmd23(struct mmc_card *card)
+{
+       return mmc_card_mmc(card) ||
+              (mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT);
+}
+
+static void mmc_test_prepare_sbc(struct mmc_test_card *test,
+                                struct mmc_request *mrq, unsigned int blocks)
+{
+       struct mmc_card *card = test->card;
+
+       if (!mrq->sbc || !mmc_host_cmd23(card->host) ||
+           !mmc_test_card_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
+           (card->quirks & MMC_QUIRK_BLK_NO_CMD23)) {
+               mrq->sbc = NULL;
+               return;
+       }
+
+       mrq->sbc->opcode = MMC_SET_BLOCK_COUNT;
+       mrq->sbc->arg = blocks;
+       mrq->sbc->flags = MMC_RSP_R1 | MMC_CMD_AC;
+}
+
 /*
  * Fill in the mmc_request structure given a set of transfer parameters.
  */
@@ -221,6 +244,8 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
        mrq->data->sg = sg;
        mrq->data->sg_len = sg_len;
 
+       mmc_test_prepare_sbc(test, mrq, blocks);
+
        mmc_set_data_timeout(mrq->data, test->card);
 }
 
@@ -693,6 +718,8 @@ static int mmc_test_check_result(struct mmc_test_card *test,
 
        ret = 0;
 
+       if (mrq->sbc && mrq->sbc->error)
+               ret = mrq->sbc->error;
        if (!ret && mrq->cmd->error)
                ret = mrq->cmd->error;
        if (!ret && mrq->data->error)
@@ -2278,6 +2305,245 @@ static int mmc_test_reset(struct mmc_test_card *test)
        return RESULT_FAIL;
 }
 
+struct mmc_test_req {
+       struct mmc_request mrq;
+       struct mmc_command sbc;
+       struct mmc_command cmd;
+       struct mmc_command stop;
+       struct mmc_command status;
+       struct mmc_data data;
+};
+
+static struct mmc_test_req *mmc_test_req_alloc(void)
+{
+       struct mmc_test_req *rq = kzalloc(sizeof(*rq), GFP_KERNEL);
+
+       if (rq) {
+               rq->mrq.cmd = &rq->cmd;
+               rq->mrq.data = &rq->data;
+               rq->mrq.stop = &rq->stop;
+       }
+
+       return rq;
+}
+
+static int mmc_test_send_status(struct mmc_test_card *test,
+                               struct mmc_command *cmd)
+{
+       memset(cmd, 0, sizeof(*cmd));
+
+       cmd->opcode = MMC_SEND_STATUS;
+       if (!mmc_host_is_spi(test->card->host))
+               cmd->arg = test->card->rca << 16;
+       cmd->flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+       return mmc_wait_for_cmd(test->card->host, cmd, 0);
+}
+
+static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
+                                    unsigned int dev_addr, int use_sbc,
+                                    int repeat_cmd, int write, int use_areq)
+{
+       struct mmc_test_req *rq = mmc_test_req_alloc();
+       struct mmc_host *host = test->card->host;
+       struct mmc_test_area *t = &test->area;
+       struct mmc_async_req areq;
+       struct mmc_request *mrq;
+       unsigned long timeout;
+       bool expired = false;
+       int ret = 0, cmd_ret;
+       u32 status = 0;
+       int count = 0;
+
+       if (!rq)
+               return -ENOMEM;
+
+       mrq = &rq->mrq;
+       if (use_sbc)
+               mrq->sbc = &rq->sbc;
+       mrq->cap_cmd_during_tfr = true;
+
+       areq.mrq = mrq;
+       areq.err_check = mmc_test_check_result_async;
+
+       mmc_test_prepare_mrq(test, mrq, t->sg, t->sg_len, dev_addr, t->blocks,
+                            512, write);
+
+       if (use_sbc && t->blocks > 1 && !mrq->sbc) {
+               ret =  mmc_host_cmd23(host) ?
+                      RESULT_UNSUP_CARD :
+                      RESULT_UNSUP_HOST;
+               goto out_free;
+       }
+
+       /* Start ongoing data request */
+       if (use_areq) {
+               mmc_start_req(host, &areq, &ret);
+               if (ret)
+                       goto out_free;
+       } else {
+               mmc_wait_for_req(host, mrq);
+       }
+
+       timeout = jiffies + msecs_to_jiffies(3000);
+       do {
+               count += 1;
+
+               /* Send status command while data transfer in progress */
+               cmd_ret = mmc_test_send_status(test, &rq->status);
+               if (cmd_ret)
+                       break;
+
+               status = rq->status.resp[0];
+               if (status & R1_ERROR) {
+                       cmd_ret = -EIO;
+                       break;
+               }
+
+               if (mmc_is_req_done(host, mrq))
+                       break;
+
+               expired = time_after(jiffies, timeout);
+               if (expired) {
+                       pr_info("%s: timeout waiting for Tran state status %#x\n",
+                               mmc_hostname(host), status);
+                       cmd_ret = -ETIMEDOUT;
+                       break;
+               }
+       } while (repeat_cmd && R1_CURRENT_STATE(status) != R1_STATE_TRAN);
+
+       /* Wait for data request to complete */
+       if (use_areq)
+               mmc_start_req(host, NULL, &ret);
+       else
+               mmc_wait_for_req_done(test->card->host, mrq);
+
+       /*
+        * For cap_cmd_during_tfr request, upper layer must send stop if
+        * required.
+        */
+       if (mrq->data->stop && (mrq->data->error || !mrq->sbc)) {
+               if (ret)
+                       mmc_wait_for_cmd(host, mrq->data->stop, 0);
+               else
+                       ret = mmc_wait_for_cmd(host, mrq->data->stop, 0);
+       }
+
+       if (ret)
+               goto out_free;
+
+       if (cmd_ret) {
+               pr_info("%s: Send Status failed: status %#x, error %d\n",
+                       mmc_hostname(test->card->host), status, cmd_ret);
+       }
+
+       ret = mmc_test_check_result(test, mrq);
+       if (ret)
+               goto out_free;
+
+       ret = mmc_test_wait_busy(test);
+       if (ret)
+               goto out_free;
+
+       if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr)
+               pr_info("%s: %d commands completed during transfer of %u blocks\n",
+                       mmc_hostname(test->card->host), count, t->blocks);
+
+       if (cmd_ret)
+               ret = cmd_ret;
+out_free:
+       kfree(rq);
+
+       return ret;
+}
+
+static int __mmc_test_cmds_during_tfr(struct mmc_test_card *test,
+                                     unsigned long sz, int use_sbc, int write,
+                                     int use_areq)
+{
+       struct mmc_test_area *t = &test->area;
+       int ret;
+
+       if (!(test->card->host->caps & MMC_CAP_CMD_DURING_TFR))
+               return RESULT_UNSUP_HOST;
+
+       ret = mmc_test_area_map(test, sz, 0, 0);
+       if (ret)
+               return ret;
+
+       ret = mmc_test_ongoing_transfer(test, t->dev_addr, use_sbc, 0, write,
+                                       use_areq);
+       if (ret)
+               return ret;
+
+       return mmc_test_ongoing_transfer(test, t->dev_addr, use_sbc, 1, write,
+                                        use_areq);
+}
+
+static int mmc_test_cmds_during_tfr(struct mmc_test_card *test, int use_sbc,
+                                   int write, int use_areq)
+{
+       struct mmc_test_area *t = &test->area;
+       unsigned long sz;
+       int ret;
+
+       for (sz = 512; sz <= t->max_tfr; sz += 512) {
+               ret = __mmc_test_cmds_during_tfr(test, sz, use_sbc, write,
+                                                use_areq);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+/*
+ * Commands during read - no Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_read(struct mmc_test_card *test)
+{
+       return mmc_test_cmds_during_tfr(test, 0, 0, 0);
+}
+
+/*
+ * Commands during write - no Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_write(struct mmc_test_card *test)
+{
+       return mmc_test_cmds_during_tfr(test, 0, 1, 0);
+}
+
+/*
+ * Commands during read - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_read_cmd23(struct mmc_test_card *test)
+{
+       return mmc_test_cmds_during_tfr(test, 1, 0, 0);
+}
+
+/*
+ * Commands during write - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_write_cmd23(struct mmc_test_card *test)
+{
+       return mmc_test_cmds_during_tfr(test, 1, 1, 0);
+}
+
+/*
+ * Commands during non-blocking read - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_read_cmd23_nonblock(struct mmc_test_card *test)
+{
+       return mmc_test_cmds_during_tfr(test, 1, 0, 1);
+}
+
+/*
+ * Commands during non-blocking write - use Set Block Count (CMD23).
+ */
+static int mmc_test_cmds_during_write_cmd23_nonblock(struct mmc_test_card *test)
+{
+       return mmc_test_cmds_during_tfr(test, 1, 1, 1);
+}
+
 static const struct mmc_test_case mmc_test_cases[] = {
        {
                .name = "Basic write (no data verification)",
@@ -2605,6 +2871,48 @@ static const struct mmc_test_case mmc_test_cases[] = {
                .name = "Reset test",
                .run = mmc_test_reset,
        },
+
+       {
+               .name = "Commands during read - no Set Block Count (CMD23)",
+               .prepare = mmc_test_area_prepare,
+               .run = mmc_test_cmds_during_read,
+               .cleanup = mmc_test_area_cleanup,
+       },
+
+       {
+               .name = "Commands during write - no Set Block Count (CMD23)",
+               .prepare = mmc_test_area_prepare,
+               .run = mmc_test_cmds_during_write,
+               .cleanup = mmc_test_area_cleanup,
+       },
+
+       {
+               .name = "Commands during read - use Set Block Count (CMD23)",
+               .prepare = mmc_test_area_prepare,
+               .run = mmc_test_cmds_during_read_cmd23,
+               .cleanup = mmc_test_area_cleanup,
+       },
+
+       {
+               .name = "Commands during write - use Set Block Count (CMD23)",
+               .prepare = mmc_test_area_prepare,
+               .run = mmc_test_cmds_during_write_cmd23,
+               .cleanup = mmc_test_area_cleanup,
+       },
+
+       {
+               .name = "Commands during non-blocking read - use Set Block Count (CMD23)",
+               .prepare = mmc_test_area_prepare,
+               .run = mmc_test_cmds_during_read_cmd23_nonblock,
+               .cleanup = mmc_test_area_cleanup,
+       },
+
+       {
+               .name = "Commands during non-blocking write - use Set Block Count (CMD23)",
+               .prepare = mmc_test_area_prepare,
+               .run = mmc_test_cmds_during_write_cmd23_nonblock,
+               .cleanup = mmc_test_area_cleanup,
+       },
 };
 
 static DEFINE_MUTEX(mmc_test_lock);
index e55cde6d436dddae261c69ef36ca4de93eaa01ac..f0ed0afe033dc753c921bbf72045ff6a2ee114e9 100644 (file)
@@ -58,6 +58,9 @@
  */
 #define MMC_BKOPS_MAX_TIMEOUT  (4 * 60 * 1000) /* max time to wait in ms */
 
+/* The max erase timeout, used when host->max_busy_timeout isn't specified */
+#define MMC_ERASE_TIMEOUT_MS   (60 * 1000) /* 60 s */
+
 static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
 
 /*
@@ -117,6 +120,24 @@ static inline void mmc_should_fail_request(struct mmc_host *host,
 
 #endif /* CONFIG_FAIL_MMC_REQUEST */
 
+static inline void mmc_complete_cmd(struct mmc_request *mrq)
+{
+       if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion))
+               complete_all(&mrq->cmd_completion);
+}
+
+void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq)
+{
+       if (!mrq->cap_cmd_during_tfr)
+               return;
+
+       mmc_complete_cmd(mrq);
+
+       pr_debug("%s: cmd done, tfr ongoing (CMD%u)\n",
+                mmc_hostname(host), mrq->cmd->opcode);
+}
+EXPORT_SYMBOL(mmc_command_done);
+
 /**
  *     mmc_request_done - finish processing an MMC request
  *     @host: MMC host which completed request
@@ -143,6 +164,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
                        cmd->retries = 0;
        }
 
+       if (host->ongoing_mrq == mrq)
+               host->ongoing_mrq = NULL;
+
+       mmc_complete_cmd(mrq);
+
        trace_mmc_request_done(host, mrq);
 
        if (err && cmd->retries && !mmc_card_removed(host->card)) {
@@ -155,7 +181,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
        } else {
                mmc_should_fail_request(host, mrq);
 
-               led_trigger_event(host->led, LED_OFF);
+               if (!host->ongoing_mrq)
+                       led_trigger_event(host->led, LED_OFF);
 
                if (mrq->sbc) {
                        pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n",
@@ -220,6 +247,15 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
                }
        }
 
+       if (mrq->cap_cmd_during_tfr) {
+               host->ongoing_mrq = mrq;
+               /*
+                * Retry path could come through here without having waiting on
+                * cmd_completion, so ensure it is reinitialised.
+                */
+               reinit_completion(&mrq->cmd_completion);
+       }
+
        trace_mmc_request_start(host, mrq);
 
        host->ops->request(host, mrq);
@@ -386,6 +422,18 @@ static void mmc_wait_done(struct mmc_request *mrq)
        complete(&mrq->completion);
 }
 
+static inline void mmc_wait_ongoing_tfr_cmd(struct mmc_host *host)
+{
+       struct mmc_request *ongoing_mrq = READ_ONCE(host->ongoing_mrq);
+
+       /*
+        * If there is an ongoing transfer, wait for the command line to become
+        * available.
+        */
+       if (ongoing_mrq && !completion_done(&ongoing_mrq->cmd_completion))
+               wait_for_completion(&ongoing_mrq->cmd_completion);
+}
+
 /*
  *__mmc_start_data_req() - starts data request
  * @host: MMC host to start the request
@@ -393,17 +441,24 @@ static void mmc_wait_done(struct mmc_request *mrq)
  *
  * Sets the done callback to be called when request is completed by the card.
  * Starts data mmc request execution
+ * If an ongoing transfer is already in progress, wait for the command line
+ * to become available before sending another command.
  */
 static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
 {
        int err;
 
+       mmc_wait_ongoing_tfr_cmd(host);
+
        mrq->done = mmc_wait_data_done;
        mrq->host = host;
 
+       init_completion(&mrq->cmd_completion);
+
        err = mmc_start_request(host, mrq);
        if (err) {
                mrq->cmd->error = err;
+               mmc_complete_cmd(mrq);
                mmc_wait_data_done(mrq);
        }
 
@@ -414,12 +469,17 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
 {
        int err;
 
+       mmc_wait_ongoing_tfr_cmd(host);
+
        init_completion(&mrq->completion);
        mrq->done = mmc_wait_done;
 
+       init_completion(&mrq->cmd_completion);
+
        err = mmc_start_request(host, mrq);
        if (err) {
                mrq->cmd->error = err;
+               mmc_complete_cmd(mrq);
                complete(&mrq->completion);
        }
 
@@ -483,8 +543,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
        return err;
 }
 
-static void mmc_wait_for_req_done(struct mmc_host *host,
-                                 struct mmc_request *mrq)
+void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
 {
        struct mmc_command *cmd;
 
@@ -525,6 +584,28 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
 
        mmc_retune_release(host);
 }
+EXPORT_SYMBOL(mmc_wait_for_req_done);
+
+/**
+ *     mmc_is_req_done - Determine if a 'cap_cmd_during_tfr' request is done
+ *     @host: MMC host
+ *     @mrq: MMC request
+ *
+ *     mmc_is_req_done() is used with requests that have
+ *     mrq->cap_cmd_during_tfr = true. mmc_is_req_done() must be called after
+ *     starting a request and before waiting for it to complete. That is,
+ *     either in between calls to mmc_start_req(), or after mmc_wait_for_req()
+ *     and before mmc_wait_for_req_done(). If it is called at other times the
+ *     result is not meaningful.
+ */
+bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq)
+{
+       if (host->areq)
+               return host->context_info.is_done_rcv;
+       else
+               return completion_done(&mrq->completion);
+}
+EXPORT_SYMBOL(mmc_is_req_done);
 
 /**
  *     mmc_pre_req - Prepare for a new request
@@ -645,13 +726,18 @@ EXPORT_SYMBOL(mmc_start_req);
  *     @mrq: MMC request to start
  *
  *     Start a new MMC custom command request for a host, and wait
- *     for the command to complete. Does not attempt to parse the
- *     response.
+ *     for the command to complete. In the case of 'cap_cmd_during_tfr'
+ *     requests, the transfer is ongoing and the caller can issue further
+ *     commands that do not use the data lines, and then wait by calling
+ *     mmc_wait_for_req_done().
+ *     Does not attempt to parse the response.
  */
 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 {
        __mmc_start_req(host, mrq);
-       mmc_wait_for_req_done(host, mrq);
+
+       if (!mrq->cap_cmd_during_tfr)
+               mmc_wait_for_req_done(host, mrq);
 }
 EXPORT_SYMBOL(mmc_wait_for_req);
 
@@ -2202,6 +2288,54 @@ out:
        return err;
 }
 
+static unsigned int mmc_align_erase_size(struct mmc_card *card,
+                                        unsigned int *from,
+                                        unsigned int *to,
+                                        unsigned int nr)
+{
+       unsigned int from_new = *from, nr_new = nr, rem;
+
+       /*
+        * When the 'card->erase_size' is power of 2, we can use round_up/down()
+        * to align the erase size efficiently.
+        */
+       if (is_power_of_2(card->erase_size)) {
+               unsigned int temp = from_new;
+
+               from_new = round_up(temp, card->erase_size);
+               rem = from_new - temp;
+
+               if (nr_new > rem)
+                       nr_new -= rem;
+               else
+                       return 0;
+
+               nr_new = round_down(nr_new, card->erase_size);
+       } else {
+               rem = from_new % card->erase_size;
+               if (rem) {
+                       rem = card->erase_size - rem;
+                       from_new += rem;
+                       if (nr_new > rem)
+                               nr_new -= rem;
+                       else
+                               return 0;
+               }
+
+               rem = nr_new % card->erase_size;
+               if (rem)
+                       nr_new -= rem;
+       }
+
+       if (nr_new == 0)
+               return 0;
+
+       *to = from_new + nr_new;
+       *from = from_new;
+
+       return nr_new;
+}
+
 /**
  * mmc_erase - erase sectors.
  * @card: card to erase
@@ -2240,26 +2374,12 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
                        return -EINVAL;
        }
 
-       if (arg == MMC_ERASE_ARG) {
-               rem = from % card->erase_size;
-               if (rem) {
-                       rem = card->erase_size - rem;
-                       from += rem;
-                       if (nr > rem)
-                               nr -= rem;
-                       else
-                               return 0;
-               }
-               rem = nr % card->erase_size;
-               if (rem)
-                       nr -= rem;
-       }
+       if (arg == MMC_ERASE_ARG)
+               nr = mmc_align_erase_size(card, &from, &to, nr);
 
        if (nr == 0)
                return 0;
 
-       to = from + nr;
-
        if (to <= from)
                return -EINVAL;
 
@@ -2352,6 +2472,8 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
        struct mmc_host *host = card->host;
        unsigned int max_discard, x, y, qty = 0, max_qty, min_qty, timeout;
        unsigned int last_timeout = 0;
+       unsigned int max_busy_timeout = host->max_busy_timeout ?
+                       host->max_busy_timeout : MMC_ERASE_TIMEOUT_MS;
 
        if (card->erase_shift) {
                max_qty = UINT_MAX >> card->erase_shift;
@@ -2374,15 +2496,15 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
         * matter what size of 'host->max_busy_timeout', but if the
         * 'host->max_busy_timeout' is large enough for more discard sectors,
         * then we can continue to increase the max discard sectors until we
-        * get a balance value.
+        * get a balance value. In cases when the 'host->max_busy_timeout'
+        * isn't specified, use the default max erase timeout.
         */
        do {
                y = 0;
                for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
                        timeout = mmc_erase_timeout(card, arg, qty + x);
 
-                       if (qty + x > min_qty &&
-                           timeout > host->max_busy_timeout)
+                       if (qty + x > min_qty && timeout > max_busy_timeout)
                                break;
 
                        if (timeout < last_timeout)
@@ -2427,9 +2549,6 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
        struct mmc_host *host = card->host;
        unsigned int max_discard, max_trim;
 
-       if (!host->max_busy_timeout)
-               return UINT_MAX;
-
        /*
         * Without erase_group_def set, MMC erase timeout depends on clock
         * frequence which can change.  In that case, the best choice is
@@ -2447,7 +2566,8 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
                max_discard = 0;
        }
        pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
-                mmc_hostname(host), max_discard, host->max_busy_timeout);
+               mmc_hostname(host), max_discard, host->max_busy_timeout ?
+               host->max_busy_timeout : MMC_ERASE_TIMEOUT_MS);
        return max_discard;
 }
 EXPORT_SYMBOL(mmc_calc_max_discard);
index f2d185cf8a8ba52e8f68651c2c517e55313c9d57..3486bc7fbb64a67a4cf1156c2ac7652541ef60bb 100644 (file)
@@ -1029,6 +1029,10 @@ static int mmc_select_hs(struct mmc_card *card)
                err = mmc_switch_status(card);
        }
 
+       if (err)
+               pr_warn("%s: switch to high-speed failed, err:%d\n",
+                       mmc_hostname(card->host), err);
+
        return err;
 }
 
@@ -1265,11 +1269,8 @@ static int mmc_select_hs400es(struct mmc_card *card)
 
        /* Switch card to HS mode */
        err = mmc_select_hs(card);
-       if (err) {
-               pr_err("%s: switch to high-speed failed, err:%d\n",
-                       mmc_hostname(host), err);
+       if (err)
                goto out_err;
-       }
 
        err = mmc_switch_status(card);
        if (err)
index 450d907c6e6c618be461acd21dfd0509f08dd35e..1304160de16828f402dad6ff1f970af70764c3b2 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/property.h>
 
 #include <linux/mmc/host.h>
 
@@ -24,6 +26,7 @@
 struct mmc_pwrseq_simple {
        struct mmc_pwrseq pwrseq;
        bool clk_enabled;
+       u32 post_power_on_delay_ms;
        struct clk *ext_clk;
        struct gpio_descs *reset_gpios;
 };
@@ -64,6 +67,9 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
        struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq);
 
        mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
+
+       if (pwrseq->post_power_on_delay_ms)
+               msleep(pwrseq->post_power_on_delay_ms);
 }
 
 static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
@@ -111,6 +117,9 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
                return PTR_ERR(pwrseq->reset_gpios);
        }
 
+       device_property_read_u32(dev, "post-power-on-delay-ms",
+                                &pwrseq->post_power_on_delay_ms);
+
        pwrseq->pwrseq.dev = dev;
        pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
        pwrseq->pwrseq.owner = THIS_MODULE;
index 0123936241b06320cf15abb5463b87399c999838..73c762a28dfed886bc6a2dd2e91cfe43343b361f 100644 (file)
@@ -223,8 +223,7 @@ static int mmc_decode_scr(struct mmc_card *card)
 static int mmc_read_ssr(struct mmc_card *card)
 {
        unsigned int au, es, et, eo;
-       int err, i;
-       u32 *ssr;
+       int i;
 
        if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
                pr_warn("%s: card lacks mandatory SD Status function\n",
@@ -232,33 +231,27 @@ static int mmc_read_ssr(struct mmc_card *card)
                return 0;
        }
 
-       ssr = kmalloc(64, GFP_KERNEL);
-       if (!ssr)
-               return -ENOMEM;
-
-       err = mmc_app_sd_status(card, ssr);
-       if (err) {
+       if (mmc_app_sd_status(card, card->raw_ssr)) {
                pr_warn("%s: problem reading SD Status register\n",
                        mmc_hostname(card->host));
-               err = 0;
-               goto out;
+               return 0;
        }
 
        for (i = 0; i < 16; i++)
-               ssr[i] = be32_to_cpu(ssr[i]);
+               card->raw_ssr[i] = be32_to_cpu(card->raw_ssr[i]);
 
        /*
         * UNSTUFF_BITS only works with four u32s so we have to offset the
         * bitfield positions accordingly.
         */
-       au = UNSTUFF_BITS(ssr, 428 - 384, 4);
+       au = UNSTUFF_BITS(card->raw_ssr, 428 - 384, 4);
        if (au) {
                if (au <= 9 || card->scr.sda_spec3) {
                        card->ssr.au = sd_au_size[au];
-                       es = UNSTUFF_BITS(ssr, 408 - 384, 16);
-                       et = UNSTUFF_BITS(ssr, 402 - 384, 6);
+                       es = UNSTUFF_BITS(card->raw_ssr, 408 - 384, 16);
+                       et = UNSTUFF_BITS(card->raw_ssr, 402 - 384, 6);
                        if (es && et) {
-                               eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
+                               eo = UNSTUFF_BITS(card->raw_ssr, 400 - 384, 2);
                                card->ssr.erase_timeout = (et * 1000) / es;
                                card->ssr.erase_offset = eo * 1000;
                        }
@@ -267,9 +260,8 @@ static int mmc_read_ssr(struct mmc_card *card)
                                mmc_hostname(card->host));
                }
        }
-out:
-       kfree(ssr);
-       return err;
+
+       return 0;
 }
 
 /*
@@ -666,6 +658,14 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
 MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
        card->raw_csd[2], card->raw_csd[3]);
 MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
+MMC_DEV_ATTR(ssr,
+       "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x\n",
+               card->raw_ssr[0], card->raw_ssr[1], card->raw_ssr[2],
+               card->raw_ssr[3], card->raw_ssr[4], card->raw_ssr[5],
+               card->raw_ssr[6], card->raw_ssr[7], card->raw_ssr[8],
+               card->raw_ssr[9], card->raw_ssr[10], card->raw_ssr[11],
+               card->raw_ssr[12], card->raw_ssr[13], card->raw_ssr[14],
+               card->raw_ssr[15]);
 MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
 MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
 MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
@@ -698,6 +698,7 @@ static struct attribute *sd_std_attrs[] = {
        &dev_attr_cid.attr,
        &dev_attr_csd.attr,
        &dev_attr_scr.attr,
+       &dev_attr_ssr.attr,
        &dev_attr_date.attr,
        &dev_attr_erase_size.attr,
        &dev_attr_preferred_erase_size.attr,
index 78cb4d5d9d58184754973a5e3eee1c64a635deb5..406e5f037e3203618e2997498df58110563e19d6 100644 (file)
@@ -26,8 +26,8 @@
  */
 void sdio_claim_host(struct sdio_func *func)
 {
-       BUG_ON(!func);
-       BUG_ON(!func->card);
+       if (WARN_ON(!func))
+               return;
 
        mmc_claim_host(func->card->host);
 }
@@ -42,8 +42,8 @@ EXPORT_SYMBOL_GPL(sdio_claim_host);
  */
 void sdio_release_host(struct sdio_func *func)
 {
-       BUG_ON(!func);
-       BUG_ON(!func->card);
+       if (WARN_ON(!func))
+               return;
 
        mmc_release_host(func->card->host);
 }
@@ -62,8 +62,8 @@ int sdio_enable_func(struct sdio_func *func)
        unsigned char reg;
        unsigned long timeout;
 
-       BUG_ON(!func);
-       BUG_ON(!func->card);
+       if (!func)
+               return -EINVAL;
 
        pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
 
@@ -112,8 +112,8 @@ int sdio_disable_func(struct sdio_func *func)
        int ret;
        unsigned char reg;
 
-       BUG_ON(!func);
-       BUG_ON(!func->card);
+       if (!func)
+               return -EINVAL;
 
        pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
 
@@ -307,6 +307,9 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
        unsigned max_blocks;
        int ret;
 
+       if (!func || (func->num > 7))
+               return -EINVAL;
+
        /* Do the bulk of the transfer using block mode (if supported). */
        if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
                /* Blocks per command is limited by host count, host transfer
@@ -367,7 +370,10 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
        int ret;
        u8 val;
 
-       BUG_ON(!func);
+       if (!func) {
+               *err_ret = -EINVAL;
+               return 0xFF;
+       }
 
        if (err_ret)
                *err_ret = 0;
@@ -398,7 +404,10 @@ void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)
 {
        int ret;
 
-       BUG_ON(!func);
+       if (!func) {
+               *err_ret = -EINVAL;
+               return;
+       }
 
        ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
        if (err_ret)
@@ -623,7 +632,10 @@ unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
        int ret;
        unsigned char val;
 
-       BUG_ON(!func);
+       if (!func) {
+               *err_ret = -EINVAL;
+               return 0xFF;
+       }
 
        if (err_ret)
                *err_ret = 0;
@@ -658,7 +670,10 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
 {
        int ret;
 
-       BUG_ON(!func);
+       if (!func) {
+               *err_ret = -EINVAL;
+               return;
+       }
 
        if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) {
                if (err_ret)
@@ -684,8 +699,8 @@ EXPORT_SYMBOL_GPL(sdio_f0_writeb);
  */
 mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func)
 {
-       BUG_ON(!func);
-       BUG_ON(!func->card);
+       if (!func)
+               return 0;
 
        return func->card->host->pm_caps;
 }
@@ -707,8 +722,8 @@ int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
 {
        struct mmc_host *host;
 
-       BUG_ON(!func);
-       BUG_ON(!func->card);
+       if (!func)
+               return -EINVAL;
 
        host = func->card->host;
 
index 34f6e80153064a9fa963c5784cd3fbec7f7b8673..90fe5545c67713b772da886f49b566a2c1cb59d9 100644 (file)
@@ -24,8 +24,6 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
        struct mmc_command cmd = {0};
        int i, err = 0;
 
-       BUG_ON(!host);
-
        cmd.opcode = SD_IO_SEND_OP_COND;
        cmd.arg = ocr;
        cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
@@ -71,8 +69,8 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
        struct mmc_command cmd = {0};
        int err;
 
-       BUG_ON(!host);
-       BUG_ON(fn > 7);
+       if (fn > 7)
+               return -EINVAL;
 
        /* sanity check */
        if (addr & ~0x1FFFF)
@@ -114,7 +112,6 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
 int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
        unsigned addr, u8 in, u8 *out)
 {
-       BUG_ON(!card);
        return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out);
 }
 
@@ -129,8 +126,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
        unsigned int nents, left_size, i;
        unsigned int seg_size = card->host->max_seg_size;
 
-       BUG_ON(!card);
-       BUG_ON(fn > 7);
        WARN_ON(blksz == 0);
 
        /* sanity check */
index a56373c7598315b8ddef5580b4a7e749958a9983..8fa478c3b0db7f4f05cc122d135af20c6065da78 100644 (file)
@@ -1216,9 +1216,11 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
        }
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       irq = platform_get_irq(pdev, 0);
-       if (!r || irq == NO_IRQ)
+       if (!r)
                return -ENODEV;
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
 
        mem_size = resource_size(r);
        mem = devm_request_mem_region(&pdev->dev, r->start, mem_size,
index da0ef1765735fe48a69a5faf72236598ce1a5cf2..7ab3d749b5aee1728e7aa7a37b530cd42eb741a7 100644 (file)
@@ -225,8 +225,12 @@ static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
         * Not supported to configure register
         * related to HS400
         */
-       if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420)
+       if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) {
+               if (timing == MMC_TIMING_MMC_HS400)
+                       dev_warn(host->dev,
+                                "cannot configure HS400, unsupported chipset\n");
                return;
+       }
 
        dqs = priv->saved_dqs_en;
        strobe = priv->saved_strobe_ctrl;
index 8e9d886bfcda3780cf5be23f17c7e2d0f7cc5922..624789496dcea22509184eb3d6a157a825c9a8f6 100644 (file)
@@ -131,11 +131,17 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
        host->bus_hz = clk_get_rate(host->biu_clk);
 }
 
+static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+       return 0;
+}
+
 static const struct dw_mci_drv_data hi6220_data = {
        .caps                   = dw_mci_hi6220_caps,
        .switch_voltage         = dw_mci_hi6220_switch_voltage,
        .set_ios                = dw_mci_hi6220_set_ios,
        .parse_dt               = dw_mci_hi6220_parse_dt,
+       .execute_tuning         = dw_mci_hi6220_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_k3_match[] = {
index 32380d5d4f6b15440497b952b035cfe3b76b74ea..22dacae42f84e22b502421b6a3e94f70be96dc5d 100644 (file)
@@ -1691,11 +1691,11 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
                                data->error = -ETIMEDOUT;
                        } else if (host->dir_status ==
                                        DW_MCI_RECV_STATUS) {
-                               data->error = -EIO;
+                               data->error = -EILSEQ;
                        }
                } else {
                        /* SDMMC_INT_SBE is included */
-                       data->error = -EIO;
+                       data->error = -EILSEQ;
                }
 
                dev_dbg(host->dev, "data error, status 0x%08x\n", status);
@@ -2523,47 +2523,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-#ifdef CONFIG_OF
-/* given a slot, find out the device node representing that slot */
-static struct device_node *dw_mci_of_find_slot_node(struct dw_mci_slot *slot)
-{
-       struct device *dev = slot->mmc->parent;
-       struct device_node *np;
-       const __be32 *addr;
-       int len;
-
-       if (!dev || !dev->of_node)
-               return NULL;
-
-       for_each_child_of_node(dev->of_node, np) {
-               addr = of_get_property(np, "reg", &len);
-               if (!addr || (len < sizeof(int)))
-                       continue;
-               if (be32_to_cpup(addr) == slot->id)
-                       return np;
-       }
-       return NULL;
-}
-
-static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
-{
-       struct device_node *np = dw_mci_of_find_slot_node(slot);
-
-       if (!np)
-               return;
-
-       if (of_property_read_bool(np, "disable-wp")) {
-               slot->mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
-               dev_warn(slot->mmc->parent,
-                       "Slot quirk 'disable-wp' is deprecated\n");
-       }
-}
-#else /* CONFIG_OF */
-static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
-{
-}
-#endif /* CONFIG_OF */
-
 static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 {
        struct mmc_host *mmc;
@@ -2626,8 +2585,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        if (host->pdata->caps2)
                mmc->caps2 = host->pdata->caps2;
 
-       dw_mci_slot_of_parse(slot);
-
        ret = mmc_of_parse(mmc);
        if (ret)
                goto err_host_allocated;
@@ -2915,6 +2872,13 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
        if (!pdata)
                return ERR_PTR(-ENOMEM);
 
+       /* find reset controller when exist */
+       pdata->rstc = devm_reset_control_get_optional(dev, NULL);
+       if (IS_ERR(pdata->rstc)) {
+               if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER)
+                       return ERR_PTR(-EPROBE_DEFER);
+       }
+
        /* find out number of slots supported */
        of_property_read_u32(np, "num-slots", &pdata->num_slots);
 
@@ -2986,7 +2950,9 @@ int dw_mci_probe(struct dw_mci *host)
 
        if (!host->pdata) {
                host->pdata = dw_mci_parse_dt(host);
-               if (IS_ERR(host->pdata)) {
+               if (PTR_ERR(host->pdata) == -EPROBE_DEFER) {
+                       return -EPROBE_DEFER;
+               } else if (IS_ERR(host->pdata)) {
                        dev_err(host->dev, "platform data not available\n");
                        return -EINVAL;
                }
@@ -3040,6 +3006,12 @@ int dw_mci_probe(struct dw_mci *host)
                }
        }
 
+       if (!IS_ERR(host->pdata->rstc)) {
+               reset_control_assert(host->pdata->rstc);
+               usleep_range(10, 50);
+               reset_control_deassert(host->pdata->rstc);
+       }
+
        setup_timer(&host->cmd11_timer,
                    dw_mci_cmd11_timer, (unsigned long)host);
 
@@ -3189,13 +3161,14 @@ err_dmaunmap:
        if (host->use_dma && host->dma_ops->exit)
                host->dma_ops->exit(host);
 
+       if (!IS_ERR(host->pdata->rstc))
+               reset_control_assert(host->pdata->rstc);
+
 err_clk_ciu:
-       if (!IS_ERR(host->ciu_clk))
-               clk_disable_unprepare(host->ciu_clk);
+       clk_disable_unprepare(host->ciu_clk);
 
 err_clk_biu:
-       if (!IS_ERR(host->biu_clk))
-               clk_disable_unprepare(host->biu_clk);
+       clk_disable_unprepare(host->biu_clk);
 
        return ret;
 }
@@ -3221,11 +3194,11 @@ void dw_mci_remove(struct dw_mci *host)
        if (host->use_dma && host->dma_ops->exit)
                host->dma_ops->exit(host);
 
-       if (!IS_ERR(host->ciu_clk))
-               clk_disable_unprepare(host->ciu_clk);
+       if (!IS_ERR(host->pdata->rstc))
+               reset_control_assert(host->pdata->rstc);
 
-       if (!IS_ERR(host->biu_clk))
-               clk_disable_unprepare(host->biu_clk);
+       clk_disable_unprepare(host->ciu_clk);
+       clk_disable_unprepare(host->biu_clk);
 }
 EXPORT_SYMBOL(dw_mci_remove);
 
index 79905ce895adaeffc6f8e07689af768398b272c1..bbad309679cf87b30f13969f7d2d2059fea1a07e 100644 (file)
@@ -257,7 +257,7 @@ static void moxart_dma_complete(void *param)
 static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
 {
        u32 len, dir_data, dir_slave;
-       unsigned long dma_time;
+       long dma_time;
        struct dma_async_tx_descriptor *desc = NULL;
        struct dma_chan *dma_chan;
 
@@ -397,7 +397,8 @@ static void moxart_prepare_data(struct moxart_host *host)
 static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct moxart_host *host = mmc_priv(mmc);
-       unsigned long pio_time, flags;
+       long pio_time;
+       unsigned long flags;
        u32 status;
 
        spin_lock_irqsave(&host->lock, flags);
index 8fe0756c8e1e750647b32a7ca417537742a145c0..81d4dc034793ddb72c8ddb1d29c7d1bbb65f1a13 100644 (file)
@@ -275,7 +275,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
        .chip    = &sdhci_acpi_chip_int,
        .caps    = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
                   MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
-                  MMC_CAP_WAIT_WHILE_BUSY,
+                  MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY,
        .caps2   = MMC_CAP2_HC_ERASE_SZ,
        .flags   = SDHCI_ACPI_RUNTIME_PM,
        .quirks  = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
index e5c634bdfdd909f20b6468c9fefc5b6ecca909c7..51dd2fd65000c966a89b8dc7d8263de939695e13 100644 (file)
@@ -253,12 +253,14 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
                goto err_pltfm_free;
        }
 
-       if (clk_set_rate(pltfm_priv->clk, host->mmc->f_max) != 0) {
+       ret = clk_set_rate(pltfm_priv->clk, host->mmc->f_max);
+       if (ret) {
                dev_err(dev, "Failed to set rate core clock\n");
                goto err_pltfm_free;
        }
 
-       if (clk_prepare_enable(pltfm_priv->clk) != 0) {
+       ret = clk_prepare_enable(pltfm_priv->clk);
+       if (ret) {
                dev_err(dev, "Failed to enable core clock\n");
                goto err_pltfm_free;
        }
index cce10fe3e19e4da258f84714be7a2657f8b4f457..159f6f64c68e78fb11cd86c9472df97aae9e6ad8 100644 (file)
@@ -98,6 +98,8 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
         * properties through mmc_of_parse().
         */
        host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+       if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm7425-sdhci"))
+               host->caps &= ~SDHCI_CAN_64BIT;
        host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
        host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
                        SDHCI_SUPPORT_DDR50);
@@ -121,6 +123,7 @@ err_clk:
 
 static const struct of_device_id sdhci_brcm_of_match[] = {
        { .compatible = "brcm,bcm7425-sdhci" },
+       { .compatible = "brcm,bcm7445-sdhci" },
        {},
 };
 MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
@@ -128,7 +131,6 @@ MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
 static struct platform_driver sdhci_brcmstb_driver = {
        .driver         = {
                .name   = "sdhci-brcmstb",
-               .owner  = THIS_MODULE,
                .pm     = &sdhci_brcmstb_pmops,
                .of_match_table = of_match_ptr(sdhci_brcm_of_match),
        },
index 99e0b334f9dfad6345a8a23e47d5c183bc018a90..1f54fd8755c8e026fd8fb99f7fdb1cbc154acc87 100644 (file)
@@ -31,6 +31,7 @@
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
 
+#define ESDHC_SYS_CTRL_DTOCV_MASK      0x0f
 #define        ESDHC_CTRL_D3CD                 0x08
 #define ESDHC_BURST_LEN_EN_INCR                (1 << 27)
 /* VENDOR SPEC register */
@@ -928,7 +929,8 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
 
-       return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27;
+       /* Doc Errata: the uSDHC actual maximum timeout count is 1 << 29 */
+       return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
 }
 
 static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
@@ -937,7 +939,8 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
        struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
 
        /* use maximum timeout counter */
-       sdhci_writeb(host, esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
+       esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
+                       esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
                        SDHCI_TIMEOUT_CONTROL);
 }
 
index e0f193f7e3e50c9f41efe074da079895199b6ca8..33601a817ea674dff7201046dcb810f5ca0c35b5 100644 (file)
@@ -35,6 +35,8 @@
 #define CLK_CTRL_TIMEOUT_MASK          (0xf << CLK_CTRL_TIMEOUT_SHIFT)
 #define CLK_CTRL_TIMEOUT_MIN_EXP       13
 
+#define PHY_CLK_TOO_SLOW_HZ            400000
+
 /*
  * On some SoCs the syscon area has a feature where the upper 16-bits of
  * each 32-bit register act as a write mask for the lower 16-bits.  This allows
@@ -65,10 +67,12 @@ struct sdhci_arasan_soc_ctl_field {
  * accessible via the syscon API.
  *
  * @baseclkfreq:       Where to find corecfg_baseclkfreq
+ * @clockmultiplier:   Where to find corecfg_clockmultiplier
  * @hiword_update:     If true, use HIWORD_UPDATE to access the syscon
  */
 struct sdhci_arasan_soc_ctl_map {
        struct sdhci_arasan_soc_ctl_field       baseclkfreq;
+       struct sdhci_arasan_soc_ctl_field       clockmultiplier;
        bool                                    hiword_update;
 };
 
@@ -77,6 +81,7 @@ struct sdhci_arasan_soc_ctl_map {
  * @host:              Pointer to the main SDHCI host structure.
  * @clk_ahb:           Pointer to the AHB clock
  * @phy:               Pointer to the generic phy
+ * @is_phy_on:         True if the PHY is on; false if not.
  * @sdcardclk_hw:      Struct for the clock we might provide to a PHY.
  * @sdcardclk:         Pointer to normal 'struct clock' for sdcardclk_hw.
  * @soc_ctl_base:      Pointer to regmap for syscon for soc_ctl registers.
@@ -86,6 +91,7 @@ struct sdhci_arasan_data {
        struct sdhci_host *host;
        struct clk      *clk_ahb;
        struct phy      *phy;
+       bool            is_phy_on;
 
        struct clk_hw   sdcardclk_hw;
        struct clk      *sdcardclk;
@@ -96,6 +102,7 @@ struct sdhci_arasan_data {
 
 static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
        .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 },
+       .clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0},
        .hiword_update = true,
 };
 
@@ -170,13 +177,47 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
        struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
        bool ctrl_phy = false;
 
-       if (clock > MMC_HIGH_52_MAX_DTR && (!IS_ERR(sdhci_arasan->phy)))
-               ctrl_phy = true;
+       if (!IS_ERR(sdhci_arasan->phy)) {
+               if (!sdhci_arasan->is_phy_on && clock <= PHY_CLK_TOO_SLOW_HZ) {
+                       /*
+                        * If PHY off, set clock to max speed and power PHY on.
+                        *
+                        * Although PHY docs apparently suggest power cycling
+                        * when changing the clock the PHY doesn't like to be
+                        * powered on while at low speeds like those used in ID
+                        * mode.  Even worse is powering the PHY on while the
+                        * clock is off.
+                        *
+                        * To workaround the PHY limitations, the best we can
+                        * do is to power it on at a faster speed and then slam
+                        * through low speeds without power cycling.
+                        */
+                       sdhci_set_clock(host, host->max_clk);
+                       spin_unlock_irq(&host->lock);
+                       phy_power_on(sdhci_arasan->phy);
+                       spin_lock_irq(&host->lock);
+                       sdhci_arasan->is_phy_on = true;
+
+                       /*
+                        * We'll now fall through to the below case with
+                        * ctrl_phy = false (so we won't turn off/on).  The
+                        * sdhci_set_clock() will set the real clock.
+                        */
+               } else if (clock > PHY_CLK_TOO_SLOW_HZ) {
+                       /*
+                        * At higher clock speeds the PHY is fine being power
+                        * cycled and docs say you _should_ power cycle when
+                        * changing clock speeds.
+                        */
+                       ctrl_phy = true;
+               }
+       }
 
-       if (ctrl_phy) {
+       if (ctrl_phy && sdhci_arasan->is_phy_on) {
                spin_unlock_irq(&host->lock);
                phy_power_off(sdhci_arasan->phy);
                spin_lock_irq(&host->lock);
+               sdhci_arasan->is_phy_on = false;
        }
 
        sdhci_set_clock(host, clock);
@@ -185,6 +226,7 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
                spin_unlock_irq(&host->lock);
                phy_power_on(sdhci_arasan->phy);
                spin_lock_irq(&host->lock);
+               sdhci_arasan->is_phy_on = true;
        }
 }
 
@@ -239,13 +281,14 @@ static int sdhci_arasan_suspend(struct device *dev)
        if (ret)
                return ret;
 
-       if (!IS_ERR(sdhci_arasan->phy)) {
+       if (!IS_ERR(sdhci_arasan->phy) && sdhci_arasan->is_phy_on) {
                ret = phy_power_off(sdhci_arasan->phy);
                if (ret) {
                        dev_err(dev, "Cannot power off phy.\n");
                        sdhci_resume_host(host);
                        return ret;
                }
+               sdhci_arasan->is_phy_on = false;
        }
 
        clk_disable(pltfm_host->clk);
@@ -281,12 +324,13 @@ static int sdhci_arasan_resume(struct device *dev)
                return ret;
        }
 
-       if (!IS_ERR(sdhci_arasan->phy)) {
+       if (!IS_ERR(sdhci_arasan->phy) && host->mmc->actual_clock) {
                ret = phy_power_on(sdhci_arasan->phy);
                if (ret) {
                        dev_err(dev, "Cannot power on phy.\n");
                        return ret;
                }
+               sdhci_arasan->is_phy_on = true;
        }
 
        return sdhci_resume_host(host);
@@ -337,6 +381,45 @@ static const struct clk_ops arasan_sdcardclk_ops = {
        .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
 };
 
+/**
+ * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier
+ *
+ * The corecfg_clockmultiplier is supposed to contain clock multiplier
+ * value of programmable clock generator.
+ *
+ * NOTES:
+ * - Many existing devices don't seem to do this and work fine.  To keep
+ *   compatibility for old hardware where the device tree doesn't provide a
+ *   register map, this function is a noop if a soc_ctl_map hasn't been provided
+ *   for this platform.
+ * - The value of corecfg_clockmultiplier should sync with that of corresponding
+ *   value reading from sdhci_capability_register. So this function is called
+ *   once at probe time and never called again.
+ *
+ * @host:              The sdhci_host
+ */
+static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
+                                               u32 value)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+       const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
+               sdhci_arasan->soc_ctl_map;
+
+       /* Having a map is optional */
+       if (!soc_ctl_map)
+               return;
+
+       /* If we have a map, we expect to have a syscon */
+       if (!sdhci_arasan->soc_ctl_base) {
+               pr_warn("%s: Have regmap, but no soc-ctl-syscon\n",
+                       mmc_hostname(host->mmc));
+               return;
+       }
+
+       sdhci_arasan_syscon_write(host, &soc_ctl_map->clockmultiplier, value);
+}
+
 /**
  * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
  *
@@ -518,6 +601,10 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
        sdhci_get_of_property(pdev);
        pltfm_host->clk = clk_xin;
 
+       if (of_device_is_compatible(pdev->dev.of_node,
+                                   "rockchip,rk3399-sdhci-5.1"))
+               sdhci_arasan_update_clockmultiplier(host, 0x0);
+
        sdhci_arasan_update_baseclkfreq(host);
 
        ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
@@ -547,12 +634,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
                        goto unreg_clk;
                }
 
-               ret = phy_power_on(sdhci_arasan->phy);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "phy_power_on err.\n");
-                       goto err_phy_power;
-               }
-
                host->mmc_host_ops.hs400_enhanced_strobe =
                                        sdhci_arasan_hs400_enhanced_strobe;
        }
@@ -564,9 +645,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
        return 0;
 
 err_add_host:
-       if (!IS_ERR(sdhci_arasan->phy))
-               phy_power_off(sdhci_arasan->phy);
-err_phy_power:
        if (!IS_ERR(sdhci_arasan->phy))
                phy_exit(sdhci_arasan->phy);
 unreg_clk:
@@ -589,7 +667,8 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
        struct clk *clk_ahb = sdhci_arasan->clk_ahb;
 
        if (!IS_ERR(sdhci_arasan->phy)) {
-               phy_power_off(sdhci_arasan->phy);
+               if (sdhci_arasan->is_phy_on)
+                       phy_power_off(sdhci_arasan->phy);
                phy_exit(sdhci_arasan->phy);
        }
 
index 239be2fde242fdfc6e0d7607d22fb9aa6c50de88..fb71c866eacc7028918e1abb667b230e784a47f4 100644 (file)
@@ -583,7 +583,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
 
        np = pdev->dev.of_node;
 
-       if (of_get_property(np, "little-endian", NULL))
+       if (of_property_read_bool(np, "little-endian"))
                host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata,
                                        sizeof(struct sdhci_esdhc));
        else
index 897cfd24ca2e834c16906fcc2442e5a37bb581e9..72a1f1f5180a9cc12f5bdc7fffaa03685500f6e0 100644 (file)
@@ -156,7 +156,7 @@ static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
        if (!gpio_is_valid(gpio))
                return;
 
-       err = gpio_request(gpio, "sd_cd");
+       err = devm_gpio_request(&slot->chip->pdev->dev, gpio, "sd_cd");
        if (err < 0)
                goto out;
 
@@ -179,7 +179,7 @@ static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
        return;
 
 out_free:
-       gpio_free(gpio);
+       devm_gpio_free(&slot->chip->pdev->dev, gpio);
 out:
        dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
 }
@@ -188,8 +188,6 @@ static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
 {
        if (slot->cd_irq >= 0)
                free_irq(slot->cd_irq, slot);
-       if (gpio_is_valid(slot->cd_gpio))
-               gpio_free(slot->cd_gpio);
 }
 
 #else
@@ -356,6 +354,7 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
 {
        slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
                                 MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
+                                MMC_CAP_CMD_DURING_TFR |
                                 MMC_CAP_WAIT_WHILE_BUSY;
        slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
        slot->hw_reset = sdhci_pci_int_hw_reset;
@@ -421,17 +420,30 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
 /* Define Host controllers for Intel Merrifield platform */
 #define INTEL_MRFLD_EMMC_0     0
 #define INTEL_MRFLD_EMMC_1     1
+#define INTEL_MRFLD_SD         2
+#define INTEL_MRFLD_SDIO       3
 
 static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
 {
-       if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_0) &&
-           (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_1))
-               /* SD support is not ready yet */
+       unsigned int func = PCI_FUNC(slot->chip->pdev->devfn);
+
+       switch (func) {
+       case INTEL_MRFLD_EMMC_0:
+       case INTEL_MRFLD_EMMC_1:
+               slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE |
+                                        MMC_CAP_8_BIT_DATA |
+                                        MMC_CAP_1_8V_DDR;
+               break;
+       case INTEL_MRFLD_SD:
+               slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+               break;
+       case INTEL_MRFLD_SDIO:
+               slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE |
+                                        MMC_CAP_POWER_OFF_CARD;
+               break;
+       default:
                return -ENODEV;
-
-       slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
-                                MMC_CAP_1_8V_DDR;
-
+       }
        return 0;
 }
 
@@ -1615,7 +1627,6 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
 
        slot->chip = chip;
        slot->host = host;
-       slot->pci_bar = bar;
        slot->rst_n_gpio = -EINVAL;
        slot->cd_gpio = -EINVAL;
        slot->cd_idx = -1;
@@ -1643,27 +1654,22 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
 
        host->irq = pdev->irq;
 
-       ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
+       ret = pcim_iomap_regions(pdev, BIT(bar), mmc_hostname(host->mmc));
        if (ret) {
                dev_err(&pdev->dev, "cannot request region\n");
                goto cleanup;
        }
 
-       host->ioaddr = pci_ioremap_bar(pdev, bar);
-       if (!host->ioaddr) {
-               dev_err(&pdev->dev, "failed to remap registers\n");
-               ret = -ENOMEM;
-               goto release;
-       }
+       host->ioaddr = pcim_iomap_table(pdev)[bar];
 
        if (chip->fixes && chip->fixes->probe_slot) {
                ret = chip->fixes->probe_slot(slot);
                if (ret)
-                       goto unmap;
+                       goto cleanup;
        }
 
        if (gpio_is_valid(slot->rst_n_gpio)) {
-               if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) {
+               if (!devm_gpio_request(&pdev->dev, slot->rst_n_gpio, "eMMC_reset")) {
                        gpio_direction_output(slot->rst_n_gpio, 1);
                        slot->host->mmc->caps |= MMC_CAP_HW_RESET;
                        slot->hw_reset = sdhci_pci_gpio_hw_reset;
@@ -1702,18 +1708,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
        return slot;
 
 remove:
-       if (gpio_is_valid(slot->rst_n_gpio))
-               gpio_free(slot->rst_n_gpio);
-
        if (chip->fixes && chip->fixes->remove_slot)
                chip->fixes->remove_slot(slot, 0);
 
-unmap:
-       iounmap(host->ioaddr);
-
-release:
-       pci_release_region(pdev, bar);
-
 cleanup:
        if (slot->data && slot->data->cleanup)
                slot->data->cleanup(slot->data);
@@ -1738,17 +1735,12 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
 
        sdhci_remove_host(slot->host, dead);
 
-       if (gpio_is_valid(slot->rst_n_gpio))
-               gpio_free(slot->rst_n_gpio);
-
        if (slot->chip->fixes && slot->chip->fixes->remove_slot)
                slot->chip->fixes->remove_slot(slot, dead);
 
        if (slot->data && slot->data->cleanup)
                slot->data->cleanup(slot->data);
 
-       pci_release_region(slot->chip->pdev, slot->pci_bar);
-
        sdhci_free_host(slot->host);
 }
 
index 7e0788712e1a1668e4cf8d5cbda1d2a6ee982f6a..9c7c08b9322387f7914024ed404055ed7ba158cb 100644 (file)
@@ -72,7 +72,6 @@ struct sdhci_pci_slot {
        struct sdhci_host       *host;
        struct sdhci_pci_data   *data;
 
-       int                     pci_bar;
        int                     rst_n_gpio;
        int                     cd_gpio;
        int                     cd_irq;
index 1d17dcfc3ffb59913f0f59ae5a880dbd9e0947d6..ad49bfaf5bf8d0f81ab306df5155d49a91a008ca 100644 (file)
@@ -156,13 +156,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
                host->quirks2 = pdata->quirks2;
        }
 
-       /*
-        * Some platforms need to probe the controller to be able to
-        * determine which caps should be used.
-        */
-       if (host->ops && host->ops->platform_init)
-               host->ops->platform_init(host);
-
        platform_set_drvdata(pdev, host);
 
        return host;
index c95ba83366a0f2d95b1b45a70824327069f6b79d..ed92ce729dde1d60b91ce5395e1802816cca448b 100644 (file)
@@ -28,6 +28,7 @@
 
 struct st_mmc_platform_data {
        struct  reset_control *rstc;
+       struct  clk *icnclk;
        void __iomem *top_ioaddr;
 };
 
@@ -353,7 +354,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
        struct sdhci_host *host;
        struct st_mmc_platform_data *pdata;
        struct sdhci_pltfm_host *pltfm_host;
-       struct clk *clk;
+       struct clk *clk, *icnclk;
        int ret = 0;
        u16 host_version;
        struct resource *res;
@@ -365,6 +366,11 @@ static int sdhci_st_probe(struct platform_device *pdev)
                return PTR_ERR(clk);
        }
 
+       /* ICN clock isn't compulsory, but use it if it's provided. */
+       icnclk = devm_clk_get(&pdev->dev, "icn");
+       if (IS_ERR(icnclk))
+               icnclk = NULL;
+
        rstc = devm_reset_control_get(&pdev->dev, NULL);
        if (IS_ERR(rstc))
                rstc = NULL;
@@ -389,6 +395,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
        }
 
        clk_prepare_enable(clk);
+       clk_prepare_enable(icnclk);
 
        /* Configure the FlashSS Top registers for setting eMMC TX/RX delay */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -400,6 +407,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
        }
 
        pltfm_host->clk = clk;
+       pdata->icnclk = icnclk;
 
        /* Configure the Arasan HC inside the flashSS */
        st_mmcss_cconfig(np, host);
@@ -422,6 +430,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
        return 0;
 
 err_out:
+       clk_disable_unprepare(icnclk);
        clk_disable_unprepare(clk);
 err_of:
        sdhci_pltfm_free(pdev);
@@ -442,6 +451,8 @@ static int sdhci_st_remove(struct platform_device *pdev)
 
        ret = sdhci_pltfm_unregister(pdev);
 
+       clk_disable_unprepare(pdata->icnclk);
+
        if (rstc)
                reset_control_assert(rstc);
 
@@ -462,6 +473,7 @@ static int sdhci_st_suspend(struct device *dev)
        if (pdata->rstc)
                reset_control_assert(pdata->rstc);
 
+       clk_disable_unprepare(pdata->icnclk);
        clk_disable_unprepare(pltfm_host->clk);
 out:
        return ret;
@@ -475,6 +487,7 @@ static int sdhci_st_resume(struct device *dev)
        struct device_node *np = dev->of_node;
 
        clk_prepare_enable(pltfm_host->clk);
+       clk_prepare_enable(pdata->icnclk);
 
        if (pdata->rstc)
                reset_control_deassert(pdata->rstc);
index 1e93dc4e303e4f5a31dae97d48955d4dd8bfa7a6..20b6ff5b4af1122f34eb24ebc83775794c8033bd 100644 (file)
@@ -391,6 +391,31 @@ static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
        .pdata = &sdhci_tegra114_pdata,
 };
 
+static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
+       .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+                 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+                 SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                 SDHCI_QUIRK_NO_HISPD_BIT |
+                 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
+                 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                  /*
+                   * The TRM states that the SD/MMC controller found on
+                   * Tegra124 can address 34 bits (the maximum supported by
+                   * the Tegra memory controller), but tests show that DMA
+                   * to or from above 4 GiB doesn't work. This is possibly
+                   * caused by missing programming, though it's not obvious
+                   * what sequence is required. Mark 64-bit DMA broken for
+                   * now to fix this for existing users (e.g. Nyan boards).
+                   */
+                  SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
+       .ops  = &tegra114_sdhci_ops,
+};
+
+static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
+       .pdata = &sdhci_tegra124_pdata,
+};
+
 static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
        .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
                  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
@@ -408,7 +433,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
 
 static const struct of_device_id sdhci_tegra_dt_match[] = {
        { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
-       { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
+       { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
        { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
        { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
        { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
index cd65d474afa2bc300064f1214a0e478e90a7366d..48055666c6557a98286dd6d07118c82a218f5c76 100644 (file)
@@ -888,7 +888,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
                                    struct mmc_request *mrq)
 {
-       return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12);
+       return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) &&
+              !mrq->cap_cmd_during_tfr;
 }
 
 static void sdhci_set_transfer_mode(struct sdhci_host *host,
@@ -1031,9 +1032,18 @@ static void sdhci_finish_data(struct sdhci_host *host)
                        sdhci_do_reset(host, SDHCI_RESET_DATA);
                }
 
-               /* Avoid triggering warning in sdhci_send_command() */
-               host->cmd = NULL;
-               sdhci_send_command(host, data->stop);
+               /*
+                * 'cap_cmd_during_tfr' request must not use the command line
+                * after mmc_command_done() has been called. It is upper layer's
+                * responsibility to send the stop command if required.
+                */
+               if (data->mrq->cap_cmd_during_tfr) {
+                       sdhci_finish_mrq(host, data->mrq);
+               } else {
+                       /* Avoid triggering warning in sdhci_send_command() */
+                       host->cmd = NULL;
+                       sdhci_send_command(host, data->stop);
+               }
        } else {
                sdhci_finish_mrq(host, data->mrq);
        }
@@ -1165,6 +1175,9 @@ static void sdhci_finish_command(struct sdhci_host *host)
                }
        }
 
+       if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd)
+               mmc_command_done(host->mmc, cmd->mrq);
+
        /*
         * The host can send and interrupt when the busy state has
         * ended, allowing us to wait without wasting CPU cycles.
@@ -2062,7 +2075,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 
                spin_unlock_irqrestore(&host->lock, flags);
                /* Wait for Buffer Read Ready interrupt */
-               wait_event_interruptible_timeout(host->buf_ready_int,
+               wait_event_timeout(host->buf_ready_int,
                                        (host->tuning_done == 1),
                                        msecs_to_jiffies(50));
                spin_lock_irqsave(&host->lock, flags);
index 0411c9f364619a5949e999fc4b1b9288504bfcfc..a2bc9e111a3ab176f00ad4e2638b088a4200d3ca 100644 (file)
@@ -555,7 +555,6 @@ struct sdhci_ops {
        void    (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
        void    (*hw_reset)(struct sdhci_host *host);
        void    (*adma_workaround)(struct sdhci_host *host, u32 intmask);
-       void    (*platform_init)(struct sdhci_host *host);
        void    (*card_event)(struct sdhci_host *host);
        void    (*voltage_switch)(struct sdhci_host *host);
        int     (*select_drive_strength)(struct sdhci_host *host,
index c3b651bf89cb4ad5e562ce41cbe1fa20c17c481e..49edff7fee49bc2b48f924d6e17469a140d416e8 100644 (file)
@@ -94,6 +94,7 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = {
        { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
        { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
        { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
+       { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
        {},
 };
 MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
@@ -213,6 +214,13 @@ static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host)
        clk_disable_unprepare(priv->clk);
 }
 
+static int sh_mobile_sdhi_card_busy(struct mmc_host *mmc)
+{
+       struct tmio_mmc_host *host = mmc_priv(mmc);
+
+       return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_DAT0);
+}
+
 static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
                                                      struct mmc_ios *ios)
 {
@@ -369,7 +377,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
        host->clk_update        = sh_mobile_sdhi_clk_update;
        host->clk_disable       = sh_mobile_sdhi_clk_disable;
        host->multi_io_quirk    = sh_mobile_sdhi_multi_io_quirk;
-       host->start_signal_voltage_switch = sh_mobile_sdhi_start_signal_voltage_switch;
+
+       /* SDR speeds are only available on Gen2+ */
+       if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) {
+               /* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */
+               host->card_busy = sh_mobile_sdhi_card_busy;
+               host->start_signal_voltage_switch =
+                       sh_mobile_sdhi_start_signal_voltage_switch;
+       }
 
        /* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
        if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */
index 2ee4c21ec55eec871c13aadaaad5bb5f9903a581..c0a5c676d0e82bd662c673c00acd385914d501e3 100644 (file)
 #define SDXC_REG_CHDA  (0x90)
 #define SDXC_REG_CBDA  (0x94)
 
+/* New registers introduced in A64 */
+#define SDXC_REG_A12A          0x058 /* SMC Auto Command 12 Register */
+#define SDXC_REG_SD_NTSR       0x05C /* SMC New Timing Set Register */
+#define SDXC_REG_DRV_DL                0x140 /* Drive Delay Control Register */
+#define SDXC_REG_SAMP_DL_REG   0x144 /* SMC sample delay control */
+#define SDXC_REG_DS_DL_REG     0x148 /* SMC data strobe delay control */
+
 #define mmc_readl(host, reg) \
        readl((host)->reg_base + SDXC_##reg)
 #define mmc_writel(host, reg, value) \
 #define SDXC_CLK_50M_DDR       3
 #define SDXC_CLK_50M_DDR_8BIT  4
 
+#define SDXC_2X_TIMING_MODE    BIT(31)
+
+#define SDXC_CAL_START         BIT(15)
+#define SDXC_CAL_DONE          BIT(14)
+#define SDXC_CAL_DL_SHIFT      8
+#define SDXC_CAL_DL_SW_EN      BIT(7)
+#define SDXC_CAL_DL_SW_SHIFT   0
+#define SDXC_CAL_DL_MASK       0x3f
+
+#define SDXC_CAL_TIMEOUT       3       /* in seconds, 3s is enough*/
+
 struct sunxi_mmc_clk_delay {
        u32 output;
        u32 sample;
 };
 
 struct sunxi_idma_des {
-       u32     config;
-       u32     buf_size;
-       u32     buf_addr_ptr1;
-       u32     buf_addr_ptr2;
+       __le32 config;
+       __le32 buf_size;
+       __le32 buf_addr_ptr1;
+       __le32 buf_addr_ptr2;
+};
+
+struct sunxi_mmc_cfg {
+       u32 idma_des_size_bits;
+       const struct sunxi_mmc_clk_delay *clk_delays;
+
+       /* does the IP block support autocalibration? */
+       bool can_calibrate;
 };
 
 struct sunxi_mmc_host {
        struct mmc_host *mmc;
        struct reset_control *reset;
+       const struct sunxi_mmc_cfg *cfg;
 
        /* IO mapping base */
        void __iomem    *reg_base;
@@ -241,7 +268,6 @@ struct sunxi_mmc_host {
        struct clk      *clk_mmc;
        struct clk      *clk_sample;
        struct clk      *clk_output;
-       const struct sunxi_mmc_clk_delay *clk_delays;
 
        /* irq */
        spinlock_t      lock;
@@ -250,7 +276,6 @@ struct sunxi_mmc_host {
        u32             sdio_imask;
 
        /* dma */
-       u32             idma_des_size_bits;
        dma_addr_t      sg_dma;
        void            *sg_cpu;
        bool            wait_dma;
@@ -322,25 +347,28 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
 {
        struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
        dma_addr_t next_desc = host->sg_dma;
-       int i, max_len = (1 << host->idma_des_size_bits);
+       int i, max_len = (1 << host->cfg->idma_des_size_bits);
 
        for (i = 0; i < data->sg_len; i++) {
-               pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN |
-                                SDXC_IDMAC_DES0_DIC;
+               pdes[i].config = cpu_to_le32(SDXC_IDMAC_DES0_CH |
+                                            SDXC_IDMAC_DES0_OWN |
+                                            SDXC_IDMAC_DES0_DIC);
 
                if (data->sg[i].length == max_len)
                        pdes[i].buf_size = 0; /* 0 == max_len */
                else
-                       pdes[i].buf_size = data->sg[i].length;
+                       pdes[i].buf_size = cpu_to_le32(data->sg[i].length);
 
                next_desc += sizeof(struct sunxi_idma_des);
-               pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
-               pdes[i].buf_addr_ptr2 = (u32)next_desc;
+               pdes[i].buf_addr_ptr1 =
+                       cpu_to_le32(sg_dma_address(&data->sg[i]));
+               pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc);
        }
 
-       pdes[0].config |= SDXC_IDMAC_DES0_FD;
-       pdes[i - 1].config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER;
-       pdes[i - 1].config &= ~SDXC_IDMAC_DES0_DIC;
+       pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD);
+       pdes[i - 1].config |= cpu_to_le32(SDXC_IDMAC_DES0_LD |
+                                         SDXC_IDMAC_DES0_ER);
+       pdes[i - 1].config &= cpu_to_le32(~SDXC_IDMAC_DES0_DIC);
        pdes[i - 1].buf_addr_ptr2 = 0;
 
        /*
@@ -653,11 +681,84 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
        return 0;
 }
 
+static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
+{
+       u32 reg = readl(host->reg_base + reg_off);
+       u32 delay;
+       unsigned long timeout;
+
+       if (!host->cfg->can_calibrate)
+               return 0;
+
+       reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT);
+       reg &= ~SDXC_CAL_DL_SW_EN;
+
+       writel(reg | SDXC_CAL_START, host->reg_base + reg_off);
+
+       dev_dbg(mmc_dev(host->mmc), "calibration started\n");
+
+       timeout = jiffies + HZ * SDXC_CAL_TIMEOUT;
+
+       while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) {
+               if (time_before(jiffies, timeout))
+                       cpu_relax();
+               else {
+                       reg &= ~SDXC_CAL_START;
+                       writel(reg, host->reg_base + reg_off);
+
+                       return -ETIMEDOUT;
+               }
+       }
+
+       delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK;
+
+       reg &= ~SDXC_CAL_START;
+       reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN;
+
+       writel(reg, host->reg_base + reg_off);
+
+       dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg);
+
+       return 0;
+}
+
+static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
+                                  struct mmc_ios *ios, u32 rate)
+{
+       int index;
+
+       if (!host->cfg->clk_delays)
+               return 0;
+
+       /* determine delays */
+       if (rate <= 400000) {
+               index = SDXC_CLK_400K;
+       } else if (rate <= 25000000) {
+               index = SDXC_CLK_25M;
+       } else if (rate <= 52000000) {
+               if (ios->timing != MMC_TIMING_UHS_DDR50 &&
+                   ios->timing != MMC_TIMING_MMC_DDR52) {
+                       index = SDXC_CLK_50M;
+               } else if (ios->bus_width == MMC_BUS_WIDTH_8) {
+                       index = SDXC_CLK_50M_DDR_8BIT;
+               } else {
+                       index = SDXC_CLK_50M_DDR;
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       clk_set_phase(host->clk_sample, host->cfg->clk_delays[index].sample);
+       clk_set_phase(host->clk_output, host->cfg->clk_delays[index].output);
+
+       return 0;
+}
+
 static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
                                  struct mmc_ios *ios)
 {
-       u32 rate, oclk_dly, rval, sclk_dly;
-       u32 clock = ios->clock;
+       long rate;
+       u32 rval, clock = ios->clock;
        int ret;
 
        /* 8 bit DDR requires a higher module clock */
@@ -666,13 +767,18 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
                clock <<= 1;
 
        rate = clk_round_rate(host->clk_mmc, clock);
-       dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %d\n",
+       if (rate < 0) {
+               dev_err(mmc_dev(host->mmc), "error rounding clk to %d: %ld\n",
+                       clock, rate);
+               return rate;
+       }
+       dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %ld\n",
                clock, rate);
 
        /* setting clock rate */
        ret = clk_set_rate(host->clk_mmc, rate);
        if (ret) {
-               dev_err(mmc_dev(host->mmc), "error setting clk to %d: %d\n",
+               dev_err(mmc_dev(host->mmc), "error setting clk to %ld: %d\n",
                        rate, ret);
                return ret;
        }
@@ -692,31 +798,15 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
        }
        mmc_writel(host, REG_CLKCR, rval);
 
-       /* determine delays */
-       if (rate <= 400000) {
-               oclk_dly = host->clk_delays[SDXC_CLK_400K].output;
-               sclk_dly = host->clk_delays[SDXC_CLK_400K].sample;
-       } else if (rate <= 25000000) {
-               oclk_dly = host->clk_delays[SDXC_CLK_25M].output;
-               sclk_dly = host->clk_delays[SDXC_CLK_25M].sample;
-       } else if (rate <= 52000000) {
-               if (ios->timing != MMC_TIMING_UHS_DDR50 &&
-                   ios->timing != MMC_TIMING_MMC_DDR52) {
-                       oclk_dly = host->clk_delays[SDXC_CLK_50M].output;
-                       sclk_dly = host->clk_delays[SDXC_CLK_50M].sample;
-               } else if (ios->bus_width == MMC_BUS_WIDTH_8) {
-                       oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].output;
-                       sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].sample;
-               } else {
-                       oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].output;
-                       sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].sample;
-               }
-       } else {
-               return -EINVAL;
-       }
+       ret = sunxi_mmc_clk_set_phase(host, ios, rate);
+       if (ret)
+               return ret;
+
+       ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL_REG);
+       if (ret)
+               return ret;
 
-       clk_set_phase(host->clk_sample, sclk_dly);
-       clk_set_phase(host->clk_output, oclk_dly);
+       /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */
 
        return sunxi_mmc_oclk_onoff(host, 1);
 }
@@ -938,14 +1028,6 @@ static int sunxi_mmc_card_busy(struct mmc_host *mmc)
        return !!(mmc_readl(host, REG_STAS) & SDXC_CARD_DATA_BUSY);
 }
 
-static const struct of_device_id sunxi_mmc_of_match[] = {
-       { .compatible = "allwinner,sun4i-a10-mmc", },
-       { .compatible = "allwinner,sun5i-a13-mmc", },
-       { .compatible = "allwinner,sun9i-a80-mmc", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
-
 static struct mmc_host_ops sunxi_mmc_ops = {
        .request         = sunxi_mmc_request,
        .set_ios         = sunxi_mmc_set_ios,
@@ -974,21 +1056,54 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = {
        [SDXC_CLK_50M_DDR_8BIT] = { .output =  72, .sample =  72 },
 };
 
+static const struct sunxi_mmc_cfg sun4i_a10_cfg = {
+       .idma_des_size_bits = 13,
+       .clk_delays = NULL,
+       .can_calibrate = false,
+};
+
+static const struct sunxi_mmc_cfg sun5i_a13_cfg = {
+       .idma_des_size_bits = 16,
+       .clk_delays = NULL,
+       .can_calibrate = false,
+};
+
+static const struct sunxi_mmc_cfg sun7i_a20_cfg = {
+       .idma_des_size_bits = 16,
+       .clk_delays = sunxi_mmc_clk_delays,
+       .can_calibrate = false,
+};
+
+static const struct sunxi_mmc_cfg sun9i_a80_cfg = {
+       .idma_des_size_bits = 16,
+       .clk_delays = sun9i_mmc_clk_delays,
+       .can_calibrate = false,
+};
+
+static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
+       .idma_des_size_bits = 16,
+       .clk_delays = NULL,
+       .can_calibrate = true,
+};
+
+static const struct of_device_id sunxi_mmc_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg },
+       { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg },
+       { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg },
+       { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
+       { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
+
 static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
                                      struct platform_device *pdev)
 {
-       struct device_node *np = pdev->dev.of_node;
        int ret;
 
-       if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc"))
-               host->idma_des_size_bits = 13;
-       else
-               host->idma_des_size_bits = 16;
-
-       if (of_device_is_compatible(np, "allwinner,sun9i-a80-mmc"))
-               host->clk_delays = sun9i_mmc_clk_delays;
-       else
-               host->clk_delays = sunxi_mmc_clk_delays;
+       host->cfg = of_device_get_match_data(&pdev->dev);
+       if (!host->cfg)
+               return -EINVAL;
 
        ret = mmc_regulator_get_supply(host->mmc);
        if (ret) {
@@ -1014,16 +1129,18 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
                return PTR_ERR(host->clk_mmc);
        }
 
-       host->clk_output = devm_clk_get(&pdev->dev, "output");
-       if (IS_ERR(host->clk_output)) {
-               dev_err(&pdev->dev, "Could not get output clock\n");
-               return PTR_ERR(host->clk_output);
-       }
+       if (host->cfg->clk_delays) {
+               host->clk_output = devm_clk_get(&pdev->dev, "output");
+               if (IS_ERR(host->clk_output)) {
+                       dev_err(&pdev->dev, "Could not get output clock\n");
+                       return PTR_ERR(host->clk_output);
+               }
 
-       host->clk_sample = devm_clk_get(&pdev->dev, "sample");
-       if (IS_ERR(host->clk_sample)) {
-               dev_err(&pdev->dev, "Could not get sample clock\n");
-               return PTR_ERR(host->clk_sample);
+               host->clk_sample = devm_clk_get(&pdev->dev, "sample");
+               if (IS_ERR(host->clk_sample)) {
+                       dev_err(&pdev->dev, "Could not get sample clock\n");
+                       return PTR_ERR(host->clk_sample);
+               }
        }
 
        host->reset = devm_reset_control_get_optional(&pdev->dev, "ahb");
@@ -1120,15 +1237,17 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
        mmc->max_blk_count      = 8192;
        mmc->max_blk_size       = 4096;
        mmc->max_segs           = PAGE_SIZE / sizeof(struct sunxi_idma_des);
-       mmc->max_seg_size       = (1 << host->idma_des_size_bits);
+       mmc->max_seg_size       = (1 << host->cfg->idma_des_size_bits);
        mmc->max_req_size       = mmc->max_seg_size * mmc->max_segs;
        /* 400kHz ~ 52MHz */
        mmc->f_min              =   400000;
        mmc->f_max              = 52000000;
        mmc->caps              |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
-                                 MMC_CAP_1_8V_DDR |
                                  MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
 
+       if (host->cfg->clk_delays)
+               mmc->caps      |= MMC_CAP_1_8V_DDR;
+
        ret = mmc_of_parse(mmc);
        if (ret)
                goto error_free_dma;
@@ -1160,6 +1279,8 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
        if (!IS_ERR(host->reset))
                reset_control_assert(host->reset);
 
+       clk_disable_unprepare(host->clk_sample);
+       clk_disable_unprepare(host->clk_output);
        clk_disable_unprepare(host->clk_mmc);
        clk_disable_unprepare(host->clk_ahb);
 
index 7f63ec05bdf41d153a7445be88798fd037ae8aaf..1f1cb2628f673bcf2460220c8acbfcc00987fb17 100644 (file)
@@ -158,6 +158,7 @@ struct tmio_mmc_host {
        void (*clk_disable)(struct tmio_mmc_host *host);
        int (*multi_io_quirk)(struct mmc_card *card,
                              unsigned int direction, int blk_size);
+       int (*card_busy)(struct mmc_host *mmc);
        int (*start_signal_voltage_switch)(struct mmc_host *mmc,
                                           struct mmc_ios *ios);
 };
index 92467efc4e2c9e59676b74a3aee2e280bb0c30e3..192817850dffac2b2a132757ae13361fd79d098d 100644 (file)
@@ -960,20 +960,12 @@ static int tmio_multi_io_quirk(struct mmc_card *card,
        return blk_size;
 }
 
-static int tmio_mmc_card_busy(struct mmc_host *mmc)
-{
-       struct tmio_mmc_host *host = mmc_priv(mmc);
-
-       return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_DAT0);
-}
-
 static struct mmc_host_ops tmio_mmc_ops = {
        .request        = tmio_mmc_request,
        .set_ios        = tmio_mmc_set_ios,
        .get_ro         = tmio_mmc_get_ro,
        .get_cd         = mmc_gpio_get_cd,
        .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
-       .card_busy      = tmio_mmc_card_busy,
        .multi_io_quirk = tmio_multi_io_quirk,
 };
 
@@ -1072,6 +1064,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
                goto host_free;
        }
 
+       tmio_mmc_ops.card_busy = _host->card_busy;
        tmio_mmc_ops.start_signal_voltage_switch = _host->start_signal_voltage_switch;
        mmc->ops = &tmio_mmc_ops;
 
index 1e819f98b94f52fe0a2f289f2e38dd4dbf72c27b..bb3e0d1dd35501bc8e02a5c513adc2af12572e0d 100644 (file)
@@ -2116,13 +2116,11 @@ static int vub300_probe(struct usb_interface *interface,
        command_out_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!command_out_urb) {
                retval = -ENOMEM;
-               dev_err(&udev->dev, "not enough memory for command_out_urb\n");
                goto error0;
        }
        command_res_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!command_res_urb) {
                retval = -ENOMEM;
-               dev_err(&udev->dev, "not enough memory for command_res_urb\n");
                goto error1;
        }
        /* this also allocates memory for our VUB300 mmc host device */
index d8673ca968ba2d65fb3da39680583ca5152641e4..73fad83acbcb6a157587180516f9ffe7c61eb7d7 100644 (file)
@@ -292,6 +292,7 @@ struct mmc_card {
        u32                     raw_cid[4];     /* raw card CID */
        u32                     raw_csd[4];     /* raw card CSD */
        u32                     raw_scr[2];     /* raw card SCR */
+       u32                     raw_ssr[16];    /* raw card SSR */
        struct mmc_cid          cid;            /* card identification */
        struct mmc_csd          csd;            /* card specific */
        struct mmc_ext_csd      ext_csd;        /* mmc v4 extended card specific */
index b01e77de1a74de9350fd96a66440566592a69bf5..368bed70aa9d54dafca0e1ae1b868bd54eeebd90 100644 (file)
@@ -133,8 +133,12 @@ struct mmc_request {
        struct mmc_command      *stop;
 
        struct completion       completion;
+       struct completion       cmd_completion;
        void                    (*done)(struct mmc_request *);/* completion function */
        struct mmc_host         *host;
+
+       /* Allow other commands during this ongoing data transfer or busy wait */
+       bool                    cap_cmd_during_tfr;
 };
 
 struct mmc_card;
@@ -146,6 +150,9 @@ extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
                                           struct mmc_async_req *, int *);
 extern int mmc_interrupt_hpi(struct mmc_card *);
 extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
+extern void mmc_wait_for_req_done(struct mmc_host *host,
+                                 struct mmc_request *mrq);
+extern bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
 extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
 extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
index 83b0edfce471c2a9ab536a5ae616e070ac34fc9e..f5af2bd35e7fdffbab622c121db2433e980f23e0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/scatterlist.h>
 #include <linux/mmc/core.h>
 #include <linux/dmaengine.h>
+#include <linux/reset.h>
 
 #define MAX_MCI_SLOTS  2
 
@@ -259,6 +260,7 @@ struct dw_mci_board {
        /* delay in mS before detecting cards after interrupt */
        u32 detect_delay_ms;
 
+       struct reset_control *rstc;
        struct dw_mci_dma_ops *dma_ops;
        struct dma_pdata *data;
 };
index aa4bfbf129e4585ebb89b25fca4b03c928925022..0b2439441cc8ccdc3e4ce58a1e7d575ea7189e29 100644 (file)
@@ -281,6 +281,7 @@ struct mmc_host {
 #define MMC_CAP_DRIVER_TYPE_A  (1 << 23)       /* Host supports Driver Type A */
 #define MMC_CAP_DRIVER_TYPE_C  (1 << 24)       /* Host supports Driver Type C */
 #define MMC_CAP_DRIVER_TYPE_D  (1 << 25)       /* Host supports Driver Type D */
+#define MMC_CAP_CMD_DURING_TFR (1 << 29)       /* Commands during data transfer */
 #define MMC_CAP_CMD23          (1 << 30)       /* CMD23 supported. */
 #define MMC_CAP_HW_RESET       (1 << 31)       /* Hardware reset */
 
@@ -382,6 +383,9 @@ struct mmc_host {
        struct mmc_async_req    *areq;          /* active async req */
        struct mmc_context_info context_info;   /* async synchronization info */
 
+       /* Ongoing data transfer that allows commands during transfer */
+       struct mmc_request      *ongoing_mrq;
+
 #ifdef CONFIG_FAIL_MMC_REQUEST
        struct fault_attr       fail_mmc_request;
 #endif
@@ -418,6 +422,7 @@ int mmc_power_restore_host(struct mmc_host *host);
 
 void mmc_detect_change(struct mmc_host *, unsigned long delay);
 void mmc_request_done(struct mmc_host *, struct mmc_request *);
+void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq);
 
 static inline void mmc_signal_sdio_irq(struct mmc_host *host)
 {
This page took 0.069611 seconds and 5 git commands to generate.