summaryrefslogtreecommitdiff
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Kconfig43
-rw-r--r--drivers/mmc/host/Makefile5
-rw-r--r--drivers/mmc/host/android-goldfish.c545
-rw-r--r--drivers/mmc/host/atmel-mci.c52
-rw-r--r--drivers/mmc/host/au1xmmc.c14
-rw-r--r--drivers/mmc/host/cavium.c5
-rw-r--r--drivers/mmc/host/cb710-mmc.c12
-rw-r--r--drivers/mmc/host/cqhci-core.c (renamed from drivers/mmc/host/cqhci.c)69
-rw-r--r--drivers/mmc/host/cqhci-crypto.c242
-rw-r--r--drivers/mmc/host/cqhci-crypto.h47
-rw-r--r--drivers/mmc/host/cqhci.h84
-rw-r--r--drivers/mmc/host/dw_mmc-zx.c234
-rw-r--r--drivers/mmc/host/dw_mmc-zx.h32
-rw-r--r--drivers/mmc/host/dw_mmc.c6
-rw-r--r--drivers/mmc/host/jz4740_mmc.c1
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c37
-rw-r--r--drivers/mmc/host/mmci.c74
-rw-r--r--drivers/mmc/host/mtk-sd.c18
-rw-r--r--drivers/mmc/host/mxs-mmc.c2
-rw-r--r--drivers/mmc/host/omap.c7
-rw-r--r--drivers/mmc/host/omap_hsmmc.c18
-rw-r--r--drivers/mmc/host/owl-mmc.c9
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c16
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c91
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c5
-rw-r--r--drivers/mmc/host/s3cmci.c6
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c6
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c3
-rw-r--r--drivers/mmc/host/sdhci-iproc.c18
-rw-r--r--drivers/mmc/host/sdhci-msm.c322
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c65
-rw-r--r--drivers/mmc/host/sdhci-of-aspeed-test.c105
-rw-r--r--drivers/mmc/host/sdhci-of-aspeed.c289
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c28
-rw-r--r--drivers/mmc/host/sdhci-pci-gli.c23
-rw-r--r--drivers/mmc/host/sdhci-pci-o2micro.c20
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h7
-rw-r--r--drivers/mmc/host/sdhci-sirf.c235
-rw-r--r--drivers/mmc/host/sdhci-sprd.c6
-rw-r--r--drivers/mmc/host/sdhci-xenon.c8
-rw-r--r--drivers/mmc/host/sdhci.c9
-rw-r--r--drivers/mmc/host/sdhci_am654.c28
-rw-r--r--drivers/mmc/host/sunxi-mmc.c30
-rw-r--r--drivers/mmc/host/tifm_sd.c7
-rw-r--r--drivers/mmc/host/tmio_mmc.h2
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c8
-rw-r--r--drivers/mmc/host/uniphier-sd.c14
-rw-r--r--drivers/mmc/host/usdhi6rol0.c4
-rw-r--r--drivers/mmc/host/via-sdmmc.c9
-rw-r--r--drivers/mmc/host/wbsd.c35
50 files changed, 1620 insertions, 1335 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 596f32637315..b236dfe2e879 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -168,6 +168,20 @@ config MMC_SDHCI_OF_ASPEED
If unsure, say N.
+config MMC_SDHCI_OF_ASPEED_TEST
+ bool "Tests for the ASPEED SDHCI driver"
+ depends on MMC_SDHCI_OF_ASPEED && KUNIT=y
+ help
+ Enable KUnit tests for the ASPEED SDHCI driver. Select this
+ option only if you will boot the kernel for the purpose of running
+ unit tests (e.g. under UML or qemu).
+
+ The KUnit tests generally exercise parts of the driver that do not
+ directly touch the hardware, for example, the phase correction
+ calculations.
+
+ If unsure, say N.
+
config MMC_SDHCI_OF_AT91
tristate "SDHCI OF support for the Atmel SDMMC controller"
depends on MMC_SDHCI_PLTFM
@@ -312,18 +326,6 @@ config MMC_SDHCI_S3C
If unsure, say N.
-config MMC_SDHCI_SIRF
- tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
- depends on ARCH_SIRF || COMPILE_TEST
- depends on MMC_SDHCI_PLTFM
- select MMC_SDHCI_IO_ACCESSORS
- help
- This selects the SDHCI support for SiRF System-on-Chip devices.
-
- If you have a controller with this interface, say Y or M here.
-
- If unsure, say N.
-
config MMC_SDHCI_PXAV3
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
depends on CLKDEV_LOOKUP
@@ -544,6 +546,7 @@ config MMC_SDHCI_MSM
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
select MMC_CQHCI
+ select QCOM_SCM if MMC_CRYPTO && ARCH_QCOM
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in Qualcomm SOCs. The controller supports
@@ -608,13 +611,6 @@ config MMC_DAVINCI
If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N.
-config MMC_GOLDFISH
- tristate "goldfish qemu Multimedia Card Interface support"
- depends on GOLDFISH || COMPILE_TEST
- help
- This selects the Goldfish Multimedia card Interface emulation
- found on the Goldfish Android virtual device emulation.
-
config MMC_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER
@@ -868,15 +864,6 @@ config MMC_DW_ROCKCHIP
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on RK3066, RK3188 and RK3288 SoC's.
-config MMC_DW_ZX
- tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
- depends on MMC_DW && ARCH_ZX
- select MMC_DW_PLTFM
- help
- This selects support for ZTE SoC specific extensions to the
- Synopsys DesignWare Memory Card Interface driver. Select this option
- for platforms based on ZX296718 SoC's.
-
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 451c25fc2c69..6df5c4774260 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
-obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
obj-$(CONFIG_MMC_SDHCI_MILBEAUT) += sdhci-milbeaut.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
@@ -34,7 +33,6 @@ obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
-obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
@@ -61,7 +59,6 @@ obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
-obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
@@ -104,6 +101,8 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o
obj-$(CONFIG_MMC_CQHCI) += cqhci.o
+cqhci-y += cqhci-core.o
+cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o
obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o
ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c
deleted file mode 100644
index e878fdf8f20a..000000000000
--- a/drivers/mmc/host/android-goldfish.c
+++ /dev/null
@@ -1,545 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2007, Google Inc.
- * Copyright 2012, Intel Inc.
- *
- * based on omap.c driver, which was
- * Copyright (C) 2004 Nokia Corporation
- * Written by Tuukka Tikkanen and Juha Yrjölä <juha.yrjola@nokia.com>
- * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
- * Other hacks (DMA, SD, etc) by David Brownell
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/major.h>
-
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/errno.h>
-#include <linux/hdreg.h>
-#include <linux/kdev_t.h>
-#include <linux/blkdev.h>
-#include <linux/mutex.h>
-#include <linux/scatterlist.h>
-#include <linux/mmc/mmc.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/card.h>
-
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/timer.h>
-#include <linux/clk.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <asm/types.h>
-#include <linux/uaccess.h>
-
-#define DRIVER_NAME "goldfish_mmc"
-
-#define BUFFER_SIZE 16384
-
-#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr))
-#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr))
-
-enum {
- /* status register */
- MMC_INT_STATUS = 0x00,
- /* set this to enable IRQ */
- MMC_INT_ENABLE = 0x04,
- /* set this to specify buffer address */
- MMC_SET_BUFFER = 0x08,
-
- /* MMC command number */
- MMC_CMD = 0x0C,
-
- /* MMC argument */
- MMC_ARG = 0x10,
-
- /* MMC response (or R2 bits 0 - 31) */
- MMC_RESP_0 = 0x14,
-
- /* MMC R2 response bits 32 - 63 */
- MMC_RESP_1 = 0x18,
-
- /* MMC R2 response bits 64 - 95 */
- MMC_RESP_2 = 0x1C,
-
- /* MMC R2 response bits 96 - 127 */
- MMC_RESP_3 = 0x20,
-
- MMC_BLOCK_LENGTH = 0x24,
- MMC_BLOCK_COUNT = 0x28,
-
- /* MMC state flags */
- MMC_STATE = 0x2C,
-
- /* MMC_INT_STATUS bits */
-
- MMC_STAT_END_OF_CMD = 1U << 0,
- MMC_STAT_END_OF_DATA = 1U << 1,
- MMC_STAT_STATE_CHANGE = 1U << 2,
- MMC_STAT_CMD_TIMEOUT = 1U << 3,
-
- /* MMC_STATE bits */
- MMC_STATE_INSERTED = 1U << 0,
- MMC_STATE_READ_ONLY = 1U << 1,
-};
-
-/*
- * Command types
- */
-#define OMAP_MMC_CMDTYPE_BC 0
-#define OMAP_MMC_CMDTYPE_BCR 1
-#define OMAP_MMC_CMDTYPE_AC 2
-#define OMAP_MMC_CMDTYPE_ADTC 3
-
-
-struct goldfish_mmc_host {
- struct mmc_request *mrq;
- struct mmc_command *cmd;
- struct mmc_data *data;
- struct device *dev;
- unsigned char id; /* 16xx chips have 2 MMC blocks */
- void *virt_base;
- unsigned int phys_base;
- int irq;
- unsigned char bus_mode;
- unsigned char hw_bus_mode;
-
- unsigned int sg_len;
- unsigned dma_done:1;
- unsigned dma_in_use:1;
-
- void __iomem *reg_base;
-};
-
-static inline int
-goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host)
-{
- return 0;
-}
-
-static ssize_t
-goldfish_mmc_show_cover_switch(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct goldfish_mmc_host *host = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" :
- "closed");
-}
-
-static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL);
-
-static void
-goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)
-{
- u32 cmdreg;
- u32 resptype;
- u32 cmdtype;
-
- host->cmd = cmd;
-
- resptype = 0;
- cmdtype = 0;
-
- /* Our hardware needs to know exact type */
- switch (mmc_resp_type(cmd)) {
- case MMC_RSP_NONE:
- break;
- case MMC_RSP_R1:
- case MMC_RSP_R1B:
- /* resp 1, 1b, 6, 7 */
- resptype = 1;
- break;
- case MMC_RSP_R2:
- resptype = 2;
- break;
- case MMC_RSP_R3:
- resptype = 3;
- break;
- default:
- dev_err(mmc_dev(mmc_from_priv(host)),
- "Invalid response type: %04x\n", mmc_resp_type(cmd));
- break;
- }
-
- if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
- cmdtype = OMAP_MMC_CMDTYPE_ADTC;
- else if (mmc_cmd_type(cmd) == MMC_CMD_BC)
- cmdtype = OMAP_MMC_CMDTYPE_BC;
- else if (mmc_cmd_type(cmd) == MMC_CMD_BCR)
- cmdtype = OMAP_MMC_CMDTYPE_BCR;
- else
- cmdtype = OMAP_MMC_CMDTYPE_AC;
-
- cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
-
- if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
- cmdreg |= 1 << 6;
-
- if (cmd->flags & MMC_RSP_BUSY)
- cmdreg |= 1 << 11;
-
- if (host->data && !(host->data->flags & MMC_DATA_WRITE))
- cmdreg |= 1 << 15;
-
- GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg);
- GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);
-}
-
-static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
- struct mmc_data *data)
-{
- if (host->dma_in_use) {
- enum dma_data_direction dma_data_dir;
-
- dma_data_dir = mmc_get_dma_dir(data);
-
- if (dma_data_dir == DMA_FROM_DEVICE) {
- /*
- * We don't really have DMA, so we need
- * to copy from our platform driver buffer
- */
- sg_copy_from_buffer(data->sg, 1, host->virt_base,
- data->sg->length);
- }
- host->data->bytes_xfered += data->sg->length;
- dma_unmap_sg(mmc_dev(mmc_from_priv(host)), data->sg,
- host->sg_len, dma_data_dir);
- }
-
- host->data = NULL;
- host->sg_len = 0;
-
- /*
- * NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
- * dozens of requests until the card finishes writing data.
- * It'd be cheaper to just wait till an EOFB interrupt arrives...
- */
-
- if (!data->stop) {
- host->mrq = NULL;
- mmc_request_done(mmc_from_priv(host), data->mrq);
- return;
- }
-
- goldfish_mmc_start_command(host, data->stop);
-}
-
-static void goldfish_mmc_end_of_data(struct goldfish_mmc_host *host,
- struct mmc_data *data)
-{
- if (!host->dma_in_use) {
- goldfish_mmc_xfer_done(host, data);
- return;
- }
- if (host->dma_done)
- goldfish_mmc_xfer_done(host, data);
-}
-
-static void goldfish_mmc_cmd_done(struct goldfish_mmc_host *host,
- struct mmc_command *cmd)
-{
- host->cmd = NULL;
- if (cmd->flags & MMC_RSP_PRESENT) {
- if (cmd->flags & MMC_RSP_136) {
- /* response type 2 */
- cmd->resp[3] =
- GOLDFISH_MMC_READ(host, MMC_RESP_0);
- cmd->resp[2] =
- GOLDFISH_MMC_READ(host, MMC_RESP_1);
- cmd->resp[1] =
- GOLDFISH_MMC_READ(host, MMC_RESP_2);
- cmd->resp[0] =
- GOLDFISH_MMC_READ(host, MMC_RESP_3);
- } else {
- /* response types 1, 1b, 3, 4, 5, 6 */
- cmd->resp[0] =
- GOLDFISH_MMC_READ(host, MMC_RESP_0);
- }
- }
-
- if (host->data == NULL || cmd->error) {
- host->mrq = NULL;
- mmc_request_done(mmc_from_priv(host), cmd->mrq);
- }
-}
-
-static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
-{
- struct goldfish_mmc_host *host = (struct goldfish_mmc_host *)dev_id;
- u16 status;
- int end_command = 0;
- int end_transfer = 0;
- int state_changed = 0;
- int cmd_timeout = 0;
-
- while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
- GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
-
- if (status & MMC_STAT_END_OF_CMD)
- end_command = 1;
-
- if (status & MMC_STAT_END_OF_DATA)
- end_transfer = 1;
-
- if (status & MMC_STAT_STATE_CHANGE)
- state_changed = 1;
-
- if (status & MMC_STAT_CMD_TIMEOUT) {
- end_command = 0;
- cmd_timeout = 1;
- }
- }
-
- if (cmd_timeout) {
- struct mmc_request *mrq = host->mrq;
- mrq->cmd->error = -ETIMEDOUT;
- host->mrq = NULL;
- mmc_request_done(mmc_from_priv(host), mrq);
- }
-
- if (end_command)
- goldfish_mmc_cmd_done(host, host->cmd);
-
- if (end_transfer) {
- host->dma_done = 1;
- goldfish_mmc_end_of_data(host, host->data);
- } else if (host->data != NULL) {
- /*
- * WORKAROUND -- after porting this driver from 2.6 to 3.4,
- * during device initialization, cases where host->data is
- * non-null but end_transfer is false would occur. Doing
- * nothing in such cases results in no further interrupts,
- * and initialization failure.
- * TODO -- find the real cause.
- */
- host->dma_done = 1;
- goldfish_mmc_end_of_data(host, host->data);
- }
-
- if (state_changed) {
- u32 state = GOLDFISH_MMC_READ(host, MMC_STATE);
- pr_info("%s: Card detect now %d\n", __func__,
- (state & MMC_STATE_INSERTED));
- mmc_detect_change(mmc_from_priv(host), 0);
- }
-
- if (!end_command && !end_transfer && !state_changed && !cmd_timeout) {
- status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
- dev_info(mmc_dev(mmc_from_priv(host)), "spurious irq 0x%04x\n",
- status);
- if (status != 0) {
- GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
- GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
- struct mmc_request *req)
-{
- struct mmc_data *data = req->data;
- int block_size;
- unsigned sg_len;
- enum dma_data_direction dma_data_dir;
-
- host->data = data;
- if (data == NULL) {
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
- host->dma_in_use = 0;
- return;
- }
-
- block_size = data->blksz;
-
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
- GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
-
- /*
- * Cope with calling layer confusion; it issues "single
- * block" writes using multi-block scatterlists.
- */
- sg_len = (data->blocks == 1) ? 1 : data->sg_len;
-
- dma_data_dir = mmc_get_dma_dir(data);
-
- host->sg_len = dma_map_sg(mmc_dev(mmc_from_priv(host)), data->sg,
- sg_len, dma_data_dir);
- host->dma_done = 0;
- host->dma_in_use = 1;
-
- if (dma_data_dir == DMA_TO_DEVICE) {
- /*
- * We don't really have DMA, so we need to copy to our
- * platform driver buffer
- */
- sg_copy_to_buffer(data->sg, 1, host->virt_base,
- data->sg->length);
- }
-}
-
-static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
-{
- struct goldfish_mmc_host *host = mmc_priv(mmc);
-
- WARN_ON(host->mrq != NULL);
-
- host->mrq = req;
- goldfish_mmc_prepare_data(host, req);
- goldfish_mmc_start_command(host, req->cmd);
-}
-
-static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- struct goldfish_mmc_host *host = mmc_priv(mmc);
-
- host->bus_mode = ios->bus_mode;
- host->hw_bus_mode = host->bus_mode;
-}
-
-static int goldfish_mmc_get_ro(struct mmc_host *mmc)
-{
- uint32_t state;
- struct goldfish_mmc_host *host = mmc_priv(mmc);
-
- state = GOLDFISH_MMC_READ(host, MMC_STATE);
- return ((state & MMC_STATE_READ_ONLY) != 0);
-}
-
-static const struct mmc_host_ops goldfish_mmc_ops = {
- .request = goldfish_mmc_request,
- .set_ios = goldfish_mmc_set_ios,
- .get_ro = goldfish_mmc_get_ro,
-};
-
-static int goldfish_mmc_probe(struct platform_device *pdev)
-{
- struct mmc_host *mmc;
- struct goldfish_mmc_host *host = NULL;
- struct resource *res;
- int ret = 0;
- int irq;
- dma_addr_t buf_addr;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (res == NULL || irq < 0)
- return -ENXIO;
-
- mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
- if (mmc == NULL) {
- ret = -ENOMEM;
- goto err_alloc_host_failed;
- }
-
- host = mmc_priv(mmc);
-
- pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end);
- host->reg_base = ioremap(res->start, resource_size(res));
- if (host->reg_base == NULL) {
- ret = -ENOMEM;
- goto ioremap_failed;
- }
- host->virt_base = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
- &buf_addr, GFP_KERNEL);
-
- if (host->virt_base == 0) {
- ret = -ENOMEM;
- goto dma_alloc_failed;
- }
- host->phys_base = buf_addr;
-
- host->id = pdev->id;
- host->irq = irq;
-
- mmc->ops = &goldfish_mmc_ops;
- mmc->f_min = 400000;
- mmc->f_max = 24000000;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
- mmc->caps2 = MMC_CAP2_NO_SDIO;
-
- /* Use scatterlist DMA to reduce per-transfer costs.
- * NOTE max_seg_size assumption that small blocks aren't
- * normally used (except e.g. for reading SD registers).
- */
- mmc->max_segs = 32;
- mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
- mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
- mmc->max_req_size = BUFFER_SIZE;
- mmc->max_seg_size = mmc->max_req_size;
-
- ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
- if (ret) {
- dev_err(&pdev->dev, "Failed IRQ Adding goldfish MMC\n");
- goto err_request_irq_failed;
- }
-
- host->dev = &pdev->dev;
- platform_set_drvdata(pdev, host);
-
- ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
- if (ret)
- dev_warn(mmc_dev(mmc), "Unable to create sysfs attributes\n");
-
- GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
- GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
- MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA |
- MMC_STAT_STATE_CHANGE | MMC_STAT_CMD_TIMEOUT);
-
- mmc_add_host(mmc);
- return 0;
-
-err_request_irq_failed:
- dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base,
- host->phys_base);
-dma_alloc_failed:
- iounmap(host->reg_base);
-ioremap_failed:
- mmc_free_host(mmc);
-err_alloc_host_failed:
- return ret;
-}
-
-static int goldfish_mmc_remove(struct platform_device *pdev)
-{
- struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
- struct mmc_host *mmc = mmc_from_priv(host);
-
- BUG_ON(host == NULL);
-
- mmc_remove_host(mmc);
- free_irq(host->irq, host);
- dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
- iounmap(host->reg_base);
- mmc_free_host(mmc);
- return 0;
-}
-
-static struct platform_driver goldfish_mmc_driver = {
- .probe = goldfish_mmc_probe,
- .remove = goldfish_mmc_remove,
- .driver = {
- .name = DRIVER_NAME,
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
-};
-
-module_platform_driver(goldfish_mmc_driver);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 444bd3a0a922..807177c953f3 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1719,9 +1719,9 @@ static void atmci_detect_change(struct timer_list *t)
}
}
-static void atmci_tasklet_func(unsigned long priv)
+static void atmci_tasklet_func(struct tasklet_struct *t)
{
- struct atmel_mci *host = (struct atmel_mci *)priv;
+ struct atmel_mci *host = from_tasklet(host, t, tasklet);
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
enum atmel_mci_state state = host->state;
@@ -2401,45 +2401,45 @@ static void atmci_get_cap(struct atmel_mci *host)
dev_info(&host->pdev->dev,
"version: 0x%x\n", version);
- host->caps.has_dma_conf_reg = 0;
- host->caps.has_pdc = 1;
- host->caps.has_cfg_reg = 0;
- host->caps.has_cstor_reg = 0;
- host->caps.has_highspeed = 0;
- host->caps.has_rwproof = 0;
- host->caps.has_odd_clk_div = 0;
- host->caps.has_bad_data_ordering = 1;
- host->caps.need_reset_after_xfer = 1;
- host->caps.need_blksz_mul_4 = 1;
- host->caps.need_notbusy_for_read_ops = 0;
+ host->caps.has_dma_conf_reg = false;
+ host->caps.has_pdc = true;
+ host->caps.has_cfg_reg = false;
+ host->caps.has_cstor_reg = false;
+ host->caps.has_highspeed = false;
+ host->caps.has_rwproof = false;
+ host->caps.has_odd_clk_div = false;
+ host->caps.has_bad_data_ordering = true;
+ host->caps.need_reset_after_xfer = true;
+ host->caps.need_blksz_mul_4 = true;
+ host->caps.need_notbusy_for_read_ops = false;
/* keep only major version number */
switch (version & 0xf00) {
case 0x600:
case 0x500:
- host->caps.has_odd_clk_div = 1;
+ host->caps.has_odd_clk_div = true;
fallthrough;
case 0x400:
case 0x300:
- host->caps.has_dma_conf_reg = 1;
- host->caps.has_pdc = 0;
- host->caps.has_cfg_reg = 1;
- host->caps.has_cstor_reg = 1;
- host->caps.has_highspeed = 1;
+ host->caps.has_dma_conf_reg = true;
+ host->caps.has_pdc = false;
+ host->caps.has_cfg_reg = true;
+ host->caps.has_cstor_reg = true;
+ host->caps.has_highspeed = true;
fallthrough;
case 0x200:
- host->caps.has_rwproof = 1;
- host->caps.need_blksz_mul_4 = 0;
- host->caps.need_notbusy_for_read_ops = 1;
+ host->caps.has_rwproof = true;
+ host->caps.need_blksz_mul_4 = false;
+ host->caps.need_notbusy_for_read_ops = true;
fallthrough;
case 0x100:
- host->caps.has_bad_data_ordering = 0;
- host->caps.need_reset_after_xfer = 0;
+ host->caps.has_bad_data_ordering = false;
+ host->caps.need_reset_after_xfer = false;
fallthrough;
case 0x0:
break;
default:
- host->caps.has_pdc = 0;
+ host->caps.has_pdc = false;
dev_warn(&host->pdev->dev,
"Unmanaged mci version, set minimum capabilities\n");
break;
@@ -2496,7 +2496,7 @@ static int atmci_probe(struct platform_device *pdev)
host->mapbase = regs->start;
- tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
+ tasklet_setup(&host->tasklet, atmci_tasklet_func);
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
if (ret) {
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index bd00515fbaba..0acc237843f7 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -253,9 +253,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host)
mmc_request_done(host->mmc, mrq);
}
-static void au1xmmc_tasklet_finish(unsigned long param)
+static void au1xmmc_tasklet_finish(struct tasklet_struct *t)
{
- struct au1xmmc_host *host = (struct au1xmmc_host *) param;
+ struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
au1xmmc_finish_request(host);
}
@@ -363,9 +363,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
au1xmmc_finish_request(host);
}
-static void au1xmmc_tasklet_data(unsigned long param)
+static void au1xmmc_tasklet_data(struct tasklet_struct *t)
{
- struct au1xmmc_host *host = (struct au1xmmc_host *)param;
+ struct au1xmmc_host *host = from_tasklet(host, t, data_task);
u32 status = __raw_readl(HOST_STATUS(host));
au1xmmc_data_complete(host, status);
@@ -1037,11 +1037,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
if (host->platdata)
mmc->caps &= ~(host->platdata->mask_host_caps);
- tasklet_init(&host->data_task, au1xmmc_tasklet_data,
- (unsigned long)host);
+ tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
- tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
- (unsigned long)host);
+ tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
if (has_dbdma()) {
ret = au1xmmc_dbdma_init(host);
diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c
index c5da3aaee334..4bb8f2800a2b 100644
--- a/drivers/mmc/host/cavium.c
+++ b/drivers/mmc/host/cavium.c
@@ -436,12 +436,11 @@ irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id)
{
struct cvm_mmc_host *host = dev_id;
struct mmc_request *req;
- unsigned long flags = 0;
u64 emm_int, rsp_sts;
bool host_done;
if (host->need_irq_handler_lock)
- spin_lock_irqsave(&host->irq_handler_lock, flags);
+ spin_lock(&host->irq_handler_lock);
else
__acquire(&host->irq_handler_lock);
@@ -504,7 +503,7 @@ no_req_done:
host->release_bus(host);
out:
if (host->need_irq_handler_lock)
- spin_unlock_irqrestore(&host->irq_handler_lock, flags);
+ spin_unlock(&host->irq_handler_lock);
else
__release(&host->irq_handler_lock);
return IRQ_RETVAL(emm_int != 0);
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index e84ed84ea4cc..6d623b2681c3 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -646,14 +646,14 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot)
return 1;
}
-static void cb710_mmc_finish_request_tasklet(unsigned long data)
+static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t)
{
- struct mmc_host *mmc = (void *)data;
- struct cb710_mmc_reader *reader = mmc_priv(mmc);
+ struct cb710_mmc_reader *reader = from_tasklet(reader, t,
+ finish_req_tasklet);
struct mmc_request *mrq = reader->mrq;
reader->mrq = NULL;
- mmc_request_done(mmc, mrq);
+ mmc_request_done(mmc_from_priv(reader), mrq);
}
static const struct mmc_host_ops cb710_mmc_host = {
@@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev)
reader = mmc_priv(mmc);
- tasklet_init(&reader->finish_req_tasklet,
- cb710_mmc_finish_request_tasklet, (unsigned long)mmc);
+ tasklet_setup(&reader->finish_req_tasklet,
+ cb710_mmc_finish_request_tasklet);
spin_lock_init(&reader->irq_lock);
cb710_dump_regs(chip, CB710_DUMP_REGS_MMC);
diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci-core.c
index 697fe40756bf..93b0432bb601 100644
--- a/drivers/mmc/host/cqhci.c
+++ b/drivers/mmc/host/cqhci-core.c
@@ -18,6 +18,7 @@
#include <linux/mmc/card.h>
#include "cqhci.h"
+#include "cqhci-crypto.h"
#define DCMD_SLOT 31
#define NUM_SLOTS 32
@@ -258,6 +259,9 @@ static void __cqhci_enable(struct cqhci_host *cq_host)
if (cq_host->caps & CQHCI_TASK_DESC_SZ_128)
cqcfg |= CQHCI_TASK_DESC_SZ;
+ if (mmc->caps2 & MMC_CAP2_CRYPTO)
+ cqcfg |= CQHCI_CRYPTO_GENERAL_ENABLE;
+
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base),
@@ -408,13 +412,15 @@ static void cqhci_disable(struct mmc_host *mmc)
}
static void cqhci_prep_task_desc(struct mmc_request *mrq,
- u64 *data, bool intr)
+ struct cqhci_host *cq_host, int tag)
{
+ __le64 *task_desc = (__le64 __force *)get_desc(cq_host, tag);
u32 req_flags = mrq->data->flags;
+ u64 desc0;
- *data = CQHCI_VALID(1) |
+ desc0 = CQHCI_VALID(1) |
CQHCI_END(1) |
- CQHCI_INT(intr) |
+ CQHCI_INT(1) |
CQHCI_ACT(0x5) |
CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) |
CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) |
@@ -425,8 +431,19 @@ static void cqhci_prep_task_desc(struct mmc_request *mrq,
CQHCI_BLK_COUNT(mrq->data->blocks) |
CQHCI_BLK_ADDR((u64)mrq->data->blk_addr);
- pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n",
- mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data);
+ task_desc[0] = cpu_to_le64(desc0);
+
+ if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) {
+ u64 desc1 = cqhci_crypto_prep_task_desc(mrq);
+
+ task_desc[1] = cpu_to_le64(desc1);
+
+ pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx%016llx\n",
+ mmc_hostname(mrq->host), mrq->tag, desc1, desc0);
+ } else {
+ pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n",
+ mmc_hostname(mrq->host), mrq->tag, desc0);
+ }
}
static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq)
@@ -567,8 +584,6 @@ static inline int cqhci_tag(struct mmc_request *mrq)
static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
int err = 0;
- u64 data = 0;
- u64 *task_desc = NULL;
int tag = cqhci_tag(mrq);
struct cqhci_host *cq_host = mmc->cqe_private;
unsigned long flags;
@@ -598,9 +613,8 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
}
if (mrq->data) {
- task_desc = (__le64 __force *)get_desc(cq_host, tag);
- cqhci_prep_task_desc(mrq, &data, 1);
- *task_desc = cpu_to_le64(data);
+ cqhci_prep_task_desc(mrq, cq_host, tag);
+
err = cqhci_prep_tran_desc(mrq, cq_host, tag);
if (err) {
pr_err("%s: cqhci: failed to setup tx desc: %d\n",
@@ -671,6 +685,7 @@ static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error,
struct cqhci_host *cq_host = mmc->cqe_private;
struct cqhci_slot *slot;
u32 terri;
+ u32 tdpe;
int tag;
spin_lock(&cq_host->lock);
@@ -709,6 +724,30 @@ static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error,
}
}
+ /*
+ * Handle ICCE ("Invalid Crypto Configuration Error"). This should
+ * never happen, since the block layer ensures that all crypto-enabled
+ * I/O requests have a valid keyslot before they reach the driver.
+ *
+ * Note that GCE ("General Crypto Error") is different; it already got
+ * handled above by checking TERRI.
+ */
+ if (status & CQHCI_IS_ICCE) {
+ tdpe = cqhci_readl(cq_host, CQHCI_TDPE);
+ WARN_ONCE(1,
+ "%s: cqhci: invalid crypto configuration error. IRQ status: 0x%08x TDPE: 0x%08x\n",
+ mmc_hostname(mmc), status, tdpe);
+ while (tdpe != 0) {
+ tag = __ffs(tdpe);
+ tdpe &= ~(1 << tag);
+ slot = &cq_host->slot[tag];
+ if (!slot->mrq)
+ continue;
+ slot->flags = cqhci_error_flags(data_error, cmd_error);
+ cqhci_recovery_needed(mmc, slot->mrq, true);
+ }
+ }
+
if (!cq_host->recovery_halt) {
/*
* The only way to guarantee forward progress is to mark at
@@ -774,7 +813,8 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status);
- if ((status & CQHCI_IS_RED) || cmd_error || data_error)
+ if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) ||
+ cmd_error || data_error)
cqhci_error_irq(mmc, status, cmd_error, data_error);
if (status & CQHCI_IS_TCC) {
@@ -1141,6 +1181,13 @@ int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc,
goto out_err;
}
+ err = cqhci_crypto_init(cq_host);
+ if (err) {
+ pr_err("%s: CQHCI crypto initialization failed\n",
+ mmc_hostname(mmc));
+ goto out_err;
+ }
+
spin_lock_init(&cq_host->lock);
init_completion(&cq_host->halt_comp);
diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c
new file mode 100644
index 000000000000..6419cfbb4ab7
--- /dev/null
+++ b/drivers/mmc/host/cqhci-crypto.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CQHCI crypto engine (inline encryption) support
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#include <linux/blk-crypto.h>
+#include <linux/keyslot-manager.h>
+#include <linux/mmc/host.h>
+
+#include "cqhci-crypto.h"
+
+/* Map from blk-crypto modes to CQHCI crypto algorithm IDs and key sizes */
+static const struct cqhci_crypto_alg_entry {
+ enum cqhci_crypto_alg alg;
+ enum cqhci_crypto_key_size key_size;
+} cqhci_crypto_algs[BLK_ENCRYPTION_MODE_MAX] = {
+ [BLK_ENCRYPTION_MODE_AES_256_XTS] = {
+ .alg = CQHCI_CRYPTO_ALG_AES_XTS,
+ .key_size = CQHCI_CRYPTO_KEY_SIZE_256,
+ },
+};
+
+static inline struct cqhci_host *
+cqhci_host_from_ksm(struct blk_keyslot_manager *ksm)
+{
+ struct mmc_host *mmc = container_of(ksm, struct mmc_host, ksm);
+
+ return mmc->cqe_private;
+}
+
+static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg,
+ int slot)
+{
+ u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg);
+ int i;
+
+ if (cq_host->ops->program_key)
+ return cq_host->ops->program_key(cq_host, cfg, slot);
+
+ /* Clear CFGE */
+ cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0]));
+
+ /* Write the key */
+ for (i = 0; i < 16; i++) {
+ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[i]),
+ slot_offset + i * sizeof(cfg->reg_val[0]));
+ }
+ /* Write dword 17 */
+ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[17]),
+ slot_offset + 17 * sizeof(cfg->reg_val[0]));
+ /* Write dword 16, which includes the new value of CFGE */
+ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]),
+ slot_offset + 16 * sizeof(cfg->reg_val[0]));
+ return 0;
+}
+
+static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+
+{
+ struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm);
+ const union cqhci_crypto_cap_entry *ccap_array =
+ cq_host->crypto_cap_array;
+ const struct cqhci_crypto_alg_entry *alg =
+ &cqhci_crypto_algs[key->crypto_cfg.crypto_mode];
+ u8 data_unit_mask = key->crypto_cfg.data_unit_size / 512;
+ int i;
+ int cap_idx = -1;
+ union cqhci_crypto_cfg_entry cfg = {};
+ int err;
+
+ BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
+ for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) {
+ if (ccap_array[i].algorithm_id == alg->alg &&
+ ccap_array[i].key_size == alg->key_size &&
+ (ccap_array[i].sdus_mask & data_unit_mask)) {
+ cap_idx = i;
+ break;
+ }
+ }
+ if (WARN_ON(cap_idx < 0))
+ return -EOPNOTSUPP;
+
+ cfg.data_unit_size = data_unit_mask;
+ cfg.crypto_cap_idx = cap_idx;
+ cfg.config_enable = CQHCI_CRYPTO_CONFIGURATION_ENABLE;
+
+ if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) {
+ /* In XTS mode, the blk_crypto_key's size is already doubled */
+ memcpy(cfg.crypto_key, key->raw, key->size/2);
+ memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2,
+ key->raw + key->size/2, key->size/2);
+ } else {
+ memcpy(cfg.crypto_key, key->raw, key->size);
+ }
+
+ err = cqhci_crypto_program_key(cq_host, &cfg, slot);
+
+ memzero_explicit(&cfg, sizeof(cfg));
+ return err;
+}
+
+static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
+{
+ /*
+ * Clear the crypto cfg on the device. Clearing CFGE
+ * might not be sufficient, so just clear the entire cfg.
+ */
+ union cqhci_crypto_cfg_entry cfg = {};
+
+ return cqhci_crypto_program_key(cq_host, &cfg, slot);
+}
+
+static int cqhci_crypto_keyslot_evict(struct blk_keyslot_manager *ksm,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm);
+
+ return cqhci_crypto_clear_keyslot(cq_host, slot);
+}
+
+/*
+ * The keyslot management operations for CQHCI crypto.
+ *
+ * Note that the block layer ensures that these are never called while the host
+ * controller is runtime-suspended. However, the CQE won't necessarily be
+ * "enabled" when these are called, i.e. CQHCI_ENABLE might not be set in the
+ * CQHCI_CFG register. But the hardware allows that.
+ */
+static const struct blk_ksm_ll_ops cqhci_ksm_ops = {
+ .keyslot_program = cqhci_crypto_keyslot_program,
+ .keyslot_evict = cqhci_crypto_keyslot_evict,
+};
+
+static enum blk_crypto_mode_num
+cqhci_find_blk_crypto_mode(union cqhci_crypto_cap_entry cap)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cqhci_crypto_algs); i++) {
+ BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
+ if (cqhci_crypto_algs[i].alg == cap.algorithm_id &&
+ cqhci_crypto_algs[i].key_size == cap.key_size)
+ return i;
+ }
+ return BLK_ENCRYPTION_MODE_INVALID;
+}
+
+/**
+ * cqhci_crypto_init - initialize CQHCI crypto support
+ * @cq_host: a cqhci host
+ *
+ * If the driver previously set MMC_CAP2_CRYPTO and the CQE declares
+ * CQHCI_CAP_CS, initialize the crypto support. This involves reading the
+ * crypto capability registers, initializing the keyslot manager, clearing all
+ * keyslots, and enabling 128-bit task descriptors.
+ *
+ * Return: 0 if crypto was initialized or isn't supported; whether
+ * MMC_CAP2_CRYPTO remains set indicates which one of those cases it is.
+ * Also can return a negative errno value on unexpected error.
+ */
+int cqhci_crypto_init(struct cqhci_host *cq_host)
+{
+ struct mmc_host *mmc = cq_host->mmc;
+ struct device *dev = mmc_dev(mmc);
+ struct blk_keyslot_manager *ksm = &mmc->ksm;
+ unsigned int num_keyslots;
+ unsigned int cap_idx;
+ enum blk_crypto_mode_num blk_mode_num;
+ unsigned int slot;
+ int err = 0;
+
+ if (!(mmc->caps2 & MMC_CAP2_CRYPTO) ||
+ !(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
+ goto out;
+
+ cq_host->crypto_capabilities.reg_val =
+ cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
+
+ cq_host->crypto_cfg_register =
+ (u32)cq_host->crypto_capabilities.config_array_ptr * 0x100;
+
+ cq_host->crypto_cap_array =
+ devm_kcalloc(dev, cq_host->crypto_capabilities.num_crypto_cap,
+ sizeof(cq_host->crypto_cap_array[0]), GFP_KERNEL);
+ if (!cq_host->crypto_cap_array) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * CCAP.CFGC is off by one, so the actual number of crypto
+ * configurations (a.k.a. keyslots) is CCAP.CFGC + 1.
+ */
+ num_keyslots = cq_host->crypto_capabilities.config_count + 1;
+
+ err = devm_blk_ksm_init(dev, ksm, num_keyslots);
+ if (err)
+ goto out;
+
+ ksm->ksm_ll_ops = cqhci_ksm_ops;
+ ksm->dev = dev;
+
+ /* Unfortunately, CQHCI crypto only supports 32 DUN bits. */
+ ksm->max_dun_bytes_supported = 4;
+
+ /*
+ * Cache all the crypto capabilities and advertise the supported crypto
+ * modes and data unit sizes to the block layer.
+ */
+ for (cap_idx = 0; cap_idx < cq_host->crypto_capabilities.num_crypto_cap;
+ cap_idx++) {
+ cq_host->crypto_cap_array[cap_idx].reg_val =
+ cpu_to_le32(cqhci_readl(cq_host,
+ CQHCI_CRYPTOCAP +
+ cap_idx * sizeof(__le32)));
+ blk_mode_num = cqhci_find_blk_crypto_mode(
+ cq_host->crypto_cap_array[cap_idx]);
+ if (blk_mode_num == BLK_ENCRYPTION_MODE_INVALID)
+ continue;
+ ksm->crypto_modes_supported[blk_mode_num] |=
+ cq_host->crypto_cap_array[cap_idx].sdus_mask * 512;
+ }
+
+ /* Clear all the keyslots so that we start in a known state. */
+ for (slot = 0; slot < num_keyslots; slot++)
+ cqhci_crypto_clear_keyslot(cq_host, slot);
+
+ /* CQHCI crypto requires the use of 128-bit task descriptors. */
+ cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
+
+ return 0;
+
+out:
+ mmc->caps2 &= ~MMC_CAP2_CRYPTO;
+ return err;
+}
diff --git a/drivers/mmc/host/cqhci-crypto.h b/drivers/mmc/host/cqhci-crypto.h
new file mode 100644
index 000000000000..60b58ee0e625
--- /dev/null
+++ b/drivers/mmc/host/cqhci-crypto.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * CQHCI crypto engine (inline encryption) support
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#ifndef LINUX_MMC_CQHCI_CRYPTO_H
+#define LINUX_MMC_CQHCI_CRYPTO_H
+
+#include <linux/mmc/host.h>
+
+#include "cqhci.h"
+
+#ifdef CONFIG_MMC_CRYPTO
+
+int cqhci_crypto_init(struct cqhci_host *host);
+
+/*
+ * Returns the crypto bits that should be set in bits 64-127 of the
+ * task descriptor.
+ */
+static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)
+{
+ if (!mrq->crypto_enabled)
+ return 0;
+
+ return CQHCI_CRYPTO_ENABLE_BIT |
+ CQHCI_CRYPTO_KEYSLOT(mrq->crypto_key_slot) |
+ mrq->data_unit_num;
+}
+
+#else /* CONFIG_MMC_CRYPTO */
+
+static inline int cqhci_crypto_init(struct cqhci_host *host)
+{
+ return 0;
+}
+
+static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_MMC_CRYPTO */
+
+#endif /* LINUX_MMC_CQHCI_CRYPTO_H */
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index 89bf6adbce8c..ba9387ed90eb 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -22,10 +22,13 @@
/* capabilities */
#define CQHCI_CAP 0x04
+#define CQHCI_CAP_CS 0x10000000 /* Crypto Support */
+
/* configuration */
#define CQHCI_CFG 0x08
#define CQHCI_DCMD 0x00001000
#define CQHCI_TASK_DESC_SZ 0x00000100
+#define CQHCI_CRYPTO_GENERAL_ENABLE 0x00000002
#define CQHCI_ENABLE 0x00000001
/* control */
@@ -39,8 +42,11 @@
#define CQHCI_IS_TCC BIT(1)
#define CQHCI_IS_RED BIT(2)
#define CQHCI_IS_TCL BIT(3)
+#define CQHCI_IS_GCE BIT(4) /* General Crypto Error */
+#define CQHCI_IS_ICCE BIT(5) /* Invalid Crypto Config Error */
-#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED)
+#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED | \
+ CQHCI_IS_GCE | CQHCI_IS_ICCE)
/* interrupt status enable */
#define CQHCI_ISTE 0x14
@@ -78,6 +84,9 @@
/* task clear */
#define CQHCI_TCLR 0x38
+/* task descriptor processing error */
+#define CQHCI_TDPE 0x3c
+
/* send status config 1 */
#define CQHCI_SSC1 0x40
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
@@ -107,6 +116,10 @@
/* command response argument */
#define CQHCI_CRA 0x5C
+/* crypto capabilities */
+#define CQHCI_CCAP 0x100
+#define CQHCI_CRYPTOCAP 0x104
+
#define CQHCI_INT_ALL 0xF
#define CQHCI_IC_DEFAULT_ICCTH 31
#define CQHCI_IC_DEFAULT_ICTOVAL 1
@@ -133,11 +146,70 @@
#define CQHCI_CMD_TIMING(x) (((x) & 1) << 22)
#define CQHCI_RESP_TYPE(x) (((x) & 0x3) << 23)
+/* crypto task descriptor fields (for bits 64-127 of task descriptor) */
+#define CQHCI_CRYPTO_ENABLE_BIT (1ULL << 47)
+#define CQHCI_CRYPTO_KEYSLOT(x) ((u64)(x) << 32)
+
/* transfer descriptor fields */
#define CQHCI_DAT_LENGTH(x) (((x) & 0xFFFF) << 16)
#define CQHCI_DAT_ADDR_LO(x) (((x) & 0xFFFFFFFF) << 32)
#define CQHCI_DAT_ADDR_HI(x) (((x) & 0xFFFFFFFF) << 0)
+/* CCAP - Crypto Capability 100h */
+union cqhci_crypto_capabilities {
+ __le32 reg_val;
+ struct {
+ u8 num_crypto_cap;
+ u8 config_count;
+ u8 reserved;
+ u8 config_array_ptr;
+ };
+};
+
+enum cqhci_crypto_key_size {
+ CQHCI_CRYPTO_KEY_SIZE_INVALID = 0,
+ CQHCI_CRYPTO_KEY_SIZE_128 = 1,
+ CQHCI_CRYPTO_KEY_SIZE_192 = 2,
+ CQHCI_CRYPTO_KEY_SIZE_256 = 3,
+ CQHCI_CRYPTO_KEY_SIZE_512 = 4,
+};
+
+enum cqhci_crypto_alg {
+ CQHCI_CRYPTO_ALG_AES_XTS = 0,
+ CQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC = 1,
+ CQHCI_CRYPTO_ALG_AES_ECB = 2,
+ CQHCI_CRYPTO_ALG_ESSIV_AES_CBC = 3,
+};
+
+/* x-CRYPTOCAP - Crypto Capability X */
+union cqhci_crypto_cap_entry {
+ __le32 reg_val;
+ struct {
+ u8 algorithm_id;
+ u8 sdus_mask; /* Supported data unit size mask */
+ u8 key_size;
+ u8 reserved;
+ };
+};
+
+#define CQHCI_CRYPTO_CONFIGURATION_ENABLE (1 << 7)
+#define CQHCI_CRYPTO_KEY_MAX_SIZE 64
+/* x-CRYPTOCFG - Crypto Configuration X */
+union cqhci_crypto_cfg_entry {
+ __le32 reg_val[32];
+ struct {
+ u8 crypto_key[CQHCI_CRYPTO_KEY_MAX_SIZE];
+ u8 data_unit_size;
+ u8 crypto_cap_idx;
+ u8 reserved_1;
+ u8 config_enable;
+ u8 reserved_multi_host;
+ u8 reserved_2;
+ u8 vsb[2];
+ u8 reserved_3[56];
+ };
+};
+
struct cqhci_host_ops;
struct mmc_host;
struct mmc_request;
@@ -196,6 +268,12 @@ struct cqhci_host {
struct completion halt_comp;
wait_queue_head_t wait_queue;
struct cqhci_slot *slot;
+
+#ifdef CONFIG_MMC_CRYPTO
+ union cqhci_crypto_capabilities crypto_capabilities;
+ union cqhci_crypto_cap_entry *crypto_cap_array;
+ u32 crypto_cfg_register;
+#endif
};
struct cqhci_host_ops {
@@ -208,6 +286,10 @@ struct cqhci_host_ops {
u64 *data);
void (*pre_enable)(struct mmc_host *mmc);
void (*post_disable)(struct mmc_host *mmc);
+#ifdef CONFIG_MMC_CRYPTO
+ int (*program_key)(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg, int slot);
+#endif
};
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
deleted file mode 100644
index 51bcc6332f3a..000000000000
--- a/drivers/mmc/host/dw_mmc-zx.c
+++ /dev/null
@@ -1,234 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
- *
- * Copyright (C) 2016, Linaro Ltd.
- * Copyright (C) 2016, ZTE Corp.
- */
-
-#include <linux/clk.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/mmc.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-
-#include "dw_mmc.h"
-#include "dw_mmc-pltfm.h"
-#include "dw_mmc-zx.h"
-
-struct dw_mci_zx_priv_data {
- struct regmap *sysc_base;
-};
-
-enum delay_type {
- DELAY_TYPE_READ, /* read dqs delay */
- DELAY_TYPE_CLK, /* clk sample delay */
-};
-
-static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
- enum delay_type dflag)
-{
- struct dw_mci_zx_priv_data *priv = host->priv;
- struct regmap *sysc_base = priv->sysc_base;
- unsigned int clksel;
- unsigned int loop = 1000;
- int ret;
-
- if (!sysc_base)
- return -EINVAL;
-
- ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
- PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
- PARA_PHASE_DET_SEL_MASK |
- PARA_DLL_LOCK_NUM_MASK |
- DLL_REG_SET | PARA_DLL_START_MASK,
- PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4));
- if (ret)
- return ret;
-
- ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
- if (ret)
- return ret;
-
- if (dflag == DELAY_TYPE_CLK) {
- clksel &= ~CLK_SAMP_DELAY_MASK;
- clksel |= CLK_SAMP_DELAY(delay);
- } else {
- clksel &= ~READ_DQS_DELAY_MASK;
- clksel |= READ_DQS_DELAY(delay);
- }
-
- regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
- regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
- PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
- DLL_REG_SET,
- PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) |
- DLL_REG_SET);
-
- do {
- ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
- if (ret)
- return ret;
-
- } while (--loop && !(clksel & ZX_DLL_LOCKED));
-
- if (!loop) {
- dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
-{
- struct dw_mci *host = slot->host;
- struct mmc_host *mmc = slot->mmc;
- int ret, len = 0, start = 0, end = 0, delay, best = 0;
-
- for (delay = 1; delay < 128; delay++) {
- ret = dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK);
- if (!ret && mmc_send_tuning(mmc, opcode, NULL)) {
- if (start >= 0) {
- end = delay - 1;
- /* check and update longest good range */
- if ((end - start) > len) {
- best = (start + end) >> 1;
- len = end - start;
- }
- }
- start = -1;
- end = 0;
- continue;
- }
- if (start < 0)
- start = delay;
- }
-
- if (start >= 0) {
- end = delay - 1;
- if ((end - start) > len) {
- best = (start + end) >> 1;
- len = end - start;
- }
- }
- if (best < 0)
- return -EIO;
-
- dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
- start, end);
- return dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK);
-}
-
-static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
- struct mmc_ios *ios)
-{
- int ret;
-
- /* config phase shift as 90 degree */
- ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ);
- if (ret < 0)
- return -EIO;
-
- return 0;
-}
-
-static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
-{
- struct dw_mci *host = slot->host;
-
- if (host->verid == 0x290a) /* only for emmc */
- return dw_mci_zx_emmc_execute_tuning(slot, opcode);
- /* TODO: Add 0x210a dedicated tuning for sd/sdio */
-
- return 0;
-}
-
-static int dw_mci_zx_parse_dt(struct dw_mci *host)
-{
- struct device_node *np = host->dev->of_node;
- struct device_node *node;
- struct dw_mci_zx_priv_data *priv;
- struct regmap *sysc_base;
-
- /* syscon is needed only by emmc */
- node = of_parse_phandle(np, "zte,aon-syscon", 0);
- if (node) {
- sysc_base = syscon_node_to_regmap(node);
- of_node_put(node);
-
- if (IS_ERR(sysc_base))
- return dev_err_probe(host->dev, PTR_ERR(sysc_base),
- "Can't get syscon\n");
- } else {
- return 0;
- }
-
- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->sysc_base = sysc_base;
- host->priv = priv;
-
- return 0;
-}
-
-static unsigned long zx_dwmmc_caps[3] = {
- MMC_CAP_CMD23,
- MMC_CAP_CMD23,
- MMC_CAP_CMD23,
-};
-
-static const struct dw_mci_drv_data zx_drv_data = {
- .caps = zx_dwmmc_caps,
- .num_caps = ARRAY_SIZE(zx_dwmmc_caps),
- .execute_tuning = dw_mci_zx_execute_tuning,
- .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning,
- .parse_dt = dw_mci_zx_parse_dt,
-};
-
-static const struct of_device_id dw_mci_zx_match[] = {
- { .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data},
- {},
-};
-MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
-
-static int dw_mci_zx_probe(struct platform_device *pdev)
-{
- const struct dw_mci_drv_data *drv_data;
- const struct of_device_id *match;
-
- match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
- drv_data = match->data;
-
- return dw_mci_pltfm_register(pdev, drv_data);
-}
-
-static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
- dw_mci_runtime_resume,
- NULL)
-};
-
-static struct platform_driver dw_mci_zx_pltfm_driver = {
- .probe = dw_mci_zx_probe,
- .remove = dw_mci_pltfm_remove,
- .driver = {
- .name = "dwmmc_zx",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .of_match_table = dw_mci_zx_match,
- .pm = &dw_mci_zx_dev_pm_ops,
- },
-};
-
-module_platform_driver(dw_mci_zx_pltfm_driver);
-
-MODULE_DESCRIPTION("ZTE emmc/sd driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
deleted file mode 100644
index 09ac52766f14..000000000000
--- a/drivers/mmc/host/dw_mmc-zx.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _DW_MMC_ZX_H_
-#define _DW_MMC_ZX_H_
-
-/* ZX296718 SoC specific DLL register offset. */
-#define LB_AON_EMMC_CFG_REG0 0x1B0
-#define LB_AON_EMMC_CFG_REG1 0x1B4
-#define LB_AON_EMMC_CFG_REG2 0x1B8
-
-/* LB_AON_EMMC_CFG_REG0 register defines */
-#define PARA_DLL_START(x) ((x) & 0xFF)
-#define PARA_DLL_START_MASK 0xFF
-#define DLL_REG_SET BIT(8)
-#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16)
-#define PARA_DLL_LOCK_NUM_MASK (7 << 16)
-#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20)
-#define PARA_PHASE_DET_SEL_MASK (7 << 20)
-#define PARA_DLL_BYPASS_MODE BIT(23)
-#define PARA_HALF_CLK_MODE BIT(24)
-
-/* LB_AON_EMMC_CFG_REG1 register defines */
-#define READ_DQS_DELAY(x) ((x) & 0x7F)
-#define READ_DQS_DELAY_MASK (0x7F)
-#define READ_DQS_BYPASS_MODE BIT(7)
-#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8)
-#define CLK_SAMP_DELAY_MASK (0x7F << 8)
-#define CLK_SAMP_BYPASS_MODE BIT(15)
-
-/* LB_AON_EMMC_CFG_REG2 register defines */
-#define ZX_DLL_LOCKED BIT(2)
-
-#endif /* _DW_MMC_ZX_H_ */
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index a5244435556b..2f4de30f650b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1952,9 +1952,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
return true;
}
-static void dw_mci_tasklet_func(unsigned long priv)
+static void dw_mci_tasklet_func(struct tasklet_struct *t)
{
- struct dw_mci *host = (struct dw_mci *)priv;
+ struct dw_mci *host = from_tasklet(host, t, tasklet);
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
@@ -3308,7 +3308,7 @@ int dw_mci_probe(struct dw_mci *host)
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
- tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+ tasklet_setup(&host->tasklet, dw_mci_tasklet_func);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
if (ret)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index a1f92fed2a55..b3c636edbb46 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -152,7 +152,6 @@ struct jz4740_mmc_host {
enum jz4740_mmc_version version;
int irq;
- int card_detect_irq;
void __iomem *base;
struct resource *mem_res;
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index 13f6a2c0ed04..eb6c02bc4a02 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -227,7 +227,6 @@ static void meson_mmc_get_transfer_mode(struct mmc_host *mmc,
struct mmc_data *data = mrq->data;
struct scatterlist *sg;
int i;
- bool use_desc_chain_mode = true;
/*
* When Controller DMA cannot directly access DDR memory, disable
@@ -237,25 +236,33 @@ static void meson_mmc_get_transfer_mode(struct mmc_host *mmc,
if (host->dram_access_quirk)
return;
- /*
- * Broken SDIO with AP6255-based WiFi on Khadas VIM Pro has been
- * reported. For some strange reason this occurs in descriptor
- * chain mode only. So let's fall back to bounce buffer mode
- * for command SD_IO_RW_EXTENDED.
- */
- if (mrq->cmd->opcode == SD_IO_RW_EXTENDED)
- return;
+ if (data->blocks > 1) {
+ /*
+ * In block mode DMA descriptor format, "length" field indicates
+ * number of blocks and there is no way to pass DMA size that
+ * is not multiple of SDIO block size, making it impossible to
+ * tie more than one memory buffer with single SDIO block.
+ * Block mode sg buffer size should be aligned with SDIO block
+ * size, otherwise chain mode could not be used.
+ */
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->length % data->blksz) {
+ WARN_ONCE(1, "unaligned sg len %u blksize %u\n",
+ sg->length, data->blksz);
+ return;
+ }
+ }
+ }
- for_each_sg(data->sg, sg, data->sg_len, i)
+ for_each_sg(data->sg, sg, data->sg_len, i) {
/* check for 8 byte alignment */
- if (sg->offset & 7) {
+ if (sg->offset % 8) {
WARN_ONCE(1, "unaligned scatterlist buffer\n");
- use_desc_chain_mode = false;
- break;
+ return;
}
+ }
- if (use_desc_chain_mode)
- data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE;
+ data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE;
}
static inline bool meson_mmc_desc_chain_mode(const struct mmc_data *data)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b5a41a7ce165..17dbc81c221e 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -36,6 +36,7 @@
#include <linux/types.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
+#include <linux/gpio/consumer.h>
#include <asm/div64.h>
#include <asm/io.h>
@@ -1888,6 +1889,65 @@ static struct mmc_host_ops mmci_ops = {
.start_signal_voltage_switch = mmci_sig_volt_switch,
};
+static void mmci_probe_level_translator(struct mmc_host *mmc)
+{
+ struct device *dev = mmc_dev(mmc);
+ struct mmci_host *host = mmc_priv(mmc);
+ struct gpio_desc *cmd_gpio;
+ struct gpio_desc *ck_gpio;
+ struct gpio_desc *ckin_gpio;
+ int clk_hi, clk_lo;
+
+ /*
+ * Assume the level translator is present if st,use-ckin is set.
+ * This is to cater for DTs which do not implement this test.
+ */
+ host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
+
+ cmd_gpio = gpiod_get(dev, "st,cmd", GPIOD_OUT_HIGH);
+ if (IS_ERR(cmd_gpio))
+ goto exit_cmd;
+
+ ck_gpio = gpiod_get(dev, "st,ck", GPIOD_OUT_HIGH);
+ if (IS_ERR(ck_gpio))
+ goto exit_ck;
+
+ ckin_gpio = gpiod_get(dev, "st,ckin", GPIOD_IN);
+ if (IS_ERR(ckin_gpio))
+ goto exit_ckin;
+
+ /* All GPIOs are valid, test whether level translator works */
+
+ /* Sample CKIN */
+ clk_hi = !!gpiod_get_value(ckin_gpio);
+
+ /* Set CK low */
+ gpiod_set_value(ck_gpio, 0);
+
+ /* Sample CKIN */
+ clk_lo = !!gpiod_get_value(ckin_gpio);
+
+ /* Tristate all */
+ gpiod_direction_input(cmd_gpio);
+ gpiod_direction_input(ck_gpio);
+
+ /* Level translator is present if CK signal is propagated to CKIN */
+ if (!clk_hi || clk_lo) {
+ host->clk_reg_add &= ~MCI_STM32_CLK_SELCKIN;
+ dev_warn(dev,
+ "Level translator inoperable, CK signal not detected on CKIN, disabling.\n");
+ }
+
+ gpiod_put(ckin_gpio);
+
+exit_ckin:
+ gpiod_put(ck_gpio);
+exit_ck:
+ gpiod_put(cmd_gpio);
+exit_cmd:
+ pinctrl_select_default_state(dev);
+}
+
static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -1913,7 +1973,7 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
if (of_get_property(np, "st,neg-edge", NULL))
host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;
if (of_get_property(np, "st,use-ckin", NULL))
- host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
+ mmci_probe_level_translator(mmc);
if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
@@ -1949,15 +2009,15 @@ static int mmci_probe(struct amba_device *dev,
if (!mmc)
return -ENOMEM;
- ret = mmci_of_parse(np, mmc);
- if (ret)
- goto host_free;
-
host = mmc_priv(mmc);
host->mmc = mmc;
host->mmc_ops = &mmci_ops;
mmc->ops = &mmci_ops;
+ ret = mmci_of_parse(np, mmc);
+ if (ret)
+ goto host_free;
+
/*
* Some variant (STM32) doesn't have opendrain bit, nevertheless
* pins can be set accordingly using pinctrl
@@ -2195,7 +2255,7 @@ static int mmci_probe(struct amba_device *dev,
return ret;
}
-static int mmci_remove(struct amba_device *dev)
+static void mmci_remove(struct amba_device *dev)
{
struct mmc_host *mmc = amba_get_drvdata(dev);
@@ -2223,8 +2283,6 @@ static int mmci_remove(struct amba_device *dev)
clk_disable_unprepare(host->clk);
mmc_free_host(mmc);
}
-
- return 0;
}
#ifdef CONFIG_PM
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index de09c6347524..898ed1b023df 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -1127,13 +1127,13 @@ static void msdc_track_cmd_data(struct msdc_host *host,
static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
{
unsigned long flags;
- bool ret;
- ret = cancel_delayed_work(&host->req_timeout);
- if (!ret) {
- /* delay work already running */
- return;
- }
+ /*
+ * No need check the return value of cancel_delayed_work, as only ONE
+ * path will go here!
+ */
+ cancel_delayed_work(&host->req_timeout);
+
spin_lock_irqsave(&host->lock, flags);
host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
@@ -1155,7 +1155,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
bool done = false;
bool sbc_error;
unsigned long flags;
- u32 *rsp = cmd->resp;
+ u32 *rsp;
if (mrq->sbc && cmd == mrq->cmd &&
(events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR
@@ -1176,6 +1176,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
if (done)
return true;
+ rsp = cmd->resp;
sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
@@ -1363,7 +1364,7 @@ static void msdc_data_xfer_next(struct msdc_host *host,
static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
struct mmc_request *mrq, struct mmc_data *data)
{
- struct mmc_command *stop = data->stop;
+ struct mmc_command *stop;
unsigned long flags;
bool done;
unsigned int check_data = events &
@@ -1379,6 +1380,7 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
if (done)
return true;
+ stop = data->stop;
if (check_data || (stop && stop->error)) {
dev_dbg(host->dev, "DMA status: 0x%8X\n",
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 56bbc6cd9c84..947581de7860 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -628,7 +628,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
ret = mmc_of_parse(mmc);
if (ret)
- goto out_clk_disable;
+ goto out_free_dma;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 6aa0537f1f84..5e5af34090f1 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -878,9 +878,9 @@ static void mmc_omap_cover_timer(struct timer_list *t)
tasklet_schedule(&slot->cover_tasklet);
}
-static void mmc_omap_cover_handler(unsigned long param)
+static void mmc_omap_cover_handler(struct tasklet_struct *t)
{
- struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
+ struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet);
int cover_open = mmc_omap_cover_is_open(slot);
mmc_detect_change(slot->mmc, 0);
@@ -1269,8 +1269,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
if (slot->pdata->get_cover_state != NULL) {
timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
- tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
- (unsigned long)slot);
+ tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler);
}
r = mmc_add_host(mmc);
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index aa9cc49206d1..2f8038d69f67 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -177,7 +177,7 @@ struct omap_hsmmc_host {
struct regulator *pbias;
bool pbias_enabled;
void __iomem *base;
- int vqmmc_enabled;
+ bool vqmmc_enabled;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
unsigned int dma_len;
@@ -232,7 +232,7 @@ static int omap_hsmmc_enable_supply(struct mmc_host *mmc)
dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n");
goto err_vqmmc;
}
- host->vqmmc_enabled = 1;
+ host->vqmmc_enabled = true;
}
return 0;
@@ -256,7 +256,7 @@ static int omap_hsmmc_disable_supply(struct mmc_host *mmc)
dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n");
return ret;
}
- host->vqmmc_enabled = 0;
+ host->vqmmc_enabled = false;
}
if (!IS_ERR(mmc->supply.vmmc)) {
@@ -285,22 +285,22 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on)
return 0;
if (power_on) {
- if (host->pbias_enabled == 0) {
+ if (!host->pbias_enabled) {
ret = regulator_enable(host->pbias);
if (ret) {
dev_err(host->dev, "pbias reg enable fail\n");
return ret;
}
- host->pbias_enabled = 1;
+ host->pbias_enabled = true;
}
} else {
- if (host->pbias_enabled == 1) {
+ if (host->pbias_enabled) {
ret = regulator_disable(host->pbias);
if (ret) {
dev_err(host->dev, "pbias reg disable fail\n");
return ret;
}
- host->pbias_enabled = 0;
+ host->pbias_enabled = false;
}
}
@@ -1861,8 +1861,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->base = base + pdata->reg_offset;
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
- host->pbias_enabled = 0;
- host->vqmmc_enabled = 0;
+ host->pbias_enabled = false;
+ host->vqmmc_enabled = false;
platform_set_drvdata(pdev, host);
diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c
index 53b81582f1af..5490962dc8e5 100644
--- a/drivers/mmc/host/owl-mmc.c
+++ b/drivers/mmc/host/owl-mmc.c
@@ -640,7 +640,7 @@ static int owl_mmc_probe(struct platform_device *pdev)
owl_host->irq = platform_get_irq(pdev, 0);
if (owl_host->irq < 0) {
ret = -EINVAL;
- goto err_free_host;
+ goto err_release_channel;
}
ret = devm_request_irq(&pdev->dev, owl_host->irq, owl_irq_handler,
@@ -648,19 +648,21 @@ static int owl_mmc_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %d\n",
owl_host->irq);
- goto err_free_host;
+ goto err_release_channel;
}
ret = mmc_add_host(mmc);
if (ret) {
dev_err(&pdev->dev, "Failed to add host\n");
- goto err_free_host;
+ goto err_release_channel;
}
dev_dbg(&pdev->dev, "Owl MMC Controller Initialized\n");
return 0;
+err_release_channel:
+ dma_release_channel(owl_host->dma);
err_free_host:
mmc_free_host(mmc);
@@ -674,6 +676,7 @@ static int owl_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
disable_irq(owl_host->irq);
+ dma_release_channel(owl_host->dma);
mmc_free_host(mmc);
return 0;
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 38f028e70633..158c21e5a942 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -768,10 +768,12 @@ static bool renesas_sdhi_auto_correction(struct tmio_mmc_host *host)
return false;
}
-static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
+static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host,
+ struct mmc_request *mrq)
{
struct renesas_sdhi *priv = host_to_priv(host);
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
+ bool ret = false;
/*
* Skip checking SCC errors when running on 4 taps in HS400 mode as
@@ -785,11 +787,19 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
if (mmc_doing_tune(host->mmc))
return false;
+ if (((mrq->cmd->error == -ETIMEDOUT) ||
+ (mrq->data && mrq->data->error == -ETIMEDOUT)) &&
+ ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ (host->ops.get_cd && host->ops.get_cd(host->mmc))))
+ ret |= true;
+
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN)
- return renesas_sdhi_auto_correction(host);
+ ret |= renesas_sdhi_auto_correction(host);
+ else
+ ret |= renesas_sdhi_manual_correction(host, use_4tap);
- return renesas_sdhi_manual_correction(host, use_4tap);
+ return ret;
}
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index fe13e1ea22dc..ff97f15e317c 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -56,6 +56,12 @@
#define INFO2_DTRANERR1 BIT(17)
#define INFO2_DTRANERR0 BIT(16)
+enum renesas_sdhi_dma_cookie {
+ COOKIE_UNMAPPED,
+ COOKIE_PRE_MAPPED,
+ COOKIE_MAPPED,
+};
+
/*
* Specification of this driver:
* - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma
@@ -172,6 +178,50 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) {
tasklet_schedule(&priv->dma_priv.dma_complete);
}
+/*
+ * renesas_sdhi_internal_dmac_map() will be called with two difference
+ * sg pointers in two mmc_data by .pre_req(), but tmio host can have a single
+ * sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg
+ * pointer in a mmc_data instead of host->sg_ptr.
+ */
+static void
+renesas_sdhi_internal_dmac_unmap(struct tmio_mmc_host *host,
+ struct mmc_data *data,
+ enum renesas_sdhi_dma_cookie cookie)
+{
+ bool unmap = cookie == COOKIE_UNMAPPED ? (data->host_cookie != cookie) :
+ (data->host_cookie == cookie);
+
+ if (unmap) {
+ dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+ mmc_get_dma_dir(data));
+ data->host_cookie = COOKIE_UNMAPPED;
+ }
+}
+
+static bool
+renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host,
+ struct mmc_data *data,
+ enum renesas_sdhi_dma_cookie cookie)
+{
+ if (data->host_cookie == COOKIE_PRE_MAPPED)
+ return true;
+
+ if (!dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+ mmc_get_dma_dir(data)))
+ return false;
+
+ data->host_cookie = cookie;
+
+ /* This DMAC cannot handle if buffer is not 128-bytes alignment */
+ if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) {
+ renesas_sdhi_internal_dmac_unmap(host, data, cookie);
+ return false;
+ }
+
+ return true;
+}
+
static void
renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
struct mmc_data *data)
@@ -182,14 +232,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags))
dtran_mode |= DTRAN_MODE_ADDR_MODE;
- if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len,
- mmc_get_dma_dir(data)))
+ if (!renesas_sdhi_internal_dmac_map(host, data, COOKIE_MAPPED))
goto force_pio;
- /* This DMAC cannot handle if buffer is not 8-bytes alignment */
- if (!IS_ALIGNED(sg_dma_address(sg), 8))
- goto force_pio_with_unmap;
-
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
@@ -212,7 +257,7 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
return;
force_pio_with_unmap:
- dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data));
+ renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED);
force_pio:
renesas_sdhi_internal_dmac_enable_dma(host, false);
@@ -245,7 +290,7 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
dir = DMA_TO_DEVICE;
renesas_sdhi_internal_dmac_enable_dma(host, false);
- dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);
+ renesas_sdhi_internal_dmac_unmap(host, host->data, COOKIE_MAPPED);
if (dir == DMA_FROM_DEVICE)
clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
@@ -274,6 +319,32 @@ static void renesas_sdhi_internal_dmac_end_dma(struct tmio_mmc_host *host)
renesas_sdhi_internal_dmac_complete(host);
}
+static void renesas_sdhi_internal_dmac_post_req(struct mmc_host *mmc,
+ struct mmc_request *mrq,
+ int err)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!data)
+ return;
+
+ renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED);
+}
+
+static void renesas_sdhi_internal_dmac_pre_req(struct mmc_host *mmc,
+ struct mmc_request *mrq)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (!data)
+ return;
+
+ data->host_cookie = COOKIE_UNMAPPED;
+ renesas_sdhi_internal_dmac_map(host, data, COOKIE_PRE_MAPPED);
+}
+
static void
renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
struct tmio_mmc_data *pdata)
@@ -295,6 +366,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
tasklet_init(&host->dma_issue,
renesas_sdhi_internal_dmac_issue_tasklet_fn,
(unsigned long)host);
+
+ /* Add pre_req and post_req */
+ host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req;
+ host->ops.post_req = renesas_sdhi_internal_dmac_post_req;
}
static void
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index e6f5bbce5685..4ca937415734 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -906,6 +906,8 @@ static int sd_power_on(struct realtek_pci_sdmmc *host)
if (host->power_state == SDMMC_POWER_ON)
return 0;
+ msleep(100);
+
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
@@ -1425,7 +1427,8 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
if (pcr->rtd3_en)
mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM;
- mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
+ mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
+ MMC_CAP2_NO_SDIO;
mmc->max_current_330 = 400;
mmc->max_current_180 = 800;
mmc->ops = &realtek_pci_sdmmc_ops;
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index a33a7823c265..0ca6f6d30b75 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -540,9 +540,9 @@ static void do_pio_write(struct s3cmci_host *host)
enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
}
-static void pio_tasklet(unsigned long data)
+static void pio_tasklet(struct tasklet_struct *t)
{
- struct s3cmci_host *host = (struct s3cmci_host *) data;
+ struct s3cmci_host *host = from_tasklet(host, t, pio_tasklet);
s3cmci_disable_irq(host, true);
@@ -1532,7 +1532,7 @@ static int s3cmci_probe(struct platform_device *pdev)
host->pdata = pdev->dev.platform_data;
spin_lock_init(&host->complete_lock);
- tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+ tasklet_setup(&host->pio_tasklet, pio_tasklet);
if (host->is2440) {
host->sdiimsk = S3C2440_SDIIMSK;
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index bbf3496f4495..f9780c65ebe9 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -314,11 +314,7 @@ err_clk:
static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
{
- int ret;
-
- ret = sdhci_pltfm_unregister(pdev);
- if (ret)
- dev_err(&pdev->dev, "failed to shutdown\n");
+ sdhci_pltfm_suspend(&pdev->dev);
}
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 16ed19f47939..a20459744d21 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1666,9 +1666,10 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
- int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+ int dead;
pm_runtime_get_sync(&pdev->dev);
+ dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index c9434b461aab..ddeaf8e1f72f 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -296,9 +296,27 @@ static const struct of_device_id sdhci_iproc_of_match[] = {
MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
#ifdef CONFIG_ACPI
+/*
+ * This is a duplicate of bcm2835_(pltfrm_)data without caps quirks
+ * which are provided by the ACPI table.
+ */
+static const struct sdhci_pltfm_data sdhci_bcm_arasan_data = {
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_NO_HISPD_BIT,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .ops = &sdhci_iproc_32only_ops,
+};
+
+static const struct sdhci_iproc_data bcm_arasan_data = {
+ .pdata = &sdhci_bcm_arasan_data,
+};
+
static const struct acpi_device_id sdhci_iproc_acpi_ids[] = {
{ .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data },
{ .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data },
+ { .id = "BCM2847", .driver_data = (kernel_ulong_t)&bcm_arasan_data },
+ { .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 9c7927b03253..5e1da4df096f 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -13,6 +13,7 @@
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
+#include <linux/qcom_scm.h>
#include <linux/regulator/consumer.h>
#include <linux/interconnect.h>
#include <linux/pinctrl/consumer.h>
@@ -255,10 +256,12 @@ struct sdhci_msm_variant_info {
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
+ void __iomem *ice_mem; /* MSM ICE mapped address (if available) */
int pwr_irq; /* power irq */
struct clk *bus_clk; /* SDHC bus voter clock */
struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/
- struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
+ /* core, iface, cal, sleep, and ice clocks */
+ struct clk_bulk_data bulk_clks[5];
unsigned long clk_rate;
struct mmc_host *mmc;
struct opp_table *opp_table;
@@ -327,8 +330,7 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val,
writel_relaxed(val, host->ioaddr + offset);
}
-static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
- unsigned int clock)
+static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host)
{
struct mmc_ios ios = host->mmc->ios;
/*
@@ -341,8 +343,8 @@ static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
ios.timing == MMC_TIMING_MMC_DDR52 ||
ios.timing == MMC_TIMING_MMC_HS400 ||
host->flags & SDHCI_HS400_TUNING)
- clock *= 2;
- return clock;
+ return 2;
+ return 1;
}
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
@@ -352,20 +354,36 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
struct mmc_ios curr_ios = host->mmc->ios;
struct clk *core_clk = msm_host->bulk_clks[0].clk;
+ unsigned long achieved_rate;
+ unsigned int desired_rate;
+ unsigned int mult;
int rc;
- clock = msm_get_clock_rate_for_bus_mode(host, clock);
- rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock);
+ mult = msm_get_clock_mult_for_bus_mode(host);
+ desired_rate = clock * mult;
+ rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
- mmc_hostname(host->mmc), clock,
- curr_ios.timing);
+ mmc_hostname(host->mmc), desired_rate, curr_ios.timing);
return;
}
- msm_host->clk_rate = clock;
+
+ /*
+ * Qualcomm clock drivers by default round clock _up_ if they can't
+ * make the requested rate. This is not good for SD. Yell if we
+ * encounter it.
+ */
+ achieved_rate = clk_get_rate(core_clk);
+ if (achieved_rate > desired_rate)
+ pr_warn("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n",
+ mmc_hostname(host->mmc), desired_rate, achieved_rate);
+ host->mmc->actual_clock = achieved_rate / mult;
+
+ /* Stash the rate we requested to use in sdhci_msm_runtime_resume() */
+ msm_host->clk_rate = desired_rate;
+
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
- mmc_hostname(host->mmc), clk_get_rate(core_clk),
- curr_ios.timing);
+ mmc_hostname(host->mmc), achieved_rate, curr_ios.timing);
}
/* Platform specific tuning */
@@ -1744,13 +1762,6 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
static void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk;
- /*
- * Keep actual_clock as zero -
- * - since there is no divider used so no need of having actual_clock.
- * - MSM controller uses SDCLK for data timeout calculation. If
- * actual_clock is zero, host->clock is taken for calculation.
- */
- host->mmc->actual_clock = 0;
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
@@ -1773,7 +1784,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
if (!clock) {
- msm_host->clk_rate = clock;
+ host->mmc->actual_clock = msm_host->clk_rate = 0;
goto out;
}
@@ -1786,6 +1797,246 @@ out:
/*****************************************************************************\
* *
+ * Inline Crypto Engine (ICE) support *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_MMC_CRYPTO
+
+#define AES_256_XTS_KEY_SIZE 64
+
+/* QCOM ICE registers */
+
+#define QCOM_ICE_REG_VERSION 0x0008
+
+#define QCOM_ICE_REG_FUSE_SETTING 0x0010
+#define QCOM_ICE_FUSE_SETTING_MASK 0x1
+#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2
+#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4
+
+#define QCOM_ICE_REG_BIST_STATUS 0x0070
+#define QCOM_ICE_BIST_STATUS_MASK 0xF0000000
+
+#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000
+
+#define sdhci_msm_ice_writel(host, val, reg) \
+ writel((val), (host)->ice_mem + (reg))
+#define sdhci_msm_ice_readl(host, reg) \
+ readl((host)->ice_mem + (reg))
+
+static bool sdhci_msm_ice_supported(struct sdhci_msm_host *msm_host)
+{
+ struct device *dev = mmc_dev(msm_host->mmc);
+ u32 regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_VERSION);
+ int major = regval >> 24;
+ int minor = (regval >> 16) & 0xFF;
+ int step = regval & 0xFFFF;
+
+ /* For now this driver only supports ICE version 3. */
+ if (major != 3) {
+ dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
+ major, minor, step);
+ return false;
+ }
+
+ dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n",
+ major, minor, step);
+
+ /* If fuses are blown, ICE might not work in the standard way. */
+ regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_FUSE_SETTING);
+ if (regval & (QCOM_ICE_FUSE_SETTING_MASK |
+ QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK |
+ QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
+ dev_warn(dev, "Fuses are blown; ICE is unusable!\n");
+ return false;
+ }
+ return true;
+}
+
+static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
+{
+ return devm_clk_get(dev, "ice");
+}
+
+static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
+ struct cqhci_host *cq_host)
+{
+ struct mmc_host *mmc = msm_host->mmc;
+ struct device *dev = mmc_dev(mmc);
+ struct resource *res;
+ int err;
+
+ if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
+ return 0;
+
+ res = platform_get_resource_byname(msm_host->pdev, IORESOURCE_MEM,
+ "ice");
+ if (!res) {
+ dev_warn(dev, "ICE registers not found\n");
+ goto disable;
+ }
+
+ if (!qcom_scm_ice_available()) {
+ dev_warn(dev, "ICE SCM interface not found\n");
+ goto disable;
+ }
+
+ msm_host->ice_mem = devm_ioremap_resource(dev, res);
+ if (IS_ERR(msm_host->ice_mem)) {
+ err = PTR_ERR(msm_host->ice_mem);
+ dev_err(dev, "Failed to map ICE registers; err=%d\n", err);
+ return err;
+ }
+
+ if (!sdhci_msm_ice_supported(msm_host))
+ goto disable;
+
+ mmc->caps2 |= MMC_CAP2_CRYPTO;
+ return 0;
+
+disable:
+ dev_warn(dev, "Disabling inline encryption support\n");
+ return 0;
+}
+
+static void sdhci_msm_ice_low_power_mode_enable(struct sdhci_msm_host *msm_host)
+{
+ u32 regval;
+
+ regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
+ /*
+ * Enable low power mode sequence
+ * [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0
+ */
+ regval |= 0x7000;
+ sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
+}
+
+static void sdhci_msm_ice_optimization_enable(struct sdhci_msm_host *msm_host)
+{
+ u32 regval;
+
+ /* ICE Optimizations Enable Sequence */
+ regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL);
+ regval |= 0xD807100;
+ /* ICE HPG requires delay before writing */
+ udelay(5);
+ sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL);
+ udelay(5);
+}
+
+/*
+ * Wait until the ICE BIST (built-in self-test) has completed.
+ *
+ * This may be necessary before ICE can be used.
+ *
+ * Note that we don't really care whether the BIST passed or failed; we really
+ * just want to make sure that it isn't still running. This is because (a) the
+ * BIST is a FIPS compliance thing that never fails in practice, (b) ICE is
+ * documented to reject crypto requests if the BIST fails, so we needn't do it
+ * in software too, and (c) properly testing storage encryption requires testing
+ * the full storage stack anyway, and not relying on hardware-level self-tests.
+ */
+static int sdhci_msm_ice_wait_bist_status(struct sdhci_msm_host *msm_host)
+{
+ u32 regval;
+ int err;
+
+ err = readl_poll_timeout(msm_host->ice_mem + QCOM_ICE_REG_BIST_STATUS,
+ regval, !(regval & QCOM_ICE_BIST_STATUS_MASK),
+ 50, 5000);
+ if (err)
+ dev_err(mmc_dev(msm_host->mmc),
+ "Timed out waiting for ICE self-test to complete\n");
+ return err;
+}
+
+static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
+{
+ if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO))
+ return;
+ sdhci_msm_ice_low_power_mode_enable(msm_host);
+ sdhci_msm_ice_optimization_enable(msm_host);
+ sdhci_msm_ice_wait_bist_status(msm_host);
+}
+
+static int __maybe_unused sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
+{
+ if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO))
+ return 0;
+ return sdhci_msm_ice_wait_bist_status(msm_host);
+}
+
+/*
+ * Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires
+ * vendor-specific SCM calls for this; it doesn't support the standard way.
+ */
+static int sdhci_msm_program_key(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg,
+ int slot)
+{
+ struct device *dev = mmc_dev(cq_host->mmc);
+ union cqhci_crypto_cap_entry cap;
+ union {
+ u8 bytes[AES_256_XTS_KEY_SIZE];
+ u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
+ } key;
+ int i;
+ int err;
+
+ if (!(cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE))
+ return qcom_scm_ice_invalidate_key(slot);
+
+ /* Only AES-256-XTS has been tested so far. */
+ cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx];
+ if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS ||
+ cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256) {
+ dev_err_ratelimited(dev,
+ "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n",
+ cap.algorithm_id, cap.key_size);
+ return -EINVAL;
+ }
+
+ memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE);
+
+ /*
+ * The SCM call byte-swaps the 32-bit words of the key. So we have to
+ * do the same, in order for the final key be correct.
+ */
+ for (i = 0; i < ARRAY_SIZE(key.words); i++)
+ __cpu_to_be32s(&key.words[i]);
+
+ err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE,
+ QCOM_SCM_ICE_CIPHER_AES_256_XTS,
+ cfg->data_unit_size);
+ memzero_explicit(&key, sizeof(key));
+ return err;
+}
+#else /* CONFIG_MMC_CRYPTO */
+static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev)
+{
+ return NULL;
+}
+
+static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
+ struct cqhci_host *cq_host)
+{
+ return 0;
+}
+
+static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
+{
+}
+
+static inline int __maybe_unused
+sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
+{
+ return 0;
+}
+#endif /* !CONFIG_MMC_CRYPTO */
+
+/*****************************************************************************\
+ * *
* MSM Command Queue Engine (CQE) *
* *
\*****************************************************************************/
@@ -1802,6 +2053,16 @@ static u32 sdhci_msm_cqe_irq(struct sdhci_host *host, u32 intmask)
return 0;
}
+static void sdhci_msm_cqe_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+
+ sdhci_cqe_enable(mmc);
+ sdhci_msm_ice_enable(msm_host);
+}
+
static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -1834,8 +2095,11 @@ static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
}
static const struct cqhci_host_ops sdhci_msm_cqhci_ops = {
- .enable = sdhci_cqe_enable,
+ .enable = sdhci_msm_cqe_enable,
.disable = sdhci_msm_cqe_disable,
+#ifdef CONFIG_MMC_CRYPTO
+ .program_key = sdhci_msm_program_key,
+#endif
};
static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
@@ -1871,6 +2135,10 @@ static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
+ ret = sdhci_msm_ice_init(msm_host, cq_host);
+ if (ret)
+ goto cleanup;
+
ret = cqhci_init(cq_host, host->mmc, dma64);
if (ret) {
dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n",
@@ -2311,6 +2579,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
clk = NULL;
msm_host->bulk_clks[3].clk = clk;
+ clk = sdhci_msm_ice_get_clk(&pdev->dev);
+ if (IS_ERR(clk))
+ clk = NULL;
+ msm_host->bulk_clks[4].clk = clk;
+
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret)
@@ -2524,12 +2797,15 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
* Whenever core-clock is gated dynamically, it's needed to
* restore the SDR DLL settings when the clock is ungated.
*/
- if (msm_host->restore_dll_config && msm_host->clk_rate)
+ if (msm_host->restore_dll_config && msm_host->clk_rate) {
ret = sdhci_msm_restore_sdr_dll_config(host);
+ if (ret)
+ return ret;
+ }
dev_pm_opp_set_rate(dev, msm_host->clk_rate);
- return ret;
+ return sdhci_msm_ice_resume(msm_host);
}
static const struct dev_pm_ops sdhci_msm_pm_ops = {
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 4f3774bcda94..839965f7c717 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -1380,26 +1380,25 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
/**
* sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support)
+ * @host: The sdhci_host
+ * @value: The value to write
*
* This should be set based on the System Address Bus.
* 0: the Core supports only 32-bit System Address Bus.
* 1: the Core supports 64-bit System Address Bus.
*
- * NOTES:
- * - For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
- * Keem Bay does not support 64-bit access.
- *
- * @host: The sdhci_host
- * @value: The value to write
+ * NOTE:
+ * For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
+ * Keem Bay does not support 64-bit access.
*/
static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
- const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
- sdhci_arasan->soc_ctl_map;
+ const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
/* Having a map is optional */
+ soc_ctl_map = sdhci_arasan->soc_ctl_map;
if (!soc_ctl_map)
return;
@@ -1508,17 +1507,16 @@ cleanup:
static int sdhci_arasan_probe(struct platform_device *pdev)
{
int ret;
- const struct of_device_id *match;
struct device_node *node;
struct clk *clk_xin;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
struct sdhci_arasan_data *sdhci_arasan;
- struct device_node *np = pdev->dev.of_node;
const struct sdhci_arasan_of_data *data;
- match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
- data = match->data;
+ data = of_device_get_match_data(dev);
host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan));
if (IS_ERR(host))
@@ -1531,42 +1529,41 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
sdhci_arasan->clk_ops = data->clk_ops;
- node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
+ node = of_parse_phandle(np, "arasan,soc-ctl-syscon", 0);
if (node) {
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
of_node_put(node);
if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
- ret = dev_err_probe(&pdev->dev,
+ ret = dev_err_probe(dev,
PTR_ERR(sdhci_arasan->soc_ctl_base),
"Can't get syscon\n");
goto err_pltfm_free;
}
}
- sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
+ sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb");
if (IS_ERR(sdhci_arasan->clk_ahb)) {
- dev_err(&pdev->dev, "clk_ahb clock not found.\n");
- ret = PTR_ERR(sdhci_arasan->clk_ahb);
+ ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb),
+ "clk_ahb clock not found.\n");
goto err_pltfm_free;
}
- clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
+ clk_xin = devm_clk_get(dev, "clk_xin");
if (IS_ERR(clk_xin)) {
- dev_err(&pdev->dev, "clk_xin clock not found.\n");
- ret = PTR_ERR(clk_xin);
+ ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n");
goto err_pltfm_free;
}
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
if (ret) {
- dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
+ dev_err(dev, "Unable to enable AHB clock.\n");
goto err_pltfm_free;
}
ret = clk_prepare_enable(clk_xin);
if (ret) {
- dev_err(&pdev->dev, "Unable to enable SD clock.\n");
+ dev_err(dev, "Unable to enable SD clock.\n");
goto clk_dis_ahb;
}
@@ -1580,8 +1577,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
pltfm_host->clk = clk_xin;
- if (of_device_is_compatible(pdev->dev.of_node,
- "rockchip,rk3399-sdhci-5.1"))
+ if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1"))
sdhci_arasan_update_clockmultiplier(host, 0x0);
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
@@ -1595,7 +1591,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan_update_baseclkfreq(host);
- ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
+ ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, dev);
if (ret)
goto clk_disable_all;
@@ -1604,29 +1600,26 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
arasan_zynqmp_execute_tuning;
}
- arasan_dt_parse_clk_phases(&pdev->dev, &sdhci_arasan->clk_data);
+ arasan_dt_parse_clk_phases(dev, &sdhci_arasan->clk_data);
ret = mmc_of_parse(host->mmc);
if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "parsing dt failed (%d)\n", ret);
+ ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
goto unreg_clk;
}
sdhci_arasan->phy = ERR_PTR(-ENODEV);
- if (of_device_is_compatible(pdev->dev.of_node,
- "arasan,sdhci-5.1")) {
- sdhci_arasan->phy = devm_phy_get(&pdev->dev,
- "phy_arasan");
+ if (of_device_is_compatible(np, "arasan,sdhci-5.1")) {
+ sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan");
if (IS_ERR(sdhci_arasan->phy)) {
- ret = PTR_ERR(sdhci_arasan->phy);
- dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
+ ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->phy),
+ "No phy for arasan,sdhci-5.1.\n");
goto unreg_clk;
}
ret = phy_init(sdhci_arasan->phy);
if (ret < 0) {
- dev_err(&pdev->dev, "phy_init err.\n");
+ dev_err(dev, "phy_init err.\n");
goto unreg_clk;
}
@@ -1651,7 +1644,7 @@ err_add_host:
if (!IS_ERR(sdhci_arasan->phy))
phy_exit(sdhci_arasan->phy);
unreg_clk:
- sdhci_arasan_unregister_sdclk(&pdev->dev);
+ sdhci_arasan_unregister_sdclk(dev);
clk_disable_all:
clk_disable_unprepare(clk_xin);
clk_dis_ahb:
diff --git a/drivers/mmc/host/sdhci-of-aspeed-test.c b/drivers/mmc/host/sdhci-of-aspeed-test.c
new file mode 100644
index 000000000000..bb67d159b7d8
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-aspeed-test.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2020 IBM Corp. */
+
+#include <kunit/test.h>
+
+static void aspeed_sdhci_phase_ddr52(struct kunit *test)
+{
+ int rate = 52000000;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 0));
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 1));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 2));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 3));
+ KUNIT_EXPECT_EQ(test, 2,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 4));
+ KUNIT_EXPECT_EQ(test, 3,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 5));
+ KUNIT_EXPECT_EQ(test, 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 23));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 24));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 25));
+
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 180));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 181));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 182));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 183));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 2,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 184));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 3,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 185));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 203));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 204));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 205));
+}
+
+static void aspeed_sdhci_phase_hs200(struct kunit *test)
+{
+ int rate = 200000000;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 0));
+ KUNIT_EXPECT_EQ(test, 0,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 5));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 6));
+ KUNIT_EXPECT_EQ(test, 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 7));
+ KUNIT_EXPECT_EQ(test, 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 89));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 90));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 91));
+ KUNIT_EXPECT_EQ(test, 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 96));
+
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 180));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 185));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 186));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 187));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 269));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 270));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 271));
+ KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15,
+ aspeed_sdhci_phase_to_tap(NULL, rate, 276));
+}
+
+static struct kunit_case aspeed_sdhci_test_cases[] = {
+ KUNIT_CASE(aspeed_sdhci_phase_ddr52),
+ KUNIT_CASE(aspeed_sdhci_phase_hs200),
+ {}
+};
+
+static struct kunit_suite aspeed_sdhci_test_suite = {
+ .name = "sdhci-of-aspeed",
+ .test_cases = aspeed_sdhci_test_cases,
+};
+
+static struct kunit_suite *aspeed_sdc_test_suite_array[] = {
+ &aspeed_sdhci_test_suite,
+ NULL,
+};
+
+static struct kunit_suite **aspeed_sdc_test_suites
+ __used __section(".kunit_test_suites") = aspeed_sdc_test_suite_array;
diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c
index 4f008ba3280e..7d8692e90996 100644
--- a/drivers/mmc/host/sdhci-of-aspeed.c
+++ b/drivers/mmc/host/sdhci-of-aspeed.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/math64.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -16,9 +17,19 @@
#include "sdhci-pltfm.h"
-#define ASPEED_SDC_INFO 0x00
-#define ASPEED_SDC_S1MMC8 BIT(25)
-#define ASPEED_SDC_S0MMC8 BIT(24)
+#define ASPEED_SDC_INFO 0x00
+#define ASPEED_SDC_S1_MMC8 BIT(25)
+#define ASPEED_SDC_S0_MMC8 BIT(24)
+#define ASPEED_SDC_PHASE 0xf4
+#define ASPEED_SDC_S1_PHASE_IN GENMASK(25, 21)
+#define ASPEED_SDC_S0_PHASE_IN GENMASK(20, 16)
+#define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11)
+#define ASPEED_SDC_S1_PHASE_IN_EN BIT(10)
+#define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8)
+#define ASPEED_SDC_S0_PHASE_OUT GENMASK(7, 3)
+#define ASPEED_SDC_S0_PHASE_IN_EN BIT(2)
+#define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0)
+#define ASPEED_SDC_PHASE_MAX 31
struct aspeed_sdc {
struct clk *clk;
@@ -28,9 +39,37 @@ struct aspeed_sdc {
void __iomem *regs;
};
+struct aspeed_sdhci_tap_param {
+ bool valid;
+
+#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4)
+ u8 in;
+ u8 out;
+};
+
+struct aspeed_sdhci_tap_desc {
+ u32 tap_mask;
+ u32 enable_mask;
+ u8 enable_value;
+};
+
+struct aspeed_sdhci_phase_desc {
+ struct aspeed_sdhci_tap_desc in;
+ struct aspeed_sdhci_tap_desc out;
+};
+
+struct aspeed_sdhci_pdata {
+ unsigned int clk_div_start;
+ const struct aspeed_sdhci_phase_desc *phase_desc;
+ size_t nr_phase_descs;
+};
+
struct aspeed_sdhci {
+ const struct aspeed_sdhci_pdata *pdata;
struct aspeed_sdc *parent;
u32 width_mask;
+ struct mmc_clk_phase_map phase_map;
+ const struct aspeed_sdhci_phase_desc *phase_desc;
};
static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
@@ -50,14 +89,125 @@ static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
spin_unlock(&sdc->lock);
}
+static u32
+aspeed_sdc_set_phase_tap(const struct aspeed_sdhci_tap_desc *desc,
+ u8 tap, bool enable, u32 reg)
+{
+ reg &= ~(desc->enable_mask | desc->tap_mask);
+ if (enable) {
+ reg |= tap << __ffs(desc->tap_mask);
+ reg |= desc->enable_value << __ffs(desc->enable_mask);
+ }
+
+ return reg;
+}
+
+static void
+aspeed_sdc_set_phase_taps(struct aspeed_sdc *sdc,
+ const struct aspeed_sdhci_phase_desc *desc,
+ const struct aspeed_sdhci_tap_param *taps)
+{
+ u32 reg;
+
+ spin_lock(&sdc->lock);
+ reg = readl(sdc->regs + ASPEED_SDC_PHASE);
+
+ reg = aspeed_sdc_set_phase_tap(&desc->in, taps->in, taps->valid, reg);
+ reg = aspeed_sdc_set_phase_tap(&desc->out, taps->out, taps->valid, reg);
+
+ writel(reg, sdc->regs + ASPEED_SDC_PHASE);
+ spin_unlock(&sdc->lock);
+}
+
+#define PICOSECONDS_PER_SECOND 1000000000000ULL
+#define ASPEED_SDHCI_NR_TAPS 15
+/* Measured value with *handwave* environmentals and static loading */
+#define ASPEED_SDHCI_MAX_TAP_DELAY_PS 1253
+static int aspeed_sdhci_phase_to_tap(struct device *dev, unsigned long rate_hz,
+ int phase_deg)
+{
+ u64 phase_period_ps;
+ u64 prop_delay_ps;
+ u64 clk_period_ps;
+ unsigned int tap;
+ u8 inverted;
+
+ phase_deg %= 360;
+
+ if (phase_deg >= 180) {
+ inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK;
+ phase_deg -= 180;
+ dev_dbg(dev,
+ "Inverting clock to reduce phase correction from %d to %d degrees\n",
+ phase_deg + 180, phase_deg);
+ } else {
+ inverted = 0;
+ }
+
+ prop_delay_ps = ASPEED_SDHCI_MAX_TAP_DELAY_PS / ASPEED_SDHCI_NR_TAPS;
+ clk_period_ps = div_u64(PICOSECONDS_PER_SECOND, (u64)rate_hz);
+ phase_period_ps = div_u64((u64)phase_deg * clk_period_ps, 360ULL);
+
+ tap = div_u64(phase_period_ps, prop_delay_ps);
+ if (tap > ASPEED_SDHCI_NR_TAPS) {
+ dev_warn(dev,
+ "Requested out of range phase tap %d for %d degrees of phase compensation at %luHz, clamping to tap %d\n",
+ tap, phase_deg, rate_hz, ASPEED_SDHCI_NR_TAPS);
+ tap = ASPEED_SDHCI_NR_TAPS;
+ }
+
+ return inverted | tap;
+}
+
+static void
+aspeed_sdhci_phases_to_taps(struct device *dev, unsigned long rate,
+ const struct mmc_clk_phase *phases,
+ struct aspeed_sdhci_tap_param *taps)
+{
+ taps->valid = phases->valid;
+
+ if (!phases->valid)
+ return;
+
+ taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg);
+ taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg);
+}
+
+static void
+aspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate)
+{
+ struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps;
+ struct mmc_clk_phase *params;
+ struct aspeed_sdhci *sdhci;
+ struct device *dev;
+
+ dev = host->mmc->parent;
+ sdhci = sdhci_pltfm_priv(sdhci_priv(host));
+
+ if (!sdhci->phase_desc)
+ return;
+
+ params = &sdhci->phase_map.phase[host->timing];
+ aspeed_sdhci_phases_to_taps(dev, rate, params, taps);
+ aspeed_sdc_set_phase_taps(sdhci->parent, sdhci->phase_desc, taps);
+ dev_dbg(dev,
+ "Using taps [%d, %d] for [%d, %d] degrees of phase correction at %luHz (%d)\n",
+ taps->in & ASPEED_SDHCI_NR_TAPS,
+ taps->out & ASPEED_SDHCI_NR_TAPS,
+ params->in_deg, params->out_deg, rate, host->timing);
+}
+
static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host;
- unsigned long parent;
+ unsigned long parent, bus;
+ struct aspeed_sdhci *sdhci;
int div;
u16 clk;
pltfm_host = sdhci_priv(host);
+ sdhci = sdhci_pltfm_priv(pltfm_host);
+
parent = clk_get_rate(pltfm_host->clk);
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
@@ -68,14 +218,34 @@ static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (WARN_ON(clock > host->max_clk))
clock = host->max_clk;
- for (div = 2; div < 256; div *= 2) {
- if ((parent / div) <= clock)
+ /*
+ * Regarding the AST2600:
+ *
+ * If (EMMC12C[7:6], EMMC12C[15:8] == 0) then
+ * period of SDCLK = period of SDMCLK.
+ *
+ * If (EMMC12C[7:6], EMMC12C[15:8] != 0) then
+ * period of SDCLK = period of SDMCLK * 2 * (EMMC12C[7:6], EMMC[15:8])
+ *
+ * If you keep EMMC12C[7:6] = 0 and EMMC12C[15:8] as one-hot,
+ * 0x1/0x2/0x4/etc, you will find it is compatible to AST2400 or AST2500
+ *
+ * Keep the one-hot behaviour for backwards compatibility except for
+ * supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture
+ * the 0-value capability in clk_div_start.
+ */
+ for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) {
+ bus = parent / div;
+ if (bus <= clock)
break;
}
+
div >>= 1;
clk = div << SDHCI_DIVIDER_SHIFT;
+ aspeed_sdhci_configure_phase(host, bus);
+
sdhci_enable_clk(host, clk);
}
@@ -157,6 +327,7 @@ static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
static int aspeed_sdhci_probe(struct platform_device *pdev)
{
+ const struct aspeed_sdhci_pdata *aspeed_pdata;
struct sdhci_pltfm_host *pltfm_host;
struct aspeed_sdhci *dev;
struct sdhci_host *host;
@@ -164,12 +335,19 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
int slot;
int ret;
+ aspeed_pdata = of_device_get_match_data(&pdev->dev);
+ if (!aspeed_pdata) {
+ dev_err(&pdev->dev, "Missing platform configuration data\n");
+ return -EINVAL;
+ }
+
host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
dev = sdhci_pltfm_priv(pltfm_host);
+ dev->pdata = aspeed_pdata;
dev->parent = dev_get_drvdata(pdev->dev.parent);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -180,8 +358,17 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
else if (slot >= 2)
return -EINVAL;
- dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
- dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
+ if (slot < dev->pdata->nr_phase_descs) {
+ dev->phase_desc = &dev->pdata->phase_desc[slot];
+ } else {
+ dev_info(&pdev->dev,
+ "Phase control not supported for slot %d\n", slot);
+ dev->phase_desc = NULL;
+ }
+
+ dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8;
+
+ dev_info(&pdev->dev, "Configured for slot %d\n", slot);
sdhci_get_of_property(pdev);
@@ -199,6 +386,9 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
if (ret)
goto err_sdhci_add;
+ if (dev->phase_desc)
+ mmc_of_parse_clk_phase(host->mmc, &dev->phase_map);
+
ret = sdhci_add_host(host);
if (ret)
goto err_sdhci_add;
@@ -230,10 +420,49 @@ static int aspeed_sdhci_remove(struct platform_device *pdev)
return 0;
}
+static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = {
+ .clk_div_start = 2,
+};
+
+static const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = {
+ /* SDHCI/Slot 0 */
+ [0] = {
+ .in = {
+ .tap_mask = ASPEED_SDC_S0_PHASE_IN,
+ .enable_mask = ASPEED_SDC_S0_PHASE_IN_EN,
+ .enable_value = 1,
+ },
+ .out = {
+ .tap_mask = ASPEED_SDC_S0_PHASE_OUT,
+ .enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN,
+ .enable_value = 3,
+ },
+ },
+ /* SDHCI/Slot 1 */
+ [1] = {
+ .in = {
+ .tap_mask = ASPEED_SDC_S1_PHASE_IN,
+ .enable_mask = ASPEED_SDC_S1_PHASE_IN_EN,
+ .enable_value = 1,
+ },
+ .out = {
+ .tap_mask = ASPEED_SDC_S1_PHASE_OUT,
+ .enable_mask = ASPEED_SDC_S1_PHASE_OUT_EN,
+ .enable_value = 3,
+ },
+ },
+};
+
+static const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = {
+ .clk_div_start = 1,
+ .phase_desc = ast2600_sdhci_phase,
+ .nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase),
+};
+
static const struct of_device_id aspeed_sdhci_of_match[] = {
- { .compatible = "aspeed,ast2400-sdhci", },
- { .compatible = "aspeed,ast2500-sdhci", },
- { .compatible = "aspeed,ast2600-sdhci", },
+ { .compatible = "aspeed,ast2400-sdhci", .data = &ast2400_sdhci_pdata, },
+ { .compatible = "aspeed,ast2500-sdhci", .data = &ast2400_sdhci_pdata, },
+ { .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, },
{ }
};
@@ -327,6 +556,29 @@ static struct platform_driver aspeed_sdc_driver = {
.remove = aspeed_sdc_remove,
};
+#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST)
+#include "sdhci-of-aspeed-test.c"
+
+static inline int aspeed_sdc_tests_init(void)
+{
+ return __kunit_test_suites_init(aspeed_sdc_test_suites);
+}
+
+static inline void aspeed_sdc_tests_exit(void)
+{
+ __kunit_test_suites_exit(aspeed_sdc_test_suites);
+}
+#else
+static inline int aspeed_sdc_tests_init(void)
+{
+ return 0;
+}
+
+static inline void aspeed_sdc_tests_exit(void)
+{
+}
+#endif
+
static int __init aspeed_sdc_init(void)
{
int rc;
@@ -337,7 +589,18 @@ static int __init aspeed_sdc_init(void)
rc = platform_driver_register(&aspeed_sdc_driver);
if (rc < 0)
- platform_driver_unregister(&aspeed_sdhci_driver);
+ goto cleanup_sdhci;
+
+ rc = aspeed_sdc_tests_init();
+ if (rc < 0) {
+ platform_driver_unregister(&aspeed_sdc_driver);
+ goto cleanup_sdhci;
+ }
+
+ return 0;
+
+cleanup_sdhci:
+ platform_driver_unregister(&aspeed_sdhci_driver);
return rc;
}
@@ -345,6 +608,8 @@ module_init(aspeed_sdc_init);
static void __exit aspeed_sdc_exit(void)
{
+ aspeed_sdc_tests_exit();
+
platform_driver_unregister(&aspeed_sdc_driver);
platform_driver_unregister(&aspeed_sdhci_driver);
}
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 4b673792b5a4..59d8d96ce206 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -16,6 +16,8 @@
#include "sdhci-pltfm.h"
+#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
+
/* DWCMSHC specific Mode Select value */
#define DWCMSHC_CTRL_HS400 0x7
@@ -49,6 +51,29 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
+static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc,
+ struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ /*
+ * No matter V4 is enabled or not, ARGUMENT2 register is 32-bit
+ * block count register which doesn't support stuff bits of
+ * CMD23 argument on dwcmsch host controller.
+ */
+ if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF))
+ host->flags &= ~SDHCI_AUTO_CMD23;
+ else
+ host->flags |= SDHCI_AUTO_CMD23;
+}
+
+static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ dwcmshc_check_auto_cmd23(mmc, mrq);
+
+ sdhci_request(mmc, mrq);
+}
+
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing)
{
@@ -87,6 +112,7 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
.ops = &sdhci_dwcmshc_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
static int dwcmshc_probe(struct platform_device *pdev)
@@ -133,6 +159,8 @@ static int dwcmshc_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
+ host->mmc_host_ops.request = dwcmshc_request;
+
err = sdhci_add_host(host);
if (err)
goto err_clk;
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index b85edd62e7f0..4a0f69b97a78 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -84,12 +84,21 @@
#define GLI_9763E_VHS_REV_W 0x2
#define PCIE_GLI_9763E_MB 0x888
#define GLI_9763E_MB_CMDQ_OFF BIT(19)
+#define GLI_9763E_MB_ERP_ON BIT(7)
#define PCIE_GLI_9763E_SCR 0x8E0
#define GLI_9763E_SCR_AXI_REQ BIT(9)
+#define PCIE_GLI_9763E_CFG2 0x8A4
+#define GLI_9763E_CFG2_L1DLY GENMASK(28, 19)
+#define GLI_9763E_CFG2_L1DLY_MAX 0x3FF
+
#define PCIE_GLI_9763E_MMC_CTRL 0x960
#define GLI_9763E_HS400_SLOW BIT(3)
+#define PCIE_GLI_9763E_CLKRXDLY 0x934
+#define GLI_9763E_HS400_RXDLY GENMASK(31, 28)
+#define GLI_9763E_HS400_RXDLY_5 0x5
+
#define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200
#define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
SDHCI_TRNS_BLK_CNT_EN | \
@@ -791,6 +800,17 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
value &= ~GLI_9763E_HS400_SLOW;
pci_write_config_dword(pdev, PCIE_GLI_9763E_MMC_CTRL, value);
+ pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG2, &value);
+ value &= ~GLI_9763E_CFG2_L1DLY;
+ /* set ASPM L1 entry delay to 260us */
+ value |= FIELD_PREP(GLI_9763E_CFG2_L1DLY, GLI_9763E_CFG2_L1DLY_MAX);
+ pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG2, value);
+
+ pci_read_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, &value);
+ value &= ~GLI_9763E_HS400_RXDLY;
+ value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5);
+ pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value);
+
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
@@ -814,7 +834,8 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value);
if (!(value & GLI_9763E_MB_CMDQ_OFF))
- host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
+ if (value & GLI_9763E_MB_ERP_ON)
+ host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
gli_pcie_enable_msi(slot);
host->mmc_host_ops.hs400_enhanced_strobe =
diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c
index fa76748d8929..94e3f72f6405 100644
--- a/drivers/mmc/host/sdhci-pci-o2micro.c
+++ b/drivers/mmc/host/sdhci-pci-o2micro.c
@@ -33,6 +33,8 @@
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
#define O2_SD_MISC_CTRL4 0xFC
+#define O2_SD_MISC_CTRL 0x1C0
+#define O2_SD_PWR_FORCE_L0 0x0002
#define O2_SD_TUNING_CTRL 0x300
#define O2_SD_PLL_SETTING 0x304
#define O2_SD_MISC_SETTING 0x308
@@ -300,6 +302,8 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
int current_bus_width = 0;
+ u32 scratch32 = 0;
+ u16 scratch = 0;
/*
* This handler only implements the eMMC tuning that is specific to
@@ -312,6 +316,17 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
(opcode != MMC_SEND_TUNING_BLOCK)))
return -EINVAL;
+
+ /* Force power mode enter L0 */
+ scratch = sdhci_readw(host, O2_SD_MISC_CTRL);
+ scratch |= O2_SD_PWR_FORCE_L0;
+ sdhci_writew(host, scratch, O2_SD_MISC_CTRL);
+
+ /* wait DLL lock, timeout value 5ms */
+ if (readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host,
+ scratch32, (scratch32 & O2_DLL_LOCK_STATUS), 1, 5000))
+ pr_warn("%s: DLL can't lock in 5ms after force L0 during tuning.\n",
+ mmc_hostname(host->mmc));
/*
* Judge the tuning reason, whether caused by dll shift
* If cause by dll shift, should call sdhci_o2_dll_recovery
@@ -344,6 +359,11 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
sdhci_set_bus_width(host, current_bus_width);
}
+ /* Cancel force power mode enter L0 */
+ scratch = sdhci_readw(host, O2_SD_MISC_CTRL);
+ scratch &= ~(O2_SD_PWR_FORCE_L0);
+ sdhci_writew(host, scratch, O2_SD_MISC_CTRL);
+
sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA);
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index 6301b81cf573..9bd717ff784b 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -111,8 +111,13 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
return host->private;
}
+extern const struct dev_pm_ops sdhci_pltfm_pmops;
+#ifdef CONFIG_PM_SLEEP
int sdhci_pltfm_suspend(struct device *dev);
int sdhci_pltfm_resume(struct device *dev);
-extern const struct dev_pm_ops sdhci_pltfm_pmops;
+#else
+static inline int sdhci_pltfm_suspend(struct device *dev) { return 0; }
+static inline int sdhci_pltfm_resume(struct device *dev) { return 0; }
+#endif
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c
deleted file mode 100644
index e9b347b3af7e..000000000000
--- a/drivers/mmc/host/sdhci-sirf.c
+++ /dev/null
@@ -1,235 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * SDHCI support for SiRF primaII and marco SoCs
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/mmc/host.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/mmc/slot-gpio.h>
-#include "sdhci-pltfm.h"
-
-#define SDHCI_CLK_DELAY_SETTING 0x4C
-#define SDHCI_SIRF_8BITBUS BIT(3)
-#define SIRF_TUNING_COUNT 16384
-
-static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
-{
- u8 ctrl;
-
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
- ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS);
-
- /*
- * CSR atlas7 and prima2 SD host version is not 3.0
- * 8bit-width enable bit of CSR SD hosts is 3,
- * while stardard hosts use bit 5
- */
- if (width == MMC_BUS_WIDTH_8)
- ctrl |= SDHCI_SIRF_8BITBUS;
- else if (width == MMC_BUS_WIDTH_4)
- ctrl |= SDHCI_CTRL_4BITBUS;
-
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
-}
-
-static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
-{
- u32 val = readl(host->ioaddr + reg);
-
- if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
- (host->mmc->caps & MMC_CAP_UHS_SDR50))) {
- /* fake CAP_1 register */
- val = SDHCI_SUPPORT_DDR50 |
- SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
- }
-
- if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
- u32 prss = val;
- /* fake chips as V3.0 host conreoller */
- prss &= ~(0xFF << 16);
- val = prss | (SDHCI_SPEC_300 << 16);
- }
- return val;
-}
-
-static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg)
-{
- u16 ret = 0;
-
- ret = readw(host->ioaddr + reg);
-
- if (unlikely(reg == SDHCI_HOST_VERSION)) {
- ret = readw(host->ioaddr + SDHCI_HOST_VERSION);
- ret |= SDHCI_SPEC_300;
- }
-
- return ret;
-}
-
-static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
-{
- int tuning_seq_cnt = 3;
- int phase;
- u8 tuned_phase_cnt = 0;
- int rc = 0, longest_range = 0;
- int start = -1, end = 0, tuning_value = -1, range = 0;
- u16 clock_setting;
- struct mmc_host *mmc = host->mmc;
-
- clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING);
- clock_setting &= ~0x3fff;
-
-retry:
- phase = 0;
- tuned_phase_cnt = 0;
- do {
- sdhci_writel(host,
- clock_setting | phase,
- SDHCI_CLK_DELAY_SETTING);
-
- if (!mmc_send_tuning(mmc, opcode, NULL)) {
- /* Tuning is successful at this tuning point */
- tuned_phase_cnt++;
- dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
- mmc_hostname(mmc), phase);
- if (start == -1)
- start = phase;
- end = phase;
- range++;
- if (phase == (SIRF_TUNING_COUNT - 1)
- && range > longest_range)
- tuning_value = (start + end) / 2;
- } else {
- dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n",
- mmc_hostname(mmc), phase);
- if (range > longest_range) {
- tuning_value = (start + end) / 2;
- longest_range = range;
- }
- start = -1;
- end = range = 0;
- }
- } while (++phase < SIRF_TUNING_COUNT);
-
- if (tuned_phase_cnt && tuning_value > 0) {
- /*
- * Finally set the selected phase in delay
- * line hw block.
- */
- phase = tuning_value;
- sdhci_writel(host,
- clock_setting | phase,
- SDHCI_CLK_DELAY_SETTING);
-
- dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
- mmc_hostname(mmc), phase);
- } else {
- if (--tuning_seq_cnt)
- goto retry;
- /* Tuning failed */
- dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
- mmc_hostname(mmc));
- rc = -EIO;
- }
-
- return rc;
-}
-
-static const struct sdhci_ops sdhci_sirf_ops = {
- .read_l = sdhci_sirf_readl_le,
- .read_w = sdhci_sirf_readw_le,
- .platform_execute_tuning = sdhci_sirf_execute_tuning,
- .set_clock = sdhci_set_clock,
- .get_max_clock = sdhci_pltfm_clk_get_max_clock,
- .set_bus_width = sdhci_sirf_set_bus_width,
- .reset = sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
-};
-
-static const struct sdhci_pltfm_data sdhci_sirf_pdata = {
- .ops = &sdhci_sirf_ops,
- .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
- SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
-};
-
-static int sdhci_sirf_probe(struct platform_device *pdev)
-{
- struct sdhci_host *host;
- struct sdhci_pltfm_host *pltfm_host;
- struct clk *clk;
- int ret;
-
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "unable to get clock");
- return PTR_ERR(clk);
- }
-
- host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, 0);
- if (IS_ERR(host))
- return PTR_ERR(host);
-
- pltfm_host = sdhci_priv(host);
- pltfm_host->clk = clk;
-
- sdhci_get_of_property(pdev);
-
- ret = clk_prepare_enable(pltfm_host->clk);
- if (ret)
- goto err_clk_prepare;
-
- ret = sdhci_add_host(host);
- if (ret)
- goto err_sdhci_add;
-
- /*
- * We must request the IRQ after sdhci_add_host(), as the tasklet only
- * gets setup in sdhci_add_host() and we oops.
- */
- ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
- if (ret == -EPROBE_DEFER)
- goto err_request_cd;
- if (!ret)
- mmc_gpiod_request_cd_irq(host->mmc);
-
- return 0;
-
-err_request_cd:
- sdhci_remove_host(host, 0);
-err_sdhci_add:
- clk_disable_unprepare(pltfm_host->clk);
-err_clk_prepare:
- sdhci_pltfm_free(pdev);
- return ret;
-}
-
-static const struct of_device_id sdhci_sirf_of_match[] = {
- { .compatible = "sirf,prima2-sdhc" },
- { }
-};
-MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
-
-static struct platform_driver sdhci_sirf_driver = {
- .driver = {
- .name = "sdhci-sirf",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .of_match_table = sdhci_sirf_of_match,
- .pm = &sdhci_pltfm_pmops,
- },
- .probe = sdhci_sirf_probe,
- .remove = sdhci_pltfm_unregister,
-};
-
-module_platform_driver(sdhci_sirf_driver);
-
-MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco");
-MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
index f85171edabeb..5dc36efff47f 100644
--- a/drivers/mmc/host/sdhci-sprd.c
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -708,14 +708,14 @@ static int sdhci_sprd_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
- struct mmc_host *mmc = host->mmc;
- mmc_remove_host(mmc);
+ sdhci_remove_host(host, 0);
+
clk_disable_unprepare(sprd_host->clk_sdio);
clk_disable_unprepare(sprd_host->clk_enable);
clk_disable_unprepare(sprd_host->clk_2x_enable);
- mmc_free_host(mmc);
+ sdhci_pltfm_free(pdev);
return 0;
}
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
index c67611fdaa8a..666cee4c7f7c 100644
--- a/drivers/mmc/host/sdhci-xenon.c
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -168,7 +168,12 @@ static void xenon_reset_exit(struct sdhci_host *host,
/* Disable tuning request and auto-retuning again */
xenon_retune_setup(host);
- xenon_set_acg(host, true);
+ /*
+ * The ACG should be turned off at the early init time, in order
+ * to solve a possible issues with the 1.8V regulator stabilization.
+ * The feature is enabled in later stage.
+ */
+ xenon_set_acg(host, false);
xenon_set_sdclk_off_idle(host, sdhc_id, false);
@@ -684,6 +689,7 @@ static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
static const struct of_device_id sdhci_xenon_dt_ids[] = {
{ .compatible = "marvell,armada-ap806-sdhci", .data = (void *)XENON_AP806},
+ { .compatible = "marvell,armada-ap807-sdhci", .data = (void *)XENON_AP807},
{ .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110},
{ .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700},
{}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 646823ddd317..2d73407ee52e 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -20,7 +20,6 @@
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/sizes.h>
-#include <linux/swiotlb.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
@@ -4582,12 +4581,8 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc->max_segs = SDHCI_MAX_SEGS;
} else if (host->flags & SDHCI_USE_SDMA) {
mmc->max_segs = 1;
- if (swiotlb_max_segment()) {
- unsigned int max_req_size = (1 << IO_TLB_SHIFT) *
- IO_TLB_SEGSIZE;
- mmc->max_req_size = min(mmc->max_req_size,
- max_req_size);
- }
+ mmc->max_req_size = min_t(size_t, mmc->max_req_size,
+ dma_max_mapping_size(mmc_dev(mmc)));
} else { /* PIO */
mmc->max_segs = SDHCI_MAX_SEGS;
}
diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index a64ea143d185..7a34649b0754 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -514,6 +514,26 @@ static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
.flags = IOMUX_PRESENT,
};
+static const struct sdhci_pltfm_data sdhci_am64_8bit_pdata = {
+ .ops = &sdhci_j721e_8bit_ops,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
+static const struct sdhci_am654_driver_data sdhci_am64_8bit_drvdata = {
+ .pdata = &sdhci_am64_8bit_pdata,
+ .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static const struct sdhci_pltfm_data sdhci_am64_4bit_pdata = {
+ .ops = &sdhci_j721e_4bit_ops,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
+static const struct sdhci_am654_driver_data sdhci_am64_4bit_drvdata = {
+ .pdata = &sdhci_am64_4bit_pdata,
+ .flags = IOMUX_PRESENT,
+};
+
static const struct soc_device_attribute sdhci_am654_devices[] = {
{ .family = "AM65X",
.revision = "SR1.0",
@@ -737,6 +757,14 @@ static const struct of_device_id sdhci_am654_of_match[] = {
.compatible = "ti,j721e-sdhci-4bit",
.data = &sdhci_j721e_4bit_drvdata,
},
+ {
+ .compatible = "ti,am64-sdhci-8bit",
+ .data = &sdhci_am64_8bit_drvdata,
+ },
+ {
+ .compatible = "ti,am64-sdhci-4bit",
+ .data = &sdhci_am64_4bit_drvdata,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_am654_of_match);
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 6310693f2ac0..2702736a1c57 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -245,6 +245,7 @@ struct sunxi_idma_des {
struct sunxi_mmc_cfg {
u32 idma_des_size_bits;
+ u32 idma_des_shift;
const struct sunxi_mmc_clk_delay *clk_delays;
/* does the IP block support autocalibration? */
@@ -344,7 +345,7 @@ static int sunxi_mmc_init_host(struct sunxi_mmc_host *host)
/* Enable CEATA support */
mmc_writel(host, REG_FUNS, SDXC_CEATA_ON);
/* Set DMA descriptor list base address */
- mmc_writel(host, REG_DLBA, host->sg_dma);
+ mmc_writel(host, REG_DLBA, host->sg_dma >> host->cfg->idma_des_shift);
rval = mmc_readl(host, REG_GCTRL);
rval |= SDXC_INTERRUPT_ENABLE_BIT;
@@ -374,8 +375,10 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
next_desc += sizeof(struct sunxi_idma_des);
pdes[i].buf_addr_ptr1 =
- cpu_to_le32(sg_dma_address(&data->sg[i]));
- pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc);
+ cpu_to_le32(sg_dma_address(&data->sg[i]) >>
+ host->cfg->idma_des_shift);
+ pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc >>
+ host->cfg->idma_des_shift);
}
pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD);
@@ -1179,6 +1182,23 @@ static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
.needs_new_timings = true,
};
+static const struct sunxi_mmc_cfg sun50i_a100_cfg = {
+ .idma_des_size_bits = 16,
+ .idma_des_shift = 2,
+ .clk_delays = NULL,
+ .can_calibrate = true,
+ .mask_data0 = true,
+ .needs_new_timings = true,
+};
+
+static const struct sunxi_mmc_cfg sun50i_a100_emmc_cfg = {
+ .idma_des_size_bits = 13,
+ .idma_des_shift = 2,
+ .clk_delays = NULL,
+ .can_calibrate = true,
+ .needs_new_timings = true,
+};
+
static const struct of_device_id sunxi_mmc_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg },
{ .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg },
@@ -1187,6 +1207,8 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
{ .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
{ .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
+ { .compatible = "allwinner,sun50i-a100-mmc", .data = &sun50i_a100_cfg },
+ { .compatible = "allwinner,sun50i-a100-emmc", .data = &sun50i_a100_emmc_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
@@ -1507,6 +1529,8 @@ static int sunxi_mmc_runtime_suspend(struct device *dev)
#endif
static const struct dev_pm_ops sunxi_mmc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
sunxi_mmc_runtime_resume,
NULL)
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index fd8b72d3e02c..9fdf7ea06e3f 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -731,9 +731,9 @@ err_out:
mmc_request_done(mmc, mrq);
}
-static void tifm_sd_end_cmd(unsigned long data)
+static void tifm_sd_end_cmd(struct tasklet_struct *t)
{
- struct tifm_sd *host = (struct tifm_sd*)data;
+ struct tifm_sd *host = from_tasklet(host, t, finish_tasklet);
struct tifm_dev *sock = host->dev;
struct mmc_host *mmc = tifm_get_drvdata(sock);
struct mmc_request *mrq;
@@ -968,8 +968,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
*/
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
- tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
- (unsigned long)host);
+ tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd);
timer_setup(&host->timer, tifm_sd_abort, 0);
mmc->ops = &tifm_sd_ops;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 784fa6ed5843..2d1db9396d4a 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -181,7 +181,7 @@ struct tmio_mmc_host {
unsigned int direction, int blk_size);
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
void (*reset)(struct tmio_mmc_host *host);
- bool (*check_retune)(struct tmio_mmc_host *host);
+ bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq);
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host);
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 942b8375179c..49c2d406c48e 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -477,8 +477,10 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host, unsigned int stat)
if (!data)
goto out;
- if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
- stat & TMIO_STAT_TXUNDERRUN)
+ if (stat & TMIO_STAT_DATATIMEOUT)
+ data->error = -ETIMEDOUT;
+ else if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
+ stat & TMIO_STAT_TXUNDERRUN)
data->error = -EILSEQ;
if (host->dma_on && (data->flags & MMC_DATA_WRITE)) {
u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
@@ -802,7 +804,7 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
}
/* Error means retune, but executed command was still successful */
- if (host->check_retune && host->check_retune(host))
+ if (host->check_retune && host->check_retune(host, mrq))
mmc_retune_needed(host->mmc);
/* If SET_BLOCK_COUNT, continue with main command */
diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c
index a6cd16771d4e..2413b6750cec 100644
--- a/drivers/mmc/host/uniphier-sd.c
+++ b/drivers/mmc/host/uniphier-sd.c
@@ -81,9 +81,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable)
}
/* external DMA engine */
-static void uniphier_sd_external_dma_issue(unsigned long arg)
+static void uniphier_sd_external_dma_issue(struct tasklet_struct *t)
{
- struct tmio_mmc_host *host = (void *)arg;
+ struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
uniphier_sd_dma_endisable(host, 1);
@@ -190,8 +190,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host,
host->chan_rx = chan;
host->chan_tx = chan;
- tasklet_init(&host->dma_issue, uniphier_sd_external_dma_issue,
- (unsigned long)host);
+ tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue);
}
static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host)
@@ -228,9 +227,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = {
.dataend = uniphier_sd_external_dma_dataend,
};
-static void uniphier_sd_internal_dma_issue(unsigned long arg)
+static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t)
{
- struct tmio_mmc_host *host = (void *)arg;
+ struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
@@ -309,8 +308,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host,
host->chan_tx = (void *)0xdeadbeaf;
- tasklet_init(&host->dma_issue, uniphier_sd_internal_dma_issue,
- (unsigned long)host);
+ tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue);
}
static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host)
diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c
index e2d5112d809d..615f3d008af1 100644
--- a/drivers/mmc/host/usdhi6rol0.c
+++ b/drivers/mmc/host/usdhi6rol0.c
@@ -1858,10 +1858,12 @@ static int usdhi6_probe(struct platform_device *pdev)
ret = mmc_add_host(mmc);
if (ret < 0)
- goto e_clk_off;
+ goto e_release_dma;
return 0;
+e_release_dma:
+ usdhi6_dma_release(host);
e_clk_off:
clk_disable_unprepare(host->clk);
e_free_mmc:
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 9b755ea0fa03..4f4c0813f9fd 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -959,14 +959,12 @@ static void via_sdc_timeout(struct timer_list *t)
spin_unlock_irqrestore(&sdhost->lock, flags);
}
-static void via_sdc_tasklet_finish(unsigned long param)
+static void via_sdc_tasklet_finish(struct tasklet_struct *t)
{
- struct via_crdr_mmc_host *host;
+ struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet);
unsigned long flags;
struct mmc_request *mrq;
- host = (struct via_crdr_mmc_host *)param;
-
spin_lock_irqsave(&host->lock, flags);
del_timer(&host->timer);
@@ -1050,8 +1048,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
INIT_WORK(&host->carddet_work, via_sdc_card_detect);
- tasklet_init(&host->finish_tasklet, via_sdc_tasklet_finish,
- (unsigned long)host);
+ tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish);
addrbase = host->sdhc_mmiobase;
writel(0x0, addrbase + VIA_CRDR_SDINTMASK);
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index cd63ea865b77..67ecd342fe5f 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
return host->mrq->cmd->data;
}
-static void wbsd_tasklet_card(unsigned long param)
+static void wbsd_tasklet_card(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, card_tasklet);
u8 csr;
int delay = -1;
@@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(unsigned long param)
mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
}
-static void wbsd_tasklet_fifo(unsigned long param)
+static void wbsd_tasklet_fifo(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1067,9 +1067,9 @@ end:
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_crc(unsigned long param)
+static void wbsd_tasklet_crc(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, crc_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1091,9 +1091,9 @@ end:
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_timeout(unsigned long param)
+static void wbsd_tasklet_timeout(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1115,9 +1115,9 @@ end:
spin_unlock(&host->lock);
}
-static void wbsd_tasklet_finish(unsigned long param)
+static void wbsd_tasklet_finish(struct tasklet_struct *t)
{
- struct wbsd_host *host = (struct wbsd_host *)param;
+ struct wbsd_host *host = from_tasklet(host, t, finish_tasklet);
struct mmc_data *data;
spin_lock(&host->lock);
@@ -1449,16 +1449,11 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq)
/*
* Set up tasklets. Must be done before requesting interrupt.
*/
- tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
- (unsigned long)host);
- tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
- (unsigned long)host);
- tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
- (unsigned long)host);
- tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
- (unsigned long)host);
- tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
- (unsigned long)host);
+ tasklet_setup(&host->card_tasklet, wbsd_tasklet_card);
+ tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo);
+ tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc);
+ tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout);
+ tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish);
/*
* Allocate interrupt.