diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2021-06-16 10:54:37 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2021-06-16 10:54:37 +0200 |
commit | 0e062b8296a22afff4157f56c5b6b64e7fac5aac (patch) | |
tree | 7cfca330bec0f26799b8a5142c9a880b215ae9b6 /drivers | |
parent | bdc5a5d220edf8c8a2685f9f3f066798f521f9e8 (diff) | |
parent | 0868df7d44538298fcf9a037916d7a4ae9bc3b48 (diff) | |
download | barebox-0e062b8296a22afff4157f56c5b6b64e7fac5aac.tar.gz barebox-0e062b8296a22afff4157f56c5b6b64e7fac5aac.tar.xz |
Merge branch 'for-next/mmc'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mci/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mci/Makefile | 1 | ||||
-rw-r--r-- | drivers/mci/arasan-sdhci.c | 208 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci-common.c | 75 | ||||
-rw-r--r-- | drivers/mci/dove-sdhci.c | 84 | ||||
-rw-r--r-- | drivers/mci/dw_mmc.c | 53 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc-common.c | 109 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc-pbl.c | 22 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc.c | 20 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc.h | 8 | ||||
-rw-r--r-- | drivers/mci/mci-bcm2835.c | 2 | ||||
-rw-r--r-- | drivers/mci/mci-core.c | 4 | ||||
-rw-r--r-- | drivers/mci/rockchip-dwcmshc-sdhci.c | 377 | ||||
-rw-r--r-- | drivers/mci/sdhci.c | 399 | ||||
-rw-r--r-- | drivers/mci/sdhci.h | 168 |
15 files changed, 1097 insertions, 440 deletions
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index 7d4e72138d..e1fcd41271 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -79,6 +79,13 @@ config MCI_MXS Enable this entry to add support to read and write SD cards on a i.MX23/i.MX28 based system. +config MCI_ROCKCHIP_DWCMSHC + bool "MCI sdhc support for Rockchip SoCs" + select MCI_SDHCI + help + Enable this entry to add support for a Rockchip derivation of the + DWCMSHC controller found on some Rockchip SoCs like the RK3568. + config MCI_S3C bool "S3C" depends on ARCH_S3C24xx diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile index 60dc100c37..b113b1c732 100644 --- a/drivers/mci/Makefile +++ b/drivers/mci/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_MCI_MXS) += mxs.o obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o obj-$(CONFIG_MCI_PXA) += pxamci.o obj-$(CONFIG_MCI_S3C) += s3c.o +obj-$(CONFIG_MCI_ROCKCHIP_DWCMSHC) += rockchip-dwcmshc-sdhci.o obj-$(CONFIG_MCI_TEGRA) += tegra-sdmmc.o obj-$(CONFIG_MCI_SPI) += mci_spi.o obj-$(CONFIG_MCI_DW) += dw_mmc.o diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c index 520bf30ff9..d45f9184cd 100644 --- a/drivers/mci/arasan-sdhci.c +++ b/drivers/mci/arasan-sdhci.c @@ -12,20 +12,20 @@ #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 | \ +#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 + SDHCI_INT_ADMAE) -#define SDHCI_ARASAN_INT_CMD_MASK SDHCI_INT_CMD_COMPLETE | \ +#define SDHCI_ARASAN_INT_CMD_MASK (SDHCI_INT_CMD_COMPLETE | \ SDHCI_INT_TIMEOUT | \ SDHCI_INT_CRC | \ SDHCI_INT_END_BIT | \ - SDHCI_INT_INDEX + SDHCI_INT_INDEX) #define SDHCI_ARASAN_BUS_WIDTH 4 #define TIMEOUT_VAL 0xE @@ -33,14 +33,12 @@ 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) { @@ -53,60 +51,24 @@ 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); + u32 val; + + val = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE); - return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT); + return !!(val & SDHCI_CARD_DETECT); } static int arasan_sdhci_card_write_protected(struct mci_host *mci) { struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci); + u32 val; - return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT); + val = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE); + + return !(val & SDHCI_WRITE_PROTECT); } static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask) @@ -115,7 +77,7 @@ static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask) /* wait for reset completion */ if (wait_on_timeout(100 * MSECOND, - !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))){ + !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))) { dev_err(host->mci.hw_dev, "SDHCI reset timeout\n"); return -ETIMEDOUT; } @@ -124,7 +86,7 @@ static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask) u8 ctrl; ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL); - ctrl |= SDHCI_CARD_DETECT_TEST_LEVEL | SDHCI_CARD_DETECT_SIGNAL_SELECTION; + ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_INS; sdhci_write8(&host->sdhci, ctrl, SDHCI_HOST_CONTROL); } @@ -141,82 +103,32 @@ static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev) return ret; sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL, - SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN); + 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); + SDHCI_ARASAN_INT_DATA_MASK | SDHCI_ARASAN_INT_CMD_MASK); + sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0); 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); - } + if (ios->clock) + sdhci_set_clock(&host->sdhci, ios->clock, host->sdhci.max_clk); - val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) & - ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT); + sdhci_set_bus_width(&host->sdhci, ios->bus_width); - 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; - } + val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL); if (ios->clock > 26000000) - val |= SDHCI_HIGHSPEED_EN; + val |= SDHCI_CTRL_HISPD; else - val &= ~SDHCI_HIGHSPEED_EN; + val &= ~SDHCI_CTRL_HISPD; sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val); } @@ -225,18 +137,21 @@ static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask) { u64 start = get_time_ns(); u16 stat; + u16 error; do { stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS); if (stat & SDHCI_INT_ERROR) { + error = sdhci_read16(&host->sdhci, + SDHCI_INT_ERROR_STATUS); dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n", - sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS)); + error); return -EPERM; } if (is_timeout(start, 1000 * MSECOND)) { dev_err(host->mci.hw_dev, - "SDHCI timeout while waiting for done\n"); + "SDHCI timeout while waiting for done\n"); return -ETIMEDOUT; } } while ((stat & mask) != mask); @@ -247,7 +162,7 @@ static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask) 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); + "error while transferring 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)); @@ -266,27 +181,31 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, mask |= SDHCI_CMD_INHIBIT_DATA; ret = wait_on_timeout(10 * MSECOND, - !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask)); - + !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask)); if (ret) { dev_err(host->mci.hw_dev, - "SDHCI timeout while waiting for idle\n"); + "SDHCI timeout while waiting for idle\n"); return ret; } sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); mask = SDHCI_INT_CMD_COMPLETE; - if (data) + if (data && data->flags == MMC_DATA_READ) mask |= SDHCI_INT_DATA_AVAIL; + if (cmd->resp_type & MMC_RSP_BUSY) + mask |= SDHCI_INT_XFER_COMPLETE; - sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false, &command, &xfer); + 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); + if (data) { + 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); @@ -300,7 +219,7 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, mask); if (data) - ret = sdhci_transfer_data(&host->sdhci, data); + ret = sdhci_transfer_data_pio(&host->sdhci, data); error: if (ret) { @@ -310,33 +229,8 @@ error: } 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; + return ret; } static int arasan_sdhci_probe(struct device_d *dev) @@ -355,7 +249,6 @@ static int arasan_sdhci_probe(struct device_d *dev) 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)) { @@ -387,12 +280,8 @@ static int arasan_sdhci_probe(struct device_d *dev) 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; + arasan_sdhci->sdhci.base = IOMEM(iores->start); + arasan_sdhci->sdhci.mci = mci; mci->send_cmd = arasan_sdhci_send_cmd; mci->set_ios = arasan_sdhci_set_ios; mci->init = arasan_sdhci_init; @@ -403,7 +292,10 @@ static int arasan_sdhci_probe(struct device_d *dev) mci->f_max = clk_get_rate(clk_xin); mci->f_min = 50000000 / 256; - arasan_sdhci_set_mci_caps(arasan_sdhci); + /* parse board supported bus width capabilities */ + mci_of_parse(&arasan_sdhci->mci); + + sdhci_setup_host(&arasan_sdhci->sdhci); dev->priv = arasan_sdhci; diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c index a83610c3d0..eff2a993db 100644 --- a/drivers/mci/atmel-sdhci-common.c +++ b/drivers/mci/atmel-sdhci-common.c @@ -11,6 +11,7 @@ #include <common.h> #include <mci.h> +#include <linux/bitfield.h> #include <mach/early_udelay.h> @@ -24,8 +25,6 @@ #include "atmel-sdhci.h" -#define AT91_SDHCI_CA1R 0x44 /* Capabilities 1 Register */ - #define AT91_SDHCI_MC1R 0x204 #define AT91_SDHCI_MC1_FCD BIT(7) #define AT91_SDHCI_CALCR 0x240 @@ -42,13 +41,13 @@ void at91_sdhci_host_capability(struct at91_sdhci *host, { u16 caps; - caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1); + caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES); - if (caps & SDHCI_HOSTCAP_VOLTAGE_330) + if (caps & SDHCI_CAN_VDD_330) *voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; - if (caps & SDHCI_HOSTCAP_VOLTAGE_300) + if (caps & SDHCI_CAN_VDD_300) *voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; - if (caps & SDHCI_HOSTCAP_VOLTAGE_180) + if (caps & SDHCI_CAN_VDD_180) *voltages |= MMC_VDD_165_195; } @@ -167,7 +166,7 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd, sdhci_write32(sdhci, SDHCI_INT_STATUS, mask); if (data) - sdhci_transfer_data(sdhci, data); + sdhci_transfer_data_pio(sdhci, data); udelay(1000); @@ -237,9 +236,9 @@ static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock) if (clock == 0) return 0; - caps = sdhci_read32(sdhci, AT91_SDHCI_CA1R); + caps = sdhci_read32(sdhci, SDHCI_CAPABILITIES_1); - caps_clk_mult = (caps & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; + caps_clk_mult = FIELD_GET(SDHCI_CLOCK_MUL_MASK, caps); if (caps_clk_mult) { for (clk_div = 1; clk_div <= 1024; clk_div++) { @@ -264,26 +263,26 @@ static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock) clk |= SDHCI_FREQ_SEL(clk_div); clk |= ((clk_div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; - clk |= SDHCI_INTCLOCK_EN; + clk |= SDHCI_CLOCK_INT_EN; sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk); ret = sdhci_read32_poll_timeout(sdhci, SDHCI_CLOCK_CONTROL, clk, - clk & SDHCI_INTCLOCK_STABLE, + clk & SDHCI_CLOCK_INT_STABLE, 20 * USEC_PER_MSEC); if (ret) { dev_warn(host->dev, "Timeout waiting for clock stable\n"); return ret; } - clk |= SDHCI_SDCLOCK_EN; + clk |= SDHCI_CLOCK_CARD_EN; sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk); reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL); if (clock > 26000000) - reg |= SDHCI_HIGHSPEED_EN; + reg |= SDHCI_CTRL_HISPD; else - reg &= ~SDHCI_HIGHSPEED_EN; + reg &= ~SDHCI_CTRL_HISPD; sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg); @@ -299,15 +298,15 @@ static int at91_sdhci_set_bus_width(struct at91_sdhci *host, unsigned bus_width) switch(bus_width) { case MMC_BUS_WIDTH_8: - reg |= SDHCI_DATA_WIDTH_8BIT; + reg |= SDHCI_CTRL_8BITBUS; break; case MMC_BUS_WIDTH_4: - reg &= ~SDHCI_DATA_WIDTH_8BIT; - reg |= SDHCI_DATA_WIDTH_4BIT; + reg &= ~SDHCI_CTRL_8BITBUS; + reg |= SDHCI_CTRL_8BITBUS; break; default: - reg &= ~SDHCI_DATA_WIDTH_8BIT; - reg &= ~SDHCI_DATA_WIDTH_4BIT; + reg &= ~SDHCI_CTRL_8BITBUS; + reg &= ~SDHCI_CTRL_8BITBUS; } sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg); @@ -379,43 +378,7 @@ int at91_sdhci_init(struct at91_sdhci *host, u32 maxclk, return 0; } -static u32 at91_sdhci_read32(struct sdhci *sdhci, int reg) -{ - return readl(to_priv(sdhci)->base + reg); -} - -static void at91_sdhci_write32(struct sdhci *sdhci, int reg, u32 value) -{ - writel(value, to_priv(sdhci)->base + reg); -} - -static u16 at91_sdhci_read16(struct sdhci *sdhci, int reg) -{ - return readw(to_priv(sdhci)->base + reg); -} - -static void at91_sdhci_write16(struct sdhci *sdhci, int reg, u16 value) -{ - writew(value, to_priv(sdhci)->base + reg); -} - -static u8 at91_sdhci_read8(struct sdhci *sdhci, int reg) -{ - return readb(to_priv(sdhci)->base + reg); -} - -static void at91_sdhci_write8(struct sdhci *sdhci, int reg, u8 value) -{ - writeb(value, to_priv(sdhci)->base + reg); -} - void at91_sdhci_mmio_init(struct at91_sdhci *host, void __iomem *base) { - host->base = base; - host->sdhci.read8 = at91_sdhci_read8; - host->sdhci.read16 = at91_sdhci_read16; - host->sdhci.read32 = at91_sdhci_read32; - host->sdhci.write8 = at91_sdhci_write8; - host->sdhci.write16 = at91_sdhci_write16; - host->sdhci.write32 = at91_sdhci_write32; + host->sdhci.base = base; } diff --git a/drivers/mci/dove-sdhci.c b/drivers/mci/dove-sdhci.c index c734a6e0dd..e6ac769bde 100644 --- a/drivers/mci/dove-sdhci.c +++ b/drivers/mci/dove-sdhci.c @@ -20,55 +20,12 @@ 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 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 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 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 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 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 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); -} - static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask) { u16 status; @@ -197,7 +154,7 @@ static u16 dove_sdhci_get_clock_divider(struct dove_sdhci *host, u32 reqclk) { u16 div; - for (div = 1; div < SDHCI_SPEC_200_MAX_CLK_DIVIDER; div *= 2) + for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) if ((host->mci.f_max / div) <= reqclk) break; div /= 2; @@ -224,33 +181,33 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios) /* set bus width */ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) & - ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT); + ~(SDHCI_CTRL_8BITBUS | SDHCI_CTRL_8BITBUS); switch (ios->bus_width) { case MMC_BUS_WIDTH_8: - val |= SDHCI_DATA_WIDTH_8BIT; + val |= SDHCI_CTRL_8BITBUS; break; case MMC_BUS_WIDTH_4: - val |= SDHCI_DATA_WIDTH_4BIT; + val |= SDHCI_CTRL_8BITBUS; break; } if (ios->clock > 26000000) - val |= SDHCI_HIGHSPEED_EN; + val |= SDHCI_CTRL_HISPD; else - val &= ~SDHCI_HIGHSPEED_EN; + val &= ~SDHCI_CTRL_HISPD; sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val); /* set bus clock */ 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); + val = SDHCI_CLOCK_INT_EN | SDHCI_FREQ_SEL(val); sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val); /* wait for internal clock stable */ start = get_time_ns(); while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) & - SDHCI_INTCLOCK_STABLE)) { + SDHCI_CLOCK_INT_STABLE)) { if (is_timeout(start, 20 * MSECOND)) { dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n"); return; @@ -258,7 +215,7 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios) } /* enable bus clock */ - sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_SDCLOCK_EN); + sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_CLOCK_CARD_EN); } static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev) @@ -290,19 +247,18 @@ static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev) static void dove_sdhci_set_mci_caps(struct dove_sdhci *host) { - u16 caps[2]; + u32 caps; - caps[0] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES); - caps[1] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1); + caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES); - if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_180) + if (caps & SDHCI_CAN_VDD_180) host->mci.voltages |= MMC_VDD_165_195; - if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_300) + if (caps & SDHCI_CAN_VDD_300) host->mci.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; - if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_330) + if (caps & SDHCI_CAN_VDD_330) host->mci.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; - if (caps[1] & SDHCI_HOSTCAP_HIGHSPEED) + if (caps & SDHCI_CAN_DO_HISPD) host->mci.host_caps |= (MMC_CAP_MMC_HIGHSPEED_52MHZ | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED); @@ -311,7 +267,7 @@ static void dove_sdhci_set_mci_caps(struct dove_sdhci *host) mci_of_parse(&host->mci); /* limit bus widths to controller capabilities */ - if ((caps[1] & SDHCI_HOSTCAP_8BIT) == 0) + if ((caps & SDHCI_CAN_DO_8BIT) == 0) host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA; } @@ -321,7 +277,7 @@ static int dove_sdhci_probe(struct device_d *dev) int ret; host = xzalloc(sizeof(*host)); - host->base = dev_request_mem_region(dev, 0); + host->sdhci.base = dev_request_mem_region(dev, 0); host->mci.max_req_size = 0x8000; host->mci.hw_dev = dev; host->mci.send_cmd = dove_sdhci_mci_send_cmd; @@ -329,12 +285,6 @@ 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; dove_sdhci_set_mci_caps(host); diff --git a/drivers/mci/dw_mmc.c b/drivers/mci/dw_mmc.c index 7979568841..930b538adc 100644 --- a/drivers/mci/dw_mmc.c +++ b/drivers/mci/dw_mmc.c @@ -25,6 +25,7 @@ struct dwmci_host { struct mci_host mci; + struct device_d *dev; struct clk *clk_biu, *clk_ciu; void *ioaddr; unsigned int fifo_size_bytes; @@ -34,6 +35,7 @@ struct dwmci_host { int ciu_div; u32 fifoth_val; u32 pwren_value; + dma_addr_t idmac_dma; }; struct dwmci_idmac { @@ -110,12 +112,12 @@ static int dwmci_prepare_data_pio(struct dwmci_host *host, } static int dwmci_prepare_data_dma(struct dwmci_host *host, - struct mci_data *data) + struct mci_data *data, dma_addr_t dma) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; - unsigned start_addr; struct dwmci_idmac *desc = host->idmac; + dma_addr_t desc_dma = host->idmac_dma; blk_cnt = data->blocks; @@ -124,12 +126,7 @@ static int dwmci_prepare_data_dma(struct dwmci_host *host, dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); - dwmci_writel(host, DWMCI_DBADDR, (uint32_t)desc); - - if (data->flags & MMC_DATA_READ) - start_addr = (uint32_t)data->dest; - else - start_addr = (uint32_t)data->src; + dwmci_writel(host, DWMCI_DBADDR, desc_dma); do { flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH; @@ -142,10 +139,12 @@ static int dwmci_prepare_data_dma(struct dwmci_host *host, cnt = data->blocksize * 8; } + desc_dma += sizeof(*desc); + desc->flags = flags; desc->cnt = cnt; - desc->addr = start_addr + (i * PAGE_SIZE); - desc->next_addr = (uint32_t)(desc + 1); + desc->addr = dma + (i * PAGE_SIZE); + desc->next_addr = desc_dma; dev_dbg(host->mci.hw_dev, "desc@ 0x%p 0x%08x 0x%08x 0x%08x 0x%08x\n", desc, flags, cnt, desc->addr, desc->next_addr); @@ -172,12 +171,12 @@ static int dwmci_prepare_data_dma(struct dwmci_host *host, } static int dwmci_prepare_data(struct dwmci_host *host, - struct mci_data *data) + struct mci_data *data, dma_addr_t dma) { if (dwmci_use_pio(host)) return dwmci_prepare_data_pio(host, data); else - return dwmci_prepare_data_dma(host, data); + return dwmci_prepare_data_dma(host, data, dma); } static int dwmci_set_transfer_mode(struct dwmci_host *host, @@ -272,6 +271,7 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) uint64_t start; int ret; unsigned int num_bytes = 0; + dma_addr_t dma = 0; start = get_time_ns(); while (1) { @@ -287,16 +287,20 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); if (data) { + num_bytes = data->blocks * data->blocksize; if (data->flags & MMC_DATA_WRITE) - dma_sync_single_for_device((unsigned long)data->src, - num_bytes, DMA_TO_DEVICE); + dma = dma_map_single(host->dev, (void *)data->src, num_bytes, + DMA_TO_DEVICE); else - dma_sync_single_for_device((unsigned long)data->dest, - num_bytes, DMA_FROM_DEVICE); + dma = dma_map_single(host->dev, data->dest, num_bytes, + DMA_FROM_DEVICE); - ret = dwmci_prepare_data(host, data); + if (dma_mapping_error(host->dev, dma)) + return -EFAULT; + + ret = dwmci_prepare_data(host, data, dma); if (ret) return ret; } @@ -400,11 +404,11 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) dwmci_writel(host, DWMCI_CTRL, ctrl); if (data->flags & MMC_DATA_WRITE) - dma_sync_single_for_cpu((unsigned long)data->src, - num_bytes, DMA_TO_DEVICE); + dma_unmap_single(host->dev, dma, num_bytes, + DMA_TO_DEVICE); else - dma_sync_single_for_cpu((unsigned long)data->dest, - num_bytes, DMA_FROM_DEVICE); + dma_unmap_single(host->dev, dma, num_bytes, + DMA_FROM_DEVICE); } } @@ -550,6 +554,9 @@ static int dw_mmc_probe(struct device_d *dev) host = xzalloc(sizeof(*host)); + dma_set_mask(dev, DMA_BIT_MASK(32)); + host->dev = dev; + host->clk_biu = clk_get(dev, "biu"); if (IS_ERR(host->clk_biu)) return PTR_ERR(host->clk_biu); @@ -567,7 +574,9 @@ static int dw_mmc_probe(struct device_d *dev) host->ioaddr = IOMEM(iores->start); host->idmac = dma_alloc_coherent(sizeof(*host->idmac) * DW_MMC_NUM_IDMACS, - DMA_ADDRESS_BROKEN); + &host->idmac_dma); + if (!host->idmac) + return -ENOMEM; host->mci.send_cmd = dwmci_cmd; host->mci.set_ios = dwmci_set_ios; diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c index 7ee0674b99..77d7e4d478 100644 --- a/drivers/mci/imx-esdhc-common.c +++ b/drivers/mci/imx-esdhc-common.c @@ -10,48 +10,41 @@ #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) +static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg) { struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci); - return readl(host->regs + reg); + return in_be32(host->sdhci.base + reg); } -static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg) +static void esdhc_op_write32_be(struct sdhci *sdhci, int reg, u32 val) { struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci); - return in_be32(host->regs + reg); + out_be32(host->sdhci.base + reg, val); } -static void esdhc_op_write32_le(struct sdhci *sdhci, int reg, u32 val) +static u16 esdhc_op_read16_be(struct sdhci *sdhci, int reg) { struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci); - writel(val, host->regs + reg); + return in_be16(host->sdhci.base + reg); } -static void esdhc_op_write32_be(struct sdhci *sdhci, int reg, u32 val) +static void esdhc_op_write16_be(struct sdhci *sdhci, int reg, u16 val) { struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci); - out_be32(host->regs + reg, val); + out_be16(host->sdhci.base + reg, val); } void esdhc_populate_sdhci(struct fsl_esdhc_host *host) { if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN) { + host->sdhci.read16 = esdhc_op_read16_be; + host->sdhci.write16 = esdhc_op_write16_be; 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; } } @@ -59,71 +52,33 @@ 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) + dma_addr_t *dma) { 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; - } + wml_value = data->blocksize / 4; - tr->dma = dma_map_single(host->dev, ptr, tr->size, tr->dir); - if (dma_mapping_error(host->dev, tr->dma)) - return -EFAULT; + 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; - sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, tr->dma); + esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_WR_WML_MASK, + wml_value << 16); } - 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; + host->sdhci.sdma_boundary = 0; 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); + sdhci_setup_data_pio(&host->sdhci, data); + else + sdhci_setup_data_dma(&host->sdhci, data, dma); return 0; } @@ -175,7 +130,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd, { u32 xfertyp, mixctrl, command; u32 irqstat; - struct fsl_esdhc_dma_transfer tr = { 0 }; + dma_addr_t dma = SDHCI_NO_DMA; int ret; sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1); @@ -185,13 +140,13 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd, /* Set up for a data transfer if we have one */ if (data) { - ret = esdhc_setup_data(host, data, &tr); + ret = esdhc_setup_data(host, data, &dma); if (ret) return ret; } sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, - !esdhc_use_pio_mode(), &command, &xfertyp); + dma != SDHCI_NO_DMA, &command, &xfertyp); if ((host->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)) @@ -248,7 +203,11 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd, /* Wait until all of the blocks are transferred */ if (data) { - ret = esdhc_do_data(host, data, &tr); + if (esdhc_use_pio_mode()) + ret = sdhci_transfer_data_pio(&host->sdhci, data); + else + ret = sdhci_transfer_data_dma(&host->sdhci, data, dma); + if (ret) return ret; } diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c index c3a8b377e2..d7136f5492 100644 --- a/drivers/mci/imx-esdhc-pbl.c +++ b/drivers/mci/imx-esdhc-pbl.c @@ -200,14 +200,14 @@ static int imx8m_esdhc_init(struct fsl_esdhc_host *host, { switch (instance) { case 0: - host->regs = IOMEM(MX8M_USDHC1_BASE_ADDR); + host->sdhci.base = IOMEM(MX8M_USDHC1_BASE_ADDR); break; case 1: - host->regs = IOMEM(MX8M_USDHC2_BASE_ADDR); + host->sdhci.base = IOMEM(MX8M_USDHC2_BASE_ADDR); break; case 2: /* Only exists on i.MX8MM, not on i.MX8MQ */ - host->regs = IOMEM(MX8MM_USDHC3_BASE_ADDR); + host->sdhci.base = IOMEM(MX8MM_USDHC3_BASE_ADDR); break; default: return -EINVAL; @@ -237,16 +237,16 @@ int imx6_esdhc_start_image(int instance) switch (instance) { case 0: - host.regs = IOMEM(MX6_USDHC1_BASE_ADDR); + host.sdhci.base = IOMEM(MX6_USDHC1_BASE_ADDR); break; case 1: - host.regs = IOMEM(MX6_USDHC2_BASE_ADDR); + host.sdhci.base = IOMEM(MX6_USDHC2_BASE_ADDR); break; case 2: - host.regs = IOMEM(MX6_USDHC3_BASE_ADDR); + host.sdhci.base = IOMEM(MX6_USDHC3_BASE_ADDR); break; case 3: - host.regs = IOMEM(MX6_USDHC4_BASE_ADDR); + host.sdhci.base = IOMEM(MX6_USDHC4_BASE_ADDR); break; default: return -EINVAL; @@ -276,13 +276,13 @@ int imx7_esdhc_start_image(int instance) switch (instance) { case 0: - host.regs = IOMEM(MX7_USDHC1_BASE_ADDR); + host.sdhci.base = IOMEM(MX7_USDHC1_BASE_ADDR); break; case 1: - host.regs = IOMEM(MX7_USDHC2_BASE_ADDR); + host.sdhci.base = IOMEM(MX7_USDHC2_BASE_ADDR); break; case 2: - host.regs = IOMEM(MX7_USDHC3_BASE_ADDR); + host.sdhci.base = IOMEM(MX7_USDHC3_BASE_ADDR); break; default: return -EINVAL; @@ -375,7 +375,7 @@ int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long .flags = ESDHC_FLAG_BIGENDIAN, }; struct fsl_esdhc_host host = { - .regs = IOMEM(0x01560000), + .sdhci.base = IOMEM(0x01560000), .socdata = &data, }; unsigned long sdram = 0x80000000; diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index 93f3c57d4e..5a664ce4c3 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -230,7 +230,6 @@ static int fsl_esdhc_probe(struct device_d *dev) struct resource *iores; struct fsl_esdhc_host *host; struct mci_host *mci; - u32 caps; int ret; unsigned long rate; struct esdhc_platform_data *pdata = dev->platform_data; @@ -265,33 +264,26 @@ static int fsl_esdhc_probe(struct device_d *dev) ret = PTR_ERR(iores); goto err_clk_disable; } - host->regs = IOMEM(iores->start); + host->sdhci.base = IOMEM(iores->start); esdhc_populate_sdhci(host); - caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES); - - if (caps & ESDHC_HOSTCAPBLT_VS18) - mci->voltages |= MMC_VDD_165_195; - if (caps & ESDHC_HOSTCAPBLT_VS30) - mci->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; - if (caps & ESDHC_HOSTCAPBLT_VS33) - mci->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; - if (pdata) { mci->host_caps = pdata->caps; if (pdata->devname) mci->devname = pdata->devname; } - if (caps & ESDHC_HOSTCAPBLT_HSS) - mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; - host->mci.send_cmd = esdhc_send_cmd; host->mci.set_ios = esdhc_set_ios; host->mci.init = esdhc_init; host->mci.card_present = esdhc_card_present; host->mci.hw_dev = dev; + host->sdhci.mci = &host->mci; + + ret = sdhci_setup_host(&host->sdhci); + if (ret) + goto err_clk_disable; rate = clk_get_rate(host->clk); host->mci.f_min = rate >> 12; diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h index 8abe1207d7..f1685eac06 100644 --- a/drivers/mci/imx-esdhc.h +++ b/drivers/mci/imx-esdhc.h @@ -37,13 +37,6 @@ #define BLKATTR_SIZE(x) (x & 0x1fff) #define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */ -#define ESDHC_HOSTCAPBLT_VS18 0x04000000 -#define ESDHC_HOSTCAPBLT_VS30 0x02000000 -#define ESDHC_HOSTCAPBLT_VS33 0x01000000 -#define ESDHC_HOSTCAPBLT_SRS 0x00800000 -#define ESDHC_HOSTCAPBLT_DMAS 0x00400000 -#define ESDHC_HOSTCAPBLT_HSS 0x00200000 - #define PIO_TIMEOUT 100000 #define IMX_SDHCI_WML 0x44 @@ -108,7 +101,6 @@ 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; }; diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c index 91027857be..0450f899c6 100644 --- a/drivers/mci/mci-bcm2835.c +++ b/drivers/mci/mci-bcm2835.c @@ -172,7 +172,7 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd, } if (!ret && data) - ret = sdhci_transfer_data(&host->sdhci, data); + ret = sdhci_transfer_data_pio(&host->sdhci, data); sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF); if (ret) { diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index a160b98894..a094f3cbf5 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1358,7 +1358,8 @@ static int __maybe_unused mci_sd_write(struct block_device *blk, mci_blk_part_switch(part); - if (host->card_write_protected && host->card_write_protected(host)) { + if (!host->disable_wp && + host->card_write_protected && host->card_write_protected(host)) { dev_err(&mci->dev, "card write protected\n"); return -EPERM; } @@ -2016,6 +2017,7 @@ void mci_of_parse_node(struct mci_host *host, host->non_removable = of_property_read_bool(np, "non-removable"); host->no_sd = of_property_read_bool(np, "no-sd"); + host->disable_wp = of_property_read_bool(np, "disable-wp"); } void mci_of_parse(struct mci_host *host) diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c new file mode 100644 index 0000000000..164f662552 --- /dev/null +++ b/drivers/mci/rockchip-dwcmshc-sdhci.c @@ -0,0 +1,377 @@ +// 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 <dma.h> +#include <linux/iopoll.h> + +#include "sdhci.h" + +/* DWCMSHC specific Mode Select value */ +#define DWCMSHC_CTRL_HS400 0x7 + +#define DWCMSHC_VER_ID 0x500 +#define DWCMSHC_VER_TYPE 0x504 +#define DWCMSHC_HOST_CTRL3 0x508 +#define DWCMSHC_EMMC_CONTROL 0x52c +#define DWCMSHC_EMMC_ATCTRL 0x540 + +/* Rockchip specific Registers */ +#define DWCMSHC_EMMC_DLL_CTRL 0x800 +#define DWCMSHC_EMMC_DLL_RXCLK 0x804 +#define DWCMSHC_EMMC_DLL_TXCLK 0x808 +#define DWCMSHC_EMMC_DLL_STRBIN 0x80c +#define DWCMSHC_EMMC_DLL_STATUS0 0x840 +#define DWCMSHC_EMMC_DLL_START BIT(0) +#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29 +#define DWCMSHC_EMMC_DLL_START_POINT 16 +#define DWCMSHC_EMMC_DLL_INC 8 +#define DWCMSHC_EMMC_DLL_DLYENA BIT(27) +#define DLL_TXCLK_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) +#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) +#define DWCMSHC_EMMC_DLL_LOCKED BIT(8) +#define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) +#define DLL_RXCLK_NO_INVERTER 1 +#define DLL_RXCLK_INVERTER 0 +#define DWCMSHC_ENHANCED_STROBE BIT(8) +#define DLL_LOCK_WO_TMOUT(x) \ + ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ + (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) + +#define SDHCI_DWCMSHC_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 + +#define SDHCI_DWCMSHC_INT_CMD_MASK SDHCI_INT_CMD_COMPLETE | \ + SDHCI_INT_TIMEOUT | \ + SDHCI_INT_CRC | \ + SDHCI_INT_END_BIT | \ + SDHCI_INT_INDEX + +enum { + CLK_CORE, + CLK_BUS, + CLK_AXI, + CLK_BLOCK, + CLK_TIMER, + CLK_MAX, +}; + +struct rk_sdhci_host { + struct mci_host mci; + struct sdhci sdhci; + struct clk_bulk_data clks[CLK_MAX]; +}; + + +static inline +struct rk_sdhci_host *to_rk_sdhci_host(struct mci_host *mci) +{ + return container_of(mci, struct rk_sdhci_host, mci); +} + +static int rk_sdhci_card_present(struct mci_host *mci) +{ + struct rk_sdhci_host *host = to_rk_sdhci_host(mci); + + return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT); +} + +static int rk_sdhci_reset(struct rk_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; + } + + return 0; +} + +static int rk_sdhci_init(struct mci_host *mci, struct device_d *dev) +{ + struct rk_sdhci_host *host = to_rk_sdhci_host(mci); + int ret; + + ret = rk_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_DWCMSHC_INT_DATA_MASK | + SDHCI_DWCMSHC_INT_CMD_MASK); + sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0x00); + + /* Disable cmd conflict check */ + sdhci_write32(&host->sdhci, DWCMSHC_HOST_CTRL3, 0x0); + /* Reset previous settings */ + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_TXCLK, 0); + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_STRBIN, 0); + + return 0; +} + +static void rk_sdhci_set_clock(struct rk_sdhci_host *host, unsigned int clock) +{ + u32 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, extra; + int err; + + host->mci.clock = 0; + + /* DO NOT TOUCH THIS SETTING */ + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_RXCLK, extra); + + if (clock == 0) + return; + + /* Rockchip platform only support 375KHz for identify mode */ + if (clock <= 400000) + clock = 375000; + + clk_set_rate(host->clks[CLK_CORE].clk, clock); + + sdhci_set_clock(&host->sdhci, clock, clk_get_rate(host->clks[CLK_CORE].clk)); + + if (clock <= 400000) + return; + + /* Reset DLL */ + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, BIT(1)); + udelay(1); + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, 0x0); + + /* Init DLL settings */ + extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT | + 0x2 << DWCMSHC_EMMC_DLL_INC | + DWCMSHC_EMMC_DLL_START; + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, extra); + err = readl_poll_timeout(host->sdhci.base + DWCMSHC_EMMC_DLL_STATUS0, + extra, DLL_LOCK_WO_TMOUT(extra), + 500 * USEC_PER_MSEC); + if (err) { + dev_err(host->mci.hw_dev, "DLL lock timeout!\n"); + return; + } + + /* Disable cmd conflict check */ + extra = sdhci_read32(&host->sdhci, DWCMSHC_HOST_CTRL3); + extra &= ~BIT(0); + sdhci_write32(&host->sdhci, DWCMSHC_HOST_CTRL3, extra); + + extra = 0x1 << 16 | /* tune clock stop en */ + 0x2 << 17 | /* pre-change delay */ + 0x3 << 19; /* post-change delay */ + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_ATCTRL, extra); + + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_TXCLK_TAPNUM_FROM_SW | + txclk_tapnum; + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_TXCLK, extra); + + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_STRBIN_TAPNUM_DEFAULT | + DLL_STRBIN_TAPNUM_FROM_SW; + sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_STRBIN, extra); +} + +static void rk_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios) +{ + struct rk_sdhci_host *host = to_rk_sdhci_host(mci); + u16 val; + + /* stop clock */ + sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0); + + if (ios->clock) + rk_sdhci_set_clock(host, ios->clock); + + sdhci_set_bus_width(&host->sdhci, ios->bus_width); + + val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL); + + if (ios->clock > 26000000) + val |= SDHCI_CTRL_HISPD; + else + val &= ~SDHCI_CTRL_HISPD; + + sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val); +} + +static int rk_sdhci_wait_for_done(struct rk_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 rk_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 rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, + struct mci_data *data) +{ + struct rk_sdhci_host *host = to_rk_sdhci_host(mci); + u32 mask, command, xfer; + int ret; + dma_addr_t dma; + + /* 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); + + sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe); + + sdhci_setup_data_dma(&host->sdhci, data, &dma); + + sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, + dma == SDHCI_NO_DMA ? false : true, + &command, &xfer); + + sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer); + + sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg); + sdhci_write16(&host->sdhci, SDHCI_COMMAND, command); + + ret = rk_sdhci_wait_for_done(host, SDHCI_INT_CMD_COMPLETE); + if (ret == -EPERM) + goto error; + else if (ret) + return ret; + + sdhci_read_response(&host->sdhci, cmd); + sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE); + + ret = sdhci_transfer_data_dma(&host->sdhci, data, dma); + +error: + if (ret) { + print_error(host, cmd->cmdidx); + rk_sdhci_reset(host, BIT(1)); /* SDHCI_RESET_CMD */ + rk_sdhci_reset(host, BIT(2)); /* SDHCI_RESET_DATA */ + } + + sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); + return ret; +} + +static int rk_sdhci_probe(struct device_d *dev) +{ + struct rk_sdhci_host *host; + struct resource *iores; + struct mci_host *mci; + int ret; + + host = xzalloc(sizeof(*host)); + + mci = &host->mci; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + host->sdhci.base = IOMEM(iores->start); + host->sdhci.mci = mci; + mci->send_cmd = rk_sdhci_send_cmd; + mci->set_ios = rk_sdhci_set_ios; + mci->init = rk_sdhci_init; + mci->card_present = rk_sdhci_card_present; + mci->hw_dev = dev; + + host->clks[CLK_CORE].id = "core"; + host->clks[CLK_BUS].id = "bus"; + host->clks[CLK_AXI].id = "axi"; + host->clks[CLK_BLOCK].id = "block"; + host->clks[CLK_TIMER].id = "timer"; + + ret = clk_bulk_get(host->mci.hw_dev, CLK_MAX, host->clks); + if (ret) { + dev_err(host->mci.hw_dev, "failed to get clocks: %s\n", + strerror(-ret)); + return ret; + } + + ret = clk_bulk_enable(CLK_MAX, host->clks); + if (ret) { + dev_err(host->mci.hw_dev, "failed to enable clocks: %s\n", + strerror(-ret)); + return ret; + } + + host->sdhci.max_clk = clk_get_rate(host->clks[CLK_CORE].clk); + + mci_of_parse(&host->mci); + + sdhci_setup_host(&host->sdhci); + + dev->priv = host; + + return mci_register(&host->mci); +} + +static __maybe_unused struct of_device_id rk_sdhci_compatible[] = { + { + .compatible = "rockchip,rk3568-dwcmshc" + }, { + /* sentinel */ + } +}; + +static struct driver_d rk_sdhci_driver = { + .name = "rk3568-dwcmshc-sdhci", + .probe = rk_sdhci_probe, + .of_compatible = DRV_OF_COMPAT(rk_sdhci_compatible), +}; +device_platform_driver(rk_sdhci_driver); diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c index dba26b2665..aca4a5a6f9 100644 --- a/drivers/mci/sdhci.c +++ b/drivers/mci/sdhci.c @@ -4,6 +4,8 @@ #include <driver.h> #include <mci.h> #include <io.h> +#include <dma.h> +#include <linux/bitfield.h> #include "sdhci.h" @@ -88,6 +90,27 @@ static void sdhci_tx_pio(struct sdhci *sdhci, struct mci_data *data, sdhci_write32(sdhci, SDHCI_BUFFER, buf[i]); } +void sdhci_set_bus_width(struct sdhci *host, int width) +{ + u8 ctrl; + + BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */ + + ctrl = sdhci_read8(host, SDHCI_HOST_CONTROL); + if (width == MMC_BUS_WIDTH_8) { + ctrl &= ~SDHCI_CTRL_4BITBUS; + ctrl |= SDHCI_CTRL_8BITBUS; + } else { + if (host->mci->host_caps & MMC_CAP_8_BIT_DATA) + ctrl &= ~SDHCI_CTRL_8BITBUS; + if (width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + else + ctrl &= ~SDHCI_CTRL_4BITBUS; + } + sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl); +} + #ifdef __PBL__ /* * Stubs to make timeout logic below work in PBL @@ -101,12 +124,112 @@ static void sdhci_tx_pio(struct sdhci *sdhci, struct mci_data *data, #endif -int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data) +void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data) +{ + if (!data) + return; + + sdhci_write16(sdhci, SDHCI_BLOCK_SIZE, sdhci->sdma_boundary | + SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize)); + sdhci_write16(sdhci, SDHCI_BLOCK_COUNT, data->blocks); +} + +void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, + dma_addr_t *dma) +{ + struct device_d *dev = sdhci->mci->hw_dev; + int nbytes; + + if (!data) + return; + + sdhci_setup_data_pio(sdhci, data); + + if (!dma) + return; + + nbytes = data->blocks * data->blocksize; + + if (data->flags & MMC_DATA_READ) + *dma = dma_map_single(dev, (void *)data->src, nbytes, + DMA_FROM_DEVICE); + else + *dma = dma_map_single(dev, data->dest, nbytes, + DMA_TO_DEVICE); + + if (dma_mapping_error(dev, *dma)) { + *dma = SDHCI_NO_DMA; + return; + } + + sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, *dma); +} + +int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data, + dma_addr_t dma) +{ + struct device_d *dev = sdhci->mci->hw_dev; + int nbytes; + u32 irqstat; + int ret; + + if (!data) + return 0; + + nbytes = data->blocks * data->blocksize; + + do { + irqstat = sdhci_read32(sdhci, SDHCI_INT_STATUS); + + if (irqstat & SDHCI_INT_DATA_END_BIT) { + ret = -EIO; + goto out; + } + + if (irqstat & SDHCI_INT_DATA_CRC) { + ret = -EBADMSG; + goto out; + } + + if (irqstat & SDHCI_INT_DATA_TIMEOUT) { + ret = -ETIMEDOUT; + goto out; + } + + if (irqstat & SDHCI_INT_DMA) { + u32 addr = sdhci_read32(sdhci, SDHCI_DMA_ADDRESS); + + /* + * DMA engine has stopped on buffer boundary. Acknowledge + * the interrupt and kick the DMA engine again. + */ + sdhci_write32(sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA); + sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, addr); + } + + if (irqstat & SDHCI_INT_XFER_COMPLETE) + break; + } while (1); + + ret = 0; +out: + if (data->flags & MMC_DATA_READ) + dma_unmap_single(dev, dma, nbytes, DMA_FROM_DEVICE); + else + dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE); + + return 0; +} + +int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data) { unsigned int block = 0; u32 stat, prs; uint64_t start = get_time_ns(); + if (!data) + return 0; + do { stat = sdhci_read32(sdhci, SDHCI_INT_STATUS); if (stat & SDHCI_INT_ERROR) @@ -139,6 +262,19 @@ int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data) return 0; } +int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma) +{ + struct device_d *dev = sdhci->mci->hw_dev; + + if (!data) + return 0; + + if (dma_mapping_error(dev, dma)) + return sdhci_transfer_data_pio(sdhci, data); + else + return sdhci_transfer_data_dma(sdhci, data, dma); +} + int sdhci_reset(struct sdhci *sdhci, u8 mask) { u8 val; @@ -149,3 +285,264 @@ int sdhci_reset(struct sdhci *sdhci, u8 mask) val, !(val & mask), 100 * USEC_PER_MSEC); } + +static u16 sdhci_get_preset_value(struct sdhci *host) +{ + u16 preset = 0; + + BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */ + + switch (host->timing) { + case MMC_TIMING_UHS_SDR12: + preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR12); + break; + case MMC_TIMING_UHS_SDR25: + preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR25); + break; + case MMC_TIMING_UHS_SDR50: + preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR50); + break; + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR104); + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: + preset = sdhci_read16(host, SDHCI_PRESET_FOR_DDR50); + break; + case MMC_TIMING_MMC_HS400: + preset = sdhci_read16(host, SDHCI_PRESET_FOR_HS400); + break; + default: + dev_warn(host->mci->hw_dev, "Invalid UHS-I mode selected\n"); + preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR12); + break; + } + return preset; +} + +u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock, + unsigned int *actual_clock, unsigned int input_clock) +{ + int div = 0; /* Initialized for compiler warning */ + int real_div = div, clk_mul = 1; + u16 clk = 0; + bool switch_base_clk = false; + + BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */ + + if (host->version >= SDHCI_SPEC_300) { + if (host->preset_enabled) { + u16 pre_val; + + clk = sdhci_read16(host, SDHCI_CLOCK_CONTROL); + pre_val = sdhci_get_preset_value(host); + div = FIELD_GET(SDHCI_PRESET_SDCLK_FREQ_MASK, pre_val); + if (host->clk_mul && + (pre_val & SDHCI_PRESET_CLKGEN_SEL)) { + clk = SDHCI_PROG_CLOCK_MODE; + real_div = div + 1; + clk_mul = host->clk_mul; + } else { + real_div = max_t(int, 1, div << 1); + } + goto clock_set; + } + + /* + * Check if the Host Controller supports Programmable Clock + * Mode. + */ + if (host->clk_mul) { + for (div = 1; div <= 1024; div++) { + if ((input_clock * host->clk_mul / div) + <= clock) + break; + } + if ((input_clock * host->clk_mul / div) <= clock) { + /* + * Set Programmable Clock Mode in the Clock + * Control register. + */ + clk = SDHCI_PROG_CLOCK_MODE; + real_div = div; + clk_mul = host->clk_mul; + div--; + } else { + /* + * Divisor can be too small to reach clock + * speed requirement. Then use the base clock. + */ + switch_base_clk = true; + } + } + + if (!host->clk_mul || switch_base_clk) { + /* Version 3.00 divisors must be a multiple of 2. */ + if (input_clock <= clock) + div = 1; + else { + for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; + div += 2) { + if ((input_clock / div) <= clock) + break; + } + } + real_div = div; + div >>= 1; + if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN) + && !div && input_clock <= 25000000) + div = 1; + } + } else { + /* Version 2.00 divisors must be a power of 2. */ + for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { + if ((input_clock / div) <= clock) + break; + } + real_div = div; + div >>= 1; + } + +clock_set: + if (real_div) + *actual_clock = (input_clock * clk_mul) / real_div; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) + << SDHCI_DIVIDER_HI_SHIFT; + + return clk; +} + +void sdhci_enable_clk(struct sdhci *host, u16 clk) +{ + u64 start; + + BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */ + + clk |= SDHCI_CLOCK_INT_EN; + sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk); + + start = get_time_ns(); + while (!(sdhci_read16(host, SDHCI_CLOCK_CONTROL) & + SDHCI_CLOCK_INT_STABLE)) { + if (is_timeout(start, 150 * MSECOND)) { + dev_err(host->mci->hw_dev, + "SDHCI clock stable timeout\n"); + return; + } + } + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk); +} + +void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock) +{ + u16 clk; + + BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */ + + host->mci->clock = 0; + + sdhci_write16(host, SDHCI_CLOCK_CONTROL, 0); + + if (clock == 0) + return; + + clk = sdhci_calc_clk(host, clock, &host->mci->clock, input_clock); + sdhci_enable_clk(host, clk); +} + +void __sdhci_read_caps(struct sdhci *host, const u16 *ver, + const u32 *caps, const u32 *caps1) +{ + u16 v; + u64 dt_caps_mask = 0; + u64 dt_caps = 0; + struct device_node *np = host->mci->hw_dev->device_node; + + BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */ + + if (host->read_caps) + return; + + host->read_caps = true; + + sdhci_reset(host, SDHCI_RESET_ALL); + + of_property_read_u64(np, "sdhci-caps-mask", &dt_caps_mask); + of_property_read_u64(np, "sdhci-caps", &dt_caps); + + v = ver ? *ver : sdhci_read16(host, SDHCI_HOST_VERSION); + host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; + + if (host->quirks & SDHCI_QUIRK_MISSING_CAPS) + return; + + if (caps) { + host->caps = *caps; + } else { + host->caps = sdhci_read32(host, SDHCI_CAPABILITIES); + host->caps &= ~lower_32_bits(dt_caps_mask); + host->caps |= lower_32_bits(dt_caps); + } + + if (host->version < SDHCI_SPEC_300) + return; + + if (caps1) { + host->caps1 = *caps1; + } else { + host->caps1 = sdhci_read32(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~upper_32_bits(dt_caps_mask); + host->caps1 |= upper_32_bits(dt_caps); + } +} + +int sdhci_setup_host(struct sdhci *host) +{ + struct mci_host *mci = host->mci; + + BUG_ON(!mci); + + sdhci_read_caps(host); + + if (!host->max_clk) { + if (host->version >= SDHCI_SPEC_300) + host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps); + else + host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps); + + host->max_clk *= 1000000; + } + + /* + * In case of Host Controller v3.00, find out whether clock + * multiplier is supported. + */ + host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1); + + /* + * In case the value in Clock Multiplier is 0, then programmable + * clock mode is not supported, otherwise the actual clock + * multiplier is one more than the value of Clock Multiplier + * in the Capabilities Register. + */ + if (host->clk_mul) + host->clk_mul += 1; + + if (host->caps & SDHCI_CAN_VDD_180) + mci->voltages |= MMC_VDD_165_195; + if (host->caps & SDHCI_CAN_VDD_300) + mci->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; + if (host->caps & SDHCI_CAN_VDD_330) + mci->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; + + if (host->caps & SDHCI_CAN_DO_HISPD) + mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + + host->sdma_boundary = SDHCI_DMA_BOUNDARY_512K; + + return 0; +} diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h index 7b3f64486f..351940a511 100644 --- a/drivers/mci/sdhci.h +++ b/drivers/mci/sdhci.h @@ -2,6 +2,7 @@ #define __MCI_SDHCI_H #include <pbl.h> +#include <dma.h> #include <linux/iopoll.h> #define SDHCI_DMA_ADDRESS 0x00 @@ -54,11 +55,18 @@ #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) +#define SDHCI_CTRL_LED BIT(0) +#define SDHCI_CTRL_4BITBUS BIT(1) +#define SDHCI_CTRL_HISPD BIT(2) +#define SDHCI_CTRL_DMA_MASK 0x18 +#define SDHCI_CTRL_SDMA 0x00 +#define SDHCI_CTRL_ADMA1 0x08 +#define SDHCI_CTRL_ADMA32 0x10 +#define SDHCI_CTRL_ADMA64 0x18 +#define SDHCI_CTRL_ADMA3 0x18 +#define SDHCI_CTRL_8BITBUS BIT(5) +#define SDHCI_CTRL_CDTEST_INS BIT(6) +#define SDHCI_CTRL_CDTEST_EN BIT(7) #define SDHCI_POWER_CONTROL 0x29 #define SDHCI_POWER_ON 0x01 #define SDHCI_POWER_180 0x0A @@ -68,15 +76,19 @@ #define SDHCI_BUS_VOLTAGE(v) ((v) << 1) #define SDHCI_BUS_POWER_EN BIT(0) #define SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET 0x2c -#define SDHCI_CLOCK_CONTROL 0x2c +#define SDHCI_CLOCK_CONTROL 0x2C +#define SDHCI_DIVIDER_SHIFT 8 #define SDHCI_DIVIDER_HI_SHIFT 6 +#define SDHCI_DIV_MASK 0xFF #define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_DIV_MASK_LEN 8 -#define SDHCI_FREQ_SEL(x) (((x) & 0xff) << 8) +#define SDHCI_FREQ_SEL(x) (((x) & 0xff) << 8) +#define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_PROG_CLOCK_MODE BIT(5) -#define SDHCI_SDCLOCK_EN BIT(2) -#define SDHCI_INTCLOCK_STABLE BIT(1) -#define SDHCI_INTCLOCK_EN BIT(0) +#define SDHCI_CLOCK_CARD_EN BIT(2) +#define SDHCI_CLOCK_PLL_EN BIT(3) +#define SDHCI_CLOCK_INT_STABLE BIT(1) +#define SDHCI_CLOCK_INT_EN BIT(0) #define SDHCI_TIMEOUT_CONTROL 0x2e #define SDHCI_SOFTWARE_RESET 0x2f #define SDHCI_RESET_ALL BIT(0) @@ -105,19 +117,66 @@ #define SDHCI_SIGNAL_ENABLE 0x38 #define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C #define SDHCI_CAPABILITIES 0x40 -#define SDHCI_CAPABILITIES_1 0x42 -#define SDHCI_HOSTCAP_VOLTAGE_180 BIT(10) -#define SDHCI_HOSTCAP_VOLTAGE_300 BIT(9) -#define SDHCI_HOSTCAP_VOLTAGE_330 BIT(8) -#define SDHCI_HOSTCAP_HIGHSPEED BIT(5) -#define SDHCI_HOSTCAP_8BIT BIT(2) - -#define SDHCI_CLOCK_MUL_MASK 0x00FF0000 +#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0) +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8) +#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8) +#define SDHCI_MAX_BLOCK_MASK 0x00030000 +#define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_8BIT 0x00040000 +#define SDHCI_CAN_DO_ADMA2 0x00080000 +#define SDHCI_CAN_DO_ADMA1 0x00100000 +#define SDHCI_CAN_DO_HISPD 0x00200000 +#define SDHCI_CAN_DO_SDMA 0x00400000 +#define SDHCI_CAN_DO_SUSPEND 0x00800000 +#define SDHCI_CAN_VDD_330 0x01000000 +#define SDHCI_CAN_VDD_300 0x02000000 +#define SDHCI_CAN_VDD_180 0x04000000 +#define SDHCI_CAN_64BIT_V4 0x08000000 +#define SDHCI_CAN_64BIT 0x10000000 + +#define SDHCI_CAPABILITIES_1 0x44 +#define SDHCI_SUPPORT_SDR50 0x00000001 +#define SDHCI_SUPPORT_SDR104 0x00000002 +#define SDHCI_SUPPORT_DDR50 0x00000004 +#define SDHCI_DRIVER_TYPE_A 0x00000010 +#define SDHCI_DRIVER_TYPE_C 0x00000020 +#define SDHCI_DRIVER_TYPE_D 0x00000040 +#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8) +#define SDHCI_USE_SDR50_TUNING 0x00002000 +#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14) +#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16) +#define SDHCI_CAN_DO_ADMA3 0x08000000 +#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */ + +#define SDHCI_PRESET_FOR_SDR12 0x66 +#define SDHCI_PRESET_FOR_SDR25 0x68 +#define SDHCI_PRESET_FOR_SDR50 0x6A +#define SDHCI_PRESET_FOR_SDR104 0x6C +#define SDHCI_PRESET_FOR_DDR50 0x6E +#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ +#define SDHCI_PRESET_CLKGEN_SEL BIT(10) +#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0) + +#define SDHCI_HOST_VERSION 0xFE +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 +#define SDHCI_SPEC_100 0 +#define SDHCI_SPEC_200 1 +#define SDHCI_SPEC_300 2 +#define SDHCI_SPEC_400 3 +#define SDHCI_SPEC_410 4 +#define SDHCI_SPEC_420 5 + #define SDHCI_CLOCK_MUL_SHIFT 16 -#define SDHCI_SPEC_200_MAX_CLK_DIVIDER 256 #define SDHCI_MMC_BOOT 0xC4 +#define SDHCI_MAX_DIV_SPEC_200 256 +#define SDHCI_MAX_DIV_SPEC_300 2046 + struct sdhci { u32 (*read32)(struct sdhci *host, int reg); u16 (*read16)(struct sdhci *host, int reg); @@ -125,44 +184,101 @@ struct sdhci { 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); + + void __iomem *base; + + int max_clk; /* Max possible freq (Hz) */ + int clk_mul; /* Clock Muliplier value */ + + unsigned int version; /* SDHCI spec. version */ + + enum mci_timing timing; + bool preset_enabled; /* Preset is enabled */ + + unsigned int quirks; +#define SDHCI_QUIRK_MISSING_CAPS BIT(27) + unsigned int quirks2; +#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN BIT(15) + u32 caps; /* CAPABILITY_0 */ + u32 caps1; /* CAPABILITY_1 */ + bool read_caps; /* Capability flags have been read */ + u32 sdma_boundary; + + struct mci_host *mci; }; static inline u32 sdhci_read32(struct sdhci *host, int reg) { - return host->read32(host, reg); + if (host->read32) + return host->read32(host, reg); + else + return readl(host->base + reg); } static inline u32 sdhci_read16(struct sdhci *host, int reg) { - return host->read16(host, reg); + if (host->read16) + return host->read16(host, reg); + else + return readw(host->base + reg); } static inline u32 sdhci_read8(struct sdhci *host, int reg) { - return host->read8(host, reg); + if (host->read8) + return host->read8(host, reg); + else + return readb(host->base + reg); } static inline void sdhci_write32(struct sdhci *host, int reg, u32 val) { - host->write32(host, reg, val); + if (host->write32) + host->write32(host, reg, val); + else + writel(val, host->base + reg); } static inline void sdhci_write16(struct sdhci *host, int reg, u32 val) { - host->write16(host, reg, val); + if (host->write16) + host->write16(host, reg, val); + else + writew(val, host->base + reg); } static inline void sdhci_write8(struct sdhci *host, int reg, u32 val) { - host->write8(host, reg, val); + if (host->write8) + host->write8(host, reg, val); + else + writeb(val, host->base + reg); } +#define SDHCI_NO_DMA DMA_ERROR_CODE 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); +void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data); +void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, dma_addr_t *dma); +int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma); +int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data); +int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data, + dma_addr_t dma); int sdhci_reset(struct sdhci *sdhci, u8 mask); +u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock, + unsigned int *actual_clock, unsigned int input_clock); +void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock); +void sdhci_enable_clk(struct sdhci *host, u16 clk); +int sdhci_setup_host(struct sdhci *host); +void __sdhci_read_caps(struct sdhci *host, const u16 *ver, + const u32 *caps, const u32 *caps1); +static inline void sdhci_read_caps(struct sdhci *host) +{ + __sdhci_read_caps(host, NULL, NULL, NULL); +} +void sdhci_set_bus_width(struct sdhci *host, int width); #define sdhci_read8_poll_timeout(sdhci, reg, val, cond, timeout_us) \ read_poll_timeout(sdhci_read8, val, cond, timeout_us, sdhci, reg) |