summaryrefslogtreecommitdiff
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Kconfig94
-rw-r--r--drivers/mmc/host/Makefile9
-rw-r--r--drivers/mmc/host/android-goldfish.c570
-rw-r--r--drivers/mmc/host/at91_mci.c1219
-rw-r--r--drivers/mmc/host/at91_mci.h115
-rw-r--r--drivers/mmc/host/atmel-mci.c4
-rw-r--r--drivers/mmc/host/au1xmmc.c4
-rw-r--r--drivers/mmc/host/bfin_sdh.c6
-rw-r--r--drivers/mmc/host/cb710-mmc.c6
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c18
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c4
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c33
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.h4
-rw-r--r--drivers/mmc/host/dw_mmc.c211
-rw-r--r--drivers/mmc/host/jz4740_mmc.c12
-rw-r--r--drivers/mmc/host/mmc_spi.c6
-rw-r--r--drivers/mmc/host/mmci.c380
-rw-r--r--drivers/mmc/host/mmci.h7
-rw-r--r--drivers/mmc/host/mvsdio.c189
-rw-r--r--drivers/mmc/host/mxcmmc.c35
-rw-r--r--drivers/mmc/host/mxs-mmc.c40
-rw-r--r--drivers/mmc/host/of_mmc_spi.c2
-rw-r--r--drivers/mmc/host/omap.c43
-rw-r--r--drivers/mmc/host/omap_hsmmc.c206
-rw-r--r--drivers/mmc/host/pxamci.c4
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c1341
-rw-r--r--drivers/mmc/host/s3cmci.c6
-rw-r--r--drivers/mmc/host/sdhci-acpi.c312
-rw-r--r--drivers/mmc/host/sdhci-bcm2835.c210
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c6
-rw-r--r--drivers/mmc/host/sdhci-dove.c124
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c280
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c71
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c6
-rw-r--r--drivers/mmc/host/sdhci-pci.c21
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c24
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h2
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c17
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c120
-rw-r--r--drivers/mmc/host/sdhci-s3c.c120
-rw-r--r--drivers/mmc/host/sdhci-spear.c21
-rw-r--r--drivers/mmc/host/sdhci-tegra.c131
-rw-r--r--drivers/mmc/host/sdhci.c559
-rw-r--r--drivers/mmc/host/sdhci.h20
-rw-r--r--drivers/mmc/host/sh_mmcif.c301
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c106
-rw-r--r--drivers/mmc/host/tmio_mmc.c6
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c89
-rw-r--r--drivers/mmc/host/via-sdmmc.c6
-rw-r--r--drivers/mmc/host/vub300.c1
-rw-r--r--drivers/mmc/host/wbsd.c28
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c1029
52 files changed, 5453 insertions, 2725 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9bf10e7bbfaf..d88219e1d86e 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -69,7 +69,7 @@ config MMC_SDHCI_PCI
If unsure, say N.
config MMC_RICOH_MMC
- bool "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
+ bool "Ricoh MMC Controller Disabler"
depends on MMC_SDHCI_PCI
help
This adds a pci quirk to disable Ricoh MMC Controller. This
@@ -81,6 +81,18 @@ config MMC_RICOH_MMC
If unsure, say Y.
+config MMC_SDHCI_ACPI
+ tristate "SDHCI support for ACPI enumerated SDHCI controllers"
+ depends on MMC_SDHCI && ACPI
+ help
+ This selects support for ACPI enumerated SDHCI controllers,
+ identified by ACPI Compatibility ID PNP0D40 or specific
+ ACPI Hardware IDs.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDHCI_PLTFM
tristate "SDHCI platform and OF driver helper"
depends on MMC_SDHCI
@@ -174,9 +186,6 @@ config MMC_SDHCI_S3C
often referrered to as the HSMMC block in some of the Samsung S3C
range of SoC.
- Note, due to the problems with DMA, the DMA support is only
- available with CONFIG_EXPERIMENTAL is selected.
-
If you have a controller with this interface, say Y or M here.
If unsure, say N.
@@ -221,7 +230,7 @@ config MMC_SDHCI_SPEAR
config MMC_SDHCI_S3C_DMA
bool "DMA support on S3C SDHCI"
- depends on MMC_SDHCI_S3C && EXPERIMENTAL
+ depends on MMC_SDHCI_S3C
help
Enable DMA support on the Samsung S3C SDHCI glue. The DMA
has proved to be problematic if the controller encounters
@@ -229,6 +238,17 @@ config MMC_SDHCI_S3C_DMA
YMMV.
+config MMC_SDHCI_BCM2835
+ tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
+ depends on ARCH_BCM2835
+ depends on MMC_SDHCI_PLTFM
+ select MMC_SDHCI_IO_ACCESSORS
+ help
+ This selects the BCM2835 SD/MMC controller. If you have a BCM2835
+ platform with SD or MMC devices, say Y or M here.
+
+ If unsure, say N.
+
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP
@@ -270,26 +290,8 @@ config MMC_AU1X
If unsure, say N.
-choice
- prompt "Atmel SD/MMC Driver"
- depends on AVR32 || ARCH_AT91
- default MMC_ATMELMCI if AVR32
- help
- Choose which driver to use for the Atmel MCI Silicon
-
-config MMC_AT91
- tristate "AT91 SD/MMC Card Interface support (DEPRECATED)"
- depends on ARCH_AT91
- help
- This selects the AT91 MCI controller. This driver will
- be removed soon (for more information have a look to
- Documentation/feature-removal-schedule.txt). Please use
- MMC_ATMEL_MCI.
-
- If unsure, say N.
-
config MMC_ATMELMCI
- tristate "Atmel Multimedia Card Interface support"
+ tristate "Atmel SD/MMC Driver (Multimedia Card Interface)"
depends on AVR32 || ARCH_AT91
help
This selects the Atmel Multimedia Card Interface driver. If
@@ -298,8 +300,6 @@ config MMC_ATMELMCI
If unsure, say N.
-endchoice
-
config MMC_ATMELMCI_DMA
bool "Atmel MCI DMA support"
depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE
@@ -338,8 +338,8 @@ config MMC_MXS
If unsure, say N.
config MMC_TIFM_SD
- tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PCI
+ tristate "TI Flash Media MMC/SD Interface support"
+ depends on PCI
select TIFM_CORE
help
Say Y here if you want to be able to access MMC/SD cards with
@@ -372,6 +372,13 @@ config MMC_DAVINCI
If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N.
+config MMC_GOLDFISH
+ tristate "goldfish qemu Multimedia Card Interface support"
+ depends on GOLDFISH
+ help
+ This selects the Goldfish Multimedia card Interface emulation
+ found on the Goldfish Android virtual device emulation.
+
config MMC_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
@@ -418,8 +425,7 @@ config MMC_S3C_PIO
the S3C MCI driver.
config MMC_S3C_DMA
- bool "Use DMA transfers only (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ bool "Use DMA transfers only"
help
Use DMA to transfer data between memory and the hardare.
@@ -428,7 +434,7 @@ config MMC_S3C_DMA
option is useful.
config MMC_S3C_PIODMA
- bool "Support for both PIO and DMA (EXPERIMENTAL)"
+ bool "Support for both PIO and DMA"
help
Compile both the PIO and DMA transfer routines into the
driver and let the platform select at run-time which one
@@ -439,8 +445,8 @@ config MMC_S3C_PIODMA
endchoice
config MMC_SDRICOH_CS
- tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PCI && PCMCIA
+ tristate "MMC/SD driver for Ricoh Bay1Controllers"
+ depends on PCI && PCMCIA
help
Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA
card whenever you insert a MMC or SD card into the card slot.
@@ -469,7 +475,7 @@ config MMC_SDHI
config MMC_CB710
tristate "ENE CB710 MMC/SD Interface support"
- depends on PCI
+ depends on PCI && GENERIC_HARDIRQS
select CB710_CORE
help
This option enables support for MMC/SD part of ENE CB710/720 Flash
@@ -541,7 +547,7 @@ config MMC_DW_PLTFM
If unsure, say Y.
config MMC_DW_EXYNOS
- tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
+ tristate "Exynos specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
select MMC_DW_PLTFM
help
@@ -621,3 +627,21 @@ config MMC_USHC
Note: These controllers only support SDIO cards and do not
support MMC or SD memory cards.
+
+config MMC_WMT
+ tristate "Wondermedia SD/MMC Host Controller support"
+ depends on ARCH_VT8500
+ default y
+ help
+ This selects support for the SD/MMC Host Controller on
+ Wondermedia WM8505/WM8650 based SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wmt-sdmmc.
+
+config MMC_REALTEK_PCI
+ tristate "Realtek PCI-E SD/MMC Card Interface Driver"
+ depends on MFD_RTSX_PCI
+ help
+ Say Y here to include driver code to support SD/MMC card interface
+ of Realtek PCI-E card reader
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 17ad0a7ba40b..c380e3cf0a3b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -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-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
@@ -17,12 +18,12 @@ obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o
-obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MSM) += msm_sdcc.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
+obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
@@ -45,6 +46,11 @@ obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
+obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
+
+obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
+
+obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
@@ -53,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.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_BCM2835) += sdhci-bcm2835.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c
new file mode 100644
index 000000000000..ef3aef0f376d
--- /dev/null
+++ b/drivers/mmc/host/android-goldfish.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2007, Google Inc.
+ * Copyright 2012, Intel Inc.
+ *
+ * based on omap.c driver, which was
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
+ * Other hacks (DMA, SD, etc) by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/major.h>
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define DRIVER_NAME "goldfish_mmc"
+
+#define BUFFER_SIZE 16384
+
+#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr))
+#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr))
+
+enum {
+ /* status register */
+ MMC_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ MMC_INT_ENABLE = 0x04,
+ /* set this to specify buffer address */
+ MMC_SET_BUFFER = 0x08,
+
+ /* MMC command number */
+ MMC_CMD = 0x0C,
+
+ /* MMC argument */
+ MMC_ARG = 0x10,
+
+ /* MMC response (or R2 bits 0 - 31) */
+ MMC_RESP_0 = 0x14,
+
+ /* MMC R2 response bits 32 - 63 */
+ MMC_RESP_1 = 0x18,
+
+ /* MMC R2 response bits 64 - 95 */
+ MMC_RESP_2 = 0x1C,
+
+ /* MMC R2 response bits 96 - 127 */
+ MMC_RESP_3 = 0x20,
+
+ MMC_BLOCK_LENGTH = 0x24,
+ MMC_BLOCK_COUNT = 0x28,
+
+ /* MMC state flags */
+ MMC_STATE = 0x2C,
+
+ /* MMC_INT_STATUS bits */
+
+ MMC_STAT_END_OF_CMD = 1U << 0,
+ MMC_STAT_END_OF_DATA = 1U << 1,
+ MMC_STAT_STATE_CHANGE = 1U << 2,
+ MMC_STAT_CMD_TIMEOUT = 1U << 3,
+
+ /* MMC_STATE bits */
+ MMC_STATE_INSERTED = 1U << 0,
+ MMC_STATE_READ_ONLY = 1U << 1,
+};
+
+/*
+ * Command types
+ */
+#define OMAP_MMC_CMDTYPE_BC 0
+#define OMAP_MMC_CMDTYPE_BCR 1
+#define OMAP_MMC_CMDTYPE_AC 2
+#define OMAP_MMC_CMDTYPE_ADTC 3
+
+
+struct goldfish_mmc_host {
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_host *mmc;
+ struct device *dev;
+ unsigned char id; /* 16xx chips have 2 MMC blocks */
+ void __iomem *virt_base;
+ unsigned int phys_base;
+ int irq;
+ unsigned char bus_mode;
+ unsigned char hw_bus_mode;
+
+ unsigned int sg_len;
+ unsigned dma_done:1;
+ unsigned dma_in_use:1;
+
+ void __iomem *reg_base;
+};
+
+static inline int
+goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host)
+{
+ return 0;
+}
+
+static ssize_t
+goldfish_mmc_show_cover_switch(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct goldfish_mmc_host *host = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" :
+ "closed");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL);
+
+static void
+goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)
+{
+ u32 cmdreg;
+ u32 resptype;
+ u32 cmdtype;
+
+ host->cmd = cmd;
+
+ resptype = 0;
+ cmdtype = 0;
+
+ /* Our hardware needs to know exact type */
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ /* resp 1, 1b, 6, 7 */
+ resptype = 1;
+ break;
+ case MMC_RSP_R2:
+ resptype = 2;
+ break;
+ case MMC_RSP_R3:
+ resptype = 3;
+ break;
+ default:
+ dev_err(mmc_dev(host->mmc),
+ "Invalid response type: %04x\n", mmc_resp_type(cmd));
+ break;
+ }
+
+ if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
+ cmdtype = OMAP_MMC_CMDTYPE_ADTC;
+ else if (mmc_cmd_type(cmd) == MMC_CMD_BC)
+ cmdtype = OMAP_MMC_CMDTYPE_BC;
+ else if (mmc_cmd_type(cmd) == MMC_CMD_BCR)
+ cmdtype = OMAP_MMC_CMDTYPE_BCR;
+ else
+ cmdtype = OMAP_MMC_CMDTYPE_AC;
+
+ cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
+
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ cmdreg |= 1 << 6;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdreg |= 1 << 11;
+
+ if (host->data && !(host->data->flags & MMC_DATA_WRITE))
+ cmdreg |= 1 << 15;
+
+ GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg);
+ GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);
+}
+
+static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
+ struct mmc_data *data)
+{
+ if (host->dma_in_use) {
+ enum dma_data_direction dma_data_dir;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ if (dma_data_dir == DMA_FROM_DEVICE) {
+ /*
+ * We don't really have DMA, so we need
+ * to copy from our platform driver buffer
+ */
+ uint8_t *dest = (uint8_t *)sg_virt(data->sg);
+ memcpy(dest, host->virt_base, data->sg->length);
+ }
+ host->data->bytes_xfered += data->sg->length;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+ dma_data_dir);
+ }
+
+ host->data = NULL;
+ host->sg_len = 0;
+
+ /*
+ * NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
+ * dozens of requests until the card finishes writing data.
+ * It'd be cheaper to just wait till an EOFB interrupt arrives...
+ */
+
+ if (!data->stop) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, data->mrq);
+ return;
+ }
+
+ goldfish_mmc_start_command(host, data->stop);
+}
+
+static void goldfish_mmc_end_of_data(struct goldfish_mmc_host *host,
+ struct mmc_data *data)
+{
+ if (!host->dma_in_use) {
+ goldfish_mmc_xfer_done(host, data);
+ return;
+ }
+ if (host->dma_done)
+ goldfish_mmc_xfer_done(host, data);
+}
+
+static void goldfish_mmc_cmd_done(struct goldfish_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ host->cmd = NULL;
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->resp[3] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_0);
+ cmd->resp[2] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_1);
+ cmd->resp[1] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_2);
+ cmd->resp[0] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_3);
+ } else {
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->resp[0] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_0);
+ }
+ }
+
+ if (host->data == NULL || cmd->error) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, cmd->mrq);
+ }
+}
+
+static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
+{
+ struct goldfish_mmc_host *host = (struct goldfish_mmc_host *)dev_id;
+ u16 status;
+ int end_command = 0;
+ int end_transfer = 0;
+ int transfer_error = 0;
+ int state_changed = 0;
+ int cmd_timeout = 0;
+
+ while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
+
+ if (status & MMC_STAT_END_OF_CMD)
+ end_command = 1;
+
+ if (status & MMC_STAT_END_OF_DATA)
+ end_transfer = 1;
+
+ if (status & MMC_STAT_STATE_CHANGE)
+ state_changed = 1;
+
+ if (status & MMC_STAT_CMD_TIMEOUT) {
+ end_command = 0;
+ cmd_timeout = 1;
+ }
+ }
+
+ if (cmd_timeout) {
+ struct mmc_request *mrq = host->mrq;
+ mrq->cmd->error = -ETIMEDOUT;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+ }
+
+ if (end_command)
+ goldfish_mmc_cmd_done(host, host->cmd);
+
+ if (transfer_error)
+ goldfish_mmc_xfer_done(host, host->data);
+ else if (end_transfer) {
+ host->dma_done = 1;
+ goldfish_mmc_end_of_data(host, host->data);
+ } else if (host->data != NULL) {
+ /*
+ * WORKAROUND -- after porting this driver from 2.6 to 3.4,
+ * during device initialization, cases where host->data is
+ * non-null but end_transfer is false would occur. Doing
+ * nothing in such cases results in no further interrupts,
+ * and initialization failure.
+ * TODO -- find the real cause.
+ */
+ host->dma_done = 1;
+ goldfish_mmc_end_of_data(host, host->data);
+ }
+
+ if (state_changed) {
+ u32 state = GOLDFISH_MMC_READ(host, MMC_STATE);
+ pr_info("%s: Card detect now %d\n", __func__,
+ (state & MMC_STATE_INSERTED));
+ mmc_detect_change(host->mmc, 0);
+ }
+
+ if (!end_command && !end_transfer &&
+ !transfer_error && !state_changed && !cmd_timeout) {
+ status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
+ dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
+ if (status != 0) {
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
+ struct mmc_request *req)
+{
+ struct mmc_data *data = req->data;
+ int block_size;
+ unsigned sg_len;
+ enum dma_data_direction dma_data_dir;
+
+ host->data = data;
+ if (data == NULL) {
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
+ host->dma_in_use = 0;
+ return;
+ }
+
+ block_size = data->blksz;
+
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
+
+ /*
+ * Cope with calling layer confusion; it issues "single
+ * block" writes using multi-block scatterlists.
+ */
+ sg_len = (data->blocks == 1) ? 1 : data->sg_len;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ sg_len, dma_data_dir);
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+
+ if (dma_data_dir == DMA_TO_DEVICE) {
+ /*
+ * We don't really have DMA, so we need to copy to our
+ * platform driver buffer
+ */
+ const uint8_t *src = (uint8_t *)sg_virt(data->sg);
+ memcpy(host->virt_base, src, data->sg->length);
+ }
+}
+
+static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = req;
+ goldfish_mmc_prepare_data(host, req);
+ goldfish_mmc_start_command(host, req->cmd);
+
+ /*
+ * This is to avoid accidentally being detected as an SDIO card
+ * in mmc_attach_sdio().
+ */
+ if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
+ req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR))
+ req->cmd->error = -EINVAL;
+}
+
+static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ host->bus_mode = ios->bus_mode;
+ host->hw_bus_mode = host->bus_mode;
+}
+
+static int goldfish_mmc_get_ro(struct mmc_host *mmc)
+{
+ uint32_t state;
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ state = GOLDFISH_MMC_READ(host, MMC_STATE);
+ return ((state & MMC_STATE_READ_ONLY) != 0);
+}
+
+static const struct mmc_host_ops goldfish_mmc_ops = {
+ .request = goldfish_mmc_request,
+ .set_ios = goldfish_mmc_set_ios,
+ .get_ro = goldfish_mmc_get_ro,
+};
+
+static int goldfish_mmc_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct goldfish_mmc_host *host = NULL;
+ struct resource *res;
+ int ret = 0;
+ int irq;
+ dma_addr_t buf_addr;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (res == NULL || irq < 0)
+ return -ENXIO;
+
+ mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
+ if (mmc == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_host_failed;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end);
+ host->reg_base = ioremap(res->start, res->end - res->start + 1);
+ if (host->reg_base == NULL) {
+ ret = -ENOMEM;
+ goto ioremap_failed;
+ }
+ host->virt_base = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
+ &buf_addr, GFP_KERNEL);
+
+ if (host->virt_base == 0) {
+ ret = -ENOMEM;
+ goto dma_alloc_failed;
+ }
+ host->phys_base = buf_addr;
+
+ host->id = pdev->id;
+ host->irq = irq;
+
+ mmc->ops = &goldfish_mmc_ops;
+ mmc->f_min = 400000;
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ /* Use scatterlist DMA to reduce per-transfer costs.
+ * NOTE max_seg_size assumption that small blocks aren't
+ * normally used (except e.g. for reading SD registers).
+ */
+ mmc->max_segs = 32;
+ mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
+ mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
+ mmc->max_req_size = BUFFER_SIZE;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed IRQ Adding goldfish MMC\n");
+ goto err_request_irq_failed;
+ }
+
+ host->dev = &pdev->dev;
+ platform_set_drvdata(pdev, host);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
+ if (ret)
+ dev_warn(mmc_dev(host->mmc),
+ "Unable to create sysfs attributes\n");
+
+ GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
+ MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA |
+ MMC_STAT_STATE_CHANGE | MMC_STAT_CMD_TIMEOUT);
+
+ mmc_add_host(mmc);
+ return 0;
+
+err_request_irq_failed:
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base,
+ host->phys_base);
+dma_alloc_failed:
+ iounmap(host->reg_base);
+ioremap_failed:
+ mmc_free_host(host->mmc);
+err_alloc_host_failed:
+ return ret;
+}
+
+static int goldfish_mmc_remove(struct platform_device *pdev)
+{
+ struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ BUG_ON(host == NULL);
+
+ mmc_remove_host(host->mmc);
+ free_irq(host->irq, host);
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
+ iounmap(host->reg_base);
+ mmc_free_host(host->mmc);
+ return 0;
+}
+
+static struct platform_driver goldfish_mmc_driver = {
+ .probe = goldfish_mmc_probe,
+ .remove = goldfish_mmc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+module_platform_driver(goldfish_mmc_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
deleted file mode 100644
index 74bed0fc23e7..000000000000
--- a/drivers/mmc/host/at91_mci.c
+++ /dev/null
@@ -1,1219 +0,0 @@
-/*
- * linux/drivers/mmc/host/at91_mci.c - ATMEL AT91 MCI Driver
- *
- * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved
- *
- * Copyright (C) 2006 Malcolm Noyes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-/*
- This is the AT91 MCI driver that has been tested with both MMC cards
- and SD-cards. Boards that support write protect are now supported.
- The CCAT91SBC001 board does not support SD cards.
-
- The three entry points are at91_mci_request, at91_mci_set_ios
- and at91_mci_get_ro.
-
- SET IOS
- This configures the device to put it into the correct mode and clock speed
- required.
-
- MCI REQUEST
- MCI request processes the commands sent in the mmc_request structure. This
- can consist of a processing command and a stop command in the case of
- multiple block transfers.
-
- There are three main types of request, commands, reads and writes.
-
- Commands are straight forward. The command is submitted to the controller and
- the request function returns. When the controller generates an interrupt to indicate
- the command is finished, the response to the command are read and the mmc_request_done
- function called to end the request.
-
- Reads and writes work in a similar manner to normal commands but involve the PDC (DMA)
- controller to manage the transfers.
-
- A read is done from the controller directly to the scatterlist passed in from the request.
- Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte
- swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug.
-
- The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY
-
- A write is slightly different in that the bytes to write are read from the scatterlist
- into a dma memory buffer (this is in case the source buffer should be read only). The
- entire write buffer is then done from this single dma memory buffer.
-
- The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY
-
- GET RO
- Gets the status of the write protect pin, if available.
-*/
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/dma-mapping.h>
-#include <linux/clk.h>
-#include <linux/atmel_pdc.h>
-#include <linux/gfp.h>
-#include <linux/highmem.h>
-
-#include <linux/mmc/host.h>
-#include <linux/mmc/sdio.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/gpio.h>
-
-#include <mach/board.h>
-#include <mach/cpu.h>
-
-#include "at91_mci.h"
-
-#define DRIVER_NAME "at91_mci"
-
-static inline int at91mci_is_mci1rev2xx(void)
-{
- return ( cpu_is_at91sam9260()
- || cpu_is_at91sam9263()
- || cpu_is_at91sam9rl()
- || cpu_is_at91sam9g10()
- || cpu_is_at91sam9g20()
- );
-}
-
-#define FL_SENT_COMMAND (1 << 0)
-#define FL_SENT_STOP (1 << 1)
-
-#define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \
- | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \
- | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)
-
-#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg))
-#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg))
-
-#define MCI_BLKSIZE 512
-#define MCI_MAXBLKSIZE 4095
-#define MCI_BLKATONCE 256
-#define MCI_BUFSIZE (MCI_BLKSIZE * MCI_BLKATONCE)
-
-/*
- * Low level type for this driver
- */
-struct at91mci_host
-{
- struct mmc_host *mmc;
- struct mmc_command *cmd;
- struct mmc_request *request;
-
- void __iomem *baseaddr;
- int irq;
-
- struct at91_mmc_data *board;
- int present;
-
- struct clk *mci_clk;
-
- /*
- * Flag indicating when the command has been sent. This is used to
- * work out whether or not to send the stop
- */
- unsigned int flags;
- /* flag for current bus settings */
- u32 bus_mode;
-
- /* DMA buffer used for transmitting */
- unsigned int* buffer;
- dma_addr_t physical_address;
- unsigned int total_length;
-
- /* Latest in the scatterlist that has been enabled for transfer, but not freed */
- int in_use_index;
-
- /* Latest in the scatterlist that has been enabled for transfer */
- int transfer_index;
-
- /* Timer for timeouts */
- struct timer_list timer;
-};
-
-/*
- * Reset the controller and restore most of the state
- */
-static void at91_reset_host(struct at91mci_host *host)
-{
- unsigned long flags;
- u32 mr;
- u32 sdcr;
- u32 dtor;
- u32 imr;
-
- local_irq_save(flags);
- imr = at91_mci_read(host, AT91_MCI_IMR);
-
- at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
-
- /* save current state */
- mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
- sdcr = at91_mci_read(host, AT91_MCI_SDCR);
- dtor = at91_mci_read(host, AT91_MCI_DTOR);
-
- /* reset the controller */
- at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
-
- /* restore state */
- at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
- at91_mci_write(host, AT91_MCI_MR, mr);
- at91_mci_write(host, AT91_MCI_SDCR, sdcr);
- at91_mci_write(host, AT91_MCI_DTOR, dtor);
- at91_mci_write(host, AT91_MCI_IER, imr);
-
- /* make sure sdio interrupts will fire */
- at91_mci_read(host, AT91_MCI_SR);
-
- local_irq_restore(flags);
-}
-
-static void at91_timeout_timer(unsigned long data)
-{
- struct at91mci_host *host;
-
- host = (struct at91mci_host *)data;
-
- if (host->request) {
- dev_err(host->mmc->parent, "Timeout waiting end of packet\n");
-
- if (host->cmd && host->cmd->data) {
- host->cmd->data->error = -ETIMEDOUT;
- } else {
- if (host->cmd)
- host->cmd->error = -ETIMEDOUT;
- else
- host->request->cmd->error = -ETIMEDOUT;
- }
-
- at91_reset_host(host);
- mmc_request_done(host->mmc, host->request);
- }
-}
-
-/*
- * Copy from sg to a dma block - used for transfers
- */
-static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
-{
- unsigned int len, i, size;
- unsigned *dmabuf = host->buffer;
-
- size = data->blksz * data->blocks;
- len = data->sg_len;
-
- /* MCI1 rev2xx Data Write Operation and number of bytes erratum */
- if (at91mci_is_mci1rev2xx())
- if (host->total_length == 12)
- memset(dmabuf, 0, 12);
-
- /*
- * Just loop through all entries. Size might not
- * be the entire list though so make sure that
- * we do not transfer too much.
- */
- for (i = 0; i < len; i++) {
- struct scatterlist *sg;
- int amount;
- unsigned int *sgbuffer;
-
- sg = &data->sg[i];
-
- sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset;
- amount = min(size, sg->length);
- size -= amount;
-
- if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
- int index;
-
- for (index = 0; index < (amount / 4); index++)
- *dmabuf++ = swab32(sgbuffer[index]);
- } else {
- char *tmpv = (char *)dmabuf;
- memcpy(tmpv, sgbuffer, amount);
- tmpv += amount;
- dmabuf = (unsigned *)tmpv;
- }
-
- kunmap_atomic(sgbuffer);
-
- if (size == 0)
- break;
- }
-
- /*
- * Check that we didn't get a request to transfer
- * more data than can fit into the SG list.
- */
- BUG_ON(size != 0);
-}
-
-/*
- * Handle after a dma read
- */
-static void at91_mci_post_dma_read(struct at91mci_host *host)
-{
- struct mmc_command *cmd;
- struct mmc_data *data;
- unsigned int len, i, size;
- unsigned *dmabuf = host->buffer;
-
- pr_debug("post dma read\n");
-
- cmd = host->cmd;
- if (!cmd) {
- pr_debug("no command\n");
- return;
- }
-
- data = cmd->data;
- if (!data) {
- pr_debug("no data\n");
- return;
- }
-
- size = data->blksz * data->blocks;
- len = data->sg_len;
-
- at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
-
- for (i = 0; i < len; i++) {
- struct scatterlist *sg;
- int amount;
- unsigned int *sgbuffer;
-
- sg = &data->sg[i];
-
- sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset;
- amount = min(size, sg->length);
- size -= amount;
-
- if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
- int index;
- for (index = 0; index < (amount / 4); index++)
- sgbuffer[index] = swab32(*dmabuf++);
- } else {
- char *tmpv = (char *)dmabuf;
- memcpy(sgbuffer, tmpv, amount);
- tmpv += amount;
- dmabuf = (unsigned *)tmpv;
- }
-
- flush_kernel_dcache_page(sg_page(sg));
- kunmap_atomic(sgbuffer);
- data->bytes_xfered += amount;
- if (size == 0)
- break;
- }
-
- pr_debug("post dma read done\n");
-}
-
-/*
- * Handle transmitted data
- */
-static void at91_mci_handle_transmitted(struct at91mci_host *host)
-{
- struct mmc_command *cmd;
- struct mmc_data *data;
-
- pr_debug("Handling the transmit\n");
-
- /* Disable the transfer */
- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
-
- /* Now wait for cmd ready */
- at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
-
- cmd = host->cmd;
- if (!cmd) return;
-
- data = cmd->data;
- if (!data) return;
-
- if (cmd->data->blocks > 1) {
- pr_debug("multiple write : wait for BLKE...\n");
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
- } else
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
-}
-
-/*
- * Update bytes transfered count during a write operation
- */
-static void at91_mci_update_bytes_xfered(struct at91mci_host *host)
-{
- struct mmc_data *data;
-
- /* always deal with the effective request (and not the current cmd) */
-
- if (host->request->cmd && host->request->cmd->error != 0)
- return;
-
- if (host->request->data) {
- data = host->request->data;
- if (data->flags & MMC_DATA_WRITE) {
- /* card is in IDLE mode now */
- pr_debug("-> bytes_xfered %d, total_length = %d\n",
- data->bytes_xfered, host->total_length);
- data->bytes_xfered = data->blksz * data->blocks;
- }
- }
-}
-
-
-/*Handle after command sent ready*/
-static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
-{
- if (!host->cmd)
- return 1;
- else if (!host->cmd->data) {
- if (host->flags & FL_SENT_STOP) {
- /*After multi block write, we must wait for NOTBUSY*/
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
- } else return 1;
- } else if (host->cmd->data->flags & MMC_DATA_WRITE) {
- /*After sendding multi-block-write command, start DMA transfer*/
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE);
- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
- }
-
- /* command not completed, have to wait */
- return 0;
-}
-
-
-/*
- * Enable the controller
- */
-static void at91_mci_enable(struct at91mci_host *host)
-{
- unsigned int mr;
-
- at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
- at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
- at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
- mr = AT91_MCI_PDCMODE | 0x34a;
-
- if (at91mci_is_mci1rev2xx())
- mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
-
- at91_mci_write(host, AT91_MCI_MR, mr);
-
- /* use Slot A or B (only one at same time) */
- at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
-}
-
-/*
- * Disable the controller
- */
-static void at91_mci_disable(struct at91mci_host *host)
-{
- at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
-}
-
-/*
- * Send a command
- */
-static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
-{
- unsigned int cmdr, mr;
- unsigned int block_length;
- struct mmc_data *data = cmd->data;
-
- unsigned int blocks;
- unsigned int ier = 0;
-
- host->cmd = cmd;
-
- /* Needed for leaving busy state before CMD1 */
- if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
- pr_debug("Clearing timeout\n");
- at91_mci_write(host, AT91_MCI_ARGR, 0);
- at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD);
- while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) {
- /* spin */
- pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
- }
- }
-
- cmdr = cmd->opcode;
-
- if (mmc_resp_type(cmd) == MMC_RSP_NONE)
- cmdr |= AT91_MCI_RSPTYP_NONE;
- else {
- /* if a response is expected then allow maximum response latancy */
- cmdr |= AT91_MCI_MAXLAT;
- /* set 136 bit response for R2, 48 bit response otherwise */
- if (mmc_resp_type(cmd) == MMC_RSP_R2)
- cmdr |= AT91_MCI_RSPTYP_136;
- else
- cmdr |= AT91_MCI_RSPTYP_48;
- }
-
- if (data) {
-
- if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) {
- if (data->blksz & 0x3) {
- pr_debug("Unsupported block size\n");
- cmd->error = -EINVAL;
- mmc_request_done(host->mmc, host->request);
- return;
- }
- if (data->flags & MMC_DATA_STREAM) {
- pr_debug("Stream commands not supported\n");
- cmd->error = -EINVAL;
- mmc_request_done(host->mmc, host->request);
- return;
- }
- }
-
- block_length = data->blksz;
- blocks = data->blocks;
-
- /* always set data start - also set direction flag for read */
- if (data->flags & MMC_DATA_READ)
- cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START);
- else if (data->flags & MMC_DATA_WRITE)
- cmdr |= AT91_MCI_TRCMD_START;
-
- if (cmd->opcode == SD_IO_RW_EXTENDED) {
- cmdr |= AT91_MCI_TRTYP_SDIO_BLOCK;
- } else {
- if (data->flags & MMC_DATA_STREAM)
- cmdr |= AT91_MCI_TRTYP_STREAM;
- if (data->blocks > 1)
- cmdr |= AT91_MCI_TRTYP_MULTIPLE;
- }
- }
- else {
- block_length = 0;
- blocks = 0;
- }
-
- if (host->flags & FL_SENT_STOP)
- cmdr |= AT91_MCI_TRCMD_STOP;
-
- if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
- cmdr |= AT91_MCI_OPDCMD;
-
- /*
- * Set the arguments and send the command
- */
- pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n",
- cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR));
-
- if (!data) {
- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS);
- at91_mci_write(host, ATMEL_PDC_RPR, 0);
- at91_mci_write(host, ATMEL_PDC_RCR, 0);
- at91_mci_write(host, ATMEL_PDC_RNPR, 0);
- at91_mci_write(host, ATMEL_PDC_RNCR, 0);
- at91_mci_write(host, ATMEL_PDC_TPR, 0);
- at91_mci_write(host, ATMEL_PDC_TCR, 0);
- at91_mci_write(host, ATMEL_PDC_TNPR, 0);
- at91_mci_write(host, ATMEL_PDC_TNCR, 0);
- ier = AT91_MCI_CMDRDY;
- } else {
- /* zero block length and PDC mode */
- mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff;
- mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0;
- mr |= (block_length << 16);
- mr |= AT91_MCI_PDCMODE;
- at91_mci_write(host, AT91_MCI_MR, mr);
-
- if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261()))
- at91_mci_write(host, AT91_MCI_BLKR,
- AT91_MCI_BLKR_BCNT(blocks) |
- AT91_MCI_BLKR_BLKLEN(block_length));
-
- /*
- * Disable the PDC controller
- */
- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
-
- if (cmdr & AT91_MCI_TRCMD_START) {
- data->bytes_xfered = 0;
- host->transfer_index = 0;
- host->in_use_index = 0;
- if (cmdr & AT91_MCI_TRDIR) {
- /*
- * Handle a read
- */
- host->total_length = 0;
-
- at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address);
- at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ?
- (blocks * block_length) : (blocks * block_length) / 4);
- at91_mci_write(host, ATMEL_PDC_RNPR, 0);
- at91_mci_write(host, ATMEL_PDC_RNCR, 0);
-
- ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
- }
- else {
- /*
- * Handle a write
- */
- host->total_length = block_length * blocks;
- /*
- * MCI1 rev2xx Data Write Operation and
- * number of bytes erratum
- */
- if (at91mci_is_mci1rev2xx())
- if (host->total_length < 12)
- host->total_length = 12;
-
- at91_mci_sg_to_dma(host, data);
-
- pr_debug("Transmitting %d bytes\n", host->total_length);
-
- at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
- at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ?
- host->total_length : host->total_length / 4);
-
- ier = AT91_MCI_CMDRDY;
- }
- }
- }
-
- /*
- * Send the command and then enable the PDC - not the other way round as
- * the data sheet says
- */
-
- at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
- at91_mci_write(host, AT91_MCI_CMDR, cmdr);
-
- if (cmdr & AT91_MCI_TRCMD_START) {
- if (cmdr & AT91_MCI_TRDIR)
- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
- }
-
- /* Enable selected interrupts */
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
-}
-
-/*
- * Process the next step in the request
- */
-static void at91_mci_process_next(struct at91mci_host *host)
-{
- if (!(host->flags & FL_SENT_COMMAND)) {
- host->flags |= FL_SENT_COMMAND;
- at91_mci_send_command(host, host->request->cmd);
- }
- else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
- host->flags |= FL_SENT_STOP;
- at91_mci_send_command(host, host->request->stop);
- } else {
- del_timer(&host->timer);
- /* the at91rm9200 mci controller hangs after some transfers,
- * and the workaround is to reset it after each transfer.
- */
- if (cpu_is_at91rm9200())
- at91_reset_host(host);
- mmc_request_done(host->mmc, host->request);
- }
-}
-
-/*
- * Handle a command that has been completed
- */
-static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status)
-{
- struct mmc_command *cmd = host->cmd;
- struct mmc_data *data = cmd->data;
-
- at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
-
- cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
- cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
- cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
- cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
-
- pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n",
- status, at91_mci_read(host, AT91_MCI_SR),
- cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
-
- if (status & AT91_MCI_ERRORS) {
- if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
- cmd->error = 0;
- }
- else {
- if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) {
- if (data) {
- if (status & AT91_MCI_DTOE)
- data->error = -ETIMEDOUT;
- else if (status & AT91_MCI_DCRCE)
- data->error = -EILSEQ;
- }
- } else {
- if (status & AT91_MCI_RTOE)
- cmd->error = -ETIMEDOUT;
- else if (status & AT91_MCI_RCRCE)
- cmd->error = -EILSEQ;
- else
- cmd->error = -EIO;
- }
-
- pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n",
- cmd->error, data ? data->error : 0,
- cmd->opcode, cmd->retries);
- }
- }
- else
- cmd->error = 0;
-
- at91_mci_process_next(host);
-}
-
-/*
- * Handle an MMC request
- */
-static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
-{
- struct at91mci_host *host = mmc_priv(mmc);
- host->request = mrq;
- host->flags = 0;
-
- /* more than 1s timeout needed with slow SD cards */
- mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
-
- at91_mci_process_next(host);
-}
-
-/*
- * Set the IOS
- */
-static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- int clkdiv;
- struct at91mci_host *host = mmc_priv(mmc);
- unsigned long at91_master_clock = clk_get_rate(host->mci_clk);
-
- host->bus_mode = ios->bus_mode;
-
- if (ios->clock == 0) {
- /* Disable the MCI controller */
- at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
- clkdiv = 0;
- }
- else {
- /* Enable the MCI controller */
- at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
-
- if ((at91_master_clock % (ios->clock * 2)) == 0)
- clkdiv = ((at91_master_clock / ios->clock) / 2) - 1;
- else
- clkdiv = (at91_master_clock / ios->clock) / 2;
-
- pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv,
- at91_master_clock / (2 * (clkdiv + 1)));
- }
- if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) {
- pr_debug("MMC: Setting controller bus width to 4\n");
- at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS);
- }
- else {
- pr_debug("MMC: Setting controller bus width to 1\n");
- at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
- }
-
- /* Set the clock divider */
- at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv);
-
- /* maybe switch power to the card */
- if (gpio_is_valid(host->board->vcc_pin)) {
- switch (ios->power_mode) {
- case MMC_POWER_OFF:
- gpio_set_value(host->board->vcc_pin, 0);
- break;
- case MMC_POWER_UP:
- gpio_set_value(host->board->vcc_pin, 1);
- break;
- case MMC_POWER_ON:
- break;
- default:
- WARN_ON(1);
- }
- }
-}
-
-/*
- * Handle an interrupt
- */
-static irqreturn_t at91_mci_irq(int irq, void *devid)
-{
- struct at91mci_host *host = devid;
- int completed = 0;
- unsigned int int_status, int_mask;
-
- int_status = at91_mci_read(host, AT91_MCI_SR);
- int_mask = at91_mci_read(host, AT91_MCI_IMR);
-
- pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask,
- int_status & int_mask);
-
- int_status = int_status & int_mask;
-
- if (int_status & AT91_MCI_ERRORS) {
- completed = 1;
-
- if (int_status & AT91_MCI_UNRE)
- pr_debug("MMC: Underrun error\n");
- if (int_status & AT91_MCI_OVRE)
- pr_debug("MMC: Overrun error\n");
- if (int_status & AT91_MCI_DTOE)
- pr_debug("MMC: Data timeout\n");
- if (int_status & AT91_MCI_DCRCE)
- pr_debug("MMC: CRC error in data\n");
- if (int_status & AT91_MCI_RTOE)
- pr_debug("MMC: Response timeout\n");
- if (int_status & AT91_MCI_RENDE)
- pr_debug("MMC: Response end bit error\n");
- if (int_status & AT91_MCI_RCRCE)
- pr_debug("MMC: Response CRC error\n");
- if (int_status & AT91_MCI_RDIRE)
- pr_debug("MMC: Response direction error\n");
- if (int_status & AT91_MCI_RINDE)
- pr_debug("MMC: Response index error\n");
- } else {
- /* Only continue processing if no errors */
-
- if (int_status & AT91_MCI_TXBUFE) {
- pr_debug("TX buffer empty\n");
- at91_mci_handle_transmitted(host);
- }
-
- if (int_status & AT91_MCI_ENDRX) {
- pr_debug("ENDRX\n");
- at91_mci_post_dma_read(host);
- }
-
- if (int_status & AT91_MCI_RXBUFF) {
- pr_debug("RX buffer full\n");
- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
- at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX);
- completed = 1;
- }
-
- if (int_status & AT91_MCI_ENDTX)
- pr_debug("Transmit has ended\n");
-
- if (int_status & AT91_MCI_NOTBUSY) {
- pr_debug("Card is ready\n");
- at91_mci_update_bytes_xfered(host);
- completed = 1;
- }
-
- if (int_status & AT91_MCI_DTIP)
- pr_debug("Data transfer in progress\n");
-
- if (int_status & AT91_MCI_BLKE) {
- pr_debug("Block transfer has ended\n");
- if (host->request->data && host->request->data->blocks > 1) {
- /* multi block write : complete multi write
- * command and send stop */
- completed = 1;
- } else {
- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
- }
- }
-
- if (int_status & AT91_MCI_SDIOIRQA)
- mmc_signal_sdio_irq(host->mmc);
-
- if (int_status & AT91_MCI_SDIOIRQB)
- mmc_signal_sdio_irq(host->mmc);
-
- if (int_status & AT91_MCI_TXRDY)
- pr_debug("Ready to transmit\n");
-
- if (int_status & AT91_MCI_RXRDY)
- pr_debug("Ready to receive\n");
-
- if (int_status & AT91_MCI_CMDRDY) {
- pr_debug("Command ready\n");
- completed = at91_mci_handle_cmdrdy(host);
- }
- }
-
- if (completed) {
- pr_debug("Completed command\n");
- at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
- at91_mci_completed_command(host, int_status);
- } else
- at91_mci_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
-{
- struct at91mci_host *host = _host;
- int present;
-
- /* entering this ISR means that we have configured det_pin:
- * we can use its value in board structure */
- present = !gpio_get_value(host->board->det_pin);
-
- /*
- * we expect this irq on both insert and remove,
- * and use a short delay to debounce.
- */
- if (present != host->present) {
- host->present = present;
- pr_debug("%s: card %s\n", mmc_hostname(host->mmc),
- present ? "insert" : "remove");
- if (!present) {
- pr_debug("****** Resetting SD-card bus width ******\n");
- at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
- }
- /* 0.5s needed because of early card detect switch firing */
- mmc_detect_change(host->mmc, msecs_to_jiffies(500));
- }
- return IRQ_HANDLED;
-}
-
-static int at91_mci_get_ro(struct mmc_host *mmc)
-{
- struct at91mci_host *host = mmc_priv(mmc);
-
- if (gpio_is_valid(host->board->wp_pin))
- return !!gpio_get_value(host->board->wp_pin);
- /*
- * Board doesn't support read only detection; let the mmc core
- * decide what to do.
- */
- return -ENOSYS;
-}
-
-static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable)
-{
- struct at91mci_host *host = mmc_priv(mmc);
-
- pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc),
- host->board->slot_b ? 'B':'A', enable ? "enable" : "disable");
- at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR,
- host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA);
-
-}
-
-static const struct mmc_host_ops at91_mci_ops = {
- .request = at91_mci_request,
- .set_ios = at91_mci_set_ios,
- .get_ro = at91_mci_get_ro,
- .enable_sdio_irq = at91_mci_enable_sdio_irq,
-};
-
-/*
- * Probe for the device
- */
-static int __init at91_mci_probe(struct platform_device *pdev)
-{
- struct mmc_host *mmc;
- struct at91mci_host *host;
- struct resource *res;
- int ret;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
-
- if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME))
- return -EBUSY;
-
- mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "couldn't allocate mmc host\n");
- goto fail6;
- }
-
- mmc->ops = &at91_mci_ops;
- mmc->f_min = 375000;
- mmc->f_max = 25000000;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = 0;
-
- mmc->max_blk_size = MCI_MAXBLKSIZE;
- mmc->max_blk_count = MCI_BLKATONCE;
- mmc->max_req_size = MCI_BUFSIZE;
- mmc->max_segs = MCI_BLKATONCE;
- mmc->max_seg_size = MCI_BUFSIZE;
-
- host = mmc_priv(mmc);
- host->mmc = mmc;
- host->bus_mode = 0;
- host->board = pdev->dev.platform_data;
- if (host->board->wire4) {
- if (at91mci_is_mci1rev2xx())
- mmc->caps |= MMC_CAP_4_BIT_DATA;
- else
- dev_warn(&pdev->dev, "4 wire bus mode not supported"
- " - using 1 wire\n");
- }
-
- host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE,
- &host->physical_address, GFP_KERNEL);
- if (!host->buffer) {
- ret = -ENOMEM;
- dev_err(&pdev->dev, "Can't allocate transmit buffer\n");
- goto fail5;
- }
-
- /* Add SDIO capability when available */
- if (at91mci_is_mci1rev2xx()) {
- /* at91mci MCI1 rev2xx sdio interrupt erratum */
- if (host->board->wire4 || !host->board->slot_b)
- mmc->caps |= MMC_CAP_SDIO_IRQ;
- }
-
- /*
- * Reserve GPIOs ... board init code makes sure these pins are set
- * up as GPIOs with the right direction (input, except for vcc)
- */
- if (gpio_is_valid(host->board->det_pin)) {
- ret = gpio_request(host->board->det_pin, "mmc_detect");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "couldn't claim card detect pin\n");
- goto fail4b;
- }
- }
- if (gpio_is_valid(host->board->wp_pin)) {
- ret = gpio_request(host->board->wp_pin, "mmc_wp");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n");
- goto fail4;
- }
- }
- if (gpio_is_valid(host->board->vcc_pin)) {
- ret = gpio_request(host->board->vcc_pin, "mmc_vcc");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n");
- goto fail3;
- }
- }
-
- /*
- * Get Clock
- */
- host->mci_clk = clk_get(&pdev->dev, "mci_clk");
- if (IS_ERR(host->mci_clk)) {
- ret = -ENODEV;
- dev_dbg(&pdev->dev, "no mci_clk?\n");
- goto fail2;
- }
-
- /*
- * Map I/O region
- */
- host->baseaddr = ioremap(res->start, resource_size(res));
- if (!host->baseaddr) {
- ret = -ENOMEM;
- goto fail1;
- }
-
- /*
- * Reset hardware
- */
- clk_enable(host->mci_clk); /* Enable the peripheral clock */
- at91_mci_disable(host);
- at91_mci_enable(host);
-
- /*
- * Allocate the MCI interrupt
- */
- host->irq = platform_get_irq(pdev, 0);
- ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED,
- mmc_hostname(mmc), host);
- if (ret) {
- dev_dbg(&pdev->dev, "request MCI interrupt failed\n");
- goto fail0;
- }
-
- setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);
-
- platform_set_drvdata(pdev, mmc);
-
- /*
- * Add host to MMC layer
- */
- if (gpio_is_valid(host->board->det_pin)) {
- host->present = !gpio_get_value(host->board->det_pin);
- }
- else
- host->present = -1;
-
- mmc_add_host(mmc);
-
- /*
- * monitor card insertion/removal if we can
- */
- if (gpio_is_valid(host->board->det_pin)) {
- ret = request_irq(gpio_to_irq(host->board->det_pin),
- at91_mmc_det_irq, 0, mmc_hostname(mmc), host);
- if (ret)
- dev_warn(&pdev->dev, "request MMC detect irq failed\n");
- else
- device_init_wakeup(&pdev->dev, 1);
- }
-
- pr_debug("Added MCI driver\n");
-
- return 0;
-
-fail0:
- clk_disable(host->mci_clk);
- iounmap(host->baseaddr);
-fail1:
- clk_put(host->mci_clk);
-fail2:
- if (gpio_is_valid(host->board->vcc_pin))
- gpio_free(host->board->vcc_pin);
-fail3:
- if (gpio_is_valid(host->board->wp_pin))
- gpio_free(host->board->wp_pin);
-fail4:
- if (gpio_is_valid(host->board->det_pin))
- gpio_free(host->board->det_pin);
-fail4b:
- if (host->buffer)
- dma_free_coherent(&pdev->dev, MCI_BUFSIZE,
- host->buffer, host->physical_address);
-fail5:
- mmc_free_host(mmc);
-fail6:
- release_mem_region(res->start, resource_size(res));
- dev_err(&pdev->dev, "probe failed, err %d\n", ret);
- return ret;
-}
-
-/*
- * Remove a device
- */
-static int __exit at91_mci_remove(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct at91mci_host *host;
- struct resource *res;
-
- if (!mmc)
- return -1;
-
- host = mmc_priv(mmc);
-
- if (host->buffer)
- dma_free_coherent(&pdev->dev, MCI_BUFSIZE,
- host->buffer, host->physical_address);
-
- if (gpio_is_valid(host->board->det_pin)) {
- if (device_can_wakeup(&pdev->dev))
- free_irq(gpio_to_irq(host->board->det_pin), host);
- device_init_wakeup(&pdev->dev, 0);
- gpio_free(host->board->det_pin);
- }
-
- at91_mci_disable(host);
- del_timer_sync(&host->timer);
- mmc_remove_host(mmc);
- free_irq(host->irq, host);
-
- clk_disable(host->mci_clk); /* Disable the peripheral clock */
- clk_put(host->mci_clk);
-
- if (gpio_is_valid(host->board->vcc_pin))
- gpio_free(host->board->vcc_pin);
- if (gpio_is_valid(host->board->wp_pin))
- gpio_free(host->board->wp_pin);
-
- iounmap(host->baseaddr);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- mmc_free_host(mmc);
- platform_set_drvdata(pdev, NULL);
- pr_debug("MCI Removed\n");
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct at91mci_host *host = mmc_priv(mmc);
- int ret = 0;
-
- if (gpio_is_valid(host->board->det_pin) && device_may_wakeup(&pdev->dev))
- enable_irq_wake(host->board->det_pin);
-
- if (mmc)
- ret = mmc_suspend_host(mmc);
-
- return ret;
-}
-
-static int at91_mci_resume(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct at91mci_host *host = mmc_priv(mmc);
- int ret = 0;
-
- if (gpio_is_valid(host->board->det_pin) && device_may_wakeup(&pdev->dev))
- disable_irq_wake(host->board->det_pin);
-
- if (mmc)
- ret = mmc_resume_host(mmc);
-
- return ret;
-}
-#else
-#define at91_mci_suspend NULL
-#define at91_mci_resume NULL
-#endif
-
-static struct platform_driver at91_mci_driver = {
- .remove = __exit_p(at91_mci_remove),
- .suspend = at91_mci_suspend,
- .resume = at91_mci_resume,
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init at91_mci_init(void)
-{
- return platform_driver_probe(&at91_mci_driver, at91_mci_probe);
-}
-
-static void __exit at91_mci_exit(void)
-{
- platform_driver_unregister(&at91_mci_driver);
-}
-
-module_init(at91_mci_init);
-module_exit(at91_mci_exit);
-
-MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver");
-MODULE_AUTHOR("Nick Randell");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:at91_mci");
diff --git a/drivers/mmc/host/at91_mci.h b/drivers/mmc/host/at91_mci.h
deleted file mode 100644
index eec3a6b1c2bc..000000000000
--- a/drivers/mmc/host/at91_mci.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * drivers/mmc/host/at91_mci.h
- *
- * Copyright (C) 2005 Ivan Kokshaysky
- * Copyright (C) SAN People
- *
- * MultiMedia Card Interface (MCI) registers.
- * Based on AT91RM9200 datasheet revision F.
- *
- * 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.
- */
-
-#ifndef AT91_MCI_H
-#define AT91_MCI_H
-
-#define AT91_MCI_CR 0x00 /* Control Register */
-#define AT91_MCI_MCIEN (1 << 0) /* Multi-Media Interface Enable */
-#define AT91_MCI_MCIDIS (1 << 1) /* Multi-Media Interface Disable */
-#define AT91_MCI_PWSEN (1 << 2) /* Power Save Mode Enable */
-#define AT91_MCI_PWSDIS (1 << 3) /* Power Save Mode Disable */
-#define AT91_MCI_SWRST (1 << 7) /* Software Reset */
-
-#define AT91_MCI_MR 0x04 /* Mode Register */
-#define AT91_MCI_CLKDIV (0xff << 0) /* Clock Divider */
-#define AT91_MCI_PWSDIV (7 << 8) /* Power Saving Divider */
-#define AT91_MCI_RDPROOF (1 << 11) /* Read Proof Enable [SAM926[03] only] */
-#define AT91_MCI_WRPROOF (1 << 12) /* Write Proof Enable [SAM926[03] only] */
-#define AT91_MCI_PDCFBYTE (1 << 13) /* PDC Force Byte Transfer [SAM926[03] only] */
-#define AT91_MCI_PDCPADV (1 << 14) /* PDC Padding Value */
-#define AT91_MCI_PDCMODE (1 << 15) /* PDC-orientated Mode */
-#define AT91_MCI_BLKLEN (0xfff << 18) /* Data Block Length */
-
-#define AT91_MCI_DTOR 0x08 /* Data Timeout Register */
-#define AT91_MCI_DTOCYC (0xf << 0) /* Data Timeout Cycle Number */
-#define AT91_MCI_DTOMUL (7 << 4) /* Data Timeout Multiplier */
-#define AT91_MCI_DTOMUL_1 (0 << 4)
-#define AT91_MCI_DTOMUL_16 (1 << 4)
-#define AT91_MCI_DTOMUL_128 (2 << 4)
-#define AT91_MCI_DTOMUL_256 (3 << 4)
-#define AT91_MCI_DTOMUL_1K (4 << 4)
-#define AT91_MCI_DTOMUL_4K (5 << 4)
-#define AT91_MCI_DTOMUL_64K (6 << 4)
-#define AT91_MCI_DTOMUL_1M (7 << 4)
-
-#define AT91_MCI_SDCR 0x0c /* SD Card Register */
-#define AT91_MCI_SDCSEL (3 << 0) /* SD Card Selector */
-#define AT91_MCI_SDCBUS (1 << 7) /* 1-bit or 4-bit bus */
-
-#define AT91_MCI_ARGR 0x10 /* Argument Register */
-
-#define AT91_MCI_CMDR 0x14 /* Command Register */
-#define AT91_MCI_CMDNB (0x3f << 0) /* Command Number */
-#define AT91_MCI_RSPTYP (3 << 6) /* Response Type */
-#define AT91_MCI_RSPTYP_NONE (0 << 6)
-#define AT91_MCI_RSPTYP_48 (1 << 6)
-#define AT91_MCI_RSPTYP_136 (2 << 6)
-#define AT91_MCI_SPCMD (7 << 8) /* Special Command */
-#define AT91_MCI_SPCMD_NONE (0 << 8)
-#define AT91_MCI_SPCMD_INIT (1 << 8)
-#define AT91_MCI_SPCMD_SYNC (2 << 8)
-#define AT91_MCI_SPCMD_ICMD (4 << 8)
-#define AT91_MCI_SPCMD_IRESP (5 << 8)
-#define AT91_MCI_OPDCMD (1 << 11) /* Open Drain Command */
-#define AT91_MCI_MAXLAT (1 << 12) /* Max Latency for Command to Response */
-#define AT91_MCI_TRCMD (3 << 16) /* Transfer Command */
-#define AT91_MCI_TRCMD_NONE (0 << 16)
-#define AT91_MCI_TRCMD_START (1 << 16)
-#define AT91_MCI_TRCMD_STOP (2 << 16)
-#define AT91_MCI_TRDIR (1 << 18) /* Transfer Direction */
-#define AT91_MCI_TRTYP (3 << 19) /* Transfer Type */
-#define AT91_MCI_TRTYP_BLOCK (0 << 19)
-#define AT91_MCI_TRTYP_MULTIPLE (1 << 19)
-#define AT91_MCI_TRTYP_STREAM (2 << 19)
-#define AT91_MCI_TRTYP_SDIO_BYTE (4 << 19)
-#define AT91_MCI_TRTYP_SDIO_BLOCK (5 << 19)
-
-#define AT91_MCI_BLKR 0x18 /* Block Register */
-#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */
-#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block length */
-
-#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */
-#define AT91_MCR_RDR 0x30 /* Receive Data Register */
-#define AT91_MCR_TDR 0x34 /* Transmit Data Register */
-
-#define AT91_MCI_SR 0x40 /* Status Register */
-#define AT91_MCI_CMDRDY (1 << 0) /* Command Ready */
-#define AT91_MCI_RXRDY (1 << 1) /* Receiver Ready */
-#define AT91_MCI_TXRDY (1 << 2) /* Transmit Ready */
-#define AT91_MCI_BLKE (1 << 3) /* Data Block Ended */
-#define AT91_MCI_DTIP (1 << 4) /* Data Transfer in Progress */
-#define AT91_MCI_NOTBUSY (1 << 5) /* Data Not Busy */
-#define AT91_MCI_ENDRX (1 << 6) /* End of RX Buffer */
-#define AT91_MCI_ENDTX (1 << 7) /* End fo TX Buffer */
-#define AT91_MCI_SDIOIRQA (1 << 8) /* SDIO Interrupt for Slot A */
-#define AT91_MCI_SDIOIRQB (1 << 9) /* SDIO Interrupt for Slot B */
-#define AT91_MCI_RXBUFF (1 << 14) /* RX Buffer Full */
-#define AT91_MCI_TXBUFE (1 << 15) /* TX Buffer Empty */
-#define AT91_MCI_RINDE (1 << 16) /* Response Index Error */
-#define AT91_MCI_RDIRE (1 << 17) /* Response Direction Error */
-#define AT91_MCI_RCRCE (1 << 18) /* Response CRC Error */
-#define AT91_MCI_RENDE (1 << 19) /* Response End Bit Error */
-#define AT91_MCI_RTOE (1 << 20) /* Response Time-out Error */
-#define AT91_MCI_DCRCE (1 << 21) /* Data CRC Error */
-#define AT91_MCI_DTOE (1 << 22) /* Data Time-out Error */
-#define AT91_MCI_OVRE (1 << 30) /* Overrun */
-#define AT91_MCI_UNRE (1 << 31) /* Underrun */
-
-#define AT91_MCI_IER 0x44 /* Interrupt Enable Register */
-#define AT91_MCI_IDR 0x48 /* Interrupt Disable Register */
-#define AT91_MCI_IMR 0x4c /* Interrupt Mask Register */
-
-#endif
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index ddf096e3803f..722af1de7967 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/types.h>
+#include <linux/platform_data/atmel.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio.h>
@@ -40,7 +41,6 @@
#include <asm/unaligned.h>
#include <mach/cpu.h>
-#include <mach/board.h>
#include "atmel-mci-regs.h"
@@ -511,7 +511,7 @@ static const struct of_device_id atmci_dt_ids[] = {
MODULE_DEVICE_TABLE(of, atmci_dt_ids);
-static struct mci_platform_data __devinit*
+static struct mci_platform_data*
atmci_of_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index dbd0c8a4e98a..127a8fade4da 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -943,7 +943,7 @@ static const struct mmc_host_ops au1xmmc_ops = {
.enable_sdio_irq = au1xmmc_enable_sdio_irq,
};
-static int __devinit au1xmmc_probe(struct platform_device *pdev)
+static int au1xmmc_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct au1xmmc_host *host;
@@ -1114,7 +1114,7 @@ out0:
return ret;
}
-static int __devexit au1xmmc_remove(struct platform_device *pdev)
+static int au1xmmc_remove(struct platform_device *pdev)
{
struct au1xmmc_host *host = platform_get_drvdata(pdev);
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
index b9b463eca1ec..fb4348c5b6ac 100644
--- a/drivers/mmc/host/bfin_sdh.c
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -522,7 +522,7 @@ static void sdh_reset(void)
SSYNC();
}
-static int __devinit sdh_probe(struct platform_device *pdev)
+static int sdh_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct sdh_host *host;
@@ -617,7 +617,7 @@ out1:
return ret;
}
-static int __devexit sdh_remove(struct platform_device *pdev)
+static int sdh_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
@@ -680,7 +680,7 @@ static int sdh_resume(struct platform_device *dev)
static struct platform_driver sdh_driver = {
.probe = sdh_probe,
- .remove = __devexit_p(sdh_remove),
+ .remove = sdh_remove,
.suspend = sdh_suspend,
.resume = sdh_resume,
.driver = {
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index 83693fd7c6b3..777ca2046b27 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -690,7 +690,7 @@ static int cb710_mmc_resume(struct platform_device *pdev)
#endif /* CONFIG_PM */
-static int __devinit cb710_mmc_init(struct platform_device *pdev)
+static int cb710_mmc_init(struct platform_device *pdev)
{
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
struct cb710_chip *chip = cb710_slot_to_chip(slot);
@@ -746,7 +746,7 @@ err_free_mmc:
return err;
}
-static int __devexit cb710_mmc_exit(struct platform_device *pdev)
+static int cb710_mmc_exit(struct platform_device *pdev)
{
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
@@ -773,7 +773,7 @@ static int __devexit cb710_mmc_exit(struct platform_device *pdev)
static struct platform_driver cb710_mmc_driver = {
.driver.name = "cb710-mmc",
.probe = cb710_mmc_init,
- .remove = __devexit_p(cb710_mmc_exit),
+ .remove = cb710_mmc_exit,
#ifdef CONFIG_PM
.suspend = cb710_mmc_suspend,
.resume = cb710_mmc_resume,
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 660bbc528862..72fd0f2c9013 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -175,16 +175,6 @@ static int dw_mci_exynos_setup_bus(struct dw_mci *host,
}
}
- gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
- if (gpio_is_valid(gpio)) {
- if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
- dev_info(host->dev, "gpio [%d] request failed\n",
- gpio);
- } else {
- dev_info(host->dev, "wp gpio not available");
- host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
- }
-
if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
return 0;
@@ -208,7 +198,7 @@ static unsigned long exynos5250_dwmmc_caps[4] = {
MMC_CAP_CMD23,
};
-static struct dw_mci_drv_data exynos5250_drv_data = {
+static const struct dw_mci_drv_data exynos5250_drv_data = {
.caps = exynos5250_dwmmc_caps,
.init = dw_mci_exynos_priv_init,
.setup_clock = dw_mci_exynos_setup_clock,
@@ -220,14 +210,14 @@ static struct dw_mci_drv_data exynos5250_drv_data = {
static const struct of_device_id dw_mci_exynos_match[] = {
{ .compatible = "samsung,exynos5250-dw-mshc",
- .data = (void *)&exynos5250_drv_data, },
+ .data = &exynos5250_drv_data, },
{},
};
-MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
+MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
int dw_mci_exynos_probe(struct platform_device *pdev)
{
- struct dw_mci_drv_data *drv_data;
+ const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index edb37e9135ae..083fcd29c9c6 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -37,7 +37,7 @@ static struct dw_mci_board pci_board_data = {
.fifo_depth = 32,
};
-static int __devinit dw_mci_pci_probe(struct pci_dev *pdev,
+static int dw_mci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *entries)
{
struct dw_mci *host;
@@ -85,7 +85,7 @@ err_disable_dev:
return ret;
}
-static void __devexit dw_mci_pci_remove(struct pci_dev *pdev)
+static void dw_mci_pci_remove(struct pci_dev *pdev)
{
struct dw_mci *host = pci_get_drvdata(pdev);
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index c960ca7ffbe6..41c27b74b003 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -10,6 +10,7 @@
* (at your option) any later version.
*/
+#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/io.h>
@@ -24,7 +25,7 @@
#include "dw_mmc.h"
int dw_mci_pltfm_register(struct platform_device *pdev,
- struct dw_mci_drv_data *drv_data)
+ const struct dw_mci_drv_data *drv_data)
{
struct dw_mci *host;
struct resource *regs;
@@ -46,12 +47,12 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
host->dev = &pdev->dev;
host->irq_flags = 0;
host->pdata = pdev->dev.platform_data;
- host->regs = devm_request_and_ioremap(&pdev->dev, regs);
- if (!host->regs)
- return -ENOMEM;
+ host->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(host->regs))
+ return PTR_ERR(host->regs);
- if (host->drv_data->init) {
- ret = host->drv_data->init(host);
+ if (drv_data && drv_data->init) {
+ ret = drv_data->init(host);
if (ret)
return ret;
}
@@ -62,12 +63,12 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
-static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev)
+static int dw_mci_pltfm_probe(struct platform_device *pdev)
{
return dw_mci_pltfm_register(pdev, NULL);
}
-static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev)
+static int dw_mci_pltfm_remove(struct platform_device *pdev)
{
struct dw_mci *host = platform_get_drvdata(pdev);
@@ -119,7 +120,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
static struct platform_driver dw_mci_pltfm_driver = {
- .remove = __exit_p(dw_mci_pltfm_remove),
+ .probe = dw_mci_pltfm_probe,
+ .remove = dw_mci_pltfm_remove,
.driver = {
.name = "dw_mmc",
.of_match_table = of_match_ptr(dw_mci_pltfm_match),
@@ -127,18 +129,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
},
};
-static int __init dw_mci_init(void)
-{
- return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe);
-}
-
-static void __exit dw_mci_exit(void)
-{
- platform_driver_unregister(&dw_mci_pltfm_driver);
-}
-
-module_init(dw_mci_init);
-module_exit(dw_mci_exit);
+module_platform_driver(dw_mci_pltfm_driver);
MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
MODULE_AUTHOR("NXP Semiconductor VietNam");
diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h
index 301f24541fc2..68e7fd2f6148 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.h
+++ b/drivers/mmc/host/dw_mmc-pltfm.h
@@ -13,8 +13,8 @@
#define _DW_MMC_PLTFM_H_
extern int dw_mci_pltfm_register(struct platform_device *pdev,
- struct dw_mci_drv_data *drv_data);
-extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev);
+ const struct dw_mci_drv_data *drv_data);
+extern int dw_mci_pltfm_remove(struct platform_device *pdev);
extern const struct dev_pm_ops dw_mci_pltfm_pmops;
#endif /* _DW_MMC_PLTFM_H_ */
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c2828f35c3b8..98342213ed21 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -34,6 +34,7 @@
#include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
#include "dw_mmc.h"
@@ -74,6 +75,8 @@ struct idmac_desc {
* struct dw_mci_slot - MMC slot state
* @mmc: The mmc_host representing this slot.
* @host: The MMC controller this slot is using.
+ * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
+ * @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
* @ctype: Card type for this slot.
* @mrq: mmc_request currently being processed or waiting to be
* processed, or NULL when the slot is idle.
@@ -88,6 +91,9 @@ struct dw_mci_slot {
struct mmc_host *mmc;
struct dw_mci *host;
+ int quirks;
+ int wp_gpio;
+
u32 ctype;
struct mmc_request *mrq;
@@ -232,6 +238,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{
struct mmc_data *data;
struct dw_mci_slot *slot = mmc_priv(mmc);
+ const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
u32 cmdr;
cmd->error = -EINPROGRESS;
@@ -261,8 +268,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
cmdr |= SDMMC_CMD_DAT_WR;
}
- if (slot->host->drv_data->prepare_command)
- slot->host->drv_data->prepare_command(slot->host, &cmdr);
+ if (drv_data && drv_data->prepare_command)
+ drv_data->prepare_command(slot->host, &cmdr);
return cmdr;
}
@@ -434,7 +441,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
return 0;
}
-static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+static const struct dw_mci_dma_ops dw_mci_idmac_ops = {
.init = dw_mci_idmac_init,
.start = dw_mci_idmac_start_dma,
.stop = dw_mci_idmac_stop_dma,
@@ -616,13 +623,13 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
cmd, arg, cmd_status);
}
-static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
{
struct dw_mci *host = slot->host;
u32 div;
u32 clk_en_a;
- if (slot->clock != host->current_speed) {
+ if (slot->clock != host->current_speed || force_clkinit) {
div = host->bus_hz / slot->clock;
if (host->bus_hz % slot->clock && host->bus_hz > slot->clock)
/*
@@ -682,9 +689,6 @@ static void __dw_mci_start_request(struct dw_mci *host,
if (host->pdata->select_slot)
host->pdata->select_slot(slot->id);
- /* Slot specific timing and width adjustment */
- dw_mci_setup_bus(slot);
-
host->cur_slot = slot;
host->mrq = mrq;
@@ -772,21 +776,19 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
+ const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
u32 regs;
- /* set default 1 bit mode */
- slot->ctype = SDMMC_CTYPE_1BIT;
-
switch (ios->bus_width) {
- case MMC_BUS_WIDTH_1:
- slot->ctype = SDMMC_CTYPE_1BIT;
- break;
case MMC_BUS_WIDTH_4:
slot->ctype = SDMMC_CTYPE_4BIT;
break;
case MMC_BUS_WIDTH_8:
slot->ctype = SDMMC_CTYPE_8BIT;
break;
+ default:
+ /* set default 1 bit mode */
+ slot->ctype = SDMMC_CTYPE_1BIT;
}
regs = mci_readl(slot->host, UHS_REG);
@@ -807,8 +809,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
slot->clock = ios->clock;
}
- if (slot->host->drv_data->set_ios)
- slot->host->drv_data->set_ios(slot->host, ios);
+ if (drv_data && drv_data->set_ios)
+ drv_data->set_ios(slot->host, ios);
+
+ /* Slot specific timing and width adjustment */
+ dw_mci_setup_bus(slot, false);
switch (ios->power_mode) {
case MMC_POWER_UP:
@@ -826,10 +831,12 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
struct dw_mci_board *brd = slot->host->pdata;
/* Use platform get_ro function, else try on board write protect */
- if (brd->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)
+ if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT)
read_only = 0;
else if (brd->get_ro)
read_only = brd->get_ro(slot->id);
+ else if (gpio_is_valid(slot->wp_gpio))
+ read_only = gpio_get_value(slot->wp_gpio);
else
read_only =
mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
@@ -1446,7 +1453,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
if (!sg_miter_next(sg_miter))
goto done;
- host->sg = sg_miter->__sg;
+ host->sg = sg_miter->piter.sg;
buf = sg_miter->addr;
remain = sg_miter->length;
offset = 0;
@@ -1501,7 +1508,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
if (!sg_miter_next(sg_miter))
goto done;
- host->sg = sg_miter->__sg;
+ host->sg = sg_miter->piter.sg;
buf = sg_miter->addr;
remain = sg_miter->length;
offset = 0;
@@ -1786,6 +1793,30 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
return NULL;
}
+static struct dw_mci_of_slot_quirks {
+ char *quirk;
+ int id;
+} of_slot_quirks[] = {
+ {
+ .quirk = "disable-wp",
+ .id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT,
+ },
+};
+
+static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
+{
+ struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
+ int quirks = 0;
+ int idx;
+
+ /* get quirks */
+ for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
+ if (of_get_property(np, of_slot_quirks[idx].quirk, NULL))
+ quirks |= of_slot_quirks[idx].id;
+
+ return quirks;
+}
+
/* find out bus-width for a given slot */
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
{
@@ -1800,7 +1831,34 @@ static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
" as 1\n");
return bus_wd;
}
+
+/* find the write protect gpio for a given slot; or -1 if none specified */
+static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
+{
+ struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
+ int gpio;
+
+ if (!np)
+ return -EINVAL;
+
+ gpio = of_get_named_gpio(np, "wp-gpios", 0);
+
+ /* Having a missing entry is valid; return silently */
+ if (!gpio_is_valid(gpio))
+ return -EINVAL;
+
+ if (devm_gpio_request(dev, gpio, "dw-mci-wp")) {
+ dev_warn(dev, "gpio [%d] request failed\n", gpio);
+ return -EINVAL;
+ }
+
+ return gpio;
+}
#else /* CONFIG_OF */
+static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
+{
+ return 0;
+}
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
{
return 1;
@@ -1809,12 +1867,17 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
{
return NULL;
}
+static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
+{
+ return -EINVAL;
+}
#endif /* CONFIG_OF */
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
{
struct mmc_host *mmc;
struct dw_mci_slot *slot;
+ const struct dw_mci_drv_data *drv_data = host->drv_data;
int ctrl_id, ret;
u8 bus_width;
@@ -1828,6 +1891,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
slot->host = host;
host->slot[id] = slot;
+ slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
+
mmc->ops = &dw_mci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
mmc->f_max = host->bus_hz;
@@ -1847,6 +1912,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
+ if (host->pdata->pm_caps)
+ mmc->pm_caps = host->pdata->pm_caps;
+
if (host->dev->of_node) {
ctrl_id = of_alias_get_id(host->dev->of_node, "mshc");
if (ctrl_id < 0)
@@ -1854,8 +1922,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
} else {
ctrl_id = to_platform_device(host->dev)->id;
}
- if (host->drv_data && host->drv_data->caps)
- mmc->caps |= host->drv_data->caps[ctrl_id];
+ if (drv_data && drv_data->caps)
+ mmc->caps |= drv_data->caps[ctrl_id];
if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2;
@@ -1867,10 +1935,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
else
bus_width = 1;
- if (host->drv_data->setup_bus) {
+ if (drv_data && drv_data->setup_bus) {
struct device_node *slot_np;
slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
- ret = host->drv_data->setup_bus(host, slot_np, bus_width);
+ ret = drv_data->setup_bus(host, slot_np, bus_width);
if (ret)
goto err_setup_bus;
}
@@ -1908,7 +1976,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
#endif /* CONFIG_MMC_DW_IDMAC */
}
- host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
+ host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc");
if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL;
@@ -1920,6 +1988,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
else
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+ slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
+
mmc_add_host(mmc);
#if defined(CONFIG_DEBUG_FS)
@@ -1957,7 +2027,7 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
static void dw_mci_init_dma(struct dw_mci *host)
{
/* Alloc memory for sg translation */
- host->sg_cpu = dma_alloc_coherent(host->dev, PAGE_SIZE,
+ host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL);
if (!host->sg_cpu) {
dev_err(host->dev, "%s: could not alloc DMA memory\n",
@@ -1968,7 +2038,7 @@ static void dw_mci_init_dma(struct dw_mci *host)
/* Determine which DMA interface to use */
#ifdef CONFIG_MMC_DW_IDMAC
host->dma_ops = &dw_mci_idmac_ops;
- dev_info(&host->dev, "Using internal DMA controller.\n");
+ dev_info(host->dev, "Using internal DMA controller.\n");
#endif
if (!host->dma_ops)
@@ -2035,6 +2105,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
struct dw_mci_board *pdata;
struct device *dev = host->dev;
struct device_node *np = dev->of_node;
+ const struct dw_mci_drv_data *drv_data = host->drv_data;
int idx, ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
@@ -2062,12 +2133,18 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
- if (host->drv_data->parse_dt) {
- ret = host->drv_data->parse_dt(host);
+ if (drv_data && drv_data->parse_dt) {
+ ret = drv_data->parse_dt(host);
if (ret)
return ERR_PTR(ret);
}
+ if (of_find_property(np, "keep-power-in-suspend", NULL))
+ pdata->pm_caps |= MMC_PM_KEEP_POWER;
+
+ if (of_find_property(np, "enable-sdio-wakeup", NULL))
+ pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+
return pdata;
}
@@ -2080,6 +2157,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
int dw_mci_probe(struct dw_mci *host)
{
+ const struct dw_mci_drv_data *drv_data = host->drv_data;
int width, i, ret = 0;
u32 fifo_size;
int init_slots = 0;
@@ -2098,26 +2176,24 @@ int dw_mci_probe(struct dw_mci *host)
return -ENODEV;
}
- host->biu_clk = clk_get(host->dev, "biu");
+ host->biu_clk = devm_clk_get(host->dev, "biu");
if (IS_ERR(host->biu_clk)) {
dev_dbg(host->dev, "biu clock not available\n");
} else {
ret = clk_prepare_enable(host->biu_clk);
if (ret) {
dev_err(host->dev, "failed to enable biu clock\n");
- clk_put(host->biu_clk);
return ret;
}
}
- host->ciu_clk = clk_get(host->dev, "ciu");
+ host->ciu_clk = devm_clk_get(host->dev, "ciu");
if (IS_ERR(host->ciu_clk)) {
dev_dbg(host->dev, "ciu clock not available\n");
} else {
ret = clk_prepare_enable(host->ciu_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu clock\n");
- clk_put(host->ciu_clk);
goto err_clk_biu;
}
}
@@ -2127,8 +2203,8 @@ int dw_mci_probe(struct dw_mci *host)
else
host->bus_hz = clk_get_rate(host->ciu_clk);
- if (host->drv_data->setup_clock) {
- ret = host->drv_data->setup_clock(host);
+ if (drv_data && drv_data->setup_clock) {
+ ret = drv_data->setup_clock(host);
if (ret) {
dev_err(host->dev,
"implementation specific clock setup failed\n");
@@ -2219,7 +2295,8 @@ int dw_mci_probe(struct dw_mci *host)
if (!host->card_workqueue)
goto err_dmaunmap;
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
- ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
+ ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
+ host->irq_flags, "dw-mci", host);
if (ret)
goto err_workqueue;
@@ -2228,6 +2305,21 @@ int dw_mci_probe(struct dw_mci *host)
else
host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+ /*
+ * Enable interrupts for command done, data over, data empty, card det,
+ * receive ready and error such as transmit, receive timeout, crc error
+ */
+ mci_writel(host, RINTSTS, 0xFFFFFFFF);
+ mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+ SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+ DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+ mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
+
+ dev_info(host->dev, "DW MMC controller at irq %d, "
+ "%d bit host data width, "
+ "%u deep fifo\n",
+ host->irq, width, fifo_size);
+
/* We need at least one slot to succeed */
for (i = 0; i < host->num_slots; i++) {
ret = dw_mci_init_slot(host, i);
@@ -2242,7 +2334,7 @@ int dw_mci_probe(struct dw_mci *host)
} else {
dev_dbg(host->dev, "attempted to initialize %d slots, "
"but failed on all\n", host->num_slots);
- goto err_init_slot;
+ goto err_workqueue;
}
/*
@@ -2257,52 +2349,29 @@ int dw_mci_probe(struct dw_mci *host)
else
host->data_offset = DATA_240A_OFFSET;
- /*
- * Enable interrupts for command done, data over, data empty, card det,
- * receive ready and error such as transmit, receive timeout, crc error
- */
- mci_writel(host, RINTSTS, 0xFFFFFFFF);
- mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
- SDMMC_INT_TXDR | SDMMC_INT_RXDR |
- DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
- mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
-
- dev_info(host->dev, "DW MMC controller at irq %d, "
- "%d bit host data width, "
- "%u deep fifo\n",
- host->irq, width, fifo_size);
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
return 0;
-err_init_slot:
- free_irq(host->irq, host);
-
err_workqueue:
destroy_workqueue(host->card_workqueue);
err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
- dma_free_coherent(host->dev, PAGE_SIZE,
- host->sg_cpu, host->sg_dma);
- if (host->vmmc) {
+ if (host->vmmc)
regulator_disable(host->vmmc);
- regulator_put(host->vmmc);
- }
err_clk_ciu:
- if (!IS_ERR(host->ciu_clk)) {
+ if (!IS_ERR(host->ciu_clk))
clk_disable_unprepare(host->ciu_clk);
- clk_put(host->ciu_clk);
- }
+
err_clk_biu:
- if (!IS_ERR(host->biu_clk)) {
+ if (!IS_ERR(host->biu_clk))
clk_disable_unprepare(host->biu_clk);
- clk_put(host->biu_clk);
- }
+
return ret;
}
EXPORT_SYMBOL(dw_mci_probe);
@@ -2324,24 +2393,19 @@ void dw_mci_remove(struct dw_mci *host)
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);
- free_irq(host->irq, host);
destroy_workqueue(host->card_workqueue);
- dma_free_coherent(host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
- if (host->vmmc) {
+ if (host->vmmc)
regulator_disable(host->vmmc);
- regulator_put(host->vmmc);
- }
if (!IS_ERR(host->ciu_clk))
clk_disable_unprepare(host->ciu_clk);
+
if (!IS_ERR(host->biu_clk))
clk_disable_unprepare(host->biu_clk);
- clk_put(host->ciu_clk);
- clk_put(host->biu_clk);
}
EXPORT_SYMBOL(dw_mci_remove);
@@ -2405,6 +2469,11 @@ int dw_mci_resume(struct dw_mci *host)
struct dw_mci_slot *slot = host->slot[i];
if (!slot)
continue;
+ if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) {
+ dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
+ dw_mci_setup_bus(slot, true);
+ }
+
ret = mmc_resume_host(host->slot[i]->mmc);
if (ret < 0)
return ret;
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index c8852a8128a9..2391c6b7a4bb 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -702,7 +702,7 @@ static const struct jz_gpio_bulk_request jz4740_mmc_pins[] = {
JZ_GPIO_BULK_PIN(MSC_DATA3),
};
-static int __devinit jz4740_mmc_request_gpio(struct device *dev, int gpio,
+static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
const char *name, bool output, int value)
{
int ret;
@@ -724,7 +724,7 @@ static int __devinit jz4740_mmc_request_gpio(struct device *dev, int gpio,
return 0;
}
-static int __devinit jz4740_mmc_request_gpios(struct platform_device *pdev)
+static int jz4740_mmc_request_gpios(struct platform_device *pdev)
{
int ret;
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
@@ -759,7 +759,7 @@ err:
return ret;
}
-static int __devinit jz4740_mmc_request_cd_irq(struct platform_device *pdev,
+static int jz4740_mmc_request_cd_irq(struct platform_device *pdev,
struct jz4740_mmc_host *host)
{
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
@@ -802,7 +802,7 @@ static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host)
return num_pins;
}
-static int __devinit jz4740_mmc_probe(struct platform_device* pdev)
+static int jz4740_mmc_probe(struct platform_device* pdev)
{
int ret;
struct mmc_host *mmc;
@@ -938,7 +938,7 @@ err_free_host:
return ret;
}
-static int __devexit jz4740_mmc_remove(struct platform_device *pdev)
+static int jz4740_mmc_remove(struct platform_device *pdev)
{
struct jz4740_mmc_host *host = platform_get_drvdata(pdev);
@@ -1004,7 +1004,7 @@ const struct dev_pm_ops jz4740_mmc_pm_ops = {
static struct platform_driver jz4740_mmc_driver = {
.probe = jz4740_mmc_probe,
- .remove = __devexit_p(jz4740_mmc_remove),
+ .remove = jz4740_mmc_remove,
.driver = {
.name = "jz4740-mmc",
.owner = THIS_MODULE,
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index a600eabbd6c3..74145d1d51f5 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1485,7 +1485,7 @@ nomem:
}
-static int __devexit mmc_spi_remove(struct spi_device *spi)
+static int mmc_spi_remove(struct spi_device *spi)
{
struct mmc_host *mmc = dev_get_drvdata(&spi->dev);
struct mmc_spi_host *host;
@@ -1517,7 +1517,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi)
return 0;
}
-static struct of_device_id mmc_spi_of_match_table[] __devinitdata = {
+static struct of_device_id mmc_spi_of_match_table[] = {
{ .compatible = "mmc-spi-slot", },
{},
};
@@ -1529,7 +1529,7 @@ static struct spi_driver mmc_spi_driver = {
.of_match_table = mmc_spi_of_match_table,
},
.probe = mmc_spi_probe,
- .remove = __devexit_p(mmc_spi_remove),
+ .remove = mmc_spi_remove,
};
module_spi_driver(mmc_spi_driver);
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index edc3e9baf0e7..372e921389c8 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -20,6 +20,7 @@
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/log2.h>
+#include <linux/mmc/pm.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/amba/bus.h>
@@ -33,6 +34,7 @@
#include <linux/amba/mmci.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
+#include <linux/pinctrl/consumer.h>
#include <asm/div64.h>
#include <asm/io.h>
@@ -58,6 +60,7 @@ static unsigned int fmax = 515633;
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
* @pwrreg_powerup: power up value for MMCIPOWER register
* @signal_direction: input/out direction of bus signals can be indicated
+ * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
*/
struct variant_data {
unsigned int clkreg;
@@ -70,6 +73,7 @@ struct variant_data {
bool blksz_datactrl16;
u32 pwrreg_powerup;
bool signal_direction;
+ bool pwrreg_clkgate;
};
static struct variant_data variant_arm = {
@@ -86,6 +90,14 @@ static struct variant_data variant_arm_extended_fifo = {
.pwrreg_powerup = MCI_PWR_UP,
};
+static struct variant_data variant_arm_extended_fifo_hwfc = {
+ .fifosize = 128 * 4,
+ .fifohalfsize = 64 * 4,
+ .clkreg_enable = MCI_ARM_HWFCEN,
+ .datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
+};
+
static struct variant_data variant_u300 = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
@@ -94,6 +106,7 @@ static struct variant_data variant_u300 = {
.sdio = true,
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
+ .pwrreg_clkgate = true,
};
static struct variant_data variant_nomadik = {
@@ -105,6 +118,7 @@ static struct variant_data variant_nomadik = {
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
+ .pwrreg_clkgate = true,
};
static struct variant_data variant_ux500 = {
@@ -117,6 +131,7 @@ static struct variant_data variant_ux500 = {
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
+ .pwrreg_clkgate = true,
};
static struct variant_data variant_ux500v2 = {
@@ -130,9 +145,28 @@ static struct variant_data variant_ux500v2 = {
.blksz_datactrl16 = true,
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
+ .pwrreg_clkgate = true,
};
/*
+ * Validate mmc prerequisites
+ */
+static int mmci_validate_data(struct mmci_host *host,
+ struct mmc_data *data)
+{
+ if (!data)
+ return 0;
+
+ if (!is_power_of_2(data->blksz)) {
+ dev_err(mmc_dev(host->mmc),
+ "unsupported block size (%d bytes)\n", data->blksz);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
* This must be called with host->lock held
*/
static void mmci_write_clkreg(struct mmci_host *host, u32 clk)
@@ -201,6 +235,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
clk |= MCI_ST_8BIT_BUS;
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+ clk |= MCI_ST_UX500_NEG_EDGE;
+
mmci_write_clkreg(host, clk);
}
@@ -261,7 +298,7 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
* no custom DMA interfaces are supported.
*/
#ifdef CONFIG_DMA_ENGINE
-static void __devinit mmci_dma_setup(struct mmci_host *host)
+static void mmci_dma_setup(struct mmci_host *host)
{
struct mmci_platform_data *plat = host->plat;
const char *rxname, *txname;
@@ -337,7 +374,7 @@ static void __devinit mmci_dma_setup(struct mmci_host *host)
}
/*
- * This is used in __devinit or __devexit so inline it
+ * This is used in or so inline it
* so it can be discarded.
*/
static inline void mmci_dma_release(struct mmci_host *host)
@@ -351,10 +388,33 @@ static inline void mmci_dma_release(struct mmci_host *host)
host->dma_rx_channel = host->dma_tx_channel = NULL;
}
+static void mmci_dma_data_error(struct mmci_host *host)
+{
+ dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+ dmaengine_terminate_all(host->dma_current);
+ host->dma_current = NULL;
+ host->dma_desc_current = NULL;
+ host->data->host_cookie = 0;
+}
+
static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
{
- struct dma_chan *chan = host->dma_current;
+ struct dma_chan *chan;
enum dma_data_direction dir;
+
+ if (data->flags & MMC_DATA_READ) {
+ dir = DMA_FROM_DEVICE;
+ chan = host->dma_rx_channel;
+ } else {
+ dir = DMA_TO_DEVICE;
+ chan = host->dma_tx_channel;
+ }
+
+ dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+}
+
+static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
+{
u32 status;
int i;
@@ -373,19 +433,13 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
* contiguous buffers. On TX, we'll get a FIFO underrun error.
*/
if (status & MCI_RXDATAAVLBLMASK) {
- dmaengine_terminate_all(chan);
+ mmci_dma_data_error(host);
if (!data->error)
data->error = -EIO;
}
- if (data->flags & MMC_DATA_WRITE) {
- dir = DMA_TO_DEVICE;
- } else {
- dir = DMA_FROM_DEVICE;
- }
-
if (!data->host_cookie)
- dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+ mmci_dma_unmap(host, data);
/*
* Use of DMA with scatter-gather is impossible.
@@ -395,16 +449,15 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n");
mmci_dma_release(host);
}
-}
-static void mmci_dma_data_error(struct mmci_host *host)
-{
- dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
- dmaengine_terminate_all(host->dma_current);
+ host->dma_current = NULL;
+ host->dma_desc_current = NULL;
}
-static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
- struct mmci_host_next *next)
+/* prepares DMA channel and DMA descriptor, returns non-zero on failure */
+static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
+ struct dma_chan **dma_chan,
+ struct dma_async_tx_descriptor **dma_desc)
{
struct variant_data *variant = host->variant;
struct dma_slave_config conf = {
@@ -422,16 +475,6 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
enum dma_data_direction buffer_dirn;
int nr_sg;
- /* Check if next job is already prepared */
- if (data->host_cookie && !next &&
- host->dma_current && host->dma_desc_current)
- return 0;
-
- if (!next) {
- host->dma_current = NULL;
- host->dma_desc_current = NULL;
- }
-
if (data->flags & MMC_DATA_READ) {
conf.direction = DMA_DEV_TO_MEM;
buffer_dirn = DMA_FROM_DEVICE;
@@ -461,29 +504,41 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
if (!desc)
goto unmap_exit;
- if (next) {
- next->dma_chan = chan;
- next->dma_desc = desc;
- } else {
- host->dma_current = chan;
- host->dma_desc_current = desc;
- }
+ *dma_chan = chan;
+ *dma_desc = desc;
return 0;
unmap_exit:
- if (!next)
- dmaengine_terminate_all(chan);
dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn);
return -ENOMEM;
}
+static inline int mmci_dma_prep_data(struct mmci_host *host,
+ struct mmc_data *data)
+{
+ /* Check if next job is already prepared. */
+ if (host->dma_current && host->dma_desc_current)
+ return 0;
+
+ /* No job were prepared thus do it now. */
+ return __mmci_dma_prep_data(host, data, &host->dma_current,
+ &host->dma_desc_current);
+}
+
+static inline int mmci_dma_prep_next(struct mmci_host *host,
+ struct mmc_data *data)
+{
+ struct mmci_host_next *nd = &host->next_data;
+ return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc);
+}
+
static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
{
int ret;
struct mmc_data *data = host->data;
- ret = mmci_dma_prep_data(host, host->data, NULL);
+ ret = mmci_dma_prep_data(host, host->data);
if (ret)
return ret;
@@ -513,19 +568,11 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
{
struct mmci_host_next *next = &host->next_data;
- if (data->host_cookie && data->host_cookie != next->cookie) {
- pr_warning("[%s] invalid cookie: data->host_cookie %d"
- " host->next_data.cookie %d\n",
- __func__, data->host_cookie, host->next_data.cookie);
- data->host_cookie = 0;
- }
-
- if (!data->host_cookie)
- return;
+ WARN_ON(data->host_cookie && data->host_cookie != next->cookie);
+ WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan));
host->dma_desc_current = next->dma_desc;
host->dma_current = next->dma_chan;
-
next->dma_desc = NULL;
next->dma_chan = NULL;
}
@@ -540,19 +587,13 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq,
if (!data)
return;
- if (data->host_cookie) {
- data->host_cookie = 0;
+ BUG_ON(data->host_cookie);
+
+ if (mmci_validate_data(host, data))
return;
- }
- /* if config for dma */
- if (((data->flags & MMC_DATA_WRITE) && host->dma_tx_channel) ||
- ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)) {
- if (mmci_dma_prep_data(host, data, nd))
- data->host_cookie = 0;
- else
- data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
- }
+ if (!mmci_dma_prep_next(host, data))
+ data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
}
static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -560,29 +601,23 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
{
struct mmci_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- struct dma_chan *chan;
- enum dma_data_direction dir;
- if (!data)
+ if (!data || !data->host_cookie)
return;
- if (data->flags & MMC_DATA_READ) {
- dir = DMA_FROM_DEVICE;
- chan = host->dma_rx_channel;
- } else {
- dir = DMA_TO_DEVICE;
- chan = host->dma_tx_channel;
- }
+ mmci_dma_unmap(host, data);
+ if (err) {
+ struct mmci_host_next *next = &host->next_data;
+ struct dma_chan *chan;
+ if (data->flags & MMC_DATA_READ)
+ chan = host->dma_rx_channel;
+ else
+ chan = host->dma_tx_channel;
+ dmaengine_terminate_all(chan);
- /* if config for dma */
- if (chan) {
- if (err)
- dmaengine_terminate_all(chan);
- if (data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, dir);
- mrq->data->host_cookie = 0;
+ next->dma_desc = NULL;
+ next->dma_chan = NULL;
}
}
@@ -603,6 +638,11 @@ static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
{
}
+static inline void mmci_dma_finalize(struct mmci_host *host,
+ struct mmc_data *data)
+{
+}
+
static inline void mmci_dma_data_error(struct mmci_host *host)
{
}
@@ -654,9 +694,34 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
/* The ST Micro variants has a special bit to enable SDIO */
if (variant->sdio && host->mmc->card)
- if (mmc_card_sdio(host->mmc->card))
+ if (mmc_card_sdio(host->mmc->card)) {
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO.
+ */
+ u32 clk;
+
datactrl |= MCI_ST_DPSM_SDIOEN;
+ /*
+ * The ST Micro variant for SDIO small write transfers
+ * needs to have clock H/W flow control disabled,
+ * otherwise the transfer will not start. The threshold
+ * depends on the rate of MCLK.
+ */
+ if (data->flags & MMC_DATA_WRITE &&
+ (host->size < 8 ||
+ (host->size <= 8 && host->mclk > 50000000)))
+ clk = host->clk_reg & ~variant->clkreg_enable;
+ else
+ clk = host->clk_reg | variant->clkreg_enable;
+
+ mmci_write_clkreg(host, clk);
+ }
+
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+ datactrl |= MCI_ST_DPSM_DDRMODE;
+
/*
* Attempt to use DMA operation mode, if this
* should fail, fall back to PIO mode
@@ -728,8 +793,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
u32 remain, success;
/* Terminate the DMA transfer */
- if (dma_inprogress(host))
+ if (dma_inprogress(host)) {
mmci_dma_data_error(host);
+ mmci_dma_unmap(host, data);
+ }
/*
* Calculate how far we are into the transfer. Note that
@@ -768,7 +835,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
if (status & MCI_DATAEND || data->error) {
if (dma_inprogress(host))
- mmci_dma_unmap(host, data);
+ mmci_dma_finalize(host, data);
mmci_stop_data(host);
if (!data->error)
@@ -805,8 +872,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
if (!cmd->data || cmd->error) {
if (host->data) {
/* Terminate the DMA transfer */
- if (dma_inprogress(host))
+ if (dma_inprogress(host)) {
mmci_dma_data_error(host);
+ mmci_dma_unmap(host, host->data);
+ }
mmci_stop_data(host);
}
mmci_request_end(host, cmd->mrq);
@@ -840,14 +909,14 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
if (unlikely(count & 0x3)) {
if (count < 4) {
unsigned char buf[4];
- readsl(base + MMCIFIFO, buf, 1);
+ ioread32_rep(base + MMCIFIFO, buf, 1);
memcpy(ptr, buf, count);
} else {
- readsl(base + MMCIFIFO, ptr, count >> 2);
+ ioread32_rep(base + MMCIFIFO, ptr, count >> 2);
count &= ~0x3;
}
} else {
- readsl(base + MMCIFIFO, ptr, count >> 2);
+ ioread32_rep(base + MMCIFIFO, ptr, count >> 2);
}
ptr += count;
@@ -877,22 +946,6 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
count = min(remain, maxcnt);
/*
- * The ST Micro variant for SDIO transfer sizes
- * less then 8 bytes should have clock H/W flow
- * control disabled.
- */
- if (variant->sdio &&
- mmc_card_sdio(host->mmc->card)) {
- u32 clk;
- if (count < 8)
- clk = host->clk_reg & ~variant->clkreg_enable;
- else
- clk = host->clk_reg | variant->clkreg_enable;
-
- mmci_write_clkreg(host, clk);
- }
-
- /*
* SDIO especially may want to send something that is
* not divisible by 4 (as opposed to card sectors
* etc), and the FIFO only accept full 32-bit writes.
@@ -900,7 +953,7 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
* byte become a 32bit write, 7 bytes will be two
* 32bit writes etc.
*/
- writesl(base + MMCIFIFO, ptr, (count + 3) >> 2);
+ iowrite32_rep(base + MMCIFIFO, ptr, (count + 3) >> 2);
ptr += count;
remain -= count;
@@ -1048,10 +1101,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(host->mrq != NULL);
- if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
- dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n",
- mrq->data->blksz);
- mrq->cmd->error = -EINVAL;
+ mrq->cmd->error = mmci_validate_data(host, mrq->data);
+ if (mrq->cmd->error) {
mmc_request_done(mmc, mrq);
return;
}
@@ -1079,7 +1130,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct variant_data *variant = host->variant;
u32 pwr = 0;
unsigned long flags;
- int ret;
pm_runtime_get_sync(mmc_dev(mmc));
@@ -1089,23 +1139,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->power_mode) {
case MMC_POWER_OFF:
- if (host->vcc)
- ret = mmc_regulator_set_ocr(mmc, host->vcc, 0);
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
break;
case MMC_POWER_UP:
- if (host->vcc) {
- ret = mmc_regulator_set_ocr(mmc, host->vcc, ios->vdd);
- if (ret) {
- dev_err(mmc_dev(mmc), "unable to set OCR\n");
- /*
- * The .set_ios() function in the mmc_host_ops
- * struct return void, and failing to set the
- * power should be rare so we print an error
- * and return here.
- */
- goto out;
- }
- }
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
+
/*
* The ST Micro variant doesn't have the PL180s MCI_PWR_UP
* and instead uses MCI_PWR_ON so apply whatever value is
@@ -1147,6 +1187,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
+ /*
+ * If clock = 0 and the variant requires the MMCIPOWER to be used for
+ * gating the clock, the MCI_PWR_ON bit is cleared.
+ */
+ if (!ios->clock && variant->pwrreg_clkgate)
+ pwr &= ~MCI_PWR_ON;
+
spin_lock_irqsave(&host->lock, flags);
mmci_set_clkreg(host, ios->clock);
@@ -1154,7 +1201,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags);
- out:
pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
}
@@ -1255,7 +1301,7 @@ static void mmci_dt_populate_generic_pdata(struct device_node *np,
}
#endif
-static int __devinit mmci_probe(struct amba_device *dev,
+static int mmci_probe(struct amba_device *dev,
const struct amba_id *id)
{
struct mmci_platform_data *plat = dev->dev.platform_data;
@@ -1360,32 +1406,36 @@ static int __devinit mmci_probe(struct amba_device *dev,
mmc->f_max = min(host->mclk, fmax);
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
-#ifdef CONFIG_REGULATOR
- /* If we're using the regulator framework, try to fetch a regulator */
- host->vcc = regulator_get(&dev->dev, "vmmc");
- if (IS_ERR(host->vcc))
- host->vcc = NULL;
- else {
- int mask = mmc_regulator_get_ocrmask(host->vcc);
-
- if (mask < 0)
- dev_err(&dev->dev, "error getting OCR mask (%d)\n",
- mask);
- else {
- host->mmc->ocr_avail = (u32) mask;
- if (plat->ocr_mask)
- dev_warn(&dev->dev,
- "Provided ocr_mask/setpower will not be used "
- "(using regulator instead)\n");
- }
+ host->pinctrl = devm_pinctrl_get(&dev->dev);
+ if (IS_ERR(host->pinctrl)) {
+ ret = PTR_ERR(host->pinctrl);
+ goto clk_disable;
}
-#endif
- /* Fall back to platform data if no regulator is found */
- if (host->vcc == NULL)
+
+ host->pins_default = pinctrl_lookup_state(host->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+
+ /* enable pins to be muxed in and configured */
+ if (!IS_ERR(host->pins_default)) {
+ ret = pinctrl_select_state(host->pinctrl, host->pins_default);
+ if (ret)
+ dev_warn(&dev->dev, "could not set default pins\n");
+ } else
+ dev_warn(&dev->dev, "could not get default pinstate\n");
+
+ /* Get regulators and the supported OCR mask */
+ mmc_regulator_get_supply(mmc);
+ if (!mmc->ocr_avail)
mmc->ocr_avail = plat->ocr_mask;
+ else if (plat->ocr_mask)
+ dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
+
mmc->caps = plat->capabilities;
mmc->caps2 = plat->capabilities2;
+ /* We support these PM capabilities. */
+ mmc->pm_caps = MMC_PM_KEEP_POWER;
+
/*
* We can do SGIO
*/
@@ -1522,7 +1572,7 @@ static int __devinit mmci_probe(struct amba_device *dev,
return ret;
}
-static int __devexit mmci_remove(struct amba_device *dev)
+static int mmci_remove(struct amba_device *dev)
{
struct mmc_host *mmc = amba_get_drvdata(dev);
@@ -1561,10 +1611,6 @@ static int __devexit mmci_remove(struct amba_device *dev)
clk_disable_unprepare(host->clk);
clk_put(host->clk);
- if (host->vcc)
- mmc_regulator_set_ocr(mmc, host->vcc, 0);
- regulator_put(host->vcc);
-
mmc_free_host(mmc);
amba_release_regions(dev);
@@ -1612,8 +1658,37 @@ static int mmci_resume(struct device *dev)
}
#endif
+#ifdef CONFIG_PM_RUNTIME
+static int mmci_runtime_suspend(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+ clk_disable_unprepare(host->clk);
+ }
+
+ return 0;
+}
+
+static int mmci_runtime_resume(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+ clk_prepare_enable(host->clk);
+ }
+
+ return 0;
+}
+#endif
+
static const struct dev_pm_ops mmci_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume)
+ SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)
};
static struct amba_id mmci_ids[] = {
@@ -1628,6 +1703,11 @@ static struct amba_id mmci_ids[] = {
.data = &variant_arm_extended_fifo,
},
{
+ .id = 0x02041180,
+ .mask = 0xff0fffff,
+ .data = &variant_arm_extended_fifo_hwfc,
+ },
+ {
.id = 0x00041181,
.mask = 0x000fffff,
.data = &variant_arm,
@@ -1669,7 +1749,7 @@ static struct amba_driver mmci_driver = {
.pm = &mmci_dev_pm_ops,
},
.probe = mmci_probe,
- .remove = __devexit_p(mmci_remove),
+ .remove = mmci_remove,
.id_table = mmci_ids,
};
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index d437ccf62d6b..1f33ad5333a0 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -28,6 +28,8 @@
#define MCI_ST_UX500_NEG_EDGE (1 << 13)
#define MCI_ST_UX500_HWFCEN (1 << 14)
#define MCI_ST_UX500_CLK_INV (1 << 15)
+/* Modified PL180 on Versatile Express platform */
+#define MCI_ARM_HWFCEN (1 << 12)
#define MMCIARGUMENT 0x008
#define MMCICOMMAND 0x00c
@@ -193,7 +195,10 @@ struct mmci_host {
/* pio stuff */
struct sg_mapping_iter sg_miter;
unsigned int size;
- struct regulator *vcc;
+
+ /* pinctrl handles */
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
#ifdef CONFIG_DMA_ENGINE
/* DMA stuff */
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index de4c20b3936c..145cdaf000d1 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -21,7 +21,11 @@
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/pinctrl/consumer.h>
#include <asm/sizes.h>
#include <asm/unaligned.h>
@@ -50,11 +54,7 @@ struct mvsd_host {
struct timer_list timer;
struct mmc_host *mmc;
struct device *dev;
- struct resource *res;
- int irq;
struct clk *clk;
- int gpio_card_detect;
- int gpio_write_protect;
};
#define mvsd_write(offs, val) writel(val, iobase + (offs))
@@ -540,13 +540,6 @@ static void mvsd_timeout_timer(unsigned long data)
mmc_request_done(host->mmc, mrq);
}
-static irqreturn_t mvsd_card_detect_irq(int irq, void *dev)
-{
- struct mvsd_host *host = dev;
- mmc_detect_change(host->mmc, msecs_to_jiffies(100));
- return IRQ_HANDLED;
-}
-
static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct mvsd_host *host = mmc_priv(mmc);
@@ -566,20 +559,6 @@ static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}
-static int mvsd_get_ro(struct mmc_host *mmc)
-{
- struct mvsd_host *host = mmc_priv(mmc);
-
- if (host->gpio_write_protect)
- return gpio_get_value(host->gpio_write_protect);
-
- /*
- * Board doesn't support read only detection; let the mmc core
- * decide what to do.
- */
- return -ENOSYS;
-}
-
static void mvsd_power_up(struct mvsd_host *host)
{
void __iomem *iobase = host->base;
@@ -676,7 +655,7 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static const struct mmc_host_ops mvsd_ops = {
.request = mvsd_request,
- .get_ro = mvsd_get_ro,
+ .get_ro = mmc_gpio_get_ro,
.set_ios = mvsd_set_ios,
.enable_sdio_irq = mvsd_enable_sdio_irq,
};
@@ -705,23 +684,20 @@ mv_conf_mbus_windows(struct mvsd_host *host,
static int __init mvsd_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct mmc_host *mmc = NULL;
struct mvsd_host *host = NULL;
- const struct mvsdio_platform_data *mvsd_data;
const struct mbus_dram_target_info *dram;
struct resource *r;
int ret, irq;
+ int gpio_card_detect, gpio_write_protect;
+ struct pinctrl *pinctrl;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- mvsd_data = pdev->dev.platform_data;
- if (!r || irq < 0 || !mvsd_data)
+ if (!r || irq < 0)
return -ENXIO;
- r = request_mem_region(r->start, SZ_1K, DRIVER_NAME);
- if (!r)
- return -EBUSY;
-
mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
@@ -731,8 +707,43 @@ static int __init mvsd_probe(struct platform_device *pdev)
host = mmc_priv(mmc);
host->mmc = mmc;
host->dev = &pdev->dev;
- host->res = r;
- host->base_clock = mvsd_data->clock / 2;
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev, "no pins associated\n");
+
+ /*
+ * Some non-DT platforms do not pass a clock, and the clock
+ * frequency is passed through platform_data. On DT platforms,
+ * a clock must always be passed, even if there is no gatable
+ * clock associated to the SDIO interface (it can simply be a
+ * fixed rate clock).
+ */
+ host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(host->clk))
+ clk_prepare_enable(host->clk);
+
+ if (np) {
+ if (IS_ERR(host->clk)) {
+ dev_err(&pdev->dev, "DT platforms must have a clock associated\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ host->base_clock = clk_get_rate(host->clk) / 2;
+ gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0);
+ gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0);
+ } else {
+ const struct mvsdio_platform_data *mvsd_data;
+ mvsd_data = pdev->dev.platform_data;
+ if (!mvsd_data) {
+ ret = -ENXIO;
+ goto out;
+ }
+ host->base_clock = mvsd_data->clock / 2;
+ gpio_card_detect = mvsd_data->gpio_card_detect;
+ gpio_write_protect = mvsd_data->gpio_write_protect;
+ }
mmc->ops = &mvsd_ops;
@@ -752,7 +763,7 @@ static int __init mvsd_probe(struct platform_device *pdev)
spin_lock_init(&host->lock);
- host->base = ioremap(r->start, SZ_4K);
+ host->base = devm_request_and_ioremap(&pdev->dev, r);
if (!host->base) {
ret = -ENOMEM;
goto out;
@@ -765,48 +776,20 @@ static int __init mvsd_probe(struct platform_device *pdev)
mvsd_power_down(host);
- ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
+ ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host);
if (ret) {
pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq);
goto out;
- } else
- host->irq = irq;
-
- /* Not all platforms can gate the clock, so it is not
- an error if the clock does not exists. */
- host->clk = clk_get(&pdev->dev, NULL);
- if (!IS_ERR(host->clk)) {
- clk_prepare_enable(host->clk);
}
- if (mvsd_data->gpio_card_detect) {
- ret = gpio_request(mvsd_data->gpio_card_detect,
- DRIVER_NAME " cd");
- if (ret == 0) {
- gpio_direction_input(mvsd_data->gpio_card_detect);
- irq = gpio_to_irq(mvsd_data->gpio_card_detect);
- ret = request_irq(irq, mvsd_card_detect_irq,
- IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
- DRIVER_NAME " cd", host);
- if (ret == 0)
- host->gpio_card_detect =
- mvsd_data->gpio_card_detect;
- else
- gpio_free(mvsd_data->gpio_card_detect);
- }
- }
- if (!host->gpio_card_detect)
+ if (gpio_is_valid(gpio_card_detect)) {
+ ret = mmc_gpio_request_cd(mmc, gpio_card_detect);
+ if (ret)
+ goto out;
+ } else
mmc->caps |= MMC_CAP_NEEDS_POLL;
- if (mvsd_data->gpio_write_protect) {
- ret = gpio_request(mvsd_data->gpio_write_protect,
- DRIVER_NAME " wp");
- if (ret == 0) {
- gpio_direction_input(mvsd_data->gpio_write_protect);
- host->gpio_write_protect =
- mvsd_data->gpio_write_protect;
- }
- }
+ mmc_gpio_request_ro(mmc, gpio_write_protect);
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
platform_set_drvdata(pdev, mmc);
@@ -816,34 +799,21 @@ static int __init mvsd_probe(struct platform_device *pdev)
pr_notice("%s: %s driver initialized, ",
mmc_hostname(mmc), DRIVER_NAME);
- if (host->gpio_card_detect)
+ if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
printk("using GPIO %d for card detection\n",
- host->gpio_card_detect);
+ gpio_card_detect);
else
printk("lacking card detect (fall back to polling)\n");
return 0;
out:
- if (host) {
- if (host->irq)
- free_irq(host->irq, host);
- if (host->gpio_card_detect) {
- free_irq(gpio_to_irq(host->gpio_card_detect), host);
- gpio_free(host->gpio_card_detect);
- }
- if (host->gpio_write_protect)
- gpio_free(host->gpio_write_protect);
- if (host->base)
- iounmap(host->base);
- }
- if (r)
- release_resource(r);
- if (mmc)
- if (!IS_ERR_OR_NULL(host->clk)) {
+ if (mmc) {
+ mmc_gpio_free_cd(mmc);
+ mmc_gpio_free_ro(mmc);
+ if (!IS_ERR(host->clk))
clk_disable_unprepare(host->clk);
- clk_put(host->clk);
- }
mmc_free_host(mmc);
+ }
return ret;
}
@@ -852,28 +822,18 @@ static int __exit mvsd_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
- if (mmc) {
- struct mvsd_host *host = mmc_priv(mmc);
+ struct mvsd_host *host = mmc_priv(mmc);
- if (host->gpio_card_detect) {
- free_irq(gpio_to_irq(host->gpio_card_detect), host);
- gpio_free(host->gpio_card_detect);
- }
- mmc_remove_host(mmc);
- free_irq(host->irq, host);
- if (host->gpio_write_protect)
- gpio_free(host->gpio_write_protect);
- del_timer_sync(&host->timer);
- mvsd_power_down(host);
- iounmap(host->base);
- release_resource(host->res);
+ mmc_gpio_free_cd(mmc);
+ mmc_gpio_free_ro(mmc);
+ mmc_remove_host(mmc);
+ del_timer_sync(&host->timer);
+ mvsd_power_down(host);
+
+ if (!IS_ERR(host->clk))
+ clk_disable_unprepare(host->clk);
+ mmc_free_host(mmc);
- if (!IS_ERR(host->clk)) {
- clk_disable_unprepare(host->clk);
- clk_put(host->clk);
- }
- mmc_free_host(mmc);
- }
platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -905,12 +865,19 @@ static int mvsd_resume(struct platform_device *dev)
#define mvsd_resume NULL
#endif
+static const struct of_device_id mvsdio_dt_ids[] = {
+ { .compatible = "marvell,orion-sdio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
+
static struct platform_driver mvsd_driver = {
.remove = __exit_p(mvsd_remove),
.suspend = mvsd_suspend,
.resume = mvsd_resume,
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = mvsdio_dt_ids,
},
};
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 565c2e4fac75..a72936eea6fa 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -41,7 +41,6 @@
#include <linux/platform_data/mmc-mxcmmc.h>
#include <linux/platform_data/dma-imx.h>
-#include <mach/hardware.h>
#define DRIVER_NAME "mxc-mmc"
#define MXCMCI_TIMEOUT_MS 10000
@@ -113,6 +112,11 @@
#define INT_WRITE_OP_DONE_EN (1 << 1)
#define INT_READ_OP_EN (1 << 0)
+enum mxcmci_type {
+ IMX21_MMC,
+ IMX31_MMC,
+};
+
struct mxcmci_host {
struct mmc_host *mmc;
struct resource *res;
@@ -153,7 +157,26 @@ struct mxcmci_host {
struct imx_dma_data dma_data;
struct timer_list watchdog;
+ enum mxcmci_type devtype;
+};
+
+static struct platform_device_id mxcmci_devtype[] = {
+ {
+ .name = "imx21-mmc",
+ .driver_data = IMX21_MMC,
+ }, {
+ .name = "imx31-mmc",
+ .driver_data = IMX31_MMC,
+ }, {
+ /* sentinel */
+ }
};
+MODULE_DEVICE_TABLE(platform, mxcmci_devtype);
+
+static inline int is_imx31_mmc(struct mxcmci_host *host)
+{
+ return host->devtype == IMX31_MMC;
+}
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
@@ -240,7 +263,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
return 0;
for_each_sg(data->sg, sg, data->sg_len, i) {
- if (sg->offset & 3 || sg->length & 3) {
+ if (sg->offset & 3 || sg->length & 3 || sg->length < 512) {
host->do_dma = 0;
return 0;
}
@@ -843,6 +866,8 @@ static void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card)
{
+ struct mxcmci_host *mxcmci = mmc_priv(host);
+
/*
* MX3 SoCs have a silicon bug which corrupts CRC calculation of
* multi-block transfers when connected SDIO peripheral doesn't
@@ -850,7 +875,7 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card)
* One way to prevent this is to only allow 1-bit transfers.
*/
- if (cpu_is_mx3() && card->type == MMC_TYPE_SDIO)
+ if (is_imx31_mmc(mxcmci) && card->type == MMC_TYPE_SDIO)
host->caps &= ~MMC_CAP_4_BIT_DATA;
else
host->caps |= MMC_CAP_4_BIT_DATA;
@@ -948,6 +973,7 @@ static int mxcmci_probe(struct platform_device *pdev)
host->mmc = mmc;
host->pdata = pdev->dev.platform_data;
+ host->devtype = pdev->id_entry->driver_data;
spin_lock_init(&host->lock);
mxcmci_init_ocr(host);
@@ -1120,6 +1146,7 @@ static const struct dev_pm_ops mxcmci_pm_ops = {
static struct platform_driver mxcmci_driver = {
.probe = mxcmci_probe,
.remove = mxcmci_remove,
+ .id_table = mxcmci_devtype,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
@@ -1134,4 +1161,4 @@ module_platform_driver(mxcmci_driver);
MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx-mmc");
+MODULE_ALIAS("platform:mxc-mmc");
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 80d1e6d4b0ae..4efe3021b217 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -43,7 +43,6 @@
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/stmp_device.h>
-#include <linux/mmc/mxs-mmc.h>
#include <linux/spi/mxs-spi.h>
#define DRIVER_NAME "mxs-mmc"
@@ -355,7 +354,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
struct dma_async_tx_descriptor *desc;
struct scatterlist *sgl = data->sg, *sg;
unsigned int sg_len = data->sg_len;
- int i;
+ unsigned int i;
unsigned short dma_data_dir, timeout;
enum dma_transfer_direction slave_dirn;
@@ -593,13 +592,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
struct mxs_mmc_host *host;
struct mmc_host *mmc;
struct resource *iores, *dmares;
- struct mxs_mmc_platform_data *pdata;
struct pinctrl *pinctrl;
int ret = 0, irq_err, irq_dma;
dma_cap_mask_t mask;
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);
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -615,9 +614,9 @@ static int mxs_mmc_probe(struct platform_device *pdev)
host = mmc_priv(mmc);
ssp = &host->ssp;
ssp->dev = &pdev->dev;
- ssp->base = devm_request_and_ioremap(&pdev->dev, iores);
- if (!ssp->base) {
- ret = -EADDRNOTAVAIL;
+ ssp->base = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(ssp->base)) {
+ ret = PTR_ERR(ssp->base);
goto out_mmc_free;
}
@@ -682,25 +681,15 @@ 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;
- pdata = mmc_dev(host->mmc)->platform_data;
- if (!pdata) {
- u32 bus_width = 0;
- 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->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0,
- &flags);
- if (flags & OF_GPIO_ACTIVE_LOW)
- host->wp_inverted = 1;
- } else {
- if (pdata->flags & SLOTF_8_BIT_CAPABLE)
- mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
- if (pdata->flags & SLOTF_4_BIT_CAPABLE)
- mmc->caps |= MMC_CAP_4_BIT_DATA;
- host->wp_gpio = pdata->wp_gpio;
- }
+ 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->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
+
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ host->wp_inverted = 1;
mmc->f_min = 400000;
mmc->f_max = 288000000;
@@ -815,3 +804,4 @@ module_platform_driver(mxs_mmc_driver);
MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral");
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c
index 1534b582c419..d720b5e05b9c 100644
--- a/drivers/mmc/host/of_mmc_spi.c
+++ b/drivers/mmc/host/of_mmc_spi.c
@@ -146,7 +146,7 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
oms->pdata.get_ro = of_mmc_spi_get_ro;
oms->detect_irq = irq_of_parse_and_map(np, 0);
- if (oms->detect_irq != NO_IRQ) {
+ if (oms->detect_irq != 0) {
oms->pdata.init = of_mmc_spi_init;
oms->pdata.exit = of_mmc_spi_exit;
} else {
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 48ad361613ef..4254975f931d 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -28,9 +28,8 @@
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/platform_data/mmc-omap.h>
-#include <plat/mmc.h>
-#include <plat/dma.h>
#define OMAP_MMC_REG_CMD 0x00
#define OMAP_MMC_REG_ARGL 0x01
@@ -72,6 +71,13 @@
#define OMAP_MMC_STAT_CARD_BUSY (1 << 2)
#define OMAP_MMC_STAT_END_OF_CMD (1 << 0)
+#define mmc_omap7xx() (host->features & MMC_OMAP7XX)
+#define mmc_omap15xx() (host->features & MMC_OMAP15XX)
+#define mmc_omap16xx() (host->features & MMC_OMAP16XX)
+#define MMC_OMAP1_MASK (MMC_OMAP7XX | MMC_OMAP15XX | MMC_OMAP16XX)
+#define mmc_omap1() (host->features & MMC_OMAP1_MASK)
+#define mmc_omap2() (!mmc_omap1())
+
#define OMAP_MMC_REG(host, reg) (OMAP_MMC_REG_##reg << (host)->reg_shift)
#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG(host, reg))
#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG(host, reg))
@@ -84,6 +90,16 @@
#define OMAP_MMC_CMDTYPE_AC 2
#define OMAP_MMC_CMDTYPE_ADTC 3
+#define OMAP_DMA_MMC_TX 21
+#define OMAP_DMA_MMC_RX 22
+#define OMAP_DMA_MMC2_TX 54
+#define OMAP_DMA_MMC2_RX 55
+
+#define OMAP24XX_DMA_MMC2_TX 47
+#define OMAP24XX_DMA_MMC2_RX 48
+#define OMAP24XX_DMA_MMC1_TX 61
+#define OMAP24XX_DMA_MMC1_RX 62
+
#define DRIVER_NAME "mmci-omap"
@@ -147,6 +163,7 @@ struct mmc_omap_host {
u32 buffer_bytes_left;
u32 total_bytes_left;
+ unsigned features;
unsigned use_dma:1;
unsigned brs_received:1, dma_done:1;
unsigned dma_in_use:1;
@@ -988,7 +1005,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
* blocksize is at least that large. Blocksize is
* usually 512 bytes; but not for some SD reads.
*/
- burst = cpu_is_omap15xx() ? 32 : 64;
+ burst = mmc_omap15xx() ? 32 : 64;
if (burst > data->blksz)
burst = data->blksz;
@@ -1104,8 +1121,7 @@ static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on,
if (slot->pdata->set_power != NULL)
slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on,
vdd);
-
- if (cpu_is_omap24xx()) {
+ if (mmc_omap2()) {
u16 w;
if (power_on) {
@@ -1214,7 +1230,7 @@ static const struct mmc_host_ops mmc_omap_ops = {
.set_ios = mmc_omap_set_ios,
};
-static int __devinit mmc_omap_new_slot(struct mmc_omap_host *host, int id)
+static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
{
struct mmc_omap_slot *slot = NULL;
struct mmc_host *mmc;
@@ -1239,7 +1255,7 @@ static int __devinit mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->ops = &mmc_omap_ops;
mmc->f_min = 400000;
- if (cpu_class_is_omap2())
+ if (mmc_omap2())
mmc->f_max = 48000000;
else
mmc->f_max = 24000000;
@@ -1309,7 +1325,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
mmc_free_host(mmc);
}
-static int __devinit mmc_omap_probe(struct platform_device *pdev)
+static int mmc_omap_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_omap_host *host = NULL;
@@ -1359,6 +1375,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
init_waitqueue_head(&host->slot_wq);
host->pdata = pdata;
+ host->features = host->pdata->slots[0].features;
host->dev = &pdev->dev;
platform_set_drvdata(pdev, host);
@@ -1391,7 +1408,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
host->dma_tx_burst = -1;
host->dma_rx_burst = -1;
- if (cpu_is_omap24xx())
+ if (mmc_omap2())
sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
else
sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
@@ -1407,7 +1424,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
sig);
#endif
- if (cpu_is_omap24xx())
+ if (mmc_omap2())
sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
else
sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
@@ -1435,7 +1452,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
}
host->nr_slots = pdata->nr_slots;
- host->reg_shift = (cpu_is_omap7xx() ? 1 : 2);
+ host->reg_shift = (mmc_omap7xx() ? 1 : 2);
host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
if (!host->mmc_omap_wq)
@@ -1478,7 +1495,7 @@ err_free_mem_region:
return ret;
}
-static int __devexit mmc_omap_remove(struct platform_device *pdev)
+static int mmc_omap_remove(struct platform_device *pdev)
{
struct mmc_omap_host *host = platform_get_drvdata(pdev);
int i;
@@ -1566,7 +1583,7 @@ static int mmc_omap_resume(struct platform_device *pdev)
static struct platform_driver mmc_omap_driver = {
.probe = mmc_omap_probe,
- .remove = __devexit_p(mmc_omap_remove),
+ .remove = mmc_omap_remove,
.suspend = mmc_omap_suspend,
.resume = mmc_omap_resume,
.driver = {
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 54bfd0cc106b..bc5807873b2c 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -37,10 +37,9 @@
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
-#include <mach/hardware.h>
-#include <plat/mmc.h>
-#include <plat/cpu.h>
+#include <linux/platform_data/mmc-omap.h>
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
@@ -62,6 +61,7 @@
#define VS18 (1 << 26)
#define VS30 (1 << 25)
+#define HSS (1 << 21)
#define SDVS18 (0x5 << 9)
#define SDVS30 (0x6 << 9)
#define SDVS33 (0x7 << 9)
@@ -78,28 +78,17 @@
#define CLKD_SHIFT 6
#define DTO_MASK 0x000F0000
#define DTO_SHIFT 16
-#define INT_EN_MASK 0x307F0033
-#define BWR_ENABLE (1 << 4)
-#define BRR_ENABLE (1 << 5)
-#define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
-#define DMA_EN 0x1
+#define DMAE 0x1
#define MSBS (1 << 5)
#define BCE (1 << 1)
#define FOUR_BIT (1 << 1)
+#define HSPE (1 << 2)
#define DDR (1 << 19)
#define DW8 (1 << 5)
-#define CC 0x1
-#define TC 0x02
#define OD 0x1
-#define ERR (1 << 15)
-#define CMD_TIMEOUT (1 << 16)
-#define DATA_TIMEOUT (1 << 20)
-#define CMD_CRC (1 << 17)
-#define DATA_CRC (1 << 21)
-#define CARD_ERR (1 << 28)
#define STAT_CLEAR 0xFFFFFFFF
#define INIT_STREAM_CMD 0x00000000
#define DUAL_VOLT_OCR_BIT 7
@@ -108,6 +97,26 @@
#define SOFTRESET (1 << 1)
#define RESETDONE (1 << 0)
+/* Interrupt masks for IE and ISE register */
+#define CC_EN (1 << 0)
+#define TC_EN (1 << 1)
+#define BWR_EN (1 << 4)
+#define BRR_EN (1 << 5)
+#define ERR_EN (1 << 15)
+#define CTO_EN (1 << 16)
+#define CCRC_EN (1 << 17)
+#define CEB_EN (1 << 18)
+#define CIE_EN (1 << 19)
+#define DTO_EN (1 << 20)
+#define DCRC_EN (1 << 21)
+#define DEB_EN (1 << 22)
+#define CERR_EN (1 << 28)
+#define BADA_EN (1 << 29)
+
+#define INT_EN_MASK (BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\
+ DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
+ BRR_EN | BWR_EN | TC_EN | CC_EN)
+
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MIN_CLOCK 400000
@@ -178,7 +187,8 @@ struct omap_hsmmc_host {
static int omap_hsmmc_card_detect(struct device *dev, int slot)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+ struct omap_mmc_platform_data *mmc = host->pdata;
/* NOTE: assumes card detect signal is active-low */
return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
@@ -186,7 +196,8 @@ static int omap_hsmmc_card_detect(struct device *dev, int slot)
static int omap_hsmmc_get_wp(struct device *dev, int slot)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+ struct omap_mmc_platform_data *mmc = host->pdata;
/* NOTE: assumes write protect signal is active-high */
return gpio_get_value_cansleep(mmc->slots[0].gpio_wp);
@@ -194,7 +205,8 @@ static int omap_hsmmc_get_wp(struct device *dev, int slot)
static int omap_hsmmc_get_cover_state(struct device *dev, int slot)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+ struct omap_mmc_platform_data *mmc = host->pdata;
/* NOTE: assumes card detect signal is active-low */
return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
@@ -204,7 +216,8 @@ static int omap_hsmmc_get_cover_state(struct device *dev, int slot)
static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+ struct omap_mmc_platform_data *mmc = host->pdata;
disable_irq(mmc->slots[0].card_detect_irq);
return 0;
@@ -212,7 +225,8 @@ static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)
static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+ struct omap_mmc_platform_data *mmc = host->pdata;
enable_irq(mmc->slots[0].card_detect_irq);
return 0;
@@ -297,7 +311,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
reg = regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) {
- dev_dbg(host->dev, "vmmc regulator missing\n");
+ dev_err(host->dev, "vmmc regulator missing\n");
return PTR_ERR(reg);
} else {
mmc_slot(host).set_power = omap_hsmmc_set_power;
@@ -450,13 +464,13 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
unsigned int irq_mask;
if (host->use_dma)
- irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE);
+ irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN);
else
irq_mask = INT_EN_MASK;
/* Disable timeout for erases */
if (cmd->opcode == MMC_ERASE)
- irq_mask &= ~DTO_ENABLE;
+ irq_mask &= ~DTO_EN;
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
@@ -489,6 +503,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
struct mmc_ios *ios = &host->mmc->ios;
unsigned long regval;
unsigned long timeout;
+ unsigned long clkdiv;
dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
@@ -496,7 +511,8 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
regval = regval & ~(CLKD_MASK | DTO_MASK);
- regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16);
+ clkdiv = calc_divisor(host, ios);
+ regval = regval | (clkdiv << 6) | (DTO << 16);
OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
@@ -507,6 +523,27 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
&& time_before(jiffies, timeout))
cpu_relax();
+ /*
+ * Enable High-Speed Support
+ * Pre-Requisites
+ * - Controller should support High-Speed-Enable Bit
+ * - Controller should not be using DDR Mode
+ * - Controller should advertise that it supports High Speed
+ * in capabilities register
+ * - MMC/SD clock coming out of controller > 25MHz
+ */
+ if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) &&
+ (ios->timing != MMC_TIMING_UHS_DDR50) &&
+ ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {
+ regval = OMAP_HSMMC_READ(host->base, HCTL);
+ if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000)
+ regval |= HSPE;
+ else
+ regval &= ~HSPE;
+
+ OMAP_HSMMC_WRITE(host->base, HCTL, regval);
+ }
+
omap_hsmmc_start_clock(host);
}
@@ -671,8 +708,8 @@ static void send_init_stream(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((reg != CC) && time_before(jiffies, timeout))
- reg = OMAP_HSMMC_READ(host->base, STAT) & CC;
+ while ((reg != CC_EN) && time_before(jiffies, timeout))
+ reg = OMAP_HSMMC_READ(host->base, STAT) & CC_EN;
OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM);
@@ -763,7 +800,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
}
if (host->use_dma)
- cmdreg |= DMA_EN;
+ cmdreg |= DMAE;
host->req_in_progress = 1;
@@ -963,16 +1000,20 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
__func__);
}
-static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, int err)
+static void hsmmc_command_incomplete(struct omap_hsmmc_host *host,
+ int err, int end_cmd)
{
- omap_hsmmc_reset_controller_fsm(host, SRC);
- host->cmd->error = err;
+ if (end_cmd) {
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ if (host->cmd)
+ host->cmd->error = err;
+ }
if (host->data) {
omap_hsmmc_reset_controller_fsm(host, SRD);
omap_hsmmc_dma_cleanup(host, err);
- }
-
+ } else if (host->mrq && host->mrq->cmd)
+ host->mrq->cmd->error = err;
}
static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
@@ -983,23 +1024,25 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
data = host->data;
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
- if (status & ERR) {
+ if (status & ERR_EN) {
omap_hsmmc_dbg_report_irq(host, status);
- if (status & (CMD_TIMEOUT | DATA_TIMEOUT))
- hsmmc_command_incomplete(host, -ETIMEDOUT);
- else if (status & (CMD_CRC | DATA_CRC))
- hsmmc_command_incomplete(host, -EILSEQ);
- end_cmd = 1;
+ if (status & (CTO_EN | CCRC_EN))
+ end_cmd = 1;
+ if (status & (CTO_EN | DTO_EN))
+ hsmmc_command_incomplete(host, -ETIMEDOUT, end_cmd);
+ else if (status & (CCRC_EN | DCRC_EN))
+ hsmmc_command_incomplete(host, -EILSEQ, end_cmd);
+
if (host->data || host->response_busy) {
- end_trans = 1;
+ end_trans = !end_cmd;
host->response_busy = 0;
}
}
- if (end_cmd || ((status & CC) && host->cmd))
+ if (end_cmd || ((status & CC_EN) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
- if ((end_trans || (status & TC)) && host->mrq)
+ if ((end_trans || (status & TC_EN)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
}
@@ -1096,7 +1139,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
return 0;
err:
- dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
+ dev_err(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
return ret;
}
@@ -1355,7 +1398,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
if (host->use_dma) {
ret = omap_hsmmc_start_dma_transfer(host, req);
if (ret != 0) {
- dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
+ dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
return ret;
}
}
@@ -1673,7 +1716,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
{
struct omap_mmc_platform_data *pdata;
struct device_node *np = dev->of_node;
- u32 bus_width;
+ u32 bus_width, max_freq;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -1700,6 +1743,12 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
if (of_find_property(np, "ti,needs-special-reset", NULL))
pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
+ if (!of_property_read_u32(np, "max-frequency", &max_freq))
+ pdata->max_freq = max_freq;
+
+ if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
+ pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
+
return pdata;
}
#else
@@ -1710,7 +1759,7 @@ static inline struct omap_mmc_platform_data
}
#endif
-static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
+static int omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_host *mmc;
@@ -1720,6 +1769,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
const struct of_device_id *match;
dma_cap_mask_t mask;
unsigned tx_req, rx_req;
+ struct pinctrl *pinctrl;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
@@ -1816,7 +1866,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
* MMC can still work without debounce clock.
*/
if (IS_ERR(host->dbclk)) {
- dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
host->dbclk = NULL;
} else if (clk_prepare_enable(host->dbclk) != 0) {
dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
@@ -1884,13 +1933,13 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
mmc_hostname(mmc), host);
if (ret) {
- dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
+ dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
goto err_irq;
}
if (pdata->init != NULL) {
if (pdata->init(&pdev->dev) != 0) {
- dev_dbg(mmc_dev(host->mmc),
+ dev_err(mmc_dev(host->mmc),
"Unable to configure MMC IRQs\n");
goto err_irq_cd_init;
}
@@ -1913,7 +1962,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
mmc_hostname(mmc), host);
if (ret) {
- dev_dbg(mmc_dev(host->mmc),
+ dev_err(mmc_dev(host->mmc),
"Unable to grab MMC CD IRQ\n");
goto err_irq_cd;
}
@@ -1923,6 +1972,11 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_disable_irq(host);
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev,
+ "pins are not configured from the driver\n");
+
omap_hsmmc_protect_card(host);
mmc_add_host(mmc);
@@ -1981,7 +2035,7 @@ err:
return ret;
}
-static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
+static int omap_hsmmc_remove(struct platform_device *pdev)
{
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
struct resource *res;
@@ -2009,9 +2063,9 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
clk_put(host->dbclk);
}
- mmc_free_host(host->mmc);
+ omap_hsmmc_gpio_free(host->pdata);
iounmap(host->base);
- omap_hsmmc_gpio_free(pdev->dev.platform_data);
+ mmc_free_host(host->mmc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
@@ -2022,6 +2076,25 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
+static int omap_hsmmc_prepare(struct device *dev)
+{
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+
+ if (host->pdata->suspend)
+ return host->pdata->suspend(dev, host->slot_id);
+
+ return 0;
+}
+
+static void omap_hsmmc_complete(struct device *dev)
+{
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+
+ if (host->pdata->resume)
+ host->pdata->resume(dev, host->slot_id);
+
+}
+
static int omap_hsmmc_suspend(struct device *dev)
{
int ret = 0;
@@ -2035,23 +2108,10 @@ static int omap_hsmmc_suspend(struct device *dev)
pm_runtime_get_sync(host->dev);
host->suspended = 1;
- if (host->pdata->suspend) {
- ret = host->pdata->suspend(dev, host->slot_id);
- if (ret) {
- dev_dbg(dev, "Unable to handle MMC board"
- " level suspend\n");
- host->suspended = 0;
- return ret;
- }
- }
ret = mmc_suspend_host(host->mmc);
if (ret) {
host->suspended = 0;
- if (host->pdata->resume) {
- if (host->pdata->resume(dev, host->slot_id))
- dev_dbg(dev, "Unmask interrupt failed\n");
- }
goto err;
}
@@ -2088,12 +2148,6 @@ static int omap_hsmmc_resume(struct device *dev)
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
omap_hsmmc_conf_bus_power(host);
- if (host->pdata->resume) {
- ret = host->pdata->resume(dev, host->slot_id);
- if (ret)
- dev_dbg(dev, "Unmask interrupt failed\n");
- }
-
omap_hsmmc_protect_card(host);
/* Notify the core to resume the host */
@@ -2109,8 +2163,10 @@ static int omap_hsmmc_resume(struct device *dev)
}
#else
+#define omap_hsmmc_prepare NULL
+#define omap_hsmmc_complete NULL
#define omap_hsmmc_suspend NULL
-#define omap_hsmmc_resume NULL
+#define omap_hsmmc_resume NULL
#endif
static int omap_hsmmc_runtime_suspend(struct device *dev)
@@ -2138,13 +2194,15 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
.suspend = omap_hsmmc_suspend,
.resume = omap_hsmmc_resume,
+ .prepare = omap_hsmmc_prepare,
+ .complete = omap_hsmmc_complete,
.runtime_suspend = omap_hsmmc_runtime_suspend,
.runtime_resume = omap_hsmmc_runtime_resume,
};
static struct platform_driver omap_hsmmc_driver = {
.probe = omap_hsmmc_probe,
- .remove = __devexit_p(omap_hsmmc_remove),
+ .remove = omap_hsmmc_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 3f9d6d577a91..2b2f65ada22e 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -584,7 +584,7 @@ static const struct of_device_id pxa_mmc_dt_ids[] = {
MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids);
-static int __devinit pxamci_of_init(struct platform_device *pdev)
+static int pxamci_of_init(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct pxamci_platform_data *pdata;
@@ -614,7 +614,7 @@ static int __devinit pxamci_of_init(struct platform_device *pdev)
return 0;
}
#else
-static int __devinit pxamci_of_init(struct platform_device *pdev)
+static int pxamci_of_init(struct platform_device *pdev)
{
return 0;
}
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
new file mode 100644
index 000000000000..f981f7d1f6e3
--- /dev/null
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -0,0 +1,1341 @@
+/* Realtek PCI-Express SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG <wei_wang@realsil.com.cn>
+ * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+#include <linux/mfd/rtsx_pci.h>
+#include <asm/unaligned.h>
+
+/* SD Tuning Data Structure
+ * Record continuous timing phase path
+ */
+struct timing_phase_path {
+ int start;
+ int end;
+ int mid;
+ int len;
+};
+
+struct realtek_pci_sdmmc {
+ struct platform_device *pdev;
+ struct rtsx_pcr *pcr;
+ struct mmc_host *mmc;
+ struct mmc_request *mrq;
+
+ struct mutex host_mutex;
+
+ u8 ssc_depth;
+ unsigned int clock;
+ bool vpclk;
+ bool double_clk;
+ bool eject;
+ bool initial_mode;
+ bool ddr_mode;
+};
+
+static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
+{
+ return &(host->pdev->dev);
+}
+
+static inline void sd_clear_error(struct realtek_pci_sdmmc *host)
+{
+ rtsx_pci_write_register(host->pcr, CARD_STOP,
+ SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR);
+}
+
+#ifdef DEBUG
+static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ u16 i;
+ u8 *ptr;
+
+ /* Print SD host internal registers */
+ rtsx_pci_init_cmd(pcr);
+ for (i = 0xFDA0; i <= 0xFDAE; i++)
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
+ for (i = 0xFD52; i <= 0xFD69; i++)
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
+ rtsx_pci_send_cmd(pcr, 100);
+
+ ptr = rtsx_pci_get_cmd_data(pcr);
+ for (i = 0xFDA0; i <= 0xFDAE; i++)
+ dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+ for (i = 0xFD52; i <= 0xFD69; i++)
+ dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+}
+#else
+#define sd_print_debug_regs(host)
+#endif /* DEBUG */
+
+static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
+ u8 *buf, int buf_len, int timeout)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err, i;
+ u8 trans_mode;
+
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40);
+
+ if (!buf)
+ buf_len = 0;
+
+ if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK)
+ trans_mode = SD_TM_AUTO_TUNING;
+ else
+ trans_mode = SD_TM_NORMAL_READ;
+
+ rtsx_pci_init_cmd(pcr);
+
+ for (i = 0; i < 5; i++)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
+ 0xFF, (u8)(byte_cnt >> 8));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+ if (trans_mode != SD_TM_AUTO_TUNING)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
+ 0xFF, trans_mode | SD_TRANSFER_START);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ err = rtsx_pci_send_cmd(pcr, timeout);
+ if (err < 0) {
+ sd_print_debug_regs(host);
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_send_cmd fail (err = %d)\n", err);
+ return err;
+ }
+
+ if (buf && buf_len) {
+ err = rtsx_pci_read_ppbuf(pcr, buf, buf_len);
+ if (err < 0) {
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_read_ppbuf fail (err = %d)\n", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
+ u8 *buf, int buf_len, int timeout)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err, i;
+ u8 trans_mode;
+
+ if (!buf)
+ buf_len = 0;
+
+ if (buf && buf_len) {
+ err = rtsx_pci_write_ppbuf(pcr, buf, buf_len);
+ if (err < 0) {
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_write_ppbuf fail (err = %d)\n", err);
+ return err;
+ }
+ }
+
+ trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3;
+ rtsx_pci_init_cmd(pcr);
+
+ if (cmd) {
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__,
+ cmd[0] - 0x40);
+
+ for (i = 0; i < 5; i++)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ SD_CMD0 + i, 0xFF, cmd[i]);
+ }
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
+ 0xFF, (u8)(byte_cnt >> 8));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+ trans_mode | SD_TRANSFER_START);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ err = rtsx_pci_send_cmd(pcr, timeout);
+ if (err < 0) {
+ sd_print_debug_regs(host);
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_send_cmd fail (err = %d)\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
+ struct mmc_command *cmd)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ u8 cmd_idx = (u8)cmd->opcode;
+ u32 arg = cmd->arg;
+ int err = 0;
+ int timeout = 100;
+ int i;
+ u8 *ptr;
+ int stat_idx = 0;
+ u8 rsp_type;
+ int rsp_len = 5;
+
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+ __func__, cmd_idx, arg);
+
+ /* Response type:
+ * R0
+ * R1, R5, R6, R7
+ * R1b
+ * R2
+ * R3, R4
+ */
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ rsp_type = SD_RSP_TYPE_R0;
+ rsp_len = 0;
+ break;
+ case MMC_RSP_R1:
+ rsp_type = SD_RSP_TYPE_R1;
+ break;
+ case MMC_RSP_R1B:
+ rsp_type = SD_RSP_TYPE_R1b;
+ break;
+ case MMC_RSP_R2:
+ rsp_type = SD_RSP_TYPE_R2;
+ rsp_len = 16;
+ break;
+ case MMC_RSP_R3:
+ rsp_type = SD_RSP_TYPE_R3;
+ break;
+ default:
+ dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (rsp_type == SD_RSP_TYPE_R1b)
+ timeout = 3000;
+
+ if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ 0xFF, SD_CLK_TOGGLE_EN);
+ if (err < 0)
+ goto out;
+ }
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
+ 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END | SD_STAT_IDLE,
+ SD_TRANSFER_END | SD_STAT_IDLE);
+
+ if (rsp_type == SD_RSP_TYPE_R2) {
+ /* Read data from ping-pong buffer */
+ for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
+ stat_idx = 16;
+ } else if (rsp_type != SD_RSP_TYPE_R0) {
+ /* Read data from SD_CMDx registers */
+ for (i = SD_CMD0; i <= SD_CMD4; i++)
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
+ stat_idx = 5;
+ }
+
+ rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
+
+ err = rtsx_pci_send_cmd(pcr, timeout);
+ if (err < 0) {
+ sd_print_debug_regs(host);
+ sd_clear_error(host);
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_send_cmd error (err = %d)\n", err);
+ goto out;
+ }
+
+ if (rsp_type == SD_RSP_TYPE_R0) {
+ err = 0;
+ goto out;
+ }
+
+ /* Eliminate returned value of CHECK_REG_CMD */
+ ptr = rtsx_pci_get_cmd_data(pcr) + 1;
+
+ /* Check (Start,Transmission) bit of Response */
+ if ((ptr[0] & 0xC0) != 0) {
+ err = -EILSEQ;
+ dev_dbg(sdmmc_dev(host), "Invalid response bit\n");
+ goto out;
+ }
+
+ /* Check CRC7 */
+ if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+ if (ptr[stat_idx] & SD_CRC7_ERR) {
+ err = -EILSEQ;
+ dev_dbg(sdmmc_dev(host), "CRC7 error\n");
+ goto out;
+ }
+ }
+
+ if (rsp_type == SD_RSP_TYPE_R2) {
+ for (i = 0; i < 4; i++) {
+ cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
+ dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
+ i, cmd->resp[i]);
+ }
+ } else {
+ cmd->resp[0] = get_unaligned_be32(ptr + 1);
+ dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n",
+ cmd->resp[0]);
+ }
+
+out:
+ cmd->error = err;
+}
+
+static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_card *card = mmc->card;
+ struct mmc_data *data = mrq->data;
+ int uhs = mmc_sd_card_uhs(card);
+ int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+ u8 cfg2, trans_mode;
+ int err;
+ size_t data_len = data->blksz * data->blocks;
+
+ if (read) {
+ cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
+ trans_mode = SD_TM_AUTO_READ_3;
+ } else {
+ cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
+ trans_mode = SD_TM_AUTO_WRITE_3;
+ }
+
+ if (!uhs)
+ cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L,
+ 0xFF, (u8)data->blocks);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H,
+ 0xFF, (u8)(data->blocks >> 8));
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
+ DMA_DONE_INT, DMA_DONE_INT);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
+ 0xFF, (u8)(data_len >> 24));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
+ 0xFF, (u8)(data_len >> 16));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
+ 0xFF, (u8)(data_len >> 8));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len);
+ if (read) {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+ 0x03 | DMA_PACK_SIZE_MASK,
+ DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
+ } else {
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+ 0x03 | DMA_PACK_SIZE_MASK,
+ DMA_DIR_TO_CARD | DMA_EN | DMA_512);
+ }
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, RING_BUFFER);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+ trans_mode | SD_TRANSFER_START);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ rtsx_pci_send_cmd_no_wait(pcr);
+
+ err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000);
+ if (err < 0) {
+ sd_clear_error(host);
+ return err;
+ }
+
+ return 0;
+}
+
+static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
+{
+ rtsx_pci_write_register(host->pcr, SD_CFG1,
+ SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_128);
+}
+
+static inline void sd_disable_initial_mode(struct realtek_pci_sdmmc *host)
+{
+ rtsx_pci_write_register(host->pcr, SD_CFG1,
+ SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_0);
+}
+
+static void sd_normal_rw(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq)
+{
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = mrq->data;
+ u8 _cmd[5], *buf;
+
+ _cmd[0] = 0x40 | (u8)cmd->opcode;
+ put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
+
+ buf = kzalloc(data->blksz, GFP_NOIO);
+ if (!buf) {
+ cmd->error = -ENOMEM;
+ return;
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ if (host->initial_mode)
+ sd_disable_initial_mode(host);
+
+ cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf,
+ data->blksz, 200);
+
+ if (host->initial_mode)
+ sd_enable_initial_mode(host);
+
+ sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+ } else {
+ sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+
+ cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf,
+ data->blksz, 200);
+ }
+
+ kfree(buf);
+}
+
+static int sd_change_phase(struct realtek_pci_sdmmc *host, u8 sample_point)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+
+ dev_dbg(sdmmc_dev(host), "%s: sample_point = %d\n",
+ __func__, sample_point);
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, CHANGE_CLK);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPRX_CTL, 0x1F, sample_point);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
+{
+ struct timing_phase_path path[MAX_PHASE + 1];
+ int i, j, cont_path_cnt;
+ int new_block, max_len, final_path_idx;
+ u8 final_phase = 0xFF;
+
+ /* Parse phase_map, take it as a bit-ring */
+ cont_path_cnt = 0;
+ new_block = 1;
+ j = 0;
+ for (i = 0; i < MAX_PHASE + 1; i++) {
+ if (phase_map & (1 << i)) {
+ if (new_block) {
+ new_block = 0;
+ j = cont_path_cnt++;
+ path[j].start = i;
+ path[j].end = i;
+ } else {
+ path[j].end = i;
+ }
+ } else {
+ new_block = 1;
+ if (cont_path_cnt) {
+ /* Calculate path length and middle point */
+ int idx = cont_path_cnt - 1;
+ path[idx].len =
+ path[idx].end - path[idx].start + 1;
+ path[idx].mid =
+ path[idx].start + path[idx].len / 2;
+ }
+ }
+ }
+
+ if (cont_path_cnt == 0) {
+ dev_dbg(sdmmc_dev(host), "No continuous phase path\n");
+ goto finish;
+ } else {
+ /* Calculate last continuous path length and middle point */
+ int idx = cont_path_cnt - 1;
+ path[idx].len = path[idx].end - path[idx].start + 1;
+ path[idx].mid = path[idx].start + path[idx].len / 2;
+ }
+
+ /* Connect the first and last continuous paths if they are adjacent */
+ if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) {
+ /* Using negative index */
+ path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
+ path[0].len += path[cont_path_cnt - 1].len;
+ path[0].mid = path[0].start + path[0].len / 2;
+ /* Convert negative middle point index to positive one */
+ if (path[0].mid < 0)
+ path[0].mid += MAX_PHASE + 1;
+ cont_path_cnt--;
+ }
+
+ /* Choose the longest continuous phase path */
+ max_len = 0;
+ final_phase = 0;
+ final_path_idx = 0;
+ for (i = 0; i < cont_path_cnt; i++) {
+ if (path[i].len > max_len) {
+ max_len = path[i].len;
+ final_phase = (u8)path[i].mid;
+ final_path_idx = i;
+ }
+
+ dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n",
+ i, path[i].start);
+ dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n",
+ i, path[i].end);
+ dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n",
+ i, path[i].len);
+ dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n",
+ i, path[i].mid);
+ }
+
+finish:
+ dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase);
+ return final_phase;
+}
+
+static void sd_wait_data_idle(struct realtek_pci_sdmmc *host)
+{
+ int err, i;
+ u8 val = 0;
+
+ for (i = 0; i < 100; i++) {
+ err = rtsx_pci_read_register(host->pcr, SD_DATA_STATE, &val);
+ if (val & SD_DATA_IDLE)
+ return;
+
+ udelay(100);
+ }
+}
+
+static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
+ u8 opcode, u8 sample_point)
+{
+ int err;
+ u8 cmd[5] = {0};
+
+ err = sd_change_phase(host, sample_point);
+ if (err < 0)
+ return err;
+
+ cmd[0] = 0x40 | opcode;
+ err = sd_read_data(host, cmd, 0x40, NULL, 0, 100);
+ if (err < 0) {
+ /* Wait till SD DATA IDLE */
+ sd_wait_data_idle(host);
+ sd_clear_error(host);
+ return err;
+ }
+
+ return 0;
+}
+
+static int sd_tuning_phase(struct realtek_pci_sdmmc *host,
+ u8 opcode, u32 *phase_map)
+{
+ int err, i;
+ u32 raw_phase_map = 0;
+
+ for (i = MAX_PHASE; i >= 0; i--) {
+ err = sd_tuning_rx_cmd(host, opcode, (u8)i);
+ if (err == 0)
+ raw_phase_map |= 1 << i;
+ }
+
+ if (phase_map)
+ *phase_map = raw_phase_map;
+
+ return 0;
+}
+
+static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
+{
+ int err, i;
+ u32 raw_phase_map[RX_TUNING_CNT] = {0}, phase_map;
+ u8 final_phase;
+
+ for (i = 0; i < RX_TUNING_CNT; i++) {
+ err = sd_tuning_phase(host, opcode, &(raw_phase_map[i]));
+ if (err < 0)
+ return err;
+
+ if (raw_phase_map[i] == 0)
+ break;
+ }
+
+ phase_map = 0xFFFFFFFF;
+ for (i = 0; i < RX_TUNING_CNT; i++) {
+ dev_dbg(sdmmc_dev(host), "RX raw_phase_map[%d] = 0x%08x\n",
+ i, raw_phase_map[i]);
+ phase_map &= raw_phase_map[i];
+ }
+ dev_dbg(sdmmc_dev(host), "RX phase_map = 0x%08x\n", phase_map);
+
+ if (phase_map) {
+ final_phase = sd_search_final_phase(host, phase_map);
+ if (final_phase == 0xFF)
+ return -EINVAL;
+
+ err = sd_change_phase(host, final_phase);
+ if (err < 0)
+ return err;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = mrq->data;
+ unsigned int data_size = 0;
+ int err;
+
+ if (host->eject) {
+ cmd->error = -ENOMEDIUM;
+ goto finish;
+ }
+
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
+ if (err) {
+ cmd->error = err;
+ goto finish;
+ }
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth,
+ host->initial_mode, host->double_clk, host->vpclk);
+ rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL);
+ rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
+ CARD_SHARE_MASK, CARD_SHARE_48_SD);
+
+ mutex_lock(&host->host_mutex);
+ host->mrq = mrq;
+ mutex_unlock(&host->host_mutex);
+
+ if (mrq->data)
+ data_size = data->blocks * data->blksz;
+
+ if (!data_size || mmc_op_multi(cmd->opcode) ||
+ (cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+ (cmd->opcode == MMC_WRITE_BLOCK)) {
+ sd_send_cmd_get_rsp(host, cmd);
+
+ if (!cmd->error && data_size) {
+ sd_rw_multi(host, mrq);
+
+ if (mmc_op_multi(cmd->opcode) && mrq->stop)
+ sd_send_cmd_get_rsp(host, mrq->stop);
+ }
+ } else {
+ sd_normal_rw(host, mrq);
+ }
+
+ if (mrq->data) {
+ if (cmd->error || data->error)
+ data->bytes_xfered = 0;
+ else
+ data->bytes_xfered = data->blocks * data->blksz;
+ }
+
+ mutex_unlock(&pcr->pcr_mutex);
+
+finish:
+ if (cmd->error)
+ dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
+
+ mutex_lock(&host->host_mutex);
+ host->mrq = NULL;
+ mutex_unlock(&host->host_mutex);
+
+ mmc_request_done(mmc, mrq);
+}
+
+static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
+ unsigned char bus_width)
+{
+ int err = 0;
+ u8 width[] = {
+ [MMC_BUS_WIDTH_1] = SD_BUS_WIDTH_1BIT,
+ [MMC_BUS_WIDTH_4] = SD_BUS_WIDTH_4BIT,
+ [MMC_BUS_WIDTH_8] = SD_BUS_WIDTH_8BIT,
+ };
+
+ if (bus_width <= MMC_BUS_WIDTH_8)
+ err = rtsx_pci_write_register(host->pcr, SD_CFG1,
+ 0x03, width[bus_width]);
+
+ return err;
+}
+
+static int sd_power_on(struct realtek_pci_sdmmc *host)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
+ CARD_SHARE_MASK, CARD_SHARE_48_SD);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN,
+ SD_CLK_EN, SD_CLK_EN);
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_SD_CARD);
+ if (err < 0)
+ return err;
+
+ err = rtsx_pci_card_power_on(pcr, RTSX_SD_CARD);
+ if (err < 0)
+ return err;
+
+ err = rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int sd_power_off(struct realtek_pci_sdmmc *host)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+
+ rtsx_pci_init_cmd(pcr);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, 0);
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+ if (err < 0)
+ return err;
+
+ err = rtsx_pci_card_power_off(pcr, RTSX_SD_CARD);
+ if (err < 0)
+ return err;
+
+ return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_SD_CARD);
+}
+
+static int sd_set_power_mode(struct realtek_pci_sdmmc *host,
+ unsigned char power_mode)
+{
+ int err;
+
+ if (power_mode == MMC_POWER_OFF)
+ err = sd_power_off(host);
+ else
+ err = sd_power_on(host);
+
+ return err;
+}
+
+static int sd_set_timing(struct realtek_pci_sdmmc *host,
+ unsigned char timing, bool *ddr_mode)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err = 0;
+
+ *ddr_mode = false;
+
+ rtsx_pci_init_cmd(pcr);
+
+ switch (timing) {
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_SDR50:
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
+ 0x0C | SD_ASYNC_FIFO_NOT_RST,
+ SD_30_MODE | SD_ASYNC_FIFO_NOT_RST);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+ CLK_LOW_FREQ, CLK_LOW_FREQ);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+ CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+ break;
+
+ case MMC_TIMING_UHS_DDR50:
+ *ddr_mode = true;
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
+ 0x0C | SD_ASYNC_FIFO_NOT_RST,
+ SD_DDR_MODE | SD_ASYNC_FIFO_NOT_RST);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+ CLK_LOW_FREQ, CLK_LOW_FREQ);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+ CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL,
+ DDR_VAR_TX_CMD_DAT, DDR_VAR_TX_CMD_DAT);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+ DDR_VAR_RX_DAT | DDR_VAR_RX_CMD,
+ DDR_VAR_RX_DAT | DDR_VAR_RX_CMD);
+ break;
+
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
+ 0x0C, SD_20_MODE);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+ CLK_LOW_FREQ, CLK_LOW_FREQ);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+ CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL,
+ SD20_TX_SEL_MASK, SD20_TX_14_AHEAD);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+ SD20_RX_SEL_MASK, SD20_RX_14_DELAY);
+ break;
+
+ default:
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ SD_CFG1, 0x0C, SD_20_MODE);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+ CLK_LOW_FREQ, CLK_LOW_FREQ);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+ CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ SD_PUSH_POINT_CTL, 0xFF, 0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+ SD20_RX_SEL_MASK, SD20_RX_POS_EDGE);
+ break;
+ }
+
+ err = rtsx_pci_send_cmd(pcr, 100);
+
+ return err;
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+
+ if (host->eject)
+ return;
+
+ if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD))
+ return;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ sd_set_bus_width(host, ios->bus_width);
+ sd_set_power_mode(host, ios->power_mode);
+ sd_set_timing(host, ios->timing, &host->ddr_mode);
+
+ host->vpclk = false;
+ host->double_clk = true;
+
+ switch (ios->timing) {
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_SDR50:
+ host->ssc_depth = RTSX_SSC_DEPTH_2M;
+ host->vpclk = true;
+ host->double_clk = false;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_UHS_SDR25:
+ host->ssc_depth = RTSX_SSC_DEPTH_1M;
+ break;
+ default:
+ host->ssc_depth = RTSX_SSC_DEPTH_500K;
+ break;
+ }
+
+ host->initial_mode = (ios->clock <= 1000000) ? true : false;
+
+ host->clock = ios->clock;
+ rtsx_pci_switch_clock(pcr, ios->clock, host->ssc_depth,
+ host->initial_mode, host->double_clk, host->vpclk);
+
+ mutex_unlock(&pcr->pcr_mutex);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ int ro = 0;
+ u32 val;
+
+ if (host->eject)
+ return -ENOMEDIUM;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ /* Check SD mechanical write-protect switch */
+ val = rtsx_pci_readl(pcr, RTSX_BIPR);
+ dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val);
+ if (val & SD_WRITE_PROTECT)
+ ro = 1;
+
+ mutex_unlock(&pcr->pcr_mutex);
+
+ return ro;
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ int cd = 0;
+ u32 val;
+
+ if (host->eject)
+ return -ENOMEDIUM;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ /* Check SD card detect */
+ val = rtsx_pci_card_exist(pcr);
+ dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val);
+ if (val & SD_EXIST)
+ cd = 1;
+
+ mutex_unlock(&pcr->pcr_mutex);
+
+ return cd;
+}
+
+static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+ u8 stat;
+
+ /* Reference to Signal Voltage Switch Sequence in SD spec.
+ * Wait for a period of time so that the card can drive SD_CMD and
+ * SD_DAT[3:0] to low after sending back CMD11 response.
+ */
+ mdelay(1);
+
+ /* SD_CMD, SD_DAT[3:0] should be driven to low by card;
+ * If either one of SD_CMD,SD_DAT[3:0] is not low,
+ * abort the voltage switch sequence;
+ */
+ err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
+ if (err < 0)
+ return err;
+
+ if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+ SD_DAT1_STATUS | SD_DAT0_STATUS))
+ return -EINVAL;
+
+ /* Stop toggle SD clock */
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ 0xFF, SD_CLK_FORCE_STOP);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+ u8 stat, mask, val;
+
+ /* Wait 1.8V output of voltage regulator in card stable */
+ msleep(50);
+
+ /* Toggle SD clock again */
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN);
+ if (err < 0)
+ return err;
+
+ /* Wait for a period of time so that the card can drive
+ * SD_DAT[3:0] to high at 1.8V
+ */
+ msleep(20);
+
+ /* SD_CMD, SD_DAT[3:0] should be pulled high by host */
+ err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
+ if (err < 0)
+ return err;
+
+ mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+ SD_DAT1_STATUS | SD_DAT0_STATUS;
+ val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+ SD_DAT1_STATUS | SD_DAT0_STATUS;
+ if ((stat & mask) != val) {
+ dev_dbg(sdmmc_dev(host),
+ "%s: SD_BUS_STAT = 0x%x\n", __func__, stat);
+ rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+ rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ int err = 0;
+ u8 voltage;
+
+ dev_dbg(sdmmc_dev(host), "%s: signal_voltage = %d\n",
+ __func__, ios->signal_voltage);
+
+ if (host->eject)
+ return -ENOMEDIUM;
+
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
+ if (err)
+ return err;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+ voltage = OUTPUT_3V3;
+ else
+ voltage = OUTPUT_1V8;
+
+ if (voltage == OUTPUT_1V8) {
+ err = sd_wait_voltage_stable_1(host);
+ if (err < 0)
+ goto out;
+ }
+
+ err = rtsx_pci_switch_output_voltage(pcr, voltage);
+ if (err < 0)
+ goto out;
+
+ if (voltage == OUTPUT_1V8) {
+ err = sd_wait_voltage_stable_2(host);
+ if (err < 0)
+ goto out;
+ }
+
+ /* Stop toggle SD clock in idle */
+ err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+
+out:
+ mutex_unlock(&pcr->pcr_mutex);
+
+ return err;
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ int err = 0;
+
+ if (host->eject)
+ return -ENOMEDIUM;
+
+ err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD);
+ if (err)
+ return err;
+
+ mutex_lock(&pcr->pcr_mutex);
+
+ rtsx_pci_start_run(pcr);
+
+ if (!host->ddr_mode)
+ err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK);
+
+ mutex_unlock(&pcr->pcr_mutex);
+
+ return err;
+}
+
+static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
+ .request = sdmmc_request,
+ .set_ios = sdmmc_set_ios,
+ .get_ro = sdmmc_get_ro,
+ .get_cd = sdmmc_get_cd,
+ .start_signal_voltage_switch = sdmmc_switch_voltage,
+ .execute_tuning = sdmmc_execute_tuning,
+};
+
+#ifdef CONFIG_PM
+static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+ struct mmc_host *mmc = host->mmc;
+ int err;
+
+ dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
+
+ err = mmc_suspend_host(mmc);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int rtsx_pci_sdmmc_resume(struct platform_device *pdev)
+{
+ struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+ struct mmc_host *mmc = host->mmc;
+
+ dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
+
+ return mmc_resume_host(mmc);
+}
+#else /* CONFIG_PM */
+#define rtsx_pci_sdmmc_suspend NULL
+#define rtsx_pci_sdmmc_resume NULL
+#endif /* CONFIG_PM */
+
+static void init_extra_caps(struct realtek_pci_sdmmc *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ struct rtsx_pcr *pcr = host->pcr;
+
+ dev_dbg(sdmmc_dev(host), "pcr->extra_caps = 0x%x\n", pcr->extra_caps);
+
+ if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50)
+ mmc->caps |= MMC_CAP_UHS_SDR50;
+ if (pcr->extra_caps & EXTRA_CAPS_SD_SDR104)
+ mmc->caps |= MMC_CAP_UHS_SDR104;
+ if (pcr->extra_caps & EXTRA_CAPS_SD_DDR50)
+ mmc->caps |= MMC_CAP_UHS_DDR50;
+ if (pcr->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+ mmc->caps |= MMC_CAP_1_8V_DDR;
+ if (pcr->extra_caps & EXTRA_CAPS_MMC_8BIT)
+ mmc->caps |= MMC_CAP_8_BIT_DATA;
+}
+
+static void realtek_init_host(struct realtek_pci_sdmmc *host)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ mmc->f_min = 250000;
+ mmc->f_max = 208000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+ MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+ MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+ mmc->max_current_330 = 400;
+ mmc->max_current_180 = 800;
+ mmc->ops = &realtek_pci_sdmmc_ops;
+
+ init_extra_caps(host);
+
+ mmc->max_segs = 256;
+ mmc->max_seg_size = 65536;
+ mmc->max_blk_size = 512;
+ mmc->max_blk_count = 65535;
+ mmc->max_req_size = 524288;
+}
+
+static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev)
+{
+ struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+
+ mmc_detect_change(host->mmc, 0);
+}
+
+static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct realtek_pci_sdmmc *host;
+ struct rtsx_pcr *pcr;
+ struct pcr_handle *handle = pdev->dev.platform_data;
+
+ if (!handle)
+ return -ENXIO;
+
+ pcr = handle->pcr;
+ if (!pcr)
+ return -ENXIO;
+
+ dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n");
+
+ mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ host = mmc_priv(mmc);
+ host->pcr = pcr;
+ host->mmc = mmc;
+ host->pdev = pdev;
+ platform_set_drvdata(pdev, host);
+ pcr->slots[RTSX_SD_CARD].p_dev = pdev;
+ pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event;
+
+ mutex_init(&host->host_mutex);
+
+ realtek_init_host(host);
+
+ mmc_add_host(mmc);
+
+ return 0;
+}
+
+static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
+{
+ struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+ struct rtsx_pcr *pcr;
+ struct mmc_host *mmc;
+
+ if (!host)
+ return 0;
+
+ pcr = host->pcr;
+ pcr->slots[RTSX_SD_CARD].p_dev = NULL;
+ pcr->slots[RTSX_SD_CARD].card_event = NULL;
+ mmc = host->mmc;
+ host->eject = true;
+
+ mutex_lock(&host->host_mutex);
+ if (host->mrq) {
+ dev_dbg(&(pdev->dev),
+ "%s: Controller removed during transfer\n",
+ mmc_hostname(mmc));
+
+ rtsx_pci_complete_unfinished_transfer(pcr);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ if (host->mrq->stop)
+ host->mrq->stop->error = -ENOMEDIUM;
+ mmc_request_done(mmc, host->mrq);
+ }
+ mutex_unlock(&host->host_mutex);
+
+ mmc_remove_host(mmc);
+ mmc_free_host(mmc);
+
+ platform_set_drvdata(pdev, NULL);
+
+ dev_dbg(&(pdev->dev),
+ ": Realtek PCI-E SDMMC controller has been removed\n");
+
+ return 0;
+}
+
+static struct platform_device_id rtsx_pci_sdmmc_ids[] = {
+ {
+ .name = DRV_NAME_RTSX_PCI_SDMMC,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, rtsx_pci_sdmmc_ids);
+
+static struct platform_driver rtsx_pci_sdmmc_driver = {
+ .probe = rtsx_pci_sdmmc_drv_probe,
+ .remove = rtsx_pci_sdmmc_drv_remove,
+ .id_table = rtsx_pci_sdmmc_ids,
+ .suspend = rtsx_pci_sdmmc_suspend,
+ .resume = rtsx_pci_sdmmc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME_RTSX_PCI_SDMMC,
+ },
+};
+module_platform_driver(rtsx_pci_sdmmc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
+MODULE_DESCRIPTION("Realtek PCI-E SD/MMC Card Host Driver");
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 4638ddab97b8..63fb265e0da6 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1540,7 +1540,7 @@ static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { }
#endif /* CONFIG_DEBUG_FS */
-static int __devinit s3cmci_probe(struct platform_device *pdev)
+static int s3cmci_probe(struct platform_device *pdev)
{
struct s3cmci_host *host;
struct mmc_host *mmc;
@@ -1819,7 +1819,7 @@ static void s3cmci_shutdown(struct platform_device *pdev)
clk_disable(host->clk);
}
-static int __devexit s3cmci_remove(struct platform_device *pdev)
+static int s3cmci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct s3cmci_host *host = mmc_priv(mmc);
@@ -1906,7 +1906,7 @@ static struct platform_driver s3cmci_driver = {
},
.id_table = s3cmci_driver_ids,
.probe = s3cmci_probe,
- .remove = __devexit_p(s3cmci_remove),
+ .remove = s3cmci_remove,
.shutdown = s3cmci_shutdown,
};
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
new file mode 100644
index 000000000000..2592dddbd965
--- /dev/null
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -0,0 +1,312 @@
+/*
+ * Secure Digital Host Controller Interface ACPI driver.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/compiler.h>
+#include <linux/stddef.h>
+#include <linux/bitops.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/acpi.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/pm.h>
+#include <linux/mmc/sdhci.h>
+
+#include "sdhci.h"
+
+enum {
+ SDHCI_ACPI_SD_CD = BIT(0),
+ SDHCI_ACPI_RUNTIME_PM = BIT(1),
+};
+
+struct sdhci_acpi_chip {
+ const struct sdhci_ops *ops;
+ unsigned int quirks;
+ unsigned int quirks2;
+ unsigned long caps;
+ unsigned int caps2;
+ mmc_pm_flag_t pm_caps;
+};
+
+struct sdhci_acpi_slot {
+ const struct sdhci_acpi_chip *chip;
+ unsigned int quirks;
+ unsigned int quirks2;
+ unsigned long caps;
+ unsigned int caps2;
+ mmc_pm_flag_t pm_caps;
+ unsigned int flags;
+};
+
+struct sdhci_acpi_host {
+ struct sdhci_host *host;
+ const struct sdhci_acpi_slot *slot;
+ struct platform_device *pdev;
+ bool use_runtime_pm;
+};
+
+static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
+{
+ return c->slot && (c->slot->flags & flag);
+}
+
+static int sdhci_acpi_enable_dma(struct sdhci_host *host)
+{
+ return 0;
+}
+
+static const struct sdhci_ops sdhci_acpi_ops_dflt = {
+ .enable_dma = sdhci_acpi_enable_dma,
+};
+
+static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
+ .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
+ .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
+ .flags = SDHCI_ACPI_RUNTIME_PM,
+ .pm_caps = MMC_PM_KEEP_POWER,
+};
+
+static const struct acpi_device_id sdhci_acpi_ids[] = {
+ { "INT33C6", (kernel_ulong_t)&sdhci_acpi_slot_int_sdio },
+ { "PNP0D40" },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
+
+static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid)
+{
+ const struct acpi_device_id *id;
+
+ for (id = sdhci_acpi_ids; id->id[0]; id++)
+ if (!strcmp(id->id, hid))
+ return (const struct sdhci_acpi_slot *)id->driver_data;
+ return NULL;
+}
+
+static int sdhci_acpi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ acpi_handle handle = ACPI_HANDLE(dev);
+ struct acpi_device *device;
+ struct sdhci_acpi_host *c;
+ struct sdhci_host *host;
+ struct resource *iomem;
+ resource_size_t len;
+ const char *hid;
+ int err;
+
+ if (acpi_bus_get_device(handle, &device))
+ return -ENODEV;
+
+ if (acpi_bus_get_status(device) || !device->status.present)
+ return -ENODEV;
+
+ hid = acpi_device_hid(device);
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iomem)
+ return -ENOMEM;
+
+ len = resource_size(iomem);
+ if (len < 0x100)
+ dev_err(dev, "Invalid iomem size!\n");
+
+ if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev)))
+ return -ENOMEM;
+
+ host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ c = sdhci_priv(host);
+ c->host = host;
+ c->slot = sdhci_acpi_get_slot(hid);
+ c->pdev = pdev;
+ c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);
+
+ platform_set_drvdata(pdev, c);
+
+ host->hw_name = "ACPI";
+ host->ops = &sdhci_acpi_ops_dflt;
+ host->irq = platform_get_irq(pdev, 0);
+
+ host->ioaddr = devm_ioremap_nocache(dev, iomem->start,
+ resource_size(iomem));
+ if (host->ioaddr == NULL) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ if (!dev->dma_mask) {
+ u64 dma_mask;
+
+ if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) {
+ /* 64-bit DMA is not supported at present */
+ dma_mask = DMA_BIT_MASK(32);
+ } else {
+ dma_mask = DMA_BIT_MASK(32);
+ }
+
+ dev->dma_mask = &dev->coherent_dma_mask;
+ dev->coherent_dma_mask = dma_mask;
+ }
+
+ if (c->slot) {
+ if (c->slot->chip) {
+ host->ops = c->slot->chip->ops;
+ host->quirks |= c->slot->chip->quirks;
+ host->quirks2 |= c->slot->chip->quirks2;
+ host->mmc->caps |= c->slot->chip->caps;
+ host->mmc->caps2 |= c->slot->chip->caps2;
+ host->mmc->pm_caps |= c->slot->chip->pm_caps;
+ }
+ host->quirks |= c->slot->quirks;
+ host->quirks2 |= c->slot->quirks2;
+ host->mmc->caps |= c->slot->caps;
+ host->mmc->caps2 |= c->slot->caps2;
+ host->mmc->pm_caps |= c->slot->pm_caps;
+ }
+
+ err = sdhci_add_host(host);
+ if (err)
+ goto err_free;
+
+ if (c->use_runtime_pm) {
+ pm_suspend_ignore_children(dev, 1);
+ pm_runtime_set_autosuspend_delay(dev, 50);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+ }
+
+ return 0;
+
+err_free:
+ platform_set_drvdata(pdev, NULL);
+ sdhci_free_host(c->host);
+ return err;
+}
+
+static int sdhci_acpi_remove(struct platform_device *pdev)
+{
+ struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int dead;
+
+ if (c->use_runtime_pm) {
+ pm_runtime_get_sync(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ }
+
+ dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
+ sdhci_remove_host(c->host, dead);
+ platform_set_drvdata(pdev, NULL);
+ sdhci_free_host(c->host);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int sdhci_acpi_suspend(struct device *dev)
+{
+ struct sdhci_acpi_host *c = dev_get_drvdata(dev);
+
+ return sdhci_suspend_host(c->host);
+}
+
+static int sdhci_acpi_resume(struct device *dev)
+{
+ struct sdhci_acpi_host *c = dev_get_drvdata(dev);
+
+ return sdhci_resume_host(c->host);
+}
+
+#else
+
+#define sdhci_acpi_suspend NULL
+#define sdhci_acpi_resume NULL
+
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int sdhci_acpi_runtime_suspend(struct device *dev)
+{
+ struct sdhci_acpi_host *c = dev_get_drvdata(dev);
+
+ return sdhci_runtime_suspend_host(c->host);
+}
+
+static int sdhci_acpi_runtime_resume(struct device *dev)
+{
+ struct sdhci_acpi_host *c = dev_get_drvdata(dev);
+
+ return sdhci_runtime_resume_host(c->host);
+}
+
+static int sdhci_acpi_runtime_idle(struct device *dev)
+{
+ return 0;
+}
+
+#else
+
+#define sdhci_acpi_runtime_suspend NULL
+#define sdhci_acpi_runtime_resume NULL
+#define sdhci_acpi_runtime_idle NULL
+
+#endif
+
+static const struct dev_pm_ops sdhci_acpi_pm_ops = {
+ .suspend = sdhci_acpi_suspend,
+ .resume = sdhci_acpi_resume,
+ .runtime_suspend = sdhci_acpi_runtime_suspend,
+ .runtime_resume = sdhci_acpi_runtime_resume,
+ .runtime_idle = sdhci_acpi_runtime_idle,
+};
+
+static struct platform_driver sdhci_acpi_driver = {
+ .driver = {
+ .name = "sdhci-acpi",
+ .owner = THIS_MODULE,
+ .acpi_match_table = sdhci_acpi_ids,
+ .pm = &sdhci_acpi_pm_ops,
+ },
+ .probe = sdhci_acpi_probe,
+ .remove = sdhci_acpi_remove,
+};
+
+module_platform_driver(sdhci_acpi_driver);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver");
+MODULE_AUTHOR("Adrian Hunter");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c
new file mode 100644
index 000000000000..8ffea05152c6
--- /dev/null
+++ b/drivers/mmc/host/sdhci-bcm2835.c
@@ -0,0 +1,210 @@
+/*
+ * BCM2835 SDHCI
+ * Copyright (C) 2012 Stephen Warren
+ * Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me
+ * Portions of the code there were obviously based on the Linux kernel at:
+ * git://github.com/raspberrypi/linux.git rpi-3.6.y
+ * commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mmc/host.h>
+#include "sdhci-pltfm.h"
+
+/*
+ * 400KHz is max freq for card ID etc. Use that as min card clock. We need to
+ * know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY.
+ */
+#define MIN_FREQ 400000
+
+/*
+ * The Arasan has a bugette whereby it may lose the content of successive
+ * writes to registers that are within two SD-card clock cycles of each other
+ * (a clock domain crossing problem). It seems, however, that the data
+ * register does not have this problem, which is just as well - otherwise we'd
+ * have to nobble the DMA engine too.
+ *
+ * This should probably be dynamically calculated based on the actual card
+ * frequency. However, this is the longest we'll have to wait, and doesn't
+ * seem to slow access down too much, so the added complexity doesn't seem
+ * worth it for now.
+ *
+ * 1/MIN_FREQ is (max) time per tick of eMMC clock.
+ * 2/MIN_FREQ is time for two ticks.
+ * Multiply by 1000000 to get uS per two ticks.
+ * *1000000 for uSecs.
+ * +1 for hack rounding.
+ */
+#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1)
+
+struct bcm2835_sdhci {
+ u32 shadow;
+};
+
+static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ writel(val, host->ioaddr + reg);
+
+ udelay(BCM2835_SDHCI_WRITE_DELAY);
+}
+
+static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg)
+{
+ u32 val = readl(host->ioaddr + reg);
+
+ if (reg == SDHCI_CAPABILITIES)
+ val |= SDHCI_CAN_VDD_330;
+
+ return val;
+}
+
+static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv;
+ u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow :
+ bcm2835_sdhci_readl(host, reg & ~3);
+ u32 word_num = (reg >> 1) & 1;
+ u32 word_shift = word_num * 16;
+ u32 mask = 0xffff << word_shift;
+ u32 newval = (oldval & ~mask) | (val << word_shift);
+
+ if (reg == SDHCI_TRANSFER_MODE)
+ bcm2835_host->shadow = newval;
+ else
+ bcm2835_sdhci_writel(host, newval, reg & ~3);
+}
+
+static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg)
+{
+ u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
+ u32 word_num = (reg >> 1) & 1;
+ u32 word_shift = word_num * 16;
+ u32 word = (val >> word_shift) & 0xffff;
+
+ return word;
+}
+
+static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ u32 oldval = bcm2835_sdhci_readl(host, reg & ~3);
+ u32 byte_num = reg & 3;
+ u32 byte_shift = byte_num * 8;
+ u32 mask = 0xff << byte_shift;
+ u32 newval = (oldval & ~mask) | (val << byte_shift);
+
+ bcm2835_sdhci_writel(host, newval, reg & ~3);
+}
+
+static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg)
+{
+ u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
+ u32 byte_num = reg & 3;
+ u32 byte_shift = byte_num * 8;
+ u32 byte = (val >> byte_shift) & 0xff;
+
+ return byte;
+}
+
+unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host)
+{
+ return MIN_FREQ;
+}
+
+static struct sdhci_ops bcm2835_sdhci_ops = {
+ .write_l = bcm2835_sdhci_writel,
+ .write_w = bcm2835_sdhci_writew,
+ .write_b = bcm2835_sdhci_writeb,
+ .read_l = bcm2835_sdhci_readl,
+ .read_w = bcm2835_sdhci_readw,
+ .read_b = bcm2835_sdhci_readb,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_min_clock = bcm2835_sdhci_get_min_clock,
+};
+
+static struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+ .ops = &bcm2835_sdhci_ops,
+};
+
+static int bcm2835_sdhci_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct bcm2835_sdhci *bcm2835_host;
+ struct sdhci_pltfm_host *pltfm_host;
+ int ret;
+
+ host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ bcm2835_host = devm_kzalloc(&pdev->dev, sizeof(*bcm2835_host),
+ GFP_KERNEL);
+ if (!bcm2835_host) {
+ dev_err(mmc_dev(host->mmc),
+ "failed to allocate bcm2835_sdhci\n");
+ return -ENOMEM;
+ }
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = bcm2835_host;
+
+ pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pltfm_host->clk)) {
+ ret = PTR_ERR(pltfm_host->clk);
+ goto err;
+ }
+
+ return sdhci_add_host(host);
+
+err:
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static int bcm2835_sdhci_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+
+ sdhci_remove_host(host, dead);
+ sdhci_pltfm_free(pdev);
+
+ return 0;
+}
+
+static const struct of_device_id bcm2835_sdhci_of_match[] = {
+ { .compatible = "brcm,bcm2835-sdhci" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match);
+
+static struct platform_driver bcm2835_sdhci_driver = {
+ .driver = {
+ .name = "sdhci-bcm2835",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_sdhci_of_match,
+ .pm = SDHCI_PLTFM_PMOPS,
+ },
+ .probe = bcm2835_sdhci_probe,
+ .remove = bcm2835_sdhci_remove,
+};
+module_platform_driver(bcm2835_sdhci_driver);
+
+MODULE_DESCRIPTION("BCM2835 SDHCI driver");
+MODULE_AUTHOR("Stephen Warren");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
index 28a870804f60..30bfdc4ae52a 100644
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -95,12 +95,12 @@ static struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
SDHCI_QUIRK_NONSTANDARD_CLOCK,
};
-static int __devinit sdhci_cns3xxx_probe(struct platform_device *pdev)
+static int sdhci_cns3xxx_probe(struct platform_device *pdev)
{
return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata);
}
-static int __devexit sdhci_cns3xxx_remove(struct platform_device *pdev)
+static int sdhci_cns3xxx_remove(struct platform_device *pdev)
{
return sdhci_pltfm_unregister(pdev);
}
@@ -112,7 +112,7 @@ static struct platform_driver sdhci_cns3xxx_driver = {
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_cns3xxx_probe,
- .remove = __devexit_p(sdhci_cns3xxx_remove),
+ .remove = sdhci_cns3xxx_remove,
};
module_platform_driver(sdhci_cns3xxx_driver);
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 90140eb03e36..169fab91778e 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -19,19 +19,30 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
#include <linux/mmc/host.h>
+#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
#include "sdhci-pltfm.h"
struct sdhci_dove_priv {
struct clk *clk;
+ int gpio_cd;
};
+static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data)
+{
+ struct sdhci_host *host = data;
+
+ tasklet_schedule(&host->card_tasklet);
+ return IRQ_HANDLED;
+}
+
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
{
u16 ret;
@@ -49,16 +60,25 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_dove_priv *priv = pltfm_host->priv;
u32 ret;
+ ret = readl(host->ioaddr + reg);
+
switch (reg) {
case SDHCI_CAPABILITIES:
- ret = readl(host->ioaddr + reg);
/* Mask the support for 3.0V */
ret &= ~SDHCI_CAN_VDD_300;
break;
- default:
- ret = readl(host->ioaddr + reg);
+ case SDHCI_PRESENT_STATE:
+ if (gpio_is_valid(priv->gpio_cd)) {
+ if (gpio_get_value(priv->gpio_cd) == 0)
+ ret |= SDHCI_CARD_PRESENT;
+ else
+ ret &= ~SDHCI_CARD_PRESENT;
+ }
+ break;
}
return ret;
}
@@ -77,57 +97,107 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT,
};
-static int __devinit sdhci_dove_probe(struct platform_device *pdev)
+static int sdhci_dove_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_dove_priv *priv;
int ret;
- ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata);
- if (ret)
- goto sdhci_dove_register_fail;
-
priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv),
GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "unable to allocate private data");
- ret = -ENOMEM;
- goto sdhci_dove_allocate_fail;
+ return -ENOMEM;
+ }
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (pdev->dev.of_node) {
+ priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
+ "cd-gpios", 0);
+ } else {
+ priv->gpio_cd = -EINVAL;
+ }
+
+ if (gpio_is_valid(priv->gpio_cd)) {
+ ret = gpio_request(priv->gpio_cd, "sdhci-cd");
+ if (ret) {
+ dev_err(&pdev->dev, "card detect gpio request failed: %d\n",
+ ret);
+ return ret;
+ }
+ gpio_direction_input(priv->gpio_cd);
+ }
+
+ host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata);
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ goto err_sdhci_pltfm_init;
}
- host = platform_get_drvdata(pdev);
pltfm_host = sdhci_priv(host);
pltfm_host->priv = priv;
- priv->clk = clk_get(&pdev->dev, NULL);
if (!IS_ERR(priv->clk))
clk_prepare_enable(priv->clk);
+
+ sdhci_get_of_property(pdev);
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_sdhci_add;
+
+ /*
+ * We must request the IRQ after sdhci_add_host(), as the tasklet only
+ * gets setup in sdhci_add_host() and we oops.
+ */
+ if (gpio_is_valid(priv->gpio_cd)) {
+ ret = request_irq(gpio_to_irq(priv->gpio_cd),
+ sdhci_dove_carddetect_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ mmc_hostname(host->mmc), host);
+ if (ret) {
+ dev_err(&pdev->dev, "card detect irq request failed: %d\n",
+ ret);
+ goto err_request_irq;
+ }
+ }
+
return 0;
-sdhci_dove_allocate_fail:
- sdhci_pltfm_unregister(pdev);
-sdhci_dove_register_fail:
+err_request_irq:
+ sdhci_remove_host(host, 0);
+err_sdhci_add:
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+ sdhci_pltfm_free(pdev);
+err_sdhci_pltfm_init:
+ if (gpio_is_valid(priv->gpio_cd))
+ gpio_free(priv->gpio_cd);
return ret;
}
-static int __devexit sdhci_dove_remove(struct platform_device *pdev)
+static int sdhci_dove_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_dove_priv *priv = pltfm_host->priv;
- if (priv->clk) {
- if (!IS_ERR(priv->clk)) {
- clk_disable_unprepare(priv->clk);
- clk_put(priv->clk);
- }
- devm_kfree(&pdev->dev, priv->clk);
+ sdhci_pltfm_unregister(pdev);
+
+ if (gpio_is_valid(priv->gpio_cd)) {
+ free_irq(gpio_to_irq(priv->gpio_cd), host);
+ gpio_free(priv->gpio_cd);
}
- return sdhci_pltfm_unregister(pdev);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
}
-static const struct of_device_id sdhci_dove_of_match_table[] __devinitdata = {
+static const struct of_device_id sdhci_dove_of_match_table[] = {
{ .compatible = "marvell,dove-sdhci", },
{}
};
@@ -141,7 +211,7 @@ static struct platform_driver sdhci_dove_driver = {
.of_match_table = of_match_ptr(sdhci_dove_of_match_table),
},
.probe = sdhci_dove_probe,
- .remove = __devexit_p(sdhci_dove_remove),
+ .remove = sdhci_dove_remove,
};
module_platform_driver(sdhci_dove_driver);
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index effc2acfe778..78ac00227c1a 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -21,6 +21,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
@@ -29,12 +30,22 @@
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
-#define SDHCI_CTRL_D3CD 0x08
+#define ESDHC_CTRL_D3CD 0x08
/* VENDOR SPEC register */
-#define SDHCI_VENDOR_SPEC 0xC0
-#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
-#define SDHCI_WTMK_LVL 0x44
-#define SDHCI_MIX_CTRL 0x48
+#define ESDHC_VENDOR_SPEC 0xc0
+#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
+#define ESDHC_WTMK_LVL 0x44
+#define ESDHC_MIX_CTRL 0x48
+#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
+/* Bits 3 and 6 are not SDHCI standard definitions */
+#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
+
+/*
+ * Our interpretation of the SDHCI_HOST_CONTROL register
+ */
+#define ESDHC_CTRL_4BITBUS (0x1 << 1)
+#define ESDHC_CTRL_8BITBUS (0x2 << 1)
+#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
/*
* There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC:
@@ -42,7 +53,7 @@
* but bit28 is used as the INT DMA ERR in fsl eSDHC design.
* Define this macro DMA error INT for fsl eSDHC
*/
-#define SDHCI_INT_VENDOR_SPEC_DMA_ERR 0x10000000
+#define ESDHC_INT_VENDOR_SPEC_DMA_ERR (1 << 28)
/*
* The CMDTYPE of the CMD register (offset 0xE) should be set to
@@ -143,23 +154,8 @@ static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, i
static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct pltfm_imx_data *imx_data = pltfm_host->priv;
- struct esdhc_platform_data *boarddata = &imx_data->boarddata;
-
- /* fake CARD_PRESENT flag */
u32 val = readl(host->ioaddr + reg);
- if (unlikely((reg == SDHCI_PRESENT_STATE)
- && gpio_is_valid(boarddata->cd_gpio))) {
- if (gpio_get_value(boarddata->cd_gpio))
- /* no card, if a valid gpio says so... */
- val &= ~SDHCI_CARD_PRESENT;
- else
- /* ... in all other cases assume card is present */
- val |= SDHCI_CARD_PRESENT;
- }
-
if (unlikely(reg == SDHCI_CAPABILITIES)) {
/* In FSL esdhc IC module, only bit20 is used to indicate the
* ADMA2 capability of esdhc, but this bit is messed up on
@@ -175,8 +171,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
}
if (unlikely(reg == SDHCI_INT_STATUS)) {
- if (val & SDHCI_INT_VENDOR_SPEC_DMA_ERR) {
- val &= ~SDHCI_INT_VENDOR_SPEC_DMA_ERR;
+ if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
+ val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
val |= SDHCI_INT_ADMA_ERROR;
}
}
@@ -188,17 +184,9 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
- struct esdhc_platform_data *boarddata = &imx_data->boarddata;
u32 data;
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
- if (boarddata->cd_type == ESDHC_CD_GPIO)
- /*
- * These interrupts won't work with a custom
- * card_detect gpio (only applied to mx25/35)
- */
- val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
-
if (val & SDHCI_INT_CARD_INT) {
/*
* Clear and then set D3CD bit to avoid missing the
@@ -209,9 +197,9 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
* re-sample it by the following steps.
*/
data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
- data &= ~SDHCI_CTRL_D3CD;
+ data &= ~ESDHC_CTRL_D3CD;
writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
- data |= SDHCI_CTRL_D3CD;
+ data |= ESDHC_CTRL_D3CD;
writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
}
}
@@ -220,15 +208,15 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
&& (reg == SDHCI_INT_STATUS)
&& (val & SDHCI_INT_DATA_END))) {
u32 v;
- v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
- v &= ~SDHCI_VENDOR_SPEC_SDIO_QUIRK;
- writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
+ v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+ v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
+ writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
}
if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
if (val & SDHCI_INT_ADMA_ERROR) {
val &= ~SDHCI_INT_ADMA_ERROR;
- val |= SDHCI_INT_VENDOR_SPEC_DMA_ERR;
+ val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
}
}
@@ -237,15 +225,18 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = pltfm_host->priv;
+
if (unlikely(reg == SDHCI_HOST_VERSION)) {
- u16 val = readw(host->ioaddr + (reg ^ 2));
- /*
- * uSDHC supports SDHCI v3.0, but it's encoded as value
- * 0x3 in host controller version register, which violates
- * SDHCI_SPEC_300 definition. Work it around here.
- */
- if ((val & SDHCI_SPEC_VER_MASK) == 3)
- return --val;
+ reg ^= 2;
+ if (is_imx6q_usdhc(imx_data)) {
+ /*
+ * The usdhc register returns a wrong host version.
+ * Correct it here.
+ */
+ return SDHCI_SPEC_300;
+ }
}
return readw(host->ioaddr + reg);
@@ -258,20 +249,32 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
switch (reg) {
case SDHCI_TRANSFER_MODE:
- /*
- * Postpone this write, we must do it together with a
- * command write that is down below.
- */
if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
&& (host->cmd->data->blocks > 1)
&& (host->cmd->data->flags & MMC_DATA_READ)) {
u32 v;
- v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
- v |= SDHCI_VENDOR_SPEC_SDIO_QUIRK;
- writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
+ v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
+ v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
+ writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
+ }
+
+ if (is_imx6q_usdhc(imx_data)) {
+ u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ /* Swap AC23 bit */
+ if (val & SDHCI_TRNS_AUTO_CMD23) {
+ val &= ~SDHCI_TRNS_AUTO_CMD23;
+ val |= ESDHC_MIX_CTRL_AC23EN;
+ }
+ m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
+ writel(m, host->ioaddr + ESDHC_MIX_CTRL);
+ } else {
+ /*
+ * Postpone this write, we must do it together with a
+ * command write that is down below.
+ */
+ imx_data->scratchpad = val;
}
- imx_data->scratchpad = val;
return;
case SDHCI_COMMAND:
if ((host->cmd->opcode == MMC_STOP_TRANSMISSION ||
@@ -279,16 +282,12 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
(imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
val |= SDHCI_CMD_ABORTCMD;
- if (is_imx6q_usdhc(imx_data)) {
- u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL);
- m = imx_data->scratchpad | (m & 0xffff0000);
- writel(m, host->ioaddr + SDHCI_MIX_CTRL);
+ if (is_imx6q_usdhc(imx_data))
writel(val << 16,
host->ioaddr + SDHCI_TRANSFER_MODE);
- } else {
+ else
writel(val << 16 | imx_data->scratchpad,
host->ioaddr + SDHCI_TRANSFER_MODE);
- }
return;
case SDHCI_BLOCK_SIZE:
val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
@@ -302,6 +301,7 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
u32 new_val;
+ u32 mask;
switch (reg) {
case SDHCI_POWER_CONTROL:
@@ -311,10 +311,8 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
*/
return;
case SDHCI_HOST_CONTROL:
- /* FSL messed up here, so we can just keep those three */
- new_val = val & (SDHCI_CTRL_LED | \
- SDHCI_CTRL_4BITBUS | \
- SDHCI_CTRL_D3CD);
+ /* FSL messed up here, so we need to manually compose it. */
+ new_val = val & SDHCI_CTRL_LED;
/* ensure the endianness */
new_val |= ESDHC_HOST_CONTROL_LE;
/* bits 8&9 are reserved on mx25 */
@@ -323,7 +321,13 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
}
- esdhc_clrset_le(host, 0xffff, new_val, reg);
+ /*
+ * Do not touch buswidth bits here. This is done in
+ * esdhc_pltfm_bus_width.
+ */
+ mask = 0xffff & ~ESDHC_CTRL_BUSWIDTH_MASK;
+
+ esdhc_clrset_le(host, mask, new_val, reg);
return;
}
esdhc_clrset_le(host, 0xff, val, reg);
@@ -336,15 +340,15 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
* circuit relies on. To work around it, we turn the clocks on back
* to keep card detection circuit functional.
*/
- if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1))
+ if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1)) {
esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
-}
-
-static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-
- return clk_get_rate(pltfm_host->clk);
+ /*
+ * The reset on usdhc fails to clear MIX_CTRL register.
+ * Do it manually here.
+ */
+ if (is_imx6q_usdhc(imx_data))
+ writel(0, host->ioaddr + ESDHC_MIX_CTRL);
+ }
}
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
@@ -362,8 +366,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
switch (boarddata->wp_type) {
case ESDHC_WP_GPIO:
- if (gpio_is_valid(boarddata->wp_gpio))
- return gpio_get_value(boarddata->wp_gpio);
+ return mmc_gpio_get_ro(host->mmc);
case ESDHC_WP_CONTROLLER:
return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
SDHCI_WRITE_PROTECT);
@@ -374,6 +377,28 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
return -ENOSYS;
}
+static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
+{
+ u32 ctrl;
+
+ switch (width) {
+ case MMC_BUS_WIDTH_8:
+ ctrl = ESDHC_CTRL_8BITBUS;
+ break;
+ case MMC_BUS_WIDTH_4:
+ ctrl = ESDHC_CTRL_4BITBUS;
+ break;
+ default:
+ ctrl = 0;
+ break;
+ }
+
+ esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
+ SDHCI_HOST_CONTROL);
+
+ return 0;
+}
+
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl_le,
.read_w = esdhc_readw_le,
@@ -381,9 +406,10 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.write_w = esdhc_writew_le,
.write_b = esdhc_writeb_le,
.set_clock = esdhc_set_clock,
- .get_max_clock = esdhc_pltfm_get_max_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock,
.get_ro = esdhc_pltfm_get_ro,
+ .platform_bus_width = esdhc_pltfm_bus_width,
};
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@@ -394,16 +420,8 @@ static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
.ops = &sdhci_esdhc_ops,
};
-static irqreturn_t cd_irq(int irq, void *data)
-{
- struct sdhci_host *sdhost = (struct sdhci_host *)data;
-
- tasklet_schedule(&sdhost->card_tasklet);
- return IRQ_HANDLED;
-};
-
#ifdef CONFIG_OF
-static int __devinit
+static int
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
struct esdhc_platform_data *boarddata)
{
@@ -429,6 +447,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
if (gpio_is_valid(boarddata->wp_gpio))
boarddata->wp_type = ESDHC_WP_GPIO;
+ of_property_read_u32(np, "bus-width", &boarddata->max_bus_width);
+
return 0;
}
#else
@@ -440,7 +460,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
}
#endif
-static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
+static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(imx_esdhc_dt_ids, &pdev->dev);
@@ -456,10 +476,10 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
- imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
+ imx_data = devm_kzalloc(&pdev->dev, sizeof(*imx_data), GFP_KERNEL);
if (!imx_data) {
err = -ENOMEM;
- goto err_imx_data;
+ goto free_sdhci;
}
if (of_id)
@@ -470,19 +490,19 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx_data->clk_ipg)) {
err = PTR_ERR(imx_data->clk_ipg);
- goto err_clk_get;
+ goto free_sdhci;
}
imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(imx_data->clk_ahb)) {
err = PTR_ERR(imx_data->clk_ahb);
- goto err_clk_get;
+ goto free_sdhci;
}
imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(imx_data->clk_per)) {
err = PTR_ERR(imx_data->clk_per);
- goto err_clk_get;
+ goto free_sdhci;
}
pltfm_host->clk = imx_data->clk_per;
@@ -494,7 +514,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(imx_data->pinctrl)) {
err = PTR_ERR(imx_data->pinctrl);
- goto pin_err;
+ goto disable_clk;
}
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
@@ -512,14 +532,14 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
* to something insane. Change it back here.
*/
if (is_imx6q_usdhc(imx_data))
- writel(0x08100810, host->ioaddr + SDHCI_WTMK_LVL);
+ writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
boarddata = &imx_data->boarddata;
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
if (!host->mmc->parent->platform_data) {
dev_err(mmc_dev(host->mmc), "no board data!\n");
err = -EINVAL;
- goto no_board_data;
+ goto disable_clk;
}
imx_data->boarddata = *((struct esdhc_platform_data *)
host->mmc->parent->platform_data);
@@ -527,35 +547,23 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
/* write_protect */
if (boarddata->wp_type == ESDHC_WP_GPIO) {
- err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
+ err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
if (err) {
- dev_warn(mmc_dev(host->mmc),
- "no write-protect pin available!\n");
- boarddata->wp_gpio = -EINVAL;
+ dev_err(mmc_dev(host->mmc),
+ "failed to request write-protect gpio!\n");
+ goto disable_clk;
}
- } else {
- boarddata->wp_gpio = -EINVAL;
+ host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
}
/* card_detect */
- if (boarddata->cd_type != ESDHC_CD_GPIO)
- boarddata->cd_gpio = -EINVAL;
-
switch (boarddata->cd_type) {
case ESDHC_CD_GPIO:
- err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
+ err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio);
if (err) {
dev_err(mmc_dev(host->mmc),
- "no card-detect pin available!\n");
- goto no_card_detect_pin;
- }
-
- err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- mmc_hostname(host->mmc), host);
- if (err) {
- dev_err(mmc_dev(host->mmc), "request irq error\n");
- goto no_card_detect_irq;
+ "failed to request card-detect gpio!\n");
+ goto disable_clk;
}
/* fall through */
@@ -572,57 +580,47 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
break;
}
+ switch (boarddata->max_bus_width) {
+ case 8:
+ host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA;
+ break;
+ case 4:
+ host->mmc->caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 1:
+ default:
+ host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+ break;
+ }
+
err = sdhci_add_host(host);
if (err)
- goto err_add_host;
+ goto disable_clk;
return 0;
-err_add_host:
- if (gpio_is_valid(boarddata->cd_gpio))
- free_irq(gpio_to_irq(boarddata->cd_gpio), host);
-no_card_detect_irq:
- if (gpio_is_valid(boarddata->cd_gpio))
- gpio_free(boarddata->cd_gpio);
- if (gpio_is_valid(boarddata->wp_gpio))
- gpio_free(boarddata->wp_gpio);
-no_card_detect_pin:
-no_board_data:
-pin_err:
+disable_clk:
clk_disable_unprepare(imx_data->clk_per);
clk_disable_unprepare(imx_data->clk_ipg);
clk_disable_unprepare(imx_data->clk_ahb);
-err_clk_get:
- kfree(imx_data);
-err_imx_data:
+free_sdhci:
sdhci_pltfm_free(pdev);
return err;
}
-static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
+static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
- struct esdhc_platform_data *boarddata = &imx_data->boarddata;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
- if (gpio_is_valid(boarddata->wp_gpio))
- gpio_free(boarddata->wp_gpio);
-
- if (gpio_is_valid(boarddata->cd_gpio)) {
- free_irq(gpio_to_irq(boarddata->cd_gpio), host);
- gpio_free(boarddata->cd_gpio);
- }
-
clk_disable_unprepare(imx_data->clk_per);
clk_disable_unprepare(imx_data->clk_ipg);
clk_disable_unprepare(imx_data->clk_ahb);
- kfree(imx_data);
-
sdhci_pltfm_free(pdev);
return 0;
@@ -637,7 +635,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
},
.id_table = imx_esdhc_devtype,
.probe = sdhci_esdhc_imx_probe,
- .remove = __devexit_p(sdhci_esdhc_imx_remove),
+ .remove = sdhci_esdhc_imx_remove,
};
module_platform_driver(sdhci_esdhc_imx_driver);
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index ae5fcbfa1eef..f32526d2d966 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -22,6 +22,7 @@
#include "sdhci-esdhc.h"
#define VENDOR_V_22 0x12
+#define VENDOR_V_23 0x13
static u32 esdhc_readl(struct sdhci_host *host, int reg)
{
u32 ret;
@@ -85,6 +86,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg)
return ret;
}
+static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ /*
+ * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
+ * when SYSCTL[RSTD]) is set for some special operations.
+ * No any impact other operation.
+ */
+ if (reg == SDHCI_INT_ENABLE)
+ val |= SDHCI_INT_BLK_GAP;
+ sdhci_be32bs_writel(host, val, reg);
+}
+
static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
{
if (reg == SDHCI_BLOCK_SIZE) {
@@ -121,6 +134,41 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
sdhci_be32bs_writeb(host, val, reg);
}
+/*
+ * For Abort or Suspend after Stop at Block Gap, ignore the ADMA
+ * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC])
+ * and Block Gap Event(IRQSTAT[BGE]) are also set.
+ * For Continue, apply soft reset for data(SYSCTL[RSTD]);
+ * and re-issue the entire read transaction from beginning.
+ */
+static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
+{
+ u32 tmp;
+ bool applicable;
+ dma_addr_t dmastart;
+ dma_addr_t dmanow;
+
+ tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
+ tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
+
+ applicable = (intmask & SDHCI_INT_DATA_END) &&
+ (intmask & SDHCI_INT_BLK_GAP) &&
+ (tmp == VENDOR_V_23);
+ if (!applicable)
+ return;
+
+ host->data->error = 0;
+ dmastart = sg_dma_address(host->data->sg);
+ dmanow = dmastart + host->data->bytes_xfered;
+ /*
+ * Force update to the next DMA block boundary.
+ */
+ dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) +
+ SDHCI_DEFAULT_BOUNDARY_SIZE;
+ host->data->bytes_xfered = dmanow - dmastart;
+ sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS);
+}
+
static int esdhc_of_enable_dma(struct sdhci_host *host)
{
setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
@@ -169,21 +217,36 @@ static void esdhc_of_resume(struct sdhci_host *host)
}
#endif
+static void esdhc_of_platform_init(struct sdhci_host *host)
+{
+ u32 vvn;
+
+ vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
+ vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
+ if (vvn == VENDOR_V_22)
+ host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
+
+ if (vvn > VENDOR_V_22)
+ host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
+}
+
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl,
.read_w = esdhc_readw,
.read_b = esdhc_readb,
- .write_l = sdhci_be32bs_writel,
+ .write_l = esdhc_writel,
.write_w = esdhc_writew,
.write_b = esdhc_writeb,
.set_clock = esdhc_of_set_clock,
.enable_dma = esdhc_of_enable_dma,
.get_max_clock = esdhc_of_get_max_clock,
.get_min_clock = esdhc_of_get_min_clock,
+ .platform_init = esdhc_of_platform_init,
#ifdef CONFIG_PM
.platform_suspend = esdhc_of_suspend,
.platform_resume = esdhc_of_resume,
#endif
+ .adma_workaround = esdhci_of_adma_workaround,
};
static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
@@ -197,12 +260,12 @@ static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
.ops = &sdhci_esdhc_ops,
};
-static int __devinit sdhci_esdhc_probe(struct platform_device *pdev)
+static int sdhci_esdhc_probe(struct platform_device *pdev)
{
return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata);
}
-static int __devexit sdhci_esdhc_remove(struct platform_device *pdev)
+static int sdhci_esdhc_remove(struct platform_device *pdev)
{
return sdhci_pltfm_unregister(pdev);
}
@@ -223,7 +286,7 @@ static struct platform_driver sdhci_esdhc_driver = {
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_esdhc_probe,
- .remove = __devexit_p(sdhci_esdhc_remove),
+ .remove = sdhci_esdhc_remove,
};
module_platform_driver(sdhci_esdhc_driver);
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
index 0ce088ae0228..c3d3715ec3d7 100644
--- a/drivers/mmc/host/sdhci-of-hlwd.c
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -66,12 +66,12 @@ static struct sdhci_pltfm_data sdhci_hlwd_pdata = {
.ops = &sdhci_hlwd_ops,
};
-static int __devinit sdhci_hlwd_probe(struct platform_device *pdev)
+static int sdhci_hlwd_probe(struct platform_device *pdev)
{
return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata);
}
-static int __devexit sdhci_hlwd_remove(struct platform_device *pdev)
+static int sdhci_hlwd_remove(struct platform_device *pdev)
{
return sdhci_pltfm_unregister(pdev);
}
@@ -90,7 +90,7 @@ static struct platform_driver sdhci_hlwd_driver = {
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_hlwd_probe,
- .remove = __devexit_p(sdhci_hlwd_remove),
+ .remove = sdhci_hlwd_remove,
};
module_platform_driver(sdhci_hlwd_driver);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 4bb74b042a06..c7ccf3034dad 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -114,6 +114,7 @@ static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
SDHCI_TIMEOUT_CLK_UNIT |
SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_DO_HISPD |
SDHCI_CAN_DO_SDMA;
return 0;
}
@@ -653,7 +654,7 @@ static const struct sdhci_pci_fixes sdhci_via = {
.probe = via_probe,
};
-static const struct pci_device_id pci_ids[] __devinitconst = {
+static const struct pci_device_id pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C822,
@@ -934,7 +935,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
return 0;
}
-static int sdhci_pci_8bit_width(struct sdhci_host *host, int width)
+static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
@@ -976,7 +977,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
static struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma,
- .platform_8bit_width = sdhci_pci_8bit_width,
+ .platform_bus_width = sdhci_pci_bus_width,
.hw_reset = sdhci_pci_hw_reset,
};
@@ -1183,7 +1184,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
* *
\*****************************************************************************/
-static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
+static struct sdhci_pci_slot *sdhci_pci_probe_slot(
struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
int slotno)
{
@@ -1196,7 +1197,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
return ERR_PTR(-ENODEV);
}
- if (pci_resource_len(pdev, bar) != 0x100) {
+ if (pci_resource_len(pdev, bar) < 0x100) {
dev_err(&pdev->dev, "Invalid iomem size. You may "
"experience problems.\n");
}
@@ -1338,7 +1339,7 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_free_host(slot->host);
}
-static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev)
+static void sdhci_pci_runtime_pm_allow(struct device *dev)
{
pm_runtime_put_noidle(dev);
pm_runtime_allow(dev);
@@ -1347,13 +1348,13 @@ static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev)
pm_suspend_ignore_children(dev, 1);
}
-static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev)
+static void sdhci_pci_runtime_pm_forbid(struct device *dev)
{
pm_runtime_forbid(dev);
pm_runtime_get_noresume(dev);
}
-static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
+static int sdhci_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct sdhci_pci_chip *chip;
@@ -1445,7 +1446,7 @@ err:
return ret;
}
-static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
+static void sdhci_pci_remove(struct pci_dev *pdev)
{
int i;
struct sdhci_pci_chip *chip;
@@ -1470,7 +1471,7 @@ static struct pci_driver sdhci_driver = {
.name = "sdhci-pci",
.id_table = pci_ids,
.probe = sdhci_pci_probe,
- .remove = __devexit_p(sdhci_pci_remove),
+ .remove = sdhci_pci_remove,
.driver = {
.pm = &sdhci_pci_pm_ops
},
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 65551a9709cc..3145a780b035 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -36,6 +36,14 @@
#endif
#include "sdhci-pltfm.h"
+unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ return clk_get_rate(pltfm_host->clk);
+}
+EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
+
static struct sdhci_ops sdhci_pltfm_ops = {
};
@@ -78,6 +86,9 @@ void sdhci_get_of_property(struct platform_device *pdev)
if (of_get_property(np, "broken-cd", NULL))
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ if (of_get_property(np, "no-1-8-v", NULL))
+ host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
@@ -89,6 +100,12 @@ void sdhci_get_of_property(struct platform_device *pdev)
clk = of_get_property(np, "clock-frequency", &size);
if (clk && size == sizeof(*clk) && *clk)
pltfm_host->clock = be32_to_cpup(clk);
+
+ if (of_find_property(np, "keep-power-in-suspend", NULL))
+ host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+
+ if (of_find_property(np, "enable-sdio-wakeup", NULL))
+ host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
}
#else
@@ -150,6 +167,13 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
goto err_remap;
}
+ /*
+ * Some platforms need to probe the controller to be able to
+ * determine which caps should be used.
+ */
+ if (host->ops && host->ops->platform_init)
+ host->ops->platform_init(host);
+
platform_set_drvdata(pdev, host);
return host;
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index 37e0e184a0bb..153b6c509ebe 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -98,6 +98,8 @@ extern int sdhci_pltfm_register(struct platform_device *pdev,
struct sdhci_pltfm_data *pdata);
extern int sdhci_pltfm_unregister(struct platform_device *pdev);
+extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
+
#ifdef CONFIG_PM
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index 8e63a9c04e31..eeb7d439db1d 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -111,17 +111,10 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
return 0;
}
-static u32 pxav2_get_max_clock(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-
- return clk_get_rate(pltfm_host->clk);
-}
-
static struct sdhci_ops pxav2_sdhci_ops = {
- .get_max_clock = pxav2_get_max_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.platform_reset_exit = pxav2_set_private_registers,
- .platform_8bit_width = pxav2_mmc_set_width,
+ .platform_bus_width = pxav2_mmc_set_width,
};
#ifdef CONFIG_OF
@@ -166,7 +159,7 @@ static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev)
}
#endif
-static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
+static int sdhci_pxav2_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
@@ -247,7 +240,7 @@ err_clk_get:
return ret;
}
-static int __devexit sdhci_pxav2_remove(struct platform_device *pdev)
+static int sdhci_pxav2_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -275,7 +268,7 @@ static struct platform_driver sdhci_pxav2_driver = {
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_pxav2_probe,
- .remove = __devexit_p(sdhci_pxav2_remove),
+ .remove = sdhci_pxav2_remove,
};
module_platform_driver(sdhci_pxav2_driver);
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index e918a2bb3af1..a0cdbc570a83 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -32,10 +32,14 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
+#define PXAV3_RPM_DELAY_MS 50
+
#define SD_CLOCK_BURST_SIZE_SETUP 0x10A
#define SDCLK_SEL 0x100
#define SDCLK_DELAY_SHIFT 9
@@ -167,6 +171,7 @@ static struct sdhci_ops pxav3_sdhci_ops = {
.platform_reset_exit = pxav3_set_private_registers,
.set_uhs_signaling = pxav3_set_uhs_signaling,
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
};
#ifdef CONFIG_OF
@@ -214,7 +219,7 @@ static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
}
#endif
-static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
+static int sdhci_pxav3_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
@@ -249,7 +254,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
- | SDHCI_QUIRK_32BIT_ADMA_SIZE;
+ | SDHCI_QUIRK_32BIT_ADMA_SIZE
+ | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
@@ -271,6 +277,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
if (pdata->quirks)
host->quirks |= pdata->quirks;
+ if (pdata->quirks2)
+ host->quirks2 |= pdata->quirks2;
if (pdata->host_caps)
host->mmc->caps |= pdata->host_caps;
if (pdata->host_caps2)
@@ -292,20 +300,37 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_suspend_ignore_children(&pdev->dev, 1);
+ pm_runtime_get_noresume(&pdev->dev);
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "failed to add host\n");
+ pm_runtime_forbid(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
goto err_add_host;
}
platform_set_drvdata(pdev, host);
+ if (pdata->pm_caps & MMC_PM_KEEP_POWER) {
+ device_init_wakeup(&pdev->dev, 1);
+ host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ;
+ } else {
+ device_init_wakeup(&pdev->dev, 0);
+ }
+
+ pm_runtime_put_autosuspend(&pdev->dev);
+
return 0;
err_add_host:
clk_disable_unprepare(clk);
clk_put(clk);
- mmc_gpio_free_cd(host->mmc);
err_cd_req:
err_clk_get:
sdhci_pltfm_free(pdev);
@@ -313,21 +338,19 @@ err_clk_get:
return ret;
}
-static int __devexit sdhci_pxav3_remove(struct platform_device *pdev)
+static int sdhci_pxav3_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = pltfm_host->priv;
- struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+ pm_runtime_get_sync(&pdev->dev);
sdhci_remove_host(host, 1);
+ pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
- if (gpio_is_valid(pdata->ext_cd_gpio))
- mmc_gpio_free_cd(host->mmc);
-
sdhci_pltfm_free(pdev);
kfree(pxa);
@@ -336,6 +359,83 @@ static int __devexit sdhci_pxav3_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int sdhci_pxav3_suspend(struct device *dev)
+{
+ int ret;
+ struct sdhci_host *host = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+ ret = sdhci_suspend_host(host);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int sdhci_pxav3_resume(struct device *dev)
+{
+ int ret;
+ struct sdhci_host *host = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+ ret = sdhci_resume_host(host);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_pxav3_runtime_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ unsigned long flags;
+
+ if (pltfm_host->clk) {
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ clk_disable_unprepare(pltfm_host->clk);
+ }
+
+ return 0;
+}
+
+static int sdhci_pxav3_runtime_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ unsigned long flags;
+
+ if (pltfm_host->clk) {
+ clk_prepare_enable(pltfm_host->clk);
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = false;
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops sdhci_pxav3_pmops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
+ SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
+ sdhci_pxav3_runtime_resume, NULL)
+};
+
+#define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops)
+
+#else
+#define SDHCI_PXAV3_PMOPS NULL
+#endif
+
static struct platform_driver sdhci_pxav3_driver = {
.driver = {
.name = "sdhci-pxav3",
@@ -343,10 +443,10 @@ static struct platform_driver sdhci_pxav3_driver = {
.of_match_table = sdhci_pxav3_of_match,
#endif
.owner = THIS_MODULE,
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = SDHCI_PXAV3_PMOPS,
},
.probe = sdhci_pxav3_probe,
- .remove = __devexit_p(sdhci_pxav3_remove),
+ .remove = sdhci_pxav3_remove,
};
module_platform_driver(sdhci_pxav3_driver);
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 2903949594c6..7363efe72287 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -24,6 +24,7 @@
#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/mmc/host.h>
@@ -57,6 +58,7 @@ struct sdhci_s3c {
int ext_cd_irq;
int ext_cd_gpio;
int *gpios;
+ struct pinctrl *pctrl;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
@@ -211,8 +213,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
if (ourhost->cur_clk != best_src) {
struct clk *clk = ourhost->clk_bus[best_src];
- clk_enable(clk);
- clk_disable(ourhost->clk_bus[ourhost->cur_clk]);
+ clk_prepare_enable(clk);
+ clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
/* turn clock off to card before changing clock source */
writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
@@ -330,14 +332,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
}
/**
- * sdhci_s3c_platform_8bit_width - support 8bit buswidth
+ * sdhci_s3c_platform_bus_width - support 8bit buswidth
* @host: The SDHCI host being queried
* @width: MMC_BUS_WIDTH_ macro for the bus width being requested
*
* We have 8-bit width support but is not a v3 controller.
- * So we add platform_8bit_width() and support 8bit width.
+ * So we add platform_bus_width() and support 8bit width.
*/
-static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width)
+static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
@@ -367,24 +369,33 @@ static struct sdhci_ops sdhci_s3c_ops = {
.get_max_clock = sdhci_s3c_get_max_clk,
.set_clock = sdhci_s3c_set_clock,
.get_min_clock = sdhci_s3c_get_min_clock,
- .platform_8bit_width = sdhci_s3c_platform_8bit_width,
+ .platform_bus_width = sdhci_s3c_platform_bus_width,
};
static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
{
struct sdhci_host *host = platform_get_drvdata(dev);
+#ifdef CONFIG_PM_RUNTIME
+ struct sdhci_s3c *sc = sdhci_priv(host);
+#endif
unsigned long flags;
if (host) {
spin_lock_irqsave(&host->lock, flags);
if (state) {
dev_dbg(&dev->dev, "card inserted.\n");
+#ifdef CONFIG_PM_RUNTIME
+ clk_prepare_enable(sc->clk_io);
+#endif
host->flags &= ~SDHCI_DEVICE_DEAD;
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
} else {
dev_dbg(&dev->dev, "card removed.\n");
host->flags |= SDHCI_DEVICE_DEAD;
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+#ifdef CONFIG_PM_RUNTIME
+ clk_disable_unprepare(sc->clk_io);
+#endif
}
tasklet_schedule(&host->card_tasklet);
spin_unlock_irqrestore(&host->lock, flags);
@@ -406,7 +417,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
struct s3c_sdhci_platdata *pdata = sc->pdata;
struct device *dev = &sc->pdev->dev;
- if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
+ if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
sc->ext_cd_gpio = pdata->ext_cd_gpio;
sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
if (sc->ext_cd_irq &&
@@ -430,7 +441,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
}
#ifdef CONFIG_OF
-static int __devinit sdhci_s3c_parse_dt(struct device *dev,
+static int sdhci_s3c_parse_dt(struct device *dev,
struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
{
struct device_node *node = dev->of_node;
@@ -449,12 +460,12 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
return -ENOMEM;
/* get the card detection method */
- if (of_get_property(node, "broken-cd", 0)) {
+ if (of_get_property(node, "broken-cd", NULL)) {
pdata->cd_type = S3C_SDHCI_CD_NONE;
goto setup_bus;
}
- if (of_get_property(node, "non-removable", 0)) {
+ if (of_get_property(node, "non-removable", NULL)) {
pdata->cd_type = S3C_SDHCI_CD_PERMANENT;
goto setup_bus;
}
@@ -477,8 +488,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
return -EINVAL;
}
- dev_info(dev, "assuming no card detect line available\n");
- pdata->cd_type = S3C_SDHCI_CD_NONE;
+ /* assuming internal card detect that will be configured by pinctrl */
+ pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
+ goto setup_bus;
found_cd:
if (pdata->cd_type == S3C_SDHCI_CD_GPIO) {
@@ -487,7 +499,7 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
if (of_get_property(node, "cd-inverted", NULL))
pdata->ext_cd_gpio_invert = 1;
} else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
- ret = gpio_request(gpio, "sdhci-cd");
+ ret = devm_gpio_request(dev, gpio, "sdhci-cd");
if (ret) {
dev_err(dev, "card detect gpio request failed\n");
return -EINVAL;
@@ -496,36 +508,31 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev,
}
setup_bus:
+ if (!IS_ERR(ourhost->pctrl))
+ return 0;
+
/* get the gpios for command, clock and data lines */
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
gpio = of_get_gpio(node, cnt);
if (!gpio_is_valid(gpio)) {
dev_err(dev, "invalid gpio[%d]\n", cnt);
- goto err_free_dt_cd_gpio;
+ return -EINVAL;
}
ourhost->gpios[cnt] = gpio;
}
for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
- ret = gpio_request(ourhost->gpios[cnt], "sdhci-gpio");
+ ret = devm_gpio_request(dev, ourhost->gpios[cnt], "sdhci-gpio");
if (ret) {
dev_err(dev, "gpio[%d] request failed\n", cnt);
- goto err_free_dt_gpios;
+ return -EINVAL;
}
}
return 0;
-
- err_free_dt_gpios:
- while (--cnt >= 0)
- gpio_free(ourhost->gpios[cnt]);
- err_free_dt_cd_gpio:
- if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
- gpio_free(ourhost->ext_cd_gpio);
- return -EINVAL;
}
#else
-static int __devinit sdhci_s3c_parse_dt(struct device *dev,
+static int sdhci_s3c_parse_dt(struct device *dev,
struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
{
return -EINVAL;
@@ -548,7 +555,7 @@ static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
platform_get_device_id(pdev)->driver_data;
}
-static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
+static int sdhci_s3c_probe(struct platform_device *pdev)
{
struct s3c_sdhci_platdata *pdata;
struct sdhci_s3c_drv_data *drv_data;
@@ -579,13 +586,15 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
- goto err_pdata;
+ goto err_pdata_io_clk;
}
+ sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev);
+
if (pdev->dev.of_node) {
ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
if (ret)
- goto err_pdata;
+ goto err_pdata_io_clk;
} else {
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
sc->ext_cd_gpio = -1; /* invalid gpio number */
@@ -603,11 +612,11 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
if (IS_ERR(sc->clk_io)) {
dev_err(dev, "failed to get io clock\n");
ret = PTR_ERR(sc->clk_io);
- goto err_io_clk;
+ goto err_pdata_io_clk;
}
/* enable the local io clock and keep it running for the moment. */
- clk_enable(sc->clk_io);
+ clk_prepare_enable(sc->clk_io);
for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
struct clk *clk;
@@ -638,14 +647,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
}
#ifndef CONFIG_PM_RUNTIME
- clk_enable(sc->clk_bus[sc->cur_clk]);
+ clk_prepare_enable(sc->clk_bus[sc->cur_clk]);
#endif
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- host->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
- if (!host->ioaddr) {
- dev_err(dev, "failed to map registers\n");
- ret = -ENXIO;
+ host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->ioaddr)) {
+ ret = PTR_ERR(host->ioaddr);
goto err_req_regs;
}
@@ -747,13 +755,14 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
sdhci_s3c_setup_card_detect_gpio(sc);
#ifdef CONFIG_PM_RUNTIME
- clk_disable(sc->clk_io);
+ if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
+ clk_disable_unprepare(sc->clk_io);
#endif
return 0;
err_req_regs:
#ifndef CONFIG_PM_RUNTIME
- clk_disable(sc->clk_bus[sc->cur_clk]);
+ clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
#endif
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
if (sc->clk_bus[ptr]) {
@@ -762,22 +771,16 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
}
err_no_busclks:
- clk_disable(sc->clk_io);
+ clk_disable_unprepare(sc->clk_io);
clk_put(sc->clk_io);
- err_io_clk:
- for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
- gpio_free(sc->gpios[ptr]);
- if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
- gpio_free(sc->ext_cd_gpio);
-
- err_pdata:
+ err_pdata_io_clk:
sdhci_free_host(host);
return ret;
}
-static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
+static int sdhci_s3c_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_s3c *sc = sdhci_priv(host);
@@ -790,11 +793,9 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
if (sc->ext_cd_irq)
free_irq(sc->ext_cd_irq, sc);
- if (gpio_is_valid(sc->ext_cd_gpio))
- gpio_free(sc->ext_cd_gpio);
-
#ifdef CONFIG_PM_RUNTIME
- clk_enable(sc->clk_io);
+ if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
+ clk_prepare_enable(sc->clk_io);
#endif
sdhci_remove_host(host, 1);
@@ -802,21 +803,16 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
#ifndef CONFIG_PM_RUNTIME
- clk_disable(sc->clk_bus[sc->cur_clk]);
+ clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
#endif
for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
if (sc->clk_bus[ptr]) {
clk_put(sc->clk_bus[ptr]);
}
}
- clk_disable(sc->clk_io);
+ clk_disable_unprepare(sc->clk_io);
clk_put(sc->clk_io);
- if (pdev->dev.of_node) {
- for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
- gpio_free(sc->gpios[ptr]);
- }
-
sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
@@ -849,8 +845,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
ret = sdhci_runtime_suspend_host(host);
- clk_disable(ourhost->clk_bus[ourhost->cur_clk]);
- clk_disable(busclk);
+ clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
+ clk_disable_unprepare(busclk);
return ret;
}
@@ -861,8 +857,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
struct clk *busclk = ourhost->clk_io;
int ret;
- clk_enable(busclk);
- clk_enable(ourhost->clk_bus[ourhost->cur_clk]);
+ clk_prepare_enable(busclk);
+ clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
ret = sdhci_runtime_resume_host(host);
return ret;
}
@@ -914,7 +910,7 @@ MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);
static struct platform_driver sdhci_s3c_driver = {
.probe = sdhci_s3c_probe,
- .remove = __devexit_p(sdhci_s3c_remove),
+ .remove = sdhci_s3c_remove,
.id_table = sdhci_s3c_driver_ids,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 6be89c032deb..c6ece0bd03b3 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -71,8 +71,7 @@ static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
}
#ifdef CONFIG_OF
-static struct sdhci_plat_data * __devinit
-sdhci_probe_config_dt(struct platform_device *pdev)
+static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_plat_data *pdata = NULL;
@@ -96,14 +95,13 @@ sdhci_probe_config_dt(struct platform_device *pdev)
return pdata;
}
#else
-static struct sdhci_plat_data * __devinit
-sdhci_probe_config_dt(struct platform_device *pdev)
+static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
{
return ERR_PTR(-ENOSYS);
}
#endif
-static int __devinit sdhci_probe(struct platform_device *pdev)
+static int sdhci_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host;
@@ -146,6 +144,11 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
goto put_clk;
}
+ ret = clk_set_rate(sdhci->clk, 50000000);
+ if (ret)
+ dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
+ clk_get_rate(sdhci->clk));
+
if (np) {
sdhci->data = sdhci_probe_config_dt(pdev);
if (IS_ERR(sdhci->data)) {
@@ -268,7 +271,7 @@ err:
return ret;
}
-static int __devexit sdhci_remove(struct platform_device *pdev)
+static int sdhci_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
@@ -297,7 +300,7 @@ static int sdhci_suspend(struct device *dev)
ret = sdhci_suspend_host(host);
if (!ret)
- clk_disable_unprepare(sdhci->clk);
+ clk_disable(sdhci->clk);
return ret;
}
@@ -308,7 +311,7 @@ static int sdhci_resume(struct device *dev)
struct spear_sdhci *sdhci = dev_get_platdata(dev);
int ret;
- ret = clk_prepare_enable(sdhci->clk);
+ ret = clk_enable(sdhci->clk);
if (ret) {
dev_dbg(dev, "Resume: Error enabling clock\n");
return ret;
@@ -336,7 +339,7 @@ static struct platform_driver sdhci_driver = {
.of_match_table = of_match_ptr(sdhci_spear_id_table),
},
.probe = sdhci_probe,
- .remove = __devexit_p(sdhci_remove),
+ .remove = sdhci_remove,
};
module_platform_driver(sdhci_driver);
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index f9eb91623701..08b06e9a3a21 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -27,8 +27,6 @@
#include <asm/gpio.h>
-#include <linux/platform_data/mmc-sdhci-tegra.h>
-
#include "sdhci-pltfm.h"
/* Tegra SDHOST controller vendor register definitions */
@@ -45,8 +43,11 @@ struct sdhci_tegra_soc_data {
};
struct sdhci_tegra {
- const struct tegra_sdhci_platform_data *plat;
const struct sdhci_tegra_soc_data *soc_data;
+ int cd_gpio;
+ int wp_gpio;
+ int power_gpio;
+ int is_8bit;
};
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
@@ -108,12 +109,11 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
- const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
- if (!gpio_is_valid(plat->wp_gpio))
+ if (!gpio_is_valid(tegra_host->wp_gpio))
return -1;
- return gpio_get_value(plat->wp_gpio);
+ return gpio_get_value(tegra_host->wp_gpio);
}
static irqreturn_t carddetect_irq(int irq, void *data)
@@ -143,15 +143,14 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
}
}
-static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
+static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
- const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
u32 ctrl;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
- if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
+ if (tegra_host->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
ctrl |= SDHCI_CTRL_8BITBUS;
} else {
@@ -170,7 +169,7 @@ static struct sdhci_ops tegra_sdhci_ops = {
.read_l = tegra_sdhci_readl,
.read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel,
- .platform_8bit_width = tegra_sdhci_8bit,
+ .platform_bus_width = tegra_sdhci_buswidth,
.platform_reset_exit = tegra_sdhci_reset_exit,
};
@@ -206,7 +205,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = {
};
#endif
-static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
+static const struct of_device_id sdhci_tegra_dt_match[] = {
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
#endif
@@ -217,40 +216,27 @@ static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
};
MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
-static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
- struct platform_device *pdev)
+static void sdhci_tegra_parse_dt(struct device *dev,
+ struct sdhci_tegra *tegra_host)
{
- struct tegra_sdhci_platform_data *plat;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = dev->of_node;
u32 bus_width;
- if (!np)
- return NULL;
-
- plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
- if (!plat) {
- dev_err(&pdev->dev, "Can't allocate platform data\n");
- return NULL;
- }
-
- plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
- plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
- plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
+ tegra_host->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
+ tegra_host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+ tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
if (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
bus_width == 8)
- plat->is_8bit = 1;
-
- return plat;
+ tegra_host->is_8bit = 1;
}
-static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
+static int sdhci_tegra_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct sdhci_tegra_soc_data *soc_data;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
- struct tegra_sdhci_platform_data *plat;
struct sdhci_tegra *tegra_host;
struct clk *clk;
int rc;
@@ -263,52 +249,40 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
host = sdhci_pltfm_init(pdev, soc_data->pdata);
if (IS_ERR(host))
return PTR_ERR(host);
-
pltfm_host = sdhci_priv(host);
- plat = pdev->dev.platform_data;
-
- if (plat == NULL)
- plat = sdhci_tegra_dt_parse_pdata(pdev);
-
- if (plat == NULL) {
- dev_err(mmc_dev(host->mmc), "missing platform data\n");
- rc = -ENXIO;
- goto err_no_plat;
- }
-
tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
if (!tegra_host) {
dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
rc = -ENOMEM;
- goto err_no_plat;
+ goto err_alloc_tegra_host;
}
-
- tegra_host->plat = plat;
tegra_host->soc_data = soc_data;
-
pltfm_host->priv = tegra_host;
- if (gpio_is_valid(plat->power_gpio)) {
- rc = gpio_request(plat->power_gpio, "sdhci_power");
+ sdhci_tegra_parse_dt(&pdev->dev, tegra_host);
+
+ if (gpio_is_valid(tegra_host->power_gpio)) {
+ rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
if (rc) {
dev_err(mmc_dev(host->mmc),
"failed to allocate power gpio\n");
goto err_power_req;
}
- gpio_direction_output(plat->power_gpio, 1);
+ gpio_direction_output(tegra_host->power_gpio, 1);
}
- if (gpio_is_valid(plat->cd_gpio)) {
- rc = gpio_request(plat->cd_gpio, "sdhci_cd");
+ if (gpio_is_valid(tegra_host->cd_gpio)) {
+ rc = gpio_request(tegra_host->cd_gpio, "sdhci_cd");
if (rc) {
dev_err(mmc_dev(host->mmc),
"failed to allocate cd gpio\n");
goto err_cd_req;
}
- gpio_direction_input(plat->cd_gpio);
+ gpio_direction_input(tegra_host->cd_gpio);
- rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
+ rc = request_irq(gpio_to_irq(tegra_host->cd_gpio),
+ carddetect_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
mmc_hostname(host->mmc), host);
@@ -319,14 +293,14 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
}
- if (gpio_is_valid(plat->wp_gpio)) {
- rc = gpio_request(plat->wp_gpio, "sdhci_wp");
+ if (gpio_is_valid(tegra_host->wp_gpio)) {
+ rc = gpio_request(tegra_host->wp_gpio, "sdhci_wp");
if (rc) {
dev_err(mmc_dev(host->mmc),
"failed to allocate wp gpio\n");
goto err_wp_req;
}
- gpio_direction_input(plat->wp_gpio);
+ gpio_direction_input(tegra_host->wp_gpio);
}
clk = clk_get(mmc_dev(host->mmc), NULL);
@@ -338,9 +312,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
clk_prepare_enable(clk);
pltfm_host->clk = clk;
- host->mmc->pm_caps = plat->pm_flags;
-
- if (plat->is_8bit)
+ if (tegra_host->is_8bit)
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
rc = sdhci_add_host(host);
@@ -353,43 +325,42 @@ err_add_host:
clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
err_clk_get:
- if (gpio_is_valid(plat->wp_gpio))
- gpio_free(plat->wp_gpio);
+ if (gpio_is_valid(tegra_host->wp_gpio))
+ gpio_free(tegra_host->wp_gpio);
err_wp_req:
- if (gpio_is_valid(plat->cd_gpio))
- free_irq(gpio_to_irq(plat->cd_gpio), host);
+ if (gpio_is_valid(tegra_host->cd_gpio))
+ free_irq(gpio_to_irq(tegra_host->cd_gpio), host);
err_cd_irq_req:
- if (gpio_is_valid(plat->cd_gpio))
- gpio_free(plat->cd_gpio);
+ if (gpio_is_valid(tegra_host->cd_gpio))
+ gpio_free(tegra_host->cd_gpio);
err_cd_req:
- if (gpio_is_valid(plat->power_gpio))
- gpio_free(plat->power_gpio);
+ if (gpio_is_valid(tegra_host->power_gpio))
+ gpio_free(tegra_host->power_gpio);
err_power_req:
-err_no_plat:
+err_alloc_tegra_host:
sdhci_pltfm_free(pdev);
return rc;
}
-static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
+static int sdhci_tegra_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
- const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
- if (gpio_is_valid(plat->wp_gpio))
- gpio_free(plat->wp_gpio);
+ if (gpio_is_valid(tegra_host->wp_gpio))
+ gpio_free(tegra_host->wp_gpio);
- if (gpio_is_valid(plat->cd_gpio)) {
- free_irq(gpio_to_irq(plat->cd_gpio), host);
- gpio_free(plat->cd_gpio);
+ if (gpio_is_valid(tegra_host->cd_gpio)) {
+ free_irq(gpio_to_irq(tegra_host->cd_gpio), host);
+ gpio_free(tegra_host->cd_gpio);
}
- if (gpio_is_valid(plat->power_gpio))
- gpio_free(plat->power_gpio);
+ if (gpio_is_valid(tegra_host->power_gpio))
+ gpio_free(tegra_host->power_gpio);
clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
@@ -407,7 +378,7 @@ static struct platform_driver sdhci_tegra_driver = {
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_tegra_probe,
- .remove = __devexit_p(sdhci_tegra_remove),
+ .remove = sdhci_tegra_remove,
};
module_platform_driver(sdhci_tegra_driver);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 7922adb42386..51bbba486f38 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -53,6 +53,7 @@ static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
+static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
#ifdef CONFIG_PM_RUNTIME
static int sdhci_runtime_pm_get(struct sdhci_host *host);
@@ -1082,6 +1083,37 @@ static void sdhci_finish_command(struct sdhci_host *host)
}
}
+static u16 sdhci_get_preset_value(struct sdhci_host *host)
+{
+ u16 ctrl, preset = 0;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+
+ switch (ctrl & SDHCI_CTRL_UHS_MASK) {
+ case SDHCI_CTRL_UHS_SDR12:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12);
+ break;
+ case SDHCI_CTRL_UHS_SDR25:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25);
+ break;
+ case SDHCI_CTRL_UHS_SDR50:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50);
+ break;
+ case SDHCI_CTRL_UHS_SDR104:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104);
+ break;
+ case SDHCI_CTRL_UHS_DDR50:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
+ break;
+ default:
+ pr_warn("%s: Invalid UHS-I mode selected\n",
+ mmc_hostname(host->mmc));
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12);
+ break;
+ }
+ return preset;
+}
+
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
int div = 0; /* Initialized for compiler warning */
@@ -1106,35 +1138,43 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
goto out;
if (host->version >= SDHCI_SPEC_300) {
+ if (sdhci_readw(host, SDHCI_HOST_CONTROL2) &
+ SDHCI_CTRL_PRESET_VAL_ENABLE) {
+ u16 pre_val;
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ pre_val = sdhci_get_preset_value(host);
+ div = (pre_val & SDHCI_PRESET_SDCLK_FREQ_MASK)
+ >> SDHCI_PRESET_SDCLK_FREQ_SHIFT;
+ if (host->clk_mul &&
+ (pre_val & SDHCI_PRESET_CLKGEN_SEL_MASK)) {
+ clk = SDHCI_PROG_CLOCK_MODE;
+ real_div = div + 1;
+ clk_mul = host->clk_mul;
+ } else {
+ real_div = max_t(int, 1, div << 1);
+ }
+ goto clock_set;
+ }
+
/*
* Check if the Host Controller supports Programmable Clock
* Mode.
*/
if (host->clk_mul) {
- u16 ctrl;
-
+ for (div = 1; div <= 1024; div++) {
+ if ((host->max_clk * host->clk_mul / div)
+ <= clock)
+ break;
+ }
/*
- * We need to figure out whether the Host Driver needs
- * to select Programmable Clock Mode, or the value can
- * be set automatically by the Host Controller based on
- * the Preset Value registers.
+ * Set Programmable Clock Mode in the Clock
+ * Control register.
*/
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (!(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
- for (div = 1; div <= 1024; div++) {
- if (((host->max_clk * host->clk_mul) /
- div) <= clock)
- break;
- }
- /*
- * Set Programmable Clock Mode in the Clock
- * Control register.
- */
- clk = SDHCI_PROG_CLOCK_MODE;
- real_div = div;
- clk_mul = host->clk_mul;
- div--;
- }
+ clk = SDHCI_PROG_CLOCK_MODE;
+ real_div = div;
+ clk_mul = host->clk_mul;
+ div--;
} else {
/* Version 3.00 divisors must be a multiple of 2. */
if (host->max_clk <= clock)
@@ -1159,6 +1199,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
div >>= 1;
}
+clock_set:
if (real_div)
host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div;
@@ -1189,6 +1230,15 @@ out:
host->clock = clock;
}
+static inline void sdhci_update_clock(struct sdhci_host *host)
+{
+ unsigned int clock;
+
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
+}
+
static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
{
u8 pwr = 0;
@@ -1258,7 +1308,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
- bool present;
+ int present;
unsigned long flags;
u32 tuning_opcode;
@@ -1287,18 +1337,21 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
- /* If polling, assume that the card is always present. */
- if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
- present = true;
- else
- present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
- SDHCI_CARD_PRESENT;
-
- /* If we're using a cd-gpio, testing the presence bit might fail. */
- if (!present) {
- int ret = mmc_gpio_get_cd(host->mmc);
- if (ret > 0)
- present = true;
+ /*
+ * Firstly check card presence from cd-gpio. The return could
+ * be one of the following possibilities:
+ * negative: cd-gpio is not available
+ * zero: cd-gpio is used, and card is removed
+ * one: cd-gpio is used, and card is present
+ */
+ present = mmc_gpio_get_cd(host->mmc);
+ if (present < 0) {
+ /* If polling, assume that the card is always present. */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ present = 1;
+ else
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
}
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
@@ -1315,16 +1368,19 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
*/
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
- /* eMMC uses cmd21 while sd and sdio use cmd19 */
- tuning_opcode = mmc->card->type == MMC_TYPE_MMC ?
- MMC_SEND_TUNING_BLOCK_HS200 :
- MMC_SEND_TUNING_BLOCK;
- spin_unlock_irqrestore(&host->lock, flags);
- sdhci_execute_tuning(mmc, tuning_opcode);
- spin_lock_irqsave(&host->lock, flags);
-
- /* Restore original mmc_request structure */
- host->mrq = mrq;
+ if (mmc->card) {
+ /* eMMC uses cmd21 but sd and sdio use cmd19 */
+ tuning_opcode =
+ mmc->card->type == MMC_TYPE_MMC ?
+ MMC_SEND_TUNING_BLOCK_HS200 :
+ MMC_SEND_TUNING_BLOCK;
+ spin_unlock_irqrestore(&host->lock, flags);
+ sdhci_execute_tuning(mmc, tuning_opcode);
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Restore original mmc_request structure */
+ host->mrq = mrq;
+ }
}
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
@@ -1361,6 +1417,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
sdhci_reinit(host);
}
+ if (host->version >= SDHCI_SPEC_300 &&
+ (ios->power_mode == MMC_POWER_UP))
+ sdhci_enable_preset_value(host, false);
+
sdhci_set_clock(host, ios->clock);
if (ios->power_mode == MMC_POWER_OFF)
@@ -1380,11 +1440,11 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
/*
* If your platform has 8-bit width support but is not a v3 controller,
* or if it requires special setup code, you should implement that in
- * platform_8bit_width().
+ * platform_bus_width().
*/
- if (host->ops->platform_8bit_width)
- host->ops->platform_8bit_width(host, ios->bus_width);
- else {
+ if (host->ops->platform_bus_width) {
+ host->ops->platform_bus_width(host, ios->bus_width);
+ } else {
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (ios->bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
@@ -1412,7 +1472,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
if (host->version >= SDHCI_SPEC_300) {
u16 clk, ctrl_2;
- unsigned int clock;
/* In case of UHS-I modes, set High Speed Enable */
if ((ios->timing == MMC_TIMING_MMC_HS200) ||
@@ -1452,9 +1511,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/* Re-enable SD Clock */
- clock = host->clock;
- host->clock = 0;
- sdhci_set_clock(host, clock);
+ sdhci_update_clock(host);
}
@@ -1484,10 +1541,22 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
+ if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) &&
+ ((ios->timing == MMC_TIMING_UHS_SDR12) ||
+ (ios->timing == MMC_TIMING_UHS_SDR25) ||
+ (ios->timing == MMC_TIMING_UHS_SDR50) ||
+ (ios->timing == MMC_TIMING_UHS_SDR104) ||
+ (ios->timing == MMC_TIMING_UHS_DDR50))) {
+ u16 preset;
+
+ sdhci_enable_preset_value(host, true);
+ preset = sdhci_get_preset_value(host);
+ ios->drv_type = (preset & SDHCI_PRESET_DRV_MASK)
+ >> SDHCI_PRESET_DRV_SHIFT;
+ }
+
/* Re-enable SD Clock */
- clock = host->clock;
- host->clock = 0;
- sdhci_set_clock(host, clock);
+ sdhci_update_clock(host);
} else
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
@@ -1605,141 +1674,91 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}
-static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host,
- u16 ctrl)
+static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
+ struct mmc_ios *ios)
{
+ u16 ctrl;
int ret;
- /* Set 1.8V Signal Enable in the Host Control2 register to 0 */
- ctrl &= ~SDHCI_CTRL_VDD_180;
- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
-
- if (host->vqmmc) {
- ret = regulator_set_voltage(host->vqmmc, 3300000, 3300000);
- if (ret) {
- pr_warning("%s: Switching to 3.3V signalling voltage "
- " failed\n", mmc_hostname(host->mmc));
- return -EIO;
- }
- }
- /* Wait for 5ms */
- usleep_range(5000, 5500);
+ /*
+ * Signal Voltage Switching is only applicable for Host Controllers
+ * v3.00 and above.
+ */
+ if (host->version < SDHCI_SPEC_300)
+ return 0;
- /* 3.3V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (!(ctrl & SDHCI_CTRL_VDD_180))
- return 0;
- pr_warning("%s: 3.3V regulator output did not became stable\n",
- mmc_hostname(host->mmc));
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_330:
+ /* Set 1.8V Signal Enable in the Host Control2 register to 0 */
+ ctrl &= ~SDHCI_CTRL_VDD_180;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
- return -EIO;
-}
+ if (host->vqmmc) {
+ ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000);
+ if (ret) {
+ pr_warning("%s: Switching to 3.3V signalling voltage "
+ " failed\n", mmc_hostname(host->mmc));
+ return -EIO;
+ }
+ }
+ /* Wait for 5ms */
+ usleep_range(5000, 5500);
-static int sdhci_do_1_8v_signal_voltage_switch(struct sdhci_host *host,
- u16 ctrl)
-{
- u8 pwr;
- u16 clk;
- u32 present_state;
- int ret;
+ /* 3.3V regulator output should be stable within 5 ms */
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (!(ctrl & SDHCI_CTRL_VDD_180))
+ return 0;
- /* Stop SDCLK */
- clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- clk &= ~SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+ pr_warning("%s: 3.3V regulator output did not became stable\n",
+ mmc_hostname(host->mmc));
+
+ return -EAGAIN;
+ case MMC_SIGNAL_VOLTAGE_180:
+ if (host->vqmmc) {
+ ret = regulator_set_voltage(host->vqmmc,
+ 1700000, 1950000);
+ if (ret) {
+ pr_warning("%s: Switching to 1.8V signalling voltage "
+ " failed\n", mmc_hostname(host->mmc));
+ return -EIO;
+ }
+ }
- /* Check whether DAT[3:0] is 0000 */
- present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
- if (!((present_state & SDHCI_DATA_LVL_MASK) >>
- SDHCI_DATA_LVL_SHIFT)) {
/*
* Enable 1.8V Signal Enable in the Host Control2
* register
*/
- if (host->vqmmc)
- ret = regulator_set_voltage(host->vqmmc,
- 1800000, 1800000);
- else
- ret = 0;
+ ctrl |= SDHCI_CTRL_VDD_180;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
- if (!ret) {
- ctrl |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ /* Wait for 5ms */
+ usleep_range(5000, 5500);
- /* Wait for 5ms */
- usleep_range(5000, 5500);
+ /* 1.8V regulator output should be stable within 5 ms */
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ if (ctrl & SDHCI_CTRL_VDD_180)
+ return 0;
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (ctrl & SDHCI_CTRL_VDD_180) {
- /* Provide SDCLK again and wait for 1ms */
- clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- clk |= SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
- usleep_range(1000, 1500);
+ pr_warning("%s: 1.8V regulator output did not became stable\n",
+ mmc_hostname(host->mmc));
- /*
- * If DAT[3:0] level is 1111b, then the card
- * was successfully switched to 1.8V signaling.
- */
- present_state = sdhci_readl(host,
- SDHCI_PRESENT_STATE);
- if ((present_state & SDHCI_DATA_LVL_MASK) ==
- SDHCI_DATA_LVL_MASK)
- return 0;
+ return -EAGAIN;
+ case MMC_SIGNAL_VOLTAGE_120:
+ if (host->vqmmc) {
+ ret = regulator_set_voltage(host->vqmmc, 1100000, 1300000);
+ if (ret) {
+ pr_warning("%s: Switching to 1.2V signalling voltage "
+ " failed\n", mmc_hostname(host->mmc));
+ return -EIO;
}
}
- }
-
- /*
- * If we are here, that means the switch to 1.8V signaling
- * failed. We power cycle the card, and retry initialization
- * sequence by setting S18R to 0.
- */
- pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
- pwr &= ~SDHCI_POWER_ON;
- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
- if (host->vmmc)
- regulator_disable(host->vmmc);
-
- /* Wait for 1ms as per the spec */
- usleep_range(1000, 1500);
- pwr |= SDHCI_POWER_ON;
- sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
- if (host->vmmc)
- regulator_enable(host->vmmc);
-
- pr_warning("%s: Switching to 1.8V signalling voltage failed, "
- "retrying with S18R set to 0\n", mmc_hostname(host->mmc));
-
- return -EAGAIN;
-}
-
-static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
- struct mmc_ios *ios)
-{
- u16 ctrl;
-
- /*
- * Signal Voltage Switching is only applicable for Host Controllers
- * v3.00 and above.
- */
- if (host->version < SDHCI_SPEC_300)
return 0;
-
- /*
- * We first check whether the request is to set signalling voltage
- * to 3.3V. If so, we change the voltage to 3.3V and return quickly.
- */
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
- return sdhci_do_3_3v_signal_voltage_switch(host, ctrl);
- else if (!(ctrl & SDHCI_CTRL_VDD_180) &&
- (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180))
- return sdhci_do_1_8v_signal_voltage_switch(host, ctrl);
- else
+ default:
/* No signal voltage switch required */
return 0;
+ }
}
static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
@@ -1756,6 +1775,19 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return err;
}
+static int sdhci_card_busy(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 present_state;
+
+ sdhci_runtime_pm_get(host);
+ /* Check whether DAT[3:0] is 0000 */
+ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ sdhci_runtime_pm_put(host);
+
+ return !(present_state & SDHCI_DATA_LVL_MASK);
+}
+
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
@@ -1952,17 +1984,15 @@ out:
return err;
}
-static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
+
+static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
{
u16 ctrl;
- unsigned long flags;
/* Host Controller v3.00 defines preset value registers */
if (host->version < SDHCI_SPEC_300)
return;
- spin_lock_irqsave(&host->lock, flags);
-
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/*
@@ -1978,17 +2008,31 @@ static void sdhci_do_enable_preset_value(struct sdhci_host *host, bool enable)
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
host->flags &= ~SDHCI_PV_ENABLED;
}
-
- spin_unlock_irqrestore(&host->lock, flags);
}
-static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable)
+static void sdhci_card_event(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
- sdhci_runtime_pm_get(host);
- sdhci_do_enable_preset_value(host, enable);
- sdhci_runtime_pm_put(host);
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Check host->mrq first in case we are runtime suspended */
+ if (host->mrq &&
+ !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ pr_err("%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ pr_err("%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
}
static const struct mmc_host_ops sdhci_ops = {
@@ -1999,7 +2043,8 @@ static const struct mmc_host_ops sdhci_ops = {
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning,
- .enable_preset_value = sdhci_enable_preset_value,
+ .card_event = sdhci_card_event,
+ .card_busy = sdhci_card_busy,
};
/*****************************************************************************\
@@ -2010,29 +2055,9 @@ static const struct mmc_host_ops sdhci_ops = {
static void sdhci_tasklet_card(unsigned long param)
{
- struct sdhci_host *host;
- unsigned long flags;
-
- host = (struct sdhci_host*)param;
-
- spin_lock_irqsave(&host->lock, flags);
-
- /* Check host->mrq first in case we are runtime suspended */
- if (host->mrq &&
- !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
- pr_err("%s: Card removed during transfer!\n",
- mmc_hostname(host->mmc));
- pr_err("%s: Resetting controller.\n",
- mmc_hostname(host->mmc));
-
- sdhci_reset(host, SDHCI_RESET_CMD);
- sdhci_reset(host, SDHCI_RESET_DATA);
-
- host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
- }
+ struct sdhci_host *host = (struct sdhci_host*)param;
- spin_unlock_irqrestore(&host->lock, flags);
+ sdhci_card_event(host->mmc);
mmc_detect_change(host->mmc, msecs_to_jiffies(200));
}
@@ -2071,14 +2096,9 @@ static void sdhci_tasklet_finish(unsigned long param)
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
/* Some controllers need this kick or reset won't work here */
- if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
- unsigned int clock;
-
+ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
/* This is to force an update */
- clock = host->clock;
- host->clock = 0;
- sdhci_set_clock(host, clock);
- }
+ sdhci_update_clock(host);
/* Spec says we should do both at the same time, but Ricoh
controllers do not like that. */
@@ -2279,6 +2299,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
sdhci_show_adma_error(host);
host->data->error = -EIO;
+ if (host->ops->adma_workaround)
+ host->ops->adma_workaround(host, intmask);
}
if (host->data->error)
@@ -2444,6 +2466,32 @@ out:
\*****************************************************************************/
#ifdef CONFIG_PM
+void sdhci_enable_irq_wakeups(struct sdhci_host *host)
+{
+ u8 val;
+ u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
+ | SDHCI_WAKE_ON_INT;
+
+ val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
+ val |= mask ;
+ /* Avoid fake wake up */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE);
+ sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
+}
+EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
+
+void sdhci_disable_irq_wakeups(struct sdhci_host *host)
+{
+ u8 val;
+ u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
+ | SDHCI_WAKE_ON_INT;
+
+ val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
+ val &= ~mask;
+ sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
+}
+EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
int sdhci_suspend_host(struct sdhci_host *host)
{
@@ -2473,8 +2521,13 @@ int sdhci_suspend_host(struct sdhci_host *host)
return ret;
}
- free_irq(host->irq, host);
-
+ if (!device_may_wakeup(mmc_dev(host->mmc))) {
+ sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+ free_irq(host->irq, host);
+ } else {
+ sdhci_enable_irq_wakeups(host);
+ enable_irq_wake(host->irq);
+ }
return ret;
}
@@ -2489,10 +2542,15 @@ int sdhci_resume_host(struct sdhci_host *host)
host->ops->enable_dma(host);
}
- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
- mmc_hostname(host->mmc), host);
- if (ret)
- return ret;
+ if (!device_may_wakeup(mmc_dev(host->mmc))) {
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+ mmc_hostname(host->mmc), host);
+ if (ret)
+ return ret;
+ } else {
+ sdhci_disable_irq_wakeups(host);
+ disable_irq_wake(host->irq);
+ }
if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) &&
(host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) {
@@ -2520,17 +2578,6 @@ int sdhci_resume_host(struct sdhci_host *host)
}
EXPORT_SYMBOL_GPL(sdhci_resume_host);
-
-void sdhci_enable_irq_wakeups(struct sdhci_host *host)
-{
- u8 val;
- val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
- val |= SDHCI_WAKE_ON_INT;
- sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
-}
-
-EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
-
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
@@ -2589,8 +2636,12 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
sdhci_do_set_ios(host, &host->mmc->ios);
sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
- if (host_flags & SDHCI_PV_ENABLED)
- sdhci_do_enable_preset_value(host, true);
+ if ((host_flags & SDHCI_PV_ENABLED) &&
+ !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
+ spin_lock_irqsave(&host->lock, flags);
+ sdhci_enable_preset_value(host, true);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
/* Set the re-tuning expiration flag */
if (host->flags & SDHCI_USING_RETUNING_TIMER)
@@ -2837,6 +2888,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
+ if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
+ mmc->caps &= ~MMC_CAP_CMD23;
+
if (caps[0] & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
@@ -2846,13 +2900,22 @@ int sdhci_add_host(struct sdhci_host *host)
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
host->vqmmc = regulator_get(mmc_dev(mmc), "vqmmc");
- if (IS_ERR(host->vqmmc)) {
- pr_info("%s: no vqmmc regulator found\n", mmc_hostname(mmc));
- host->vqmmc = NULL;
- }
- else if (regulator_is_supported_voltage(host->vqmmc, 1800000, 1800000))
+ if (IS_ERR_OR_NULL(host->vqmmc)) {
+ if (PTR_ERR(host->vqmmc) < 0) {
+ pr_info("%s: no vqmmc regulator found\n",
+ mmc_hostname(mmc));
+ host->vqmmc = NULL;
+ }
+ } else {
regulator_enable(host->vqmmc);
- else
+ if (!regulator_is_supported_voltage(host->vqmmc, 1700000,
+ 1950000))
+ caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
+ SDHCI_SUPPORT_SDR50 |
+ SDHCI_SUPPORT_DDR50);
+ }
+
+ if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
SDHCI_SUPPORT_DDR50);
@@ -2904,24 +2967,28 @@ int sdhci_add_host(struct sdhci_host *host)
ocr_avail = 0;
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
- if (IS_ERR(host->vmmc)) {
- pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
- host->vmmc = NULL;
- } else
- regulator_enable(host->vmmc);
+ if (IS_ERR_OR_NULL(host->vmmc)) {
+ if (PTR_ERR(host->vmmc) < 0) {
+ pr_info("%s: no vmmc regulator found\n",
+ mmc_hostname(mmc));
+ host->vmmc = NULL;
+ }
+ }
#ifdef CONFIG_REGULATOR
- if (host->vmmc) {
- ret = regulator_is_supported_voltage(host->vmmc, 3300000,
- 3300000);
+ /*
+ * Voltage range check makes sense only if regulator reports
+ * any voltage value.
+ */
+ if (host->vmmc && regulator_get_voltage(host->vmmc) > 0) {
+ ret = regulator_is_supported_voltage(host->vmmc, 2700000,
+ 3600000);
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330)))
caps[0] &= ~SDHCI_CAN_VDD_330;
- ret = regulator_is_supported_voltage(host->vmmc, 3000000,
- 3000000);
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300)))
caps[0] &= ~SDHCI_CAN_VDD_300;
- ret = regulator_is_supported_voltage(host->vmmc, 1800000,
- 1800000);
+ ret = regulator_is_supported_voltage(host->vmmc, 1700000,
+ 1950000);
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180)))
caps[0] &= ~SDHCI_CAN_VDD_180;
}
@@ -3116,6 +3183,7 @@ int sdhci_add_host(struct sdhci_host *host)
#ifdef SDHCI_USE_LEDS_CLASS
reset:
sdhci_reset(host, SDHCI_RESET_ALL);
+ sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
free_irq(host->irq, host);
#endif
untasklet:
@@ -3158,6 +3226,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
if (!dead)
sdhci_reset(host, SDHCI_RESET_ALL);
+ sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
free_irq(host->irq, host);
del_timer_sync(&host->timer);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 97653ea8942b..379e09d9f3c1 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -120,6 +120,7 @@
#define SDHCI_SIGNAL_ENABLE 0x38
#define SDHCI_INT_RESPONSE 0x00000001
#define SDHCI_INT_DATA_END 0x00000002
+#define SDHCI_INT_BLK_GAP 0x00000004
#define SDHCI_INT_DMA_END 0x00000008
#define SDHCI_INT_SPACE_AVAIL 0x00000010
#define SDHCI_INT_DATA_AVAIL 0x00000020
@@ -146,7 +147,8 @@
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
- SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
+ SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
+ SDHCI_INT_BLK_GAP)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C
@@ -227,6 +229,18 @@
/* 60-FB reserved */
+#define SDHCI_PRESET_FOR_SDR12 0x66
+#define SDHCI_PRESET_FOR_SDR25 0x68
+#define SDHCI_PRESET_FOR_SDR50 0x6A
+#define SDHCI_PRESET_FOR_SDR104 0x6C
+#define SDHCI_PRESET_FOR_DDR50 0x6E
+#define SDHCI_PRESET_DRV_MASK 0xC000
+#define SDHCI_PRESET_DRV_SHIFT 14
+#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
+#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10
+#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
+#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0
+
#define SDHCI_SLOT_INT_STATUS 0xFC
#define SDHCI_HOST_VERSION 0xFE
@@ -267,7 +281,7 @@ struct sdhci_ops {
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
- int (*platform_8bit_width)(struct sdhci_host *host,
+ int (*platform_bus_width)(struct sdhci_host *host,
int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
@@ -278,6 +292,8 @@ struct sdhci_ops {
void (*hw_reset)(struct sdhci_host *host);
void (*platform_suspend)(struct sdhci_host *host);
void (*platform_resume)(struct sdhci_host *host);
+ void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
+ void (*platform_init)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 11d2bc3b51d5..ba76a532ae30 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -56,6 +56,7 @@
#include <linux/mmc/sh_mmcif.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
@@ -88,6 +89,7 @@
#define CMD_SET_TBIT (1 << 7) /* 1: tran mission bit "Low" */
#define CMD_SET_OPDM (1 << 6) /* 1: open/drain */
#define CMD_SET_CCSH (1 << 5)
+#define CMD_SET_DARS (1 << 2) /* Dual Data Rate */
#define CMD_SET_DATW_1 ((0 << 1) | (0 << 0)) /* 1bit */
#define CMD_SET_DATW_4 ((0 << 1) | (1 << 0)) /* 4bit */
#define CMD_SET_DATW_8 ((1 << 1) | (0 << 0)) /* 8bit */
@@ -127,6 +129,10 @@
INT_CCSTO | INT_CRCSTO | INT_WDATTO | \
INT_RDATTO | INT_RBSYTO | INT_RSPTO)
+#define INT_ALL (INT_RBSYE | INT_CRSPE | INT_BUFREN | \
+ INT_BUFWEN | INT_CMD12DRE | INT_BUFRE | \
+ INT_DTRANE | INT_CMD12RBE | INT_CMD12CRE)
+
/* CE_INT_MASK */
#define MASK_ALL 0x00000000
#define MASK_MCCSDE (1 << 29)
@@ -158,6 +164,11 @@
MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \
MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO)
+#define MASK_CLEAN (INT_ERR_STS | MASK_MRBSYE | MASK_MCRSPE | \
+ MASK_MBUFREN | MASK_MBUFWEN | \
+ MASK_MCMD12DRE | MASK_MBUFRE | MASK_MDTRANE | \
+ MASK_MCMD12RBE | MASK_MCMD12CRE)
+
/* CE_HOST_STS1 */
#define STS1_CMDSEQ (1 << 31)
@@ -195,6 +206,7 @@ enum mmcif_state {
STATE_IDLE,
STATE_REQUEST,
STATE_IOS,
+ STATE_TIMEOUT,
};
enum mmcif_wait_for {
@@ -216,6 +228,7 @@ struct sh_mmcif_host {
struct clk *hclk;
unsigned int clk;
int bus_width;
+ unsigned char timing;
bool sd_error;
bool dying;
long timeout;
@@ -230,6 +243,7 @@ struct sh_mmcif_host {
int sg_blkidx;
bool power;
bool card_present;
+ struct mutex thread_lock;
/* DMA support */
struct dma_chan *chan_rx;
@@ -253,23 +267,14 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
static void mmcif_dma_complete(void *arg)
{
struct sh_mmcif_host *host = arg;
- struct mmc_data *data = host->mrq->data;
+ struct mmc_request *mrq = host->mrq;
dev_dbg(&host->pd->dev, "Command completed\n");
- if (WARN(!data, "%s: NULL data in DMA completion!\n",
+ if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n",
dev_name(&host->pd->dev)))
return;
- if (data->flags & MMC_DATA_READ)
- dma_unmap_sg(host->chan_rx->device->dev,
- data->sg, data->sg_len,
- DMA_FROM_DEVICE);
- else
- dma_unmap_sg(host->chan_tx->device->dev,
- data->sg, data->sg_len,
- DMA_TO_DEVICE);
-
complete(&host->dma_complete);
}
@@ -423,8 +428,6 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
if (ret < 0)
goto ecfgrx;
- init_completion(&host->dma_complete);
-
return;
ecfgrx:
@@ -520,13 +523,16 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
}
if (state2 & STS2_CRC_ERR) {
- dev_dbg(&host->pd->dev, ": CRC error\n");
+ dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n",
+ host->state, host->wait_for);
ret = -EIO;
} else if (state2 & STS2_TIMEOUT_ERR) {
- dev_dbg(&host->pd->dev, ": Timeout\n");
+ dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n",
+ host->state, host->wait_for);
ret = -ETIMEDOUT;
} else {
- dev_dbg(&host->pd->dev, ": End/Index error\n");
+ dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n",
+ host->state, host->wait_for);
ret = -EIO;
}
return ret;
@@ -549,10 +555,7 @@ static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p)
host->pio_ptr = p;
}
- if (host->sg_idx == data->sg_len)
- return false;
-
- return true;
+ return host->sg_idx != data->sg_len;
}
static void sh_mmcif_single_read(struct sh_mmcif_host *host,
@@ -562,7 +565,6 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host,
BLOCK_SIZE_MASK) + 3;
host->wait_for = MMCIF_WAIT_FOR_READ;
- schedule_delayed_work(&host->timeout_work, host->timeout);
/* buf read enable */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
@@ -576,6 +578,7 @@ static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -604,7 +607,7 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
host->sg_idx = 0;
host->sg_blkidx = 0;
host->pio_ptr = sg_virt(data->sg);
- schedule_delayed_work(&host->timeout_work, host->timeout);
+
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
}
@@ -616,6 +619,7 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -627,7 +631,6 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
if (!sh_mmcif_next_block(host, p))
return false;
- schedule_delayed_work(&host->timeout_work, host->timeout);
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
return true;
@@ -640,7 +643,6 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host,
BLOCK_SIZE_MASK) + 3;
host->wait_for = MMCIF_WAIT_FOR_WRITE;
- schedule_delayed_work(&host->timeout_work, host->timeout);
/* buf write enable */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
@@ -654,6 +656,7 @@ static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -682,7 +685,7 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
host->sg_idx = 0;
host->sg_blkidx = 0;
host->pio_ptr = sg_virt(data->sg);
- schedule_delayed_work(&host->timeout_work, host->timeout);
+
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
}
@@ -694,6 +697,7 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
if (host->sd_error) {
data->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
return false;
}
@@ -705,7 +709,6 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
if (!sh_mmcif_next_block(host, p))
return false;
- schedule_delayed_work(&host->timeout_work, host->timeout);
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
return true;
@@ -756,6 +759,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
}
switch (opc) {
/* RBSY */
+ case MMC_SLEEP_AWAKE:
case MMC_SWITCH:
case MMC_STOP_TRANSMISSION:
case MMC_SET_WRITE_PROT:
@@ -781,6 +785,17 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
dev_err(&host->pd->dev, "Unsupported bus width.\n");
break;
}
+ switch (host->timing) {
+ case MMC_TIMING_UHS_DDR50:
+ /*
+ * MMC core will only set this timing, if the host
+ * advertises the MMC_CAP_UHS_DDR50 capability. MMCIF
+ * implementations with this capability, e.g. sh73a0,
+ * will have to set it in their platform data.
+ */
+ tmp |= CMD_SET_DARS;
+ break;
+ }
}
/* DWEN */
if (opc == MMC_WRITE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK)
@@ -824,7 +839,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
sh_mmcif_single_read(host, mrq);
return 0;
default:
- dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc);
+ dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc);
return -EINVAL;
}
}
@@ -838,6 +853,7 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
switch (opc) {
/* response busy check */
+ case MMC_SLEEP_AWAKE:
case MMC_SWITCH:
case MMC_STOP_TRANSMISSION:
case MMC_SET_WRITE_PROT:
@@ -885,7 +901,6 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
}
host->wait_for = MMCIF_WAIT_FOR_STOP;
- schedule_delayed_work(&host->timeout_work, host->timeout);
}
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -895,6 +910,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_lock_irqsave(&host->lock, flags);
if (host->state != STATE_IDLE) {
+ dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
spin_unlock_irqrestore(&host->lock, flags);
mrq->cmd->error = -EAGAIN;
mmc_request_done(mmc, mrq);
@@ -911,6 +927,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR)
break;
case MMC_APP_CMD:
+ case SD_IO_RW_DIRECT:
host->state = STATE_IDLE;
mrq->cmd->error = -ETIMEDOUT;
mmc_request_done(mmc, mrq);
@@ -957,6 +974,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_lock_irqsave(&host->lock, flags);
if (host->state != STATE_IDLE) {
+ dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
spin_unlock_irqrestore(&host->lock, flags);
return;
}
@@ -981,7 +999,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
if (host->power) {
- pm_runtime_put(&host->pd->dev);
+ pm_runtime_put_sync(&host->pd->dev);
clk_disable(host->hclk);
host->power = false;
if (ios->power_mode == MMC_POWER_OFF)
@@ -1001,6 +1019,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sh_mmcif_clock_control(host, ios->clock);
}
+ host->timing = ios->timing;
host->bus_width = ios->bus_width;
host->state = STATE_IDLE;
}
@@ -1038,14 +1057,14 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
case MMC_SELECT_CARD:
case MMC_APP_CMD:
cmd->error = -ETIMEDOUT;
- host->sd_error = false;
break;
default:
cmd->error = sh_mmcif_error_manage(host);
- dev_dbg(&host->pd->dev, "Cmd(d'%d) error %d\n",
- cmd->opcode, cmd->error);
break;
}
+ dev_dbg(&host->pd->dev, "CMD%d error %d\n",
+ cmd->opcode, cmd->error);
+ host->sd_error = false;
return false;
}
if (!(cmd->flags & MMC_RSP_PRESENT)) {
@@ -1058,6 +1077,12 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
if (!data)
return false;
+ /*
+ * Completion can be signalled from DMA callback and error, so, have to
+ * reset here, before setting .dma_active
+ */
+ init_completion(&host->dma_complete);
+
if (data->flags & MMC_DATA_READ) {
if (host->chan_rx)
sh_mmcif_start_dma_rx(host);
@@ -1068,34 +1093,47 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
if (!host->dma_active) {
data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode);
- if (!data->error)
- return true;
- return false;
+ return !data->error;
}
/* Running in the IRQ thread, can sleep */
time = wait_for_completion_interruptible_timeout(&host->dma_complete,
host->timeout);
+
+ if (data->flags & MMC_DATA_READ)
+ dma_unmap_sg(host->chan_rx->device->dev,
+ data->sg, data->sg_len,
+ DMA_FROM_DEVICE);
+ else
+ dma_unmap_sg(host->chan_tx->device->dev,
+ data->sg, data->sg_len,
+ DMA_TO_DEVICE);
+
if (host->sd_error) {
dev_err(host->mmc->parent,
"Error IRQ while waiting for DMA completion!\n");
/* Woken up by an error IRQ: abort DMA */
- if (data->flags & MMC_DATA_READ)
- dmaengine_terminate_all(host->chan_rx);
- else
- dmaengine_terminate_all(host->chan_tx);
data->error = sh_mmcif_error_manage(host);
} else if (!time) {
+ dev_err(host->mmc->parent, "DMA timeout!\n");
data->error = -ETIMEDOUT;
} else if (time < 0) {
+ dev_err(host->mmc->parent,
+ "wait_for_completion_...() error %ld!\n", time);
data->error = time;
}
sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC,
BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
host->dma_active = false;
- if (data->error)
+ if (data->error) {
data->bytes_xfered = 0;
+ /* Abort DMA */
+ if (data->flags & MMC_DATA_READ)
+ dmaengine_terminate_all(host->chan_rx);
+ else
+ dmaengine_terminate_all(host->chan_tx);
+ }
return false;
}
@@ -1103,11 +1141,21 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
- struct mmc_request *mrq = host->mrq;
- struct mmc_data *data = mrq->data;
+ struct mmc_request *mrq;
+ bool wait = false;
cancel_delayed_work_sync(&host->timeout_work);
+ mutex_lock(&host->thread_lock);
+
+ mrq = host->mrq;
+ if (!mrq) {
+ dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
+ host->state, host->wait_for);
+ mutex_unlock(&host->thread_lock);
+ return IRQ_HANDLED;
+ }
+
/*
* All handlers return true, if processing continues, and false, if the
* request has to be completed - successfully or not
@@ -1115,35 +1163,32 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
switch (host->wait_for) {
case MMCIF_WAIT_FOR_REQUEST:
/* We're too late, the timeout has already kicked in */
+ mutex_unlock(&host->thread_lock);
return IRQ_HANDLED;
case MMCIF_WAIT_FOR_CMD:
- if (sh_mmcif_end_cmd(host))
- /* Wait for data */
- return IRQ_HANDLED;
+ /* Wait for data? */
+ wait = sh_mmcif_end_cmd(host);
break;
case MMCIF_WAIT_FOR_MREAD:
- if (sh_mmcif_mread_block(host))
- /* Wait for more data */
- return IRQ_HANDLED;
+ /* Wait for more data? */
+ wait = sh_mmcif_mread_block(host);
break;
case MMCIF_WAIT_FOR_READ:
- if (sh_mmcif_read_block(host))
- /* Wait for data end */
- return IRQ_HANDLED;
+ /* Wait for data end? */
+ wait = sh_mmcif_read_block(host);
break;
case MMCIF_WAIT_FOR_MWRITE:
- if (sh_mmcif_mwrite_block(host))
- /* Wait data to write */
- return IRQ_HANDLED;
+ /* Wait data to write? */
+ wait = sh_mmcif_mwrite_block(host);
break;
case MMCIF_WAIT_FOR_WRITE:
- if (sh_mmcif_write_block(host))
- /* Wait for data end */
- return IRQ_HANDLED;
+ /* Wait for data end? */
+ wait = sh_mmcif_write_block(host);
break;
case MMCIF_WAIT_FOR_STOP:
if (host->sd_error) {
mrq->stop->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error);
break;
}
sh_mmcif_get_cmd12response(host, mrq->stop);
@@ -1151,22 +1196,35 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
break;
case MMCIF_WAIT_FOR_READ_END:
case MMCIF_WAIT_FOR_WRITE_END:
- if (host->sd_error)
- data->error = sh_mmcif_error_manage(host);
+ if (host->sd_error) {
+ mrq->data->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error);
+ }
break;
default:
BUG();
}
+ if (wait) {
+ schedule_delayed_work(&host->timeout_work, host->timeout);
+ /* Wait for more data */
+ mutex_unlock(&host->thread_lock);
+ return IRQ_HANDLED;
+ }
+
if (host->wait_for != MMCIF_WAIT_FOR_STOP) {
+ struct mmc_data *data = mrq->data;
if (!mrq->cmd->error && data && !data->error)
data->bytes_xfered =
data->blocks * data->blksz;
if (mrq->stop && !mrq->cmd->error && (!data || !data->error)) {
sh_mmcif_stop_cmd(host, mrq);
- if (!mrq->stop->error)
+ if (!mrq->stop->error) {
+ schedule_delayed_work(&host->timeout_work, host->timeout);
+ mutex_unlock(&host->thread_lock);
return IRQ_HANDLED;
+ }
}
}
@@ -1175,6 +1233,8 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
+ mutex_unlock(&host->thread_lock);
+
return IRQ_HANDLED;
}
@@ -1182,60 +1242,22 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
u32 state;
- int err = 0;
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
+ sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
- if (state & INT_ERR_STS) {
- /* error interrupts - process first */
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
- err = 1;
- } else if (state & INT_RBSYE) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT,
- ~(INT_RBSYE | INT_CRSPE));
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE);
- } else if (state & INT_CRSPE) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE);
- } else if (state & INT_BUFREN) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
- } else if (state & INT_BUFWEN) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
- } else if (state & INT_CMD12DRE) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT,
- ~(INT_CMD12DRE | INT_CMD12RBE |
- INT_CMD12CRE | INT_BUFRE));
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
- } else if (state & INT_BUFRE) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
- } else if (state & INT_DTRANE) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT,
- ~(INT_CMD12DRE | INT_CMD12RBE |
- INT_CMD12CRE | INT_DTRANE));
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
- } else if (state & INT_CMD12RBE) {
- sh_mmcif_writel(host->addr, MMCIF_CE_INT,
- ~(INT_CMD12RBE | INT_CMD12CRE));
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
- } else {
- dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state);
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
- err = 1;
- }
- if (err) {
+ if (state & ~MASK_CLEAN)
+ dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n",
+ state);
+
+ if (state & INT_ERR_STS || state & ~INT_ALL) {
host->sd_error = true;
- dev_dbg(&host->pd->dev, "int err state = %08x\n", state);
- }
- if (host->state == STATE_IDLE) {
- dev_info(&host->pd->dev, "Spurious IRQ status 0x%x", state);
- return IRQ_HANDLED;
+ dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state);
}
if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
+ if (!host->mrq)
+ dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state);
if (!host->dma_active)
return IRQ_WAKE_THREAD;
else if (host->sd_error)
@@ -1252,11 +1274,24 @@ static void mmcif_timeout_work(struct work_struct *work)
struct delayed_work *d = container_of(work, struct delayed_work, work);
struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
struct mmc_request *mrq = host->mrq;
+ unsigned long flags;
if (host->dying)
/* Don't run after mmc_remove_host() */
return;
+ dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
+ host->wait_for, mrq->cmd->opcode);
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->state == STATE_IDLE) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ host->state = STATE_TIMEOUT;
+ spin_unlock_irqrestore(&host->lock, flags);
+
/*
* Handle races with cancel_delayed_work(), unless
* cancel_delayed_work_sync() is used
@@ -1302,7 +1337,7 @@ static void sh_mmcif_init_ocr(struct sh_mmcif_host *host)
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
}
-static int __devinit sh_mmcif_probe(struct platform_device *pdev)
+static int sh_mmcif_probe(struct platform_device *pdev)
{
int ret = 0, irq[2];
struct mmc_host *mmc;
@@ -1310,11 +1345,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
struct resource *res;
void __iomem *reg;
- char clk_name[8];
+ const char *name;
irq[0] = platform_get_irq(pdev, 0);
irq[1] = platform_get_irq(pdev, 1);
- if (irq[0] < 0 || irq[1] < 0) {
+ if (irq[0] < 0) {
dev_err(&pdev->dev, "Get irq error\n");
return -ENXIO;
}
@@ -1334,10 +1369,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto ealloch;
}
+ mmc_of_parse(mmc);
host = mmc_priv(mmc);
host->mmc = mmc;
host->addr = reg;
- host->timeout = 1000;
+ host->timeout = msecs_to_jiffies(1000);
host->pd = pdev;
@@ -1346,7 +1382,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
mmc->ops = &sh_mmcif_ops;
sh_mmcif_init_ocr(host);
- mmc->caps = MMC_CAP_MMC_HIGHSPEED;
+ mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY;
if (pd && pd->caps)
mmc->caps |= pd->caps;
mmc->max_segs = 32;
@@ -1360,11 +1396,10 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
host->power = false;
- snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id);
- host->hclk = clk_get(&pdev->dev, clk_name);
+ host->hclk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->hclk)) {
ret = PTR_ERR(host->hclk);
- dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret);
+ dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
goto eclkget;
}
ret = sh_mmcif_clk_update(host);
@@ -1380,15 +1415,19 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
sh_mmcif_sync_reset(host);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
- ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host);
+ name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error";
+ ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, name, host);
if (ret) {
- dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
+ dev_err(&pdev->dev, "request_irq error (%s)\n", name);
goto ereqirq0;
}
- ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host);
- if (ret) {
- dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
- goto ereqirq1;
+ if (irq[1] >= 0) {
+ ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt,
+ 0, "sh_mmc:int", host);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
+ goto ereqirq1;
+ }
}
if (pd && pd->use_cd_gpio) {
@@ -1397,6 +1436,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
goto erqcd;
}
+ mutex_init(&host->thread_lock);
+
clk_disable(host->hclk);
ret = mmc_add_host(mmc);
if (ret < 0)
@@ -1410,10 +1451,9 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
return ret;
emmcaddh:
- if (pd && pd->use_cd_gpio)
- mmc_gpio_free_cd(mmc);
erqcd:
- free_irq(irq[1], host);
+ if (irq[1] >= 0)
+ free_irq(irq[1], host);
ereqirq1:
free_irq(irq[0], host);
ereqirq0:
@@ -1430,10 +1470,9 @@ ealloch:
return ret;
}
-static int __devexit sh_mmcif_remove(struct platform_device *pdev)
+static int sh_mmcif_remove(struct platform_device *pdev)
{
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
- struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
int irq[2];
host->dying = true;
@@ -1442,9 +1481,6 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
dev_pm_qos_hide_latency_limit(&pdev->dev);
- if (pd && pd->use_cd_gpio)
- mmc_gpio_free_cd(host->mmc);
-
mmc_remove_host(host->mmc);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
@@ -1462,13 +1498,14 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
irq[1] = platform_get_irq(pdev, 1);
free_irq(irq[0], host);
- free_irq(irq[1], host);
+ if (irq[1] >= 0)
+ free_irq(irq[1], host);
platform_set_drvdata(pdev, NULL);
+ clk_disable(host->hclk);
mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev);
- clk_disable(host->hclk);
pm_runtime_disable(&pdev->dev);
return 0;
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 0bdc146178db..fe90853900b4 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sh_mobile_sdhi.h>
@@ -32,6 +33,16 @@
#include "tmio_mmc.h"
+struct sh_mobile_sdhi_of_data {
+ unsigned long tmio_flags;
+};
+
+static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
+ {
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
+ },
+};
+
struct sh_mobile_sdhi {
struct clk *clk;
struct tmio_mmc_data mmc_data;
@@ -117,17 +128,26 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
};
-static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
+static const struct of_device_id sh_mobile_sdhi_of_match[] = {
+ { .compatible = "renesas,shmobile-sdhi" },
+ { .compatible = "renesas,sh7372-sdhi" },
+ { .compatible = "renesas,r8a7740-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
+
+static int sh_mobile_sdhi_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id =
+ of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
struct sh_mobile_sdhi *priv;
struct tmio_mmc_data *mmc_data;
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
struct tmio_mmc_host *host;
- char clk_name[8];
int irq, ret, i = 0;
bool multiplexed_isr = true;
- priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) {
dev_err(&pdev->dev, "kzalloc failed\n");
return -ENOMEM;
@@ -136,29 +156,26 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data = &priv->mmc_data;
if (p) {
- p->pdata = mmc_data;
if (p->init) {
ret = p->init(pdev, &sdhi_ops);
if (ret)
- goto einit;
+ return ret;
}
}
- snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
- priv->clk = clk_get(&pdev->dev, clk_name);
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
ret = PTR_ERR(priv->clk);
+ dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
goto eclkget;
}
mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
+ mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
if (p) {
mmc_data->flags = p->tmio_flags;
- if (mmc_data->flags & TMIO_MMC_HAS_IDLE_WAIT)
- mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
mmc_data->ocr_mask = p->tmio_ocr_mask;
mmc_data->capabilities |= p->tmio_caps;
mmc_data->capabilities2 |= p->tmio_caps2;
@@ -189,6 +206,11 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
*/
mmc_data->flags |= TMIO_MMC_SDIO_IRQ;
+ if (of_id && of_id->data) {
+ const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
+ mmc_data->flags |= of_data->tmio_flags;
+ }
+
ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
if (ret < 0)
goto eprobe;
@@ -201,33 +223,33 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
if (irq >= 0) {
multiplexed_isr = false;
- ret = request_irq(irq, tmio_mmc_card_detect_irq, 0,
+ ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_card_detect_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
- goto eirq_card_detect;
+ goto eirq;
}
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
if (irq >= 0) {
multiplexed_isr = false;
- ret = request_irq(irq, tmio_mmc_sdio_irq, 0,
+ ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdio_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
- goto eirq_sdio;
+ goto eirq;
}
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
if (irq >= 0) {
multiplexed_isr = false;
- ret = request_irq(irq, tmio_mmc_sdcard_irq, 0,
+ ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdcard_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
- goto eirq_sdcard;
+ goto eirq;
} else if (!multiplexed_isr) {
dev_err(&pdev->dev,
"Principal SD-card IRQ is missing among named interrupts\n");
ret = irq;
- goto eirq_sdcard;
+ goto eirq;
}
if (multiplexed_isr) {
@@ -236,46 +258,30 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
if (irq < 0)
break;
i++;
- ret = request_irq(irq, tmio_mmc_irq, 0,
+ ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
- goto eirq_multiplexed;
+ goto eirq;
}
/* There must be at least one IRQ source */
if (!i)
- goto eirq_multiplexed;
+ goto eirq;
}
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long)
(platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
- mmc_data->hclk / 1000000);
+ host->mmc->f_max / 1000000);
return ret;
-eirq_multiplexed:
- while (i--) {
- irq = platform_get_irq(pdev, i);
- free_irq(irq, host);
- }
-eirq_sdcard:
- irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
- if (irq >= 0)
- free_irq(irq, host);
-eirq_sdio:
- irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
- if (irq >= 0)
- free_irq(irq, host);
-eirq_card_detect:
+eirq:
tmio_mmc_host_remove(host);
eprobe:
- clk_put(priv->clk);
eclkget:
if (p && p->cleanup)
p->cleanup(pdev);
-einit:
- kfree(priv);
return ret;
}
@@ -283,29 +289,13 @@ static int sh_mobile_sdhi_remove(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);
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
- int i = 0, irq;
-
- if (p)
- p->pdata = NULL;
tmio_mmc_host_remove(host);
- while (1) {
- irq = platform_get_irq(pdev, i++);
- if (irq < 0)
- break;
- free_irq(irq, host);
- }
-
- clk_put(priv->clk);
-
if (p && p->cleanup)
p->cleanup(pdev);
- kfree(priv);
-
return 0;
}
@@ -316,12 +306,6 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
.runtime_resume = tmio_mmc_host_runtime_resume,
};
-static const struct of_device_id sh_mobile_sdhi_of_match[] = {
- { .compatible = "renesas,shmobile-sdhi" },
- { }
-};
-MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
-
static struct platform_driver sh_mobile_sdhi_driver = {
.driver = {
.name = "sh_mobile_sdhi",
@@ -330,7 +314,7 @@ static struct platform_driver sh_mobile_sdhi_driver = {
.of_match_table = sh_mobile_sdhi_of_match,
},
.probe = sh_mobile_sdhi_probe,
- .remove = __devexit_p(sh_mobile_sdhi_remove),
+ .remove = sh_mobile_sdhi_remove,
};
module_platform_driver(sh_mobile_sdhi_driver);
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 113ce6c9cf32..139212e79cde 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -57,7 +57,7 @@ static int tmio_mmc_resume(struct platform_device *dev)
#define tmio_mmc_resume NULL
#endif
-static int __devinit tmio_mmc_probe(struct platform_device *pdev)
+static int tmio_mmc_probe(struct platform_device *pdev)
{
const struct mfd_cell *cell = mfd_get_cell(pdev);
struct tmio_mmc_data *pdata;
@@ -107,7 +107,7 @@ out:
return ret;
}
-static int __devexit tmio_mmc_remove(struct platform_device *pdev)
+static int tmio_mmc_remove(struct platform_device *pdev)
{
const struct mfd_cell *cell = mfd_get_cell(pdev);
struct mmc_host *mmc = platform_get_drvdata(pdev);
@@ -133,7 +133,7 @@ static struct platform_driver tmio_mmc_driver = {
.owner = THIS_MODULE,
},
.probe = tmio_mmc_probe,
- .remove = __devexit_p(tmio_mmc_remove),
+ .remove = tmio_mmc_remove,
.suspend = tmio_mmc_suspend,
.resume = tmio_mmc_resume,
};
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 0d8a9bbe30be..f508ecb5b8a7 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -43,6 +43,7 @@
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -155,6 +156,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
host->set_clk_div(host->pdev, (clk>>22) & 1);
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff);
+ msleep(10);
}
static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
@@ -768,16 +770,48 @@ static int tmio_mmc_clk_update(struct mmc_host *mmc)
return ret;
}
-static void tmio_mmc_set_power(struct tmio_mmc_host *host, struct mmc_ios *ios)
+static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
{
struct mmc_host *mmc = host->mmc;
+ int ret = 0;
+
+ /* .set_ios() is returning void, so, no chance to report an error */
if (host->set_pwr)
- host->set_pwr(host->pdev, ios->power_mode != MMC_POWER_OFF);
+ host->set_pwr(host->pdev, 1);
+
+ if (!IS_ERR(mmc->supply.vmmc)) {
+ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ /*
+ * Attention: empiric value. With a b43 WiFi SDIO card this
+ * delay proved necessary for reliable card-insertion probing.
+ * 100us were not enough. Is this the same 140us delay, as in
+ * tmio_mmc_set_ios()?
+ */
+ udelay(200);
+ }
+ /*
+ * It seems, VccQ should be switched on after Vcc, this is also what the
+ * omap_hsmmc.c driver does.
+ */
+ if (!IS_ERR(mmc->supply.vqmmc) && !ret) {
+ regulator_enable(mmc->supply.vqmmc);
+ udelay(200);
+ }
+}
+
+static void tmio_mmc_power_off(struct tmio_mmc_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ if (!IS_ERR(mmc->supply.vqmmc))
+ regulator_disable(mmc->supply.vqmmc);
+
if (!IS_ERR(mmc->supply.vmmc))
- /* Errors ignored... */
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
- ios->power_mode ? ios->vdd : 0);
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+ if (host->set_pwr)
+ host->set_pwr(host->pdev, 0);
}
/* Set MMC clock / power.
@@ -828,18 +862,20 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (!host->power) {
tmio_mmc_clk_update(mmc);
pm_runtime_get_sync(dev);
- host->power = true;
}
tmio_mmc_set_clock(host, ios->clock);
- /* power up SD bus */
- tmio_mmc_set_power(host, ios);
+ if (!host->power) {
+ /* power up SD card and the bus */
+ tmio_mmc_power_on(host, ios->vdd);
+ host->power = true;
+ }
/* start bus clock */
tmio_mmc_clk_start(host);
} else if (ios->power_mode != MMC_POWER_UP) {
- if (ios->power_mode == MMC_POWER_OFF)
- tmio_mmc_set_power(host, ios);
if (host->power) {
struct tmio_mmc_data *pdata = host->pdata;
+ if (ios->power_mode == MMC_POWER_OFF)
+ tmio_mmc_power_off(host);
tmio_mmc_clk_stop(host);
host->power = false;
pm_runtime_put(dev);
@@ -918,7 +954,18 @@ static void tmio_mmc_init_ocr(struct tmio_mmc_host *host)
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
}
-int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
+static void tmio_mmc_of_parse(struct platform_device *pdev,
+ struct tmio_mmc_data *pdata)
+{
+ const struct device_node *np = pdev->dev.of_node;
+ if (!np)
+ return;
+
+ if (of_get_property(np, "toshiba,mmc-wrprotect-disable", NULL))
+ pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
+}
+
+int tmio_mmc_host_probe(struct tmio_mmc_host **host,
struct platform_device *pdev,
struct tmio_mmc_data *pdata)
{
@@ -928,6 +975,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
int ret;
u32 irq_mask = TMIO_MASK_CMD;
+ tmio_mmc_of_parse(pdev, pdata);
+
+ if (!(pdata->flags & TMIO_MMC_HAS_IDLE_WAIT))
+ pdata->write16_hook = NULL;
+
res_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res_ctl)
return -EINVAL;
@@ -936,6 +988,8 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
if (!mmc)
return -ENOMEM;
+ mmc_of_parse(mmc);
+
pdata->dev = &pdev->dev;
_host = mmc_priv(mmc);
_host->pdata = pdata;
@@ -956,7 +1010,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
}
mmc->ops = &tmio_mmc_ops;
- mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities;
+ mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps2 = pdata->capabilities2;
mmc->max_segs = 32;
mmc->max_blk_size = 512;
@@ -968,7 +1022,8 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
- mmc->caps & MMC_CAP_NONREMOVABLE);
+ mmc->caps & MMC_CAP_NONREMOVABLE ||
+ mmc->slot.cd_irq >= 0);
_host->power = false;
pm_runtime_enable(&pdev->dev);
@@ -1060,16 +1115,8 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
void tmio_mmc_host_remove(struct tmio_mmc_host *host)
{
struct platform_device *pdev = host->pdev;
- struct tmio_mmc_data *pdata = host->pdata;
struct mmc_host *mmc = host->mmc;
- if (pdata->flags & TMIO_MMC_USE_GPIO_CD)
- /*
- * This means we can miss a card-eject, but this is anyway
- * possible, because of delayed processing of hotplug events.
- */
- mmc_gpio_free_cd(mmc);
-
if (!host->native_hotplug)
pm_runtime_get_sync(&pdev->dev);
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index f18becef156d..4f84586c6e9e 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1082,7 +1082,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
msleep(1);
}
-static int __devinit via_sd_probe(struct pci_dev *pcidev,
+static int via_sd_probe(struct pci_dev *pcidev,
const struct pci_device_id *id)
{
struct mmc_host *mmc;
@@ -1176,7 +1176,7 @@ disable:
return ret;
}
-static void __devexit via_sd_remove(struct pci_dev *pcidev)
+static void via_sd_remove(struct pci_dev *pcidev)
{
struct via_crdr_mmc_host *sdhost = pci_get_drvdata(pcidev);
unsigned long flags;
@@ -1332,7 +1332,7 @@ static struct pci_driver via_sd_driver = {
.name = DRV_NAME,
.id_table = via_ids,
.probe = via_sd_probe,
- .remove = __devexit_p(via_sd_remove),
+ .remove = via_sd_remove,
.suspend = via_sd_suspend,
.resume = via_sd_resume,
};
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index d5655a63eda4..cb9f361c03ab 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -2362,6 +2362,7 @@ error4:
error1:
usb_free_urb(command_out_urb);
error0:
+ usb_put_dev(udev);
return retval;
}
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 64acd9ce141c..e954b7758876 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -1196,7 +1196,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id)
* Allocate/free MMC structure.
*/
-static int __devinit wbsd_alloc_mmc(struct device *dev)
+static int wbsd_alloc_mmc(struct device *dev)
{
struct mmc_host *mmc;
struct wbsd_host *host;
@@ -1288,7 +1288,7 @@ static void wbsd_free_mmc(struct device *dev)
* Scan for known chip id:s
*/
-static int __devinit wbsd_scan(struct wbsd_host *host)
+static int wbsd_scan(struct wbsd_host *host)
{
int i, j, k;
int id;
@@ -1344,7 +1344,7 @@ static int __devinit wbsd_scan(struct wbsd_host *host)
* Allocate/free io port ranges
*/
-static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
+static int wbsd_request_region(struct wbsd_host *host, int base)
{
if (base & 0x7)
return -EINVAL;
@@ -1374,7 +1374,7 @@ static void wbsd_release_regions(struct wbsd_host *host)
* Allocate/free DMA port and buffer
*/
-static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
+static void wbsd_request_dma(struct wbsd_host *host, int dma)
{
if (dma < 0)
return;
@@ -1452,7 +1452,7 @@ static void wbsd_release_dma(struct wbsd_host *host)
* Allocate/free IRQ.
*/
-static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
+static int wbsd_request_irq(struct wbsd_host *host, int irq)
{
int ret;
@@ -1502,7 +1502,7 @@ static void wbsd_release_irq(struct wbsd_host *host)
* Allocate all resources for the host.
*/
-static int __devinit wbsd_request_resources(struct wbsd_host *host,
+static int wbsd_request_resources(struct wbsd_host *host,
int base, int irq, int dma)
{
int ret;
@@ -1644,7 +1644,7 @@ static void wbsd_chip_poweroff(struct wbsd_host *host)
* *
\*****************************************************************************/
-static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
+static int wbsd_init(struct device *dev, int base, int irq, int dma,
int pnp)
{
struct wbsd_host *host = NULL;
@@ -1735,7 +1735,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
return 0;
}
-static void __devexit wbsd_shutdown(struct device *dev, int pnp)
+static void wbsd_shutdown(struct device *dev, int pnp)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct wbsd_host *host;
@@ -1762,13 +1762,13 @@ static void __devexit wbsd_shutdown(struct device *dev, int pnp)
* Non-PnP
*/
-static int __devinit wbsd_probe(struct platform_device *dev)
+static int wbsd_probe(struct platform_device *dev)
{
/* Use the module parameters for resources */
return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0);
}
-static int __devexit wbsd_remove(struct platform_device *dev)
+static int wbsd_remove(struct platform_device *dev)
{
wbsd_shutdown(&dev->dev, 0);
@@ -1781,7 +1781,7 @@ static int __devexit wbsd_remove(struct platform_device *dev)
#ifdef CONFIG_PNP
-static int __devinit
+static int
wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
{
int io, irq, dma;
@@ -1801,7 +1801,7 @@ wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
}
-static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
+static void wbsd_pnp_remove(struct pnp_dev *dev)
{
wbsd_shutdown(&dev->dev, 1);
}
@@ -1941,7 +1941,7 @@ static struct platform_device *wbsd_device;
static struct platform_driver wbsd_driver = {
.probe = wbsd_probe,
- .remove = __devexit_p(wbsd_remove),
+ .remove = wbsd_remove,
.suspend = wbsd_platform_suspend,
.resume = wbsd_platform_resume,
@@ -1957,7 +1957,7 @@ static struct pnp_driver wbsd_pnp_driver = {
.name = DRIVER_NAME,
.id_table = pnp_dev_table,
.probe = wbsd_pnp_probe,
- .remove = __devexit_p(wbsd_pnp_remove),
+ .remove = wbsd_pnp_remove,
.suspend = wbsd_pnp_suspend,
.resume = wbsd_pnp_resume,
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
new file mode 100644
index 000000000000..c6d001509e5a
--- /dev/null
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -0,0 +1,1029 @@
+/*
+ * WM8505/WM8650 SD/MMC Host Controller
+ *
+ * Copyright (C) 2010 Tony Prisk
+ * Copyright (C) 2008 WonderMedia Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+
+#include <asm/byteorder.h>
+
+
+#define DRIVER_NAME "wmt-sdhc"
+
+
+/* MMC/SD controller registers */
+#define SDMMC_CTLR 0x00
+#define SDMMC_CMD 0x01
+#define SDMMC_RSPTYPE 0x02
+#define SDMMC_ARG 0x04
+#define SDMMC_BUSMODE 0x08
+#define SDMMC_BLKLEN 0x0C
+#define SDMMC_BLKCNT 0x0E
+#define SDMMC_RSP 0x10
+#define SDMMC_CBCR 0x20
+#define SDMMC_INTMASK0 0x24
+#define SDMMC_INTMASK1 0x25
+#define SDMMC_STS0 0x28
+#define SDMMC_STS1 0x29
+#define SDMMC_STS2 0x2A
+#define SDMMC_STS3 0x2B
+#define SDMMC_RSPTIMEOUT 0x2C
+#define SDMMC_CLK 0x30 /* VT8500 only */
+#define SDMMC_EXTCTRL 0x34
+#define SDMMC_SBLKLEN 0x38
+#define SDMMC_DMATIMEOUT 0x3C
+
+
+/* SDMMC_CTLR bit fields */
+#define CTLR_CMD_START 0x01
+#define CTLR_CMD_WRITE 0x04
+#define CTLR_FIFO_RESET 0x08
+
+/* SDMMC_BUSMODE bit fields */
+#define BM_SPI_MODE 0x01
+#define BM_FOURBIT_MODE 0x02
+#define BM_EIGHTBIT_MODE 0x04
+#define BM_SD_OFF 0x10
+#define BM_SPI_CS 0x20
+#define BM_SD_POWER 0x40
+#define BM_SOFT_RESET 0x80
+#define BM_ONEBIT_MASK 0xFD
+
+/* SDMMC_BLKLEN bit fields */
+#define BLKL_CRCERR_ABORT 0x0800
+#define BLKL_CD_POL_HIGH 0x1000
+#define BLKL_GPI_CD 0x2000
+#define BLKL_DATA3_CD 0x4000
+#define BLKL_INT_ENABLE 0x8000
+
+/* SDMMC_INTMASK0 bit fields */
+#define INT0_MBLK_TRAN_DONE_INT_EN 0x10
+#define INT0_BLK_TRAN_DONE_INT_EN 0x20
+#define INT0_CD_INT_EN 0x40
+#define INT0_DI_INT_EN 0x80
+
+/* SDMMC_INTMASK1 bit fields */
+#define INT1_CMD_RES_TRAN_DONE_INT_EN 0x02
+#define INT1_CMD_RES_TOUT_INT_EN 0x04
+#define INT1_MBLK_AUTO_STOP_INT_EN 0x08
+#define INT1_DATA_TOUT_INT_EN 0x10
+#define INT1_RESCRC_ERR_INT_EN 0x20
+#define INT1_RCRC_ERR_INT_EN 0x40
+#define INT1_WCRC_ERR_INT_EN 0x80
+
+/* SDMMC_STS0 bit fields */
+#define STS0_WRITE_PROTECT 0x02
+#define STS0_CD_DATA3 0x04
+#define STS0_CD_GPI 0x08
+#define STS0_MBLK_DONE 0x10
+#define STS0_BLK_DONE 0x20
+#define STS0_CARD_DETECT 0x40
+#define STS0_DEVICE_INS 0x80
+
+/* SDMMC_STS1 bit fields */
+#define STS1_SDIO_INT 0x01
+#define STS1_CMDRSP_DONE 0x02
+#define STS1_RSP_TIMEOUT 0x04
+#define STS1_AUTOSTOP_DONE 0x08
+#define STS1_DATA_TIMEOUT 0x10
+#define STS1_RSP_CRC_ERR 0x20
+#define STS1_RCRC_ERR 0x40
+#define STS1_WCRC_ERR 0x80
+
+/* SDMMC_STS2 bit fields */
+#define STS2_CMD_RES_BUSY 0x10
+#define STS2_DATARSP_BUSY 0x20
+#define STS2_DIS_FORCECLK 0x80
+
+
+/* MMC/SD DMA Controller Registers */
+#define SDDMA_GCR 0x100
+#define SDDMA_IER 0x104
+#define SDDMA_ISR 0x108
+#define SDDMA_DESPR 0x10C
+#define SDDMA_RBR 0x110
+#define SDDMA_DAR 0x114
+#define SDDMA_BAR 0x118
+#define SDDMA_CPR 0x11C
+#define SDDMA_CCR 0x120
+
+
+/* SDDMA_GCR bit fields */
+#define DMA_GCR_DMA_EN 0x00000001
+#define DMA_GCR_SOFT_RESET 0x00000100
+
+/* SDDMA_IER bit fields */
+#define DMA_IER_INT_EN 0x00000001
+
+/* SDDMA_ISR bit fields */
+#define DMA_ISR_INT_STS 0x00000001
+
+/* SDDMA_RBR bit fields */
+#define DMA_RBR_FORMAT 0x40000000
+#define DMA_RBR_END 0x80000000
+
+/* SDDMA_CCR bit fields */
+#define DMA_CCR_RUN 0x00000080
+#define DMA_CCR_IF_TO_PERIPHERAL 0x00000000
+#define DMA_CCR_PERIPHERAL_TO_IF 0x00400000
+
+/* SDDMA_CCR event status */
+#define DMA_CCR_EVT_NO_STATUS 0x00000000
+#define DMA_CCR_EVT_UNDERRUN 0x00000001
+#define DMA_CCR_EVT_OVERRUN 0x00000002
+#define DMA_CCR_EVT_DESP_READ 0x00000003
+#define DMA_CCR_EVT_DATA_RW 0x00000004
+#define DMA_CCR_EVT_EARLY_END 0x00000005
+#define DMA_CCR_EVT_SUCCESS 0x0000000F
+
+#define PDMA_READ 0x00
+#define PDMA_WRITE 0x01
+
+#define WMT_SD_POWER_OFF 0
+#define WMT_SD_POWER_ON 1
+
+struct wmt_dma_descriptor {
+ u32 flags;
+ u32 data_buffer_addr;
+ u32 branch_addr;
+ u32 reserved1;
+};
+
+struct wmt_mci_caps {
+ unsigned int f_min;
+ unsigned int f_max;
+ u32 ocr_avail;
+ u32 caps;
+ u32 max_seg_size;
+ u32 max_segs;
+ u32 max_blk_size;
+};
+
+struct wmt_mci_priv {
+ struct mmc_host *mmc;
+ void __iomem *sdmmc_base;
+
+ int irq_regular;
+ int irq_dma;
+
+ void *dma_desc_buffer;
+ dma_addr_t dma_desc_device_addr;
+
+ struct completion cmdcomp;
+ struct completion datacomp;
+
+ struct completion *comp_cmd;
+ struct completion *comp_dma;
+
+ struct mmc_request *req;
+ struct mmc_command *cmd;
+
+ struct clk *clk_sdmmc;
+ struct device *dev;
+
+ u8 power_inverted;
+ u8 cd_inverted;
+};
+
+static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)
+{
+ u32 reg_tmp;
+ if (enable) {
+ if (priv->power_inverted) {
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp | BM_SD_OFF,
+ priv->sdmmc_base + SDMMC_BUSMODE);
+ } else {
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp & (~BM_SD_OFF),
+ priv->sdmmc_base + SDMMC_BUSMODE);
+ }
+ } else {
+ if (priv->power_inverted) {
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp & (~BM_SD_OFF),
+ priv->sdmmc_base + SDMMC_BUSMODE);
+ } else {
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp | BM_SD_OFF,
+ priv->sdmmc_base + SDMMC_BUSMODE);
+ }
+ }
+}
+
+static void wmt_mci_read_response(struct mmc_host *mmc)
+{
+ struct wmt_mci_priv *priv;
+ int idx1, idx2;
+ u8 tmp_resp;
+ u32 response;
+
+ priv = mmc_priv(mmc);
+
+ for (idx1 = 0; idx1 < 4; idx1++) {
+ response = 0;
+ for (idx2 = 0; idx2 < 4; idx2++) {
+ if ((idx1 == 3) && (idx2 == 3))
+ tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP);
+ else
+ tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP +
+ (idx1*4) + idx2 + 1);
+ response |= (tmp_resp << (idx2 * 8));
+ }
+ priv->cmd->resp[idx1] = cpu_to_be32(response);
+ }
+}
+
+static void wmt_mci_start_command(struct wmt_mci_priv *priv)
+{
+ u32 reg_tmp;
+
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
+ writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR);
+}
+
+static int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype,
+ u32 arg, u8 rsptype)
+{
+ struct wmt_mci_priv *priv;
+ u32 reg_tmp;
+
+ priv = mmc_priv(mmc);
+
+ /* write command, arg, resptype registers */
+ writeb(command, priv->sdmmc_base + SDMMC_CMD);
+ writel(arg, priv->sdmmc_base + SDMMC_ARG);
+ writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE);
+
+ /* reset response FIFO */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
+ writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR);
+
+ /* ensure clock enabled - VT3465 */
+ wmt_set_sd_power(priv, WMT_SD_POWER_ON);
+
+ /* clear status bits */
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS2);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS3);
+
+ /* set command type */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
+ writeb((reg_tmp & 0x0F) | (cmdtype << 4),
+ priv->sdmmc_base + SDMMC_CTLR);
+
+ return 0;
+}
+
+static void wmt_mci_disable_dma(struct wmt_mci_priv *priv)
+{
+ writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR);
+ writel(0, priv->sdmmc_base + SDDMA_IER);
+}
+
+static void wmt_complete_data_request(struct wmt_mci_priv *priv)
+{
+ struct mmc_request *req;
+ req = priv->req;
+
+ req->data->bytes_xfered = req->data->blksz * req->data->blocks;
+
+ /* unmap the DMA pages used for write data */
+ if (req->data->flags & MMC_DATA_WRITE)
+ dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg,
+ req->data->sg_len, DMA_TO_DEVICE);
+ else
+ dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg,
+ req->data->sg_len, DMA_FROM_DEVICE);
+
+ /* Check if the DMA ISR returned a data error */
+ if ((req->cmd->error) || (req->data->error))
+ mmc_request_done(priv->mmc, req);
+ else {
+ wmt_mci_read_response(priv->mmc);
+ if (!req->data->stop) {
+ /* single-block read/write requests end here */
+ mmc_request_done(priv->mmc, req);
+ } else {
+ /*
+ * we change the priv->cmd variable so the response is
+ * stored in the stop struct rather than the original
+ * calling command struct
+ */
+ priv->comp_cmd = &priv->cmdcomp;
+ init_completion(priv->comp_cmd);
+ priv->cmd = req->data->stop;
+ wmt_mci_send_command(priv->mmc, req->data->stop->opcode,
+ 7, req->data->stop->arg, 9);
+ wmt_mci_start_command(priv);
+ }
+ }
+}
+
+static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data)
+{
+ struct mmc_host *mmc;
+ struct wmt_mci_priv *priv;
+
+ int status;
+
+ priv = (struct wmt_mci_priv *)data;
+ mmc = priv->mmc;
+
+ status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F;
+
+ if (status != DMA_CCR_EVT_SUCCESS) {
+ dev_err(priv->dev, "DMA Error: Status = %d\n", status);
+ priv->req->data->error = -ETIMEDOUT;
+ complete(priv->comp_dma);
+ return IRQ_HANDLED;
+ }
+
+ priv->req->data->error = 0;
+
+ wmt_mci_disable_dma(priv);
+
+ complete(priv->comp_dma);
+
+ if (priv->comp_cmd) {
+ if (completion_done(priv->comp_cmd)) {
+ /*
+ * if the command (regular) interrupt has already
+ * completed, finish off the request otherwise we wait
+ * for the command interrupt and finish from there.
+ */
+ wmt_complete_data_request(priv);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wmt_mci_regular_isr(int irq_num, void *data)
+{
+ struct wmt_mci_priv *priv;
+ u32 status0;
+ u32 status1;
+ u32 status2;
+ u32 reg_tmp;
+ int cmd_done;
+
+ priv = (struct wmt_mci_priv *)data;
+ cmd_done = 0;
+ status0 = readb(priv->sdmmc_base + SDMMC_STS0);
+ status1 = readb(priv->sdmmc_base + SDMMC_STS1);
+ status2 = readb(priv->sdmmc_base + SDMMC_STS2);
+
+ /* Check for card insertion */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0);
+ if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) {
+ mmc_detect_change(priv->mmc, 0);
+ if (priv->cmd)
+ priv->cmd->error = -ETIMEDOUT;
+ if (priv->comp_cmd)
+ complete(priv->comp_cmd);
+ if (priv->comp_dma) {
+ wmt_mci_disable_dma(priv);
+ complete(priv->comp_dma);
+ }
+ writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0);
+ return IRQ_HANDLED;
+ }
+
+ if ((!priv->req->data) ||
+ ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) {
+ /* handle non-data & stop_transmission requests */
+ if (status1 & STS1_CMDRSP_DONE) {
+ priv->cmd->error = 0;
+ cmd_done = 1;
+ } else if ((status1 & STS1_RSP_TIMEOUT) ||
+ (status1 & STS1_DATA_TIMEOUT)) {
+ priv->cmd->error = -ETIMEDOUT;
+ cmd_done = 1;
+ }
+
+ if (cmd_done) {
+ priv->comp_cmd = NULL;
+
+ if (!priv->cmd->error)
+ wmt_mci_read_response(priv->mmc);
+
+ priv->cmd = NULL;
+
+ mmc_request_done(priv->mmc, priv->req);
+ }
+ } else {
+ /* handle data requests */
+ if (status1 & STS1_CMDRSP_DONE) {
+ if (priv->cmd)
+ priv->cmd->error = 0;
+ if (priv->comp_cmd)
+ complete(priv->comp_cmd);
+ }
+
+ if ((status1 & STS1_RSP_TIMEOUT) ||
+ (status1 & STS1_DATA_TIMEOUT)) {
+ if (priv->cmd)
+ priv->cmd->error = -ETIMEDOUT;
+ if (priv->comp_cmd)
+ complete(priv->comp_cmd);
+ if (priv->comp_dma) {
+ wmt_mci_disable_dma(priv);
+ complete(priv->comp_dma);
+ }
+ }
+
+ if (priv->comp_dma) {
+ /*
+ * If the dma interrupt has already completed, finish
+ * off the request; otherwise we wait for the DMA
+ * interrupt and finish from there.
+ */
+ if (completion_done(priv->comp_dma))
+ wmt_complete_data_request(priv);
+ }
+ }
+
+ writeb(status0, priv->sdmmc_base + SDMMC_STS0);
+ writeb(status1, priv->sdmmc_base + SDMMC_STS1);
+ writeb(status2, priv->sdmmc_base + SDMMC_STS2);
+
+ return IRQ_HANDLED;
+}
+
+static void wmt_reset_hardware(struct mmc_host *mmc)
+{
+ struct wmt_mci_priv *priv;
+ u32 reg_tmp;
+
+ priv = mmc_priv(mmc);
+
+ /* reset controller */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE);
+
+ /* reset response FIFO */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
+ writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR);
+
+ /* enable GPI pin to detect card */
+ writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN);
+
+ /* clear interrupt status */
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
+
+ /* setup interrupts */
+ writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base +
+ SDMMC_INTMASK0);
+ writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN |
+ INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1);
+
+ /* set the DMA timeout */
+ writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT);
+
+ /* auto clock freezing enable */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2);
+ writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2);
+
+ /* set a default clock speed of 400Khz */
+ clk_set_rate(priv->clk_sdmmc, 400000);
+}
+
+static int wmt_dma_init(struct mmc_host *mmc)
+{
+ struct wmt_mci_priv *priv;
+
+ priv = mmc_priv(mmc);
+
+ writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR);
+ writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR);
+ if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0)
+ return 0;
+ else
+ return 1;
+}
+
+static void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc,
+ u16 req_count, u32 buffer_addr, u32 branch_addr, int end)
+{
+ desc->flags = 0x40000000 | req_count;
+ if (end)
+ desc->flags |= 0x80000000;
+ desc->data_buffer_addr = buffer_addr;
+ desc->branch_addr = branch_addr;
+}
+
+static void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir)
+{
+ struct wmt_mci_priv *priv;
+ u32 reg_tmp;
+
+ priv = mmc_priv(mmc);
+
+ /* Enable DMA Interrupts */
+ writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER);
+
+ /* Write DMA Descriptor Pointer Register */
+ writel(descaddr, priv->sdmmc_base + SDDMA_DESPR);
+
+ writel(0x00, priv->sdmmc_base + SDDMA_CCR);
+
+ if (dir == PDMA_WRITE) {
+ reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR);
+ writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base +
+ SDDMA_CCR);
+ } else {
+ reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR);
+ writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base +
+ SDDMA_CCR);
+ }
+}
+
+static void wmt_dma_start(struct wmt_mci_priv *priv)
+{
+ u32 reg_tmp;
+
+ reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR);
+ writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR);
+}
+
+static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct wmt_mci_priv *priv;
+ struct wmt_dma_descriptor *desc;
+ u8 command;
+ u8 cmdtype;
+ u32 arg;
+ u8 rsptype;
+ u32 reg_tmp;
+
+ struct scatterlist *sg;
+ int i;
+ int sg_cnt;
+ int offset;
+ u32 dma_address;
+ int desc_cnt;
+
+ priv = mmc_priv(mmc);
+ priv->req = req;
+
+ /*
+ * Use the cmd variable to pass a pointer to the resp[] structure
+ * This is required on multi-block requests to pass the pointer to the
+ * stop command
+ */
+ priv->cmd = req->cmd;
+
+ command = req->cmd->opcode;
+ arg = req->cmd->arg;
+ rsptype = mmc_resp_type(req->cmd);
+ cmdtype = 0;
+
+ /* rsptype=7 only valid for SPI commands - should be =2 for SD */
+ if (rsptype == 7)
+ rsptype = 2;
+ /* rsptype=21 is R1B, convert for controller */
+ if (rsptype == 21)
+ rsptype = 9;
+
+ if (!req->data) {
+ wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype);
+ wmt_mci_start_command(priv);
+ /* completion is now handled in the regular_isr() */
+ }
+ if (req->data) {
+ priv->comp_cmd = &priv->cmdcomp;
+ init_completion(priv->comp_cmd);
+
+ wmt_dma_init(mmc);
+
+ /* set controller data length */
+ reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
+ writew((reg_tmp & 0xF800) | (req->data->blksz - 1),
+ priv->sdmmc_base + SDMMC_BLKLEN);
+
+ /* set controller block count */
+ writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT);
+
+ desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer;
+
+ if (req->data->flags & MMC_DATA_WRITE) {
+ sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg,
+ req->data->sg_len, DMA_TO_DEVICE);
+ cmdtype = 1;
+ if (req->data->blocks > 1)
+ cmdtype = 3;
+ } else {
+ sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg,
+ req->data->sg_len, DMA_FROM_DEVICE);
+ cmdtype = 2;
+ if (req->data->blocks > 1)
+ cmdtype = 4;
+ }
+
+ dma_address = priv->dma_desc_device_addr + 16;
+ desc_cnt = 0;
+
+ for_each_sg(req->data->sg, sg, sg_cnt, i) {
+ offset = 0;
+ while (offset < sg_dma_len(sg)) {
+ wmt_dma_init_descriptor(desc, req->data->blksz,
+ sg_dma_address(sg)+offset,
+ dma_address, 0);
+ desc++;
+ desc_cnt++;
+ offset += req->data->blksz;
+ dma_address += 16;
+ if (desc_cnt == req->data->blocks)
+ break;
+ }
+ }
+ desc--;
+ desc->flags |= 0x80000000;
+
+ if (req->data->flags & MMC_DATA_WRITE)
+ wmt_dma_config(mmc, priv->dma_desc_device_addr,
+ PDMA_WRITE);
+ else
+ wmt_dma_config(mmc, priv->dma_desc_device_addr,
+ PDMA_READ);
+
+ wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype);
+
+ priv->comp_dma = &priv->datacomp;
+ init_completion(priv->comp_dma);
+
+ wmt_dma_start(priv);
+ wmt_mci_start_command(priv);
+ }
+}
+
+static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct wmt_mci_priv *priv;
+ u32 reg_tmp;
+
+ priv = mmc_priv(mmc);
+
+ if (ios->power_mode == MMC_POWER_UP) {
+ wmt_reset_hardware(mmc);
+
+ wmt_set_sd_power(priv, WMT_SD_POWER_ON);
+ }
+ if (ios->power_mode == MMC_POWER_OFF)
+ wmt_set_sd_power(priv, WMT_SD_POWER_OFF);
+
+ if (ios->clock != 0)
+ clk_set_rate(priv->clk_sdmmc, ios->clock);
+
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
+ writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL);
+ break;
+ case MMC_BUS_WIDTH_4:
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base +
+ SDMMC_BUSMODE);
+
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
+ writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL);
+ break;
+ case MMC_BUS_WIDTH_1:
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base +
+ SDMMC_BUSMODE);
+
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
+ writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL);
+ break;
+ }
+}
+
+static int wmt_mci_get_ro(struct mmc_host *mmc)
+{
+ struct wmt_mci_priv *priv = mmc_priv(mmc);
+
+ return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT);
+}
+
+static int wmt_mci_get_cd(struct mmc_host *mmc)
+{
+ struct wmt_mci_priv *priv = mmc_priv(mmc);
+ u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3;
+
+ return !(cd ^ priv->cd_inverted);
+}
+
+static struct mmc_host_ops wmt_mci_ops = {
+ .request = wmt_mci_request,
+ .set_ios = wmt_mci_set_ios,
+ .get_ro = wmt_mci_get_ro,
+ .get_cd = wmt_mci_get_cd,
+};
+
+/* Controller capabilities */
+static struct wmt_mci_caps wm8505_caps = {
+ .f_min = 390425,
+ .f_max = 50000000,
+ .ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_SD_HIGHSPEED,
+ .max_seg_size = 65024,
+ .max_segs = 128,
+ .max_blk_size = 2048,
+};
+
+static struct of_device_id wmt_mci_dt_ids[] = {
+ { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps },
+ { /* Sentinel */ },
+};
+
+static int wmt_mci_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct wmt_mci_priv *priv;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(wmt_mci_dt_ids, &pdev->dev);
+ const struct wmt_mci_caps *wmt_caps = of_id->data;
+ int ret;
+ int regular_irq, dma_irq;
+
+ if (!of_id || !of_id->data) {
+ dev_err(&pdev->dev, "Controller capabilities data missing\n");
+ return -EFAULT;
+ }
+
+ if (!np) {
+ dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n");
+ return -EFAULT;
+ }
+
+ regular_irq = irq_of_parse_and_map(np, 0);
+ dma_irq = irq_of_parse_and_map(np, 1);
+
+ if (!regular_irq || !dma_irq) {
+ dev_err(&pdev->dev, "Getting IRQs failed!\n");
+ ret = -ENXIO;
+ goto fail1;
+ }
+
+ mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev);
+ if (!mmc) {
+ dev_err(&pdev->dev, "Failed to allocate mmc_host\n");
+ ret = -ENOMEM;
+ goto fail1;
+ }
+
+ mmc->ops = &wmt_mci_ops;
+ mmc->f_min = wmt_caps->f_min;
+ mmc->f_max = wmt_caps->f_max;
+ mmc->ocr_avail = wmt_caps->ocr_avail;
+ mmc->caps = wmt_caps->caps;
+
+ mmc->max_seg_size = wmt_caps->max_seg_size;
+ mmc->max_segs = wmt_caps->max_segs;
+ mmc->max_blk_size = wmt_caps->max_blk_size;
+
+ mmc->max_req_size = (16*512*mmc->max_segs);
+ mmc->max_blk_count = mmc->max_req_size / 512;
+
+ priv = mmc_priv(mmc);
+ priv->mmc = mmc;
+ priv->dev = &pdev->dev;
+
+ priv->power_inverted = 0;
+ priv->cd_inverted = 0;
+
+ if (of_get_property(np, "sdon-inverted", NULL))
+ priv->power_inverted = 1;
+ if (of_get_property(np, "cd-inverted", NULL))
+ priv->cd_inverted = 1;
+
+ priv->sdmmc_base = of_iomap(np, 0);
+ if (!priv->sdmmc_base) {
+ dev_err(&pdev->dev, "Failed to map IO space\n");
+ ret = -ENOMEM;
+ goto fail2;
+ }
+
+ priv->irq_regular = regular_irq;
+ priv->irq_dma = dma_irq;
+
+ ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Register regular IRQ fail\n");
+ goto fail3;
+ }
+
+ ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv);
+ if (ret) {
+ dev_err(&pdev->dev, "Register DMA IRQ fail\n");
+ goto fail4;
+ }
+
+ /* alloc some DMA buffers for descriptors/transfers */
+ priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev,
+ mmc->max_blk_count * 16,
+ &priv->dma_desc_device_addr,
+ 208);
+ if (!priv->dma_desc_buffer) {
+ dev_err(&pdev->dev, "DMA alloc fail\n");
+ ret = -EPERM;
+ goto fail5;
+ }
+
+ platform_set_drvdata(pdev, mmc);
+
+ priv->clk_sdmmc = of_clk_get(np, 0);
+ if (IS_ERR(priv->clk_sdmmc)) {
+ dev_err(&pdev->dev, "Error getting clock\n");
+ ret = PTR_ERR(priv->clk_sdmmc);
+ goto fail5;
+ }
+
+ clk_prepare_enable(priv->clk_sdmmc);
+
+ /* configure the controller to a known 'ready' state */
+ wmt_reset_hardware(mmc);
+
+ mmc_add_host(mmc);
+
+ dev_info(&pdev->dev, "WMT SDHC Controller initialized\n");
+
+ return 0;
+fail5:
+ free_irq(dma_irq, priv);
+fail4:
+ free_irq(regular_irq, priv);
+fail3:
+ iounmap(priv->sdmmc_base);
+fail2:
+ mmc_free_host(mmc);
+fail1:
+ return ret;
+}
+
+static int wmt_mci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct wmt_mci_priv *priv;
+ struct resource *res;
+ u32 reg_tmp;
+
+ mmc = platform_get_drvdata(pdev);
+ priv = mmc_priv(mmc);
+
+ /* reset SD controller */
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE);
+ reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
+ writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
+
+ /* release the dma buffers */
+ dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16,
+ priv->dma_desc_buffer, priv->dma_desc_device_addr);
+
+ mmc_remove_host(mmc);
+
+ free_irq(priv->irq_regular, priv);
+ free_irq(priv->irq_dma, priv);
+
+ iounmap(priv->sdmmc_base);
+
+ clk_disable_unprepare(priv->clk_sdmmc);
+ clk_put(priv->clk_sdmmc);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ mmc_free_host(mmc);
+
+ platform_set_drvdata(pdev, NULL);
+
+ dev_info(&pdev->dev, "WMT MCI device removed\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wmt_mci_suspend(struct device *dev)
+{
+ u32 reg_tmp;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct wmt_mci_priv *priv;
+ int ret;
+
+ if (!mmc)
+ return 0;
+
+ priv = mmc_priv(mmc);
+ ret = mmc_suspend_host(mmc);
+
+ if (!ret) {
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
+ SDMMC_BUSMODE);
+
+ reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
+ writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
+
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
+ writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
+
+ clk_disable(priv->clk_sdmmc);
+ }
+ return ret;
+}
+
+static int wmt_mci_resume(struct device *dev)
+{
+ u32 reg_tmp;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct wmt_mci_priv *priv;
+ int ret = 0;
+
+ if (mmc) {
+ priv = mmc_priv(mmc);
+ clk_enable(priv->clk_sdmmc);
+
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
+ writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
+ SDMMC_BUSMODE);
+
+ reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
+ writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE),
+ priv->sdmmc_base + SDMMC_BLKLEN);
+
+ reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0);
+ writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base +
+ SDMMC_INTMASK0);
+
+ ret = mmc_resume_host(mmc);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops wmt_mci_pm = {
+ .suspend = wmt_mci_suspend,
+ .resume = wmt_mci_resume,
+};
+
+#define wmt_mci_pm_ops (&wmt_mci_pm)
+
+#else /* !CONFIG_PM */
+
+#define wmt_mci_pm_ops NULL
+
+#endif
+
+static struct platform_driver wmt_mci_driver = {
+ .probe = wmt_mci_probe,
+ .remove = wmt_mci_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = wmt_mci_pm_ops,
+ .of_match_table = wmt_mci_dt_ids,
+ },
+};
+
+module_platform_driver(wmt_mci_driver);
+
+MODULE_DESCRIPTION("Wondermedia MMC/SD Driver");
+MODULE_AUTHOR("Tony Prisk");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids);