diff options
Diffstat (limited to 'drivers/mci')
-rw-r--r-- | drivers/mci/Kconfig | 20 | ||||
-rw-r--r-- | drivers/mci/Makefile | 6 | ||||
-rw-r--r-- | drivers/mci/arasan-sdhci.c | 423 | ||||
-rw-r--r-- | drivers/mci/dove-sdhci.c | 159 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc-common.c | 275 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc-pbl.c | 333 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc.c | 441 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc.h | 108 | ||||
-rw-r--r-- | drivers/mci/mci-bcm2835.c | 227 | ||||
-rw-r--r-- | drivers/mci/mci-core.c | 4 | ||||
-rw-r--r-- | drivers/mci/sdhci.c | 140 | ||||
-rw-r--r-- | drivers/mci/sdhci.h | 127 | ||||
-rw-r--r-- | drivers/mci/tegra-sdmmc.c | 159 |
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; |