Merge branch 'fixes' into next
authorUlf Hansson <ulf.hansson@linaro.org>
Wed, 21 Mar 2018 10:11:30 +0000 (11:11 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Wed, 21 Mar 2018 10:11:30 +0000 (11:11 +0100)
40 files changed:
Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/mtk-sd.txt
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
Documentation/devicetree/bindings/mmc/tmio_mmc.txt
arch/arm/boot/dts/lpc18xx.dtsi
arch/arm/boot/dts/socfpga_arria10_socdk_sdmmc.dts
arch/arm/boot/dts/socfpga_arria5.dtsi
arch/arm/boot/dts/socfpga_cyclone5.dtsi
arch/arm/boot/dts/socfpga_vt.dts
arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
arch/arm64/boot/dts/hisilicon/hi3660.dtsi
arch/sh/boards/mach-kfr2r09/setup.c
drivers/mmc/core/block.c
drivers/mmc/core/core.c
drivers/mmc/core/debugfs.c
drivers/mmc/core/host.h
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/core/slot-gpio.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/dw_mmc-hi3798cv200.c [new file with mode: 0644]
drivers/mmc/host/dw_mmc-pci.c
drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/mtk-sd.c
drivers/mmc/host/renesas_sdhi_internal_dmac.c
drivers/mmc/host/renesas_sdhi_sys_dmac.c
drivers/mmc/host/sdhci-iproc.c
drivers/mmc/host/sdhci-omap.c
drivers/mmc/host/sdhci-pci-core.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sh_mmcif.c
drivers/mmc/host/sunxi-mmc.c
drivers/mmc/host/tmio_mmc_core.c
drivers/mmc/host/ushc.c
include/linux/mfd/tmio.h
include/linux/mmc/slot-gpio.h

diff --git a/Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/hi3798cv200-dw-mshc.txt
new file mode 100644 (file)
index 0000000..a0693b7
--- /dev/null
@@ -0,0 +1,40 @@
+* Hisilicon Hi3798CV200 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 Hi3798CV200
+specific extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+- compatible: Should contain "hisilicon,hi3798cv200-dw-mshc".
+- clocks: A list of phandle + clock-specifier pairs for the clocks listed
+  in clock-names.
+- clock-names: Should contain the following:
+       "ciu" - The ciu clock described in synopsys-dw-mshc.txt.
+       "biu" - The biu clock described in synopsys-dw-mshc.txt.
+       "ciu-sample" - Hi3798CV200 extended phase clock for ciu sampling.
+       "ciu-drive"  - Hi3798CV200 extended phase clock for ciu driving.
+
+Example:
+
+       emmc: mmc@9830000 {
+               compatible = "hisilicon,hi3798cv200-dw-mshc";
+               reg = <0x9830000 0x10000>;
+               interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&crg HISTB_MMC_CIU_CLK>,
+                        <&crg HISTB_MMC_BIU_CLK>,
+                        <&crg HISTB_MMC_SAMPLE_CLK>,
+                        <&crg HISTB_MMC_DRV_CLK>;
+               clock-names = "ciu", "biu", "ciu-sample", "ciu-drive";
+               fifo-depth = <256>;
+               clock-frequency = <200000000>;
+               cap-mmc-highspeed;
+               mmc-ddr-1_8v;
+               mmc-hs200-1_8v;
+               non-removable;
+               bus-width = <8>;
+       };
index 9b80176..f33467a 100644 (file)
@@ -12,6 +12,7 @@ Required properties:
        "mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
        "mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
        "mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
+       "mediatek,mt7622-mmc": for MT7622 SoC
        "mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC
 
 - reg: physical base address of the controller and length
index ef3e5f1..7e5e427 100644 (file)
@@ -59,15 +59,6 @@ Optional properties:
   is specified and the ciu clock is specified then we'll try to set the ciu
   clock to this at probe time.
 
-* clock-freq-min-max (DEPRECATED): Minimum and Maximum clock frequency for card output
-  clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
-         (Use the "max-frequency" instead of "clock-freq-min-max".)
-
-* num-slots (DEPRECATED): specifies the number of slots supported by the controller.
-  The number of physical slots actually used could be equal or less than the
-  value specified by num-slots. If this property is not specified, the value
-  of num-slot property is assumed to be 1.
-
 * fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
   specified, the default value of the fifo size is determined from the
   controller registers.
index d8685cb..2d5287e 100644 (file)
@@ -50,7 +50,6 @@ Required properties:
          2: R7S72100
 
 Optional properties:
-- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
 - pinctrl-names: should be "default", "state_uhs"
 - pinctrl-0: should contain default/high speed pin ctrl
 - pinctrl-1: should contain uhs mode pin ctrl
index 7cae9c5..10b8249 100644 (file)
                        compatible = "snps,dw-mshc";
                        reg = <0x40004000 0x1000>;
                        interrupts = <6>;
-                       num-slots = <1>;
                        clocks = <&ccu2 CLK_SDIO>, <&ccu1 CLK_CPU_SDIO>;
                        clock-names = "ciu", "biu";
                        resets = <&rgu 20>;
index 040a164..5822fd2 100644 (file)
@@ -20,7 +20,6 @@
 
 &mmc {
        status = "okay";
-       num-slots = <1>;
        cap-sd-highspeed;
        broken-cd;
        bus-width = <4>;
index 8c03729..e59461f 100644 (file)
@@ -30,7 +30,6 @@
                };
 
                mmc0: dwmmc0@ff704000 {
-                       num-slots = <1>;
                        broken-cd;
                        bus-width = <4>;
                        cap-mmc-highspeed;
index a05e3df..68ced67 100644 (file)
@@ -31,7 +31,6 @@
                };
 
                mmc0: dwmmc0@ff704000 {
-                       num-slots = <1>;
                        broken-cd;
                        bus-width = <4>;
                        cap-mmc-highspeed;
index dfe2193..547c386 100644 (file)
@@ -42,7 +42,6 @@
                };
 
                dwmmc0@ff704000 {
-                       num-slots = <1>;
                        broken-cd;
                        bus-width = <4>;
                        cap-mmc-highspeed;
index 0007564..51ce583 100644 (file)
@@ -88,7 +88,6 @@
 
 &mmc {
        status = "okay";
-       num-slots = <1>;
        cap-sd-highspeed;
        broken-cd;
        bus-width = <4>;
index 63d4f9d..a7ecd90 100644 (file)
                        #size-cells = <0>;
                        cd-inverted;
                        compatible = "hisilicon,hi3660-dw-mshc";
-                       num-slots = <1>;
                        bus-width = <0x4>;
                        disable-wp;
                        cap-sd-highspeed;
                        compatible = "hisilicon,hi3660-dw-mshc";
                        reg = <0x0 0xff3ff000 0x0 0x1000>;
                        interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
-                       num-slots = <1>;
                        clocks = <&crg_ctrl HI3660_CLK_GATE_SDIO0>,
                                 <&crg_ctrl HI3660_HCLK_GATE_SDIO0>;
                        clock-names = "ciu", "biu";
index 5deb2d8..6af7777 100644 (file)
@@ -375,8 +375,8 @@ static struct resource kfr2r09_sh_sdhi0_resources[] = {
 static struct tmio_mmc_data sh7724_sdhi0_data = {
        .chan_priv_tx   = (void *)SHDMA_SLAVE_SDHI0_TX,
        .chan_priv_rx   = (void *)SHDMA_SLAVE_SDHI0_RX,
-       .flags          = TMIO_MMC_WRPROTECT_DISABLE,
        .capabilities   = MMC_CAP_SDIO_IRQ,
+       .capabilities2  = MMC_CAP2_NO_WRITE_PROTECT,
 };
 
 static struct platform_device kfr2r09_sh_sdhi0_device = {
index 2cfb963..a2b9c25 100644 (file)
@@ -376,22 +376,15 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
                return idata;
        }
 
-       idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
-       if (!idata->buf) {
-               err = -ENOMEM;
+       idata->buf = memdup_user((void __user *)(unsigned long)
+                                idata->ic.data_ptr, idata->buf_bytes);
+       if (IS_ERR(idata->buf)) {
+               err = PTR_ERR(idata->buf);
                goto idata_err;
        }
 
-       if (copy_from_user(idata->buf, (void __user *)(unsigned long)
-                                       idata->ic.data_ptr, idata->buf_bytes)) {
-               err = -EFAULT;
-               goto copy_err;
-       }
-
        return idata;
 
-copy_err:
-       kfree(idata->buf);
 idata_err:
        kfree(idata);
 out:
index c0ba6d8..121ce50 100644 (file)
@@ -2369,7 +2369,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
                return card->pref_erase;
 
        max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
-       if (mmc_can_trim(card)) {
+       if (max_discard && mmc_can_trim(card)) {
                max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
                if (max_trim < max_discard)
                        max_discard = max_trim;
@@ -2655,8 +2655,7 @@ void mmc_start_host(struct mmc_host *host)
 void mmc_stop_host(struct mmc_host *host)
 {
        if (host->slot.cd_irq >= 0) {
-               if (host->slot.cd_wake_enabled)
-                       disable_irq_wake(host->slot.cd_irq);
+               mmc_gpio_set_cd_wake(host, false);
                disable_irq(host->slot.cd_irq);
        }
 
index 0f4a7d7..c51e0c0 100644 (file)
@@ -196,18 +196,7 @@ static int mmc_ios_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int mmc_ios_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mmc_ios_show, inode->i_private);
-}
-
-static const struct file_operations mmc_ios_fops = {
-       .open           = mmc_ios_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(mmc_ios);
 
 static int mmc_clock_opt_get(void *data, u64 *val)
 {
index 06ec19b..4805438 100644 (file)
@@ -56,7 +56,8 @@ static inline int mmc_host_uhs(struct mmc_host *host)
        return host->caps &
                (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
                 MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
-                MMC_CAP_UHS_DDR50);
+                MMC_CAP_UHS_DDR50) &&
+              host->caps & MMC_CAP_4_BIT_DATA;
 }
 
 static inline bool mmc_card_hs200(struct mmc_card *card)
index 208a762..6f8ebd6 100644 (file)
@@ -792,6 +792,7 @@ MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
 MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
 MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
 MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
 MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
 
 static ssize_t mmc_fwrev_show(struct device *dev,
@@ -848,6 +849,7 @@ static struct attribute *mmc_std_attrs[] = {
        &dev_attr_raw_rpmb_size_mult.attr,
        &dev_attr_rel_sectors.attr,
        &dev_attr_ocr.attr,
+       &dev_attr_rca.attr,
        &dev_attr_dsr.attr,
        &dev_attr_cmdq_en.attr,
        NULL,
index 62b84dd..baf3d5d 100644 (file)
@@ -291,8 +291,6 @@ static int mmc_read_switch(struct mmc_card *card)
                return 0;
        }
 
-       err = -EIO;
-
        status = kmalloc(64, GFP_KERNEL);
        if (!status)
                return -ENOMEM;
@@ -582,9 +580,6 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
        int err;
        u8 *status;
 
-       if (!card->scr.sda_spec3)
-               return 0;
-
        if (!(card->csd.cmdclass & CCC_SWITCH))
                return 0;
 
@@ -593,14 +588,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
                return -ENOMEM;
 
        /* Set 4-bit bus width */
-       if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
-           (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
-               err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
-               if (err)
-                       goto out;
+       err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+       if (err)
+               goto out;
 
-               mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-       }
+       mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
 
        /*
         * Select the bus speed mode depending on host
@@ -676,6 +668,7 @@ MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
 MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
 MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
 MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
 
 
 static ssize_t mmc_dsr_show(struct device *dev,
@@ -709,6 +702,7 @@ static struct attribute *sd_std_attrs[] = {
        &dev_attr_oemid.attr,
        &dev_attr_serial.attr,
        &dev_attr_ocr.attr,
+       &dev_attr_rca.attr,
        &dev_attr_dsr.attr,
        NULL,
 };
@@ -1033,7 +1027,7 @@ retry:
        }
 
        /* Initialization sequence for UHS-I cards */
-       if (rocr & SD_ROCR_S18A) {
+       if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
                err = mmc_sd_init_uhs_card(card);
                if (err)
                        goto free_card;
index cc43687..c599a62 100644 (file)
@@ -518,11 +518,10 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
        if (!card->scr.sda_spec3)
                return 0;
 
-       /*
-        * Switch to wider bus (if supported).
-        */
-       if (card->host->caps & MMC_CAP_4_BIT_DATA)
-               err = sdio_enable_4bit_bus(card);
+       /* Switch to wider bus */
+       err = sdio_enable_4bit_bus(card);
+       if (err)
+               goto out;
 
        /* Set the driver strength for the card */
        sdio_select_driver_type(card);
index 3698b05..31f7dbb 100644 (file)
@@ -149,11 +149,30 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
 
        if (irq < 0)
                host->caps |= MMC_CAP_NEEDS_POLL;
-       else if ((host->caps & MMC_CAP_CD_WAKE) && !enable_irq_wake(irq))
-               host->slot.cd_wake_enabled = true;
 }
 EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
 
+int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
+{
+       int ret = 0;
+
+       if (!(host->caps & MMC_CAP_CD_WAKE) ||
+           host->slot.cd_irq < 0 ||
+           on == host->slot.cd_wake_enabled)
+               return 0;
+
+       if (on) {
+               ret = enable_irq_wake(host->slot.cd_irq);
+               host->slot.cd_wake_enabled = !ret;
+       } else {
+               disable_irq_wake(host->slot.cd_irq);
+               host->slot.cd_wake_enabled = false;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
+
 /* Register an alternate interrupt service routine for
  * the card-detect GPIO.
  */
index 620c2d9..de36fff 100644 (file)
@@ -718,6 +718,15 @@ config MMC_DW_EXYNOS
          Synopsys DesignWare Memory Card Interface driver. Select this option
          for platforms based on Exynos4 and Exynos5 SoC's.
 
+config MMC_DW_HI3798CV200
+       tristate "Hi3798CV200 specific extensions for Synopsys DW Memory Card Interface"
+       depends on MMC_DW
+       select MMC_DW_PLTFM
+       help
+         This selects support for HiSilicon Hi3798CV200 SoC specific extensions to the
+         Synopsys DesignWare Memory Card Interface driver. Select this option
+         for platforms based on HiSilicon Hi3798CV200 SoC.
+
 config MMC_DW_K3
        tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
        depends on MMC_DW
index 84cd138..00ec9a2 100644 (file)
@@ -51,6 +51,7 @@ obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o
 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_HI3798CV200) += dw_mmc-hi3798cv200.o
 obj-$(CONFIG_MMC_DW_K3)                += dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)       += dw_mmc-pci.o
 obj-$(CONFIG_MMC_DW_ROCKCHIP)  += dw_mmc-rockchip.o
diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c
new file mode 100644 (file)
index 0000000..f9b333f
--- /dev/null
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define ALL_INT_CLR            0x1ffff
+
+struct hi3798cv200_priv {
+       struct clk *sample_clk;
+       struct clk *drive_clk;
+};
+
+static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+       struct hi3798cv200_priv *priv = host->priv;
+       u32 val;
+
+       val = mci_readl(host, UHS_REG);
+       if (ios->timing == MMC_TIMING_MMC_DDR52 ||
+           ios->timing == MMC_TIMING_UHS_DDR50)
+               val |= SDMMC_UHS_DDR;
+       else
+               val &= ~SDMMC_UHS_DDR;
+       mci_writel(host, UHS_REG, val);
+
+       val = mci_readl(host, ENABLE_SHIFT);
+       if (ios->timing == MMC_TIMING_MMC_DDR52)
+               val |= SDMMC_ENABLE_PHASE;
+       else
+               val &= ~SDMMC_ENABLE_PHASE;
+       mci_writel(host, ENABLE_SHIFT, val);
+
+       val = mci_readl(host, DDR_REG);
+       if (ios->timing == MMC_TIMING_MMC_HS400)
+               val |= SDMMC_DDR_HS400;
+       else
+               val &= ~SDMMC_DDR_HS400;
+       mci_writel(host, DDR_REG, val);
+
+       if (ios->timing == MMC_TIMING_MMC_HS ||
+           ios->timing == MMC_TIMING_LEGACY)
+               clk_set_phase(priv->drive_clk, 180);
+       else if (ios->timing == MMC_TIMING_MMC_HS200)
+               clk_set_phase(priv->drive_clk, 135);
+}
+
+static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
+                                            u32 opcode)
+{
+       int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
+       struct dw_mci *host = slot->host;
+       struct hi3798cv200_priv *priv = host->priv;
+       int raise_point = -1, fall_point = -1;
+       int err, prev_err = -1;
+       int found = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(degrees); i++) {
+               clk_set_phase(priv->sample_clk, degrees[i]);
+               mci_writel(host, RINTSTS, ALL_INT_CLR);
+
+               err = mmc_send_tuning(slot->mmc, opcode, NULL);
+               if (!err)
+                       found = 1;
+
+               if (i > 0) {
+                       if (err && !prev_err)
+                               fall_point = i - 1;
+                       if (!err && prev_err)
+                               raise_point = i;
+               }
+
+               if (raise_point != -1 && fall_point != -1)
+                       goto tuning_out;
+
+               prev_err = err;
+               err = 0;
+       }
+
+tuning_out:
+       if (found) {
+               if (raise_point == -1)
+                       raise_point = 0;
+               if (fall_point == -1)
+                       fall_point = ARRAY_SIZE(degrees) - 1;
+               if (fall_point < raise_point) {
+                       if ((raise_point + fall_point) >
+                           (ARRAY_SIZE(degrees) - 1))
+                               i = fall_point / 2;
+                       else
+                               i = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
+               } else {
+                       i = (raise_point + fall_point) / 2;
+               }
+
+               clk_set_phase(priv->sample_clk, degrees[i]);
+               dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
+                       raise_point, fall_point, degrees[i]);
+       } else {
+               dev_err(host->dev, "No valid clk_sample shift! use default\n");
+               err = -EINVAL;
+       }
+
+       mci_writel(host, RINTSTS, ALL_INT_CLR);
+       return err;
+}
+
+static int dw_mci_hi3798cv200_init(struct dw_mci *host)
+{
+       struct hi3798cv200_priv *priv;
+       int ret;
+
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
+       if (IS_ERR(priv->sample_clk)) {
+               dev_err(host->dev, "failed to get ciu-sample clock\n");
+               return PTR_ERR(priv->sample_clk);
+       }
+
+       priv->drive_clk = devm_clk_get(host->dev, "ciu-drive");
+       if (IS_ERR(priv->drive_clk)) {
+               dev_err(host->dev, "failed to get ciu-drive clock\n");
+               return PTR_ERR(priv->drive_clk);
+       }
+
+       ret = clk_prepare_enable(priv->sample_clk);
+       if (ret) {
+               dev_err(host->dev, "failed to enable ciu-sample clock\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(priv->drive_clk);
+       if (ret) {
+               dev_err(host->dev, "failed to enable ciu-drive clock\n");
+               goto disable_sample_clk;
+       }
+
+       host->priv = priv;
+       return 0;
+
+disable_sample_clk:
+       clk_disable_unprepare(priv->sample_clk);
+       return ret;
+}
+
+static const struct dw_mci_drv_data hi3798cv200_data = {
+       .init = dw_mci_hi3798cv200_init,
+       .set_ios = dw_mci_hi3798cv200_set_ios,
+       .execute_tuning = dw_mci_hi3798cv200_execute_tuning,
+};
+
+static int dw_mci_hi3798cv200_probe(struct platform_device *pdev)
+{
+       return dw_mci_pltfm_register(pdev, &hi3798cv200_data);
+}
+
+static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
+{
+       struct dw_mci *host = platform_get_drvdata(pdev);
+       struct hi3798cv200_priv *priv = host->priv;
+
+       clk_disable_unprepare(priv->drive_clk);
+       clk_disable_unprepare(priv->sample_clk);
+
+       return dw_mci_pltfm_remove(pdev);
+}
+
+static const struct of_device_id dw_mci_hi3798cv200_match[] = {
+       { .compatible = "hisilicon,hi3798cv200-dw-mshc", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, dw_mci_hi3798cv200_match);
+static struct platform_driver dw_mci_hi3798cv200_driver = {
+       .probe = dw_mci_hi3798cv200_probe,
+       .remove = dw_mci_hi3798cv200_remove,
+       .driver = {
+               .name = "dwmmc_hi3798cv200",
+               .of_match_table = dw_mci_hi3798cv200_match,
+       },
+};
+module_platform_driver(dw_mci_hi3798cv200_driver);
+
+MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc_hi3798cv200");
index ab87132..3ad07d7 100644 (file)
@@ -29,7 +29,6 @@
                                MMC_CAP_SDIO_IRQ)
 
 static struct dw_mci_board pci_board_data = {
-       .num_slots                      = 1,
        .caps                           = DW_MCI_CAPABILITIES,
        .bus_hz                         = 33 * 1000 * 1000,
        .detect_delay_ms                = 200,
index 3392952..40d7de2 100644 (file)
@@ -282,11 +282,11 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
 
        priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
        if (IS_ERR(priv->drv_clk))
-               dev_dbg(host->dev, "ciu_drv not available\n");
+               dev_dbg(host->dev, "ciu-drive not available\n");
 
        priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
        if (IS_ERR(priv->sample_clk))
-               dev_dbg(host->dev, "ciu_sample not available\n");
+               dev_dbg(host->dev, "ciu-sample not available\n");
 
        host->priv = priv;
 
index 06d4741..29a1afa 100644 (file)
@@ -147,19 +147,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
 
        return 0;
 }
-
-static int dw_mci_req_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, dw_mci_req_show, inode->i_private);
-}
-
-static const struct file_operations dw_mci_req_fops = {
-       .owner          = THIS_MODULE,
-       .open           = dw_mci_req_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dw_mci_req);
 
 static int dw_mci_regs_show(struct seq_file *s, void *v)
 {
@@ -178,19 +166,7 @@ static int dw_mci_regs_show(struct seq_file *s, void *v)
 
        return 0;
 }
-
-static int dw_mci_regs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, dw_mci_regs_show, inode->i_private);
-}
-
-static const struct file_operations dw_mci_regs_fops = {
-       .owner          = THIS_MODULE,
-       .open           = dw_mci_regs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(dw_mci_regs);
 
 static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
 {
@@ -2030,7 +2006,6 @@ static void dw_mci_tasklet_func(unsigned long priv)
                        set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
                        err = dw_mci_command_complete(host, cmd);
                        if (cmd == mrq->sbc && !err) {
-                               prev_state = state = STATE_SENDING_CMD;
                                __dw_mci_start_request(host, host->slot,
                                                       mrq->cmd);
                                goto unlock;
@@ -2826,6 +2801,10 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
        if (host->pdata->caps2)
                mmc->caps2 = host->pdata->caps2;
 
+       mmc->f_min = DW_MCI_FREQ_MIN;
+       if (!mmc->f_max)
+               mmc->f_max = DW_MCI_FREQ_MAX;
+
        /* Process SDIO IRQs through the sdio_irq_work. */
        if (mmc->caps & MMC_CAP_SDIO_IRQ)
                mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@@ -2838,7 +2817,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
        struct mmc_host *mmc;
        struct dw_mci_slot *slot;
        int ret;
-       u32 freq[2];
 
        mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
        if (!mmc)
@@ -2852,16 +2830,6 @@ static int dw_mci_init_slot(struct dw_mci *host)
        host->slot = slot;
 
        mmc->ops = &dw_mci_ops;
-       if (device_property_read_u32_array(host->dev, "clock-freq-min-max",
-                                          freq, 2)) {
-               mmc->f_min = DW_MCI_FREQ_MIN;
-               mmc->f_max = DW_MCI_FREQ_MAX;
-       } else {
-               dev_info(host->dev,
-                       "'clock-freq-min-max' property was deprecated.\n");
-               mmc->f_min = freq[0];
-               mmc->f_max = freq[1];
-       }
 
        /*if there are external regulators, get them*/
        ret = mmc_regulator_get_supply(mmc);
@@ -3160,10 +3128,6 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
                        return ERR_PTR(-EPROBE_DEFER);
        }
 
-       /* find out number of slots supported */
-       if (!device_property_read_u32(dev, "num-slots", &pdata->num_slots))
-               dev_info(dev, "'num-slots' was deprecated.\n");
-
        if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
                dev_info(dev,
                         "fifo-depth property not found, using value of FIFOTH register as default\n");
index 1424bd4..46e9f8e 100644 (file)
@@ -65,8 +65,7 @@ struct dw_mci_dma_slave {
  * @fifo_reg: Pointer to MMIO registers for data FIFO
  * @sg: Scatterlist entry currently being processed by PIO code, if any.
  * @sg_miter: PIO mapping scatterlist iterator.
- * @cur_slot: The slot which is currently using the controller.
- * @mrq: The request currently being processed on @cur_slot,
+ * @mrq: The request currently being processed on @slot,
  *     or NULL if the controller is idle.
  * @cmd: The command currently being sent to the card, or NULL.
  * @data: The data currently being transferred, or NULL if no data
@@ -102,7 +101,6 @@ struct dw_mci_dma_slave {
  * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
  *     rate and timeout calculations.
  * @current_speed: Configured rate of the controller.
- * @num_slots: Number of slots available.
  * @fifoth_val: The value of FIFOTH register.
  * @verid: Denote Version ID.
  * @dev: Device associated with the MMC controller.
@@ -134,17 +132,17 @@ struct dw_mci_dma_slave {
  * =======
  *
  * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @slot, @mrq and @state. These must always be updated
  * at the same time while holding @lock.
+ * The @mrq field of struct dw_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
  *
  * @irq_lock is an irq-safe spinlock protecting the INTMASK register
  * to allow the interrupt handler to modify it directly.  Held for only long
  * enough to read-modify-write INTMASK and no other locks are grabbed when
  * holding this one.
  *
- * The @mrq field of struct dw_mci_slot is also protected by @lock,
- * and must always be written at the same time as the slot is added to
- * @queue.
- *
  * @pending_events and @completed_events are accessed using atomic bit
  * operations, so they don't need any locking.
  *
@@ -253,8 +251,6 @@ struct dma_pdata;
 
 /* Board platform data */
 struct dw_mci_board {
-       u32 num_slots;
-
        unsigned int bus_hz; /* Clock speed at the cclk_in pad */
 
        u32 caps;       /* Capabilities */
@@ -318,11 +314,12 @@ struct dw_mci_board {
 #define SDMMC_BUFADDR          0x098
 #define SDMMC_CDTHRCTL         0x100
 #define SDMMC_UHS_REG_EXT      0x108
+#define SDMMC_DDR_REG          0x10c
 #define SDMMC_ENABLE_SHIFT     0x110
 #define SDMMC_DATA(x)          (x)
 /*
-* Registers to support idmac 64-bit address mode
-*/
+ * Registers to support idmac 64-bit address mode
+ */
 #define SDMMC_DBADDRL          0x088
 #define SDMMC_DBADDRU          0x08c
 #define SDMMC_IDSTS64          0x090
@@ -443,13 +440,19 @@ struct dw_mci_board {
 #define SDMMC_CARD_WR_THR_EN           BIT(2)
 #define SDMMC_CARD_RD_THR_EN           BIT(0)
 /* UHS-1 register defines */
+#define SDMMC_UHS_DDR                  BIT(16)
 #define SDMMC_UHS_18V                  BIT(0)
+/* DDR register defines */
+#define SDMMC_DDR_HS400                        BIT(31)
+/* Enable shift register defines */
+#define SDMMC_ENABLE_PHASE             BIT(0)
 /* All ctrl reset bits */
 #define SDMMC_CTRL_ALL_RESET_FLAGS \
        (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
 
 /* FIFO register access macros. These should not change the data endian-ness
- * as they are written to memory to be dealt with by the upper layers */
+ * as they are written to memory to be dealt with by the upper layers
+ */
 #define mci_fifo_readw(__reg)  __raw_readw(__reg)
 #define mci_fifo_readl(__reg)  __raw_readl(__reg)
 #define mci_fifo_readq(__reg)  __raw_readq(__reg)
index 6457a7d..cb274e8 100644 (file)
@@ -438,11 +438,23 @@ static const struct mtk_mmc_compatible mt2712_compat = {
        .enhance_rx = true,
 };
 
+static const struct mtk_mmc_compatible mt7622_compat = {
+       .clk_div_bits = 12,
+       .hs400_tune = false,
+       .pad_tune_reg = MSDC_PAD_TUNE0,
+       .async_fifo = true,
+       .data_tune = true,
+       .busy_check = true,
+       .stop_clk_fix = true,
+       .enhance_rx = true,
+};
+
 static const struct of_device_id msdc_of_ids[] = {
        { .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
        { .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
        { .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
        { .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
+       { .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
        {}
 };
 MODULE_DEVICE_TABLE(of, msdc_of_ids);
index 7c03cfe..8e0acd1 100644 (file)
@@ -71,9 +71,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
 };
 
 static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
-       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
-                         TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
-                         TMIO_MMC_MIN_RCAR2,
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+                         TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_CMD23,
        .bus_shift      = 2,
@@ -145,7 +144,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
        u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE;
        enum dma_data_direction dir;
        int ret;
-       u32 irq_mask;
 
        /* This DMAC cannot handle if sg_len is not 1 */
        WARN_ON(host->sg_len > 1);
@@ -157,11 +155,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
        if (data->flags & MMC_DATA_READ) {
                dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
                dir = DMA_FROM_DEVICE;
-               irq_mask = TMIO_STAT_RXRDY;
        } else {
                dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
                dir = DMA_TO_DEVICE;
-               irq_mask = TMIO_STAT_TXRQ;
        }
 
        ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir);
@@ -170,9 +166,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
 
        renesas_sdhi_internal_dmac_enable_dma(host, true);
 
-       /* disable PIO irqs to avoid "PIO IRQ in DMA mode!" */
-       tmio_mmc_disable_mmc_irqs(host, irq_mask);
-
        /* set dma parameters */
        renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
                                            dtran_mode);
index 82d757c..848e50c 100644 (file)
@@ -40,8 +40,7 @@ static const struct renesas_sdhi_of_data of_rz_compatible = {
 };
 
 static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = {
-       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
-                         TMIO_MMC_CLK_ACTUAL,
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL,
        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
 };
 
@@ -58,9 +57,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
 };
 
 static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
-       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
-                         TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
-                         TMIO_MMC_MIN_RCAR2,
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+                         TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_CMD23,
        .dma_buswidth   = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -79,9 +77,8 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
 };
 
 static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
-       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
-                         TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY |
-                         TMIO_MMC_MIN_RCAR2,
+       .tmio_flags     = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
+                         TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
        .capabilities   = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
                          MMC_CAP_CMD23,
        .bus_shift      = 2,
@@ -205,8 +202,6 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
                return;
        }
 
-       tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_RXRDY);
-
        /* The only sg element can be unaligned, use our bounce buffer then */
        if (!aligned) {
                sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
@@ -280,8 +275,6 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
                return;
        }
 
-       tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_TXRQ);
-
        /* The only sg element can be unaligned, use our bounce buffer then */
        if (!aligned) {
                unsigned long flags;
index 61666d2..0ef741b 100644 (file)
@@ -214,6 +214,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
                  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
                  SDHCI_QUIRK_MISSING_CAPS |
                  SDHCI_QUIRK_NO_HISPD_BIT,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
        .ops = &sdhci_iproc_32only_ops,
 };
 
index 628bfe9..1456abd 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
 
 #include "sdhci-pltfm.h"
 
 #define SDHCI_OMAP_CON         0x12c
 #define CON_DW8                        BIT(5)
 #define CON_DMA_MASTER         BIT(20)
+#define CON_DDR                        BIT(19)
+#define CON_CLKEXTFREE         BIT(16)
+#define CON_PADEN              BIT(15)
 #define CON_INIT               BIT(1)
 #define CON_OD                 BIT(0)
 
+#define SDHCI_OMAP_DLL         0x0134
+#define DLL_SWT                        BIT(20)
+#define DLL_FORCE_SR_C_SHIFT   13
+#define DLL_FORCE_SR_C_MASK    (0x7f << DLL_FORCE_SR_C_SHIFT)
+#define DLL_FORCE_VALUE                BIT(12)
+#define DLL_CALIB              BIT(1)
+
 #define SDHCI_OMAP_CMD         0x20c
 
+#define SDHCI_OMAP_PSTATE      0x0224
+#define PSTATE_DLEV_DAT0       BIT(20)
+#define PSTATE_DATI            BIT(1)
+
 #define SDHCI_OMAP_HCTL                0x228
 #define HCTL_SDBP              BIT(8)
 #define HCTL_SDVS_SHIFT                9
 
 #define SDHCI_OMAP_AC12                0x23c
 #define AC12_V1V8_SIGEN                BIT(19)
+#define AC12_SCLK_SEL          BIT(23)
 
 #define SDHCI_OMAP_CAPA                0x240
 #define CAPA_VS33              BIT(24)
 #define CAPA_VS30              BIT(25)
 #define CAPA_VS18              BIT(26)
 
+#define SDHCI_OMAP_CAPA2       0x0244
+#define CAPA2_TSDR50           BIT(13)
+
 #define SDHCI_OMAP_TIMEOUT     1               /* 1 msec */
 
 #define SYSCTL_CLKD_MAX                0x3FF
 #define IOV_3V0                        3000000         /* 300000 uV */
 #define IOV_3V3                        3300000         /* 330000 uV */
 
+#define MAX_PHASE_DELAY                0x7C
+
+/* sdhci-omap controller flags */
+#define SDHCI_OMAP_REQUIRE_IODELAY     BIT(0)
+
 struct sdhci_omap_data {
        u32 offset;
+       u8 flags;
 };
 
 struct sdhci_omap_host {
@@ -82,8 +107,16 @@ struct sdhci_omap_host {
        struct sdhci_host       *host;
        u8                      bus_mode;
        u8                      power_mode;
+       u8                      timing;
+       u8                      flags;
+
+       struct pinctrl          *pinctrl;
+       struct pinctrl_state    **pinctrl_state;
 };
 
+static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host);
+static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host);
+
 static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
                                   unsigned int offset)
 {
@@ -191,6 +224,178 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
        }
 }
 
+static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host,
+                                     int count)
+{
+       int i;
+       u32 reg;
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+       reg |= DLL_FORCE_VALUE;
+       reg &= ~DLL_FORCE_SR_C_MASK;
+       reg |= (count << DLL_FORCE_SR_C_SHIFT);
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+
+       reg |= DLL_CALIB;
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+       for (i = 0; i < 1000; i++) {
+               reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+               if (reg & DLL_CALIB)
+                       break;
+       }
+       reg &= ~DLL_CALIB;
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+}
+
+static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host)
+{
+       u32 reg;
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+       reg &= ~AC12_SCLK_SEL;
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+       reg &= ~(DLL_FORCE_VALUE | DLL_SWT);
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+}
+
+static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct sdhci_host *host = mmc_priv(mmc);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
+       struct device *dev = omap_host->dev;
+       struct mmc_ios *ios = &mmc->ios;
+       u32 start_window = 0, max_window = 0;
+       u8 cur_match, prev_match = 0;
+       u32 length = 0, max_len = 0;
+       u32 ier = host->ier;
+       u32 phase_delay = 0;
+       int ret = 0;
+       u32 reg;
+
+       pltfm_host = sdhci_priv(host);
+       omap_host = sdhci_pltfm_priv(pltfm_host);
+       dev = omap_host->dev;
+
+       /* clock tuning is not needed for upto 52MHz */
+       if (ios->clock <= 52000000)
+               return 0;
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2);
+       if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
+               return 0;
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
+       reg |= DLL_SWT;
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
+
+       /*
+        * OMAP5/DRA74X/DRA72x Errata i802:
+        * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
+        * during the tuning procedure. So disable it during the
+        * tuning procedure.
+        */
+       ier &= ~SDHCI_INT_DATA_CRC;
+       sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+       sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+
+       while (phase_delay <= MAX_PHASE_DELAY) {
+               sdhci_omap_set_dll(omap_host, phase_delay);
+
+               cur_match = !mmc_send_tuning(mmc, opcode, NULL);
+               if (cur_match) {
+                       if (prev_match) {
+                               length++;
+                       } else {
+                               start_window = phase_delay;
+                               length = 1;
+                       }
+               }
+
+               if (length > max_len) {
+                       max_window = start_window;
+                       max_len = length;
+               }
+
+               prev_match = cur_match;
+               phase_delay += 4;
+       }
+
+       if (!max_len) {
+               dev_err(dev, "Unable to find match\n");
+               ret = -EIO;
+               goto tuning_error;
+       }
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+       if (!(reg & AC12_SCLK_SEL)) {
+               ret = -EIO;
+               goto tuning_error;
+       }
+
+       phase_delay = max_window + 4 * (max_len >> 1);
+       sdhci_omap_set_dll(omap_host, phase_delay);
+
+       goto ret;
+
+tuning_error:
+       dev_err(dev, "Tuning failed\n");
+       sdhci_omap_disable_tuning(omap_host);
+
+ret:
+       sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+       sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+       sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+       return ret;
+}
+
+static int sdhci_omap_card_busy(struct mmc_host *mmc)
+{
+       u32 reg, ac12;
+       int ret = false;
+       struct sdhci_host *host = mmc_priv(mmc);
+       struct sdhci_pltfm_host *pltfm_host;
+       struct sdhci_omap_host *omap_host;
+       u32 ier = host->ier;
+
+       pltfm_host = sdhci_priv(host);
+       omap_host = sdhci_pltfm_priv(pltfm_host);
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+       ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
+       reg &= ~CON_CLKEXTFREE;
+       if (ac12 & AC12_V1V8_SIGEN)
+               reg |= CON_CLKEXTFREE;
+       reg |= CON_PADEN;
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+       disable_irq(host->irq);
+       ier |= SDHCI_INT_CARD_INT;
+       sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+       sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+
+       /*
+        * Delay is required for PSTATE to correctly reflect
+        * DLEV/CLEV values after PADEN is set.
+        */
+       usleep_range(50, 100);
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE);
+       if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0))
+               ret = true;
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+       reg &= ~(CON_CLKEXTFREE | CON_PADEN);
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+       sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+       sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+       enable_irq(host->irq);
+
+       return ret;
+}
+
 static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
                                                  struct mmc_ios *ios)
 {
@@ -244,6 +449,39 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
        return 0;
 }
 
+static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing)
+{
+       int ret;
+       struct pinctrl_state *pinctrl_state;
+       struct device *dev = omap_host->dev;
+
+       if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
+               return;
+
+       if (omap_host->timing == timing)
+               return;
+
+       sdhci_omap_stop_clock(omap_host);
+
+       pinctrl_state = omap_host->pinctrl_state[timing];
+       ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state);
+       if (ret) {
+               dev_err(dev, "failed to select pinctrl state\n");
+               return;
+       }
+
+       sdhci_omap_start_clock(omap_host);
+       omap_host->timing = timing;
+}
+
+static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host,
+                                     u8 power_mode)
+{
+       if (omap_host->bus_mode == MMC_POWER_OFF)
+               sdhci_omap_disable_tuning(omap_host);
+       omap_host->power_mode = power_mode;
+}
+
 static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
                                    unsigned int mode)
 {
@@ -272,7 +510,9 @@ static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        omap_host = sdhci_pltfm_priv(pltfm_host);
 
        sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
+       sdhci_omap_set_timing(omap_host, ios->timing);
        sdhci_set_ios(mmc, ios);
+       sdhci_omap_set_power_mode(omap_host, ios->power_mode);
 }
 
 static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
@@ -401,8 +641,26 @@ static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
        sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
 
        enable_irq(host->irq);
+}
 
-       omap_host->power_mode = power_mode;
+static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
+                                        unsigned int timing)
+{
+       u32 reg;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
+
+       sdhci_omap_stop_clock(omap_host);
+
+       reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
+       if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
+               reg |= CON_DDR;
+       else
+               reg &= ~CON_DDR;
+       sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
+
+       sdhci_set_uhs_signaling(host, timing);
+       sdhci_omap_start_clock(omap_host);
 }
 
 static struct sdhci_ops sdhci_omap_ops = {
@@ -414,7 +672,7 @@ static struct sdhci_ops sdhci_omap_ops = {
        .set_bus_width = sdhci_omap_set_bus_width,
        .platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
        .reset = sdhci_reset,
-       .set_uhs_signaling = sdhci_set_uhs_signaling,
+       .set_uhs_signaling = sdhci_omap_set_uhs_signaling,
 };
 
 static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
@@ -453,14 +711,15 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = {
                  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
                  SDHCI_QUIRK_NO_HISPD_BIT |
                  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
-       .quirks2 = SDHCI_QUIRK2_NO_1_8_V |
-                  SDHCI_QUIRK2_ACMD23_BROKEN |
+       .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
+                  SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
                   SDHCI_QUIRK2_RSP_136_HAS_CRC,
        .ops = &sdhci_omap_ops,
 };
 
 static const struct sdhci_omap_data dra7_data = {
        .offset = 0x200,
+       .flags  = SDHCI_OMAP_REQUIRE_IODELAY,
 };
 
 static const struct of_device_id omap_sdhci_match[] = {
@@ -469,6 +728,108 @@ static const struct of_device_id omap_sdhci_match[] = {
 };
 MODULE_DEVICE_TABLE(of, omap_sdhci_match);
 
+static struct pinctrl_state
+*sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode,
+                                 u32 *caps, u32 capmask)
+{
+       struct device *dev = omap_host->dev;
+       struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
+
+       if (!(*caps & capmask))
+               goto ret;
+
+       pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode);
+       if (IS_ERR(pinctrl_state)) {
+               dev_err(dev, "no pinctrl state for %s mode", mode);
+               *caps &= ~capmask;
+       }
+
+ret:
+       return pinctrl_state;
+}
+
+static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host
+                                                  *omap_host)
+{
+       struct device *dev = omap_host->dev;
+       struct sdhci_host *host = omap_host->host;
+       struct mmc_host *mmc = host->mmc;
+       u32 *caps = &mmc->caps;
+       u32 *caps2 = &mmc->caps2;
+       struct pinctrl_state *state;
+       struct pinctrl_state **pinctrl_state;
+
+       if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY))
+               return 0;
+
+       pinctrl_state = devm_kzalloc(dev, sizeof(*pinctrl_state) *
+                                    (MMC_TIMING_MMC_HS200 + 1), GFP_KERNEL);
+       if (!pinctrl_state)
+               return -ENOMEM;
+
+       omap_host->pinctrl = devm_pinctrl_get(omap_host->dev);
+       if (IS_ERR(omap_host->pinctrl)) {
+               dev_err(dev, "Cannot get pinctrl\n");
+               return PTR_ERR(omap_host->pinctrl);
+       }
+
+       state = pinctrl_lookup_state(omap_host->pinctrl, "default");
+       if (IS_ERR(state)) {
+               dev_err(dev, "no pinctrl state for default mode\n");
+               return PTR_ERR(state);
+       }
+       pinctrl_state[MMC_TIMING_LEGACY] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps,
+                                                MMC_CAP_UHS_SDR104);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_UHS_SDR104] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps,
+                                                MMC_CAP_UHS_DDR50);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_UHS_DDR50] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps,
+                                                MMC_CAP_UHS_SDR50);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_UHS_SDR50] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps,
+                                                MMC_CAP_UHS_SDR25);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_UHS_SDR25] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps,
+                                                MMC_CAP_UHS_SDR12);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_UHS_SDR12] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps,
+                                                MMC_CAP_1_8V_DDR);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_MMC_DDR52] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
+                                                MMC_CAP_SD_HIGHSPEED);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_SD_HS] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps,
+                                                MMC_CAP_MMC_HIGHSPEED);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_MMC_HS] = state;
+
+       state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2,
+                                                MMC_CAP2_HS200_1_8V_SDR);
+       if (!IS_ERR(state))
+               pinctrl_state[MMC_TIMING_MMC_HS200] = state;
+
+       omap_host->pinctrl_state = pinctrl_state;
+
+       return 0;
+}
+
 static int sdhci_omap_probe(struct platform_device *pdev)
 {
        int ret;
@@ -504,6 +865,9 @@ static int sdhci_omap_probe(struct platform_device *pdev)
        omap_host->host = host;
        omap_host->base = host->ioaddr;
        omap_host->dev = dev;
+       omap_host->power_mode = MMC_POWER_UNDEFINED;
+       omap_host->timing = MMC_TIMING_LEGACY;
+       omap_host->flags = data->flags;
        host->ioaddr += offset;
 
        mmc = host->mmc;
@@ -552,10 +916,16 @@ static int sdhci_omap_probe(struct platform_device *pdev)
                goto err_put_sync;
        }
 
+       ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host);
+       if (ret)
+               goto err_put_sync;
+
        host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
        host->mmc_host_ops.start_signal_voltage_switch =
                                        sdhci_omap_start_signal_voltage_switch;
        host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
+       host->mmc_host_ops.card_busy = sdhci_omap_card_busy;
+       host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
 
        sdhci_read_caps(host);
        host->caps |= SDHCI_CAN_DO_ADMA2;
index 82c4f05..787434e 100644 (file)
@@ -41,18 +41,25 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host);
 static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
 {
        mmc_pm_flag_t pm_flags = 0;
+       bool cap_cd_wake = false;
        int i;
 
        for (i = 0; i < chip->num_slots; i++) {
                struct sdhci_pci_slot *slot = chip->slots[i];
 
-               if (slot)
+               if (slot) {
                        pm_flags |= slot->host->mmc->pm_flags;
+                       if (slot->host->mmc->caps & MMC_CAP_CD_WAKE)
+                               cap_cd_wake = true;
+               }
        }
 
-       return device_set_wakeup_enable(&chip->pdev->dev,
-                                       (pm_flags & MMC_PM_KEEP_POWER) &&
-                                       (pm_flags & MMC_PM_WAKE_SDIO_IRQ));
+       if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
+               return device_wakeup_enable(&chip->pdev->dev);
+       else if (!cap_cd_wake)
+               return device_wakeup_disable(&chip->pdev->dev);
+
+       return 0;
 }
 
 static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
@@ -76,6 +83,9 @@ static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
                ret = sdhci_suspend_host(host);
                if (ret)
                        goto err_pci_suspend;
+
+               if (device_may_wakeup(&chip->pdev->dev))
+                       mmc_gpio_set_cd_wake(host->mmc, true);
        }
 
        return 0;
@@ -99,6 +109,8 @@ int sdhci_pci_resume_host(struct sdhci_pci_chip *chip)
                ret = sdhci_resume_host(slot->host);
                if (ret)
                        return ret;
+
+               mmc_gpio_set_cd_wake(slot->host->mmc, false);
        }
 
        return 0;
@@ -712,26 +724,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot)
        return ret;
 }
 
-static void glk_cqe_enable(struct mmc_host *mmc)
-{
-       struct sdhci_host *host = mmc_priv(mmc);
-       u32 reg;
-
-       /*
-        * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
-        * the case after tuning, so ensure the buffer is drained.
-        */
-       reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
-       while (reg & SDHCI_DATA_AVAILABLE) {
-               sdhci_readl(host, SDHCI_BUFFER);
-               reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
-       }
-
-       sdhci_cqe_enable(mmc);
-}
-
 static const struct cqhci_host_ops glk_cqhci_ops = {
-       .enable         = glk_cqe_enable,
+       .enable         = sdhci_cqe_enable,
        .disable        = sdhci_cqe_disable,
        .dumpregs       = sdhci_pci_dumpregs,
 };
@@ -1716,6 +1710,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
        if (device_can_wakeup(&pdev->dev))
                host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
 
+       if (host->mmc->caps & MMC_CAP_CD_WAKE)
+               device_init_wakeup(&pdev->dev, true);
+
        if (slot->cd_idx >= 0) {
                ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
                                           slot->cd_override_level, 0, NULL);
index 2020e57..2ededa7 100644 (file)
@@ -2899,6 +2899,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
 \*****************************************************************************/
 
 #ifdef CONFIG_PM
+
+static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
+{
+       return mmc_card_is_removable(host->mmc) &&
+              !(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
+              !mmc_can_gpio_cd(host->mmc);
+}
+
 /*
  * To enable wakeup events, the corresponding events have to be enabled in
  * the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
@@ -2915,13 +2923,18 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
        u8 wake_val = 0;
        u8 val;
 
-       if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)) {
+       if (sdhci_cd_irq_can_wakeup(host)) {
                wake_val |= SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE;
                irq_val |= SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE;
        }
 
-       wake_val |= SDHCI_WAKE_ON_INT;
-       irq_val |= SDHCI_INT_CARD_INT;
+       if (mmc_card_wake_sdio_irq(host->mmc)) {
+               wake_val |= SDHCI_WAKE_ON_INT;
+               irq_val |= SDHCI_INT_CARD_INT;
+       }
+
+       if (!irq_val)
+               return false;
 
        val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
        val &= ~mask;
index 7bb00c6..4c2a1f8 100644 (file)
@@ -7,13 +7,6 @@
  * 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.
- *
- *
- * TODO
- *  1. DMA
- *  2. Power management
- *  3. Handle MMC errors better
- *
  */
 
 /*
@@ -67,7 +60,6 @@
 #include <linux/module.h>
 
 #define DRIVER_NAME    "sh_mmcif"
-#define DRIVER_VERSION "2010-04-28"
 
 /* CE_CMD_SET */
 #define CMD_MASK               0x3f000000
index bad612d..20cfb20 100644 (file)
@@ -268,6 +268,7 @@ struct sunxi_mmc_cfg {
 };
 
 struct sunxi_mmc_host {
+       struct device *dev;
        struct mmc_host *mmc;
        struct reset_control *reset;
        const struct sunxi_mmc_cfg *cfg;
@@ -1165,6 +1166,80 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
 
+static int sunxi_mmc_enable(struct sunxi_mmc_host *host)
+{
+       int ret;
+
+       if (!IS_ERR(host->reset)) {
+               ret = reset_control_reset(host->reset);
+               if (ret) {
+                       dev_err(host->dev, "Couldn't reset the MMC controller (%d)\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       ret = clk_prepare_enable(host->clk_ahb);
+       if (ret) {
+               dev_err(host->dev, "Couldn't enable the bus clocks (%d)\n", ret);
+               goto error_assert_reset;
+       }
+
+       ret = clk_prepare_enable(host->clk_mmc);
+       if (ret) {
+               dev_err(host->dev, "Enable mmc clk err %d\n", ret);
+               goto error_disable_clk_ahb;
+       }
+
+       ret = clk_prepare_enable(host->clk_output);
+       if (ret) {
+               dev_err(host->dev, "Enable output clk err %d\n", ret);
+               goto error_disable_clk_mmc;
+       }
+
+       ret = clk_prepare_enable(host->clk_sample);
+       if (ret) {
+               dev_err(host->dev, "Enable sample clk err %d\n", ret);
+               goto error_disable_clk_output;
+       }
+
+       /*
+        * Sometimes the controller asserts the irq on boot for some reason,
+        * make sure the controller is in a sane state before enabling irqs.
+        */
+       ret = sunxi_mmc_reset_host(host);
+       if (ret)
+               goto error_disable_clk_sample;
+
+       return 0;
+
+error_disable_clk_sample:
+       clk_disable_unprepare(host->clk_sample);
+error_disable_clk_output:
+       clk_disable_unprepare(host->clk_output);
+error_disable_clk_mmc:
+       clk_disable_unprepare(host->clk_mmc);
+error_disable_clk_ahb:
+       clk_disable_unprepare(host->clk_ahb);
+error_assert_reset:
+       if (!IS_ERR(host->reset))
+               reset_control_assert(host->reset);
+       return ret;
+}
+
+static void sunxi_mmc_disable(struct sunxi_mmc_host *host)
+{
+       sunxi_mmc_reset_host(host);
+
+       clk_disable_unprepare(host->clk_sample);
+       clk_disable_unprepare(host->clk_output);
+       clk_disable_unprepare(host->clk_mmc);
+       clk_disable_unprepare(host->clk_ahb);
+
+       if (!IS_ERR(host->reset))
+               reset_control_assert(host->reset);
+}
+
 static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
                                      struct platform_device *pdev)
 {
@@ -1214,66 +1289,21 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
        if (PTR_ERR(host->reset) == -EPROBE_DEFER)
                return PTR_ERR(host->reset);
 
-       ret = clk_prepare_enable(host->clk_ahb);
-       if (ret) {
-               dev_err(&pdev->dev, "Enable ahb clk err %d\n", ret);
-               return ret;
-       }
-
-       ret = clk_prepare_enable(host->clk_mmc);
-       if (ret) {
-               dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret);
-               goto error_disable_clk_ahb;
-       }
-
-       ret = clk_prepare_enable(host->clk_output);
-       if (ret) {
-               dev_err(&pdev->dev, "Enable output clk err %d\n", ret);
-               goto error_disable_clk_mmc;
-       }
-
-       ret = clk_prepare_enable(host->clk_sample);
-       if (ret) {
-               dev_err(&pdev->dev, "Enable sample clk err %d\n", ret);
-               goto error_disable_clk_output;
-       }
-
-       if (!IS_ERR(host->reset)) {
-               ret = reset_control_reset(host->reset);
-               if (ret) {
-                       dev_err(&pdev->dev, "reset err %d\n", ret);
-                       goto error_disable_clk_sample;
-               }
-       }
-
-       /*
-        * Sometimes the controller asserts the irq on boot for some reason,
-        * make sure the controller is in a sane state before enabling irqs.
-        */
-       ret = sunxi_mmc_reset_host(host);
+       ret = sunxi_mmc_enable(host);
        if (ret)
-               goto error_assert_reset;
+               return ret;
 
        host->irq = platform_get_irq(pdev, 0);
        if (host->irq <= 0) {
                ret = -EINVAL;
-               goto error_assert_reset;
+               goto error_disable_mmc;
        }
 
        return devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,
                        sunxi_mmc_handle_manual_stop, 0, "sunxi-mmc", host);
 
-error_assert_reset:
-       if (!IS_ERR(host->reset))
-               reset_control_assert(host->reset);
-error_disable_clk_sample:
-       clk_disable_unprepare(host->clk_sample);
-error_disable_clk_output:
-       clk_disable_unprepare(host->clk_output);
-error_disable_clk_mmc:
-       clk_disable_unprepare(host->clk_mmc);
-error_disable_clk_ahb:
-       clk_disable_unprepare(host->clk_ahb);
+error_disable_mmc:
+       sunxi_mmc_disable(host);
        return ret;
 }
 
@@ -1288,8 +1318,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "mmc alloc host failed\n");
                return -ENOMEM;
        }
+       platform_set_drvdata(pdev, mmc);
 
        host = mmc_priv(mmc);
+       host->dev = &pdev->dev;
        host->mmc = mmc;
        spin_lock_init(&host->lock);
 
@@ -1353,7 +1385,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
                goto error_free_dma;
 
        dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
-       platform_set_drvdata(pdev, mmc);
        return 0;
 
 error_free_dma:
@@ -1370,16 +1401,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
 
        mmc_remove_host(mmc);
        disable_irq(host->irq);
-       sunxi_mmc_reset_host(host);
-
-       if (!IS_ERR(host->reset))
-               reset_control_assert(host->reset);
-
-       clk_disable_unprepare(host->clk_sample);
-       clk_disable_unprepare(host->clk_output);
-       clk_disable_unprepare(host->clk_mmc);
-       clk_disable_unprepare(host->clk_ahb);
-
+       sunxi_mmc_disable(host);
        dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
        mmc_free_host(mmc);
 
index 3349424..e30df9a 100644 (file)
@@ -278,7 +278,6 @@ static void tmio_mmc_reset_work(struct work_struct *work)
 
        host->cmd = NULL;
        host->data = NULL;
-       host->force_pio = false;
 
        spin_unlock_irqrestore(&host->lock, flags);
 
@@ -350,8 +349,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
                        c |= TRANSFER_READ;
        }
 
-       if (!host->native_hotplug)
-               irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
        tmio_mmc_enable_mmc_irqs(host, irq_mask);
 
        /* Fire off the command */
@@ -623,15 +620,21 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat)
         */
        if (host->data && (!cmd->error || cmd->error == -EILSEQ)) {
                if (host->data->flags & MMC_DATA_READ) {
-                       if (host->force_pio || !host->chan_rx)
+                       if (host->force_pio || !host->chan_rx) {
                                tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_READOP);
-                       else
+                       } else {
+                               tmio_mmc_disable_mmc_irqs(host,
+                                                         TMIO_MASK_READOP);
                                tasklet_schedule(&host->dma_issue);
+                       }
                } else {
-                       if (host->force_pio || !host->chan_tx)
+                       if (host->force_pio || !host->chan_tx) {
                                tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
-                       else
+                       } else {
+                               tmio_mmc_disable_mmc_irqs(host,
+                                                         TMIO_MASK_WRITEOP);
                                tasklet_schedule(&host->dma_issue);
+                       }
                }
        } else {
                schedule_work(&host->done);
@@ -755,6 +758,7 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
 
        tmio_mmc_init_sg(host, data);
        host->data = data;
+       host->force_pio = false;
 
        /* Set transfer length / blocksize */
        sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
@@ -846,7 +850,6 @@ static void tmio_process_mrq(struct tmio_mmc_host *host,
        return;
 
 fail:
-       host->force_pio = false;
        host->mrq = NULL;
        mrq->cmd->error = ret;
        mmc_request_done(host->mmc, mrq);
@@ -896,7 +899,6 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
        if (host->cmd != mrq->sbc) {
                host->cmd = NULL;
                host->data = NULL;
-               host->force_pio = false;
                host->mrq = NULL;
        }
 
@@ -1061,10 +1063,17 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 static int tmio_mmc_get_ro(struct mmc_host *mmc)
 {
        struct tmio_mmc_host *host = mmc_priv(mmc);
-       struct tmio_mmc_data *pdata = host->pdata;
 
-       return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
-                (sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
+       return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
+                TMIO_STAT_WRPROTECT);
+}
+
+static int tmio_mmc_get_cd(struct mmc_host *mmc)
+{
+       struct tmio_mmc_host *host = mmc_priv(mmc);
+
+       return !!(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) &
+                 TMIO_STAT_SIGSTATE);
 }
 
 static int tmio_multi_io_quirk(struct mmc_card *card,
@@ -1082,7 +1091,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
        .request        = tmio_mmc_request,
        .set_ios        = tmio_mmc_set_ios,
        .get_ro         = tmio_mmc_get_ro,
-       .get_cd         = mmc_gpio_get_cd,
+       .get_cd         = tmio_mmc_get_cd,
        .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
        .multi_io_quirk = tmio_multi_io_quirk,
        .hw_reset       = tmio_mmc_hw_reset,
@@ -1114,15 +1123,20 @@ static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
 }
 
 static void tmio_mmc_of_parse(struct platform_device *pdev,
-                             struct tmio_mmc_data *pdata)
+                             struct mmc_host *mmc)
 {
        const struct device_node *np = pdev->dev.of_node;
 
        if (!np)
                return;
 
+       /*
+        * DEPRECATED:
+        * For new platforms, please use "disable-wp" instead of
+        * "toshiba,mmc-wrprotect-disable"
+        */
        if (of_get_property(np, "toshiba,mmc-wrprotect-disable", NULL))
-               pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
+               mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
 }
 
 struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
@@ -1157,7 +1171,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
                goto free;
        }
 
-       tmio_mmc_of_parse(pdev, pdata);
+       tmio_mmc_of_parse(pdev, mmc);
 
        platform_set_drvdata(pdev, host);
 
@@ -1181,7 +1195,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
        struct tmio_mmc_data *pdata = _host->pdata;
        struct mmc_host *mmc = _host->mmc;
        int ret;
-       u32 irq_mask = TMIO_MASK_CMD;
 
        /*
         * Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
@@ -1230,6 +1243,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
        if (mmc_can_gpio_ro(mmc))
                _host->ops.get_ro = mmc_gpio_get_ro;
 
+       if (mmc_can_gpio_cd(mmc))
+               _host->ops.get_cd = mmc_gpio_get_cd;
+
        _host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
                                  mmc->caps & MMC_CAP_NEEDS_POLL ||
                                  !mmc_card_is_removable(mmc));
@@ -1260,15 +1276,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
        _host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
        tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
 
-       /* Unmask the IRQs we want to know about */
-       if (!_host->chan_rx)
-               irq_mask |= TMIO_MASK_READOP;
-       if (!_host->chan_tx)
-               irq_mask |= TMIO_MASK_WRITEOP;
-       if (!_host->native_hotplug)
-               irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
-
-       _host->sdcard_irq_mask &= ~irq_mask;
+       if (_host->native_hotplug)
+               tmio_mmc_enable_mmc_irqs(_host,
+                               TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
 
        spin_lock_init(&_host->lock);
        mutex_init(&_host->ios_lock);
@@ -1367,6 +1377,10 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
        if (host->clk_cache)
                tmio_mmc_set_clock(host, host->clk_cache);
 
+       if (host->native_hotplug)
+               tmio_mmc_enable_mmc_irqs(host,
+                               TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
+
        tmio_mmc_enable_dma(host, true);
 
        if (tmio_mmc_can_retune(host) && host->select_tuning(host))
index 1d84335..81dac17 100644 (file)
@@ -309,8 +309,6 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req)
 
        /* Submit CSW. */
        ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC);
-       if (ret < 0)
-               goto out;
 
 out:
        spin_unlock_irqrestore(&ushc->lock, flags);
index 396a103..91f9221 100644 (file)
@@ -36,7 +36,6 @@
        } while (0)
 
 /* tmio MMC platform flags */
-#define TMIO_MMC_WRPROTECT_DISABLE     BIT(0)
 /*
  * Some controllers can support a 2-byte block size when the bus width
  * is configured in 4-bit mode.
index 91f1ba0..06607c5 100644 (file)
@@ -31,6 +31,7 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
                         unsigned int debounce, bool *gpio_invert);
 void mmc_gpio_set_cd_isr(struct mmc_host *host,
                         irqreturn_t (*isr)(int irq, void *dev_id));
+int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on);
 void mmc_gpiod_request_cd_irq(struct mmc_host *host);
 bool mmc_can_gpio_cd(struct mmc_host *host);
 bool mmc_can_gpio_ro(struct mmc_host *host);