summaryrefslogtreecommitdiffstats
path: root/drivers/mci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mci')
-rw-r--r--drivers/mci/Kconfig20
-rw-r--r--drivers/mci/Makefile6
-rw-r--r--drivers/mci/arasan-sdhci.c423
-rw-r--r--drivers/mci/dove-sdhci.c159
-rw-r--r--drivers/mci/imx-esdhc-common.c275
-rw-r--r--drivers/mci/imx-esdhc-pbl.c333
-rw-r--r--drivers/mci/imx-esdhc.c441
-rw-r--r--drivers/mci/imx-esdhc.h108
-rw-r--r--drivers/mci/mci-bcm2835.c227
-rw-r--r--drivers/mci/mci-core.c4
-rw-r--r--drivers/mci/sdhci.c140
-rw-r--r--drivers/mci/sdhci.h127
-rw-r--r--drivers/mci/tegra-sdmmc.c159
13 files changed, 1341 insertions, 1081 deletions
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 4a71a46097..80b3a26002 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -1,3 +1,6 @@
+config MCI_SDHCI
+ bool
+
menuconfig MCI
bool "MCI drivers"
select DISK
@@ -69,10 +72,12 @@ config MCI_BCM283X
config MCI_BCM283X_SDHOST
bool "BCM283X sdhost"
depends on ARCH_BCM283X
+ select MCI_SDHCI
config MCI_DOVE
bool "Marvell Dove SDHCI"
depends on ARCH_DOVE
+ select MCI_SDHCI
help
Enable this entry to add support to read and write SD cards on a
Marvell Dove SoC based system.
@@ -87,6 +92,7 @@ config MCI_IMX
config MCI_IMX_ESDHC
bool "i.MX esdhc"
depends on ARCH_IMX || ARCH_LAYERSCAPE
+ select MCI_SDHCI
help
Enable this entry to add support to read and write SD cards on a
Freescale i.MX25/35/51 based system.
@@ -97,9 +103,6 @@ config MCI_IMX_ESDHC_PIO
help
mostly useful for debugging. Normally you should use DMA.
-config MCI_IMX_ESDHC_PBL
- bool
-
config MCI_OMAP_HSMMC
bool "OMAP HSMMC"
depends on ARCH_OMAP4 || ARCH_OMAP3 || ARCH_AM33XX
@@ -131,10 +134,17 @@ config MCI_MMCI
config MCI_TEGRA
bool "Tegra SD/MMC"
depends on ARCH_TEGRA
+ select MCI_SDHCI
help
Enable this to support SD and MMC card read/write on a Tegra based
systems.
+config MCI_ARASAN
+ bool "Arasan SDHCI Controller"
+ help
+ Enable this to support SD and MMC card read/write on systems with
+ the Arasan SD3.0 / SDIO3.0 / eMMC4.51 host controller.
+
config MCI_SPI
bool "MMC/SD over SPI"
select CRC7
@@ -163,3 +173,7 @@ config MCI_STM32_SDMMC2
say Y or M here.
endif
+
+config MCI_IMX_ESDHC_PBL
+ bool
+ select MCI_SDHCI
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 04c1287fee..54eb65978e 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,11 +1,12 @@
obj-$(CONFIG_MCI) += mci-core.o
+obj-$(CONFIG_MCI_ARASAN) += arasan-sdhci.o
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o
obj-$(CONFIG_MCI_BCM283X_SDHOST) += bcm2835-sdhost.o
obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o
obj-$(CONFIG_MCI_IMX) += imx.o
-obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
-pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o
+obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o imx-esdhc-common.o
+pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o imx-esdhc-common.o
obj-$(CONFIG_MCI_MXS) += mxs.o
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_MCI_PXA) += pxamci.o
@@ -15,3 +16,4 @@ obj-$(CONFIG_MCI_SPI) += mci_spi.o
obj-$(CONFIG_MCI_DW) += dw_mmc.o
obj-$(CONFIG_MCI_MMCI) += mmci.o
obj-$(CONFIG_MCI_STM32_SDMMC2) += stm32_sdmmc2.o
+obj-pbl-$(CONFIG_MCI_SDHCI) += sdhci.o
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
new file mode 100644
index 0000000000..b43a4f8ddf
--- /dev/null
+++ b/drivers/mci/arasan-sdhci.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <mci.h>
+
+#include "sdhci.h"
+
+#define SDHCI_ARASAN_HCAP_CLK_FREQ_MASK 0xFF00
+#define SDHCI_ARASAN_HCAP_CLK_FREQ_SHIFT 8
+#define SDHCI_INT_ADMAE BIT(29)
+#define SDHCI_ARASAN_INT_DATA_MASK SDHCI_INT_XFER_COMPLETE | \
+ SDHCI_INT_DMA | \
+ SDHCI_INT_SPACE_AVAIL | \
+ SDHCI_INT_DATA_AVAIL | \
+ SDHCI_INT_DATA_TIMEOUT | \
+ SDHCI_INT_DATA_CRC | \
+ SDHCI_INT_DATA_END_BIT | \
+ SDHCI_INT_ADMAE
+
+#define SDHCI_ARASAN_INT_CMD_MASK SDHCI_INT_CMD_COMPLETE | \
+ SDHCI_INT_TIMEOUT | \
+ SDHCI_INT_CRC | \
+ SDHCI_INT_END_BIT | \
+ SDHCI_INT_INDEX
+
+#define SDHCI_ARASAN_BUS_WIDTH 4
+#define TIMEOUT_VAL 0xE
+
+struct arasan_sdhci_host {
+ struct mci_host mci;
+ struct sdhci sdhci;
+ void __iomem *ioaddr;
+ unsigned int quirks; /* Arasan deviations from spec */
+/* Controller does not have CD wired and will not function normally without */
+#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
+#define SDHCI_ARASAN_QUIRK_NO_1_8_V BIT(1)
+};
+
+
+static inline
+struct arasan_sdhci_host *to_arasan_sdhci_host(struct mci_host *mci)
+{
+ return container_of(mci, struct arasan_sdhci_host, mci);
+}
+
+static inline
+struct arasan_sdhci_host *sdhci_to_arasan(struct sdhci *sdhci)
+{
+ return container_of(sdhci, struct arasan_sdhci_host, sdhci);
+}
+
+static void arasan_sdhci_writel(struct sdhci *sdhci, int reg, u32 val)
+{
+ struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+
+ writel(val, p->ioaddr + reg);
+}
+
+static void arasan_sdhci_writew(struct sdhci *sdhci, int reg, u16 val)
+{
+ struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+
+ writew(val, p->ioaddr + reg);
+}
+
+static void arasan_sdhci_writeb(struct sdhci *sdhci, int reg, u8 val)
+{
+ struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+
+ writeb(val, p->ioaddr + reg);
+}
+
+static u32 arasan_sdhci_readl(struct sdhci *sdhci, int reg)
+{
+ struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+
+ return readl(p->ioaddr + reg);
+}
+
+static u16 arasan_sdhci_readw(struct sdhci *sdhci, int reg)
+{
+ struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+
+ return readw(p->ioaddr + reg);
+}
+
+static u8 arasan_sdhci_readb(struct sdhci *sdhci, int reg)
+{
+ struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+
+ return readb(p->ioaddr + reg);
+}
+
+static int arasan_sdhci_card_present(struct mci_host *mci)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+
+ return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT);
+}
+
+static int arasan_sdhci_card_write_protected(struct mci_host *mci)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+
+ return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
+}
+
+static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask)
+{
+ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask);
+
+ /* wait for reset completion */
+ if (wait_on_timeout(100 * MSECOND,
+ !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))){
+ dev_err(host->mci.hw_dev, "SDHCI reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
+ u8 ctrl;
+
+ ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+ ctrl |= SDHCI_CARD_DETECT_TEST_LEVEL | SDHCI_CARD_DETECT_SIGNAL_SELECTION;
+ sdhci_write8(&host->sdhci, ctrl, SDHCI_HOST_CONTROL);
+ }
+
+ return 0;
+}
+
+static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ int ret;
+
+ ret = arasan_sdhci_reset(host, SDHCI_RESET_ALL);
+ if (ret)
+ return ret;
+
+ sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL,
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
+ SDHCI_ARASAN_INT_DATA_MASK |
+ SDHCI_ARASAN_INT_CMD_MASK);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+
+ return 0;
+}
+
+#define SDHCI_MAX_DIV_SPEC_300 2046
+
+static u16 arasan_sdhci_get_clock_divider(struct arasan_sdhci_host *host,
+ unsigned int reqclk)
+{
+ u16 div;
+
+ for (div = 1; div < SDHCI_MAX_DIV_SPEC_300; div += 2)
+ if ((host->mci.f_max / div) <= reqclk)
+ break;
+ div /= 2;
+
+ return div;
+}
+
+#define SDHCI_FREQ_SEL_10_BIT(x) (((x) & 0x300) >> 2)
+
+static void arasan_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ u16 val;
+
+ /* stop clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+ if (ios->clock) {
+ u64 start;
+
+ /* set & start clock */
+ val = arasan_sdhci_get_clock_divider(host, ios->clock);
+ /* Bit 6 & 7 are upperbits of 10bit divider */
+ val = SDHCI_FREQ_SEL(val) | SDHCI_FREQ_SEL_10_BIT(val);
+ val |= SDHCI_INTCLOCK_EN;
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+ start = get_time_ns();
+ while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) &
+ SDHCI_INTCLOCK_STABLE)) {
+ if (is_timeout(start, 20 * MSECOND)) {
+ dev_err(host->mci.hw_dev,
+ "SDHCI clock stable timeout\n");
+ return;
+ }
+ }
+ /* enable bus clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL,
+ val | SDHCI_SDCLOCK_EN);
+ }
+
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) &
+ ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT);
+
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ val |= SDHCI_DATA_WIDTH_8BIT;
+ break;
+ case MMC_BUS_WIDTH_4:
+ val |= SDHCI_DATA_WIDTH_4BIT;
+ break;
+ }
+
+ if (ios->clock > 26000000)
+ val |= SDHCI_HIGHSPEED_EN;
+ else
+ val &= ~SDHCI_HIGHSPEED_EN;
+
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
+}
+
+static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask)
+{
+ u64 start = get_time_ns();
+ u16 stat;
+
+ do {
+ stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
+ if (stat & SDHCI_INT_ERROR) {
+ dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
+ sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS));
+ return -EPERM;
+ }
+
+ if (is_timeout(start, 1000 * MSECOND)) {
+ dev_err(host->mci.hw_dev,
+ "SDHCI timeout while waiting for done\n");
+ return -ETIMEDOUT;
+ }
+ } while ((stat & mask) != mask);
+
+ return 0;
+}
+
+static void print_error(struct arasan_sdhci_host *host, int cmdidx)
+{
+ dev_err(host->mci.hw_dev,
+ "error while transfering data for command %d\n", cmdidx);
+ dev_err(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
+}
+
+static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ u32 mask, command, xfer;
+ int ret;
+
+ /* Wait for idle before next command */
+ mask = SDHCI_CMD_INHIBIT_CMD;
+ if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
+ mask |= SDHCI_CMD_INHIBIT_DATA;
+
+ ret = wait_on_timeout(10 * MSECOND,
+ !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
+
+ if (ret) {
+ dev_err(host->mci.hw_dev,
+ "SDHCI timeout while waiting for idle\n");
+ return ret;
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ mask = SDHCI_INT_CMD_COMPLETE;
+ if (data)
+ mask |= SDHCI_INT_XFER_COMPLETE;
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false, &command, &xfer);
+
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, TIMEOUT_VAL);
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
+ SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+ ret = arasan_sdhci_wait_for_done(host, mask);
+ if (ret == -EPERM)
+ goto error;
+ else if (ret)
+ return ret;
+
+ sdhci_read_response(&host->sdhci, cmd);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, mask);
+
+ if (data)
+ ret = sdhci_transfer_data(&host->sdhci, data);
+
+error:
+ if (ret) {
+ print_error(host, cmd->cmdidx);
+ arasan_sdhci_reset(host, BIT(1)); // SDHCI_RESET_CMD
+ arasan_sdhci_reset(host, BIT(2)); // SDHCI_RESET_DATA
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ return ret;
+}
+
+
+static void arasan_sdhci_set_mci_caps(struct arasan_sdhci_host *host)
+{
+ u16 caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);
+
+ if ((caps & SDHCI_HOSTCAP_VOLTAGE_180) &&
+ !(host->quirks & SDHCI_ARASAN_QUIRK_NO_1_8_V))
+ host->mci.voltages |= MMC_VDD_165_195;
+ if (caps & SDHCI_HOSTCAP_VOLTAGE_300)
+ host->mci.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+ if (caps & SDHCI_HOSTCAP_VOLTAGE_330)
+ host->mci.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ if (caps & SDHCI_HOSTCAP_HIGHSPEED)
+ host->mci.host_caps |= (MMC_CAP_MMC_HIGHSPEED_52MHZ |
+ MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_SD_HIGHSPEED);
+
+ /* parse board supported bus width capabilities */
+ mci_of_parse(&host->mci);
+
+ /* limit bus widths to controller capabilities */
+ if (!(caps & SDHCI_HOSTCAP_8BIT))
+ host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA;
+}
+
+static int arasan_sdhci_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct arasan_sdhci_host *arasan_sdhci;
+ struct clk *clk_xin, *clk_ahb;
+ struct resource *iores;
+ struct mci_host *mci;
+ int ret;
+
+ arasan_sdhci = xzalloc(sizeof(*arasan_sdhci));
+
+ mci = &arasan_sdhci->mci;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ arasan_sdhci->ioaddr = IOMEM(iores->start);
+
+ clk_ahb = clk_get(dev, "clk_ahb");
+ if (IS_ERR(clk_ahb)) {
+ dev_err(dev, "clk_ahb clock not found.\n");
+ return PTR_ERR(clk_ahb);
+ }
+
+ clk_xin = clk_get(dev, "clk_xin");
+ if (IS_ERR(clk_xin)) {
+ dev_err(dev, "clk_xin clock not found.\n");
+ return PTR_ERR(clk_xin);
+ }
+ ret = clk_enable(clk_ahb);
+ if (ret) {
+ dev_err(dev, "Failed to enable AHB clock: %s\n",
+ strerror(ret));
+ return ret;
+ }
+
+ ret = clk_enable(clk_xin);
+ if (ret) {
+ dev_err(dev, "Failed to enable SD clock: %s\n", strerror(ret));
+ return ret;
+ }
+
+ if (of_property_read_bool(np, "xlnx,fails-without-test-cd"))
+ arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST;
+
+ if (of_property_read_bool(np, "no-1-8-v"))
+ arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_NO_1_8_V;
+
+ arasan_sdhci->sdhci.read32 = arasan_sdhci_readl;
+ arasan_sdhci->sdhci.read16 = arasan_sdhci_readw;
+ arasan_sdhci->sdhci.read8 = arasan_sdhci_readb;
+ arasan_sdhci->sdhci.write32 = arasan_sdhci_writel;
+ arasan_sdhci->sdhci.write16 = arasan_sdhci_writew;
+ arasan_sdhci->sdhci.write8 = arasan_sdhci_writeb;
+ mci->send_cmd = arasan_sdhci_send_cmd;
+ mci->set_ios = arasan_sdhci_set_ios;
+ mci->init = arasan_sdhci_init;
+ mci->card_present = arasan_sdhci_card_present;
+ mci->card_write_protected = arasan_sdhci_card_write_protected;
+ mci->hw_dev = dev;
+
+ mci->f_max = clk_get_rate(clk_xin);
+ mci->f_min = 50000000 / 256;
+
+ arasan_sdhci_set_mci_caps(arasan_sdhci);
+
+ dev->priv = arasan_sdhci;
+
+ return mci_register(&arasan_sdhci->mci);
+}
+
+static __maybe_unused struct of_device_id arasan_sdhci_compatible[] = {
+ { .compatible = "arasan,sdhci-8.9a" },
+ { /* sentinel */ }
+};
+
+static struct driver_d arasan_sdhci_driver = {
+ .name = "arasan-sdhci",
+ .probe = arasan_sdhci_probe,
+ .of_compatible = DRV_OF_COMPAT(arasan_sdhci_compatible),
+};
+device_platform_driver(arasan_sdhci_driver);
diff --git a/drivers/mci/dove-sdhci.c b/drivers/mci/dove-sdhci.c
index caee4107eb..bccda53994 100644
--- a/drivers/mci/dove-sdhci.c
+++ b/drivers/mci/dove-sdhci.c
@@ -33,38 +33,51 @@
struct dove_sdhci {
struct mci_host mci;
void __iomem *base;
+ struct sdhci sdhci;
};
#define priv_from_mci_host(h) \
container_of(h, struct dove_sdhci, mci);
-static inline void dove_sdhci_writel(struct dove_sdhci *p, int reg, u32 val)
+static void dove_sdhci_writel(struct sdhci *sdhci, int reg, u32 val)
{
+ struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
+
writel(val, p->base + reg);
}
-static inline void dove_sdhci_writew(struct dove_sdhci *p, int reg, u16 val)
+static void dove_sdhci_writew(struct sdhci *sdhci, int reg, u16 val)
{
+ struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
+
writew(val, p->base + reg);
}
-static inline void dove_sdhci_writeb(struct dove_sdhci *p, int reg, u8 val)
+static void dove_sdhci_writeb(struct sdhci *sdhci, int reg, u8 val)
{
+ struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
+
writeb(val, p->base + reg);
}
-static inline u32 dove_sdhci_readl(struct dove_sdhci *p, int reg)
+static u32 dove_sdhci_readl(struct sdhci *sdhci, int reg)
{
+ struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
+
return readl(p->base + reg);
}
-static inline u16 dove_sdhci_readw(struct dove_sdhci *p, int reg)
+static u16 dove_sdhci_readw(struct sdhci *sdhci, int reg)
{
+ struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
+
return readw(p->base + reg);
}
-static inline u8 dove_sdhci_readb(struct dove_sdhci *p, int reg)
+static u8 dove_sdhci_readb(struct sdhci *sdhci, int reg)
{
+ struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
+
return readb(p->base + reg);
}
@@ -75,7 +88,7 @@ static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask)
start = get_time_ns();
while (1) {
- status = dove_sdhci_readw(host, SDHCI_INT_NORMAL_STATUS);
+ status = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
if (status & SDHCI_INT_ERROR)
return -EPERM;
/* this special quirk is necessary, as the dma
@@ -83,8 +96,8 @@ static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask)
* restart after acknowledging it this way.
*/
if (status & SDHCI_INT_DMA) {
- u32 addr = dove_sdhci_readl(host, SDHCI_DMA_ADDRESS);
- dove_sdhci_writel(host, SDHCI_DMA_ADDRESS, addr);
+ u32 addr = sdhci_read32(&host->sdhci, SDHCI_DMA_ADDRESS);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, addr);
}
if (status & mask)
break;
@@ -100,12 +113,13 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data)
{
u16 val;
+ u32 command, xfer;
u64 start;
int ret;
- unsigned int num_bytes = data->blocks * data->blocksize;
+ unsigned int num_bytes = 0;
struct dove_sdhci *host = priv_from_mci_host(mci);
- dove_sdhci_writel(host, SDHCI_INT_STATUS, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
/* Do not wait for CMD_INHIBIT_DAT on stop commands */
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
@@ -116,7 +130,7 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
/* Wait for bus idle */
start = get_time_ns();
while (1) {
- if (!(dove_sdhci_readw(host, SDHCI_PRESENT_STATE) & val))
+ if (!(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & val))
break;
if (is_timeout(start, 10 * MSECOND)) {
dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
@@ -126,14 +140,15 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
/* setup transfer data */
if (data) {
+ num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_READ)
- dove_sdhci_writel(host, SDHCI_DMA_ADDRESS, (u32)data->dest);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest);
else
- dove_sdhci_writel(host, SDHCI_DMA_ADDRESS, (u32)data->src);
- dove_sdhci_writew(host, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src);
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
- dove_sdhci_writew(host, SDHCI_BLOCK_COUNT, data->blocks);
- dove_sdhci_writeb(host, SDHCI_TIMEOUT_CONTROL, 0xe);
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
if (data->flags & MMC_DATA_WRITE)
@@ -145,84 +160,48 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
}
/* setup transfer mode */
- val = 0;
- if (data) {
- val |= SDHCI_DMA_EN | SDHCI_BLOCK_COUNT_EN;
- if (data->blocks > 1)
- val |= SDHCI_MULTIPLE_BLOCKS;
- if (data->flags & MMC_DATA_READ)
- val |= SDHCI_DATA_TO_HOST;
- }
- dove_sdhci_writew(host, SDHCI_TRANSFER_MODE, val);
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, true, &command, &xfer);
- dove_sdhci_writel(host, SDHCI_ARGUMENT, cmd->cmdarg);
-
- if (!(cmd->resp_type & MMC_RSP_PRESENT))
- val = SDHCI_RESP_NONE;
- else if (cmd->resp_type & MMC_RSP_136)
- val = SDHCI_RESP_TYPE_136;
- else if (cmd->resp_type & MMC_RSP_BUSY)
- val = SDHCI_RESP_TYPE_48_BUSY;
- else
- val = SDHCI_RESP_TYPE_48;
-
- if (cmd->resp_type & MMC_RSP_CRC)
- val |= SDHCI_CMD_CRC_CHECK_EN;
- if (cmd->resp_type & MMC_RSP_OPCODE)
- val |= SDHCI_CMD_INDEX_CHECK_EN;
- if (data)
- val |= SDHCI_DATA_PRESENT;
- val |= SDHCI_CMD_INDEX(cmd->cmdidx);
-
- dove_sdhci_writew(host, SDHCI_COMMAND, val);
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
ret = dove_sdhci_wait_for_done(host, SDHCI_INT_CMD_COMPLETE);
if (ret) {
dev_err(host->mci.hw_dev, "error on command %d\n", cmd->cmdidx);
dev_err(host->mci.hw_dev, "state = %04x %04x, interrupt = %04x %04x\n",
- dove_sdhci_readw(host, SDHCI_PRESENT_STATE),
- dove_sdhci_readw(host, SDHCI_PRESENT_STATE1),
- dove_sdhci_readw(host, SDHCI_INT_NORMAL_STATUS),
- dove_sdhci_readw(host, SDHCI_INT_ERROR_STATUS));
+ sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE1),
+ sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS),
+ sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS));
goto cmd_error;
}
- /* CRC is stripped so we need to do some shifting. */
- if (cmd->resp_type & MMC_RSP_136) {
- int i;
- for (i = 0; i < 4; i++) {
- cmd->response[i] = dove_sdhci_readl(host,
- SDHCI_RESPONSE_0 + 4*(3-i)) << 8;
- if (i != 3)
- cmd->response[i] |= dove_sdhci_readb(host,
- SDHCI_RESPONSE_0 + 4*(3-i) - 1);
- }
- } else
- cmd->response[0] = dove_sdhci_readl(host, SDHCI_RESPONSE_0);
+ sdhci_read_response(&host->sdhci, cmd);
- if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
- num_bytes, DMA_TO_DEVICE);
- else
- dma_sync_single_for_cpu((unsigned long)data->dest,
+ if (data) {
+ if (data->flags & MMC_DATA_WRITE)
+ dma_sync_single_for_cpu((unsigned long)data->src,
+ num_bytes, DMA_TO_DEVICE);
+ else
+ dma_sync_single_for_cpu((unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
- if (data) {
ret = dove_sdhci_wait_for_done(host, SDHCI_INT_XFER_COMPLETE);
if (ret) {
dev_err(host->mci.hw_dev, "error while transfering data for command %d\n",
cmd->cmdidx);
dev_err(host->mci.hw_dev, "state = %04x %04x, interrupt = %04x %04x\n",
- dove_sdhci_readw(host, SDHCI_PRESENT_STATE),
- dove_sdhci_readw(host, SDHCI_PRESENT_STATE1),
- dove_sdhci_readw(host, SDHCI_INT_NORMAL_STATUS),
- dove_sdhci_readw(host, SDHCI_INT_ERROR_STATUS));
+ sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE1),
+ sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS),
+ sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS));
goto cmd_error;
}
}
cmd_error:
- dove_sdhci_writel(host, SDHCI_INT_STATUS, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
return ret;
}
@@ -252,11 +231,11 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
/* enable bus power */
val = SDHCI_BUS_VOLTAGE_330;
- dove_sdhci_writeb(host, SDHCI_POWER_CONTROL, val | SDHCI_BUS_POWER_EN);
+ sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL, val | SDHCI_BUS_POWER_EN);
udelay(400);
/* set bus width */
- val = dove_sdhci_readb(host, SDHCI_HOST_CONTROL) &
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) &
~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT);
switch (ios->bus_width) {
case MMC_BUS_WIDTH_8:
@@ -272,17 +251,17 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
else
val &= ~SDHCI_HIGHSPEED_EN;
- dove_sdhci_writeb(host, SDHCI_HOST_CONTROL, val);
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
/* set bus clock */
- dove_sdhci_writew(host, SDHCI_CLOCK_CONTROL, 0);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
val = dove_sdhci_get_clock_divider(host, ios->clock);
val = SDHCI_INTCLOCK_EN | SDHCI_FREQ_SEL(val);
- dove_sdhci_writew(host, SDHCI_CLOCK_CONTROL, val);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
/* wait for internal clock stable */
start = get_time_ns();
- while (!(dove_sdhci_readw(host, SDHCI_CLOCK_CONTROL) &
+ while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) &
SDHCI_INTCLOCK_STABLE)) {
if (is_timeout(start, 20 * MSECOND)) {
dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n");
@@ -291,7 +270,7 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
}
/* enable bus clock */
- dove_sdhci_writew(host, SDHCI_CLOCK_CONTROL, val | SDHCI_SDCLOCK_EN);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_SDCLOCK_EN);
}
static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
@@ -300,12 +279,12 @@ static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
struct dove_sdhci *host = priv_from_mci_host(mci);
/* reset sdhci controller */
- dove_sdhci_writeb(host, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
+ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
/* wait for reset completion */
start = get_time_ns();
while (1) {
- if ((dove_sdhci_readb(host, SDHCI_SOFTWARE_RESET) &
+ if ((sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) &
SDHCI_RESET_ALL) == 0)
break;
if (is_timeout(start, 100 * MSECOND)) {
@@ -314,9 +293,9 @@ static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
}
}
- dove_sdhci_writel(host, SDHCI_INT_STATUS, ~0);
- dove_sdhci_writel(host, SDHCI_INT_ENABLE, ~0);
- dove_sdhci_writel(host, SDHCI_SIGNAL_ENABLE, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0);
return 0;
}
@@ -325,8 +304,8 @@ static void dove_sdhci_set_mci_caps(struct dove_sdhci *host)
{
u16 caps[2];
- caps[0] = dove_sdhci_readw(host, SDHCI_CAPABILITIES);
- caps[1] = dove_sdhci_readw(host, SDHCI_CAPABILITIES_1);
+ caps[0] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES);
+ caps[1] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);
if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_180)
host->mci.voltages |= MMC_VDD_165_195;
@@ -368,6 +347,12 @@ static int dove_sdhci_probe(struct device_d *dev)
host->mci.init = dove_sdhci_mci_init;
host->mci.f_max = 50000000;
host->mci.f_min = host->mci.f_max / 256;
+ host->sdhci.read32 = dove_sdhci_readl;
+ host->sdhci.read16 = dove_sdhci_readw;
+ host->sdhci.read8 = dove_sdhci_readb;
+ host->sdhci.write32 = dove_sdhci_writel;
+ host->sdhci.write16 = dove_sdhci_writew;
+ host->sdhci.write8 = dove_sdhci_writeb;
dev->priv = host;
dev->detect = dove_sdhci_detect;
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
new file mode 100644
index 0000000000..d0bef9470c
--- /dev/null
+++ b/drivers/mci/imx-esdhc-common.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <io.h>
+#include <mci.h>
+#include <pbl.h>
+
+#include "sdhci.h"
+#include "imx-esdhc.h"
+
+#define PRSSTAT_DAT0 0x01000000
+
+struct fsl_esdhc_dma_transfer {
+ dma_addr_t dma;
+ unsigned int size;
+ enum dma_data_direction dir;
+};
+
+static u32 esdhc_op_read32_le(struct sdhci *sdhci, int reg)
+{
+ struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+
+ return readl(host->regs + reg);
+}
+
+static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg)
+{
+ struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+
+ return in_be32(host->regs + reg);
+}
+
+static void esdhc_op_write32_le(struct sdhci *sdhci, int reg, u32 val)
+{
+ struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+
+ writel(val, host->regs + reg);
+}
+
+static void esdhc_op_write32_be(struct sdhci *sdhci, int reg, u32 val)
+{
+ struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+
+ out_be32(host->regs + reg, val);
+}
+
+void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
+{
+ if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN) {
+ host->sdhci.read32 = esdhc_op_read32_be;
+ host->sdhci.write32 = esdhc_op_write32_be;
+ } else {
+ host->sdhci.read32 = esdhc_op_read32_le;
+ host->sdhci.write32 = esdhc_op_write32_le;
+ }
+}
+
+static bool esdhc_use_pio_mode(void)
+{
+ return IN_PBL || IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO);
+}
+static int esdhc_setup_data(struct fsl_esdhc_host *host, struct mci_data *data,
+ struct fsl_esdhc_dma_transfer *tr)
+{
+ u32 wml_value;
+ void *ptr;
+
+ if (!esdhc_use_pio_mode()) {
+ wml_value = data->blocksize/4;
+
+ if (data->flags & MMC_DATA_READ) {
+ if (wml_value > 0x10)
+ wml_value = 0x10;
+
+ esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_RD_WML_MASK, wml_value);
+ } else {
+ if (wml_value > 0x80)
+ wml_value = 0x80;
+
+ esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_WR_WML_MASK,
+ wml_value << 16);
+ }
+
+ tr->size = data->blocks * data->blocksize;
+
+ if (data->flags & MMC_DATA_WRITE) {
+ ptr = (void *)data->src;
+ tr->dir = DMA_TO_DEVICE;
+ } else {
+ ptr = data->dest;
+ tr->dir = DMA_FROM_DEVICE;
+ }
+
+ tr->dma = dma_map_single(host->dev, ptr, tr->size, tr->dir);
+ if (dma_mapping_error(host->dev, tr->dma))
+ return -EFAULT;
+
+
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, tr->dma);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT, data->blocks << 16 | data->blocksize);
+
+ return 0;
+}
+
+static int esdhc_do_data(struct fsl_esdhc_host *host, struct mci_data *data,
+ struct fsl_esdhc_dma_transfer *tr)
+{
+ u32 irqstat;
+
+ if (esdhc_use_pio_mode())
+ return sdhci_transfer_data(&host->sdhci, data);
+
+ do {
+ irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
+
+ if (irqstat & DATA_ERR)
+ return -EIO;
+
+ if (irqstat & SDHCI_INT_DATA_TIMEOUT)
+ return -ETIMEDOUT;
+ } while (!(irqstat & SDHCI_INT_XFER_COMPLETE) &&
+ (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_DATA_LINE_ACTIVE));
+
+ dma_unmap_single(host->dev, tr->dma, tr->size, tr->dir);
+
+ return 0;
+}
+
+static bool esdhc_match32(struct fsl_esdhc_host *host, unsigned int off,
+ unsigned int mask, unsigned int val)
+{
+ const unsigned int reg = sdhci_read32(&host->sdhci, off) & mask;
+
+ return reg == val;
+}
+
+#ifdef __PBL__
+/*
+ * Stubs to make timeout logic below work in PBL
+ */
+
+#define get_time_ns() 0
+/*
+ * Use time in us (approx) as a busy counter timeout value
+ */
+#define is_timeout(s, t) ((s)++ > ((t) / 1024))
+
+static void __udelay(int us)
+{
+ volatile int i;
+
+ for (i = 0; i < us * 4; i++);
+}
+
+#define udelay(n) __udelay(n)
+#undef dev_err
+#define dev_err(d, ...) pr_err(__VA_ARGS__)
+
+#endif
+
+int esdhc_poll(struct fsl_esdhc_host *host, unsigned int off,
+ unsigned int mask, unsigned int val,
+ uint64_t timeout)
+{
+ return wait_on_timeout(timeout,
+ esdhc_match32(host, off, mask, val));
+}
+
+int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ u32 xfertyp, mixctrl, command;
+ u32 irqstat;
+ struct fsl_esdhc_dma_transfer tr = { 0 };
+ int ret;
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1);
+
+ /* Wait at least 8 SD clock cycles before the next command */
+ udelay(1);
+
+ /* Set up for a data transfer if we have one */
+ if (data) {
+ ret = esdhc_setup_data(host, data, &tr);
+ if (ret)
+ return ret;
+ }
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ !esdhc_use_pio_mode(), &command, &xfertyp);
+
+ if ((host->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) &&
+ (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION))
+ command |= SDHCI_COMMAND_CMDTYP_ABORT;
+
+ /* Send the command */
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+
+ if (esdhc_is_usdhc(host)) {
+ /* write lower-half of xfertyp to mixctrl */
+ mixctrl = xfertyp;
+ /* Keep the bits 22-25 of the register as is */
+ mixctrl |= (sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL) & (0xF << 22));
+ sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND,
+ command << 16 | xfertyp);
+
+ /* Wait for the command to complete */
+ ret = esdhc_poll(host, SDHCI_INT_STATUS,
+ SDHCI_INT_CMD_COMPLETE, SDHCI_INT_CMD_COMPLETE,
+ 100 * MSECOND);
+ if (ret) {
+ dev_err(host->dev, "timeout 1\n");
+ return -ETIMEDOUT;
+ }
+
+ irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, irqstat);
+
+ if (irqstat & CMD_ERR)
+ return -EIO;
+
+ if (irqstat & SDHCI_INT_TIMEOUT)
+ return -ETIMEDOUT;
+
+ /* Workaround for ESDHC errata ENGcm03648 / ENGcm12360 */
+ if (!data && (cmd->resp_type & MMC_RSP_BUSY)) {
+ /*
+ * Poll on DATA0 line for cmd with busy signal for
+ * timout / 10 usec since DLA polling can be insecure.
+ */
+ ret = esdhc_poll(host, SDHCI_PRESENT_STATE,
+ PRSSTAT_DAT0, PRSSTAT_DAT0,
+ 2500 * MSECOND);
+ if (ret) {
+ dev_err(host->dev, "timeout PRSSTAT_DAT0\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ sdhci_read_response(&host->sdhci, cmd);
+
+ /* Wait until all of the blocks are transferred */
+ if (data) {
+ ret = esdhc_do_data(host, data, &tr);
+ if (ret)
+ return ret;
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1);
+
+ /* Wait for the bus to be idle */
+ ret = esdhc_poll(host, SDHCI_PRESENT_STATE,
+ SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA, 0,
+ SECOND);
+ if (ret) {
+ dev_err(host->dev, "timeout 2\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = esdhc_poll(host, SDHCI_PRESENT_STATE,
+ SDHCI_DATA_LINE_ACTIVE, 0,
+ 100 * MSECOND);
+ if (ret) {
+ dev_err(host->dev, "timeout 3\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index f93ddfa0d5..c0d27fb7eb 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -30,206 +30,24 @@
#define SECTOR_SIZE 512
#define SECTOR_WML (SECTOR_SIZE / sizeof(u32))
-struct esdhc {
- void __iomem *regs;
- bool is_mx6;
- bool is_be;
- bool wrap_wml;
-};
-
-static uint32_t esdhc_read32(struct esdhc *esdhc, int reg)
-{
- if (esdhc->is_be)
- return in_be32(esdhc->regs + reg);
- else
- return readl(esdhc->regs + reg);
-}
+#define esdhc_send_cmd __esdhc_send_cmd
-static void esdhc_write32(struct esdhc *esdhc, int reg, uint32_t val)
-{
- if (esdhc->is_be)
- out_be32(esdhc->regs + reg, val);
- else
- writel(val, esdhc->regs + reg);
-}
-
-static void __udelay(int us)
-{
- volatile int i;
-
- for (i = 0; i < us * 4; i++);
-}
-
-static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data)
-{
- u32 xfertyp = 0;
-
- if (data)
- xfertyp |= COMMAND_DPSEL | TRANSFER_MODE_MSBSEL |
- TRANSFER_MODE_BCEN |TRANSFER_MODE_DTDSEL;
-
- if (cmd->resp_type & MMC_RSP_CRC)
- xfertyp |= COMMAND_CCCEN;
- if (cmd->resp_type & MMC_RSP_OPCODE)
- xfertyp |= COMMAND_CICEN;
- if (cmd->resp_type & MMC_RSP_136)
- xfertyp |= COMMAND_RSPTYP_136;
- else if (cmd->resp_type & MMC_RSP_BUSY)
- xfertyp |= COMMAND_RSPTYP_48_BUSY;
- else if (cmd->resp_type & MMC_RSP_PRESENT)
- xfertyp |= COMMAND_RSPTYP_48;
-
- return COMMAND_CMD(cmd->cmdidx) | xfertyp;
-}
-
-static int esdhc_do_data(struct esdhc *esdhc, struct mci_data *data)
-{
- char *buffer;
- u32 databuf;
- u32 size;
- u32 irqstat;
- u32 present;
-
- buffer = data->dest;
-
- size = data->blocksize * data->blocks;
- irqstat = esdhc_read32(esdhc, SDHCI_INT_STATUS);
-
- while (size) {
- int i;
- int timeout = 1000000;
-
- while (1) {
- present = esdhc_read32(esdhc, SDHCI_PRESENT_STATE) & PRSSTAT_BREN;
- if (present)
- break;
- if (!--timeout) {
- pr_err("read time out\n");
- return -ETIMEDOUT;
- }
- }
-
- for (i = 0; i < SECTOR_WML; i++) {
- databuf = esdhc_read32(esdhc, SDHCI_BUFFER);
- *((u32 *)buffer) = databuf;
- buffer += 4;
- size -= 4;
- }
- }
-
- return 0;
-}
-
-static int
-esdhc_send_cmd(struct esdhc *esdhc, struct mci_cmd *cmd, struct mci_data *data)
-{
- u32 xfertyp, mixctrl;
- u32 irqstat;
- int ret;
- int timeout;
-
- esdhc_write32(esdhc, SDHCI_INT_STATUS, -1);
-
- /* Wait at least 8 SD clock cycles before the next command */
- __udelay(1);
-
- if (data) {
- unsigned long dest = (unsigned long)data->dest;
-
- if (dest > 0xffffffff)
- return -EINVAL;
-
- /* Set up for a data transfer if we have one */
- esdhc_write32(esdhc, SDHCI_DMA_ADDRESS, (u32)dest);
- esdhc_write32(esdhc, SDHCI_BLOCK_SIZE__BLOCK_COUNT, data->blocks << 16 | SECTOR_SIZE);
- }
-
- /* Figure out the transfer arguments */
- xfertyp = esdhc_xfertyp(cmd, data);
-
- /* Send the command */
- esdhc_write32(esdhc, SDHCI_ARGUMENT, cmd->cmdarg);
-
- if (esdhc->is_mx6) {
- /* write lower-half of xfertyp to mixctrl */
- mixctrl = xfertyp & 0xFFFF;
- /* Keep the bits 22-25 of the register as is */
- mixctrl |= (esdhc_read32(esdhc, IMX_SDHCI_MIXCTRL) & (0xF << 22));
- esdhc_write32(esdhc, IMX_SDHCI_MIXCTRL, mixctrl);
- }
-
- esdhc_write32(esdhc, SDHCI_TRANSFER_MODE__COMMAND, xfertyp);
-
- /* Wait for the command to complete */
- timeout = 10000;
- while (!(esdhc_read32(esdhc, SDHCI_INT_STATUS) & IRQSTAT_CC)) {
- __udelay(1);
- if (!timeout--)
- return -ETIMEDOUT;
- }
-
- irqstat = esdhc_read32(esdhc, SDHCI_INT_STATUS);
- esdhc_write32(esdhc, SDHCI_INT_STATUS, irqstat);
-
- if (irqstat & CMD_ERR)
- return -EIO;
-
- if (irqstat & IRQSTAT_CTOE)
- return -ETIMEDOUT;
-
- /* Copy the response to the response buffer */
- cmd->response[0] = esdhc_read32(esdhc, SDHCI_RESPONSE_0);
-
- /* Wait until all of the blocks are transferred */
- if (data) {
- ret = esdhc_do_data(esdhc, data);
- if (ret)
- return ret;
- }
-
- esdhc_write32(esdhc, SDHCI_INT_STATUS, -1);
-
- /* Wait for the bus to be idle */
- timeout = 10000;
- while (esdhc_read32(esdhc, SDHCI_PRESENT_STATE) &
- (PRSSTAT_CICHB | PRSSTAT_CIDHB | PRSSTAT_DLA)) {
- __udelay(1);
- if (!timeout--)
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int esdhc_read_blocks(struct esdhc *esdhc, void *dst, size_t len)
+static int esdhc_read_blocks(struct fsl_esdhc_host *host, void *dst, size_t len)
{
struct mci_cmd cmd;
struct mci_data data;
- u32 val, wml;
+ u32 val;
int ret;
- esdhc_write32(esdhc, SDHCI_INT_ENABLE,
- IRQSTATEN_CC | IRQSTATEN_TC | IRQSTATEN_CINT | IRQSTATEN_CTOE |
- IRQSTATEN_CCE | IRQSTATEN_CEBE | IRQSTATEN_CIE |
- IRQSTATEN_DTOE | IRQSTATEN_DCE | IRQSTATEN_DEBE |
- IRQSTATEN_DINT);
-
- wml = FIELD_PREP(WML_WR_BRST_LEN, 16) |
- FIELD_PREP(WML_WR_WML_MASK, SECTOR_WML) |
- FIELD_PREP(WML_RD_BRST_LEN, 16) |
- FIELD_PREP(WML_RD_WML_MASK, SECTOR_WML);
- /*
- * Some SoCs intrpret 0 as MAX value so for those cases the
- * above value translates to zero
- */
- if (esdhc->wrap_wml)
- wml = 0;
-
- esdhc_write32(esdhc, IMX_SDHCI_WML, wml);
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
+ SDHCI_INT_CMD_COMPLETE | SDHCI_INT_XFER_COMPLETE |
+ SDHCI_INT_CARD_INT | SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
+ SDHCI_INT_END_BIT | SDHCI_INT_INDEX | SDHCI_INT_DATA_TIMEOUT |
+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT | SDHCI_INT_DMA);
- val = esdhc_read32(esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
+ val = sdhci_read32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
val |= SYSCTL_HCKEN | SYSCTL_IPGEN;
- esdhc_write32(esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+ sdhci_write32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
cmd.cmdarg = 0;
@@ -240,7 +58,7 @@ static int esdhc_read_blocks(struct esdhc *esdhc, void *dst, size_t len)
data.blocksize = SECTOR_SIZE;
data.flags = MMC_DATA_READ;
- ret = esdhc_send_cmd(esdhc, &cmd, &data);
+ ret = esdhc_send_cmd(host, &cmd, &data);
if (ret) {
pr_debug("send command failed with %d\n", ret);
return ret;
@@ -250,13 +68,13 @@ static int esdhc_read_blocks(struct esdhc *esdhc, void *dst, size_t len)
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
- esdhc_send_cmd(esdhc, &cmd, NULL);
+ esdhc_send_cmd(host, &cmd, NULL);
return 0;
}
#ifdef CONFIG_ARCH_IMX
-static int esdhc_search_header(struct esdhc *esdhc,
+static int esdhc_search_header(struct fsl_esdhc_host *host,
struct imx_flash_header_v2 **header_pointer,
void *buffer, u32 *offset)
{
@@ -266,7 +84,7 @@ static int esdhc_search_header(struct esdhc *esdhc,
struct imx_flash_header_v2 *hdr;
for (i = 0; i < header_count; i++) {
- ret = esdhc_read_blocks(esdhc, buf,
+ ret = esdhc_read_blocks(host, buf,
*offset + SZ_1K + SECTOR_SIZE);
if (ret)
return ret;
@@ -303,7 +121,7 @@ static int esdhc_search_header(struct esdhc *esdhc,
}
static int
-esdhc_start_image(struct esdhc *esdhc, ptrdiff_t address, ptrdiff_t entry,
+esdhc_start_image(struct fsl_esdhc_host *host, ptrdiff_t address, ptrdiff_t entry,
u32 offset)
{
@@ -316,7 +134,7 @@ esdhc_start_image(struct esdhc *esdhc, ptrdiff_t address, ptrdiff_t entry,
len = imx_image_size();
len = ALIGN(len, SECTOR_SIZE);
- ret = esdhc_search_header(esdhc, &hdr, buf, &offset);
+ ret = esdhc_search_header(host, &hdr, buf, &offset);
if (ret)
return ret;
@@ -351,7 +169,7 @@ esdhc_start_image(struct esdhc *esdhc, ptrdiff_t address, ptrdiff_t entry,
buf = (void *)(entry - ofs);
}
- ret = esdhc_read_blocks(esdhc, buf, offset + len);
+ ret = esdhc_read_blocks(host, buf, offset + len);
if (ret) {
pr_err("Loading image failed with %d\n", ret);
return ret;
@@ -366,6 +184,40 @@ esdhc_start_image(struct esdhc *esdhc, ptrdiff_t address, ptrdiff_t entry,
bb();
}
+static void imx_esdhc_init(struct fsl_esdhc_host *host,
+ struct esdhc_soc_data *data)
+{
+ data->flags = ESDHC_FLAG_USDHC;
+ host->socdata = data;
+ esdhc_populate_sdhci(host);
+
+ sdhci_write32(&host->sdhci, IMX_SDHCI_WML,
+ FIELD_PREP(WML_WR_BRST_LEN, 16) |
+ FIELD_PREP(WML_WR_WML_MASK, SECTOR_WML) |
+ FIELD_PREP(WML_RD_BRST_LEN, 16) |
+ FIELD_PREP(WML_RD_WML_MASK, SECTOR_WML));
+}
+
+static int imx8_esdhc_init(struct fsl_esdhc_host *host,
+ struct esdhc_soc_data *data,
+ int instance)
+{
+ switch (instance) {
+ case 0:
+ host->regs = IOMEM(MX8MQ_USDHC1_BASE_ADDR);
+ break;
+ case 1:
+ host->regs = IOMEM(MX8MQ_USDHC2_BASE_ADDR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ imx_esdhc_init(host, data);
+
+ return 0;
+}
+
/**
* imx6_esdhc_start_image - Load and start an image from USDHC controller
* @instance: The USDHC controller instance (0..4)
@@ -380,30 +232,29 @@ esdhc_start_image(struct esdhc *esdhc, ptrdiff_t address, ptrdiff_t entry,
*/
int imx6_esdhc_start_image(int instance)
{
- struct esdhc esdhc;
+ struct esdhc_soc_data data;
+ struct fsl_esdhc_host host;
switch (instance) {
case 0:
- esdhc.regs = IOMEM(MX6_USDHC1_BASE_ADDR);
+ host.regs = IOMEM(MX6_USDHC1_BASE_ADDR);
break;
case 1:
- esdhc.regs = IOMEM(MX6_USDHC2_BASE_ADDR);
+ host.regs = IOMEM(MX6_USDHC2_BASE_ADDR);
break;
case 2:
- esdhc.regs = IOMEM(MX6_USDHC3_BASE_ADDR);
+ host.regs = IOMEM(MX6_USDHC3_BASE_ADDR);
break;
case 3:
- esdhc.regs = IOMEM(MX6_USDHC4_BASE_ADDR);
+ host.regs = IOMEM(MX6_USDHC4_BASE_ADDR);
break;
default:
return -EINVAL;
}
- esdhc.is_be = 0;
- esdhc.is_mx6 = 1;
- esdhc.wrap_wml = false;
+ imx_esdhc_init(&host, &data);
- return esdhc_start_image(&esdhc, 0x10000000, 0x10000000, 0);
+ return esdhc_start_image(&host, 0x10000000, 0x10000000, 0);
}
/**
@@ -420,24 +271,15 @@ int imx6_esdhc_start_image(int instance)
*/
int imx8_esdhc_start_image(int instance)
{
- struct esdhc esdhc;
-
- switch (instance) {
- case 0:
- esdhc.regs = IOMEM(MX8MQ_USDHC1_BASE_ADDR);
- break;
- case 1:
- esdhc.regs = IOMEM(MX8MQ_USDHC2_BASE_ADDR);
- break;
- default:
- return -EINVAL;
- }
+ struct esdhc_soc_data data;
+ struct fsl_esdhc_host host;
+ int ret;
- esdhc.is_be = 0;
- esdhc.is_mx6 = 1;
- esdhc.wrap_wml = false;
+ ret = imx8_esdhc_init(&host, &data, instance);
+ if (ret)
+ return ret;
- return esdhc_start_image(&esdhc, MX8MQ_DDR_CSD1_BASE_ADDR,
+ return esdhc_start_image(&host, MX8MQ_DDR_CSD1_BASE_ADDR,
MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K);
}
@@ -445,24 +287,14 @@ int imx8_esdhc_load_piggy(int instance)
{
void *buf, *piggy;
struct imx_flash_header_v2 *hdr = NULL;
- struct esdhc esdhc;
+ struct esdhc_soc_data data;
+ struct fsl_esdhc_host host;
int ret, len;
int offset = SZ_32K;
- switch (instance) {
- case 0:
- esdhc.regs = IOMEM(MX8MQ_USDHC1_BASE_ADDR);
- break;
- case 1:
- esdhc.regs = IOMEM(MX8MQ_USDHC2_BASE_ADDR);
- break;
- default:
- return -EINVAL;
- }
-
- esdhc.is_be = 0;
- esdhc.is_mx6 = 1;
- esdhc.wrap_wml = false;
+ ret = imx8_esdhc_init(&host, &data, instance);
+ if (ret)
+ return ret;
/*
* We expect to be running at MX8MQ_ATF_BL33_BASE_ADDR where the atf
@@ -471,14 +303,14 @@ int imx8_esdhc_load_piggy(int instance)
*/
buf = (void *)MX8MQ_ATF_BL33_BASE_ADDR + SZ_32M;
- ret = esdhc_search_header(&esdhc, &hdr, buf, &offset);
+ ret = esdhc_search_header(&host, &hdr, buf, &offset);
if (ret)
return ret;
len = offset + hdr->boot_data.size + piggydata_size();
len = ALIGN(len, SECTOR_SIZE);
- ret = esdhc_read_blocks(&esdhc, buf, len);
+ ret = esdhc_read_blocks(&host, buf, len);
/*
* Calculate location of the piggydata at the offset loaded into RAM
@@ -516,28 +348,33 @@ int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long
{
int ret;
uint32_t val;
- struct esdhc esdhc = {
+ struct esdhc_soc_data data = {
+ .flags = ESDHC_FLAG_BIGENDIAN,
+ };
+ struct fsl_esdhc_host host = {
.regs = IOMEM(0x01560000),
- .is_be = true,
- .wrap_wml = true,
+ .socdata = &data,
};
unsigned long sdram = 0x80000000;
void (*barebox)(unsigned long, unsigned long, unsigned long) =
(void *)(sdram + LS1046A_SD_IMAGE_OFFSET);
+ esdhc_populate_sdhci(&host);
+ sdhci_write32(&host.sdhci, IMX_SDHCI_WML, 0);
+
/*
* The ROM leaves us here with a clock frequency of around 400kHz. Speed
* this up a bit. FIXME: The resulting frequency has not yet been verified
* to work on all cards.
*/
- val = esdhc_read32(&esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
+ val = sdhci_read32(&host.sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
val &= ~0x0000fff0;
val |= (8 << 8) | (3 << 4);
- esdhc_write32(&esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+ sdhci_write32(&host.sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
- esdhc_write32(&esdhc, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
+ sdhci_write32(&host.sdhci, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
- ret = esdhc_read_blocks(&esdhc, (void *)sdram,
+ ret = esdhc_read_blocks(&host, (void *)sdram,
ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512));
if (ret) {
pr_err("%s: reading blocks failed with: %d\n", __func__, ret);
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index db3450a26d..4816608a23 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -39,267 +39,12 @@
#include "sdhci.h"
#include "imx-esdhc.h"
-/*
- * The CMDTYPE of the CMD register (offset 0xE) should be set to
- * "11" when the STOP CMD12 is issued on imx53 to abort one
- * open ended multi-blk IO. Otherwise the TC INT wouldn't
- * be generated.
- * In exact block transfer, the controller doesn't complete the
- * operations automatically as required at the end of the
- * transfer and remains on hold if the abort command is not sent.
- * As a result, the TC flag is not asserted and SW received timeout
- * exeception. Bit1 of Vendor Spec registor is used to fix it.
- */
-#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1)
-/*
- * The flag enables the workaround for ESDHC errata ENGcm07207 which
- * affects i.MX25 and i.MX35.
- */
-#define ESDHC_FLAG_ENGCM07207 BIT(2)
-/*
- * The flag tells that the ESDHC controller is an USDHC block that is
- * integrated on the i.MX6 series.
- */
-#define ESDHC_FLAG_USDHC BIT(3)
-/* The IP supports manual tuning process */
-#define ESDHC_FLAG_MAN_TUNING BIT(4)
-/* The IP supports standard tuning process */
-#define ESDHC_FLAG_STD_TUNING BIT(5)
-/* The IP has SDHCI_CAPABILITIES_1 register */
-#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
-/*
- * The IP has errata ERR004536
- * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
- * when reading data from the card
- */
-#define ESDHC_FLAG_ERR004536 BIT(7)
-/* The IP supports HS200 mode */
-#define ESDHC_FLAG_HS200 BIT(8)
-/* The IP supports HS400 mode */
-#define ESDHC_FLAG_HS400 BIT(9)
-
-/* Need to access registers in bigendian mode */
-#define ESDHC_FLAG_BIGENDIAN BIT(10)
-/* Enable cache snooping */
-#define ESDHC_FLAG_CACHE_SNOOPING BIT(11)
-
-struct esdhc_soc_data {
- u32 flags;
- const char *clkidx;
-};
+#define PRSSTAT_SDSTB 0x00000008
-struct fsl_esdhc_host {
- struct mci_host mci;
- void __iomem *regs;
- struct device_d *dev;
- struct clk *clk;
- const struct esdhc_soc_data *socdata;
-};
#define to_fsl_esdhc(mci) container_of(mci, struct fsl_esdhc_host, mci)
-#define SDHCI_CMD_ABORTCMD (0xC0 << 16)
-
-static inline int esdhc_is_usdhc(struct fsl_esdhc_host *data)
-{
- return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
-}
-
-static inline u32 esdhc_read32(struct fsl_esdhc_host *host, unsigned int reg)
-{
- if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN)
- return in_be32(host->regs + reg);
- else
- return readl(host->regs + reg);
-}
-
-static inline void esdhc_write32(struct fsl_esdhc_host *host, unsigned int reg,
- u32 val)
-{
- if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN)
- out_be32(host->regs + reg, val);
- else
- writel(val, host->regs + reg);
-}
-
-static inline void esdhc_clrsetbits32(struct fsl_esdhc_host *host, unsigned int reg,
- u32 clear, u32 set)
-{
- u32 val;
-
- val = esdhc_read32(host, reg);
- val &= ~clear;
- val |= set;
- esdhc_write32(host, reg, val);
-}
-
-static inline void esdhc_clrbits32(struct fsl_esdhc_host *host, unsigned int reg,
- u32 clear)
-{
- esdhc_clrsetbits32(host, reg, clear, 0);
-}
-
-static inline void esdhc_setbits32(struct fsl_esdhc_host *host, unsigned int reg,
- u32 set)
-{
- esdhc_clrsetbits32(host, reg, 0, set);
-}
-
-/* Return the XFERTYP flags for a given command and data packet */
-static u32 esdhc_xfertyp(struct fsl_esdhc_host *host,
- struct mci_cmd *cmd, struct mci_data *data)
-{
- u32 xfertyp = 0;
-
- if (data) {
- xfertyp |= COMMAND_DPSEL;
-
- if (!IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO))
- xfertyp |= TRANSFER_MODE_DMAEN;
-
- if (data->blocks > 1) {
- xfertyp |= TRANSFER_MODE_MSBSEL;
- xfertyp |= TRANSFER_MODE_BCEN;
- }
-
- if (data->flags & MMC_DATA_READ)
- xfertyp |= TRANSFER_MODE_DTDSEL;
- }
-
- if (cmd->resp_type & MMC_RSP_CRC)
- xfertyp |= COMMAND_CCCEN;
- if (cmd->resp_type & MMC_RSP_OPCODE)
- xfertyp |= COMMAND_CICEN;
- if (cmd->resp_type & MMC_RSP_136)
- xfertyp |= COMMAND_RSPTYP_136;
- else if (cmd->resp_type & MMC_RSP_BUSY)
- xfertyp |= COMMAND_RSPTYP_48_BUSY;
- else if (cmd->resp_type & MMC_RSP_PRESENT)
- xfertyp |= COMMAND_RSPTYP_48;
- if ((host->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) &&
- (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION))
- xfertyp |= SDHCI_CMD_ABORTCMD;
-
- return COMMAND_CMD(cmd->cmdidx) | xfertyp;
-}
-
-/*
- * PIO Read/Write Mode reduce the performace as DMA is not used in this mode.
- */
-static int
-esdhc_pio_read_write(struct mci_host *mci, struct mci_data *data)
-{
- struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
- u32 blocks;
- char *buffer;
- u32 databuf;
- u32 size;
- u32 irqstat;
- u32 timeout;
-
- if (data->flags & MMC_DATA_READ) {
- blocks = data->blocks;
- buffer = data->dest;
- while (blocks) {
- timeout = PIO_TIMEOUT;
- size = data->blocksize;
- irqstat = esdhc_read32(host, SDHCI_INT_STATUS);
- while (!(esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_BREN)
- && --timeout);
- if (timeout <= 0) {
- dev_err(host->dev, "Data Read Failed\n");
- return -ETIMEDOUT;
- }
- while (size && (!(irqstat & IRQSTAT_TC))) {
- udelay(100); /* Wait before last byte transfer complete */
- irqstat = esdhc_read32(host, SDHCI_INT_STATUS);
- databuf = esdhc_read32(host, SDHCI_BUFFER);
- *((u32 *)buffer) = databuf;
- buffer += 4;
- size -= 4;
- }
- blocks--;
- }
- } else {
- blocks = data->blocks;
- buffer = (char *)data->src;
- while (blocks) {
- timeout = PIO_TIMEOUT;
- size = data->blocksize;
- irqstat = esdhc_read32(host, SDHCI_INT_STATUS);
- while (!(esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_BWEN)
- && --timeout);
- if (timeout <= 0) {
- dev_err(host->dev, "Data Write Failed\n");
- return -ETIMEDOUT;
- }
- while (size && (!(irqstat & IRQSTAT_TC))) {
- udelay(100); /* Wait before last byte transfer complete */
- databuf = *((u32 *)buffer);
- buffer += 4;
- size -= 4;
- irqstat = esdhc_read32(host, SDHCI_INT_STATUS);
- esdhc_write32(host, SDHCI_BUFFER, databuf);
- }
- blocks--;
- }
- }
-
- return 0;
-}
-
-static int esdhc_setup_data(struct mci_host *mci, struct mci_data *data,
- dma_addr_t dma)
-{
- struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
- u32 wml_value;
-
- if (!IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO)) {
- wml_value = data->blocksize/4;
-
- if (data->flags & MMC_DATA_READ) {
- if (wml_value > 0x10)
- wml_value = 0x10;
-
- esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_RD_WML_MASK, wml_value);
- } else {
- if (wml_value > 0x80)
- wml_value = 0x80;
-
- esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_WR_WML_MASK,
- wml_value << 16);
- }
- esdhc_write32(host, SDHCI_DMA_ADDRESS, dma);
- }
-
- esdhc_write32(host, SDHCI_BLOCK_SIZE__BLOCK_COUNT, data->blocks << 16 | data->blocksize);
-
- return 0;
-}
-
-static int esdhc_do_data(struct mci_host *mci, struct mci_data *data)
-{
- struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
- u32 irqstat;
-
- if (IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO))
- return esdhc_pio_read_write(mci, data);
-
- do {
- irqstat = esdhc_read32(host, SDHCI_INT_STATUS);
-
- if (irqstat & DATA_ERR)
- return -EIO;
-
- if (irqstat & IRQSTAT_DTOE)
- return -ETIMEDOUT;
- } while (!(irqstat & IRQSTAT_TC) &&
- (esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_DLA));
-
- return 0;
-}
-
/*
* Sends a command out on the bus. Takes the mci pointer,
* a command pointer, and an optional data pointer.
@@ -307,137 +52,9 @@ static int esdhc_do_data(struct mci_host *mci, struct mci_data *data)
static int
esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
{
- u32 xfertyp, mixctrl;
- u32 irqstat;
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
- unsigned int num_bytes = 0;
- int ret;
- void *ptr;
- enum dma_data_direction dir = 0;
- dma_addr_t dma = 0;
-
- esdhc_write32(host, SDHCI_INT_STATUS, -1);
-
- /* Wait at least 8 SD clock cycles before the next command */
- udelay(1);
-
- /* Set up for a data transfer if we have one */
- if (data) {
- int err;
-
- if (!IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO)) {
- num_bytes = data->blocks * data->blocksize;
-
- if (data->flags & MMC_DATA_WRITE) {
- ptr = (void *)data->src;
- dir = DMA_TO_DEVICE;
- } else {
- ptr = data->dest;
- dir = DMA_FROM_DEVICE;
- }
-
- dma = dma_map_single(host->dev, ptr, num_bytes, dir);
- if (dma_mapping_error(host->dev, dma))
- return -EFAULT;
- }
-
- err = esdhc_setup_data(mci, data, dma);
- if(err)
- return err;
- }
-
- /* Figure out the transfer arguments */
- xfertyp = esdhc_xfertyp(host, cmd, data);
-
- /* Send the command */
- esdhc_write32(host, SDHCI_ARGUMENT, cmd->cmdarg);
-
- if (esdhc_is_usdhc(host)) {
- /* write lower-half of xfertyp to mixctrl */
- mixctrl = xfertyp & 0xFFFF;
- /* Keep the bits 22-25 of the register as is */
- mixctrl |= (esdhc_read32(host, IMX_SDHCI_MIXCTRL) & (0xF << 22));
- esdhc_write32(host, IMX_SDHCI_MIXCTRL, mixctrl);
- }
-
- esdhc_write32(host, SDHCI_TRANSFER_MODE__COMMAND, xfertyp);
-
- /* Wait for the command to complete */
- ret = wait_on_timeout(100 * MSECOND,
- esdhc_read32(host, SDHCI_INT_STATUS) & IRQSTAT_CC);
- if (ret) {
- dev_dbg(host->dev, "timeout 1\n");
- return -ETIMEDOUT;
- }
-
- irqstat = esdhc_read32(host, SDHCI_INT_STATUS);
- esdhc_write32(host, SDHCI_INT_STATUS, irqstat);
-
- if (irqstat & CMD_ERR)
- return -EIO;
-
- if (irqstat & IRQSTAT_CTOE)
- return -ETIMEDOUT;
-
- /* Workaround for ESDHC errata ENGcm03648 / ENGcm12360 */
- if (!data && (cmd->resp_type & MMC_RSP_BUSY)) {
- /*
- * Poll on DATA0 line for cmd with busy signal for
- * timout / 10 usec since DLA polling can be insecure.
- */
- ret = wait_on_timeout(2500 * MSECOND,
- (esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_DAT0));
-
- if (ret) {
- dev_err(host->dev, "timeout PRSSTAT_DAT0\n");
- return -ETIMEDOUT;
- }
- }
-
- /* Copy the response to the response buffer */
- if (cmd->resp_type & MMC_RSP_136) {
- u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
-
- cmdrsp3 = esdhc_read32(host, SDHCI_RESPONSE_3);
- cmdrsp2 = esdhc_read32(host, SDHCI_RESPONSE_2);
- cmdrsp1 = esdhc_read32(host, SDHCI_RESPONSE_1);
- cmdrsp0 = esdhc_read32(host, SDHCI_RESPONSE_0);
- cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
- cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
- cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
- cmd->response[3] = (cmdrsp0 << 8);
- } else
- cmd->response[0] = esdhc_read32(host, SDHCI_RESPONSE_0);
-
- /* Wait until all of the blocks are transferred */
- if (data) {
- ret = esdhc_do_data(mci, data);
- if (ret)
- return ret;
- if (!IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO))
- dma_unmap_single(host->dev, dma, num_bytes, dir);
- }
-
- esdhc_write32(host, SDHCI_INT_STATUS, -1);
-
- /* Wait for the bus to be idle */
- ret = wait_on_timeout(SECOND,
- !(esdhc_read32(host, SDHCI_PRESENT_STATE) &
- (PRSSTAT_CICHB | PRSSTAT_CIDHB)));
- if (ret) {
- dev_err(host->dev, "timeout 2\n");
- return -ETIMEDOUT;
- }
-
- ret = wait_on_timeout(100 * MSECOND,
- !(esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_DLA));
- if (ret) {
- dev_err(host->dev, "timeout 3\n");
- return -ETIMEDOUT;
- }
-
- return 0;
+ return __esdhc_send_cmd(host, cmd, data);
}
static void set_sysctl(struct mci_host *mci, u32 clock)
@@ -486,16 +103,18 @@ static void set_sysctl(struct mci_host *mci, u32 clock)
esdhc_clrsetbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
SYSCTL_CLOCK_MASK, clk);
- wait_on_timeout(10 * MSECOND,
- esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_SDSTB);
+ esdhc_poll(host, SDHCI_PRESENT_STATE,
+ PRSSTAT_SDSTB, PRSSTAT_SDSTB,
+ 10 * MSECOND);
clk = SYSCTL_PEREN | SYSCTL_CKEN | SYSCTL_INITA;
esdhc_setbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
clk);
- wait_on_timeout(1 * MSECOND,
- !(esdhc_read32(host, SDHCI_CLOCK_CONTROL) & SYSCTL_INITA));
+ esdhc_poll(host, SDHCI_CLOCK_CONTROL,
+ SYSCTL_INITA, SYSCTL_INITA,
+ 10 * MSECOND);
}
static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
@@ -540,7 +159,7 @@ static int esdhc_card_present(struct mci_host *mci)
case ESDHC_CD_PERMANENT:
return 1;
case ESDHC_CD_CONTROLLER:
- return !(esdhc_read32(host, SDHCI_PRESENT_STATE) & PRSSTAT_WPSPL);
+ return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
case ESDHC_CD_GPIO:
ret = gpio_direction_input(pdata->cd_gpio);
if (ret)
@@ -553,34 +172,29 @@ static int esdhc_card_present(struct mci_host *mci)
static int esdhc_reset(struct fsl_esdhc_host *host)
{
- uint64_t start;
int val;
/* reset the controller */
- esdhc_write32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ sdhci_write32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
SYSCTL_RSTA);
/* extra register reset for i.MX6 Solo/DualLite */
if (esdhc_is_usdhc(host)) {
/* reset bit FBCLK_SEL */
- val = esdhc_read32(host, IMX_SDHCI_MIXCTRL);
+ val = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
val &= ~IMX_SDHCI_MIX_CTRL_FBCLK_SEL;
- esdhc_write32(host, IMX_SDHCI_MIXCTRL, val);
+ sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, val);
/* reset delay line settings in IMX_SDHCI_DLL_CTRL */
- esdhc_write32(host, IMX_SDHCI_DLL_CTRL, 0x0);
+ sdhci_write32(&host->sdhci, IMX_SDHCI_DLL_CTRL, 0x0);
}
- start = get_time_ns();
/* hardware clears the bit when it is done */
- while (1) {
- if (!(esdhc_read32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET)
- & SYSCTL_RSTA))
- break;
- if (is_timeout(start, 100 * MSECOND)) {
- dev_err(host->dev, "Reset never completed.\n");
- return -EIO;
- }
+ if (esdhc_poll(host,
+ SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ SYSCTL_RSTA, 0, 100 * MSECOND)) {
+ dev_err(host->dev, "Reset never completed.\n");
+ return -EIO;
}
return 0;
@@ -595,11 +209,11 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
if (ret)
return ret;
- esdhc_write32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ sdhci_write32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
SYSCTL_HCKEN | SYSCTL_IPGEN);
/* RSTA doesn't reset MMC_BOOT register, so manually reset it */
- esdhc_write32(host, SDHCI_MMC_BOOT, 0);
+ sdhci_write32(&host->sdhci, SDHCI_MMC_BOOT, 0);
/* Enable cache snooping */
if (host->socdata->flags & ESDHC_FLAG_CACHE_SNOOPING)
@@ -608,13 +222,14 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
/* Set the initial clock speed */
set_sysctl(mci, 400000);
- esdhc_write32(host, SDHCI_INT_ENABLE, IRQSTATEN_CC | IRQSTATEN_TC |
- IRQSTATEN_CINT | IRQSTATEN_CTOE | IRQSTATEN_CCE |
- IRQSTATEN_CEBE | IRQSTATEN_CIE | IRQSTATEN_DTOE |
- IRQSTATEN_DCE | IRQSTATEN_DEBE | IRQSTATEN_DINT);
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, SDHCI_INT_CMD_COMPLETE |
+ SDHCI_INT_XFER_COMPLETE | SDHCI_INT_CARD_INT |
+ SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+ SDHCI_INT_INDEX | SDHCI_INT_DATA_TIMEOUT |
+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT | SDHCI_INT_DMA);
/* Put the PROCTL reg back to the default */
- esdhc_write32(host, SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
+ sdhci_write32(&host->sdhci, SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
PROCTL_INIT);
/* Set timout to the maximum value */
@@ -673,7 +288,9 @@ static int fsl_esdhc_probe(struct device_d *dev)
}
host->regs = IOMEM(iores->start);
- caps = esdhc_read32(host, SDHCI_CAPABILITIES);
+ esdhc_populate_sdhci(host);
+
+ caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES);
if (caps & ESDHC_HOSTCAPBLT_VS18)
mci->voltages |= MMC_VDD_165_195;
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index 2d5471969d..4bf890edf9 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -22,6 +22,7 @@
#ifndef __FSL_ESDHC_H__
#define __FSL_ESDHC_H__
+#include <dma.h>
#include <errno.h>
#include <asm/byteorder.h>
#include <linux/bitfield.h>
@@ -35,8 +36,8 @@
#define SYSCTL_HCKEN 0x00000002
#define SYSCTL_IPGEN 0x00000001
-#define CMD_ERR (IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE)
-#define DATA_ERR (IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE)
+#define CMD_ERR (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC)
+#define DATA_ERR (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT)
#define PROCTL_INIT 0x00000020
#define PROCTL_DTW_4 0x00000002
@@ -69,9 +70,106 @@
#define ESDHC_DMA_SYSCTL 0x40c /* Layerscape specific */
#define ESDHC_SYSCTL_DMA_SNOOP BIT(6)
-struct fsl_esdhc_cfg {
- u32 esdhc_base;
- u32 no_snoop;
+
+/*
+ * The CMDTYPE of the CMD register (offset 0xE) should be set to
+ * "11" when the STOP CMD12 is issued on imx53 to abort one
+ * open ended multi-blk IO. Otherwise the TC INT wouldn't
+ * be generated.
+ * In exact block transfer, the controller doesn't complete the
+ * operations automatically as required at the end of the
+ * transfer and remains on hold if the abort command is not sent.
+ * As a result, the TC flag is not asserted and SW received timeout
+ * exeception. Bit1 of Vendor Spec registor is used to fix it.
+ */
+#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1)
+/*
+ * The flag enables the workaround for ESDHC errata ENGcm07207 which
+ * affects i.MX25 and i.MX35.
+ */
+#define ESDHC_FLAG_ENGCM07207 BIT(2)
+/*
+ * The flag tells that the ESDHC controller is an USDHC block that is
+ * integrated on the i.MX6 series.
+ */
+#define ESDHC_FLAG_USDHC BIT(3)
+/* The IP supports manual tuning process */
+#define ESDHC_FLAG_MAN_TUNING BIT(4)
+/* The IP supports standard tuning process */
+#define ESDHC_FLAG_STD_TUNING BIT(5)
+/* The IP has SDHCI_CAPABILITIES_1 register */
+#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
+
+/*
+ * The IP has errata ERR004536
+ * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
+ * when reading data from the card
+ */
+#define ESDHC_FLAG_ERR004536 BIT(7)
+/* The IP supports HS200 mode */
+#define ESDHC_FLAG_HS200 BIT(8)
+/* The IP supports HS400 mode */
+#define ESDHC_FLAG_HS400 BIT(9)
+/* Need to access registers in bigendian mode */
+#define ESDHC_FLAG_BIGENDIAN BIT(10)
+/* Enable cache snooping */
+#define ESDHC_FLAG_CACHE_SNOOPING BIT(11)
+
+struct esdhc_soc_data {
+ u32 flags;
+ const char *clkidx;
+};
+
+struct fsl_esdhc_host {
+ struct mci_host mci;
+ struct clk *clk;
+ struct device_d *dev;
+ void __iomem *regs;
+ const struct esdhc_soc_data *socdata;
+ struct sdhci sdhci;
};
+static inline int esdhc_is_usdhc(struct fsl_esdhc_host *data)
+{
+ return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
+}
+
+static inline struct fsl_esdhc_host *sdhci_to_esdhc(struct sdhci *sdhci)
+{
+ return container_of(sdhci, struct fsl_esdhc_host, sdhci);
+}
+
+static inline void
+esdhc_clrsetbits32(struct fsl_esdhc_host *host, unsigned int reg,
+ u32 clear, u32 set)
+{
+ u32 val;
+
+ val = sdhci_read32(&host->sdhci, reg);
+ val &= ~clear;
+ val |= set;
+ sdhci_write32(&host->sdhci, reg, val);
+}
+
+static inline void
+esdhc_clrbits32(struct fsl_esdhc_host *host, unsigned int reg,
+ u32 clear)
+{
+ esdhc_clrsetbits32(host, reg, clear, 0);
+}
+
+static inline void
+esdhc_setbits32(struct fsl_esdhc_host *host, unsigned int reg,
+ u32 set)
+{
+ esdhc_clrsetbits32(host, reg, 0, set);
+}
+
+void esdhc_populate_sdhci(struct fsl_esdhc_host *host);
+int esdhc_poll(struct fsl_esdhc_host *host, unsigned int off,
+ unsigned int mask, unsigned int val,
+ uint64_t timeout);
+int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
+ struct mci_data *data);
+
#endif /* __FSL_ESDHC_H__ */
diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c
index 2ed1251672..b18d681870 100644
--- a/drivers/mci/mci-bcm2835.c
+++ b/drivers/mci/mci-bcm2835.c
@@ -51,10 +51,13 @@ struct bcm2835_mci_host {
u32 max_clock;
u32 version;
uint64_t last_write;
+ struct sdhci sdhci;
};
-static void bcm2835_mci_write(struct bcm2835_mci_host *host, u32 reg, u32 val)
+static void bcm2835_sdhci_write32(struct sdhci *sdhci, int reg, u32 val)
{
+ struct bcm2835_mci_host *host = container_of(sdhci, struct bcm2835_mci_host, sdhci);
+
/*
* The Arasan has a bugette whereby it may lose the content of
* successive writes to registers that are within two SD-card clock
@@ -64,95 +67,21 @@ static void bcm2835_mci_write(struct bcm2835_mci_host *host, u32 reg, u32 val)
* too)
*/
- if (host->last_write != 0)
- while ((get_time_ns() - host->last_write) < twoticks_delay)
- ;
- host->last_write = get_time_ns();
- writel(val, host->regs + reg);
-}
-
-static u32 bcm2835_mci_read(struct bcm2835_mci_host *host, u32 reg)
-{
- return readl(host->regs + reg);
-}
+ if (reg != SDHCI_BUFFER) {
+ if (host->last_write != 0)
+ while ((get_time_ns() - host->last_write) < twoticks_delay)
+ ;
+ host->last_write = get_time_ns();
+ }
-/* Create special write data function since the data
- * register is not affected by the twoticks_delay bug
- * and we can thus get better speed here
- */
-static void bcm2835_mci_write_data(struct bcm2835_mci_host *host, u32 *p)
-{
- writel(*p, host->regs + SDHCI_BUFFER);
+ writel(val, host->regs + reg);
}
-/* Make a read data functions as well just to keep structure */
-static void bcm2835_mci_read_data(struct bcm2835_mci_host *host, u32 *p)
+static u32 bcm2835_sdhci_read32(struct sdhci *sdhci, int reg)
{
- *p = readl(host->regs + SDHCI_BUFFER);
-}
+ struct bcm2835_mci_host *host = container_of(sdhci, struct bcm2835_mci_host, sdhci);
-static int bcm2835_mci_transfer_data(struct bcm2835_mci_host *host,
- struct mci_cmd *cmd, struct mci_data *data) {
- u32 *p;
- u32 data_size, status, intr_status = 0;
- u32 data_ready_intr_mask;
- u32 data_ready_status_mask;
- int i = 0;
- void (*read_write_func)(struct bcm2835_mci_host*, u32*);
-
- data_size = data->blocksize * data->blocks;
-
- if (data->flags & MMC_DATA_READ) {
- p = (u32 *) data->dest;
- data_ready_intr_mask = IRQSTAT_BRR;
- data_ready_status_mask = PRSSTAT_BREN;
- read_write_func = &bcm2835_mci_read_data;
- } else {
- p = (u32 *) data->src;
- data_ready_intr_mask = IRQSTAT_BWR;
- data_ready_status_mask = PRSSTAT_BWEN;
- read_write_func = &bcm2835_mci_write_data;
- }
- do {
- intr_status = bcm2835_mci_read(host, SDHCI_INT_STATUS);
- if (intr_status & IRQSTAT_CIE) {
- dev_err(host->hw_dev,
- "Error occured while transferring data: 0x%X\n",
- intr_status);
- return -EPERM;
- }
- if (intr_status & data_ready_intr_mask) {
- status = bcm2835_mci_read(host, SDHCI_PRESENT_STATE);
- if ((status & data_ready_status_mask) == 0)
- continue;
- /*Clear latest int and transfer one block size of data*/
- bcm2835_mci_write(host, SDHCI_INT_STATUS,
- data_ready_intr_mask);
- for (i = 0; i < data->blocksize; i += 4) {
- read_write_func(host, p);
- p++;
- data_size -= 4;
- }
- }
- } while ((intr_status & IRQSTAT_TC) == 0);
-
- if (data_size != 0) {
- if (data->flags & MMC_DATA_READ)
- dev_err(host->hw_dev, "Error while reading:\n");
- else
- dev_err(host->hw_dev, "Error while writing:\n");
-
- dev_err(host->hw_dev, "Transferred %d bytes of data, wanted %d\n",
- (data->blocksize * data->blocks) - data_size,
- data->blocksize * data->blocks);
-
- dev_err(host->hw_dev, "Status: 0x%X, Interrupt: 0x%X\n",
- bcm2835_mci_read(host, SDHCI_PRESENT_STATE),
- bcm2835_mci_read(host, SDHCI_INT_STATUS));
-
- return -EPERM;
- }
- return 0;
+ return readl(host->regs + reg);
}
static u32 bcm2835_mci_wait_command_done(struct bcm2835_mci_host *host)
@@ -160,11 +89,10 @@ static u32 bcm2835_mci_wait_command_done(struct bcm2835_mci_host *host)
u32 interrupt = 0;
while (true) {
- interrupt = bcm2835_mci_read(
- host, SDHCI_INT_STATUS);
- if (interrupt & IRQSTAT_CIE)
+ interrupt = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
+ if (interrupt & SDHCI_INT_INDEX)
return -EPERM;
- if (interrupt & IRQSTAT_CC)
+ if (interrupt & SDHCI_INT_CMD_COMPLETE)
break;
}
return 0;
@@ -174,15 +102,15 @@ static void bcm2835_mci_reset_emmc(struct bcm2835_mci_host *host, u32 reset,
u32 wait_for)
{
u32 ret;
- u32 current = bcm2835_mci_read(host,
+ u32 current = sdhci_read32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
current | reset);
while (true) {
- ret = bcm2835_mci_read(host,
+ ret = sdhci_read32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
if (ret & wait_for)
continue;
@@ -199,31 +127,14 @@ static void bcm2835_mci_reset_emmc(struct bcm2835_mci_host *host, u32 reset,
*/
static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data) {
- u32 command, block_data = 0, ret = 0;
- u32 wait_inhibit_mask = PRSSTAT_CICHB | PRSSTAT_CIDHB;
+ u32 command, block_data = 0, ret = 0, transfer_mode = 0;
+ u32 wait_inhibit_mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
struct bcm2835_mci_host *host = to_bcm2835_host(mci);
- command = COMMAND_CMD(cmd->cmdidx);
-
- if (cmd->resp_type != MMC_RSP_NONE) {
- if (cmd->resp_type & MMC_RSP_136)
- command |= COMMAND_RSPTYP_136;
- else if (cmd->resp_type & MMC_RSP_BUSY)
- command |= COMMAND_RSPTYP_48_BUSY;
- else
- command |= COMMAND_RSPTYP_48;
- if (cmd->resp_type & MMC_RSP_CRC)
- command |= COMMAND_CCCEN;
- }
- if (data != NULL) {
- command |= COMMAND_DPSEL | TRANSFER_MODE_BCEN;
-
- if (data->blocks > 1)
- command |= TRANSFER_MODE_MSBSEL;
-
- if (data->flags & MMC_DATA_READ)
- command |= TRANSFER_MODE_DTDSEL;
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false,
+ &command, &transfer_mode);
+ if (data != NULL) {
block_data = (data->blocks << BLOCK_SHIFT);
block_data |= data->blocksize;
}
@@ -231,55 +142,39 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
/* We shouldn't wait for data inihibit for stop commands, even
though they might use busy signaling */
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
- wait_inhibit_mask = PRSSTAT_CICHB;
+ wait_inhibit_mask = SDHCI_CMD_INHIBIT_CMD;
/*Wait for old command*/
- while (bcm2835_mci_read(host, SDHCI_PRESENT_STATE)
+ while (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE)
& wait_inhibit_mask)
;
- bcm2835_mci_write(host, SDHCI_INT_ENABLE, 0xFFFFFFFF);
- bcm2835_mci_write(host, SDHCI_INT_STATUS, 0xFFFFFFFF);
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, 0xFFFFFFFF);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
- bcm2835_mci_write(host, SDHCI_BLOCK_SIZE__BLOCK_COUNT, block_data);
- bcm2835_mci_write(host, SDHCI_ARGUMENT, cmd->cmdarg);
- bcm2835_mci_write(host, SDHCI_TRANSFER_MODE__COMMAND, command);
+ sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT, block_data);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND,
+ command << 16 | transfer_mode);
ret = bcm2835_mci_wait_command_done(host);
if (ret) {
dev_err(host->hw_dev, "Error while executing command %d\n",
cmd->cmdidx);
dev_err(host->hw_dev, "Status: 0x%X, Interrupt: 0x%X\n",
- bcm2835_mci_read(host, SDHCI_PRESENT_STATE),
- bcm2835_mci_read(host, SDHCI_INT_STATUS));
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read32(&host->sdhci, SDHCI_INT_STATUS));
}
if (cmd->resp_type != 0 && ret != -1) {
- int i = 0;
-
- /* CRC is stripped so we need to do some shifting. */
- if (cmd->resp_type & MMC_RSP_136) {
- for (i = 0; i < 4; i++) {
- cmd->response[i] = bcm2835_mci_read(
- host,
- SDHCI_RESPONSE_0 + (3 - i) * 4) << 8;
- if (i != 3)
- cmd->response[i] |=
- readb((u32) (host->regs) +
- SDHCI_RESPONSE_0 +
- (3 - i) * 4 - 1);
- }
- } else {
- cmd->response[0] = bcm2835_mci_read(
- host, SDHCI_RESPONSE_0);
- }
- bcm2835_mci_write(host, SDHCI_INT_STATUS,
- IRQSTAT_CC);
+ sdhci_read_response(&host->sdhci, cmd);
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
}
if (!ret && data)
- ret = bcm2835_mci_transfer_data(host, cmd, data);
+ ret = sdhci_transfer_data(&host->sdhci, data);
- bcm2835_mci_write(host, SDHCI_INT_STATUS, 0xFFFFFFFF);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
if (ret) {
bcm2835_mci_reset_emmc(host, CONTROL1_CMDRST,
CONTROL1_CMDRST);
@@ -343,19 +238,19 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
struct bcm2835_mci_host *host = to_bcm2835_host(mci);
- current_val = bcm2835_mci_read(host,
+ current_val = sdhci_read32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL);
switch (ios->bus_width) {
case MMC_BUS_WIDTH_4:
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
current_val | CONTROL0_4DATA);
host->bus_width = 1;
dev_dbg(host->hw_dev, "Changing bus width to 4\n");
break;
case MMC_BUS_WIDTH_1:
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
current_val & ~CONTROL0_4DATA);
host->bus_width = 0;
@@ -367,15 +262,15 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
return;
}
if (ios->clock != host->clock && ios->clock != 0) {
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
0x00);
if (ios->clock > 26000000) {
- enable = bcm2835_mci_read(host,
+ enable = sdhci_read32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL);
enable |= CONTROL0_HISPEED;
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
enable);
}
@@ -388,18 +283,18 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
enable |= (divider_msb << CONTROL1_CLKMSB);
enable |= CONTROL1_INTCLKENA | CONTROL1_TIMEOUT;
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
enable);
while (true) {
- u32 ret = bcm2835_mci_read(host,
+ u32 ret = sdhci_read32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
if (ret & CONTROL1_CLK_STABLE)
break;
}
enable |= CONTROL1_CLKENA;
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
enable);
@@ -437,37 +332,37 @@ static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
bcm2835_mci_reset_emmc(host, enable | reset, CONTROL1_HOSTRST);
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
0x00);
- bcm2835_mci_write(host, SDHCI_ACMD12_ERR__HOST_CONTROL2,
+ sdhci_write32(&host->sdhci, SDHCI_ACMD12_ERR__HOST_CONTROL2,
0x00);
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
enable);
while (true) {
- ret = bcm2835_mci_read(host,
+ ret = sdhci_read32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
if (ret & CONTROL1_CLK_STABLE)
break;
}
enable |= CONTROL1_CLKENA;
- bcm2835_mci_write(host,
+ sdhci_write32(&host->sdhci,
SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
enable);
/*Delay atelast 74 clk cycles for card init*/
mdelay(100);
- bcm2835_mci_write(host, SDHCI_INT_ENABLE,
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
0xFFFFFFFF);
- bcm2835_mci_write(host, SDHCI_INT_STATUS,
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS,
0xFFFFFFFF);
/*Now write command 0 and see if we get response*/
- bcm2835_mci_write(host, SDHCI_ARGUMENT, 0x0);
- bcm2835_mci_write(host, SDHCI_TRANSFER_MODE__COMMAND, 0x0);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, 0x0);
+ sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND, 0x0);
return bcm2835_mci_wait_command_done(host);
}
@@ -507,6 +402,9 @@ static int bcm2835_mci_probe(struct device_d *hw_dev)
host->hw_dev = hw_dev;
host->max_clock = clk_get_rate(clk);
+ host->sdhci.read32 = bcm2835_sdhci_read32;
+ host->sdhci.write32 = bcm2835_sdhci_write32;
+
mci_of_parse(&host->mci);
iores = dev_request_mem_resource(hw_dev, 0);
@@ -540,8 +438,7 @@ static int bcm2835_mci_probe(struct device_d *hw_dev)
twoticks_delay = ((2 * 1000000000) / MIN_FREQ) + 1;
- host->version = bcm2835_mci_read(
- host, BCM2835_MCI_SLOTISR_VER);
+ host->version = sdhci_read32(&host->sdhci, BCM2835_MCI_SLOTISR_VER);
host->version = (host->version >> 16) & 0xFF;
return mci_register(&host->mci);
}
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 9e39cbbb55..67257bcd18 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1819,10 +1819,6 @@ int mci_register(struct mci_host *host)
host->supply = regulator_get(host->hw_dev, "vmmc");
if (IS_ERR(host->supply)) {
- if (host->supply == ERR_PTR(-EPROBE_DEFER)) {
- ret = -EPROBE_DEFER;
- goto err_free;
- }
dev_err(&mci->dev, "Failed to get 'vmmc' regulator.\n");
host->supply = NULL;
}
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
new file mode 100644
index 0000000000..172c8343a1
--- /dev/null
+++ b/drivers/mci/sdhci.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <driver.h>
+#include <mci.h>
+#include <io.h>
+
+#include "sdhci.h"
+
+void sdhci_read_response(struct sdhci *sdhci, struct mci_cmd *cmd)
+{
+ if (cmd->resp_type & MMC_RSP_136) {
+ u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
+
+ cmdrsp3 = sdhci_read32(sdhci, SDHCI_RESPONSE_3);
+ cmdrsp2 = sdhci_read32(sdhci, SDHCI_RESPONSE_2);
+ cmdrsp1 = sdhci_read32(sdhci, SDHCI_RESPONSE_1);
+ cmdrsp0 = sdhci_read32(sdhci, SDHCI_RESPONSE_0);
+ cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
+ cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
+ cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
+ cmd->response[3] = (cmdrsp0 << 8);
+ } else {
+ cmd->response[0] = sdhci_read32(sdhci, SDHCI_RESPONSE_0);
+ }
+}
+
+void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
+ struct mci_data *data, bool dma, u32 *command,
+ u32 *xfer)
+{
+ *command = 0;
+ *xfer = 0;
+
+ if (!(cmd->resp_type & MMC_RSP_PRESENT))
+ *command |= SDHCI_RESP_NONE;
+ else if (cmd->resp_type & MMC_RSP_136)
+ *command |= SDHCI_RESP_TYPE_136;
+ else if (cmd->resp_type & MMC_RSP_BUSY)
+ *command |= SDHCI_RESP_TYPE_48_BUSY;
+ else
+ *command |= SDHCI_RESP_TYPE_48;
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ *command |= SDHCI_CMD_CRC_CHECK_EN;
+ if (cmd->resp_type & MMC_RSP_OPCODE)
+ *command |= SDHCI_CMD_INDEX_CHECK_EN;
+
+ *command |= SDHCI_CMD_INDEX(cmd->cmdidx);
+
+ if (data) {
+ *command |= SDHCI_DATA_PRESENT;
+
+ *xfer |= SDHCI_BLOCK_COUNT_EN;
+
+ if (data->blocks > 1)
+ *xfer |= SDHCI_MULTIPLE_BLOCKS;
+
+ if (data->flags & MMC_DATA_READ)
+ *xfer |= SDHCI_DATA_TO_HOST;
+
+ if (dma)
+ *xfer |= SDHCI_DMA_EN;
+ }
+}
+
+static void sdhci_rx_pio(struct sdhci *sdhci, struct mci_data *data,
+ unsigned int block)
+{
+ u32 *buf = (u32 *)data->dest;
+ int i;
+
+ buf += block * data->blocksize / sizeof(u32);
+
+ for (i = 0; i < data->blocksize / sizeof(u32); i++)
+ buf[i] = sdhci_read32(sdhci, SDHCI_BUFFER);
+}
+
+static void sdhci_tx_pio(struct sdhci *sdhci, struct mci_data *data,
+ unsigned int block)
+{
+ const u32 *buf = (const u32 *)data->src;
+ int i;
+
+ buf += block * data->blocksize / sizeof(u32);
+
+ for (i = 0; i < data->blocksize / sizeof(u32); i++)
+ sdhci_write32(sdhci, SDHCI_BUFFER, buf[i]);
+}
+
+#ifdef __PBL__
+/*
+ * Stubs to make timeout logic below work in PBL
+ */
+
+#define get_time_ns() 0
+/*
+ * Use time in us as a busy counter timeout value
+ */
+#define is_timeout(s, t) ((s)++ > ((t) / 1000))
+
+#endif
+
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
+{
+ unsigned int block = 0;
+ u32 stat, prs;
+ uint64_t start = get_time_ns();
+
+ do {
+ stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ if (stat & SDHCI_INT_ERROR)
+ return -EIO;
+
+ if (block >= data->blocks)
+ continue;
+
+ prs = sdhci_read32(sdhci, SDHCI_PRESENT_STATE);
+
+ if (prs & SDHCI_BUFFER_READ_ENABLE &&
+ data->flags & MMC_DATA_READ) {
+ sdhci_rx_pio(sdhci, data, block);
+ block++;
+ start = get_time_ns();
+ }
+
+ if (prs & SDHCI_BUFFER_WRITE_ENABLE &&
+ !(data->flags & MMC_DATA_READ)) {
+ sdhci_tx_pio(sdhci, data, block);
+ block++;
+ start = get_time_ns();
+ }
+
+ if (is_timeout(start, 10 * SECOND))
+ return -ETIMEDOUT;
+
+ } while (!(stat & SDHCI_INT_XFER_COMPLETE));
+
+ return 0;
+}
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 90595e6433..a307dc97cd 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -24,6 +24,9 @@
#define SDHCI_DMA_EN BIT(0)
#define SDHCI_COMMAND 0x0e
#define SDHCI_CMD_INDEX(c) (((c) & 0x3f) << 8)
+#define SDHCI_COMMAND_CMDTYP_SUSPEND (1 << 6)
+#define SDHCI_COMMAND_CMDTYP_RESUME (2 << 6)
+#define SDHCI_COMMAND_CMDTYP_ABORT (3 << 6)
#define SDHCI_DATA_PRESENT BIT(5)
#define SDHCI_CMD_INDEX_CHECK_EN BIT(4)
#define SDHCI_CMD_CRC_CHECK_EN BIT(3)
@@ -37,11 +40,18 @@
#define SDHCI_RESPONSE_3 0x1c
#define SDHCI_BUFFER 0x20
#define SDHCI_PRESENT_STATE 0x24
+#define SDHCI_WRITE_PROTECT BIT(19)
+#define SDHCI_CARD_DETECT BIT(18)
+#define SDHCI_BUFFER_READ_ENABLE BIT(11)
+#define SDHCI_BUFFER_WRITE_ENABLE BIT(10)
+#define SDHCI_DATA_LINE_ACTIVE BIT(2)
#define SDHCI_CMD_INHIBIT_DATA BIT(1)
#define SDHCI_CMD_INHIBIT_CMD BIT(0)
#define SDHCI_PRESENT_STATE1 0x26
#define SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL 0x28
#define SDHCI_HOST_CONTROL 0x28
+#define SDHCI_CARD_DETECT_SIGNAL_SELECTION BIT(7)
+#define SDHCI_CARD_DETECT_TEST_LEVEL BIT(6)
#define SDHCI_DATA_WIDTH_8BIT BIT(5)
#define SDHCI_HIGHSPEED_EN BIT(2)
#define SDHCI_DATA_WIDTH_4BIT BIT(1)
@@ -60,7 +70,17 @@
#define SDHCI_RESET_ALL BIT(0)
#define SDHCI_INT_STATUS 0x30
#define SDHCI_INT_NORMAL_STATUS 0x30
+#define SDHCI_INT_DATA_END_BIT BIT(22)
+#define SDHCI_INT_DATA_CRC BIT(21)
+#define SDHCI_INT_DATA_TIMEOUT BIT(20)
+#define SDHCI_INT_INDEX BIT(19)
+#define SDHCI_INT_END_BIT BIT(18)
+#define SDHCI_INT_CRC BIT(17)
+#define SDHCI_INT_TIMEOUT BIT(16)
#define SDHCI_INT_ERROR BIT(15)
+#define SDHCI_INT_CARD_INT BIT(8)
+#define SDHCI_INT_DATA_AVAIL BIT(5)
+#define SDHCI_INT_SPACE_AVAIL BIT(4)
#define SDHCI_INT_DMA BIT(3)
#define SDHCI_INT_XFER_COMPLETE BIT(1)
#define SDHCI_INT_CMD_COMPLETE BIT(0)
@@ -79,74 +99,49 @@
#define SDHCI_SPEC_200_MAX_CLK_DIVIDER 256
#define SDHCI_MMC_BOOT 0xC4
-#define COMMAND_CMD(x) ((x & 0x3f) << 24)
-#define COMMAND_CMDTYP_NORMAL 0x0
-#define COMMAND_CMDTYP_SUSPEND 0x00400000
-#define COMMAND_CMDTYP_RESUME 0x00800000
-#define COMMAND_CMDTYP_ABORT 0x00c00000
-#define COMMAND_DPSEL 0x00200000
-#define COMMAND_CICEN 0x00100000
-#define COMMAND_CCCEN 0x00080000
-#define COMMAND_RSPTYP_NONE 0
-#define COMMAND_RSPTYP_136 0x00010000
-#define COMMAND_RSPTYP_48 0x00020000
-#define COMMAND_RSPTYP_48_BUSY 0x00030000
-#define TRANSFER_MODE_MSBSEL 0x00000020
-#define TRANSFER_MODE_DTDSEL 0x00000010
-#define TRANSFER_MODE_AC12EN 0x00000004
-#define TRANSFER_MODE_BCEN 0x00000002
-#define TRANSFER_MODE_DMAEN 0x00000001
+struct sdhci {
+ u32 (*read32)(struct sdhci *host, int reg);
+ u16 (*read16)(struct sdhci *host, int reg);
+ u8 (*read8)(struct sdhci *host, int reg);
+ void (*write32)(struct sdhci *host, int reg, u32 val);
+ void (*write16)(struct sdhci *host, int reg, u16 val);
+ void (*write8)(struct sdhci *host, int reg, u8 val);
+};
-#define IRQSTAT_TE 0x04000000
-#define IRQSTAT_DMAE 0x02000000
-#define IRQSTAT_AC12E 0x01000000
-#define IRQSTAT_CLE 0x00800000
-#define IRQSTAT_DEBE 0x00400000
-#define IRQSTAT_DCE 0x00200000
-#define IRQSTAT_DTOE 0x00100000
-#define IRQSTAT_CIE 0x00080000
-#define IRQSTAT_CEBE 0x00040000
-#define IRQSTAT_CCE 0x00020000
-#define IRQSTAT_CTOE 0x00010000
-#define IRQSTAT_CINT 0x00000100
-#define IRQSTAT_CRM 0x00000080
-#define IRQSTAT_CINS 0x00000040
-#define IRQSTAT_BRR 0x00000020
-#define IRQSTAT_BWR 0x00000010
-#define IRQSTAT_DINT 0x00000008
-#define IRQSTAT_BGE 0x00000004
-#define IRQSTAT_TC 0x00000002
-#define IRQSTAT_CC 0x00000001
+static inline u32 sdhci_read32(struct sdhci *host, int reg)
+{
+ return host->read32(host, reg);
+}
-#define IRQSTATEN_DMAE 0x10000000
-#define IRQSTATEN_AC12E 0x01000000
-#define IRQSTATEN_DEBE 0x00400000
-#define IRQSTATEN_DCE 0x00200000
-#define IRQSTATEN_DTOE 0x00100000
-#define IRQSTATEN_CIE 0x00080000
-#define IRQSTATEN_CEBE 0x00040000
-#define IRQSTATEN_CCE 0x00020000
-#define IRQSTATEN_CTOE 0x00010000
-#define IRQSTATEN_CINT 0x00000100
-#define IRQSTATEN_CRM 0x00000080
-#define IRQSTATEN_CINS 0x00000040
-#define IRQSTATEN_BRR 0x00000020
-#define IRQSTATEN_BWR 0x00000010
-#define IRQSTATEN_DINT 0x00000008
-#define IRQSTATEN_BGE 0x00000004
-#define IRQSTATEN_TC 0x00000002
-#define IRQSTATEN_CC 0x00000001
+static inline u32 sdhci_read16(struct sdhci *host, int reg)
+{
+ return host->read16(host, reg);
+}
-#define PRSSTAT_DAT0 0x01000000
-#define PRSSTAT_CLSL 0x00800000
-#define PRSSTAT_WPSPL 0x00080000
-#define PRSSTAT_CDPL 0x00040000
-#define PRSSTAT_CINS 0x00010000
-#define PRSSTAT_BREN 0x00000800
-#define PRSSTAT_BWEN 0x00000400
-#define PRSSTAT_SDSTB 0x00000008
-#define PRSSTAT_DLA 0x00000004
-#define PRSSTAT_CIDHB 0x00000002
-#define PRSSTAT_CICHB 0x00000001
+static inline u32 sdhci_read8(struct sdhci *host, int reg)
+{
+ return host->read8(host, reg);
+}
+
+static inline void sdhci_write32(struct sdhci *host, int reg, u32 val)
+{
+ host->write32(host, reg, val);
+}
+
+static inline void sdhci_write16(struct sdhci *host, int reg, u32 val)
+{
+ host->write16(host, reg, val);
+}
+
+static inline void sdhci_write8(struct sdhci *host, int reg, u32 val)
+{
+ host->write8(host, reg, val);
+}
+
+void sdhci_read_response(struct sdhci *host, struct mci_cmd *cmd);
+void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
+ struct mci_data *data, bool dma, u32 *command,
+ u32 *xfer);
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data);
#endif /* __MCI_SDHCI_H */
diff --git a/drivers/mci/tegra-sdmmc.c b/drivers/mci/tegra-sdmmc.c
index 4c47918076..1cc75dc524 100644
--- a/drivers/mci/tegra-sdmmc.c
+++ b/drivers/mci/tegra-sdmmc.c
@@ -74,9 +74,24 @@ struct tegra_sdmmc_host {
struct clk *clk;
struct reset_control *reset;
int gpio_cd, gpio_pwr;
+ struct sdhci sdhci;
};
#define to_tegra_sdmmc_host(mci) container_of(mci, struct tegra_sdmmc_host, mci)
+static u32 tegra_sdmmc_read32(struct sdhci *sdhci, int reg)
+{
+ struct tegra_sdmmc_host *host = container_of(sdhci, struct tegra_sdmmc_host, sdhci);
+
+ return readl(host->regs + reg);
+}
+
+static void tegra_sdmmc_write32(struct sdhci *sdhci, int reg, u32 val)
+{
+ struct tegra_sdmmc_host *host = container_of(sdhci, struct tegra_sdmmc_host, sdhci);
+
+ writel(val, host->regs + reg);
+}
+
static int tegra_sdmmc_wait_inhibit(struct tegra_sdmmc_host *host,
struct mci_cmd *cmd, struct mci_data *data,
unsigned int timeout)
@@ -91,7 +106,7 @@ static int tegra_sdmmc_wait_inhibit(struct tegra_sdmmc_host *host,
val |= TEGRA_SDMMC_PRESENT_STATE_CMD_INHIBIT_DAT;
wait_on_timeout(timeout * MSECOND,
- !(readl(host->regs + TEGRA_SDMMC_PRESENT_STATE) & val));
+ !(sdhci_read32(&host->sdhci, TEGRA_SDMMC_PRESENT_STATE) & val));
return 0;
}
@@ -101,7 +116,7 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
unsigned int num_bytes = 0;
- u32 val = 0;
+ u32 val = 0, command, xfer;
int ret;
ret = tegra_sdmmc_wait_inhibit(host, cmd, data, 10);
@@ -115,102 +130,66 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
if (data->flags & MMC_DATA_WRITE) {
dma_sync_single_for_device((unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
- writel((u32)data->src, host->regs + SDHCI_DMA_ADDRESS);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src);
} else {
dma_sync_single_for_device((unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
- writel((u32)data->dest, host->regs + SDHCI_DMA_ADDRESS);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest);
}
- writel((7 << 12) | data->blocks << 16 | data->blocksize,
- host->regs + SDHCI_BLOCK_SIZE__BLOCK_COUNT);
+ sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT,
+ (7 << 12) | data->blocks << 16 | data->blocksize);
}
- writel(cmd->cmdarg, host->regs + SDHCI_ARGUMENT);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
- if (data) {
- if (data->blocks > 1)
- val |= TRANSFER_MODE_MSBSEL;
-
- if (data->flags & MMC_DATA_READ)
- val |= TRANSFER_MODE_DTDSEL;
-
- val |= TRANSFER_MODE_DMAEN | TRANSFER_MODE_BCEN;
- }
-
- if (!(cmd->resp_type & MMC_RSP_PRESENT))
- val |= COMMAND_RSPTYP_NONE;
- else if (cmd->resp_type & MMC_RSP_136)
- val |= COMMAND_RSPTYP_136;
- else if (cmd->resp_type & MMC_RSP_BUSY)
- val |= COMMAND_RSPTYP_48_BUSY;
- else
- val |= COMMAND_RSPTYP_48;
-
- if (cmd->resp_type & MMC_RSP_CRC)
- val |= COMMAND_CCCEN;
- if (cmd->resp_type & MMC_RSP_OPCODE)
- val |= COMMAND_CICEN;
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, true, &command, &xfer);
- if (data)
- val |= COMMAND_DPSEL;
-
- writel(COMMAND_CMD(cmd->cmdidx) | val,
- host->regs + SDHCI_TRANSFER_MODE__COMMAND);
+ sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND,
+ command << 16 | xfer);
ret = wait_on_timeout(100 * MSECOND,
- (val = readl(host->regs + SDHCI_INT_STATUS))
- & IRQSTAT_CC);
+ (val = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS))
+ & SDHCI_INT_CMD_COMPLETE);
if (ret) {
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
return ret;
}
- if ((val & IRQSTAT_CC) && !data)
- writel(val, host->regs + SDHCI_INT_STATUS);
+ if ((val & SDHCI_INT_CMD_COMPLETE) && !data)
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
if (val & TEGRA_SDMMC_INTERRUPT_STATUS_CMD_TIMEOUT) {
/* Timeout Error */
dev_dbg(mci->hw_dev, "timeout: %08x cmd %d\n", val, cmd->cmdidx);
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
return -ETIMEDOUT;
} else if (val & TEGRA_SDMMC_INTERRUPT_STATUS_ERR_INTERRUPT) {
/* Error Interrupt */
dev_dbg(mci->hw_dev, "error: %08x cmd %d\n", val, cmd->cmdidx);
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
return -EIO;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (cmd->resp_type & MMC_RSP_136) {
- u32 cmdrsp[4];
-
- cmdrsp[3] = readl(host->regs + SDHCI_RESPONSE_3);
- cmdrsp[2] = readl(host->regs + SDHCI_RESPONSE_2);
- cmdrsp[1] = readl(host->regs + SDHCI_RESPONSE_1);
- cmdrsp[0] = readl(host->regs + SDHCI_RESPONSE_0);
- cmd->response[0] = (cmdrsp[3] << 8) | (cmdrsp[2] >> 24);
- cmd->response[1] = (cmdrsp[2] << 8) | (cmdrsp[1] >> 24);
- cmd->response[2] = (cmdrsp[1] << 8) | (cmdrsp[0] >> 24);
- cmd->response[3] = (cmdrsp[0] << 8);
- } else if (cmd->resp_type & MMC_RSP_BUSY) {
+ sdhci_read_response(&host->sdhci, cmd);
+
+ if (cmd->resp_type & MMC_RSP_BUSY) {
ret = wait_on_timeout(100 * MSECOND,
- readl(host->regs + TEGRA_SDMMC_PRESENT_STATE)
+ sdhci_read32(&host->sdhci, TEGRA_SDMMC_PRESENT_STATE)
& (1 << 20));
if (ret) {
dev_err(mci->hw_dev, "card is still busy\n");
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
return ret;
}
- cmd->response[0] = readl(host->regs + SDHCI_RESPONSE_0);
- } else {
- cmd->response[0] = readl(host->regs + SDHCI_RESPONSE_0);
+ cmd->response[0] = sdhci_read32(&host->sdhci, SDHCI_RESPONSE_0);
}
}
@@ -218,15 +197,15 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
uint64_t start = get_time_ns();
while (1) {
- val = readl(host->regs + SDHCI_INT_STATUS);
+ val = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
if (val & TEGRA_SDMMC_INTERRUPT_STATUS_ERR_INTERRUPT) {
/* Error Interrupt */
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
dev_err(mci->hw_dev,
"error during transfer: 0x%08x\n", val);
return -EIO;
- } else if (val & IRQSTAT_DINT) {
+ } else if (val & SDHCI_INT_DMA) {
/*
* DMA Interrupt, restart the transfer where
* it was interrupted.
@@ -234,27 +213,26 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
u32 address = readl(host->regs +
SDHCI_DMA_ADDRESS);
- writel(IRQSTAT_DINT,
- host->regs + SDHCI_INT_STATUS);
- writel(address, host->regs + SDHCI_DMA_ADDRESS);
- } else if (val & IRQSTAT_TC) {
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, address);
+ } else if (val & SDHCI_INT_XFER_COMPLETE) {
/* Transfer Complete */;
break;
} else if (is_timeout(start, 2 * SECOND)) {
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
dev_err(mci->hw_dev, "MMC Timeout\n"
" Interrupt status 0x%08x\n"
" Interrupt status enable 0x%08x\n"
" Interrupt signal enable 0x%08x\n"
" Present status 0x%08x\n",
val,
- readl(host->regs + SDHCI_INT_ENABLE),
- readl(host->regs + SDHCI_SIGNAL_ENABLE),
- readl(host->regs + SDHCI_PRESENT_STATE));
+ sdhci_read32(&host->sdhci, SDHCI_INT_ENABLE),
+ sdhci_read32(&host->sdhci, SDHCI_SIGNAL_ENABLE),
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE));
return -ETIMEDOUT;
}
}
- writel(val, host->regs + SDHCI_INT_STATUS);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
if (data->flags & MMC_DATA_WRITE)
dma_sync_single_for_cpu((unsigned long)data->src,
@@ -277,25 +255,25 @@ static void tegra_sdmmc_set_clock(struct tegra_sdmmc_host *host, u32 clock)
}
/* clear clock related bits */
- val = readl(host->regs + TEGRA_SDMMC_CLK_CNTL);
+ val = sdhci_read32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL);
val &= 0xffff0000;
- writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, val);
/* set new frequency */
val |= prediv << 8;
val |= TEGRA_SDMMC_CLK_CNTL_INTERNAL_CLOCK_EN;
- writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, val);
clk_set_rate(host->clk, adjusted_clock);
/* wait for controller to settle */
wait_on_timeout(10 * MSECOND,
- !(readl(host->regs + TEGRA_SDMMC_CLK_CNTL) &
+ !(sdhci_read32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL) &
TEGRA_SDMMC_CLK_INTERNAL_CLOCK_STABLE));
/* enable card clock */
val |= TEGRA_SDMMC_CLK_CNTL_SD_CLOCK_EN;
- writel(val, host->regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, val);
}
static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
@@ -308,7 +286,7 @@ static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
tegra_sdmmc_set_clock(host, ios->clock);
/* set bus width */
- val = readl(host->regs + TEGRA_SDMMC_PWR_CNTL);
+ val = sdhci_read32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL);
val &= ~(0x21);
if (ios->bus_width == MMC_BUS_WIDTH_8)
@@ -316,7 +294,7 @@ static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
else if (ios->bus_width == MMC_BUS_WIDTH_4)
val |= (1 << 1);
- writel(val, host->regs + TEGRA_SDMMC_PWR_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
}
static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
@@ -327,8 +305,8 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
int ret;
/* reset controller */
- writel(TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL,
- regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL,
+ TEGRA_SDMMC_CLK_CNTL_SW_RESET_FOR_ALL);
ret = wait_on_timeout(100 * MSECOND,
!(readl(regs + TEGRA_SDMMC_CLK_CNTL) &
@@ -342,7 +320,7 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
val = readl(regs + TEGRA_SDMMC_PWR_CNTL);
val &= ~(0xff << 8);
val |= TEGRA_SDMMC_PWR_CNTL_33_V | TEGRA_SDMMC_PWR_CNTL_SD_BUS;
- writel(val, regs + TEGRA_SDMMC_PWR_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
/* sdmmc1 and sdmmc3 on T30 need a bit of padctrl init */
if (of_device_is_compatible(mci->hw_dev->device_node,
@@ -351,21 +329,21 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
val = readl(regs + TEGRA_SDMMC_SDMEMCOMPPADCTRL);
val &= 0xfffffff0;
val |= 0x7 << TEGRA_SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_SHIFT;
- writel(val, regs + TEGRA_SDMMC_SDMEMCOMPPADCTRL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_SDMEMCOMPPADCTRL, val);
val = readl(regs + TEGRA_SDMMC_AUTO_CAL_CONFIG);
val &= 0xffff0000;
val |= (0x62 << TEGRA_SDMMC_AUTO_CAL_CONFIG_PU_OFFSET_SHIFT) |
(0x70 << TEGRA_SDMMC_AUTO_CAL_CONFIG_PD_OFFSET_SHIFT) |
TEGRA_SDMMC_AUTO_CAL_CONFIG_ENABLE;
- writel(val, regs + TEGRA_SDMMC_AUTO_CAL_CONFIG);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_AUTO_CAL_CONFIG, val);
}
/* setup signaling */
- writel(0xffffffff, regs + TEGRA_SDMMC_INT_STAT_EN);
- writel(0xffffffff, regs + TEGRA_SDMMC_INT_SIG_EN);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_STAT_EN, 0xffffffff);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_SIG_EN, 0xffffffff);
- writel(0xe << 16, regs + TEGRA_SDMMC_CLK_CNTL);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_CLK_CNTL, 0xe << 16);
val = readl(regs + TEGRA_SDMMC_INT_STAT_EN);
val &= ~(0xffff);
@@ -374,12 +352,12 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
TEGRA_SDMMC_INT_STAT_EN_DMA_INTERRUPT |
TEGRA_SDMMC_INT_STAT_EN_BUFFER_WRITE_READY |
TEGRA_SDMMC_INT_STAT_EN_BUFFER_READ_READY);
- writel(val, regs + TEGRA_SDMMC_INT_STAT_EN);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_STAT_EN, val);
val = readl(regs + TEGRA_SDMMC_INT_SIG_EN);
val &= ~(0xffff);
val |= TEGRA_SDMMC_INT_SIG_EN_XFER_COMPLETE;
- writel(val, regs + TEGRA_SDMMC_INT_SIG_EN);
+ sdhci_write32(&host->sdhci, TEGRA_SDMMC_INT_SIG_EN, val);
tegra_sdmmc_set_clock(host, 400000);
@@ -398,7 +376,7 @@ static int tegra_sdmmc_card_present(struct mci_host *mci)
return gpio_get_value(host->gpio_cd) ? 0 : 1;
}
- return !(readl(host->regs + SDHCI_PRESENT_STATE) & PRSSTAT_WPSPL);
+ return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
}
static int tegra_sdmmc_detect(struct device_d *dev)
@@ -442,6 +420,9 @@ static int tegra_sdmmc_probe(struct device_d *dev)
}
host->regs = IOMEM(iores->start);
+ host->sdhci.read32 = tegra_sdmmc_read32;
+ host->sdhci.write32 = tegra_sdmmc_write32;
+
mci->hw_dev = dev;
mci->f_max = 48000000;
mci->f_min = 375000;