summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-02-21 12:04:54 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-21 12:04:54 -0800
commite67bd12d6036ae3de9eeb0ba52e43691264ec850 (patch)
tree0232867e68e2b56fd601f7c31b0d5f87d7ec3792 /drivers/mmc
parentcdc194705d26fdd7fc5446b5d830f2bbe2b22c30 (diff)
parent8c7cdbf9272c300dc093da3c62fa3b4bc6dc960e (diff)
downloadop-kernel-dev-e67bd12d6036ae3de9eeb0ba52e43691264ec850.zip
op-kernel-dev-e67bd12d6036ae3de9eeb0ba52e43691264ec850.tar.gz
Merge tag 'mmc-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Add support for Marvell SD8787 Wifi/BT chip - Improve UHS support for SDIO - Invent MMC_CAP_3_3V_DDR and a DT binding for eMMC DDR 3.3V mode - Detect Auto BKOPS enable bit - Export eMMC device lifetime information through sysfs - First take to slim down the public mmc headers to avoid abuse - Re-factoring of the mmc block device driver to prepare for blkmq - Cleanup code for the mmc block device driver - Clarify and cleanup code dealing with data requests - Cleanup some code by converting to ida_simple_ functions - Cleanup code dealing with card quirks - Cleanup private and public mmc header files MMC host: - Don't rely on public mmc headers to include non-mmc related headers - meson: Add support for eMMC HS400 mode - meson: Various cleanups and improvements - omap_hsmmc: Use the proper provided busy timeout from the core - sunxi: Enable new timings for the A64 MMC controllers - sunxi: Improvements for clock management - tmio: Improvements for SDIO interrupts - mxs-mmc: Add CMD23 support - sdhci-msm: Enable HS400 enhanced strobe mode support - sdhci-msm: Correct HS400 tuning sequence - sdhci-acpi: Support deferred probe - sdhci-pci: Add support for eMMC HS200 tuning mode on AMD - mediatek: Correct the implementation of card busy detection - dw_mmc: Initial support for ZX mmc controller - sh_mobile_sdhi: Enable support for eMMC HS200 mode - sh_mmcif: Various cleanups and improvements" * tag 'mmc-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (145 commits) mmc: core: add mmc prefix for blk_fixups mmc: core: move all quirks together into quirks.h mmc: core: improve the quirks for sdio devices mmc: core: move some sdio IDs out of quirks file mmc: core: change quirks.c to be a header file mmc: sdhci-cadence: fix bit shift of read data from PHY port mmc: Adding AUTO_BKOPS_EN bit set for Auto BKOPS support mmc: MAN_BKOPS_EN inverse debug message logic mmc: meson-gx: add support for HS400 mode mmc: meson-gx: remove unneeded checks in remove mmc: meson-gx: reduce bounce buffer size mmc: meson-gx: set max block count and request size mmc: meson-gx: improve interrupt handling mmc: meson-gx: improve meson_mmc_irq_thread mmc: meson-gx: improve meson_mmc_clk_set mmc: meson-gx: minor improvements in meson_mmc_set_ios mmc: meson: Assign the minimum clk rate as close to 400KHz as possible mmc: core: start to break apart mmc_start_areq() mmc: block: respect bool returned from blk_end_request() mmc: block: return errorcode from mmc_sd_num_wr_blocks() ...
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/Kconfig10
-rw-r--r--drivers/mmc/core/Makefile3
-rw-r--r--drivers/mmc/core/block.c413
-rw-r--r--drivers/mmc/core/block.h10
-rw-r--r--drivers/mmc/core/bus.c2
-rw-r--r--drivers/mmc/core/bus.h16
-rw-r--r--drivers/mmc/core/card.h221
-rw-r--r--drivers/mmc/core/core.c116
-rw-r--r--drivers/mmc/core/core.h45
-rw-r--r--drivers/mmc/core/debugfs.c2
-rw-r--r--drivers/mmc/core/host.c24
-rw-r--r--drivers/mmc/core/host.h48
-rw-r--r--drivers/mmc/core/mmc.c76
-rw-r--r--drivers/mmc/core/mmc_ops.c44
-rw-r--r--drivers/mmc/core/mmc_ops.h14
-rw-r--r--drivers/mmc/core/mmc_test.c116
-rw-r--r--drivers/mmc/core/pwrseq.h6
-rw-r--r--drivers/mmc/core/pwrseq_sd8787.c117
-rw-r--r--drivers/mmc/core/queue.c16
-rw-r--r--drivers/mmc/core/queue.h13
-rw-r--r--drivers/mmc/core/quirks.c83
-rw-r--r--drivers/mmc/core/quirks.h148
-rw-r--r--drivers/mmc/core/sd.c5
-rw-r--r--drivers/mmc/core/sd.h5
-rw-r--r--drivers/mmc/core/sd_ops.c30
-rw-r--r--drivers/mmc/core/sd_ops.h9
-rw-r--r--drivers/mmc/core/sdio.c46
-rw-r--r--drivers/mmc/core/sdio_bus.c1
-rw-r--r--drivers/mmc/core/sdio_bus.h3
-rw-r--r--drivers/mmc/core/sdio_cis.h3
-rw-r--r--drivers/mmc/core/sdio_io.c2
-rw-r--r--drivers/mmc/core/sdio_irq.c2
-rw-r--r--drivers/mmc/core/sdio_ops.c10
-rw-r--r--drivers/mmc/core/sdio_ops.h5
-rw-r--r--drivers/mmc/core/slot-gpio.c6
-rw-r--r--drivers/mmc/core/slot-gpio.h2
-rw-r--r--drivers/mmc/host/Kconfig9
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/davinci_mmc.c1
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c1
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c1
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c1
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c1
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c1
-rw-r--r--drivers/mmc/host/dw_mmc-zx.c241
-rw-r--r--drivers/mmc/host/dw_mmc-zx.h31
-rw-r--r--drivers/mmc/host/dw_mmc.c30
-rw-r--r--drivers/mmc/host/dw_mmc.h263
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c118
-rw-r--r--drivers/mmc/host/mmci.c7
-rw-r--r--drivers/mmc/host/mmci.h3
-rw-r--r--drivers/mmc/host/mtk-sd.c8
-rw-r--r--drivers/mmc/host/mxs-mmc.c16
-rw-r--r--drivers/mmc/host/omap.c2
-rw-r--r--drivers/mmc/host/omap_hsmmc.c29
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c2
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c2
-rw-r--r--drivers/mmc/host/s3cmci.c1
-rw-r--r--drivers/mmc/host/sdhci-acpi.c5
-rw-r--r--drivers/mmc/host/sdhci-cadence.c3
-rw-r--r--drivers/mmc/host/sdhci-esdhc.h44
-rw-r--r--drivers/mmc/host/sdhci-iproc.c11
-rw-r--r--drivers/mmc/host/sdhci-msm.c377
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c39
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c97
-rw-r--r--drivers/mmc/host/sdhci-pci.h1
-rw-r--r--drivers/mmc/host/sdhci-s3c-regs.h87
-rw-r--r--drivers/mmc/host/sdhci-s3c.c71
-rw-r--r--drivers/mmc/host/sdhci.c10
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mmc/host/sh_mmcif.c28
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c95
-rw-r--r--drivers/mmc/host/sunxi-mmc.c114
-rw-r--r--drivers/mmc/host/tmio_mmc.h3
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c61
-rw-r--r--drivers/mmc/host/via-sdmmc.c1
-rw-r--r--drivers/mmc/host/vub300.c8
-rw-r--r--drivers/mmc/host/wbsd.c7
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c1
79 files changed, 2416 insertions, 1090 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index cdfa852..fc1ecda 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -12,6 +12,16 @@ config PWRSEQ_EMMC
This driver can also be built as a module. If so, the module
will be called pwrseq_emmc.
+config PWRSEQ_SD8787
+ tristate "HW reset support for SD8787 BT + Wifi module"
+ depends on OF && (MWIFIEX || BT_MRVL_SDIO)
+ help
+ This selects hardware reset support for the SD8787 BT + Wifi
+ module. By default this option is set to n.
+
+ This driver can also be built as a module. If so, the module
+ will be called pwrseq_sd8787.
+
config PWRSEQ_SIMPLE
tristate "Simple HW reset support for MMC"
default y
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index b2a257d..7e3ed1a 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -7,9 +7,10 @@ mmc_core-y := core.o bus.o host.o \
mmc.o mmc_ops.o sd.o sd_ops.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o \
- quirks.o slot-gpio.o
+ slot-gpio.o
mmc_core-$(CONFIG_OF) += pwrseq.o
obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
+obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o
obj-$(CONFIG_PWRSEQ_EMMC) += pwrseq_emmc.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index cb1698f..1621fa0 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -47,6 +47,13 @@
#include "queue.h"
#include "block.h"
+#include "core.h"
+#include "card.h"
+#include "host.h"
+#include "bus.h"
+#include "mmc_ops.h"
+#include "quirks.h"
+#include "sd_ops.h"
MODULE_ALIAS("mmc:block");
#ifdef MODULE_PARAM_PREFIX
@@ -54,12 +61,6 @@ MODULE_ALIAS("mmc:block");
#endif
#define MODULE_PARAM_PREFIX "mmcblk."
-#define INAND_CMD38_ARG_EXT_CSD 113
-#define INAND_CMD38_ARG_ERASE 0x00
-#define INAND_CMD38_ARG_TRIM 0x01
-#define INAND_CMD38_ARG_SECERASE 0x80
-#define INAND_CMD38_ARG_SECTRIM1 0x81
-#define INAND_CMD38_ARG_SECTRIM2 0x88
#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
#define MMC_SANITIZE_REQ_TIMEOUT 240000
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
@@ -84,7 +85,6 @@ static int max_devices;
#define MAX_DEVICES 256
static DEFINE_IDA(mmc_blk_ida);
-static DEFINE_SPINLOCK(mmc_blk_lock);
/*
* There is one mmc_blk_data per slot.
@@ -157,11 +157,7 @@ static void mmc_blk_put(struct mmc_blk_data *md)
if (md->usage == 0) {
int devidx = mmc_get_devidx(md->disk);
blk_cleanup_queue(md->queue.queue);
-
- spin_lock(&mmc_blk_lock);
- ida_remove(&mmc_blk_ida, devidx);
- spin_unlock(&mmc_blk_lock);
-
+ ida_simple_remove(&mmc_blk_ida, devidx);
put_disk(md->disk);
kfree(md);
}
@@ -442,9 +438,9 @@ out:
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_blk_ioc_data *idata)
{
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
- struct mmc_request mrq = {NULL};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
+ struct mmc_request mrq = {};
struct scatterlist sg;
int err;
int is_rpmb = false;
@@ -762,15 +758,15 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
return 0;
}
-static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
+static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
{
int err;
u32 result;
__be32 *blocks;
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
@@ -780,9 +776,9 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
- return (u32)-1;
+ return err;
if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
- return (u32)-1;
+ return -EIO;
memset(&cmd, 0, sizeof(struct mmc_command));
@@ -802,7 +798,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
blocks = kmalloc(4, GFP_KERNEL);
if (!blocks)
- return (u32)-1;
+ return -ENOMEM;
sg_init_one(&sg, blocks, 4);
@@ -812,14 +808,16 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
kfree(blocks);
if (cmd.error || data.error)
- result = (u32)-1;
+ return -EIO;
+
+ *written_blocks = result;
- return result;
+ return 0;
}
static int get_card_status(struct mmc_card *card, u32 *status, int retries)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err;
cmd.opcode = MMC_SEND_STATUS;
@@ -884,7 +882,7 @@ static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
struct request *req, bool *gen_err, u32 *stop_status)
{
struct mmc_host *host = card->host;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err;
bool use_r1b_resp = rq_data_dir(req) == WRITE;
@@ -1143,7 +1141,7 @@ int mmc_access_rpmb(struct mmc_queue *mq)
return false;
}
-static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
+static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
@@ -1152,7 +1150,7 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
if (!mmc_can_erase(card)) {
err = -EOPNOTSUPP;
- goto out;
+ goto fail;
}
from = blk_rq_pos(req);
@@ -1164,29 +1162,26 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
arg = MMC_TRIM_ARG;
else
arg = MMC_ERASE_ARG;
-retry:
- if (card->quirks & MMC_QUIRK_INAND_CMD38) {
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- INAND_CMD38_ARG_EXT_CSD,
- arg == MMC_TRIM_ARG ?
- INAND_CMD38_ARG_TRIM :
- INAND_CMD38_ARG_ERASE,
- 0);
- if (err)
- goto out;
- }
- err = mmc_erase(card, from, nr, arg);
-out:
- if (err == -EIO && !mmc_blk_reset(md, card->host, type))
- goto retry;
+ do {
+ err = 0;
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ arg == MMC_TRIM_ARG ?
+ INAND_CMD38_ARG_TRIM :
+ INAND_CMD38_ARG_ERASE,
+ 0);
+ }
+ if (!err)
+ err = mmc_erase(card, from, nr, arg);
+ } while (err == -EIO && !mmc_blk_reset(md, card->host, type));
if (!err)
mmc_blk_reset_success(md, type);
+fail:
blk_end_request(req, err, blk_rq_bytes(req));
-
- return err ? 0 : 1;
}
-static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
+static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
struct request *req)
{
struct mmc_blk_data *md = mq->blkdata;
@@ -1249,11 +1244,9 @@ out_retry:
mmc_blk_reset_success(md, type);
out:
blk_end_request(req, err, blk_rq_bytes(req));
-
- return err ? 0 : 1;
}
-static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
+static void mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
@@ -1264,8 +1257,6 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
ret = -EIO;
blk_end_request_all(req, ret);
-
- return ret ? 0 : 1;
}
/*
@@ -1303,7 +1294,7 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card,
struct mmc_async_req *areq)
{
struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
- mmc_active);
+ areq);
struct mmc_blk_request *brq = &mq_mrq->brq;
struct request *req = mq_mrq->req;
int need_retune = card->host->need_retune;
@@ -1559,17 +1550,19 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
brq->data.sg_len = i;
}
- mqrq->mmc_active.mrq = &brq->mrq;
- mqrq->mmc_active.err_check = mmc_blk_err_check;
+ mqrq->areq.mrq = &brq->mrq;
+ mqrq->areq.err_check = mmc_blk_err_check;
mmc_queue_bounce_pre(mqrq);
}
-static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
- struct mmc_blk_request *brq, struct request *req,
- int ret)
+static bool mmc_blk_rw_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
+ struct mmc_blk_request *brq, struct request *req,
+ bool old_req_pending)
{
struct mmc_queue_req *mq_rq;
+ bool req_pending;
+
mq_rq = container_of(brq, struct mmc_queue_req, brq);
/*
@@ -1582,62 +1575,104 @@ static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
*/
if (mmc_card_sd(card)) {
u32 blocks;
+ int err;
- blocks = mmc_sd_num_wr_blocks(card);
- if (blocks != (u32)-1) {
- ret = blk_end_request(req, 0, blocks << 9);
- }
+ err = mmc_sd_num_wr_blocks(card, &blocks);
+ if (err)
+ req_pending = old_req_pending;
+ else
+ req_pending = blk_end_request(req, 0, blocks << 9);
} else {
- ret = blk_end_request(req, 0, brq->data.bytes_xfered);
+ req_pending = blk_end_request(req, 0, brq->data.bytes_xfered);
}
- return ret;
+ return req_pending;
+}
+
+static void mmc_blk_rw_cmd_abort(struct mmc_card *card, struct request *req)
+{
+ if (mmc_card_removed(card))
+ req->rq_flags |= RQF_QUIET;
+ while (blk_end_request(req, -EIO, blk_rq_cur_bytes(req)));
+}
+
+/**
+ * mmc_blk_rw_try_restart() - tries to restart the current async request
+ * @mq: the queue with the card and host to restart
+ * @req: a new request that want to be started after the current one
+ */
+static void mmc_blk_rw_try_restart(struct mmc_queue *mq, struct request *req)
+{
+ if (!req)
+ return;
+
+ /*
+ * If the card was removed, just cancel everything and return.
+ */
+ if (mmc_card_removed(mq->card)) {
+ req->rq_flags |= RQF_QUIET;
+ blk_end_request_all(req, -EIO);
+ return;
+ }
+ /* Else proceed and try to restart the current async request */
+ mmc_blk_rw_rq_prep(mq->mqrq_cur, mq->card, 0, mq);
+ mmc_start_areq(mq->card->host, &mq->mqrq_cur->areq, NULL);
}
-static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
+static void mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *new_req)
{
struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq;
- int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
+ int disable_multi = 0, retry = 0, type, retune_retry_done = 0;
enum mmc_blk_status status;
struct mmc_queue_req *mq_rq;
- struct request *req;
- struct mmc_async_req *areq;
+ struct request *old_req;
+ struct mmc_async_req *new_areq;
+ struct mmc_async_req *old_areq;
+ bool req_pending = true;
- if (!rqc && !mq->mqrq_prev->req)
- return 0;
+ if (!new_req && !mq->mqrq_prev->req)
+ return;
do {
- if (rqc) {
+ if (new_req) {
/*
* When 4KB native sector is enabled, only 8 blocks
* multiple read or write is allowed
*/
if (mmc_large_sector(card) &&
- !IS_ALIGNED(blk_rq_sectors(rqc), 8)) {
+ !IS_ALIGNED(blk_rq_sectors(new_req), 8)) {
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
- rqc->rq_disk->disk_name);
- mq_rq = mq->mqrq_cur;
- req = rqc;
- rqc = NULL;
- goto cmd_abort;
+ new_req->rq_disk->disk_name);
+ mmc_blk_rw_cmd_abort(card, new_req);
+ return;
}
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
- areq = &mq->mqrq_cur->mmc_active;
+ new_areq = &mq->mqrq_cur->areq;
} else
- areq = NULL;
- areq = mmc_start_req(card->host, areq, &status);
- if (!areq) {
+ new_areq = NULL;
+
+ old_areq = mmc_start_areq(card->host, new_areq, &status);
+ if (!old_areq) {
+ /*
+ * We have just put the first request into the pipeline
+ * and there is nothing more to do until it is
+ * complete.
+ */
if (status == MMC_BLK_NEW_REQUEST)
- mq->flags |= MMC_QUEUE_NEW_REQUEST;
- return 0;
+ mq->new_request = true;
+ return;
}
- mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
+ /*
+ * An asynchronous request has been completed and we proceed
+ * to handle the result of it.
+ */
+ mq_rq = container_of(old_areq, struct mmc_queue_req, areq);
brq = &mq_rq->brq;
- req = mq_rq->req;
- type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
+ old_req = mq_rq->req;
+ type = rq_data_dir(old_req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
mmc_queue_bounce_post(mq_rq);
switch (status) {
@@ -1648,28 +1683,32 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
*/
mmc_blk_reset_success(md, type);
- ret = blk_end_request(req, 0,
- brq->data.bytes_xfered);
-
+ req_pending = blk_end_request(old_req, 0,
+ brq->data.bytes_xfered);
/*
* If the blk_end_request function returns non-zero even
* though all data has been transferred and no errors
* were returned by the host controller, it's a bug.
*/
- if (status == MMC_BLK_SUCCESS && ret) {
+ if (status == MMC_BLK_SUCCESS && req_pending) {
pr_err("%s BUG rq_tot %d d_xfer %d\n",
- __func__, blk_rq_bytes(req),
+ __func__, blk_rq_bytes(old_req),
brq->data.bytes_xfered);
- rqc = NULL;
- goto cmd_abort;
+ mmc_blk_rw_cmd_abort(card, old_req);
+ return;
}
break;
case MMC_BLK_CMD_ERR:
- ret = mmc_blk_cmd_err(md, card, brq, req, ret);
- if (mmc_blk_reset(md, card->host, type))
- goto cmd_abort;
- if (!ret)
- goto start_new_req;
+ req_pending = mmc_blk_rw_cmd_err(md, card, brq, old_req, req_pending);
+ if (mmc_blk_reset(md, card->host, type)) {
+ mmc_blk_rw_cmd_abort(card, old_req);
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
+ }
+ if (!req_pending) {
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
+ }
break;
case MMC_BLK_RETRY:
retune_retry_done = brq->retune_retry_done;
@@ -1679,22 +1718,27 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
case MMC_BLK_ABORT:
if (!mmc_blk_reset(md, card->host, type))
break;
- goto cmd_abort;
+ mmc_blk_rw_cmd_abort(card, old_req);
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
case MMC_BLK_DATA_ERR: {
int err;
err = mmc_blk_reset(md, card->host, type);
if (!err)
break;
- if (err == -ENODEV)
- goto cmd_abort;
+ if (err == -ENODEV) {
+ mmc_blk_rw_cmd_abort(card, old_req);
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
+ }
/* Fall through */
}
case MMC_BLK_ECC_ERR:
if (brq->data.blocks > 1) {
/* Redo read one sector at a time */
pr_warn("%s: retrying using single block read\n",
- req->rq_disk->disk_name);
+ old_req->rq_disk->disk_name);
disable_multi = 1;
break;
}
@@ -1703,57 +1747,40 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
* time, so we only reach here after trying to
* read a single sector.
*/
- ret = blk_end_request(req, -EIO,
- brq->data.blksz);
- if (!ret)
- goto start_new_req;
+ req_pending = blk_end_request(old_req, -EIO,
+ brq->data.blksz);
+ if (!req_pending) {
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
+ }
break;
case MMC_BLK_NOMEDIUM:
- goto cmd_abort;
+ mmc_blk_rw_cmd_abort(card, old_req);
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
default:
pr_err("%s: Unhandled return value (%d)",
- req->rq_disk->disk_name, status);
- goto cmd_abort;
+ old_req->rq_disk->disk_name, status);
+ mmc_blk_rw_cmd_abort(card, old_req);
+ mmc_blk_rw_try_restart(mq, new_req);
+ return;
}
- if (ret) {
+ if (req_pending) {
/*
* In case of a incomplete request
* prepare it again and resend.
*/
mmc_blk_rw_rq_prep(mq_rq, card,
disable_multi, mq);
- mmc_start_req(card->host,
- &mq_rq->mmc_active, NULL);
+ mmc_start_areq(card->host,
+ &mq_rq->areq, NULL);
mq_rq->brq.retune_retry_done = retune_retry_done;
}
- } while (ret);
-
- return 1;
-
- cmd_abort:
- if (mmc_card_removed(card))
- req->rq_flags |= RQF_QUIET;
- while (ret)
- ret = blk_end_request(req, -EIO,
- blk_rq_cur_bytes(req));
-
- start_new_req:
- if (rqc) {
- if (mmc_card_removed(card)) {
- rqc->rq_flags |= RQF_QUIET;
- blk_end_request_all(rqc, -EIO);
- } else {
- mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
- mmc_start_req(card->host,
- &mq->mqrq_cur->mmc_active, NULL);
- }
- }
-
- return 0;
+ } while (req_pending);
}
-int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
int ret;
struct mmc_blk_data *md = mq->blkdata;
@@ -1769,32 +1796,31 @@ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
if (req) {
blk_end_request_all(req, -EIO);
}
- ret = 0;
goto out;
}
- mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
+ mq->new_request = false;
if (req && req_op(req) == REQ_OP_DISCARD) {
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
- ret = mmc_blk_issue_discard_rq(mq, req);
+ mmc_blk_issue_discard_rq(mq, req);
} else if (req && req_op(req) == REQ_OP_SECURE_ERASE) {
/* complete ongoing async transfer before issuing secure erase*/
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
- ret = mmc_blk_issue_secdiscard_rq(mq, req);
+ mmc_blk_issue_secdiscard_rq(mq, req);
} else if (req && req_op(req) == REQ_OP_FLUSH) {
/* complete ongoing async transfer before issuing flush */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
- ret = mmc_blk_issue_flush(mq, req);
+ mmc_blk_issue_flush(mq, req);
} else {
- ret = mmc_blk_issue_rw_rq(mq, req);
+ mmc_blk_issue_rw_rq(mq, req);
}
out:
- if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || req_is_special)
+ if ((!req && !mq->new_request) || req_is_special)
/*
* Release host when there are no more requests
* and after special request(discard, flush) is done.
@@ -1802,7 +1828,6 @@ out:
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
*/
mmc_put_card(card);
- return ret;
}
static inline int mmc_blk_readonly(struct mmc_card *card)
@@ -1821,23 +1846,9 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
struct mmc_blk_data *md;
int devidx, ret;
-again:
- if (!ida_pre_get(&mmc_blk_ida, GFP_KERNEL))
- return ERR_PTR(-ENOMEM);
-
- spin_lock(&mmc_blk_lock);
- ret = ida_get_new(&mmc_blk_ida, &devidx);
- spin_unlock(&mmc_blk_lock);
-
- if (ret == -EAGAIN)
- goto again;
- else if (ret)
- return ERR_PTR(ret);
-
- if (devidx >= max_devices) {
- ret = -ENOSPC;
- goto out;
- }
+ devidx = ida_simple_get(&mmc_blk_ida, 0, max_devices, GFP_KERNEL);
+ if (devidx < 0)
+ return ERR_PTR(devidx);
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
if (!md) {
@@ -1926,9 +1937,7 @@ again:
err_kfree:
kfree(md);
out:
- spin_lock(&mmc_blk_lock);
- ida_remove(&mmc_blk_ida, devidx);
- spin_unlock(&mmc_blk_lock);
+ ida_simple_remove(&mmc_blk_ida, devidx);
return ERR_PTR(ret);
}
@@ -2093,80 +2102,6 @@ force_ro_fail:
return ret;
}
-static const struct mmc_fixup blk_fixups[] =
-{
- MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
- MMC_QUIRK_INAND_CMD38),
- MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
- MMC_QUIRK_INAND_CMD38),
- MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
- MMC_QUIRK_INAND_CMD38),
- MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
- MMC_QUIRK_INAND_CMD38),
- MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
- MMC_QUIRK_INAND_CMD38),
-
- /*
- * Some MMC cards experience performance degradation with CMD23
- * instead of CMD12-bounded multiblock transfers. For now we'll
- * black list what's bad...
- * - Certain Toshiba cards.
- *
- * N.B. This doesn't affect SD cards.
- */
- MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_BLK_NO_CMD23),
- MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_BLK_NO_CMD23),
- MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_BLK_NO_CMD23),
- MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_BLK_NO_CMD23),
- MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_BLK_NO_CMD23),
-
- /*
- * Some MMC cards need longer data read timeout than indicated in CSD.
- */
- MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
- MMC_QUIRK_LONG_READ_TIME),
- MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_LONG_READ_TIME),
-
- /*
- * On these Samsung MoviNAND parts, performing secure erase or
- * secure trim can result in unrecoverable corruption due to a
- * firmware bug.
- */
- MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
- MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
-
- /*
- * On Some Kingston eMMCs, performing trim can result in
- * unrecoverable data conrruption occasionally due to a firmware bug.
- */
- MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_TRIM_BROKEN),
- MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
- MMC_QUIRK_TRIM_BROKEN),
-
- END_FIXUP
-};
-
static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md, *part_md;
@@ -2178,7 +2113,7 @@ static int mmc_blk_probe(struct mmc_card *card)
if (!(card->csd.cmdclass & CCC_BLOCK_READ))
return -ENODEV;
- mmc_fixup_device(card, blk_fixups);
+ mmc_fixup_device(card, mmc_blk_fixups);
md = mmc_blk_alloc(card);
if (IS_ERR(md))
diff --git a/drivers/mmc/core/block.h b/drivers/mmc/core/block.h
index cdabb2e..860ca7c 100644
--- a/drivers/mmc/core/block.h
+++ b/drivers/mmc/core/block.h
@@ -1 +1,9 @@
-int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req);
+#ifndef _MMC_CORE_BLOCK_H
+#define _MMC_CORE_BLOCK_H
+
+struct mmc_queue;
+struct request;
+
+void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req);
+
+#endif
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index c64266f..3012465 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -23,6 +23,8 @@
#include <linux/mmc/host.h>
#include "core.h"
+#include "card.h"
+#include "host.h"
#include "sdio_cis.h"
#include "bus.h"
diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h
index 00a1971..72b0ef03 100644
--- a/drivers/mmc/core/bus.h
+++ b/drivers/mmc/core/bus.h
@@ -11,6 +11,11 @@
#ifndef _MMC_CORE_BUS_H
#define _MMC_CORE_BUS_H
+#include <linux/device.h>
+
+struct mmc_host;
+struct mmc_card;
+
#define MMC_DEV_ATTR(name, fmt, args...) \
static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
@@ -27,5 +32,14 @@ void mmc_remove_card(struct mmc_card *card);
int mmc_register_bus(void);
void mmc_unregister_bus(void);
-#endif
+struct mmc_driver {
+ struct device_driver drv;
+ int (*probe)(struct mmc_card *card);
+ void (*remove)(struct mmc_card *card);
+ void (*shutdown)(struct mmc_card *card);
+};
+int mmc_register_driver(struct mmc_driver *drv);
+void mmc_unregister_driver(struct mmc_driver *drv);
+
+#endif
diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h
new file mode 100644
index 0000000..f06cd91
--- /dev/null
+++ b/drivers/mmc/core/card.h
@@ -0,0 +1,221 @@
+/*
+ * Private header for the mmc subsystem
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef _MMC_CORE_CARD_H
+#define _MMC_CORE_CARD_H
+
+#include <linux/mmc/card.h>
+
+#define mmc_card_name(c) ((c)->cid.prod_name)
+#define mmc_card_id(c) (dev_name(&(c)->dev))
+#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
+
+/* Card states */
+#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
+#define MMC_STATE_READONLY (1<<1) /* card is read-only */
+#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */
+#define MMC_CARD_SDXC (1<<3) /* card is SDXC */
+#define MMC_CARD_REMOVED (1<<4) /* card has been removed */
+#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */
+#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */
+
+#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
+#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
+#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
+#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
+#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
+#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
+#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED)
+
+#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
+#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
+#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
+#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
+#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
+#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
+#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
+#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
+#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
+
+/*
+ * The world is not perfect and supplies us with broken mmc/sdio devices.
+ * For at least some of these bugs we need a work-around.
+ */
+struct mmc_fixup {
+ /* CID-specific fields. */
+ const char *name;
+
+ /* Valid revision range */
+ u64 rev_start, rev_end;
+
+ unsigned int manfid;
+ unsigned short oemid;
+
+ /* SDIO-specific fields. You can use SDIO_ANY_ID here of course */
+ u16 cis_vendor, cis_device;
+
+ /* for MMC cards */
+ unsigned int ext_csd_rev;
+
+ void (*vendor_fixup)(struct mmc_card *card, int data);
+ int data;
+};
+
+#define CID_MANFID_ANY (-1u)
+#define CID_OEMID_ANY ((unsigned short) -1)
+#define CID_NAME_ANY (NULL)
+
+#define EXT_CSD_REV_ANY (-1u)
+
+#define CID_MANFID_SANDISK 0x2
+#define CID_MANFID_TOSHIBA 0x11
+#define CID_MANFID_MICRON 0x13
+#define CID_MANFID_SAMSUNG 0x15
+#define CID_MANFID_KINGSTON 0x70
+#define CID_MANFID_HYNIX 0x90
+
+#define END_FIXUP { NULL }
+
+#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
+ _cis_vendor, _cis_device, \
+ _fixup, _data, _ext_csd_rev) \
+ { \
+ .name = (_name), \
+ .manfid = (_manfid), \
+ .oemid = (_oemid), \
+ .rev_start = (_rev_start), \
+ .rev_end = (_rev_end), \
+ .cis_vendor = (_cis_vendor), \
+ .cis_device = (_cis_device), \
+ .vendor_fixup = (_fixup), \
+ .data = (_data), \
+ .ext_csd_rev = (_ext_csd_rev), \
+ }
+
+#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
+ _fixup, _data, _ext_csd_rev) \
+ _FIXUP_EXT(_name, _manfid, \
+ _oemid, _rev_start, _rev_end, \
+ SDIO_ANY_ID, SDIO_ANY_ID, \
+ _fixup, _data, _ext_csd_rev) \
+
+#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
+ MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \
+ EXT_CSD_REV_ANY)
+
+#define MMC_FIXUP_EXT_CSD_REV(_name, _manfid, _oemid, _fixup, _data, \
+ _ext_csd_rev) \
+ MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data, \
+ _ext_csd_rev)
+
+#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
+ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
+ CID_OEMID_ANY, 0, -1ull, \
+ _vendor, _device, \
+ _fixup, _data, EXT_CSD_REV_ANY) \
+
+#define cid_rev(hwrev, fwrev, year, month) \
+ (((u64) hwrev) << 40 | \
+ ((u64) fwrev) << 32 | \
+ ((u64) year) << 16 | \
+ ((u64) month))
+
+#define cid_rev_card(card) \
+ cid_rev(card->cid.hwrev, \
+ card->cid.fwrev, \
+ card->cid.year, \
+ card->cid.month)
+
+/*
+ * Unconditionally quirk add/remove.
+ */
+static inline void __maybe_unused add_quirk(struct mmc_card *card, int data)
+{
+ card->quirks |= data;
+}
+
+static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
+{
+ card->quirks &= ~data;
+}
+
+/*
+ * Quirk add/remove for MMC products.
+ */
+static inline void __maybe_unused add_quirk_mmc(struct mmc_card *card, int data)
+{
+ if (mmc_card_mmc(card))
+ card->quirks |= data;
+}
+
+static inline void __maybe_unused remove_quirk_mmc(struct mmc_card *card,
+ int data)
+{
+ if (mmc_card_mmc(card))
+ card->quirks &= ~data;
+}
+
+/*
+ * Quirk add/remove for SD products.
+ */
+static inline void __maybe_unused add_quirk_sd(struct mmc_card *card, int data)
+{
+ if (mmc_card_sd(card))
+ card->quirks |= data;
+}
+
+static inline void __maybe_unused remove_quirk_sd(struct mmc_card *card,
+ int data)
+{
+ if (mmc_card_sd(card))
+ card->quirks &= ~data;
+}
+
+static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_LENIENT_FN0;
+}
+
+static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+}
+
+static inline int mmc_card_disable_cd(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_DISABLE_CD;
+}
+
+static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
+}
+
+static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_BROKEN_BYTE_MODE_512;
+}
+
+static inline int mmc_card_long_read_time(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_LONG_READ_TIME;
+}
+
+static inline int mmc_card_broken_irq_polling(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING;
+}
+
+static inline int mmc_card_broken_hpi(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_BROKEN_HPI;
+}
+
+#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1076b9d..926e0fd 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -40,6 +40,7 @@
#include <trace/events/mmc.h>
#include "core.h"
+#include "card.h"
#include "bus.h"
#include "host.h"
#include "sdio_bus.h"
@@ -630,10 +631,41 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
}
/**
- * mmc_start_req - start a non-blocking request
+ * mmc_finalize_areq() - finalize an asynchronous request
+ * @host: MMC host to finalize any ongoing request on
+ *
+ * Returns the status of the ongoing asynchronous request, but
+ * MMC_BLK_SUCCESS if no request was going on.
+ */
+static enum mmc_blk_status mmc_finalize_areq(struct mmc_host *host)
+{
+ enum mmc_blk_status status;
+
+ if (!host->areq)
+ return MMC_BLK_SUCCESS;
+
+ status = mmc_wait_for_data_req_done(host, host->areq->mrq);
+ if (status == MMC_BLK_NEW_REQUEST)
+ return status;
+
+ /*
+ * Check BKOPS urgency for each R1 response
+ */
+ if (host->card && mmc_card_mmc(host->card) &&
+ ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
+ (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
+ (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
+ mmc_start_bkops(host->card, true);
+ }
+
+ return status;
+}
+
+/**
+ * mmc_start_areq - start an asynchronous request
* @host: MMC host to start command
- * @areq: async request to start
- * @error: out parameter returns 0 for success, otherwise non zero
+ * @areq: asynchronous request to start
+ * @ret_stat: out parameter for status
*
* Start a new MMC custom command request for a host.
* If there is on ongoing async request wait for completion
@@ -645,11 +677,11 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
* return the completed request. If there is no ongoing request, NULL
* is returned without waiting. NULL is not an error condition.
*/
-struct mmc_async_req *mmc_start_req(struct mmc_host *host,
- struct mmc_async_req *areq,
- enum mmc_blk_status *ret_stat)
+struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
+ struct mmc_async_req *areq,
+ enum mmc_blk_status *ret_stat)
{
- enum mmc_blk_status status = MMC_BLK_SUCCESS;
+ enum mmc_blk_status status;
int start_err = 0;
struct mmc_async_req *data = host->areq;
@@ -657,44 +689,25 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
if (areq)
mmc_pre_req(host, areq->mrq);
- if (host->areq) {
- status = mmc_wait_for_data_req_done(host, host->areq->mrq);
- if (status == MMC_BLK_NEW_REQUEST) {
- if (ret_stat)
- *ret_stat = status;
- /*
- * The previous request was not completed,
- * nothing to return
- */
- return NULL;
- }
- /*
- * Check BKOPS urgency for each R1 response
- */
- if (host->card && mmc_card_mmc(host->card) &&
- ((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
- (mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
- (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
-
- /* Cancel the prepared request */
- if (areq)
- mmc_post_req(host, areq->mrq, -EINVAL);
-
- mmc_start_bkops(host->card, true);
+ /* Finalize previous request */
+ status = mmc_finalize_areq(host);
- /* prepare the request again */
- if (areq)
- mmc_pre_req(host, areq->mrq);
- }
+ /* The previous request is still going on... */
+ if (status == MMC_BLK_NEW_REQUEST) {
+ if (ret_stat)
+ *ret_stat = status;
+ return NULL;
}
+ /* Fine so far, start the new request! */
if (status == MMC_BLK_SUCCESS && areq)
start_err = __mmc_start_data_req(host, areq->mrq);
+ /* Postprocess the old request at this point */
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
- /* Cancel a prepared request if it was not started. */
+ /* Cancel a prepared request if it was not started. */
if ((status != MMC_BLK_SUCCESS || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL);
@@ -707,7 +720,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
*ret_stat = status;
return data;
}
-EXPORT_SYMBOL(mmc_start_req);
+EXPORT_SYMBOL(mmc_start_areq);
/**
* mmc_wait_for_req - start a request and wait for completion
@@ -807,7 +820,7 @@ EXPORT_SYMBOL(mmc_interrupt_hpi);
*/
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
- struct mmc_request mrq = {NULL};
+ struct mmc_request mrq = {};
WARN_ON(!host->claimed);
@@ -1630,7 +1643,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return ocr;
}
-int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{
int err = 0;
int old_signal_voltage = host->ios.signal_voltage;
@@ -1646,20 +1659,13 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
}
-int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
+int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err = 0;
u32 clock;
/*
- * Send CMD11 only if the request is to switch the card to
- * 1.8V signalling.
- */
- if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
- return __mmc_set_signal_voltage(host, signal_voltage);
-
- /*
* If we cannot switch voltages, return failure so the caller
* can continue without UHS mode
*/
@@ -1697,7 +1703,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
host->ios.clock = 0;
mmc_set_ios(host);
- if (__mmc_set_signal_voltage(host, signal_voltage)) {
+ if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) {
/*
* Voltages may not have been switched, but we've already
* sent CMD11, so a power cycle is required anyway
@@ -1806,11 +1812,11 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
mmc_set_initial_state(host);
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
- if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)
+ if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330))
dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");
- else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0)
+ else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");
- else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0)
+ else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
/*
@@ -2129,7 +2135,7 @@ static unsigned int mmc_erase_timeout(struct mmc_card *card,
static int mmc_do_erase(struct mmc_card *card, unsigned int from,
unsigned int to, unsigned int arg)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
unsigned int qty = 0, busy_timeout = 0;
bool use_r1b_resp = false;
unsigned long timeout;
@@ -2551,7 +2557,7 @@ EXPORT_SYMBOL(mmc_calc_max_discard);
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
if (mmc_card_blockaddr(card) || mmc_card_ddr52(card) ||
mmc_card_hs400(card) || mmc_card_hs400es(card))
@@ -2567,7 +2573,7 @@ EXPORT_SYMBOL(mmc_set_blocklen);
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
bool is_rel_write)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = MMC_SET_BLOCK_COUNT;
cmd.arg = blockcount & 0x0000FFFF;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 0fa86a2..55f543f 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -12,6 +12,11 @@
#define _MMC_CORE_CORE_H
#include <linux/delay.h>
+#include <linux/sched.h>
+
+struct mmc_host;
+struct mmc_card;
+struct mmc_request;
#define MMC_CMD_RETRIES 3
@@ -43,8 +48,8 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
-int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
-int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
+int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr);
+int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
@@ -69,6 +74,7 @@ void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host);
int _mmc_detect_card_removed(struct mmc_host *host);
+int mmc_detect_card_removed(struct mmc_host *host);
int mmc_attach_mmc(struct mmc_host *host);
int mmc_attach_sd(struct mmc_host *host);
@@ -98,5 +104,38 @@ static inline void mmc_register_pm_notifier(struct mmc_host *host) { }
static inline void mmc_unregister_pm_notifier(struct mmc_host *host) { }
#endif
-#endif
+void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq);
+bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
+
+int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg);
+int mmc_can_erase(struct mmc_card *card);
+int mmc_can_trim(struct mmc_card *card);
+int mmc_can_discard(struct mmc_card *card);
+int mmc_can_sanitize(struct mmc_card *card);
+int mmc_can_secure_erase_trim(struct mmc_card *card);
+int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
+ unsigned int nr);
+unsigned int mmc_calc_max_discard(struct mmc_card *card);
+
+int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
+int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
+ bool is_rel_write);
+
+int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
+void mmc_release_host(struct mmc_host *host);
+void mmc_get_card(struct mmc_card *card);
+void mmc_put_card(struct mmc_card *card);
+
+/**
+ * mmc_claim_host - exclusively claim a host
+ * @host: mmc host to claim
+ *
+ * Claim a host for a set of operations.
+ */
+static inline void mmc_claim_host(struct mmc_host *host)
+{
+ __mmc_claim_host(host, NULL);
+}
+#endif
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 30623b8..a1fba57 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -20,6 +20,8 @@
#include <linux/mmc/host.h>
#include "core.h"
+#include "card.h"
+#include "host.h"
#include "mmc_ops.h"
#ifdef CONFIG_FAIL_MMC_REQUEST
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 98f25ff..3f8c85d 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -34,14 +34,11 @@
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
static DEFINE_IDA(mmc_host_ida);
-static DEFINE_SPINLOCK(mmc_host_lock);
static void mmc_host_classdev_release(struct device *dev)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
- spin_lock(&mmc_host_lock);
- ida_remove(&mmc_host_ida, host->index);
- spin_unlock(&mmc_host_lock);
+ ida_simple_remove(&mmc_host_ida, host->index);
kfree(host);
}
@@ -301,6 +298,8 @@ int mmc_of_parse(struct mmc_host *host)
if (of_property_read_bool(np, "wakeup-source") ||
of_property_read_bool(np, "enable-sdio-wakeup")) /* legacy */
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ if (of_property_read_bool(np, "mmc-ddr-3_3v"))
+ host->caps |= MMC_CAP_3_3V_DDR;
if (of_property_read_bool(np, "mmc-ddr-1_8v"))
host->caps |= MMC_CAP_1_8V_DDR;
if (of_property_read_bool(np, "mmc-ddr-1_2v"))
@@ -354,22 +353,13 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
/* scanning will be enabled when we're ready */
host->rescan_disable = 1;
-again:
- if (!ida_pre_get(&mmc_host_ida, GFP_KERNEL)) {
+ err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL);
+ if (err < 0) {
kfree(host);
return NULL;
}
- spin_lock(&mmc_host_lock);
- err = ida_get_new(&mmc_host_ida, &host->index);
- spin_unlock(&mmc_host_lock);
-
- if (err == -EAGAIN) {
- goto again;
- } else if (err) {
- kfree(host);
- return NULL;
- }
+ host->index = err;
dev_set_name(&host->class_dev, "mmc%d", host->index);
@@ -381,6 +371,8 @@ again:
if (mmc_gpio_alloc(host)) {
put_device(&host->class_dev);
+ ida_simple_remove(&mmc_host_ida, host->index);
+ kfree(host);
return NULL;
}
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 992bf53..fb6a76a 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -10,6 +10,7 @@
*/
#ifndef _MMC_CORE_HOST_H
#define _MMC_CORE_HOST_H
+
#include <linux/mmc/host.h>
int mmc_register_host_class(void);
@@ -20,6 +21,53 @@ void mmc_retune_disable(struct mmc_host *host);
void mmc_retune_hold(struct mmc_host *host);
void mmc_retune_release(struct mmc_host *host);
int mmc_retune(struct mmc_host *host);
+void mmc_retune_pause(struct mmc_host *host);
+void mmc_retune_unpause(struct mmc_host *host);
+
+static inline void mmc_retune_recheck(struct mmc_host *host)
+{
+ if (host->hold_retune <= 1)
+ host->retune_now = 1;
+}
+
+static inline int mmc_host_cmd23(struct mmc_host *host)
+{
+ return host->caps & MMC_CAP_CMD23;
+}
+
+static inline int mmc_boot_partition_access(struct mmc_host *host)
+{
+ return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
+}
+
+static inline int mmc_host_uhs(struct mmc_host *host)
+{
+ return host->caps &
+ (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_DDR50);
+}
+
+static inline bool mmc_card_hs200(struct mmc_card *card)
+{
+ return card->host->ios.timing == MMC_TIMING_MMC_HS200;
+}
+
+static inline bool mmc_card_ddr52(struct mmc_card *card)
+{
+ return card->host->ios.timing == MMC_TIMING_MMC_DDR52;
+}
+
+static inline bool mmc_card_hs400(struct mmc_card *card)
+{
+ return card->host->ios.timing == MMC_TIMING_MMC_HS400;
+}
+
+static inline bool mmc_card_hs400es(struct mmc_card *card)
+{
+ return card->host->ios.enhanced_strobe;
+}
+
#endif
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 0fccca0..7fd7228 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -21,9 +21,11 @@
#include <linux/mmc/mmc.h>
#include "core.h"
+#include "card.h"
#include "host.h"
#include "bus.h"
#include "mmc_ops.h"
+#include "quirks.h"
#include "sd_ops.h"
#define DEFAULT_CMD6_TIMEOUT_MS 500
@@ -47,17 +49,6 @@ static const unsigned int tacc_mant[] = {
35, 40, 45, 50, 55, 60, 70, 80,
};
-static const struct mmc_fixup mmc_ext_csd_fixups[] = {
- /*
- * Certain Hynix eMMC 4.41 cards might get broken when HPI feature
- * is used so disable the HPI feature for such buggy cards.
- */
- MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
- 0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
-
- END_FIXUP
-};
-
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
@@ -212,7 +203,7 @@ static void mmc_select_card_type(struct mmc_card *card)
avail_type |= EXT_CSD_CARD_TYPE_HS_52;
}
- if (caps & MMC_CAP_1_8V_DDR &&
+ if (caps & (MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR) &&
card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) {
hs_max_dtr = MMC_HIGH_DDR_MAX_DTR;
avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V;
@@ -307,6 +298,18 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
}
}
+static void mmc_part_add(struct mmc_card *card, unsigned int size,
+ unsigned int part_cfg, char *name, int idx, bool ro,
+ int area_type)
+{
+ card->part[card->nr_parts].size = size;
+ card->part[card->nr_parts].part_cfg = part_cfg;
+ sprintf(card->part[card->nr_parts].name, name, idx);
+ card->part[card->nr_parts].force_ro = ro;
+ card->part[card->nr_parts].area_type = area_type;
+ card->nr_parts++;
+}
+
static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd)
{
int idx;
@@ -530,8 +533,14 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
EXT_CSD_MANUAL_BKOPS_MASK);
card->ext_csd.raw_bkops_status =
ext_csd[EXT_CSD_BKOPS_STATUS];
- if (!card->ext_csd.man_bkops_en)
- pr_debug("%s: MAN_BKOPS_EN bit is not set\n",
+ if (card->ext_csd.man_bkops_en)
+ pr_debug("%s: MAN_BKOPS_EN bit is set\n",
+ mmc_hostname(card->host));
+ card->ext_csd.auto_bkops_en =
+ (ext_csd[EXT_CSD_BKOPS_EN] &
+ EXT_CSD_AUTO_BKOPS_MASK);
+ if (card->ext_csd.auto_bkops_en)
+ pr_debug("%s: AUTO_BKOPS_EN bit is set\n",
mmc_hostname(card->host));
}
@@ -617,6 +626,12 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.ffu_capable =
(ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) &&
!(ext_csd[EXT_CSD_FW_CONFIG] & 0x1);
+
+ card->ext_csd.pre_eol_info = ext_csd[EXT_CSD_PRE_EOL_INFO];
+ card->ext_csd.device_life_time_est_typ_a =
+ ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A];
+ card->ext_csd.device_life_time_est_typ_b =
+ ext_csd[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B];
}
/* eMMC v5.1 or later */
@@ -764,6 +779,10 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
+MMC_DEV_ATTR(pre_eol_info, "%02x\n", card->ext_csd.pre_eol_info);
+MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n",
+ card->ext_csd.device_life_time_est_typ_a,
+ card->ext_csd.device_life_time_est_typ_b);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
card->ext_csd.enhanced_area_offset);
@@ -817,6 +836,8 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_prv.attr,
+ &dev_attr_pre_eol_info.attr,
+ &dev_attr_life_time.attr,
&dev_attr_serial.attr,
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
@@ -1095,16 +1116,19 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
*
* WARNING: eMMC rules are NOT the same as SD DDR
*/
- err = -EINVAL;
- if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) {
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
+ if (!err)
+ return 0;
+ }
- if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V &&
+ host->caps & MMC_CAP_1_8V_DDR)
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
/* make sure vccq is 3.3v after switching disaster */
if (err)
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
return err;
}
@@ -1271,10 +1295,10 @@ static int mmc_select_hs400es(struct mmc_card *card)
}
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400_1_2V)
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V)
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
/* If fails try again during next card power cycle */
if (err)
@@ -1380,10 +1404,10 @@ static int mmc_select_hs200(struct mmc_card *card)
old_signal_voltage = host->ios.signal_voltage;
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
- err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+ err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
/* If fails try again during next card power cycle */
if (err)
@@ -1425,7 +1449,7 @@ static int mmc_select_hs200(struct mmc_card *card)
err:
if (err) {
/* fall back to the old signal voltage, if fails report error */
- if (__mmc_set_signal_voltage(host, old_signal_voltage))
+ if (mmc_set_signal_voltage(host, old_signal_voltage))
err = -EIO;
pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
@@ -1805,7 +1829,7 @@ static int mmc_can_sleep(struct mmc_card *card)
static int mmc_sleep(struct mmc_host *host)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
struct mmc_card *card = host->card;
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
int err;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index e6ea850..fe80f26 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -57,7 +57,7 @@ static const u8 tuning_blk_pattern_8bit[] = {
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
@@ -79,7 +79,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = MMC_SELECT_CARD;
@@ -115,7 +115,7 @@ int mmc_deselect_cards(struct mmc_host *host)
*/
int mmc_set_dsr(struct mmc_host *host)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = MMC_SET_DSR;
@@ -128,7 +128,7 @@ int mmc_set_dsr(struct mmc_host *host)
int mmc_go_idle(struct mmc_host *host)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
/*
* Non-SPI hosts need to prevent chipselect going active during
@@ -164,7 +164,7 @@ int mmc_go_idle(struct mmc_host *host)
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int i, err = 0;
cmd.opcode = MMC_SEND_OP_COND;
@@ -203,7 +203,7 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
@@ -220,7 +220,7 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
int mmc_set_relative_addr(struct mmc_card *card)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16;
@@ -233,7 +233,7 @@ static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = opcode;
cmd.arg = arg;
@@ -256,9 +256,9 @@ static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
{
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
mrq.cmd = &cmd;
@@ -387,7 +387,7 @@ EXPORT_SYMBOL_GPL(mmc_get_ext_csd);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err;
cmd.opcode = MMC_SPI_READ_OCR;
@@ -402,7 +402,7 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err;
cmd.opcode = MMC_SPI_CRC_ON_OFF;
@@ -530,7 +530,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
{
struct mmc_host *host = card->host;
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
bool use_r1b_resp = use_busy_signal;
unsigned char old_timing = host->ios.timing;
@@ -610,9 +610,9 @@ EXPORT_SYMBOL_GPL(mmc_switch);
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error)
{
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
struct mmc_ios *ios = &host->ios;
const u8 *tuning_block_pattern;
@@ -679,7 +679,7 @@ EXPORT_SYMBOL_GPL(mmc_send_tuning);
int mmc_abort_tuning(struct mmc_host *host, u32 opcode)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
/*
* eMMC specification specifies that CMD12 can be used to stop a tuning
@@ -706,9 +706,9 @@ static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len)
{
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
u8 *data_buf;
u8 *test_buf;
@@ -802,7 +802,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
unsigned int opcode;
int err;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index abd525e..74beea8 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -12,6 +12,11 @@
#ifndef _MMC_MMC_OPS_H
#define _MMC_MMC_OPS_H
+#include <linux/types.h>
+
+struct mmc_host;
+struct mmc_card;
+
int mmc_select_card(struct mmc_card *card);
int mmc_deselect_cards(struct mmc_host *host);
int mmc_set_dsr(struct mmc_host *host);
@@ -26,12 +31,21 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
+int mmc_interrupt_hpi(struct mmc_card *card);
int mmc_can_ext_csd(struct mmc_card *card);
+int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
int mmc_switch_status(struct mmc_card *card);
int __mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, unsigned char timing,
bool use_busy_signal, bool send_status, bool retry_crc_err);
+int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
+ unsigned int timeout_ms);
+int mmc_stop_bkops(struct mmc_card *card);
+int mmc_read_bkops_status(struct mmc_card *card);
+void mmc_start_bkops(struct mmc_card *card, bool from_exception);
+int mmc_can_reset(struct mmc_card *card);
+int mmc_flush_cache(struct mmc_card *card);
#endif
diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c
index 3ab6e52..f99ac31 100644
--- a/drivers/mmc/core/mmc_test.c
+++ b/drivers/mmc/core/mmc_test.c
@@ -22,6 +22,11 @@
#include <linux/seq_file.h>
#include <linux/module.h>
+#include "core.h"
+#include "card.h"
+#include "host.h"
+#include "bus.h"
+
#define RESULT_OK 0
#define RESULT_FAIL 1
#define RESULT_UNSUP_HOST 2
@@ -260,7 +265,7 @@ static int mmc_test_busy(struct mmc_command *cmd)
static int mmc_test_wait_busy(struct mmc_test_card *test)
{
int ret, busy;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
busy = 0;
do {
@@ -277,8 +282,7 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
if (!busy && mmc_test_busy(&cmd)) {
busy = 1;
if (test->card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
- pr_info("%s: Warning: Host did not "
- "wait for busy state to end.\n",
+ pr_info("%s: Warning: Host did not wait for busy state to end.\n",
mmc_hostname(test->card->host));
}
} while (mmc_test_busy(&cmd));
@@ -292,10 +296,10 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
u8 *buffer, unsigned addr, unsigned blksz, int write)
{
- struct mmc_request mrq = {0};
- struct mmc_command cmd = {0};
- struct mmc_command stop = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_command stop = {};
+ struct mmc_data data = {};
struct scatterlist sg;
@@ -357,12 +361,11 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
if (max_segs > max_page_cnt)
max_segs = max_page_cnt;
- mem = kzalloc(sizeof(struct mmc_test_mem), GFP_KERNEL);
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
if (!mem)
return NULL;
- mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_segs,
- GFP_KERNEL);
+ mem->arr = kcalloc(max_segs, sizeof(*mem->arr), GFP_KERNEL);
if (!mem->arr)
goto out_free;
@@ -546,7 +549,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test,
if (!test->gr)
return;
- tr = kmalloc(sizeof(struct mmc_test_transfer_result), GFP_KERNEL);
+ tr = kmalloc(sizeof(*tr), GFP_KERNEL);
if (!tr)
return;
@@ -641,11 +644,11 @@ static int __mmc_test_prepare(struct mmc_test_card *test, int write)
if (write)
memset(test->buffer, 0xDF, 512);
else {
- for (i = 0;i < 512;i++)
+ for (i = 0; i < 512; i++)
test->buffer[i] = i;
}
- for (i = 0;i < BUFFER_SIZE / 512;i++) {
+ for (i = 0; i < BUFFER_SIZE / 512; i++) {
ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1);
if (ret)
return ret;
@@ -674,7 +677,7 @@ static int mmc_test_cleanup(struct mmc_test_card *test)
memset(test->buffer, 0, 512);
- for (i = 0;i < BUFFER_SIZE / 512;i++) {
+ for (i = 0; i < BUFFER_SIZE / 512; i++) {
ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1);
if (ret)
return ret;
@@ -850,7 +853,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
for (i = 0; i < count; i++) {
mmc_test_prepare_mrq(test, cur_areq->mrq, sg, sg_len, dev_addr,
blocks, blksz, write);
- done_areq = mmc_start_req(test->card->host, cur_areq, &status);
+ done_areq = mmc_start_areq(test->card->host, cur_areq, &status);
if (status != MMC_BLK_SUCCESS || (!done_areq && i > 0)) {
ret = RESULT_FAIL;
@@ -869,7 +872,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
dev_addr += blocks;
}
- done_areq = mmc_start_req(test->card->host, NULL, &status);
+ done_areq = mmc_start_areq(test->card->host, NULL, &status);
if (status != MMC_BLK_SUCCESS)
ret = RESULT_FAIL;
@@ -885,10 +888,10 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
unsigned blocks, unsigned blksz, int write)
{
- struct mmc_request mrq = {0};
- struct mmc_command cmd = {0};
- struct mmc_command stop = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_command stop = {};
+ struct mmc_data data = {};
mrq.cmd = &cmd;
mrq.data = &data;
@@ -910,10 +913,10 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
static int mmc_test_broken_transfer(struct mmc_test_card *test,
unsigned blocks, unsigned blksz, int write)
{
- struct mmc_request mrq = {0};
- struct mmc_command cmd = {0};
- struct mmc_command stop = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_command stop = {};
+ struct mmc_data data = {};
struct scatterlist sg;
@@ -946,7 +949,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
unsigned long flags;
if (write) {
- for (i = 0;i < blocks * blksz;i++)
+ for (i = 0; i < blocks * blksz; i++)
test->scratch[i] = i;
} else {
memset(test->scratch, 0, BUFFER_SIZE);
@@ -980,7 +983,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
memset(test->buffer, 0, sectors * 512);
- for (i = 0;i < sectors;i++) {
+ for (i = 0; i < sectors; i++) {
ret = mmc_test_buffer_transfer(test,
test->buffer + i * 512,
dev_addr + i, 512, 0);
@@ -988,12 +991,12 @@ static int mmc_test_transfer(struct mmc_test_card *test,
return ret;
}
- for (i = 0;i < blocks * blksz;i++) {
+ for (i = 0; i < blocks * blksz; i++) {
if (test->buffer[i] != (u8)i)
return RESULT_FAIL;
}
- for (;i < sectors * 512;i++) {
+ for (; i < sectors * 512; i++) {
if (test->buffer[i] != 0xDF)
return RESULT_FAIL;
}
@@ -1001,7 +1004,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
local_irq_save(flags);
sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
local_irq_restore(flags);
- for (i = 0;i < blocks * blksz;i++) {
+ for (i = 0; i < blocks * blksz; i++) {
if (test->scratch[i] != (u8)i)
return RESULT_FAIL;
}
@@ -1086,7 +1089,7 @@ static int mmc_test_multi_write(struct mmc_test_card *test)
sg_init_one(&sg, test->buffer, size);
- return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
+ return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
}
static int mmc_test_multi_read(struct mmc_test_card *test)
@@ -1107,7 +1110,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
sg_init_one(&sg, test->buffer, size);
- return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
+ return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
}
static int mmc_test_pow2_write(struct mmc_test_card *test)
@@ -1118,7 +1121,7 @@ static int mmc_test_pow2_write(struct mmc_test_card *test)
if (!test->card->csd.write_partial)
return RESULT_UNSUP_CARD;
- for (i = 1; i < 512;i <<= 1) {
+ for (i = 1; i < 512; i <<= 1) {
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
if (ret)
@@ -1136,7 +1139,7 @@ static int mmc_test_pow2_read(struct mmc_test_card *test)
if (!test->card->csd.read_partial)
return RESULT_UNSUP_CARD;
- for (i = 1; i < 512;i <<= 1) {
+ for (i = 1; i < 512; i <<= 1) {
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
if (ret)
@@ -1154,7 +1157,7 @@ static int mmc_test_weird_write(struct mmc_test_card *test)
if (!test->card->csd.write_partial)
return RESULT_UNSUP_CARD;
- for (i = 3; i < 512;i += 7) {
+ for (i = 3; i < 512; i += 7) {
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
if (ret)
@@ -1172,7 +1175,7 @@ static int mmc_test_weird_read(struct mmc_test_card *test)
if (!test->card->csd.read_partial)
return RESULT_UNSUP_CARD;
- for (i = 3; i < 512;i += 7) {
+ for (i = 3; i < 512; i += 7) {
sg_init_one(&sg, test->buffer, i);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
if (ret)
@@ -1231,7 +1234,7 @@ static int mmc_test_align_multi_write(struct mmc_test_card *test)
for (i = 1; i < TEST_ALIGN_END; i++) {
sg_init_one(&sg, test->buffer + i, size);
- ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
+ ret = mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
if (ret)
return ret;
}
@@ -1258,7 +1261,7 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)
for (i = 1; i < TEST_ALIGN_END; i++) {
sg_init_one(&sg, test->buffer + i, size);
- ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
+ ret = mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
if (ret)
return ret;
}
@@ -1357,7 +1360,7 @@ static int mmc_test_multi_write_high(struct mmc_test_card *test)
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, size, 0);
- return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
+ return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
}
static int mmc_test_multi_read_high(struct mmc_test_card *test)
@@ -1379,7 +1382,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
sg_init_table(&sg, 1);
sg_set_page(&sg, test->highmem, size, 0);
- return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
+ return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
}
#else
@@ -1533,7 +1536,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test)
/*
* Initialize an area for testing large transfers. The test area is set to the
- * middle of the card because cards may have different charateristics at the
+ * middle of the card because cards may have different characteristics at the
* front (for FAT file system optimization). Optionally, the area is erased
* (if the card supports it) which may improve write performance. Optionally,
* the area is filled with data for subsequent read tests.
@@ -1579,7 +1582,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
if (!t->mem)
return -ENOMEM;
- t->sg = kmalloc(sizeof(struct scatterlist) * t->max_segs, GFP_KERNEL);
+ t->sg = kmalloc_array(t->max_segs, sizeof(*t->sg), GFP_KERNEL);
if (!t->sg) {
ret = -ENOMEM;
goto out_free;
@@ -2147,7 +2150,7 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
int i;
for (i = 0 ; i < rw->len && ret == 0; i++) {
- ret = mmc_test_rw_multiple(test, rw, 512*1024, rw->size,
+ ret = mmc_test_rw_multiple(test, rw, 512 * 1024, rw->size,
rw->sg_len[i]);
if (ret)
break;
@@ -2399,7 +2402,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
/* Start ongoing data request */
if (use_areq) {
- mmc_start_req(host, &test_areq.areq, &blkstat);
+ mmc_start_areq(host, &test_areq.areq, &blkstat);
if (blkstat != MMC_BLK_SUCCESS) {
ret = RESULT_FAIL;
goto out_free;
@@ -2437,7 +2440,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
/* Wait for data request to complete */
if (use_areq) {
- mmc_start_req(host, NULL, &blkstat);
+ mmc_start_areq(host, NULL, &blkstat);
if (blkstat != MMC_BLK_SUCCESS)
ret = RESULT_FAIL;
} else {
@@ -2954,7 +2957,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
mmc_claim_host(test->card->host);
- for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
+ for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++) {
struct mmc_test_general_result *gr;
if (testcase && ((i + 1) != testcase))
@@ -2967,16 +2970,14 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
if (mmc_test_cases[i].prepare) {
ret = mmc_test_cases[i].prepare(test);
if (ret) {
- pr_info("%s: Result: Prepare "
- "stage failed! (%d)\n",
+ pr_info("%s: Result: Prepare stage failed! (%d)\n",
mmc_hostname(test->card->host),
ret);
continue;
}
}
- gr = kzalloc(sizeof(struct mmc_test_general_result),
- GFP_KERNEL);
+ gr = kzalloc(sizeof(*gr), GFP_KERNEL);
if (gr) {
INIT_LIST_HEAD(&gr->tr_lst);
@@ -3005,13 +3006,11 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
mmc_hostname(test->card->host));
break;
case RESULT_UNSUP_HOST:
- pr_info("%s: Result: UNSUPPORTED "
- "(by host)\n",
+ pr_info("%s: Result: UNSUPPORTED (by host)\n",
mmc_hostname(test->card->host));
break;
case RESULT_UNSUP_CARD:
- pr_info("%s: Result: UNSUPPORTED "
- "(by card)\n",
+ pr_info("%s: Result: UNSUPPORTED (by card)\n",
mmc_hostname(test->card->host));
break;
default:
@@ -3026,8 +3025,7 @@ static void mmc_test_run(struct mmc_test_card *test, int testcase)
if (mmc_test_cases[i].cleanup) {
ret = mmc_test_cases[i].cleanup(test);
if (ret) {
- pr_info("%s: Warning: Cleanup "
- "stage failed! (%d)\n",
+ pr_info("%s: Warning: Cleanup stage failed! (%d)\n",
mmc_hostname(test->card->host),
ret);
}
@@ -3113,7 +3111,7 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf,
if (ret)
return ret;
- test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
+ test = kzalloc(sizeof(*test), GFP_KERNEL);
if (!test)
return -ENOMEM;
@@ -3163,9 +3161,9 @@ static int mtf_testlist_show(struct seq_file *sf, void *data)
mutex_lock(&mmc_test_lock);
- seq_printf(sf, "0:\tRun all tests\n");
+ seq_puts(sf, "0:\tRun all tests\n");
for (i = 0; i < ARRAY_SIZE(mmc_test_cases); i++)
- seq_printf(sf, "%d:\t%s\n", i+1, mmc_test_cases[i].name);
+ seq_printf(sf, "%d:\t%s\n", i + 1, mmc_test_cases[i].name);
mutex_unlock(&mmc_test_lock);
@@ -3218,7 +3216,7 @@ static int __mmc_test_register_dbgfs_file(struct mmc_card *card,
return -ENODEV;
}
- df = kmalloc(sizeof(struct mmc_test_dbgfs_file), GFP_KERNEL);
+ df = kmalloc(sizeof(*df), GFP_KERNEL);
if (!df) {
debugfs_remove(file);
dev_err(&card->dev,
diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h
index d69e751..39c911a 100644
--- a/drivers/mmc/core/pwrseq.h
+++ b/drivers/mmc/core/pwrseq.h
@@ -8,7 +8,11 @@
#ifndef _MMC_CORE_PWRSEQ_H
#define _MMC_CORE_PWRSEQ_H
-#include <linux/mmc/host.h>
+#include <linux/types.h>
+
+struct mmc_host;
+struct device;
+struct module;
struct mmc_pwrseq_ops {
void (*pre_power_on)(struct mmc_host *host);
diff --git a/drivers/mmc/core/pwrseq_sd8787.c b/drivers/mmc/core/pwrseq_sd8787.c
new file mode 100644
index 0000000..1a21e14
--- /dev/null
+++ b/drivers/mmc/core/pwrseq_sd8787.c
@@ -0,0 +1,117 @@
+/*
+ * pwrseq_sd8787.c - power sequence support for Marvell SD8787 BT + Wifi chip
+ *
+ * Copyright (C) 2016 Matt Ranostay <matt@ranostay.consulting>
+ *
+ * Based on the original work pwrseq_simple.c
+ * Copyright (C) 2014 Linaro Ltd
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+
+#include <linux/mmc/host.h>
+
+#include "pwrseq.h"
+
+struct mmc_pwrseq_sd8787 {
+ struct mmc_pwrseq pwrseq;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *pwrdn_gpio;
+};
+
+#define to_pwrseq_sd8787(p) container_of(p, struct mmc_pwrseq_sd8787, pwrseq)
+
+static void mmc_pwrseq_sd8787_pre_power_on(struct mmc_host *host)
+{
+ struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
+
+ gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
+
+ msleep(300);
+ gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 1);
+}
+
+static void mmc_pwrseq_sd8787_power_off(struct mmc_host *host)
+{
+ struct mmc_pwrseq_sd8787 *pwrseq = to_pwrseq_sd8787(host->pwrseq);
+
+ gpiod_set_value_cansleep(pwrseq->pwrdn_gpio, 0);
+ gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
+}
+
+static const struct mmc_pwrseq_ops mmc_pwrseq_sd8787_ops = {
+ .pre_power_on = mmc_pwrseq_sd8787_pre_power_on,
+ .power_off = mmc_pwrseq_sd8787_power_off,
+};
+
+static const struct of_device_id mmc_pwrseq_sd8787_of_match[] = {
+ { .compatible = "mmc-pwrseq-sd8787",},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, mmc_pwrseq_sd8787_of_match);
+
+static int mmc_pwrseq_sd8787_probe(struct platform_device *pdev)
+{
+ struct mmc_pwrseq_sd8787 *pwrseq;
+ struct device *dev = &pdev->dev;
+
+ pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
+ if (!pwrseq)
+ return -ENOMEM;
+
+ pwrseq->pwrdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_LOW);
+ if (IS_ERR(pwrseq->pwrdn_gpio))
+ return PTR_ERR(pwrseq->pwrdn_gpio);
+
+ pwrseq->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(pwrseq->reset_gpio))
+ return PTR_ERR(pwrseq->reset_gpio);
+
+ pwrseq->pwrseq.dev = dev;
+ pwrseq->pwrseq.ops = &mmc_pwrseq_sd8787_ops;
+ pwrseq->pwrseq.owner = THIS_MODULE;
+ platform_set_drvdata(pdev, pwrseq);
+
+ return mmc_pwrseq_register(&pwrseq->pwrseq);
+}
+
+static int mmc_pwrseq_sd8787_remove(struct platform_device *pdev)
+{
+ struct mmc_pwrseq_sd8787 *pwrseq = platform_get_drvdata(pdev);
+
+ mmc_pwrseq_unregister(&pwrseq->pwrseq);
+
+ return 0;
+}
+
+static struct platform_driver mmc_pwrseq_sd8787_driver = {
+ .probe = mmc_pwrseq_sd8787_probe,
+ .remove = mmc_pwrseq_sd8787_remove,
+ .driver = {
+ .name = "pwrseq_sd8787",
+ .of_match_table = mmc_pwrseq_sd8787_of_match,
+ },
+};
+
+module_platform_driver(mmc_pwrseq_sd8787_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 033f641..493eb10 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -20,6 +20,8 @@
#include "queue.h"
#include "block.h"
+#include "core.h"
+#include "card.h"
#define MMC_QUEUE_BOUNCESZ 65536
@@ -75,8 +77,8 @@ static int mmc_queue_thread(void *d)
set_current_state(TASK_RUNNING);
mmc_blk_issue_rq(mq, req);
cond_resched();
- if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
- mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
+ if (mq->new_request) {
+ mq->new_request = false;
continue; /* fetch again */
}
@@ -143,7 +145,7 @@ static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
{
struct scatterlist *sg;
- sg = kmalloc(sizeof(struct scatterlist)*sg_len, GFP_KERNEL);
+ sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
if (!sg)
*err = -ENOMEM;
else {
@@ -390,8 +392,8 @@ void mmc_queue_suspend(struct mmc_queue *mq)
struct request_queue *q = mq->queue;
unsigned long flags;
- if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
- mq->flags |= MMC_QUEUE_SUSPENDED;
+ if (!mq->suspended) {
+ mq->suspended |= true;
spin_lock_irqsave(q->queue_lock, flags);
blk_stop_queue(q);
@@ -410,8 +412,8 @@ void mmc_queue_resume(struct mmc_queue *mq)
struct request_queue *q = mq->queue;
unsigned long flags;
- if (mq->flags & MMC_QUEUE_SUSPENDED) {
- mq->flags &= ~MMC_QUEUE_SUSPENDED;
+ if (mq->suspended) {
+ mq->suspended = false;
up(&mq->thread_sem);
diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h
index dac8c3d..e298f10 100644
--- a/drivers/mmc/core/queue.h
+++ b/drivers/mmc/core/queue.h
@@ -1,6 +1,11 @@
#ifndef MMC_QUEUE_H
#define MMC_QUEUE_H
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+
static inline bool mmc_req_is_special(struct request *req)
{
return req &&
@@ -9,7 +14,6 @@ static inline bool mmc_req_is_special(struct request *req)
req_op(req) == REQ_OP_SECURE_ERASE);
}
-struct request;
struct task_struct;
struct mmc_blk_data;
@@ -29,16 +33,15 @@ struct mmc_queue_req {
char *bounce_buf;
struct scatterlist *bounce_sg;
unsigned int bounce_sg_len;
- struct mmc_async_req mmc_active;
+ struct mmc_async_req areq;
};
struct mmc_queue {
struct mmc_card *card;
struct task_struct *thread;
struct semaphore thread_sem;
- unsigned int flags;
-#define MMC_QUEUE_SUSPENDED (1 << 0)
-#define MMC_QUEUE_NEW_REQUEST (1 << 1)
+ bool new_request;
+ bool suspended;
bool asleep;
struct mmc_blk_data *blkdata;
struct request_queue *queue;
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
deleted file mode 100644
index ca9cade..0000000
--- a/drivers/mmc/core/quirks.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * This file contains work-arounds for many known SD/MMC
- * and SDIO hardware bugs.
- *
- * Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com>
- * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
- * Inspired from pci fixup code:
- * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
- *
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/export.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/sdio_ids.h>
-
-#ifndef SDIO_VENDOR_ID_TI
-#define SDIO_VENDOR_ID_TI 0x0097
-#endif
-
-#ifndef SDIO_DEVICE_ID_TI_WL1271
-#define SDIO_DEVICE_ID_TI_WL1271 0x4076
-#endif
-
-#ifndef SDIO_VENDOR_ID_STE
-#define SDIO_VENDOR_ID_STE 0x0020
-#endif
-
-#ifndef SDIO_DEVICE_ID_STE_CW1200
-#define SDIO_DEVICE_ID_STE_CW1200 0x2280
-#endif
-
-#ifndef SDIO_DEVICE_ID_MARVELL_8797_F0
-#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
-#endif
-
-static const struct mmc_fixup mmc_fixup_methods[] = {
- SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
- add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
-
- SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
- add_quirk, MMC_QUIRK_DISABLE_CD),
-
- SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
- add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
-
- SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
- add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
-
- END_FIXUP
-};
-
-void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
-{
- const struct mmc_fixup *f;
- u64 rev = cid_rev_card(card);
-
- /* Non-core specific workarounds. */
- if (!table)
- table = mmc_fixup_methods;
-
- for (f = table; f->vendor_fixup; f++) {
- if ((f->manfid == CID_MANFID_ANY ||
- f->manfid == card->cid.manfid) &&
- (f->oemid == CID_OEMID_ANY ||
- f->oemid == card->cid.oemid) &&
- (f->name == CID_NAME_ANY ||
- !strncmp(f->name, card->cid.prod_name,
- sizeof(card->cid.prod_name))) &&
- (f->cis_vendor == card->cis.vendor ||
- f->cis_vendor == (u16) SDIO_ANY_ID) &&
- (f->cis_device == card->cis.device ||
- f->cis_device == (u16) SDIO_ANY_ID) &&
- (f->ext_csd_rev == EXT_CSD_REV_ANY ||
- f->ext_csd_rev == card->ext_csd.rev) &&
- rev >= f->rev_start && rev <= f->rev_end) {
- dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
- f->vendor_fixup(card, f->data);
- }
- }
-}
-EXPORT_SYMBOL(mmc_fixup_device);
diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h
new file mode 100644
index 0000000..fb72593
--- /dev/null
+++ b/drivers/mmc/core/quirks.h
@@ -0,0 +1,148 @@
+/*
+ * This file contains work-arounds for many known SD/MMC
+ * and SDIO hardware bugs.
+ *
+ * Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com>
+ * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
+ * Inspired from pci fixup code:
+ * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ *
+ */
+
+#include <linux/mmc/sdio_ids.h>
+
+#include "card.h"
+
+static const struct mmc_fixup mmc_blk_fixups[] = {
+#define INAND_CMD38_ARG_EXT_CSD 113
+#define INAND_CMD38_ARG_ERASE 0x00
+#define INAND_CMD38_ARG_TRIM 0x01
+#define INAND_CMD38_ARG_SECERASE 0x80
+#define INAND_CMD38_ARG_SECTRIM1 0x81
+#define INAND_CMD38_ARG_SECTRIM2 0x88
+ /* CMD38 argument is passed through EXT_CSD[113] */
+ MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
+ MMC_QUIRK_INAND_CMD38),
+ MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
+ MMC_QUIRK_INAND_CMD38),
+ MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
+ MMC_QUIRK_INAND_CMD38),
+ MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
+ MMC_QUIRK_INAND_CMD38),
+ MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
+ MMC_QUIRK_INAND_CMD38),
+
+ /*
+ * Some MMC cards experience performance degradation with CMD23
+ * instead of CMD12-bounded multiblock transfers. For now we'll
+ * black list what's bad...
+ * - Certain Toshiba cards.
+ *
+ * N.B. This doesn't affect SD cards.
+ */
+ MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BLK_NO_CMD23),
+ MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BLK_NO_CMD23),
+ MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BLK_NO_CMD23),
+ MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BLK_NO_CMD23),
+ MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BLK_NO_CMD23),
+
+ /*
+ * Some MMC cards need longer data read timeout than indicated in CSD.
+ */
+ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
+ MMC_QUIRK_LONG_READ_TIME),
+ MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_LONG_READ_TIME),
+
+ /*
+ * On these Samsung MoviNAND parts, performing secure erase or
+ * secure trim can result in unrecoverable corruption due to a
+ * firmware bug.
+ */
+ MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+
+ /*
+ * On Some Kingston eMMCs, performing trim can result in
+ * unrecoverable data conrruption occasionally due to a firmware bug.
+ */
+ MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_TRIM_BROKEN),
+ MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_TRIM_BROKEN),
+
+ END_FIXUP
+};
+
+static const struct mmc_fixup mmc_ext_csd_fixups[] = {
+ /*
+ * Certain Hynix eMMC 4.41 cards might get broken when HPI feature
+ * is used so disable the HPI feature for such buggy cards.
+ */
+ MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
+ 0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
+
+ END_FIXUP
+};
+
+static const struct mmc_fixup sdio_fixup_methods[] = {
+ SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
+ add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
+ add_quirk, MMC_QUIRK_DISABLE_CD),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
+ add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
+ add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
+
+ END_FIXUP
+};
+
+static inline void mmc_fixup_device(struct mmc_card *card,
+ const struct mmc_fixup *table)
+{
+ const struct mmc_fixup *f;
+ u64 rev = cid_rev_card(card);
+
+ for (f = table; f->vendor_fixup; f++) {
+ if ((f->manfid == CID_MANFID_ANY ||
+ f->manfid == card->cid.manfid) &&
+ (f->oemid == CID_OEMID_ANY ||
+ f->oemid == card->cid.oemid) &&
+ (f->name == CID_NAME_ANY ||
+ !strncmp(f->name, card->cid.prod_name,
+ sizeof(card->cid.prod_name))) &&
+ (f->cis_vendor == card->cis.vendor ||
+ f->cis_vendor == (u16) SDIO_ANY_ID) &&
+ (f->cis_device == card->cis.device ||
+ f->cis_device == (u16) SDIO_ANY_ID) &&
+ (f->ext_csd_rev == EXT_CSD_REV_ANY ||
+ f->ext_csd_rev == card->ext_csd.rev) &&
+ rev >= f->rev_start && rev <= f->rev_end) {
+ dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
+ f->vendor_fixup(card, f->data);
+ }
+ }
+}
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index a614f37..89531b4 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -22,6 +22,8 @@
#include <linux/mmc/sd.h>
#include "core.h"
+#include "card.h"
+#include "host.h"
#include "bus.h"
#include "mmc_ops.h"
#include "sd.h"
@@ -786,8 +788,7 @@ try_again:
*/
if (!mmc_host_is_spi(host) && rocr &&
((*rocr & 0x41000000) == 0x41000000)) {
- err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
- pocr);
+ err = mmc_set_uhs_voltage(host, pocr);
if (err == -EAGAIN) {
retries--;
goto try_again;
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
index aab824a..1ada980 100644
--- a/drivers/mmc/core/sd.h
+++ b/drivers/mmc/core/sd.h
@@ -1,10 +1,13 @@
#ifndef _MMC_CORE_SD_H
#define _MMC_CORE_SD_H
-#include <linux/mmc/card.h>
+#include <linux/types.h>
extern struct device_type sd_type;
+struct mmc_host;
+struct mmc_card;
+
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
void mmc_decode_cid(struct mmc_card *card);
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index de125a4..9d5824a 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -25,7 +25,7 @@
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
if (WARN_ON(card && card->host != host))
return -EINVAL;
@@ -68,7 +68,7 @@ EXPORT_SYMBOL_GPL(mmc_app_cmd);
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries)
{
- struct mmc_request mrq = {NULL};
+ struct mmc_request mrq = {};
int i, err;
@@ -120,7 +120,7 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd);
int mmc_app_set_bus_width(struct mmc_card *card, int width)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = SD_APP_SET_BUS_WIDTH;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
@@ -141,7 +141,7 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int i, err = 0;
cmd.opcode = SD_APP_OP_COND;
@@ -185,7 +185,7 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err;
static const u8 test_pattern = 0xAA;
u8 result_pattern;
@@ -217,7 +217,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
@@ -235,9 +235,9 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
{
int err;
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
void *data_buf;
@@ -290,9 +290,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp)
{
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
/* NOTE: caller guarantees resp is heap-allocated */
@@ -332,9 +332,9 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
{
int err;
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg;
/* NOTE: caller guarantees ssr is heap-allocated */
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index ffc2305..784f8e6 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -12,6 +12,12 @@
#ifndef _MMC_SD_OPS_H
#define _MMC_SD_OPS_H
+#include <linux/types.h>
+
+struct mmc_card;
+struct mmc_host;
+struct mmc_command;
+
int mmc_app_set_bus_width(struct mmc_card *card, int width);
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
@@ -20,6 +26,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
+int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
+int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
+ struct mmc_command *cmd, int retries);
#endif
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index ecbc529..fae732c 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -20,7 +20,10 @@
#include <linux/mmc/sdio_ids.h>
#include "core.h"
+#include "card.h"
+#include "host.h"
#include "bus.h"
+#include "quirks.h"
#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
@@ -541,6 +544,15 @@ out:
return err;
}
+static void mmc_sdio_resend_if_cond(struct mmc_host *host,
+ struct mmc_card *card)
+{
+ sdio_reset(host);
+ mmc_go_idle(host);
+ mmc_send_if_cond(host, host->ocr_avail);
+ mmc_remove_card(card);
+}
+
/*
* Handle the detection and initialisation of a card.
*
@@ -624,24 +636,21 @@ try_again:
* to switch to 1.8V signaling level. No 1.8v signalling if
* UHS mode is not enabled to maintain compatibility and some
* systems that claim 1.8v signalling in fact do not support
- * it.
+ * it. Per SDIO spec v3, section 3.1.2, if the voltage is already
+ * 1.8v, the card sets S18A to 0 in the R4 response. So it will
+ * fails to check rocr & R4_18V_PRESENT, but we still need to
+ * try to init uhs card. sdio_read_cccr will take over this task
+ * to make sure which speed mode should work.
*/
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
- err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
- ocr_card);
+ err = mmc_set_uhs_voltage(host, ocr_card);
if (err == -EAGAIN) {
- sdio_reset(host);
- mmc_go_idle(host);
- mmc_send_if_cond(host, host->ocr_avail);
- mmc_remove_card(card);
+ mmc_sdio_resend_if_cond(host, card);
retries--;
goto try_again;
} else if (err) {
ocr &= ~R4_18V_PRESENT;
}
- err = 0;
- } else {
- ocr &= ~R4_18V_PRESENT;
}
/*
@@ -698,11 +707,20 @@ try_again:
}
/*
- * Read the common registers.
+ * Read the common registers. Note that we should try to
+ * validate whether UHS would work or not.
*/
err = sdio_read_cccr(card, ocr);
- if (err)
- goto remove;
+ if (err) {
+ mmc_sdio_resend_if_cond(host, card);
+ if (ocr & R4_18V_PRESENT) {
+ /* Retry init sequence, but without R4_18V_PRESENT. */
+ retries = 0;
+ goto try_again;
+ } else {
+ goto remove;
+ }
+ }
/*
* Read the common CIS tuples.
@@ -721,7 +739,7 @@ try_again:
card = oldcard;
}
card->ocr = ocr_card;
- mmc_fixup_device(card, NULL);
+ mmc_fixup_device(card, sdio_fixup_methods);
if (card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_setup_card(host, card, oldcard != NULL);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 86f5b32..e992a7f 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include "core.h"
+#include "card.h"
#include "sdio_cis.h"
#include "sdio_bus.h"
diff --git a/drivers/mmc/core/sdio_bus.h b/drivers/mmc/core/sdio_bus.h
index 567a768..b69a254 100644
--- a/drivers/mmc/core/sdio_bus.h
+++ b/drivers/mmc/core/sdio_bus.h
@@ -11,6 +11,9 @@
#ifndef _MMC_CORE_SDIO_BUS_H
#define _MMC_CORE_SDIO_BUS_H
+struct mmc_card;
+struct sdio_func;
+
struct sdio_func *sdio_alloc_func(struct mmc_card *card);
int sdio_add_func(struct sdio_func *func);
void sdio_remove_func(struct sdio_func *func);
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h
index 4d903c2..16aa563 100644
--- a/drivers/mmc/core/sdio_cis.h
+++ b/drivers/mmc/core/sdio_cis.h
@@ -14,6 +14,9 @@
#ifndef _MMC_SDIO_CIS_H
#define _MMC_SDIO_CIS_H
+struct mmc_card;
+struct sdio_func;
+
int sdio_read_common_cis(struct mmc_card *card);
void sdio_free_common_cis(struct mmc_card *card);
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 406e5f0..74195d7 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -16,6 +16,8 @@
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
+#include "core.h"
+#include "card.h"
/**
* sdio_claim_host - exclusively claim a bus for a certain SDIO function
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index f1faf9a..d29faf2 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -27,6 +27,8 @@
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
+#include "core.h"
+#include "card.h"
static int process_sdio_pending_irqs(struct mmc_host *host)
{
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
index 90fe554..3c0d3ab 100644
--- a/drivers/mmc/core/sdio_ops.c
+++ b/drivers/mmc/core/sdio_ops.c
@@ -21,7 +21,7 @@
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int i, err = 0;
cmd.opcode = SD_IO_SEND_OP_COND;
@@ -66,7 +66,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
unsigned addr, u8 in, u8 *out)
{
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
int err;
if (fn > 7)
@@ -118,9 +118,9 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_data data = {0};
+ struct mmc_request mrq = {};
+ struct mmc_command cmd = {};
+ struct mmc_data data = {};
struct scatterlist sg, *sg_ptr;
struct sg_table sgtable;
unsigned int nents, left_size, i;
diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h
index 5660c7f..bed8a83 100644
--- a/drivers/mmc/core/sdio_ops.h
+++ b/drivers/mmc/core/sdio_ops.h
@@ -12,14 +12,19 @@
#ifndef _MMC_SDIO_OPS_H
#define _MMC_SDIO_OPS_H
+#include <linux/types.h>
#include <linux/mmc/sdio.h>
+struct mmc_host;
+struct mmc_card;
+
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out);
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
int sdio_reset(struct mmc_host *host);
+unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz);
static inline bool mmc_is_io_op(u32 opcode)
{
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index babe591..a8450a8 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -235,9 +235,6 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
struct gpio_desc *desc;
int ret;
- if (!con_id)
- con_id = ctx->cd_label;
-
desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -289,9 +286,6 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
struct gpio_desc *desc;
int ret;
- if (!con_id)
- con_id = ctx->ro_label;
-
desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
if (IS_ERR(desc))
return PTR_ERR(desc);
diff --git a/drivers/mmc/core/slot-gpio.h b/drivers/mmc/core/slot-gpio.h
index 8c1854d..a06fd84 100644
--- a/drivers/mmc/core/slot-gpio.h
+++ b/drivers/mmc/core/slot-gpio.h
@@ -8,6 +8,8 @@
#ifndef _MMC_CORE_SLOTGPIO_H
#define _MMC_CORE_SLOTGPIO_H
+struct mmc_host;
+
int mmc_gpio_alloc(struct mmc_host *host);
#endif
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2eb9701..f08691a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -683,6 +683,15 @@ 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 HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index ccc9c4c..6d548c4 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.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
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 36b5af8..1e2600d 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -36,6 +36,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/mmc/slot-gpio.h>
+#include <linux/interrupt.h>
#include <linux/platform_data/mmc-davinci.h>
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index e133528..25691cc 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -13,7 +13,6 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
-#include <linux/mmc/dw_mmc.h>
#include <linux/mmc/mmc.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index 9821e6b..e38fb00 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -11,7 +11,6 @@
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/mmc/host.h>
-#include <linux/mmc/dw_mmc.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index ab82796..ab87132 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
-#include <linux/mmc/dw_mmc.h>
#include "dw_mmc.h"
#define PCI_BAR_NO 2
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 1236d49b..58c13e2 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -20,7 +20,6 @@
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
-#include <linux/mmc/dw_mmc.h>
#include <linux/of.h>
#include <linux/clk.h>
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 9a46e46..372fb6e 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -11,7 +11,6 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
-#include <linux/mmc/dw_mmc.h>
#include <linux/of_address.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/pm_runtime.h>
diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
new file mode 100644
index 0000000..d38e94a
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.c
@@ -0,0 +1,241 @@
+/*
+ * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2016, Linaro Ltd.
+ * Copyright (C) 2016, ZTE Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#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;
+ int ret;
+
+ /* 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)) {
+ ret = PTR_ERR(sysc_base);
+ if (ret != -EPROBE_DEFER)
+ dev_err(host->dev, "Can't get syscon: %d\n",
+ ret);
+ return ret;
+ }
+ } 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,
+ .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",
+ .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
new file mode 100644
index 0000000..f369997
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.h
@@ -0,0 +1,31 @@
+#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 73db085..a9ac0b4 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -32,7 +32,6 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
-#include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
@@ -1113,11 +1112,15 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
mci_writel(host, CTRL, temp);
/*
- * Use the initial fifoth_val for PIO mode.
+ * Use the initial fifoth_val for PIO mode. If wm_algined
+ * is set, we set watermark same as data size.
* If next issued data may be transfered by DMA mode,
* prev_blksz should be invalidated.
*/
- mci_writel(host, FIFOTH, host->fifoth_val);
+ if (host->wm_aligned)
+ dw_mci_adjust_fifoth(host, data);
+ else
+ mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
} else {
/*
@@ -1179,11 +1182,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
if ((clock != slot->__clk_old &&
!test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) ||
force_clkinit) {
- dev_info(&slot->mmc->class_dev,
- "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
- slot->id, host->bus_hz, clock,
- div ? ((host->bus_hz / div) >> 1) :
- host->bus_hz, div);
+ /* Silent the verbose log if calling from PM context */
+ if (!force_clkinit)
+ dev_info(&slot->mmc->class_dev,
+ "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
+ slot->id, host->bus_hz, clock,
+ div ? ((host->bus_hz / div) >> 1) :
+ host->bus_hz, div);
/*
* If card is polling, display the message only
@@ -2977,6 +2982,11 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
+ of_property_read_u32(np, "data-addr", &host->data_addr_override);
+
+ if (of_get_property(np, "fifo-watermark-aligned", NULL))
+ host->wm_aligned = true;
+
if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
pdata->bus_hz = clock_frequency;
@@ -3180,7 +3190,9 @@ int dw_mci_probe(struct dw_mci *host)
host->verid = SDMMC_GET_VERID(mci_readl(host, VERID));
dev_info(host->dev, "Version ID is %04x\n", host->verid);
- if (host->verid < DW_MMC_240A)
+ if (host->data_addr_override)
+ host->fifo_reg = host->regs + host->data_addr_override;
+ else if (host->verid < DW_MMC_240A)
host->fifo_reg = host->regs + DATA_OFFSET;
else
host->fifo_reg = host->regs + DATA_240A_OFFSET;
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index c594658..ce34736 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -14,6 +14,269 @@
#ifndef _DW_MMC_H_
#define _DW_MMC_H_
+#include <linux/scatterlist.h>
+#include <linux/mmc/core.h>
+#include <linux/dmaengine.h>
+#include <linux/reset.h>
+#include <linux/interrupt.h>
+
+#define MAX_MCI_SLOTS 2
+
+enum dw_mci_state {
+ STATE_IDLE = 0,
+ STATE_SENDING_CMD,
+ STATE_SENDING_DATA,
+ STATE_DATA_BUSY,
+ STATE_SENDING_STOP,
+ STATE_DATA_ERROR,
+ STATE_SENDING_CMD11,
+ STATE_WAITING_CMD11_DONE,
+};
+
+enum {
+ EVENT_CMD_COMPLETE = 0,
+ EVENT_XFER_COMPLETE,
+ EVENT_DATA_COMPLETE,
+ EVENT_DATA_ERROR,
+};
+
+enum dw_mci_cookie {
+ COOKIE_UNMAPPED,
+ COOKIE_PRE_MAPPED, /* mapped by pre_req() of dwmmc */
+ COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */
+};
+
+struct mmc_data;
+
+enum {
+ TRANS_MODE_PIO = 0,
+ TRANS_MODE_IDMAC,
+ TRANS_MODE_EDMAC
+};
+
+struct dw_mci_dma_slave {
+ struct dma_chan *ch;
+ enum dma_transfer_direction direction;
+};
+
+/**
+ * struct dw_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @irq_lock: Spinlock protecting the INTMASK setting.
+ * @regs: Pointer to MMIO registers.
+ * @fifo_reg: Pointer to MMIO registers for data FIFO
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @sg_miter: PIO mapping scatterlist iterator.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ * or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ * transfer is in progress.
+ * @stop_abort: The command currently prepared for stoping transfer.
+ * @prev_blksz: The former transfer blksz record.
+ * @timing: Record of current ios timing.
+ * @use_dma: Whether DMA channel is initialized or not.
+ * @using_dma: Whether DMA is in use for the current transfer.
+ * @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
+ * @sg_dma: Bus address of DMA buffer.
+ * @sg_cpu: Virtual address of DMA buffer.
+ * @dma_ops: Pointer to platform-specific DMA callbacks.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ * @ring_size: Buffer size for idma descriptors.
+ * command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @dms: structure of slave-dma private data.
+ * @phy_regs: physical address of controller's register map
+ * @data_status: Snapshot of SR taken upon completion of the current
+ * data transfer. Only valid when EVENT_DATA_COMPLETE or
+ * EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ * to be sent.
+ * @dir_status: Direction of current transfer.
+ * @tasklet: Tasklet running the request state machine.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ * to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ * processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ * rate and timeout calculations.
+ * @current_speed: Configured rate of the controller.
+ * @num_slots: Number of slots available.
+ * @fifoth_val: The value of FIFOTH register.
+ * @verid: Denote Version ID.
+ * @dev: Device associated with the MMC controller.
+ * @pdata: Platform data associated with the MMC controller.
+ * @drv_data: Driver specific data for identified variant of the controller
+ * @priv: Implementation defined private data.
+ * @biu_clk: Pointer to bus interface unit clock instance.
+ * @ciu_clk: Pointer to card interface unit clock instance.
+ * @slot: Slots sharing this MMC controller.
+ * @fifo_depth: depth of FIFO.
+ * @data_addr_override: override fifo reg offset with this value.
+ * @wm_aligned: force fifo watermark equal with data length in PIO mode.
+ * Set as true if alignment is needed.
+ * @data_shift: log2 of FIFO item size.
+ * @part_buf_start: Start index in part_buf.
+ * @part_buf_count: Bytes of partial data in part_buf.
+ * @part_buf: Simple buffer for partial fifo reads/writes.
+ * @push_data: Pointer to FIFO push function.
+ * @pull_data: Pointer to FIFO pull function.
+ * @vqmmc_enabled: Status of vqmmc, should be true or false.
+ * @irq_flags: The flags to be passed to request_irq.
+ * @irq: The irq value to be passed to request_irq.
+ * @sdio_id0: Number of slot0 in the SDIO interrupt registers.
+ * @cmd11_timer: Timer for SD3.0 voltage switch over scheme.
+ * @dto_timer: Timer for broken data transfer over scheme.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * @irq_lock is an irq-safe spinlock protecting the INTMASK register
+ * to allow the interrupt handler to modify it directly. Held for only long
+ * enough to read-modify-write INTMASK and no other locks are grabbed when
+ * holding this one.
+ *
+ * The @mrq field of struct dw_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interrupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
+struct dw_mci {
+ spinlock_t lock;
+ spinlock_t irq_lock;
+ void __iomem *regs;
+ void __iomem *fifo_reg;
+ u32 data_addr_override;
+ bool wm_aligned;
+
+ struct scatterlist *sg;
+ struct sg_mapping_iter sg_miter;
+
+ struct dw_mci_slot *cur_slot;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_command stop_abort;
+ unsigned int prev_blksz;
+ unsigned char timing;
+
+ /* DMA interface members*/
+ int use_dma;
+ int using_dma;
+ int dma_64bit_address;
+
+ dma_addr_t sg_dma;
+ void *sg_cpu;
+ const struct dw_mci_dma_ops *dma_ops;
+ /* For idmac */
+ unsigned int ring_size;
+
+ /* For edmac */
+ struct dw_mci_dma_slave *dms;
+ /* Registers's physical base address */
+ resource_size_t phy_regs;
+
+ u32 cmd_status;
+ u32 data_status;
+ u32 stop_cmdr;
+ u32 dir_status;
+ struct tasklet_struct tasklet;
+ unsigned long pending_events;
+ unsigned long completed_events;
+ enum dw_mci_state state;
+ struct list_head queue;
+
+ u32 bus_hz;
+ u32 current_speed;
+ u32 num_slots;
+ u32 fifoth_val;
+ u16 verid;
+ struct device *dev;
+ struct dw_mci_board *pdata;
+ const struct dw_mci_drv_data *drv_data;
+ void *priv;
+ struct clk *biu_clk;
+ struct clk *ciu_clk;
+ struct dw_mci_slot *slot[MAX_MCI_SLOTS];
+
+ /* FIFO push and pull */
+ int fifo_depth;
+ int data_shift;
+ u8 part_buf_start;
+ u8 part_buf_count;
+ union {
+ u16 part_buf16;
+ u32 part_buf32;
+ u64 part_buf;
+ };
+ void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+ void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+ bool vqmmc_enabled;
+ unsigned long irq_flags; /* IRQ flags */
+ int irq;
+
+ int sdio_id0;
+
+ struct timer_list cmd11_timer;
+ struct timer_list dto_timer;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+ /* DMA Ops */
+ int (*init)(struct dw_mci *host);
+ int (*start)(struct dw_mci *host, unsigned int sg_len);
+ void (*complete)(void *host);
+ void (*stop)(struct dw_mci *host);
+ void (*cleanup)(struct dw_mci *host);
+ void (*exit)(struct dw_mci *host);
+};
+
+struct dma_pdata;
+
+/* Board platform data */
+struct dw_mci_board {
+ u32 num_slots;
+
+ unsigned int bus_hz; /* Clock speed at the cclk_in pad */
+
+ u32 caps; /* Capabilities */
+ u32 caps2; /* More capabilities */
+ u32 pm_caps; /* PM capabilities */
+ /*
+ * Override fifo depth. If 0, autodetect it from the FIFOTH register,
+ * but note that this may not be reliable after a bootloader has used
+ * it.
+ */
+ unsigned int fifo_depth;
+
+ /* delay in mS before detecting cards after interrupt */
+ u32 detect_delay_ms;
+
+ struct reset_control *rstc;
+ struct dw_mci_dma_ops *dma_ops;
+ struct dma_pdata *data;
+};
+
#define DW_MMC_240A 0x240a
#define DW_MMC_280A 0x280a
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index 0973935..5a95978 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -35,6 +35,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
#define DRIVER_NAME "meson-gx-mmc"
@@ -82,6 +83,7 @@
#define CFG_RC_CC_MASK 0xf
#define CFG_STOP_CLOCK BIT(22)
#define CFG_CLK_ALWAYS_ON BIT(18)
+#define CFG_CHK_DS BIT(20)
#define CFG_AUTO_CLK BIT(23)
#define SD_EMMC_STATUS 0x48
@@ -131,7 +133,7 @@ struct meson_host {
struct clk_mux mux;
struct clk *mux_clk;
struct clk *mux_parent[MUX_CLK_NUM_PARENTS];
- unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS];
+ unsigned long current_clock;
struct clk_divider cfg_div;
struct clk *cfg_div_clk;
@@ -178,7 +180,7 @@ struct sd_emmc_desc {
static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
{
struct mmc_host *mmc = host->mmc;
- int ret = 0;
+ int ret;
u32 cfg;
if (clk_rate) {
@@ -188,7 +190,7 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
clk_rate = mmc->f_min;
}
- if (clk_rate == mmc->actual_clock)
+ if (clk_rate == host->current_clock)
return 0;
/* stop clock */
@@ -201,29 +203,34 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
dev_dbg(host->dev, "change clock rate %u -> %lu\n",
mmc->actual_clock, clk_rate);
- if (clk_rate == 0) {
+ if (!clk_rate) {
mmc->actual_clock = 0;
+ host->current_clock = 0;
+ /* return with clock being stopped */
return 0;
}
ret = clk_set_rate(host->cfg_div_clk, clk_rate);
- if (ret)
- dev_warn(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
- clk_rate, ret);
- else if (clk_rate && clk_rate != clk_get_rate(host->cfg_div_clk))
- dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n",
- clk_rate, clk_get_rate(host->cfg_div_clk), ret);
- else
- mmc->actual_clock = clk_rate;
-
- /* (re)start clock, if non-zero */
- if (!ret && clk_rate) {
- cfg = readl(host->regs + SD_EMMC_CFG);
- cfg &= ~CFG_STOP_CLOCK;
- writel(cfg, host->regs + SD_EMMC_CFG);
+ if (ret) {
+ dev_err(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
+ clk_rate, ret);
+ return ret;
}
- return ret;
+ mmc->actual_clock = clk_get_rate(host->cfg_div_clk);
+ host->current_clock = clk_rate;
+
+ if (clk_rate != mmc->actual_clock)
+ dev_dbg(host->dev,
+ "divider requested rate %lu != actual rate %u\n",
+ clk_rate, mmc->actual_clock);
+
+ /* (re)start clock */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ cfg &= ~CFG_STOP_CLOCK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+
+ return 0;
}
/*
@@ -239,7 +246,6 @@ static int meson_mmc_clk_init(struct meson_host *host)
const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
unsigned int mux_parent_count = 0;
const char *clk_div_parents[1];
- unsigned int f_min = UINT_MAX;
u32 clk_reg, cfg;
/* get the mux parents */
@@ -256,20 +262,10 @@ static int meson_mmc_clk_init(struct meson_host *host)
return ret;
}
- host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]);
mux_parent_names[i] = __clk_get_name(host->mux_parent[i]);
mux_parent_count++;
- if (host->mux_parent_rate[i] < f_min)
- f_min = host->mux_parent_rate[i];
}
- /* cacluate f_min based on input clocks, and max divider value */
- if (f_min != UINT_MAX)
- f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX);
- else
- f_min = 4000000; /* default min: 400 MHz */
- host->mmc->f_min = f_min;
-
/* create the mux */
snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev));
init.name = clk_name;
@@ -324,9 +320,13 @@ static int meson_mmc_clk_init(struct meson_host *host)
writel(cfg, host->regs + SD_EMMC_CFG);
ret = clk_prepare_enable(host->cfg_div_clk);
- if (!ret)
- ret = meson_mmc_clk_set(host, f_min);
+ if (ret)
+ return ret;
+ /* Get the nearest minimum clock to 400KHz */
+ host->mmc->f_min = clk_round_rate(host->cfg_div_clk, 400000);
+
+ ret = meson_mmc_clk_set(host, host->mmc->f_min);
if (!ret)
clk_disable_unprepare(host->cfg_div_clk);
@@ -378,7 +378,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
meson_mmc_clk_set(host, ios->clock);
/* Bus width */
- val = readl(host->regs + SD_EMMC_CFG);
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
bus_width = CFG_BUS_WIDTH_1;
@@ -393,7 +392,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_err(host->dev, "Invalid ios->bus_width: %u. Setting to 4.\n",
ios->bus_width);
bus_width = CFG_BUS_WIDTH_4;
- return;
}
val = readl(host->regs + SD_EMMC_CFG);
@@ -411,6 +409,16 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
val &= ~(CFG_RC_CC_MASK << CFG_RC_CC_SHIFT);
val |= ilog2(SD_EMMC_CFG_CMD_GAP) << CFG_RC_CC_SHIFT;
+ val &= ~CFG_DDR;
+ if (ios->timing == MMC_TIMING_UHS_DDR50 ||
+ ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_MMC_HS400)
+ val |= CFG_DDR;
+
+ val &= ~CFG_CHK_DS;
+ if (ios->timing == MMC_TIMING_MMC_HS400)
+ val |= CFG_CHK_DS;
+
writel(val, host->regs + SD_EMMC_CFG);
if (val != orig)
@@ -480,9 +488,9 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
blk_len >>= CFG_BLK_LEN_SHIFT;
if (blk_len != ilog2(cmd->data->blksz)) {
- dev_warn(host->dev, "%s: update blk_len %d -> %d\n",
+ dev_dbg(host->dev, "%s: update blk_len %d -> %d\n",
__func__, blk_len,
- ilog2(cmd->data->blksz));
+ ilog2(cmd->data->blksz));
blk_len = ilog2(cmd->data->blksz);
cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
cfg |= blk_len << CFG_BLK_LEN_SHIFT;
@@ -545,11 +553,6 @@ static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
/* Stop execution */
writel(0, host->regs + SD_EMMC_START);
- /* clear, ack, enable all interrupts */
- writel(0, host->regs + SD_EMMC_IRQ_EN);
- writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
- writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
-
host->mrq = mrq;
if (mrq->sbc)
@@ -669,7 +672,6 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
struct mmc_command *cmd = host->cmd;
struct mmc_data *data;
unsigned int xfer_bytes;
- int ret = IRQ_HANDLED;
if (WARN_ON(!mrq))
return IRQ_NONE;
@@ -678,14 +680,12 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
return IRQ_NONE;
data = cmd->data;
- if (data) {
+ if (data && data->flags & MMC_DATA_READ) {
xfer_bytes = data->blksz * data->blocks;
- if (data->flags & MMC_DATA_READ) {
- WARN_ON(xfer_bytes > host->bounce_buf_size);
- sg_copy_from_buffer(data->sg, data->sg_len,
- host->bounce_buf, xfer_bytes);
- data->bytes_xfered = xfer_bytes;
- }
+ WARN_ON(xfer_bytes > host->bounce_buf_size);
+ sg_copy_from_buffer(data->sg, data->sg_len,
+ host->bounce_buf, xfer_bytes);
+ data->bytes_xfered = xfer_bytes;
}
meson_mmc_read_resp(host->mmc, cmd);
@@ -694,7 +694,7 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
else
meson_mmc_start_cmd(host->mmc, data->stop);
- return ret;
+ return IRQ_HANDLED;
}
/*
@@ -742,7 +742,8 @@ static int meson_mmc_probe(struct platform_device *pdev)
ret = mmc_of_parse(mmc);
if (ret) {
- dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
+ if (ret != -EPROBE_DEFER)
+ dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
goto free_host;
}
@@ -780,6 +781,7 @@ static int meson_mmc_probe(struct platform_device *pdev)
/* clear, ack, enable all interrupts */
writel(0, host->regs + SD_EMMC_IRQ_EN);
writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
ret = devm_request_threaded_irq(&pdev->dev, host->irq,
meson_mmc_irq, meson_mmc_irq_thread,
@@ -787,8 +789,11 @@ static int meson_mmc_probe(struct platform_device *pdev)
if (ret)
goto free_host;
+ mmc->max_blk_count = CMD_CFG_LENGTH_MASK;
+ mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
+
/* data bounce buffer */
- host->bounce_buf_size = SZ_512K;
+ host->bounce_buf_size = mmc->max_req_size;
host->bounce_buf =
dma_alloc_coherent(host->dev, host->bounce_buf_size,
&host->bounce_dma_addr, GFP_KERNEL);
@@ -814,12 +819,11 @@ static int meson_mmc_remove(struct platform_device *pdev)
{
struct meson_host *host = dev_get_drvdata(&pdev->dev);
- if (WARN_ON(!host))
- return 0;
+ /* disable interrupts */
+ writel(0, host->regs + SD_EMMC_IRQ_EN);
- if (host->bounce_buf)
- dma_free_coherent(host->dev, host->bounce_buf_size,
- host->bounce_buf, host->bounce_dma_addr);
+ dma_free_coherent(host->dev, host->bounce_buf_size,
+ host->bounce_buf, host->bounce_dma_addr);
clk_disable_unprepare(host->cfg_div_clk);
clk_disable_unprepare(host->core_clk);
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b597244..0c6420b 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -507,6 +507,7 @@ static void mmci_dma_data_error(struct mmci_host *host)
{
dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
dmaengine_terminate_all(host->dma_current);
+ host->dma_in_progress = false;
host->dma_current = NULL;
host->dma_desc_current = NULL;
host->data->host_cookie = 0;
@@ -565,6 +566,7 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
mmci_dma_release(host);
}
+ host->dma_in_progress = false;
host->dma_current = NULL;
host->dma_desc_current = NULL;
}
@@ -665,6 +667,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
dev_vdbg(mmc_dev(host->mmc),
"Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
data->sg_len, data->blksz, data->blocks, data->flags);
+ host->dma_in_progress = true;
dmaengine_submit(host->dma_desc_current);
dma_async_issue_pending(host->dma_current);
@@ -740,8 +743,10 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
if (host->dma_desc_current == next->dma_desc)
host->dma_desc_current = NULL;
- if (host->dma_current == next->dma_chan)
+ if (host->dma_current == next->dma_chan) {
+ host->dma_in_progress = false;
host->dma_current = NULL;
+ }
next->dma_desc = NULL;
next->dma_chan = NULL;
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 56322c6..4a8bef1 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -245,8 +245,9 @@ struct mmci_host {
struct dma_chan *dma_tx_channel;
struct dma_async_tx_descriptor *dma_desc_current;
struct mmci_host_next next_data;
+ bool dma_in_progress;
-#define dma_inprogress(host) ((host)->dma_current)
+#define dma_inprogress(host) ((host)->dma_in_progress)
#else
#define dma_inprogress(host) (0)
#endif
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 10ef2ae..8e32580 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -28,6 +28,7 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/interrupt.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
@@ -1074,11 +1075,8 @@ static int msdc_card_busy(struct mmc_host *mmc)
struct msdc_host *host = mmc_priv(mmc);
u32 status = readl(host->base + MSDC_PS);
- /* check if any pin between dat[0:3] is low */
- if (((status >> 16) & 0xf) != 0xf)
- return 1;
-
- return 0;
+ /* only check if data0 is low */
+ return !(status & BIT(16));
}
static void msdc_request_timeout(struct work_struct *work)
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index c8b8ac6..add1e70 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -153,7 +153,11 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
}
}
- if (data) {
+ if (cmd == mrq->sbc) {
+ /* Finished CMD23, now send actual command. */
+ mxs_mmc_start_cmd(host, mrq->cmd);
+ return;
+ } else if (data) {
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, ssp->dma_dir);
/*
@@ -166,7 +170,7 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
data->bytes_xfered = 0;
host->data = NULL;
- if (mrq->stop) {
+ if (data->stop && (data->error || !mrq->sbc)) {
mxs_mmc_start_cmd(host, mrq->stop);
return;
}
@@ -495,7 +499,11 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(host->mrq != NULL);
host->mrq = mrq;
- mxs_mmc_start_cmd(host, mrq->cmd);
+
+ if (mrq->sbc)
+ mxs_mmc_start_cmd(host, mrq->sbc);
+ else
+ mxs_mmc_start_cmd(host, mrq->cmd);
}
static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -642,7 +650,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
/* set mmc core parameters */
mmc->ops = &mxs_mmc_ops;
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
- MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
+ MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
host->broken_cd = of_property_read_bool(np, "broken-cd");
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index be3c49f..bd49f34 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -893,7 +893,7 @@ static void mmc_omap_cover_handler(unsigned long param)
* If no card is inserted, we postpone polling until
* the cover has been closed.
*/
- if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
+ if (slot->mmc->card == NULL)
return;
mod_timer(&slot->cover_timer,
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index ad11c4c..a58bd65 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1162,7 +1162,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
if (status & ERR_EN) {
omap_hsmmc_dbg_report_irq(host, status);
- if (status & (CTO_EN | CCRC_EN))
+ if (status & (CTO_EN | CCRC_EN | CEB_EN))
end_cmd = 1;
if (host->data || host->response_busy) {
end_trans = !end_cmd;
@@ -1469,10 +1469,11 @@ static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,
}
static void set_data_timeout(struct omap_hsmmc_host *host,
- unsigned int timeout_ns,
+ unsigned long long timeout_ns,
unsigned int timeout_clks)
{
- unsigned int timeout, cycle_ns;
+ unsigned long long timeout = timeout_ns;
+ unsigned int cycle_ns;
uint32_t reg, clkd, dto = 0;
reg = OMAP_HSMMC_READ(host->base, SYSCTL);
@@ -1481,7 +1482,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
clkd = 1;
cycle_ns = 1000000000 / (host->clk_rate / clkd);
- timeout = timeout_ns / cycle_ns;
+ do_div(timeout, cycle_ns);
timeout += timeout_clks;
if (timeout) {
while ((timeout & 0x80000000) == 0) {
@@ -1527,16 +1528,24 @@ static int
omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
{
int ret;
+ unsigned long long timeout;
+
host->data = req->data;
if (req->data == NULL) {
OMAP_HSMMC_WRITE(host->base, BLK, 0);
- /*
- * Set an arbitrary 100ms data timeout for commands with
- * busy signal.
- */
- if (req->cmd->flags & MMC_RSP_BUSY)
- set_data_timeout(host, 100000000U, 0);
+ if (req->cmd->flags & MMC_RSP_BUSY) {
+ timeout = req->cmd->busy_timeout * NSEC_PER_MSEC;
+
+ /*
+ * Set an arbitrary 100ms data timeout for commands with
+ * busy signal and no indication of busy_timeout.
+ */
+ if (!timeout)
+ timeout = 100000000U;
+
+ set_data_timeout(host, timeout, 0);
+ }
return 0;
}
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index ecb99a8..41b5771 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -707,7 +707,7 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
u8 opcode, u8 sample_point)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
err = sd_change_phase(host, sample_point, true);
if (err < 0)
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index dc1abd1..12d2fbe 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -682,7 +682,7 @@ static int sd_tuning_rx_cmd(struct rtsx_usb_sdmmc *host,
u8 opcode, u8 sample_point)
{
int err;
- struct mmc_command cmd = {0};
+ struct mmc_command cmd = {};
err = sd_change_phase(host, sample_point, 0);
if (err)
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 932a4b1..7a173f8 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -21,6 +21,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 278a5a4..9dcb704 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -467,7 +467,10 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
- if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) {
+ err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL);
+ if (err) {
+ if (err == -EPROBE_DEFER)
+ goto err_free;
dev_warn(dev, "failed to setup card detect gpio\n");
c->use_runtime_pm = false;
}
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index 4b0ecb9..316cfec 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -17,6 +17,7 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
#include "sdhci-pltfm.h"
@@ -25,7 +26,7 @@
#define SDHCI_CDNS_HRS04_ACK BIT(26)
#define SDHCI_CDNS_HRS04_RD BIT(25)
#define SDHCI_CDNS_HRS04_WR BIT(24)
-#define SDHCI_CDNS_HRS04_RDATA_SHIFT 12
+#define SDHCI_CDNS_HRS04_RDATA_SHIFT 16
#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index de132e2..ece8b37 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -24,30 +24,36 @@
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
SDHCI_QUIRK_NO_HISPD_BIT)
-#define ESDHC_PROCTL 0x28
-
-#define ESDHC_SYSTEM_CONTROL 0x2c
-#define ESDHC_CLOCK_MASK 0x0000fff0
-#define ESDHC_PREDIV_SHIFT 8
-#define ESDHC_DIVIDER_SHIFT 4
-#define ESDHC_CLOCK_PEREN 0x00000004
-#define ESDHC_CLOCK_HCKEN 0x00000002
-#define ESDHC_CLOCK_IPGEN 0x00000001
-
/* pltfm-specific */
#define ESDHC_HOST_CONTROL_LE 0x20
/*
- * P2020 interpretation of the SDHCI_HOST_CONTROL register
+ * eSDHC register definition
*/
-#define ESDHC_CTRL_4BITBUS (0x1 << 1)
-#define ESDHC_CTRL_8BITBUS (0x2 << 1)
-#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
-
-/* OF-specific */
-#define ESDHC_DMA_SYSCTL 0x40c
-#define ESDHC_DMA_SNOOP 0x00000040
-#define ESDHC_HOST_CONTROL_RES 0x01
+/* Present State Register */
+#define ESDHC_PRSSTAT 0x24
+#define ESDHC_CLOCK_STABLE 0x00000008
+
+/* Protocol Control Register */
+#define ESDHC_PROCTL 0x28
+#define ESDHC_CTRL_4BITBUS (0x1 << 1)
+#define ESDHC_CTRL_8BITBUS (0x2 << 1)
+#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
+#define ESDHC_HOST_CONTROL_RES 0x01
+
+/* System Control Register */
+#define ESDHC_SYSTEM_CONTROL 0x2c
+#define ESDHC_CLOCK_MASK 0x0000fff0
+#define ESDHC_PREDIV_SHIFT 8
+#define ESDHC_DIVIDER_SHIFT 4
+#define ESDHC_CLOCK_SDCLKEN 0x00000008
+#define ESDHC_CLOCK_PEREN 0x00000004
+#define ESDHC_CLOCK_HCKEN 0x00000002
+#define ESDHC_CLOCK_IPGEN 0x00000001
+
+/* Control Register for DMA transfer */
+#define ESDHC_DMA_SYSCTL 0x40c
+#define ESDHC_DMA_SNOOP 0x00000040
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index d7046d6..3275d49 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -211,14 +211,19 @@ static const struct sdhci_iproc_data iproc_data = {
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
- SDHCI_QUIRK_MISSING_CAPS,
+ SDHCI_QUIRK_MISSING_CAPS |
+ SDHCI_QUIRK_NO_HISPD_BIT,
.ops = &sdhci_iproc_32only_ops,
};
static const struct sdhci_iproc_data bcm2835_data = {
.pdata = &sdhci_bcm2835_pltfm_data,
- .caps = SDHCI_CAN_VDD_330,
- .caps1 = 0x00000000,
+ .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
+ & SDHCI_MAX_BLOCK_MASK) |
+ SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_DO_HISPD,
+ .caps1 = SDHCI_DRIVER_TYPE_A |
+ SDHCI_DRIVER_TYPE_C,
.mmc_caps = 0x00000000,
};
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 32879b8..10cdc84 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -69,6 +69,7 @@
#define CORE_DLL_CLOCK_DISABLE BIT(21)
#define CORE_VENDOR_SPEC 0x10c
+#define CORE_VENDOR_SPEC_POR_VAL 0xa1c
#define CORE_CLK_PWRSAVE BIT(1)
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
@@ -102,6 +103,7 @@
#define CORE_DDR_200_CFG 0x184
#define CORE_CDC_T4_DLY_SEL BIT(0)
+#define CORE_CMDIN_RCLK_EN BIT(1)
#define CORE_START_CDC_TRAFFIC BIT(6)
#define CORE_VENDOR_SPEC3 0x1b0
#define CORE_PWRSAVE_DLL BIT(3)
@@ -138,6 +140,46 @@ struct sdhci_msm_host {
bool use_cdclp533;
};
+static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
+ unsigned int clock)
+{
+ struct mmc_ios ios = host->mmc->ios;
+ /*
+ * The SDHC requires internal clock frequency to be double the
+ * actual clock that will be set for DDR mode. The controller
+ * uses the faster clock(100/400MHz) for some of its parts and
+ * send the actual required clock (50/200MHz) to the card.
+ */
+ if (ios.timing == MMC_TIMING_UHS_DDR50 ||
+ ios.timing == MMC_TIMING_MMC_DDR52 ||
+ ios.timing == MMC_TIMING_MMC_HS400 ||
+ host->flags & SDHCI_HS400_TUNING)
+ clock *= 2;
+ return clock;
+}
+
+static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
+ unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_ios curr_ios = host->mmc->ios;
+ int rc;
+
+ clock = msm_get_clock_rate_for_bus_mode(host, clock);
+ rc = clk_set_rate(msm_host->clk, clock);
+ if (rc) {
+ pr_err("%s: Failed to set clock at rate %u at timing %d\n",
+ mmc_hostname(host->mmc), clock,
+ curr_ios.timing);
+ return;
+ }
+ msm_host->clk_rate = clock;
+ pr_debug("%s: Setting clock at rate %lu at timing %d\n",
+ mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
+ curr_ios.timing);
+}
+
/* Platform specific tuning */
static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
{
@@ -464,6 +506,122 @@ static int msm_init_cm_dll(struct sdhci_host *host)
return 0;
}
+static void msm_hc_select_default(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ u32 config;
+
+ if (!msm_host->use_cdclp533) {
+ config = readl_relaxed(host->ioaddr +
+ CORE_VENDOR_SPEC3);
+ config &= ~CORE_PWRSAVE_DLL;
+ writel_relaxed(config, host->ioaddr +
+ CORE_VENDOR_SPEC3);
+ }
+
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ config &= ~CORE_HC_MCLK_SEL_MASK;
+ config |= CORE_HC_MCLK_SEL_DFLT;
+ writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+
+ /*
+ * Disable HC_SELECT_IN to be able to use the UHS mode select
+ * configuration from Host Control2 register for all other
+ * modes.
+ * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
+ * in VENDOR_SPEC_FUNC
+ */
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ config &= ~CORE_HC_SELECT_IN_EN;
+ config &= ~CORE_HC_SELECT_IN_MASK;
+ writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+
+ /*
+ * Make sure above writes impacting free running MCLK are completed
+ * before changing the clk_rate at GCC.
+ */
+ wmb();
+}
+
+static void msm_hc_select_hs400(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_ios ios = host->mmc->ios;
+ u32 config, dll_lock;
+ int rc;
+
+ /* Select the divided clock (free running MCLK/2) */
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ config &= ~CORE_HC_MCLK_SEL_MASK;
+ config |= CORE_HC_MCLK_SEL_HS400;
+
+ writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+ /*
+ * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
+ * register
+ */
+ if ((msm_host->tuning_done || ios.enhanced_strobe) &&
+ !msm_host->calibration_done) {
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+ config |= CORE_HC_SELECT_IN_HS400;
+ config |= CORE_HC_SELECT_IN_EN;
+ writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+ }
+ if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
+ /*
+ * Poll on DLL_LOCK or DDR_DLL_LOCK bits in
+ * CORE_DLL_STATUS to be set. This should get set
+ * within 15 us at 200 MHz.
+ */
+ rc = readl_relaxed_poll_timeout(host->ioaddr +
+ CORE_DLL_STATUS,
+ dll_lock,
+ (dll_lock &
+ (CORE_DLL_LOCK |
+ CORE_DDR_DLL_LOCK)), 10,
+ 1000);
+ if (rc == -ETIMEDOUT)
+ pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
+ mmc_hostname(host->mmc), dll_lock);
+ }
+ /*
+ * Make sure above writes impacting free running MCLK are completed
+ * before changing the clk_rate at GCC.
+ */
+ wmb();
+}
+
+/*
+ * sdhci_msm_hc_select_mode :- In general all timing modes are
+ * controlled via UHS mode select in Host Control2 register.
+ * eMMC specific HS200/HS400 doesn't have their respective modes
+ * defined here, hence we use these values.
+ *
+ * HS200 - SDR104 (Since they both are equivalent in functionality)
+ * HS400 - This involves multiple configurations
+ * Initially SDR104 - when tuning is required as HS200
+ * Then when switching to DDR @ 400MHz (HS400) we use
+ * the vendor specific HC_SELECT_IN to control the mode.
+ *
+ * In addition to controlling the modes we also need to select the
+ * correct input clock for DLL depending on the mode.
+ *
+ * HS400 - divided clock (free running MCLK/2)
+ * All other modes - default (free running MCLK)
+ */
+void sdhci_msm_hc_select_mode(struct sdhci_host *host)
+{
+ struct mmc_ios ios = host->mmc->ios;
+
+ if (ios.timing == MMC_TIMING_MMC_HS400 ||
+ host->flags & SDHCI_HS400_TUNING)
+ msm_hc_select_hs400(host);
+ else
+ msm_hc_select_default(host);
+}
+
static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -506,19 +664,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
config &= ~CORE_START_CDC_TRAFFIC;
writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
- /*
- * Perform CDC Register Initialization Sequence
- *
- * CORE_CSR_CDC_CTLR_CFG0 0x11800EC
- * CORE_CSR_CDC_CTLR_CFG1 0x3011111
- * CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000
- * CORE_CSR_CDC_CAL_TIMER_CFG1 0x4
- * CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020
- * CORE_CSR_CDC_COARSE_CAL_CFG 0xB19
- * CORE_CSR_CDC_DELAY_CFG 0x3AC
- * CORE_CDC_OFFSET_CFG 0x0
- * CORE_CDC_SLAVE_DDA_CFG 0x16334
- */
+ /* Perform CDC Register Initialization Sequence */
writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1);
@@ -526,7 +672,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
- writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
+ writel_relaxed(0x4E2, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
@@ -579,6 +725,7 @@ out:
static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
{
+ struct mmc_host *mmc = host->mmc;
u32 dll_status, config;
int ret;
@@ -593,6 +740,12 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
*/
writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG);
+ if (mmc->ios.enhanced_strobe) {
+ config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
+ config |= CORE_CMDIN_RCLK_EN;
+ writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
+ }
+
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
config |= CORE_DDR_CAL_EN;
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
@@ -627,6 +780,7 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_host *mmc = host->mmc;
int ret;
u32 config;
@@ -640,14 +794,17 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
if (ret)
goto out;
- /* Set the selected phase in delay line hw block */
- ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
- if (ret)
- goto out;
+ if (!mmc->ios.enhanced_strobe) {
+ /* Set the selected phase in delay line hw block */
+ ret = msm_config_cm_dll_phase(host,
+ msm_host->saved_tuning_phase);
+ if (ret)
+ goto out;
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config |= CORE_CMD_DAT_TRACK_SEL;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ }
- config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
- config |= CORE_CMD_DAT_TRACK_SEL;
- writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
if (msm_host->use_cdclp533)
ret = sdhci_msm_cdclp533_calibration(host);
else
@@ -658,12 +815,12 @@ out:
return ret;
}
-static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
+ struct sdhci_host *host = mmc_priv(mmc);
int tuning_seq_cnt = 3;
u8 phase, tuned_phases[16], tuned_phase_cnt = 0;
int rc;
- struct mmc_host *mmc = host->mmc;
struct mmc_ios ios = host->mmc->ios;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
@@ -678,6 +835,17 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
ios.timing == MMC_TIMING_UHS_SDR104))
return 0;
+ /*
+ * For HS400 tuning in HS200 timing requires:
+ * - select MCLK/2 in VENDOR_SPEC
+ * - program MCLK to 400MHz (or nearest supported) in GCC
+ */
+ if (host->flags & SDHCI_HS400_TUNING) {
+ sdhci_msm_hc_select_mode(host);
+ msm_set_clock_rate_for_bus_mode(host, ios.clock);
+ host->flags &= ~SDHCI_HS400_TUNING;
+ }
+
retry:
/* First of all reset the tuning block */
rc = msm_init_cm_dll(host);
@@ -732,6 +900,30 @@ retry:
return rc;
}
+/*
+ * sdhci_msm_hs400 - Calibrate the DLL for HS400 bus speed mode operation.
+ * This needs to be done for both tuning and enhanced_strobe mode.
+ * DLL operation is only needed for clock > 100MHz. For clock <= 100MHz
+ * fixed feedback clock is used.
+ */
+static void sdhci_msm_hs400(struct sdhci_host *host, struct mmc_ios *ios)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ if (host->clock > CORE_FREQ_100MHZ &&
+ (msm_host->tuning_done || ios->enhanced_strobe) &&
+ !msm_host->calibration_done) {
+ ret = sdhci_msm_hs400_dll_calibration(host);
+ if (!ret)
+ msm_host->calibration_done = true;
+ else
+ pr_err("%s: Failed to calibrate DLL for hs400 mode (%d)\n",
+ mmc_hostname(host->mmc), ret);
+ }
+}
+
static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
unsigned int uhs)
{
@@ -800,12 +992,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
spin_unlock_irq(&host->lock);
- /* CDCLP533 HW calibration is only required for HS400 mode*/
- if (host->clock > CORE_FREQ_100MHZ &&
- msm_host->tuning_done && !msm_host->calibration_done &&
- mmc->ios.timing == MMC_TIMING_MMC_HS400)
- if (!sdhci_msm_hs400_dll_calibration(host))
- msm_host->calibration_done = true;
+
+ if (mmc->ios.timing == MMC_TIMING_MMC_HS400)
+ sdhci_msm_hs400(host, &mmc->ios);
+
spin_lock_irq(&host->lock);
}
@@ -893,9 +1083,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
- struct mmc_ios curr_ios = host->mmc->ios;
- u32 config, dll_lock;
- int rc;
if (!clock) {
msm_host->clk_rate = clock;
@@ -903,117 +1090,11 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
}
spin_unlock_irq(&host->lock);
- /*
- * The SDHC requires internal clock frequency to be double the
- * actual clock that will be set for DDR mode. The controller
- * uses the faster clock(100/400MHz) for some of its parts and
- * send the actual required clock (50/200MHz) to the card.
- */
- if (curr_ios.timing == MMC_TIMING_UHS_DDR50 ||
- curr_ios.timing == MMC_TIMING_MMC_DDR52 ||
- curr_ios.timing == MMC_TIMING_MMC_HS400)
- clock *= 2;
- /*
- * In general all timing modes are controlled via UHS mode select in
- * Host Control2 register. eMMC specific HS200/HS400 doesn't have
- * their respective modes defined here, hence we use these values.
- *
- * HS200 - SDR104 (Since they both are equivalent in functionality)
- * HS400 - This involves multiple configurations
- * Initially SDR104 - when tuning is required as HS200
- * Then when switching to DDR @ 400MHz (HS400) we use
- * the vendor specific HC_SELECT_IN to control the mode.
- *
- * In addition to controlling the modes we also need to select the
- * correct input clock for DLL depending on the mode.
- *
- * HS400 - divided clock (free running MCLK/2)
- * All other modes - default (free running MCLK)
- */
- if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
- /* Select the divided clock (free running MCLK/2) */
- config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
- config &= ~CORE_HC_MCLK_SEL_MASK;
- config |= CORE_HC_MCLK_SEL_HS400;
- writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
- /*
- * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
- * register
- */
- if (msm_host->tuning_done && !msm_host->calibration_done) {
- /*
- * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
- * field in VENDOR_SPEC_FUNC
- */
- config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
- config |= CORE_HC_SELECT_IN_HS400;
- config |= CORE_HC_SELECT_IN_EN;
- writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
- }
- if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
- /*
- * Poll on DLL_LOCK or DDR_DLL_LOCK bits in
- * CORE_DLL_STATUS to be set. This should get set
- * within 15 us at 200 MHz.
- */
- rc = readl_relaxed_poll_timeout(host->ioaddr +
- CORE_DLL_STATUS,
- dll_lock,
- (dll_lock &
- (CORE_DLL_LOCK |
- CORE_DDR_DLL_LOCK)), 10,
- 1000);
- if (rc == -ETIMEDOUT)
- pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
- mmc_hostname(host->mmc), dll_lock);
- }
- } else {
- if (!msm_host->use_cdclp533) {
- config = readl_relaxed(host->ioaddr +
- CORE_VENDOR_SPEC3);
- config &= ~CORE_PWRSAVE_DLL;
- writel_relaxed(config, host->ioaddr +
- CORE_VENDOR_SPEC3);
- }
+ sdhci_msm_hc_select_mode(host);
- config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
- config &= ~CORE_HC_MCLK_SEL_MASK;
- config |= CORE_HC_MCLK_SEL_DFLT;
- writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+ msm_set_clock_rate_for_bus_mode(host, clock);
- /*
- * Disable HC_SELECT_IN to be able to use the UHS mode select
- * configuration from Host Control2 register for all other
- * modes.
- * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
- * in VENDOR_SPEC_FUNC
- */
- config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
- config &= ~CORE_HC_SELECT_IN_EN;
- config &= ~CORE_HC_SELECT_IN_MASK;
- writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
- }
-
- /*
- * Make sure above writes impacting free running MCLK are completed
- * before changing the clk_rate at GCC.
- */
- wmb();
-
- rc = clk_set_rate(msm_host->clk, clock);
- if (rc) {
- pr_err("%s: Failed to set clock at rate %u at timing %d\n",
- mmc_hostname(host->mmc), clock,
- curr_ios.timing);
- goto out_lock;
- }
- msm_host->clk_rate = clock;
- pr_debug("%s: Setting clock at rate %lu at timing %d\n",
- mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
- curr_ios.timing);
-
-out_lock:
spin_lock_irq(&host->lock);
out:
__sdhci_msm_set_clock(host, clock);
@@ -1027,7 +1108,6 @@ static const struct of_device_id sdhci_msm_dt_match[] = {
MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
static const struct sdhci_ops sdhci_msm_ops = {
- .platform_execute_tuning = sdhci_msm_execute_tuning,
.reset = sdhci_reset,
.set_clock = sdhci_msm_set_clock,
.get_min_clock = sdhci_msm_get_min_clock,
@@ -1134,17 +1214,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto clk_disable;
}
- config = readl_relaxed(msm_host->core_mem + CORE_POWER);
- config |= CORE_SW_RST;
- writel_relaxed(config, msm_host->core_mem + CORE_POWER);
-
- /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
- usleep_range(1000, 5000);
- if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
- dev_err(&pdev->dev, "Stuck in reset\n");
- ret = -ETIMEDOUT;
- goto clk_disable;
- }
+ /* Reset the vendor spec register to power on reset state */
+ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
+ host->ioaddr + CORE_VENDOR_SPEC);
/* Set HC_MODE_EN bit in HC_MODE register */
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
@@ -1210,6 +1282,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
MSM_MMC_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev);
+ host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning;
ret = sdhci_add_host(host);
if (ret)
goto pm_runtime_disable;
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 9a6eb44..d3aa671 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -431,6 +431,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
int pre_div = 1;
int div = 1;
+ u32 timeout;
u32 temp;
host->mmc->actual_clock = 0;
@@ -451,8 +452,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
}
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
- temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
- | ESDHC_CLOCK_MASK);
+ temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
+ ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
@@ -472,7 +473,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
| (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
- mdelay(1);
+
+ /* Wait max 20 ms */
+ timeout = 20;
+ while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
+ if (timeout == 0) {
+ pr_err("%s: Internal clock never stabilised.\n",
+ mmc_hostname(host->mmc));
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ temp |= ESDHC_CLOCK_SDCLKEN;
+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
}
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
@@ -569,16 +584,19 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = {
};
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
- .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
- | SDHCI_QUIRK_NO_CARD_NO_RESET
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .quirks = ESDHC_DEFAULT_QUIRKS |
+#ifdef CONFIG_PPC
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+#endif
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.ops = &sdhci_esdhc_be_ops,
};
static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = {
- .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
- | SDHCI_QUIRK_NO_CARD_NO_RESET
- | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+ .quirks = ESDHC_DEFAULT_QUIRKS |
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.ops = &sdhci_esdhc_le_ops,
};
@@ -643,8 +661,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
of_device_is_compatible(np, "fsl,p5020-esdhc") ||
of_device_is_compatible(np, "fsl,p4080-esdhc") ||
of_device_is_compatible(np, "fsl,p1020-esdhc") ||
- of_device_is_compatible(np, "fsl,t1040-esdhc") ||
- of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
+ of_device_is_compatible(np, "fsl,t1040-esdhc"))
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
if (of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 1a72d32..982b3e3 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -424,7 +424,6 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
- slot->cd_con_id = NULL;
slot->cd_idx = 0;
slot->cd_override_level = true;
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXT_SD ||
@@ -866,6 +865,86 @@ enum amd_chipset_gen {
AMD_CHIPSET_UNKNOWN,
};
+/* AMD registers */
+#define AMD_SD_AUTO_PATTERN 0xB8
+#define AMD_MSLEEP_DURATION 4
+#define AMD_SD_MISC_CONTROL 0xD0
+#define AMD_MAX_TUNE_VALUE 0x0B
+#define AMD_AUTO_TUNE_SEL 0x10800
+#define AMD_FIFO_PTR 0x30
+#define AMD_BIT_MASK 0x1F
+
+static void amd_tuning_reset(struct sdhci_host *host)
+{
+ unsigned int val;
+
+ val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ val |= SDHCI_CTRL_PRESET_VAL_ENABLE | SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+
+ val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ val &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+}
+
+static void amd_config_tuning_phase(struct pci_dev *pdev, u8 phase)
+{
+ unsigned int val;
+
+ pci_read_config_dword(pdev, AMD_SD_AUTO_PATTERN, &val);
+ val &= ~AMD_BIT_MASK;
+ val |= (AMD_AUTO_TUNE_SEL | (phase << 1));
+ pci_write_config_dword(pdev, AMD_SD_AUTO_PATTERN, val);
+}
+
+static void amd_enable_manual_tuning(struct pci_dev *pdev)
+{
+ unsigned int val;
+
+ pci_read_config_dword(pdev, AMD_SD_MISC_CONTROL, &val);
+ val |= AMD_FIFO_PTR;
+ pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val);
+}
+
+static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u8 valid_win = 0;
+ u8 valid_win_max = 0;
+ u8 valid_win_end = 0;
+ u8 ctrl, tune_around;
+
+ amd_tuning_reset(host);
+
+ for (tune_around = 0; tune_around < 12; tune_around++) {
+ amd_config_tuning_phase(pdev, tune_around);
+
+ if (mmc_send_tuning(host->mmc, opcode, NULL)) {
+ valid_win = 0;
+ msleep(AMD_MSLEEP_DURATION);
+ ctrl = SDHCI_RESET_CMD | SDHCI_RESET_DATA;
+ sdhci_writeb(host, ctrl, SDHCI_SOFTWARE_RESET);
+ } else if (++valid_win > valid_win_max) {
+ valid_win_max = valid_win;
+ valid_win_end = tune_around;
+ }
+ }
+
+ if (!valid_win_max) {
+ dev_err(&pdev->dev, "no tuning point found\n");
+ return -EIO;
+ }
+
+ amd_config_tuning_phase(pdev, valid_win_end - valid_win_max / 2);
+
+ amd_enable_manual_tuning(pdev);
+
+ host->mmc->retune_period = 0;
+
+ return 0;
+}
+
static int amd_probe(struct sdhci_pci_chip *chip)
{
struct pci_dev *smbus_dev;
@@ -888,16 +967,24 @@ static int amd_probe(struct sdhci_pci_chip *chip)
}
}
- if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
+ if (gen == AMD_CHIPSET_BEFORE_ML || gen == AMD_CHIPSET_CZ)
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
- chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
- }
return 0;
}
+static const struct sdhci_ops amd_sdhci_pci_ops = {
+ .set_clock = sdhci_set_clock,
+ .enable_dma = sdhci_pci_enable_dma,
+ .set_bus_width = sdhci_pci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .platform_execute_tuning = amd_execute_tuning,
+};
+
static const struct sdhci_pci_fixes sdhci_amd = {
.probe = amd_probe,
+ .ops = &amd_sdhci_pci_ops,
};
static const struct pci_device_id pci_ids[] = {
@@ -1817,7 +1904,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
if (slot->cd_idx >= 0) {
- ret = mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx,
+ ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx,
slot->cd_override_level, 0, NULL);
if (ret == -EPROBE_DEFER)
goto remove;
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 4abdaed..36f7434 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -81,7 +81,6 @@ struct sdhci_pci_slot {
int cd_gpio;
int cd_irq;
- char *cd_con_id;
int cd_idx;
bool cd_override_level;
diff --git a/drivers/mmc/host/sdhci-s3c-regs.h b/drivers/mmc/host/sdhci-s3c-regs.h
deleted file mode 100644
index e34049ad..0000000
--- a/drivers/mmc/host/sdhci-s3c-regs.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/* linux/arch/arm/plat-s3c/include/plat/regs-sdhci.h
- *
- * Copyright 2008 Openmoko, Inc.
- * Copyright 2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C Platform - SDHCI (HSMMC) register definitions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef __PLAT_S3C_SDHCI_REGS_H
-#define __PLAT_S3C_SDHCI_REGS_H __FILE__
-
-#define S3C_SDHCI_CONTROL2 (0x80)
-#define S3C_SDHCI_CONTROL3 (0x84)
-#define S3C64XX_SDHCI_CONTROL4 (0x8C)
-
-#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
-#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
-#define S3C_SDHCI_CTRL2_CDINVRXD3 (1 << 29)
-#define S3C_SDHCI_CTRL2_SLCARDOUT (1 << 28)
-
-#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
-#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
-#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
-
-#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
-#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16)
-#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
-
-#define S3C_SDHCI_CTRL2_ENFBCLKTX (1 << 15)
-#define S3C_SDHCI_CTRL2_ENFBCLKRX (1 << 14)
-#define S3C_SDHCI_CTRL2_SDCDSEL (1 << 13)
-#define S3C_SDHCI_CTRL2_SDSIGPC (1 << 12)
-#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11)
-
-#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9)
-#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9)
-#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9)
-#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9)
-#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9)
-#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9)
-
-#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8)
-#define S3C_SDHCI_CTRL2_RWAITMODE (1 << 7)
-#define S3C_SDHCI_CTRL2_DISBUFRD (1 << 6)
-#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4)
-#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4)
-#define S3C_SDHCI_CTRL2_PWRSYNC (1 << 3)
-#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1)
-#define S3C_SDHCI_CTRL2_HWINITFIN (1 << 0)
-
-#define S3C_SDHCI_CTRL3_FCSEL3 (1 << 31)
-#define S3C_SDHCI_CTRL3_FCSEL2 (1 << 23)
-#define S3C_SDHCI_CTRL3_FCSEL1 (1 << 15)
-#define S3C_SDHCI_CTRL3_FCSEL0 (1 << 7)
-
-#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24)
-#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24)
-#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24)
-
-#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16)
-#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16)
-#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16)
-
-#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8)
-#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8)
-#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8)
-
-#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0)
-#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0)
-#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0)
-
-#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16)
-#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16)
-#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16)
-#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16)
-#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16)
-#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16)
-
-#define S3C64XX_SDHCI_CONTROL4_BUSY (1)
-
-#endif /* __PLAT_S3C_SDHCI_REGS_H */
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index de219ca..3e5c83d 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -29,11 +29,80 @@
#include <linux/mmc/host.h>
-#include "sdhci-s3c-regs.h"
#include "sdhci.h"
#define MAX_BUS_CLK (4)
+#define S3C_SDHCI_CONTROL2 (0x80)
+#define S3C_SDHCI_CONTROL3 (0x84)
+#define S3C64XX_SDHCI_CONTROL4 (0x8C)
+
+#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR BIT(31)
+#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK BIT(30)
+#define S3C_SDHCI_CTRL2_CDINVRXD3 BIT(29)
+#define S3C_SDHCI_CTRL2_SLCARDOUT BIT(28)
+
+#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
+#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
+#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
+
+#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
+#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16)
+#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
+
+#define S3C_SDHCI_CTRL2_ENFBCLKTX BIT(15)
+#define S3C_SDHCI_CTRL2_ENFBCLKRX BIT(14)
+#define S3C_SDHCI_CTRL2_SDCDSEL BIT(13)
+#define S3C_SDHCI_CTRL2_SDSIGPC BIT(12)
+#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART BIT(11)
+
+#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9)
+#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9)
+
+#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD BIT(8)
+#define S3C_SDHCI_CTRL2_RWAITMODE BIT(7)
+#define S3C_SDHCI_CTRL2_DISBUFRD BIT(6)
+
+#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4)
+#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4)
+#define S3C_SDHCI_CTRL2_PWRSYNC BIT(3)
+#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON BIT(1)
+#define S3C_SDHCI_CTRL2_HWINITFIN BIT(0)
+
+#define S3C_SDHCI_CTRL3_FCSEL3 BIT(31)
+#define S3C_SDHCI_CTRL3_FCSEL2 BIT(23)
+#define S3C_SDHCI_CTRL3_FCSEL1 BIT(15)
+#define S3C_SDHCI_CTRL3_FCSEL0 BIT(7)
+
+#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24)
+#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24)
+#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24)
+
+#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16)
+#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16)
+#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16)
+
+#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8)
+#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8)
+#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8)
+
+#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0)
+#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0)
+#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0)
+
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16)
+
+#define S3C64XX_SDHCI_CONTROL4_BUSY (1)
+
/**
* struct sdhci_s3c - S3C SDHCI instance
* @host: The SDHCI host created
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0def995..6fdd7a7 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2021,8 +2021,8 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode,
unsigned long flags)
{
struct mmc_host *mmc = host->mmc;
- struct mmc_command cmd = {0};
- struct mmc_request mrq = {NULL};
+ struct mmc_command cmd = {};
+ struct mmc_request mrq = {};
cmd.opcode = opcode;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -2114,7 +2114,6 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
spin_lock_irqsave(&host->lock, flags);
hs400_tuning = host->flags & SDHCI_HS400_TUNING;
- host->flags &= ~SDHCI_HS400_TUNING;
if (host->tuning_mode == SDHCI_TUNING_MODE_1)
tuning_count = host->tuning_count;
@@ -2156,7 +2155,9 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->ops->platform_execute_tuning) {
spin_unlock_irqrestore(&host->lock, flags);
- return host->ops->platform_execute_tuning(host, opcode);
+ err = host->ops->platform_execute_tuning(host, opcode);
+ spin_lock_irqsave(&host->lock, flags);
+ goto out_unlock;
}
host->mmc->retune_period = tuning_count;
@@ -2167,6 +2168,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
sdhci_end_tuning(host);
out_unlock:
+ host->flags &= ~SDHCI_HS400_TUNING;
spin_unlock_irqrestore(&host->lock, flags);
return err;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 0b66f21..edf3adf 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -17,6 +17,8 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/interrupt.h>
#include <linux/mmc/host.h>
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 9007784..4062d6b 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -1079,26 +1079,10 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->state = STATE_IDLE;
}
-static int sh_mmcif_get_cd(struct mmc_host *mmc)
-{
- struct sh_mmcif_host *host = mmc_priv(mmc);
- struct device *dev = sh_mmcif_host_to_dev(host);
- struct sh_mmcif_plat_data *p = dev->platform_data;
- int ret = mmc_gpio_get_cd(mmc);
-
- if (ret >= 0)
- return ret;
-
- if (!p || !p->get_cd)
- return -ENOSYS;
- else
- return p->get_cd(host->pd);
-}
-
static struct mmc_host_ops sh_mmcif_ops = {
.request = sh_mmcif_request,
.set_ios = sh_mmcif_set_ios,
- .get_cd = sh_mmcif_get_cd,
+ .get_cd = mmc_gpio_get_cd,
};
static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
@@ -1443,8 +1427,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
host->mmc = mmc;
host->addr = reg;
host->timeout = msecs_to_jiffies(10000);
- host->ccs_enable = !pd || !pd->ccs_unsupported;
- host->clk_ctrl2_enable = pd && pd->clk_ctrl2_present;
+ host->ccs_enable = true;
+ host->clk_ctrl2_enable = false;
host->pd = pdev;
@@ -1509,12 +1493,6 @@ static int sh_mmcif_probe(struct platform_device *pdev)
}
}
- if (pd && pd->use_cd_gpio) {
- ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0);
- if (ret < 0)
- goto err_clk;
- }
-
mutex_init(&host->thread_lock);
ret = mmc_add_host(mmc);
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index d46c2d0..bc6be0d 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -143,6 +143,7 @@ MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
struct sh_mobile_sdhi {
struct clk *clk;
+ struct clk *clk_cd;
struct tmio_mmc_data mmc_data;
struct tmio_mmc_dma dma_priv;
struct pinctrl *pinctrl;
@@ -190,6 +191,12 @@ static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host)
if (ret < 0)
return ret;
+ ret = clk_prepare_enable(priv->clk_cd);
+ if (ret < 0) {
+ clk_disable_unprepare(priv->clk);
+ return ret;
+ }
+
/*
* The clock driver may not know what maximum frequency
* actually works, so it should be set with the max-frequency
@@ -255,6 +262,7 @@ static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host)
struct sh_mobile_sdhi *priv = host_to_priv(host);
clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_cd);
}
static int sh_mobile_sdhi_card_busy(struct mmc_host *mmc)
@@ -335,9 +343,6 @@ static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
{
struct sh_mobile_sdhi *priv;
- if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
- return 0;
-
priv = host_to_priv(host);
/* set sampling clock selection range */
@@ -444,12 +449,7 @@ static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host)
static bool sh_mobile_sdhi_check_scc_error(struct tmio_mmc_host *host)
{
- struct sh_mobile_sdhi *priv;
-
- if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
- return 0;
-
- priv = host_to_priv(host);
+ struct sh_mobile_sdhi *priv = host_to_priv(host);
/* Check SCC error */
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
@@ -468,9 +468,6 @@ static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
{
struct sh_mobile_sdhi *priv;
- if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
- return;
-
priv = host_to_priv(host);
/* Reset SCC */
@@ -556,8 +553,7 @@ static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id =
- of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
+ const struct sh_mobile_sdhi_of_data *of_data = of_device_get_match_data(&pdev->dev);
struct sh_mobile_sdhi *priv;
struct tmio_mmc_data *mmc_data;
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
@@ -584,6 +580,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
goto eprobe;
}
+ /*
+ * Some controllers provide a 2nd clock just to run the internal card
+ * detection logic. Unfortunately, the existing driver architecture does
+ * not support a separation of clocks for runtime PM usage. When
+ * native hotplug is used, the tmio driver assumes that the core
+ * must continue to run for card detect to stay active, so we cannot
+ * disable it.
+ * Additionally, it is prohibited to supply a clock to the core but not
+ * to the card detect circuit. That leaves us with if separate clocks
+ * are presented, we must treat them both as virtually 1 clock.
+ */
+ priv->clk_cd = devm_clk_get(&pdev->dev, "cd");
+ if (IS_ERR(priv->clk_cd))
+ priv->clk_cd = NULL;
+
priv->pinctrl = devm_pinctrl_get(&pdev->dev);
if (!IS_ERR(priv->pinctrl)) {
priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
@@ -598,9 +609,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
goto eprobe;
}
- if (of_id && of_id->data) {
- const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
+ if (of_data) {
mmc_data->flags |= of_data->tmio_flags;
mmc_data->ocr_mask = of_data->tmio_ocr_mask;
mmc_data->capabilities |= of_data->capabilities;
@@ -623,11 +633,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
host->card_busy = sh_mobile_sdhi_card_busy;
host->start_signal_voltage_switch =
sh_mobile_sdhi_start_signal_voltage_switch;
- host->init_tuning = sh_mobile_sdhi_init_tuning;
- host->prepare_tuning = sh_mobile_sdhi_prepare_tuning;
- host->select_tuning = sh_mobile_sdhi_select_tuning;
- host->check_scc_error = sh_mobile_sdhi_check_scc_error;
- host->hw_reset = sh_mobile_sdhi_hw_reset;
}
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
@@ -659,40 +664,40 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
*/
mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL;
- /*
- * All SDHI need SDIO_INFO1 reserved bit
- */
- mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;
+ /* All SDHI have SDIO status bits which must be 1 */
+ mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
ret = tmio_mmc_host_probe(host, mmc_data);
if (ret < 0)
goto efree;
- if (host->mmc->caps & MMC_CAP_UHS_SDR104) {
+ /* Enable tuning iff we have an SCC and a supported mode */
+ if (of_data && of_data->scc_offset &&
+ (host->mmc->caps & MMC_CAP_UHS_SDR104 ||
+ host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR)) {
+ const struct sh_mobile_sdhi_scc *taps = of_data->taps;
+ bool hit = false;
+
host->mmc->caps |= MMC_CAP_HW_RESET;
- if (of_id && of_id->data) {
- const struct sh_mobile_sdhi_of_data *of_data;
- const struct sh_mobile_sdhi_scc *taps;
- bool hit = false;
-
- of_data = of_id->data;
- taps = of_data->taps;
-
- for (i = 0; i < of_data->taps_num; i++) {
- if (taps[i].clk_rate == 0 ||
- taps[i].clk_rate == host->mmc->f_max) {
- host->scc_tappos = taps->tap;
- hit = true;
- break;
- }
+ for (i = 0; i < of_data->taps_num; i++) {
+ if (taps[i].clk_rate == 0 ||
+ taps[i].clk_rate == host->mmc->f_max) {
+ host->scc_tappos = taps->tap;
+ hit = true;
+ break;
}
+ }
- if (!hit)
- dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
+ if (!hit)
+ dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
- priv->scc_ctl = host->ctl + of_data->scc_offset;
- }
+ priv->scc_ctl = host->ctl + of_data->scc_offset;
+ host->init_tuning = sh_mobile_sdhi_init_tuning;
+ host->prepare_tuning = sh_mobile_sdhi_prepare_tuning;
+ host->select_tuning = sh_mobile_sdhi_select_tuning;
+ host->check_scc_error = sh_mobile_sdhi_check_scc_error;
+ host->hw_reset = sh_mobile_sdhi_hw_reset;
}
i = 0;
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index b1d1303..6ffcd28 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -5,6 +5,7 @@
* (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch>
* (C) Copyright 2013-2014 David Lanzend�rfer <david.lanzendoerfer@o2s.ch>
* (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2017 Sootech SA
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -101,6 +102,7 @@
(SDXC_SOFT_RESET | SDXC_FIFO_RESET | SDXC_DMA_RESET)
/* clock control bits */
+#define SDXC_MASK_DATA0 BIT(31)
#define SDXC_CARD_CLOCK_ON BIT(16)
#define SDXC_LOW_POWER_ON BIT(17)
@@ -253,6 +255,11 @@ struct sunxi_mmc_cfg {
/* does the IP block support autocalibration? */
bool can_calibrate;
+
+ /* Does DATA0 needs to be masked while the clock is updated */
+ bool mask_data0;
+
+ bool needs_new_timings;
};
struct sunxi_mmc_host {
@@ -654,11 +661,16 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
unsigned long expire = jiffies + msecs_to_jiffies(750);
u32 rval;
+ dev_dbg(mmc_dev(host->mmc), "%sabling the clock\n",
+ oclk_en ? "en" : "dis");
+
rval = mmc_readl(host, REG_CLKCR);
- rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON);
+ rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0);
if (oclk_en)
rval |= SDXC_CARD_CLOCK_ON;
+ if (host->cfg->mask_data0)
+ rval |= SDXC_MASK_DATA0;
mmc_writel(host, REG_CLKCR, rval);
@@ -678,46 +690,29 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
return -EIO;
}
+ if (host->cfg->mask_data0) {
+ rval = mmc_readl(host, REG_CLKCR);
+ mmc_writel(host, REG_CLKCR, rval & ~SDXC_MASK_DATA0);
+ }
+
return 0;
}
static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
{
- u32 reg = readl(host->reg_base + reg_off);
- u32 delay;
- unsigned long timeout;
-
if (!host->cfg->can_calibrate)
return 0;
- reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT);
- reg &= ~SDXC_CAL_DL_SW_EN;
-
- writel(reg | SDXC_CAL_START, host->reg_base + reg_off);
-
- dev_dbg(mmc_dev(host->mmc), "calibration started\n");
-
- timeout = jiffies + HZ * SDXC_CAL_TIMEOUT;
-
- while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) {
- if (time_before(jiffies, timeout))
- cpu_relax();
- else {
- reg &= ~SDXC_CAL_START;
- writel(reg, host->reg_base + reg_off);
-
- return -ETIMEDOUT;
- }
- }
-
- delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK;
-
- reg &= ~SDXC_CAL_START;
- reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN;
-
- writel(reg, host->reg_base + reg_off);
-
- dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg);
+ /*
+ * FIXME:
+ * This is not clear how the calibration is supposed to work
+ * yet. The best rate have been obtained by simply setting the
+ * delay to 0, as Allwinner does in its BSP.
+ *
+ * The only mode that doesn't have such a delay is HS400, that
+ * is in itself a TODO.
+ */
+ writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off);
return 0;
}
@@ -745,6 +740,7 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
index = SDXC_CLK_50M_DDR;
}
} else {
+ dev_dbg(mmc_dev(host->mmc), "Invalid clock... returning\n");
return -EINVAL;
}
@@ -757,10 +753,21 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
struct mmc_ios *ios)
{
+ struct mmc_host *mmc = host->mmc;
long rate;
u32 rval, clock = ios->clock;
int ret;
+ ret = sunxi_mmc_oclk_onoff(host, 0);
+ if (ret)
+ return ret;
+
+ /* Our clock is gated now */
+ mmc->actual_clock = 0;
+
+ if (!ios->clock)
+ return 0;
+
/* 8 bit DDR requires a higher module clock */
if (ios->timing == MMC_TIMING_MMC_DDR52 &&
ios->bus_width == MMC_BUS_WIDTH_8)
@@ -768,25 +775,21 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
rate = clk_round_rate(host->clk_mmc, clock);
if (rate < 0) {
- dev_err(mmc_dev(host->mmc), "error rounding clk to %d: %ld\n",
+ dev_err(mmc_dev(mmc), "error rounding clk to %d: %ld\n",
clock, rate);
return rate;
}
- dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %ld\n",
+ dev_dbg(mmc_dev(mmc), "setting clk to %d, rounded %ld\n",
clock, rate);
/* setting clock rate */
ret = clk_set_rate(host->clk_mmc, rate);
if (ret) {
- dev_err(mmc_dev(host->mmc), "error setting clk to %ld: %d\n",
+ dev_err(mmc_dev(mmc), "error setting clk to %ld: %d\n",
rate, ret);
return ret;
}
- ret = sunxi_mmc_oclk_onoff(host, 0);
- if (ret)
- return ret;
-
/* clear internal divider */
rval = mmc_readl(host, REG_CLKCR);
rval &= ~0xff;
@@ -798,6 +801,9 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
}
mmc_writel(host, REG_CLKCR, rval);
+ if (host->cfg->needs_new_timings)
+ mmc_writel(host, REG_SD_NTSR, SDXC_2X_TIMING_MODE);
+
ret = sunxi_mmc_clk_set_phase(host, ios, rate);
if (ret)
return ret;
@@ -806,9 +812,22 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
if (ret)
return ret;
- /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */
+ /*
+ * FIXME:
+ *
+ * In HS400 we'll also need to calibrate the data strobe
+ * signal. This should only happen on the MMC2 controller (at
+ * least on the A64).
+ */
+
+ ret = sunxi_mmc_oclk_onoff(host, 1);
+ if (ret)
+ return ret;
+
+ /* And we just enabled our clock back */
+ mmc->actual_clock = rate;
- return sunxi_mmc_oclk_onoff(host, 1);
+ return 0;
}
static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -882,7 +901,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
mmc_writel(host, REG_GCTRL, rval);
/* set up clock */
- if (ios->clock && ios->power_mode) {
+ if (ios->power_mode) {
host->ferror = sunxi_mmc_clk_set_rate(host, ios);
/* Android code had a usleep_range(50000, 55000); here */
}
@@ -1089,6 +1108,14 @@ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
.idma_des_size_bits = 16,
.clk_delays = NULL,
.can_calibrate = true,
+ .mask_data0 = true,
+ .needs_new_timings = true,
+};
+
+static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
+ .idma_des_size_bits = 13,
+ .clk_delays = NULL,
+ .can_calibrate = true,
};
static const struct of_device_id sunxi_mmc_of_match[] = {
@@ -1097,6 +1124,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
{ .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg },
{ .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 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 9e20bcf..2b349d4 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -24,6 +24,7 @@
#include <linux/pagemap.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
+#include <linux/interrupt.h>
#define CTL_SD_CMD 0x00
#define CTL_ARG_REG 0x04
@@ -90,6 +91,8 @@
#define TMIO_SDIO_STAT_EXWT 0x8000
#define TMIO_SDIO_MASK_ALL 0xc007
+#define TMIO_SDIO_SETBITS_MASK 0x0006
+
/* Define some IRQ masks */
/* This is the mask used at reset by the chip */
#define TMIO_MASK_ALL 0x837f031d
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 2064fa1..6b789a7 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -134,18 +134,25 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
struct tmio_mmc_host *host = mmc_priv(mmc);
if (enable && !host->sdio_irq_enabled) {
+ u16 sdio_status;
+
/* Keep device active while SDIO irq is enabled */
pm_runtime_get_sync(mmc_dev(mmc));
- host->sdio_irq_enabled = true;
+ host->sdio_irq_enabled = true;
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
~TMIO_SDIO_STAT_IOIRQ;
- sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
+
+ /* Clear obsolete interrupts before enabling */
+ sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS) & ~TMIO_SDIO_MASK_ALL;
+ if (host->pdata->flags & TMIO_MMC_SDIO_STATUS_SETBITS)
+ sdio_status |= TMIO_SDIO_SETBITS_MASK;
+ sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
+
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
} else if (!enable && host->sdio_irq_enabled) {
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
- sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
host->sdio_irq_enabled = false;
pm_runtime_mark_last_busy(mmc_dev(mmc));
@@ -711,9 +718,8 @@ static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host,
return false;
}
-static void tmio_mmc_sdio_irq(int irq, void *devid)
+static void __tmio_mmc_sdio_irq(struct tmio_mmc_host *host)
{
- struct tmio_mmc_host *host = devid;
struct mmc_host *mmc = host->mmc;
struct tmio_mmc_data *pdata = host->pdata;
unsigned int ireg, status;
@@ -726,8 +732,8 @@ static void tmio_mmc_sdio_irq(int irq, void *devid)
ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdio_irq_mask;
sdio_status = status & ~TMIO_SDIO_MASK_ALL;
- if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK)
- sdio_status |= 6;
+ if (pdata->flags & TMIO_MMC_SDIO_STATUS_SETBITS)
+ sdio_status |= TMIO_SDIO_SETBITS_MASK;
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
@@ -754,7 +760,7 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid)
if (__tmio_mmc_sdcard_irq(host, ireg, status))
return IRQ_HANDLED;
- tmio_mmc_sdio_irq(irq, devid);
+ __tmio_mmc_sdio_irq(host);
return IRQ_HANDLED;
}
@@ -902,6 +908,12 @@ static int tmio_mmc_clk_enable(struct tmio_mmc_host *host)
return host->clk_enable(host);
}
+static void tmio_mmc_clk_disable(struct tmio_mmc_host *host)
+{
+ if (host->clk_disable)
+ host->clk_disable(host);
+}
+
static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd)
{
struct mmc_host *mmc = host->mmc;
@@ -1145,7 +1157,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
ret = mmc_of_parse(mmc);
if (ret < 0)
- goto host_free;
+ return ret;
_host->pdata = pdata;
platform_set_drvdata(pdev, mmc);
@@ -1155,14 +1167,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
ret = tmio_mmc_init_ocr(_host);
if (ret < 0)
- goto host_free;
+ return ret;
_host->ctl = devm_ioremap(&pdev->dev,
res_ctl->start, resource_size(res_ctl));
- if (!_host->ctl) {
- ret = -ENOMEM;
- goto host_free;
- }
+ if (!_host->ctl)
+ return -ENOMEM;
tmio_mmc_ops.card_busy = _host->card_busy;
tmio_mmc_ops.start_signal_voltage_switch = _host->start_signal_voltage_switch;
@@ -1179,8 +1189,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
- !mmc_card_is_removable(mmc) ||
- mmc->slot.cd_irq >= 0);
+ !mmc_card_is_removable(mmc));
/*
* On Gen2+, eMMC with NONREMOVABLE currently fails because native
@@ -1200,10 +1209,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
* Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
* looping forever...
*/
- if (mmc->f_min == 0) {
- ret = -EINVAL;
- goto host_free;
- }
+ if (mmc->f_min == 0)
+ return -EINVAL;
/*
* While using internal tmio hardware logic for card detection, we need
@@ -1232,7 +1239,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
if (pdata->flags & TMIO_MMC_SDIO_IRQ) {
_host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(_host, CTL_SDIO_IRQ_MASK, _host->sdio_irq_mask);
- sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0000);
+ sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0001);
}
spin_lock_init(&_host->lock);
@@ -1268,10 +1275,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
}
return 0;
-
-host_free:
-
- return ret;
}
EXPORT_SYMBOL(tmio_mmc_host_probe);
@@ -1280,6 +1283,9 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
struct platform_device *pdev = host->pdev;
struct mmc_host *mmc = host->mmc;
+ if (host->pdata->flags & TMIO_MMC_SDIO_IRQ)
+ sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
+
if (!host->native_hotplug)
pm_runtime_get_sync(&pdev->dev);
@@ -1292,6 +1298,8 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+
+ tmio_mmc_clk_disable(host);
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
@@ -1306,8 +1314,7 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
if (host->clk_cache)
tmio_mmc_clk_stop(host);
- if (host->clk_disable)
- host->clk_disable(host);
+ tmio_mmc_clk_disable(host);
return 0;
}
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 63fac78..6380044 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -13,6 +13,7 @@
#include <linux/dma-mapping.h>
#include <linux/highmem.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/mmc/host.h>
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index bb3e0d1..c061e7c 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -640,8 +640,6 @@ static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300)
mutex_lock(&vub300->irq_mutex);
if (vub300->irq_enabled)
mmc_signal_sdio_irq(vub300->mmc);
- else if (vub300->irqs_queued)
- vub300->irqs_queued += 1;
else
vub300->irqs_queued += 1;
vub300->irq_disabled = 0;
@@ -728,8 +726,7 @@ static void vub300_deadwork_thread(struct work_struct *work)
*/
} else if (vub300->card_present) {
check_vub300_port_status(vub300);
- } else if (vub300->mmc && vub300->mmc->card &&
- mmc_card_present(vub300->mmc->card)) {
+ } else if (vub300->mmc && vub300->mmc->card) {
/*
* the MMC core must not have responded
* to the previous indication - lets
@@ -1756,8 +1753,7 @@ static void vub300_cmndwork_thread(struct work_struct *work)
int data_length;
mutex_lock(&vub300->cmd_mutex);
init_completion(&vub300->command_complete);
- if (likely(vub300->vub_name[0]) || !vub300->mmc->card ||
- !mmc_card_present(vub300->mmc->card)) {
+ if (likely(vub300->vub_name[0]) || !vub300->mmc->card) {
/*
* the name of the EMPTY Pseudo firmware file
* is used as a flag to indicate that the file
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 80a3b11..bd04e8b 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -1437,11 +1437,14 @@ err:
static void wbsd_release_dma(struct wbsd_host *host)
{
- if (!dma_mapping_error(mmc_dev(host->mmc), host->dma_addr)) {
+ /*
+ * host->dma_addr is valid here iff host->dma_buffer is not NULL.
+ */
+ if (host->dma_buffer) {
dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+ kfree(host->dma_buffer);
}
- kfree(host->dma_buffer);
if (host->dma >= 0)
free_dma(host->dma);
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index 5af0055..21ebba8 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -20,6 +20,7 @@
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/gpio.h>
+#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
OpenPOWER on IntegriCloud