]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'mmc/mmc-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 16 Jan 2014 02:47:37 +0000 (13:47 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 16 Jan 2014 02:47:37 +0000 (13:47 +1100)
Conflicts:
MAINTAINERS

33 files changed:
Documentation/devicetree/bindings/mmc/arasan,sdhci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt [new file with mode: 0644]
MAINTAINERS
drivers/mmc/card/block.c
drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/dw_mmc-k3.c [new file with mode: 0644]
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/mxs-mmc.c
drivers/mmc/host/sdhci-acpi.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-of-arasan.c [new file with mode: 0644]
drivers/mmc/host/sdhci-pci-o2micro.c [new file with mode: 0644]
drivers/mmc/host/sdhci-pci-o2micro.h [new file with mode: 0644]
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci-pci.h [new file with mode: 0644]
drivers/mmc/host/sdhci-pltfm.c
drivers/mmc/host/sdhci-pltfm.h
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sh_mmcif.c
drivers/mmc/host/sh_mobile_sdhi.c
drivers/mmc/host/tmio_mmc.c
drivers/mmc/host/tmio_mmc.h
drivers/mmc/host/tmio_mmc_dma.c
drivers/mmc/host/tmio_mmc_pio.c
include/linux/mfd/tmio.h
include/linux/mmc/sdhci.h
include/linux/mmc/tmio.h

diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
new file mode 100644 (file)
index 0000000..98ee2ab
--- /dev/null
@@ -0,0 +1,27 @@
+Device Tree Bindings for the Arasan SDHCI Controller
+
+  The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only
+  deviations are documented here.
+
+  [1] Documentation/devicetree/bindings/mmc/mmc.txt
+  [2] Documentation/devicetree/bindings/clock/clock-bindings.txt
+  [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+
+Required Properties:
+  - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
+  - reg: From mmc bindings: Register location and length.
+  - clocks: From clock bindings: Handles to clock inputs.
+  - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
+  - interrupts: Interrupt specifier
+  - interrupt-parent: Phandle for the interrupt controller that services
+                     interrupts for this device.
+
+Example:
+       sdhci@e0100000 {
+               compatible = "arasan,sdhci-8.9a";
+               reg = <0xe0100000 0x1000>;
+               clock-names = "clk_xin", "clk_ahb";
+               clocks = <&clkc 21>, <&clkc 32>;
+               interrupt-parent = <&gic>;
+               interrupts = <0 24 4>;
+       } ;
diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
new file mode 100644 (file)
index 0000000..d7e2d7f
--- /dev/null
@@ -0,0 +1,60 @@
+* Hisilicon specific extensions to the Synopsys Designware Mobile
+  Storage Host Controller
+
+Read synopsys-dw-mshc.txt for more details
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be one of the following.
+  - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extentions.
+
+* clock-freq-table: should be the frequency (in Hz) array of the ciu clock
+       in each supported mode.
+       0. CIU clock rate in Hz for DS mode
+       1. CIU clock rate in Hz for MMC HS mode
+       2. CIU clock rate in Hz for SD HS mode
+       3. CIU clock rate in Hz for SDR12 mode
+       4. CIU clock rate in Hz for SDR25 mode
+       5. CIU clock rate in Hz for SDR50 mode
+       6. CIU clock rate in Hz for SDR104 mode
+       7. CIU clock rate in Hz for DDR50 mode
+       8. CIU clock rate in Hz for HS200 mode
+
+Example:
+
+       /* for Hi3620 */
+
+       /* SoC portion */
+       dwmmc_0: dwmmc0@fcd03000 {
+               compatible = "hisilicon,hi4511-dw-mshc";
+               reg = <0xfcd03000 0x1000>;
+               interrupts = <0 16 4>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
+               clock-names = "ciu", "biu";
+               clock-freq-table =
+               <25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
+       };
+
+       /* Board portion */
+       dwmmc0@fcd03000 {
+               num-slots = <1>;
+               vmmc-supply = <&ldo12>;
+               fifo-depth = <0x100>;
+               supports-highspeed;
+               pinctrl-names = "default";
+               pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
+               slot@0 {
+                       reg = <0>;
+                       bus-width = <4>;
+                       disable-wp;
+                       cd-gpios = <&gpio10 3 0>;
+               };
+       };
index 8bf5cc44d2d6b3bdc50342bcf9f200126e10c531..a10fc4126f02cb30c9f363fa8140e241e0f4dbd0 100644 (file)
@@ -1395,6 +1395,7 @@ F:        drivers/cpuidle/cpuidle-zynq.c
 N:     zynq
 N:     xilinx
 F:     drivers/clocksource/cadence_ttc_timer.c
+F:     drivers/mmc/host/sdhci-of-arasan.c
 
 ARM SMMU DRIVER
 M:     Will Deacon <will.deacon@arm.com>
@@ -5779,7 +5780,7 @@ F:        drivers/mfd/
 F:     include/linux/mfd/
 
 MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
-M:     Chris Ball <cjb@laptop.org>
+M:     Chris Ball <chris@printf.net>
 L:     linux-mmc@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
 S:     Maintained
@@ -7607,7 +7608,7 @@ S:        Maintained
 F:     drivers/mmc/host/sdricoh_cs.c
 
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
-M:     Chris Ball <cjb@laptop.org>
+M:     Chris Ball <chris@printf.net>
 L:     linux-mmc@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
 S:     Maintained
@@ -8782,7 +8783,6 @@ F:        include/linux/toshiba.h
 F:     include/uapi/linux/toshiba.h
 
 TMIO MMC DRIVER
-M:     Guennadi Liakhovetski <g.liakhovetski@gmx.de>
 M:     Ian Molton <ian@mnementh.co.uk>
 L:     linux-mmc@vger.kernel.org
 S:     Maintained
index 29d5d988a51cc6233ce71215a3d88bcc91167c94..7b5424f398ace049d0a6c4be9724a191c7ad4f54 100644 (file)
@@ -1959,6 +1959,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
        struct mmc_card *card = md->queue.card;
        struct mmc_host *host = card->host;
        unsigned long flags;
+       unsigned int cmd_flags = req ? req->cmd_flags : 0;
 
        if (req && !mq->mqrq_prev->req)
                /* claim host only for the first request */
@@ -1974,7 +1975,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
        }
 
        mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
-       if (req && req->cmd_flags & REQ_DISCARD) {
+       if (cmd_flags & REQ_DISCARD) {
                /* complete ongoing async transfer before issuing discard */
                if (card->host->areq)
                        mmc_blk_issue_rw_rq(mq, NULL);
@@ -1983,7 +1984,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                        ret = mmc_blk_issue_secdiscard_rq(mq, req);
                else
                        ret = mmc_blk_issue_discard_rq(mq, req);
-       } else if (req && req->cmd_flags & REQ_FLUSH) {
+       } else if (cmd_flags & REQ_FLUSH) {
                /* complete ongoing async transfer before issuing flush */
                if (card->host->areq)
                        mmc_blk_issue_rw_rq(mq, NULL);
@@ -1999,7 +2000,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 
 out:
        if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
-            (req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK)))
+            (cmd_flags & MMC_REQ_SPECIAL_MASK))
                /*
                 * Release host when there are no more requests
                 * and after special request(discard, flush) is done.
index 57a2b403bf8e9107204c3cda03671a5a1c8bd87d..098374b1ab2b99cb769d7ddf6a2a9d0a2a7672cb 100644 (file)
@@ -2460,7 +2460,8 @@ void mmc_rescan(struct work_struct *work)
         */
        mmc_bus_put(host);
 
-       if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
+       if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
+                       host->ops->get_cd(host) == 0) {
                mmc_claim_host(host);
                mmc_power_off(host);
                mmc_release_host(host);
index f631f5a9bf7948b848754cc3ba475619c957a79b..98e9eb0f6643149f6b589cb1319b2a0a0905ee1d 100644 (file)
@@ -1119,14 +1119,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
         */
        if (mmc_card_highspeed(card)) {
                if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
-                       && ((host->caps & (MMC_CAP_1_8V_DDR |
-                            MMC_CAP_UHS_DDR50))
-                               == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
+                       && (host->caps & MMC_CAP_1_8V_DDR))
                                ddr = MMC_1_8V_DDR_MODE;
                else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
-                       && ((host->caps & (MMC_CAP_1_2V_DDR |
-                            MMC_CAP_UHS_DDR50))
-                               == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
+                       && (host->caps & MMC_CAP_1_2V_DDR))
                                ddr = MMC_1_2V_DDR_MODE;
        }
 
index 6f42050b7ccc6d829d6b58ece04b1177e73cbe80..692fdb177294c242f3df6c9e1313df0584f100b3 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/err.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/pm_runtime.h>
@@ -45,6 +46,13 @@ static const unsigned int tacc_mant[] = {
        35,     40,     45,     50,     55,     60,     70,     80,
 };
 
+static const unsigned int sd_au_size[] = {
+       0,              SZ_16K / 512,           SZ_32K / 512,   SZ_64K / 512,
+       SZ_128K / 512,  SZ_256K / 512,          SZ_512K / 512,  SZ_1M / 512,
+       SZ_2M / 512,    SZ_4M / 512,            SZ_8M / 512,    (SZ_8M + SZ_4M) / 512,
+       SZ_16M / 512,   (SZ_16M + SZ_8M) / 512, SZ_32M / 512,   SZ_64M / 512,
+};
+
 #define UNSTUFF_BITS(resp,start,size)                                  \
        ({                                                              \
                const int __size = size;                                \
@@ -216,7 +224,7 @@ static int mmc_decode_scr(struct mmc_card *card)
 static int mmc_read_ssr(struct mmc_card *card)
 {
        unsigned int au, es, et, eo;
-       int err, i, max_au;
+       int err, i;
        u32 *ssr;
 
        if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
@@ -240,26 +248,25 @@ static int mmc_read_ssr(struct mmc_card *card)
        for (i = 0; i < 16; i++)
                ssr[i] = be32_to_cpu(ssr[i]);
 
-       /* SD3.0 increases max AU size to 64MB (0xF) from 4MB (0x9) */
-       max_au = card->scr.sda_spec3 ? 0xF : 0x9;
-
        /*
         * UNSTUFF_BITS only works with four u32s so we have to offset the
         * bitfield positions accordingly.
         */
        au = UNSTUFF_BITS(ssr, 428 - 384, 4);
-       if (au > 0 && au <= max_au) {
-               card->ssr.au = 1 << (au + 4);
-               es = UNSTUFF_BITS(ssr, 408 - 384, 16);
-               et = UNSTUFF_BITS(ssr, 402 - 384, 6);
-               eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
-               if (es && et) {
-                       card->ssr.erase_timeout = (et * 1000) / es;
-                       card->ssr.erase_offset = eo * 1000;
+       if (au) {
+               if (au <= 9 || card->scr.sda_spec3) {
+                       card->ssr.au = sd_au_size[au];
+                       es = UNSTUFF_BITS(ssr, 408 - 384, 16);
+                       et = UNSTUFF_BITS(ssr, 402 - 384, 6);
+                       if (es && et) {
+                               eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
+                               card->ssr.erase_timeout = (et * 1000) / es;
+                               card->ssr.erase_offset = eo * 1000;
+                       }
+               } else {
+                       pr_warning("%s: SD Status: Invalid Allocation Unit size.\n",
+                                  mmc_hostname(card->host));
                }
-       } else {
-               pr_warning("%s: SD Status: Invalid Allocation Unit "
-                       "size.\n", mmc_hostname(card->host));
        }
 out:
        kfree(ssr);
index 4e8ca9d5570f80b40fc1c174521a0812c1c8a367..1384f67abe2190ffdb52264f73924b1ec9c3d0e2 100644 (file)
@@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM
 
          If unsure, say N.
 
+config MMC_SDHCI_OF_ARASAN
+       tristate "SDHCI OF support for the Arasan SDHCI controllers"
+       depends on MMC_SDHCI_PLTFM
+       depends on OF
+       help
+         This selects the Arasan Secure Digital Host Controller Interface
+         (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
+
+         If you have a controller with this interface, say Y or M here.
+
+         If unsure, say N.
+
 config MMC_SDHCI_OF_ESDHC
        tristate "SDHCI OF support for the Freescale eSDHC controller"
        depends on MMC_SDHCI_PLTFM
@@ -479,7 +491,8 @@ config MMC_TMIO
 
 config MMC_SDHI
        tristate "SH-Mobile SDHI SD/SDIO controller support"
-       depends on SUPERH || ARCH_SHMOBILE
+       depends on SUPERH || ARM
+       depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
        select MMC_TMIO_CORE
        help
          This provides support for the SDHI SD/SDIO controller found in
@@ -575,6 +588,16 @@ config MMC_DW_SOCFPGA
          This selects support for Altera SoCFPGA specific extensions to the
          Synopsys DesignWare Memory Card Interface driver.
 
+config MMC_DW_K3
+       tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
+       depends on MMC_DW
+       select MMC_DW_PLTFM
+       select MMC_DW_IDMAC
+       help
+         This selects support for Hisilicon K3 SoC specific extensions to the
+         Synopsys DesignWare Memory Card Interface driver. Select this option
+         for platforms based on Hisilicon K3 SoC's.
+
 config MMC_DW_PCI
        tristate "Synopsys Designware MCI support on PCI bus"
        depends on MMC_DW && PCI
@@ -588,7 +611,8 @@ config MMC_DW_PCI
 
 config MMC_SH_MMCIF
        tristate "SuperH Internal MMCIF support"
-       depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
+       depends on MMC_BLOCK
+       depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
        help
          This selects the MMC Host Interface controller (MMCIF).
 
index c41d0c36450958840242959e7d911576d4f5102d..3483b6b6b880a592cfc564a2dd70c66e762760fe 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS)           += mxs-mmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
 obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI))       += sdhci-pci-data.o
+obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI))       += sdhci-pci-o2micro.o
 obj-$(CONFIG_MMC_SDHCI_ACPI)   += sdhci-acpi.o
 obj-$(CONFIG_MMC_SDHCI_PXAV3)  += sdhci-pxav3.o
 obj-$(CONFIG_MMC_SDHCI_PXAV2)  += sdhci-pxav2.o
@@ -43,6 +44,7 @@ obj-$(CONFIG_MMC_DW)          += dw_mmc.o
 obj-$(CONFIG_MMC_DW_PLTFM)     += dw_mmc-pltfm.o
 obj-$(CONFIG_MMC_DW_EXYNOS)    += dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_SOCFPGA)   += dw_mmc-socfpga.o
+obj-$(CONFIG_MMC_DW_K3)                += dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)       += dw_mmc-pci.o
 obj-$(CONFIG_MMC_SH_MMCIF)     += sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)       += jz4740_mmc.o
@@ -57,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX)               += sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)      += sdhci-esdhc-imx.o
 obj-$(CONFIG_MMC_SDHCI_DOVE)           += sdhci-dove.o
 obj-$(CONFIG_MMC_SDHCI_TEGRA)          += sdhci-tegra.o
+obj-$(CONFIG_MMC_SDHCI_OF_ARASAN)      += sdhci-of-arasan.o
 obj-$(CONFIG_MMC_SDHCI_OF_ESDHC)       += sdhci-of-esdhc.o
 obj-$(CONFIG_MMC_SDHCI_OF_HLWD)                += sdhci-of-hlwd.o
 obj-$(CONFIG_MMC_SDHCI_BCM_KONA)       += sdhci-bcm-kona.o
index 2cbb4516d3530fd4167db6533c6da4740ed60a46..42706ea0ba85407cdead6259756fd4ed1b898016 100644 (file)
@@ -1192,11 +1192,22 @@ static void atmci_start_request(struct atmel_mci *host,
        iflags |= ATMCI_CMDRDY;
        cmd = mrq->cmd;
        cmdflags = atmci_prepare_command(slot->mmc, cmd);
-       atmci_send_command(host, cmd, cmdflags);
+
+       /*
+        * DMA transfer should be started before sending the command to avoid
+        * unexpected errors especially for read operations in SDIO mode.
+        * Unfortunately, in PDC mode, command has to be sent before starting
+        * the transfer.
+        */
+       if (host->submit_data != &atmci_submit_data_dma)
+               atmci_send_command(host, cmd, cmdflags);
 
        if (data)
                host->submit_data(host, data);
 
+       if (host->submit_data == &atmci_submit_data_dma)
+               atmci_send_command(host, cmd, cmdflags);
+
        if (mrq->stop) {
                host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
                host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
@@ -1391,8 +1402,14 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                clk_unprepare(host->mck);
 
        switch (ios->power_mode) {
+       case MMC_POWER_OFF:
+               if (!IS_ERR(mmc->supply.vmmc))
+                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+               break;
        case MMC_POWER_UP:
                set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
+               if (!IS_ERR(mmc->supply.vmmc))
+                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
                break;
        default:
                /*
@@ -2204,6 +2221,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
        }
 
        host->slot[id] = slot;
+       mmc_regulator_get_supply(mmc);
        mmc_add_host(mmc);
 
        if (gpio_is_valid(slot->detect_pin)) {
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
new file mode 100644 (file)
index 0000000..3542a03
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/of_address.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define MAX_NUMS       10
+struct dw_mci_k3_priv_data {
+       u32     clk_table[MAX_NUMS];
+};
+
+static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+       struct dw_mci_k3_priv_data *priv = host->priv;
+       u32 rate = priv->clk_table[ios->timing];
+       int ret;
+
+       if (!rate) {
+               dev_warn(host->dev,
+                       "no specified rate in timing %u\n", ios->timing);
+               return;
+       }
+
+       ret = clk_set_rate(host->ciu_clk, rate);
+       if (ret)
+               dev_warn(host->dev, "failed to set clock rate %uHz\n", rate);
+
+       host->bus_hz = clk_get_rate(host->ciu_clk);
+}
+
+static int dw_mci_k3_parse_dt(struct dw_mci *host)
+{
+       struct dw_mci_k3_priv_data *priv;
+       struct device_node *node = host->dev->of_node;
+       struct property *prop;
+       const __be32 *cur;
+       u32 val, num = 0;
+
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(host->dev, "mem alloc failed for private data\n");
+               return -ENOMEM;
+       }
+       host->priv = priv;
+
+       of_property_for_each_u32(node, "clock-freq-table", prop, cur, val) {
+               if (num >= MAX_NUMS)
+                       break;
+               priv->clk_table[num++] = val;
+       }
+       return 0;
+}
+
+static const struct dw_mci_drv_data k3_drv_data = {
+       .set_ios                = dw_mci_k3_set_ios,
+       .parse_dt               = dw_mci_k3_parse_dt,
+};
+
+static const struct of_device_id dw_mci_k3_match[] = {
+       { .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
+
+static int dw_mci_k3_probe(struct platform_device *pdev)
+{
+       const struct dw_mci_drv_data *drv_data;
+       const struct of_device_id *match;
+
+       match = of_match_node(dw_mci_k3_match, pdev->dev.of_node);
+       drv_data = match->data;
+
+       return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static int dw_mci_k3_suspend(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+       int ret;
+
+       ret = dw_mci_suspend(host);
+       if (!ret)
+               clk_disable_unprepare(host->ciu_clk);
+
+       return ret;
+}
+
+static int dw_mci_k3_resume(struct device *dev)
+{
+       struct dw_mci *host = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(host->ciu_clk);
+       if (ret) {
+               dev_err(host->dev, "failed to enable ciu clock\n");
+               return ret;
+       }
+
+       return dw_mci_resume(host);
+}
+
+static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
+
+static struct platform_driver dw_mci_k3_pltfm_driver = {
+       .probe          = dw_mci_k3_probe,
+       .remove         = dw_mci_pltfm_remove,
+       .driver         = {
+               .name           = "dwmmc_k3",
+               .of_match_table = dw_mci_k3_match,
+               .pm             = &dw_mci_k3_pmops,
+       },
+};
+
+module_platform_driver(dw_mci_k3_pltfm_driver);
+
+MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc-k3");
index 4bce0deec362c02ab8b5c99cf074ab0b7d669d6c..a776f24f43112484779ac89f031fcb7f6093335d 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/workqueue.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/mmc/slot-gpio.h>
 
 #include "dw_mmc.h"
 
@@ -1032,20 +1033,26 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
        int present;
        struct dw_mci_slot *slot = mmc_priv(mmc);
        struct dw_mci_board *brd = slot->host->pdata;
+       int gpio_cd = !mmc_gpio_get_cd(mmc);
 
        /* Use platform get_cd function, else try onboard card detect */
        if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
                present = 1;
        else if (brd->get_cd)
                present = !brd->get_cd(slot->id);
+       else if (!IS_ERR_VALUE(gpio_cd))
+               present = !!gpio_cd;
        else
                present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
                        == 0 ? 1 : 0;
 
-       if (present)
+       if (present) {
+               set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
                dev_dbg(&mmc->class_dev, "card is present\n");
-       else
+       } else {
+               clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
                dev_dbg(&mmc->class_dev, "card is not present\n");
+       }
 
        return present;
 }
@@ -1926,10 +1933,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
                        /* Card change detected */
                        slot->last_detect_state = present;
 
-                       /* Mark card as present if applicable */
-                       if (present != 0)
-                               set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
-
                        /* Clean up queue if present */
                        mrq = slot->mrq;
                        if (mrq) {
@@ -1977,8 +1980,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
 
                        /* Power down slot */
                        if (present == 0) {
-                               clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
-
                                /* Clear down the FIFO */
                                dw_mci_fifo_reset(host);
 #ifdef CONFIG_MMC_DW_IDMAC
@@ -2079,6 +2080,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
 
        return gpio;
 }
+
+/* find the cd gpio for a given slot; or -1 if none specified */
+static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
+                                       struct mmc_host *mmc)
+{
+       struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
+       int gpio;
+
+       if (!np)
+               return;
+
+       gpio = of_get_named_gpio(np, "cd-gpios", 0);
+
+       /* Having a missing entry is valid; return silently */
+       if (!gpio_is_valid(gpio))
+               return;
+
+       if (mmc_gpio_request_cd(mmc, gpio, 0))
+               dev_warn(dev, "gpio [%d] request failed\n", gpio);
+}
 #else /* CONFIG_OF */
 static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
 {
@@ -2096,6 +2117,11 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
 {
        return -EINVAL;
 }
+static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
+                                       struct mmc_host *mmc)
+{
+       return;
+}
 #endif /* CONFIG_OF */
 
 static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
@@ -2197,12 +2223,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 #endif /* CONFIG_MMC_DW_IDMAC */
        }
 
-       if (dw_mci_get_cd(mmc))
-               set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
-       else
-               clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
-
        slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
+       dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc);
 
        ret = mmc_add_host(mmc);
        if (ret)
index 50fc9df791b2934deb5acb25532440dd32347551..073e871a0fc8fe8ef00e6aca2da8a46b172488e6 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/module.h>
@@ -69,37 +70,25 @@ struct mxs_mmc_host {
        unsigned char                   bus_width;
        spinlock_t                      lock;
        int                             sdio_irq_en;
-       int                             wp_gpio;
-       bool                            wp_inverted;
-       bool                            cd_inverted;
-       bool                            broken_cd;
-       bool                            non_removable;
 };
 
-static int mxs_mmc_get_ro(struct mmc_host *mmc)
+static int mxs_mmc_get_cd(struct mmc_host *mmc)
 {
        struct mxs_mmc_host *host = mmc_priv(mmc);
-       int ret;
-
-       if (!gpio_is_valid(host->wp_gpio))
-               return -EINVAL;
-
-       ret = gpio_get_value(host->wp_gpio);
+       struct mxs_ssp *ssp = &host->ssp;
+       int present, ret;
 
-       if (host->wp_inverted)
-               ret = !ret;
+       ret = mmc_gpio_get_cd(mmc);
+       if (ret >= 0)
+               return ret;
 
-       return ret;
-}
+       present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
+                       BM_SSP_STATUS_CARD_DETECT);
 
-static int mxs_mmc_get_cd(struct mmc_host *mmc)
-{
-       struct mxs_mmc_host *host = mmc_priv(mmc);
-       struct mxs_ssp *ssp = &host->ssp;
+       if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
+               present = !present;
 
-       return host->non_removable || host->broken_cd ||
-               !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
-                 BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted;
+       return present;
 }
 
 static int mxs_mmc_reset(struct mxs_mmc_host *host)
@@ -549,7 +538,7 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 
 static const struct mmc_host_ops mxs_mmc_ops = {
        .request = mxs_mmc_request,
-       .get_ro = mxs_mmc_get_ro,
+       .get_ro = mmc_gpio_get_ro,
        .get_cd = mxs_mmc_get_cd,
        .set_ios = mxs_mmc_set_ios,
        .enable_sdio_irq = mxs_mmc_enable_sdio_irq,
@@ -579,15 +568,12 @@ static int mxs_mmc_probe(struct platform_device *pdev)
 {
        const struct of_device_id *of_id =
                        of_match_device(mxs_mmc_dt_ids, &pdev->dev);
-       struct device_node *np = pdev->dev.of_node;
        struct mxs_mmc_host *host;
        struct mmc_host *mmc;
        struct resource *iores;
        int ret = 0, irq_err;
        struct regulator *reg_vmmc;
-       enum of_gpio_flags flags;
        struct mxs_ssp *ssp;
-       u32 bus_width = 0;
 
        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq_err = platform_get_irq(pdev, 0);
@@ -648,23 +634,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
        mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
                    MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
 
-       of_property_read_u32(np, "bus-width", &bus_width);
-       if (bus_width == 4)
-               mmc->caps |= MMC_CAP_4_BIT_DATA;
-       else if (bus_width == 8)
-               mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
-       host->broken_cd = of_property_read_bool(np, "broken-cd");
-       host->non_removable = of_property_read_bool(np, "non-removable");
-       if (host->non_removable)
-               mmc->caps |= MMC_CAP_NONREMOVABLE;
-       host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
-       if (flags & OF_GPIO_ACTIVE_LOW)
-               host->wp_inverted = 1;
-
-       host->cd_inverted = of_property_read_bool(np, "cd-inverted");
-
        mmc->f_min = 400000;
        mmc->f_max = 288000000;
+
+       ret = mmc_of_parse(mmc);
+       if (ret)
+               goto out_clk_disable;
+
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 
        mmc->max_segs = 52;
index ef19874fcd1f4dcd8ab6d9821091f542021ccc8f..1fa645384242e3138cfb664d56bb4f3e9ac392ef 100644 (file)
@@ -144,6 +144,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
        { "80860F14" , "3" , &sdhci_acpi_slot_int_sd   },
        { "INT33BB"  , "2" , &sdhci_acpi_slot_int_sdio },
        { "INT33C6"  , NULL, &sdhci_acpi_slot_int_sdio },
+       { "INT3436"  , NULL, &sdhci_acpi_slot_int_sdio },
        { "PNP0D40"  },
        { },
 };
@@ -152,6 +153,7 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
        { "80860F14" },
        { "INT33BB"  },
        { "INT33C6"  },
+       { "INT3436"  },
        { "PNP0D40"  },
        { },
 };
index 461a4c3f4ef725924319058e531aaa457bffd7d5..b841bb7cd3714c11982a7f2b652a946dfb5e811c 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/of_gpio.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_data/mmc-esdhc-imx.h>
+#include <linux/pm_runtime.h>
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
 
@@ -45,6 +46,8 @@
 #define  ESDHC_MIX_CTRL_FBCLK_SEL      (1 << 25)
 /* Bits 3 and 6 are not SDHCI standard definitions */
 #define  ESDHC_MIX_CTRL_SDHCI_MASK     0xb7
+/* Tuning bits */
+#define  ESDHC_MIX_CTRL_TUNING_MASK    0x03c00000
 
 /* dll control register */
 #define ESDHC_DLL_CTRL                 0x60
@@ -385,6 +388,22 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
                return ret;
        }
 
+       if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
+               if (esdhc_is_usdhc(imx_data)) {
+                       u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
+                       /* Swap AC23 bit */
+                       if (m & ESDHC_MIX_CTRL_AC23EN) {
+                               ret &= ~ESDHC_MIX_CTRL_AC23EN;
+                               ret |= SDHCI_TRNS_AUTO_CMD23;
+                       }
+               } else {
+                       ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
+               }
+
+               return ret;
+       }
+
        return readw(host->ioaddr + reg);
 }
 
@@ -421,24 +440,20 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
                        u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
                        u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
-                       new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+                       if (val & SDHCI_CTRL_TUNED_CLK) {
+                               v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+                       } else {
+                               v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+                               m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+                       }
+
                        if (val & SDHCI_CTRL_EXEC_TUNING) {
-                               new_val |= ESDHC_STD_TUNING_EN |
-                                               ESDHC_TUNING_START_TAP;
                                v |= ESDHC_MIX_CTRL_EXE_TUNE;
                                m |= ESDHC_MIX_CTRL_FBCLK_SEL;
                        } else {
-                               new_val &= ~ESDHC_STD_TUNING_EN;
                                v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
-                               m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
                        }
 
-                       if (val & SDHCI_CTRL_TUNED_CLK)
-                               v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
-                       else
-                               v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
-
-                       writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
                        writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
                        writel(m, host->ioaddr + ESDHC_MIX_CTRL);
                }
@@ -546,7 +561,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
                 * Do it manually here.
                 */
                if (esdhc_is_usdhc(imx_data)) {
-                       writel(0, host->ioaddr + ESDHC_MIX_CTRL);
+                       /* the tuning bits should be kept during reset */
+                       new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
+                       writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
+                                       host->ioaddr + ESDHC_MIX_CTRL);
                        imx_data->is_ddr = 0;
                }
        }
@@ -558,19 +576,17 @@ static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
        struct esdhc_platform_data *boarddata = &imx_data->boarddata;
 
-       u32 f_host = clk_get_rate(pltfm_host->clk);
-
-       if (boarddata->f_max && (boarddata->f_max < f_host))
+       if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock))
                return boarddata->f_max;
        else
-               return f_host;
+               return pltfm_host->clock;
 }
 
 static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 
-       return clk_get_rate(pltfm_host->clk) / 256 / 16;
+       return pltfm_host->clock / 256 / 16;
 }
 
 static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
@@ -578,7 +594,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct pltfm_imx_data *imx_data = pltfm_host->priv;
-       unsigned int host_clock = clk_get_rate(pltfm_host->clk);
+       unsigned int host_clock = pltfm_host->clock;
        int pre_div = 2;
        int div = 1;
        u32 temp, val;
@@ -681,6 +697,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
        /* FIXME: delay a bit for card to be ready for next tuning due to errors */
        mdelay(1);
 
+       pm_runtime_get_sync(host->mmc->parent);
        reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
        reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
                        ESDHC_MIX_CTRL_FBCLK_SEL;
@@ -699,7 +716,7 @@ static void esdhc_request_done(struct mmc_request *mrq)
 static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
 {
        struct mmc_command cmd = {0};
-       struct mmc_request mrq = {0};
+       struct mmc_request mrq = {NULL};
        struct mmc_data data = {0};
        struct scatterlist sg;
        char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
@@ -809,6 +826,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
                pinctrl = imx_data->pins_100mhz;
                break;
        case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_MMC_HS200:
                pinctrl = imx_data->pins_200mhz;
                break;
        default:
@@ -836,6 +854,7 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
                imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
                break;
        case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_MMC_HS200:
                imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
                break;
        case MMC_TIMING_UHS_DDR50:
@@ -976,7 +995,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        }
 
        pltfm_host->clk = imx_data->clk_per;
-
+       pltfm_host->clock = clk_get_rate(pltfm_host->clk);
        clk_prepare_enable(imx_data->clk_per);
        clk_prepare_enable(imx_data->clk_ipg);
        clk_prepare_enable(imx_data->clk_ahb);
@@ -1009,11 +1028,18 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        if (esdhc_is_usdhc(imx_data)) {
                writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
                host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+               host->mmc->caps |= MMC_CAP_1_8V_DDR;
        }
 
        if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
                sdhci_esdhc_ops.platform_execute_tuning =
                                        esdhc_executing_tuning;
+
+       if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
+               writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) |
+                       ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
+                       host->ioaddr + ESDHC_TUNING_CTRL);
+
        boarddata = &imx_data->boarddata;
        if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
                if (!host->mmc->parent->platform_data) {
@@ -1053,7 +1079,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                break;
 
        case ESDHC_CD_PERMANENT:
-               host->mmc->caps = MMC_CAP_NONREMOVABLE;
+               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
                break;
 
        case ESDHC_CD_NONE:
@@ -1094,6 +1120,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        if (err)
                goto disable_clk;
 
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_suspend_ignore_children(&pdev->dev, 1);
+
        return 0;
 
 disable_clk:
@@ -1114,21 +1146,63 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
 
        sdhci_remove_host(host, dead);
 
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       if (!IS_ENABLED(CONFIG_PM_RUNTIME)) {
+               clk_disable_unprepare(imx_data->clk_per);
+               clk_disable_unprepare(imx_data->clk_ipg);
+               clk_disable_unprepare(imx_data->clk_ahb);
+       }
+
+       sdhci_pltfm_free(pdev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_esdhc_runtime_suspend(struct device *dev)
+{
+       struct sdhci_host *host = dev_get_drvdata(dev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+       int ret;
+
+       ret = sdhci_runtime_suspend_host(host);
+
        clk_disable_unprepare(imx_data->clk_per);
        clk_disable_unprepare(imx_data->clk_ipg);
        clk_disable_unprepare(imx_data->clk_ahb);
 
-       sdhci_pltfm_free(pdev);
+       return ret;
+}
 
-       return 0;
+static int sdhci_esdhc_runtime_resume(struct device *dev)
+{
+       struct sdhci_host *host = dev_get_drvdata(dev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_imx_data *imx_data = pltfm_host->priv;
+
+       clk_prepare_enable(imx_data->clk_per);
+       clk_prepare_enable(imx_data->clk_ipg);
+       clk_prepare_enable(imx_data->clk_ahb);
+
+       return sdhci_runtime_resume_host(host);
 }
+#endif
+
+static const struct dev_pm_ops sdhci_esdhc_pmops = {
+       SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
+       SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
+                               sdhci_esdhc_runtime_resume, NULL)
+};
 
 static struct platform_driver sdhci_esdhc_imx_driver = {
        .driver         = {
                .name   = "sdhci-esdhc-imx",
                .owner  = THIS_MODULE,
                .of_match_table = imx_esdhc_dt_ids,
-               .pm     = SDHCI_PLTFM_PMOPS,
+               .pm     = &sdhci_esdhc_pmops,
        },
        .id_table       = imx_esdhc_devtype,
        .probe          = sdhci_esdhc_imx_probe,
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
new file mode 100644 (file)
index 0000000..f7c7cf6
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Arasan Secure Digital Host Controller Interface.
+ * Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
+ * Copyright (c) 2012 Wind River Systems, Inc.
+ * Copyright (C) 2013 Pengutronix e.K.
+ * Copyright (C) 2013 Xilinx Inc.
+ *
+ * Based on sdhci-of-esdhc.c
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *         Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/module.h>
+#include "sdhci-pltfm.h"
+
+#define SDHCI_ARASAN_CLK_CTRL_OFFSET   0x2c
+
+#define CLK_CTRL_TIMEOUT_SHIFT         16
+#define CLK_CTRL_TIMEOUT_MASK          (0xf << CLK_CTRL_TIMEOUT_SHIFT)
+#define CLK_CTRL_TIMEOUT_MIN_EXP       13
+
+/**
+ * struct sdhci_arasan_data
+ * @clk_ahb:   Pointer to the AHB clock
+ */
+struct sdhci_arasan_data {
+       struct clk      *clk_ahb;
+};
+
+static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
+{
+       u32 div;
+       unsigned long freq;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+       div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
+       div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
+
+       freq = clk_get_rate(pltfm_host->clk);
+       freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
+
+       return freq;
+}
+
+static struct sdhci_ops sdhci_arasan_ops = {
+       .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+       .get_timeout_clock = sdhci_arasan_get_timeout_clock,
+};
+
+static struct sdhci_pltfm_data sdhci_arasan_pdata = {
+       .ops = &sdhci_arasan_ops,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * sdhci_arasan_suspend - Suspend method for the driver
+ * @dev:       Address of the device structure
+ * Returns 0 on success and error value on error
+ *
+ * Put the device in a low power state.
+ */
+static int sdhci_arasan_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct sdhci_host *host = platform_get_drvdata(pdev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
+       int ret;
+
+       ret = sdhci_suspend_host(host);
+       if (ret)
+               return ret;
+
+       clk_disable(pltfm_host->clk);
+       clk_disable(sdhci_arasan->clk_ahb);
+
+       return 0;
+}
+
+/**
+ * sdhci_arasan_resume - Resume method for the driver
+ * @dev:       Address of the device structure
+ * Returns 0 on success and error value on error
+ *
+ * Resume operation after suspend
+ */
+static int sdhci_arasan_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct sdhci_host *host = platform_get_drvdata(pdev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
+       int ret;
+
+       ret = clk_enable(sdhci_arasan->clk_ahb);
+       if (ret) {
+               dev_err(dev, "Cannot enable AHB clock.\n");
+               return ret;
+       }
+
+       ret = clk_enable(pltfm_host->clk);
+       if (ret) {
+               dev_err(dev, "Cannot enable SD clock.\n");
+               clk_disable(sdhci_arasan->clk_ahb);
+               return ret;
+       }
+
+       return sdhci_resume_host(host);
+}
+#endif /* ! CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
+                        sdhci_arasan_resume);
+
+static int sdhci_arasan_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct clk *clk_xin;
+       struct sdhci_host *host;
+       struct sdhci_pltfm_host *pltfm_host;
+       struct sdhci_arasan_data *sdhci_arasan;
+
+       sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
+                       GFP_KERNEL);
+       if (!sdhci_arasan)
+               return -ENOMEM;
+
+       sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
+       if (IS_ERR(sdhci_arasan->clk_ahb)) {
+               dev_err(&pdev->dev, "clk_ahb clock not found.\n");
+               return PTR_ERR(sdhci_arasan->clk_ahb);
+       }
+
+       clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
+       if (IS_ERR(clk_xin)) {
+               dev_err(&pdev->dev, "clk_xin clock not found.\n");
+               return PTR_ERR(clk_xin);
+       }
+
+       ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(clk_xin);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to enable SD clock.\n");
+               goto clk_dis_ahb;
+       }
+
+       host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
+       if (IS_ERR(host)) {
+               ret = PTR_ERR(host);
+               dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
+               goto clk_disable_all;
+       }
+
+       sdhci_get_of_property(pdev);
+       pltfm_host = sdhci_priv(host);
+       pltfm_host->priv = sdhci_arasan;
+       pltfm_host->clk = clk_xin;
+
+       ret = sdhci_add_host(host);
+       if (ret) {
+               dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
+               goto err_pltfm_free;
+       }
+
+       return 0;
+
+err_pltfm_free:
+       sdhci_pltfm_free(pdev);
+clk_disable_all:
+       clk_disable_unprepare(clk_xin);
+clk_dis_ahb:
+       clk_disable_unprepare(sdhci_arasan->clk_ahb);
+
+       return ret;
+}
+
+static int sdhci_arasan_remove(struct platform_device *pdev)
+{
+       struct sdhci_host *host = platform_get_drvdata(pdev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
+
+       clk_disable_unprepare(pltfm_host->clk);
+       clk_disable_unprepare(sdhci_arasan->clk_ahb);
+
+       return sdhci_pltfm_unregister(pdev);
+}
+
+static const struct of_device_id sdhci_arasan_of_match[] = {
+       { .compatible = "arasan,sdhci-8.9a" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
+
+static struct platform_driver sdhci_arasan_driver = {
+       .driver = {
+               .name = "sdhci-arasan",
+               .owner = THIS_MODULE,
+               .of_match_table = sdhci_arasan_of_match,
+               .pm = &sdhci_arasan_dev_pm_ops,
+       },
+       .probe = sdhci_arasan_probe,
+       .remove = sdhci_arasan_remove,
+};
+
+module_platform_driver(sdhci_arasan_driver);
+
+MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
+MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c
new file mode 100644 (file)
index 0000000..f49666b
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 BayHub Technology Ltd.
+ *
+ * Authors: Peter Guo <peter.guo@bayhubtech.com>
+ *          Adam Lee <adam.lee@canonical.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/pci.h>
+
+#include "sdhci.h"
+#include "sdhci-pci.h"
+#include "sdhci-pci-o2micro.h"
+
+void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
+{
+       u32 scratch_32;
+       int ret;
+       /* Improve write performance for SD3.0 */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14));
+       pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32);
+
+       /* Enable Link abnormal reset generating Reset */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~((1 << 19) | (1 << 11));
+       scratch_32 |= (1 << 10);
+       pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32);
+
+       /* set card power over current protection */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 |= (1 << 4);
+       pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32);
+
+       /* adjust the output delay for SD mode */
+       pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492);
+
+       /* Set the output voltage setting of Aux 1.2v LDO */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~(3 << 12);
+       pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32);
+
+       /* Set Max power supply capability of SD host */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~(0x01FE);
+       scratch_32 |= 0x00CC;
+       pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32);
+       /* Set DLL Tuning Window */
+       ret = pci_read_config_dword(chip->pdev,
+                                   O2_SD_TUNING_CTRL, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~(0x000000FF);
+       scratch_32 |= 0x00000066;
+       pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32);
+
+       /* Set UHS2 T_EIDLE */
+       ret = pci_read_config_dword(chip->pdev,
+                                   O2_SD_UHS2_L1_CTRL, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~(0x000000FC);
+       scratch_32 |= 0x00000084;
+       pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32);
+
+       /* Set UHS2 Termination */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~((1 << 21) | (1 << 30));
+
+       /* Set RTD3 function disabled */
+       scratch_32 |= ((1 << 29) | (1 << 28));
+       pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32);
+
+       /* Set L1 Entrance Timer */
+       ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~(0xf0000000);
+       scratch_32 |= 0x30000000;
+       pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32);
+
+       ret = pci_read_config_dword(chip->pdev,
+                                   O2_SD_MISC_CTRL4, &scratch_32);
+       if (ret)
+               return;
+       scratch_32 &= ~(0x000f0000);
+       scratch_32 |= 0x00080000;
+       pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32);
+}
+EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init);
+
+int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
+{
+       struct sdhci_pci_chip *chip;
+       struct sdhci_host *host;
+       u32 reg;
+
+       chip = slot->chip;
+       host = slot->host;
+       switch (chip->pdev->device) {
+       case PCI_DEVICE_ID_O2_SDS0:
+       case PCI_DEVICE_ID_O2_SEABIRD0:
+       case PCI_DEVICE_ID_O2_SEABIRD1:
+       case PCI_DEVICE_ID_O2_SDS1:
+       case PCI_DEVICE_ID_O2_FUJIN2:
+               reg = sdhci_readl(host, O2_SD_VENDOR_SETTING);
+               if (reg & 0x1)
+                       host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+
+               if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2)
+                       break;
+               /* set dll watch dog timer */
+               reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2);
+               reg |= (1 << 12);
+               sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2);
+
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot);
+
+int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
+{
+       int ret;
+       u8 scratch;
+       u32 scratch_32;
+
+       switch (chip->pdev->device) {
+       case PCI_DEVICE_ID_O2_8220:
+       case PCI_DEVICE_ID_O2_8221:
+       case PCI_DEVICE_ID_O2_8320:
+       case PCI_DEVICE_ID_O2_8321:
+               /* This extra setup is required due to broken ADMA. */
+               ret = pci_read_config_byte(chip->pdev,
+                               O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+               scratch &= 0x7f;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+
+               /* Set Multi 3 to VCC3V# */
+               pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
+
+               /* Disable CLK_REQ# support after media DET */
+               ret = pci_read_config_byte(chip->pdev,
+                               O2_SD_CLKREQ, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x20;
+               pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
+
+               /* Choose capabilities, enable SDMA.  We have to write 0x01
+                * to the capabilities register first to unlock it.
+                */
+               ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x01;
+               pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
+               pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
+
+               /* Disable ADMA1/2 */
+               pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
+               pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
+
+               /* Disable the infinite transfer mode */
+               ret = pci_read_config_byte(chip->pdev,
+                               O2_SD_INF_MOD, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x08;
+               pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
+
+               /* Lock WP */
+               ret = pci_read_config_byte(chip->pdev,
+                               O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x80;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+               break;
+       case PCI_DEVICE_ID_O2_SDS0:
+       case PCI_DEVICE_ID_O2_SDS1:
+       case PCI_DEVICE_ID_O2_FUJIN2:
+               /* UnLock WP */
+               ret = pci_read_config_byte(chip->pdev,
+                               O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+
+               scratch &= 0x7f;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+
+               /* Set timeout CLK */
+               ret = pci_read_config_dword(chip->pdev,
+                                           O2_SD_CLK_SETTING, &scratch_32);
+               if (ret)
+                       return ret;
+
+               scratch_32 &= ~(0xFF00);
+               scratch_32 |= 0x07E0C800;
+               pci_write_config_dword(chip->pdev,
+                                      O2_SD_CLK_SETTING, scratch_32);
+
+               ret = pci_read_config_dword(chip->pdev,
+                                           O2_SD_CLKREQ, &scratch_32);
+               if (ret)
+                       return ret;
+               scratch_32 |= 0x3;
+               pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32);
+
+               ret = pci_read_config_dword(chip->pdev,
+                                           O2_SD_PLL_SETTING, &scratch_32);
+               if (ret)
+                       return ret;
+
+               scratch_32 &= ~(0x1F3F070E);
+               scratch_32 |= 0x18270106;
+               pci_write_config_dword(chip->pdev,
+                                      O2_SD_PLL_SETTING, scratch_32);
+
+               /* Disable UHS1 funciton */
+               ret = pci_read_config_dword(chip->pdev,
+                                           O2_SD_CAP_REG2, &scratch_32);
+               if (ret)
+                       return ret;
+               scratch_32 &= ~(0xE0);
+               pci_write_config_dword(chip->pdev,
+                                      O2_SD_CAP_REG2, scratch_32);
+
+               if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2)
+                       sdhci_pci_o2_fujin2_pci_init(chip);
+
+               /* Lock WP */
+               ret = pci_read_config_byte(chip->pdev,
+                                          O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x80;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+               break;
+       case PCI_DEVICE_ID_O2_SEABIRD0:
+       case PCI_DEVICE_ID_O2_SEABIRD1:
+               /* UnLock WP */
+               ret = pci_read_config_byte(chip->pdev,
+                               O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+
+               scratch &= 0x7f;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+
+               ret = pci_read_config_dword(chip->pdev,
+                                           O2_SD_FUNC_REG0, &scratch_32);
+
+               if ((scratch_32 & 0xff000000) == 0x01000000) {
+                       scratch_32 &= 0x0000FFFF;
+                       scratch_32 |= 0x1F340000;
+
+                       pci_write_config_dword(chip->pdev,
+                                              O2_SD_PLL_SETTING, scratch_32);
+               } else {
+                       scratch_32 &= 0x0000FFFF;
+                       scratch_32 |= 0x2c280000;
+
+                       pci_write_config_dword(chip->pdev,
+                                              O2_SD_PLL_SETTING, scratch_32);
+
+                       ret = pci_read_config_dword(chip->pdev,
+                                                   O2_SD_FUNC_REG4,
+                                                   &scratch_32);
+                       scratch_32 |= (1 << 22);
+                       pci_write_config_dword(chip->pdev,
+                                              O2_SD_FUNC_REG4, scratch_32);
+               }
+
+               /* Lock WP */
+               ret = pci_read_config_byte(chip->pdev,
+                                          O2_SD_LOCK_WP, &scratch);
+               if (ret)
+                       return ret;
+               scratch |= 0x80;
+               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe);
+
+int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
+{
+       sdhci_pci_o2_probe(chip);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume);
diff --git a/drivers/mmc/host/sdhci-pci-o2micro.h b/drivers/mmc/host/sdhci-pci-o2micro.h
new file mode 100644 (file)
index 0000000..dbec4c9
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 BayHub Technology Ltd.
+ *
+ * Authors: Peter Guo <peter.guo@bayhubtech.com>
+ *          Adam Lee <adam.lee@canonical.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SDHCI_PCI_O2MICRO_H
+#define __SDHCI_PCI_O2MICRO_H
+
+#include "sdhci-pci.h"
+
+/*
+ * O2Micro device IDs
+ */
+
+#define PCI_DEVICE_ID_O2_SDS0          0x8420
+#define PCI_DEVICE_ID_O2_SDS1          0x8421
+#define PCI_DEVICE_ID_O2_FUJIN2                0x8520
+#define PCI_DEVICE_ID_O2_SEABIRD0      0x8620
+#define PCI_DEVICE_ID_O2_SEABIRD1      0x8621
+
+/*
+ * O2Micro device registers
+ */
+
+#define O2_SD_MISC_REG5                0x64
+#define O2_SD_LD0_CTRL         0x68
+#define O2_SD_DEV_CTRL         0x88
+#define O2_SD_LOCK_WP          0xD3
+#define O2_SD_TEST_REG         0xD4
+#define O2_SD_FUNC_REG0                0xDC
+#define O2_SD_MULTI_VCC3V      0xEE
+#define O2_SD_CLKREQ           0xEC
+#define O2_SD_CAPS             0xE0
+#define O2_SD_ADMA1            0xE2
+#define O2_SD_ADMA2            0xE7
+#define O2_SD_INF_MOD          0xF1
+#define O2_SD_MISC_CTRL4       0xFC
+#define O2_SD_TUNING_CTRL      0x300
+#define O2_SD_PLL_SETTING      0x304
+#define O2_SD_CLK_SETTING      0x328
+#define O2_SD_CAP_REG2         0x330
+#define O2_SD_CAP_REG0         0x334
+#define O2_SD_UHS1_CAP_SETTING 0x33C
+#define O2_SD_DELAY_CTRL       0x350
+#define O2_SD_UHS2_L1_CTRL     0x35C
+#define O2_SD_FUNC_REG3                0x3E0
+#define O2_SD_FUNC_REG4                0x3E4
+
+#define O2_SD_VENDOR_SETTING   0x110
+#define O2_SD_VENDOR_SETTING2  0x1C8
+
+extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip);
+
+extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
+
+extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
+
+extern int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
+
+#endif /* __SDHCI_PCI_O2MICRO_H */
index 8f753811fc7a6ccec6ca8bf5090cc98287013f45..f46b84ce4ec8dc13ab8d5412117373478591e0bf 100644 (file)
 #include <linux/mmc/sdhci-pci-data.h>
 
 #include "sdhci.h"
-
-/*
- * PCI device IDs
- */
-#define PCI_DEVICE_ID_INTEL_PCH_SDIO0  0x8809
-#define PCI_DEVICE_ID_INTEL_PCH_SDIO1  0x880a
-#define PCI_DEVICE_ID_INTEL_BYT_EMMC   0x0f14
-#define PCI_DEVICE_ID_INTEL_BYT_SDIO   0x0f15
-#define PCI_DEVICE_ID_INTEL_BYT_SD     0x0f16
-#define PCI_DEVICE_ID_INTEL_BYT_EMMC2  0x0f50
-#define PCI_DEVICE_ID_INTEL_MRFL_MMC   0x1190
-#define PCI_DEVICE_ID_INTEL_CLV_SDIO0  0x08f9
-#define PCI_DEVICE_ID_INTEL_CLV_SDIO1  0x08fa
-#define PCI_DEVICE_ID_INTEL_CLV_SDIO2  0x08fb
-#define PCI_DEVICE_ID_INTEL_CLV_EMMC0  0x08e5
-#define PCI_DEVICE_ID_INTEL_CLV_EMMC1  0x08e6
-
-/*
- * PCI registers
- */
-
-#define PCI_SDHCI_IFPIO                        0x00
-#define PCI_SDHCI_IFDMA                        0x01
-#define PCI_SDHCI_IFVENDOR             0x02
-
-#define PCI_SLOT_INFO                  0x40    /* 8 bits */
-#define  PCI_SLOT_INFO_SLOTS(x)                ((x >> 4) & 7)
-#define  PCI_SLOT_INFO_FIRST_BAR_MASK  0x07
-
-#define MAX_SLOTS                      8
-
-struct sdhci_pci_chip;
-struct sdhci_pci_slot;
-
-struct sdhci_pci_fixes {
-       unsigned int            quirks;
-       unsigned int            quirks2;
-       bool                    allow_runtime_pm;
-
-       int                     (*probe) (struct sdhci_pci_chip *);
-
-       int                     (*probe_slot) (struct sdhci_pci_slot *);
-       void                    (*remove_slot) (struct sdhci_pci_slot *, int);
-
-       int                     (*suspend) (struct sdhci_pci_chip *);
-       int                     (*resume) (struct sdhci_pci_chip *);
-};
-
-struct sdhci_pci_slot {
-       struct sdhci_pci_chip   *chip;
-       struct sdhci_host       *host;
-       struct sdhci_pci_data   *data;
-
-       int                     pci_bar;
-       int                     rst_n_gpio;
-       int                     cd_gpio;
-       int                     cd_irq;
-
-       void (*hw_reset)(struct sdhci_host *host);
-};
-
-struct sdhci_pci_chip {
-       struct pci_dev          *pdev;
-
-       unsigned int            quirks;
-       unsigned int            quirks2;
-       bool                    allow_runtime_pm;
-       const struct sdhci_pci_fixes *fixes;
-
-       int                     num_slots;      /* Slots on controller */
-       struct sdhci_pci_slot   *slots[MAX_SLOTS]; /* Pointers to host slots */
-};
-
+#include "sdhci-pci.h"
+#include "sdhci-pci-o2micro.h"
 
 /*****************************************************************************\
  *                                                                           *
@@ -381,6 +310,7 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
 
 static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
        .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+       .quirks2        = SDHCI_QUIRK2_BROKEN_HS200,
        .probe_slot     = intel_mrfl_mmc_probe_slot,
 };
 
@@ -393,65 +323,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
 #define O2_SD_ADMA2            0xE7
 #define O2_SD_INF_MOD          0xF1
 
-static int o2_probe(struct sdhci_pci_chip *chip)
-{
-       int ret;
-       u8 scratch;
-
-       switch (chip->pdev->device) {
-       case PCI_DEVICE_ID_O2_8220:
-       case PCI_DEVICE_ID_O2_8221:
-       case PCI_DEVICE_ID_O2_8320:
-       case PCI_DEVICE_ID_O2_8321:
-               /* This extra setup is required due to broken ADMA. */
-               ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
-               if (ret)
-                       return ret;
-               scratch &= 0x7f;
-               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
-
-               /* Set Multi 3 to VCC3V# */
-               pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
-
-               /* Disable CLK_REQ# support after media DET */
-               ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch);
-               if (ret)
-                       return ret;
-               scratch |= 0x20;
-               pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
-
-               /* Choose capabilities, enable SDMA.  We have to write 0x01
-                * to the capabilities register first to unlock it.
-                */
-               ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
-               if (ret)
-                       return ret;
-               scratch |= 0x01;
-               pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
-               pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
-
-               /* Disable ADMA1/2 */
-               pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
-               pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
-
-               /* Disable the infinite transfer mode */
-               ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch);
-               if (ret)
-                       return ret;
-               scratch |= 0x08;
-               pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
-
-               /* Lock WP */
-               ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
-               if (ret)
-                       return ret;
-               scratch |= 0x80;
-               pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
-       }
-
-       return 0;
-}
-
 static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
 {
        u8 scratch;
@@ -642,7 +513,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
 }
 
 static const struct sdhci_pci_fixes sdhci_o2 = {
-       .probe          = o2_probe,
+       .probe = sdhci_pci_o2_probe,
+       .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+       .probe_slot = sdhci_pci_o2_probe_slot,
+       .resume = sdhci_pci_o2_resume,
 };
 
 static const struct sdhci_pci_fixes sdhci_jmicron = {
@@ -1055,6 +929,46 @@ static const struct pci_device_id pci_ids[] = {
                .driver_data    = (kernel_ulong_t)&sdhci_o2,
        },
 
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_FUJIN2,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_SDS0,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_SDS1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_SEABIRD0,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_O2,
+               .device         = PCI_DEVICE_ID_O2_SEABIRD1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_o2,
+       },
+
        {       /* Generic SD host controller */
                PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
        },
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
new file mode 100644 (file)
index 0000000..0347804
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef __SDHCI_PCI_H
+#define __SDHCI_PCI_H
+
+/*
+ * PCI device IDs
+ */
+
+#define PCI_DEVICE_ID_INTEL_PCH_SDIO0  0x8809
+#define PCI_DEVICE_ID_INTEL_PCH_SDIO1  0x880a
+#define PCI_DEVICE_ID_INTEL_BYT_EMMC   0x0f14
+#define PCI_DEVICE_ID_INTEL_BYT_SDIO   0x0f15
+#define PCI_DEVICE_ID_INTEL_BYT_SD     0x0f16
+#define PCI_DEVICE_ID_INTEL_BYT_EMMC2  0x0f50
+#define PCI_DEVICE_ID_INTEL_MRFL_MMC   0x1190
+#define PCI_DEVICE_ID_INTEL_CLV_SDIO0  0x08f9
+#define PCI_DEVICE_ID_INTEL_CLV_SDIO1  0x08fa
+#define PCI_DEVICE_ID_INTEL_CLV_SDIO2  0x08fb
+#define PCI_DEVICE_ID_INTEL_CLV_EMMC0  0x08e5
+#define PCI_DEVICE_ID_INTEL_CLV_EMMC1  0x08e6
+
+/*
+ * PCI registers
+ */
+
+#define PCI_SDHCI_IFPIO                        0x00
+#define PCI_SDHCI_IFDMA                        0x01
+#define PCI_SDHCI_IFVENDOR             0x02
+
+#define PCI_SLOT_INFO                  0x40    /* 8 bits */
+#define  PCI_SLOT_INFO_SLOTS(x)                ((x >> 4) & 7)
+#define  PCI_SLOT_INFO_FIRST_BAR_MASK  0x07
+
+#define MAX_SLOTS                      8
+
+struct sdhci_pci_chip;
+struct sdhci_pci_slot;
+
+struct sdhci_pci_fixes {
+       unsigned int            quirks;
+       unsigned int            quirks2;
+       bool                    allow_runtime_pm;
+
+       int                     (*probe) (struct sdhci_pci_chip *);
+
+       int                     (*probe_slot) (struct sdhci_pci_slot *);
+       void                    (*remove_slot) (struct sdhci_pci_slot *, int);
+
+       int                     (*suspend) (struct sdhci_pci_chip *);
+       int                     (*resume) (struct sdhci_pci_chip *);
+};
+
+struct sdhci_pci_slot {
+       struct sdhci_pci_chip   *chip;
+       struct sdhci_host       *host;
+       struct sdhci_pci_data   *data;
+
+       int                     pci_bar;
+       int                     rst_n_gpio;
+       int                     cd_gpio;
+       int                     cd_irq;
+
+       void (*hw_reset)(struct sdhci_host *host);
+};
+
+struct sdhci_pci_chip {
+       struct pci_dev          *pdev;
+
+       unsigned int            quirks;
+       unsigned int            quirks2;
+       bool                    allow_runtime_pm;
+       const struct sdhci_pci_fixes *fixes;
+
+       int                     num_slots;      /* Slots on controller */
+       struct sdhci_pci_slot   *slots[MAX_SLOTS]; /* Pointers to host slots */
+};
+
+#endif /* __SDHCI_PCI_H */
index e2065a44dffcbe42da8c156534a7f2f7151fe2cd..bef250e954188c3feb5d6d0889c1c32ef620de39 100644 (file)
@@ -237,19 +237,21 @@ int sdhci_pltfm_unregister(struct platform_device *pdev)
 EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
 
 #ifdef CONFIG_PM
-static int sdhci_pltfm_suspend(struct device *dev)
+int sdhci_pltfm_suspend(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
 
        return sdhci_suspend_host(host);
 }
+EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
 
-static int sdhci_pltfm_resume(struct device *dev)
+int sdhci_pltfm_resume(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
 
        return sdhci_resume_host(host);
 }
+EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
 
 const struct dev_pm_ops sdhci_pltfm_pmops = {
        .suspend        = sdhci_pltfm_suspend,
index e15ced79f7ede521c49bc0c068a78a81324e6ada..04bc2481e5c32cdd5c68242815655994f724d3e7 100644 (file)
@@ -111,6 +111,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
 }
 
 #ifdef CONFIG_PM
+extern int sdhci_pltfm_suspend(struct device *dev);
+extern int sdhci_pltfm_resume(struct device *dev);
 extern const struct dev_pm_ops sdhci_pltfm_pmops;
 #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
 #else
index 5b7b2eba8a5423f3ea4966d1181f26c2d30b685f..a835898a68dd3be47fcbb720a9e83c5da3701458 100644 (file)
@@ -198,6 +198,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = {
 };
 
 static const struct of_device_id sdhci_tegra_dt_match[] = {
+       { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
        { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
        { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
        { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
index bd8a0982aec33ec99b95b97fbcbdd65dee642770..ec3eb30845c76fb5295a5c2027319d289ce9dd1d 100644 (file)
@@ -898,8 +898,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
        u16 mode;
        struct mmc_data *data = cmd->data;
 
-       if (data == NULL)
+       if (data == NULL) {
+               /* clear Auto CMD settings for no data CMDs */
+               mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
+               sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
+                               SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
                return;
+       }
 
        WARN_ON(!host->data);
 
@@ -1391,6 +1396,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                                        mmc->card->type == MMC_TYPE_MMC ?
                                        MMC_SEND_TUNING_BLOCK_HS200 :
                                        MMC_SEND_TUNING_BLOCK;
+
+                               /* Here we need to set the host->mrq to NULL,
+                                * in case the pending finish_tasklet
+                                * finishes it incorrectly.
+                                */
+                               host->mrq = NULL;
+
                                spin_unlock_irqrestore(&host->lock, flags);
                                sdhci_execute_tuning(mmc, tuning_opcode);
                                spin_lock_irqsave(&host->lock, flags);
@@ -3004,7 +3016,8 @@ int sdhci_add_host(struct sdhci_host *host)
                /* SD3.0: SDR104 is supported so (for eMMC) the caps2
                 * field can be promoted to support HS200.
                 */
-               mmc->caps2 |= MMC_CAP2_HS200;
+               if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
+                       mmc->caps2 |= MMC_CAP2_HS200;
        } else if (caps[1] & SDHCI_SUPPORT_SDR50)
                mmc->caps |= MMC_CAP_UHS_SDR50;
 
index d032b080ac4de66311e390458629204332a6ca57..54730f4aac87f0143ca08a5ca740634376fc4871 100644 (file)
@@ -381,73 +381,75 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
                desc, cookie);
 }
 
-static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
-                                struct sh_mmcif_plat_data *pdata)
+static struct dma_chan *
+sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
+                        struct sh_mmcif_plat_data *pdata,
+                        enum dma_transfer_direction direction)
 {
-       struct resource *res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
        struct dma_slave_config cfg;
+       struct dma_chan *chan;
+       unsigned int slave_id;
+       struct resource *res;
        dma_cap_mask_t mask;
        int ret;
 
-       host->dma_active = false;
-
-       if (pdata) {
-               if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
-                       return;
-       } else if (!host->pd->dev.of_node) {
-               return;
-       }
-
-       /* We can only either use DMA for both Tx and Rx or not use it at all */
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
 
-       host->chan_tx = dma_request_slave_channel_compat(mask, shdma_chan_filter,
-                               pdata ? (void *)pdata->slave_id_tx : NULL,
-                               &host->pd->dev, "tx");
-       dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
-               host->chan_tx);
+       if (pdata)
+               slave_id = direction == DMA_MEM_TO_DEV
+                        ? pdata->slave_id_tx : pdata->slave_id_rx;
+       else
+               slave_id = 0;
 
-       if (!host->chan_tx)
-               return;
+       chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
+                               (void *)(unsigned long)slave_id, &host->pd->dev,
+                               direction == DMA_MEM_TO_DEV ? "tx" : "rx");
+
+       dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
+               direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
+
+       if (!chan)
+               return NULL;
+
+       res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
 
        /* In the OF case the driver will get the slave ID from the DT */
-       if (pdata)
-               cfg.slave_id = pdata->slave_id_tx;
-       cfg.direction = DMA_MEM_TO_DEV;
+       cfg.slave_id = slave_id;
+       cfg.direction = direction;
        cfg.dst_addr = res->start + MMCIF_CE_DATA;
        cfg.src_addr = 0;
-       ret = dmaengine_slave_config(host->chan_tx, &cfg);
-       if (ret < 0)
-               goto ecfgtx;
+       ret = dmaengine_slave_config(chan, &cfg);
+       if (ret < 0) {
+               dma_release_channel(chan);
+               return NULL;
+       }
 
-       host->chan_rx = dma_request_slave_channel_compat(mask, shdma_chan_filter,
-                               pdata ? (void *)pdata->slave_id_rx : NULL,
-                               &host->pd->dev, "rx");
-       dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
-               host->chan_rx);
+       return chan;
+}
 
-       if (!host->chan_rx)
-               goto erqrx;
+static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
+                                struct sh_mmcif_plat_data *pdata)
+{
+       host->dma_active = false;
 
-       if (pdata)
-               cfg.slave_id = pdata->slave_id_rx;
-       cfg.direction = DMA_DEV_TO_MEM;
-       cfg.dst_addr = 0;
-       cfg.src_addr = res->start + MMCIF_CE_DATA;
-       ret = dmaengine_slave_config(host->chan_rx, &cfg);
-       if (ret < 0)
-               goto ecfgrx;
+       if (pdata) {
+               if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
+                       return;
+       } else if (!host->pd->dev.of_node) {
+               return;
+       }
 
-       return;
+       /* We can only either use DMA for both Tx and Rx or not use it at all */
+       host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV);
+       if (!host->chan_tx)
+               return;
 
-ecfgrx:
-       dma_release_channel(host->chan_rx);
-       host->chan_rx = NULL;
-erqrx:
-ecfgtx:
-       dma_release_channel(host->chan_tx);
-       host->chan_tx = NULL;
+       host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM);
+       if (!host->chan_rx) {
+               dma_release_channel(host->chan_tx);
+               host->chan_tx = NULL;
+       }
 }
 
 static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
index f344659dceac2739f47e032cbaaec2c54c86b6f6..2d6ce257a2737c7a55b444d51858d392eda0f97b 100644 (file)
@@ -33,6 +33,8 @@
 
 #include "tmio_mmc.h"
 
+#define EXT_ACC           0xe4
+
 struct sh_mobile_sdhi_of_data {
        unsigned long tmio_flags;
 };
@@ -54,7 +56,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int
        struct mmc_host *mmc = platform_get_drvdata(pdev);
        struct tmio_mmc_host *host = mmc_priv(mmc);
        struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
-       int ret = clk_enable(priv->clk);
+       int ret = clk_prepare_enable(priv->clk);
        if (ret < 0)
                return ret;
 
@@ -67,7 +69,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
        struct mmc_host *mmc = platform_get_drvdata(pdev);
        struct tmio_mmc_host *host = mmc_priv(mmc);
        struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
-       clk_disable(priv->clk);
+       clk_disable_unprepare(priv->clk);
 }
 
 static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
@@ -133,9 +135,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
        struct tmio_mmc_data *mmc_data;
        struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
        struct tmio_mmc_host *host;
+       struct resource *res;
        int irq, ret, i = 0;
        bool multiplexed_isr = true;
        struct tmio_mmc_dma *dma_priv;
+       u16 ver;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
        if (priv == NULL) {
@@ -206,10 +214,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
                mmc_data->flags |= of_data->tmio_flags;
        }
 
+       /* SD control register space size is 0x100, 0x200 for bus_shift=1 */
+       mmc_data->bus_shift = resource_size(res) >> 9;
+
        ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
        if (ret < 0)
                goto eprobe;
 
+       /*
+        * FIXME:
+        * this Workaround can be more clever method
+        */
+       ver = sd_ctrl_read16(host, CTL_VERSION);
+       if (ver == 0xCB0D)
+               sd_ctrl_write16(host, EXT_ACC, 1);
+
        /*
         * Allow one or more specific (named) ISRs or
         * one or more multiplexed (un-named) ISRs.
index 8860d4d2bc22329cc7d8b5335e537695bfadf213..1900abb0423695ea06f9444376c62f2516b3b998 100644 (file)
@@ -62,6 +62,7 @@ static int tmio_mmc_probe(struct platform_device *pdev)
        const struct mfd_cell *cell = mfd_get_cell(pdev);
        struct tmio_mmc_data *pdata;
        struct tmio_mmc_host *host;
+       struct resource *res;
        int ret = -EINVAL, irq;
 
        if (pdev->num_resources != 2)
@@ -84,6 +85,14 @@ static int tmio_mmc_probe(struct platform_device *pdev)
                        goto out;
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       /* SD control register space size is 0x200, 0x400 for bus_shift=1 */
+       pdata->bus_shift = resource_size(res) >> 10;
+       pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
+
        ret = tmio_mmc_host_probe(&host, pdev, pdata);
        if (ret)
                goto cell_disable;
index 86fd21e000994d622b7117e88236533a471770e9..aaa9c7e9e730ea072a639e00e64eaa382d15c794 100644 (file)
@@ -58,7 +58,6 @@ enum tmio_mmc_power {
 
 struct tmio_mmc_host {
        void __iomem *ctl;
-       unsigned long bus_shift;
        struct mmc_command      *cmd;
        struct mmc_request      *mrq;
        struct mmc_data         *data;
@@ -176,19 +175,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev);
 
 static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
 {
-       return readw(host->ctl + (addr << host->bus_shift));
+       return readw(host->ctl + (addr << host->pdata->bus_shift));
 }
 
 static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
                u16 *buf, int count)
 {
-       readsw(host->ctl + (addr << host->bus_shift), buf, count);
+       readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
 }
 
 static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
 {
-       return readw(host->ctl + (addr << host->bus_shift)) |
-              readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
+       return readw(host->ctl + (addr << host->pdata->bus_shift)) |
+              readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;
 }
 
 static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
@@ -198,19 +197,19 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val
         */
        if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
                return;
-       writew(val, host->ctl + (addr << host->bus_shift));
+       writew(val, host->ctl + (addr << host->pdata->bus_shift));
 }
 
 static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
                u16 *buf, int count)
 {
-       writesw(host->ctl + (addr << host->bus_shift), buf, count);
+       writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
 }
 
 static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
 {
-       writew(val, host->ctl + (addr << host->bus_shift));
-       writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
+       writew(val, host->ctl + (addr << host->pdata->bus_shift));
+       writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift));
 }
 
 
index 65edb4a62452dc09dfb5318f0adaef6b6f3b2e17..03e7b280cb4c71addd6d10fe52989a3206dd6c18 100644 (file)
@@ -293,7 +293,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
                if (pdata->dma->chan_priv_tx)
                        cfg.slave_id = pdata->dma->slave_id_tx;
                cfg.direction = DMA_MEM_TO_DEV;
-               cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
+               cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
                cfg.src_addr = 0;
                ret = dmaengine_slave_config(host->chan_tx, &cfg);
                if (ret < 0)
index f3b2d8ca1eca5cc831d2269e4617938bf3d90bfb..8d8abf23a61120a03555a9c3a90e9d7571431c15 100644 (file)
@@ -161,10 +161,8 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
 
 static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
 {
-       struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
-
        /* implicit BUG_ON(!res) */
-       if (resource_size(res) > 0x100) {
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
                sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
                msleep(10);
        }
@@ -176,14 +174,12 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
 
 static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
 {
-       struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
-
        sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
                sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
        msleep(10);
 
        /* implicit BUG_ON(!res) */
-       if (resource_size(res) > 0x100) {
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
                sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
                msleep(10);
        }
@@ -191,16 +187,14 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
 
 static void tmio_mmc_reset(struct tmio_mmc_host *host)
 {
-       struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
-
        /* FIXME - should we set stop clock reg here */
        sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
        /* implicit BUG_ON(!res) */
-       if (resource_size(res) > 0x100)
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
                sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
        msleep(10);
        sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
-       if (resource_size(res) > 0x100)
+       if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
                sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
        msleep(10);
 }
@@ -944,17 +938,25 @@ static const struct mmc_host_ops tmio_mmc_ops = {
        .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
 };
 
-static void tmio_mmc_init_ocr(struct tmio_mmc_host *host)
+static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
 {
        struct tmio_mmc_data *pdata = host->pdata;
        struct mmc_host *mmc = host->mmc;
 
        mmc_regulator_get_supply(mmc);
 
+       /* use ocr_mask if no regulator */
        if (!mmc->ocr_avail)
-               mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34;
-       else if (pdata->ocr_mask)
-               dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
+               mmc->ocr_avail =  pdata->ocr_mask;
+
+       /*
+        * try again.
+        * There is possibility that regulator has not been probed
+        */
+       if (!mmc->ocr_avail)
+               return -EPROBE_DEFER;
+
+       return 0;
 }
 
 static void tmio_mmc_of_parse(struct platform_device *pdev,
@@ -1005,8 +1007,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
        _host->set_pwr = pdata->set_pwr;
        _host->set_clk_div = pdata->set_clk_div;
 
-       /* SD control register space size is 0x200, 0x400 for bus_shift=1 */
-       _host->bus_shift = resource_size(res_ctl) >> 10;
+       ret = tmio_mmc_init_ocr(_host);
+       if (ret < 0)
+               goto host_free;
 
        _host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
        if (!_host->ctl) {
@@ -1016,14 +1019,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
 
        mmc->ops = &tmio_mmc_ops;
        mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
-       mmc->caps2 = pdata->capabilities2;
+       mmc->caps2 |= pdata->capabilities2;
        mmc->max_segs = 32;
        mmc->max_blk_size = 512;
        mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
                mmc->max_segs;
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
        mmc->max_seg_size = mmc->max_req_size;
-       tmio_mmc_init_ocr(_host);
 
        _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
                                  mmc->caps & MMC_CAP_NEEDS_POLL ||
index b22883d60500ee540315f6bdd56e2e9f11be56aa..8f6f2e91e7ae639db87613a5426c64effd683996 100644 (file)
  */
 #define TMIO_MMC_USE_GPIO_CD           (1 << 5)
 
+/*
+ * Some controllers doesn't have over 0x100 register.
+ * it is used to checking accessibility of
+ * CTL_SD_CARD_CLK_CTL / CTL_CLK_AND_WAIT_CTL
+ */
+#define TMIO_MMC_HAVE_HIGH_REG         (1 << 6)
+
 int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
 int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
 void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
@@ -102,6 +109,7 @@ struct tmio_mmc_data {
        unsigned long                   capabilities;
        unsigned long                   capabilities2;
        unsigned long                   flags;
+       unsigned long                   bus_shift;
        u32                             ocr_mask;       /* available voltages */
        struct tmio_mmc_dma             *dma;
        struct device                   *dev;
index 3e781b8c0be74dd76e658c4ba78ecbb72516e153..362927c48f978904d1eb86c57e34e5f267f93f10 100644 (file)
@@ -98,6 +98,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON              (1<<4)
 /* Controller has a non-standard host control register */
 #define SDHCI_QUIRK2_BROKEN_HOST_CONTROL               (1<<5)
+/* Controller does not support HS200 */
+#define SDHCI_QUIRK2_BROKEN_HS200                      (1<<6)
 
        int irq;                /* Device IRQ */
        void __iomem *ioaddr;   /* Mapped address */
index a1c1f321e519d4b015255dd06f1bdec87511d263..84d9053b5dca3ef453ac611fd6b223c02e1903c6 100644 (file)
@@ -33,6 +33,7 @@
 #define CTL_SDIO_IRQ_MASK 0x38
 #define CTL_DMA_ENABLE 0xd8
 #define CTL_RESET_SD 0xe0
+#define CTL_VERSION 0xe2
 #define CTL_SDIO_REGS 0x100
 #define CTL_CLK_AND_WAIT_CTL 0x138
 #define CTL_RESET_SDIO 0x1e0