summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/block.c118
-rw-r--r--drivers/mmc/core/bus.c14
-rw-r--r--drivers/mmc/core/bus.h2
-rw-r--r--drivers/mmc/core/card.h15
-rw-r--r--drivers/mmc/core/core.c93
-rw-r--r--drivers/mmc/core/core.h16
-rw-r--r--drivers/mmc/core/debugfs.c10
-rw-r--r--drivers/mmc/core/host.c11
-rw-r--r--drivers/mmc/core/host.h8
-rw-r--r--drivers/mmc/core/mmc.c183
-rw-r--r--drivers/mmc/core/mmc_ops.c78
-rw-r--r--drivers/mmc/core/mmc_ops.h2
-rw-r--r--drivers/mmc/core/mmc_test.c50
-rw-r--r--drivers/mmc/core/pwrseq_simple.c3
-rw-r--r--drivers/mmc/core/queue.c10
-rw-r--r--drivers/mmc/core/quirks.h22
-rw-r--r--drivers/mmc/core/regulator.c77
-rw-r--r--drivers/mmc/core/sd.c80
-rw-r--r--drivers/mmc/core/sd_uhs2.c4
-rw-r--r--drivers/mmc/core/sdio.c14
-rw-r--r--drivers/mmc/core/sdio_bus.c5
-rw-r--r--drivers/mmc/core/sdio_uart.c2
-rw-r--r--drivers/mmc/core/slot-gpio.c20
-rw-r--r--drivers/mmc/host/Kconfig46
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/alcor.c31
-rw-r--r--drivers/mmc/host/atmel-mci.c48
-rw-r--r--drivers/mmc/host/au1xmmc.c32
-rw-r--r--drivers/mmc/host/bcm2835.c36
-rw-r--r--drivers/mmc/host/cavium-thunderx.c4
-rw-r--r--drivers/mmc/host/cavium.c10
-rw-r--r--drivers/mmc/host/cb710-mmc.c27
-rw-r--r--drivers/mmc/host/cqhci-crypto.c46
-rw-r--r--drivers/mmc/host/cqhci.h9
-rw-r--r--drivers/mmc/host/davinci_mmc.c44
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c54
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798mv200.c8
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c9
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c9
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c28
-rw-r--r--drivers/mmc/host/dw_mmc.c155
-rw-r--r--drivers/mmc/host/dw_mmc.h30
-rw-r--r--drivers/mmc/host/jz4740_mmc.c47
-rw-r--r--drivers/mmc/host/litex_mmc.c12
-rw-r--r--drivers/mmc/host/loongson2-mmc.c1030
-rw-r--r--drivers/mmc/host/meson-mx-sdhc-clkc.c4
-rw-r--r--drivers/mmc/host/meson-mx-sdhc-mmc.c13
-rw-r--r--drivers/mmc/host/meson-mx-sdio.c346
-rw-r--r--drivers/mmc/host/mmc_spi.c8
-rw-r--r--drivers/mmc/host/mmci.c41
-rw-r--r--drivers/mmc/host/moxart-mmc.c40
-rw-r--r--drivers/mmc/host/mtk-sd.c312
-rw-r--r--drivers/mmc/host/mvsdio.c32
-rw-r--r--drivers/mmc/host/mxcmmc.c45
-rw-r--r--drivers/mmc/host/mxs-mmc.c37
-rw-r--r--drivers/mmc/host/omap.c29
-rw-r--r--drivers/mmc/host/omap_hsmmc.c37
-rw-r--r--drivers/mmc/host/owl-mmc.c37
-rw-r--r--drivers/mmc/host/pxamci.c88
-rw-r--r--drivers/mmc/host/renesas_sdhi.h5
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c246
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c18
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c3
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c7
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c85
-rw-r--r--drivers/mmc/host/sdhci-acpi.c49
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c2
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c173
-rw-r--r--drivers/mmc/host/sdhci-cadence.c104
-rw-r--r--drivers/mmc/host/sdhci-dove.c12
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c329
-rw-r--r--drivers/mmc/host/sdhci-esdhc-mcf.c25
-rw-r--r--drivers/mmc/host/sdhci-iproc.c18
-rw-r--r--drivers/mmc/host/sdhci-milbeaut.c19
-rw-r--r--drivers/mmc/host/sdhci-msm.c245
-rw-r--r--drivers/mmc/host/sdhci-npcm.c15
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c69
-rw-r--r--drivers/mmc/host/sdhci-of-aspeed.c10
-rw-r--r--drivers/mmc/host/sdhci-of-at91.c50
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c774
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c19
-rw-r--r--drivers/mmc/host/sdhci-of-k1.c308
-rw-r--r--drivers/mmc/host/sdhci-of-ma35d1.c23
-rw-r--r--drivers/mmc/host/sdhci-of-sparx5.c24
-rw-r--r--drivers/mmc/host/sdhci-omap.c47
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c124
-rw-r--r--drivers/mmc/host/sdhci-pci-gli.c105
-rw-r--r--drivers/mmc/host/sdhci-pci.h1
-rw-r--r--drivers/mmc/host/sdhci-pic32.c9
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c16
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h1
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c26
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c70
-rw-r--r--drivers/mmc/host/sdhci-s3c.c37
-rw-r--r--drivers/mmc/host/sdhci-spear.c17
-rw-r--r--drivers/mmc/host/sdhci-sprd.c44
-rw-r--r--drivers/mmc/host/sdhci-st.c12
-rw-r--r--drivers/mmc/host/sdhci-tegra.c23
-rw-r--r--drivers/mmc/host/sdhci-uhs2.c23
-rw-r--r--drivers/mmc/host/sdhci-xenon.c37
-rw-r--r--drivers/mmc/host/sdhci.c75
-rw-r--r--drivers/mmc/host/sdhci.h30
-rw-r--r--drivers/mmc/host/sdhci_am654.c82
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.c13
-rw-r--r--drivers/mmc/host/sdricoh_cs.c10
-rw-r--r--drivers/mmc/host/sh_mmcif.c30
-rw-r--r--drivers/mmc/host/sunplus-mmc.c2
-rw-r--r--drivers/mmc/host/sunxi-mmc.c33
-rw-r--r--drivers/mmc/host/tifm_sd.c15
-rw-r--r--drivers/mmc/host/tmio_mmc.h29
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c64
-rw-r--r--drivers/mmc/host/toshsd.c12
-rw-r--r--drivers/mmc/host/uniphier-sd.c8
-rw-r--r--drivers/mmc/host/usdhi6rol0.c34
-rw-r--r--drivers/mmc/host/ushc.c4
-rw-r--r--drivers/mmc/host/via-sdmmc.c23
-rw-r--r--drivers/mmc/host/vub300.c30
-rw-r--r--drivers/mmc/host/wbsd.c8
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c24
119 files changed, 5138 insertions, 2235 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 4830628510e6..fb6eb2d79b4f 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -79,47 +79,9 @@ MODULE_ALIAS("mmc:block");
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
#define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8)
-/**
- * struct rpmb_frame - rpmb frame as defined by eMMC 5.1 (JESD84-B51)
- *
- * @stuff : stuff bytes
- * @key_mac : The authentication key or the message authentication
- * code (MAC) depending on the request/response type.
- * The MAC will be delivered in the last (or the only)
- * block of data.
- * @data : Data to be written or read by signed access.
- * @nonce : Random number generated by the host for the requests
- * and copied to the response by the RPMB engine.
- * @write_counter: Counter value for the total amount of the successful
- * authenticated data write requests made by the host.
- * @addr : Address of the data to be programmed to or read
- * from the RPMB. Address is the serial number of
- * the accessed block (half sector 256B).
- * @block_count : Number of blocks (half sectors, 256B) requested to be
- * read/programmed.
- * @result : Includes information about the status of the write counter
- * (valid, expired) and result of the access made to the RPMB.
- * @req_resp : Defines the type of request and response to/from the memory.
- *
- * The stuff bytes and big-endian properties are modeled to fit to the spec.
- */
-struct rpmb_frame {
- u8 stuff[196];
- u8 key_mac[32];
- u8 data[256];
- u8 nonce[16];
- __be32 write_counter;
- __be16 addr;
- __be16 block_count;
- __be16 result;
- __be16 req_resp;
-} __packed;
-
-#define RPMB_PROGRAM_KEY 0x1 /* Program RPMB Authentication Key */
-#define RPMB_GET_WRITE_COUNTER 0x2 /* Read RPMB write counter */
-#define RPMB_WRITE_DATA 0x3 /* Write data to RPMB partition */
-#define RPMB_READ_DATA 0x4 /* Read data from RPMB partition */
-#define RPMB_RESULT_READ 0x5 /* Read result request (Internal) */
+#define RPMB_FRAME_SIZE sizeof(struct rpmb_frame)
+#define CHECK_SIZE_NEQ(val) ((val) != sizeof(struct rpmb_frame))
+#define CHECK_SIZE_ALIGNED(val) IS_ALIGNED((val), sizeof(struct rpmb_frame))
static DEFINE_MUTEX(block_mutex);
@@ -387,10 +349,10 @@ static umode_t mmc_disk_attrs_is_visible(struct kobject *kobj,
if (a == &dev_attr_ro_lock_until_next_power_on.attr &&
(md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
md->queue.card->ext_csd.boot_ro_lockable) {
- mode = S_IRUGO;
+ mode = 0444;
if (!(md->queue.card->ext_csd.boot_ro_lock &
EXT_CSD_BOOT_WP_B_PWR_WP_DIS))
- mode |= S_IWUSR;
+ mode |= 0200;
}
mmc_blk_put(md);
@@ -435,9 +397,9 @@ static void mmc_blk_release(struct gendisk *disk)
}
static int
-mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+mmc_blk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
{
- geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16);
+ geo->cylinders = get_capacity(disk) / (4 * 16);
geo->heads = 4;
geo->sectors = 16;
return 0;
@@ -995,7 +957,6 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
u32 result;
__be32 *blocks;
u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
- unsigned int noio_flag;
struct mmc_request mrq = {};
struct mmc_command cmd = {};
@@ -1020,9 +981,7 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
mrq.cmd = &cmd;
mrq.data = &data;
- noio_flag = memalloc_noio_save();
- blocks = kmalloc(resp_sz, GFP_KERNEL);
- memalloc_noio_restore(noio_flag);
+ blocks = kmalloc(resp_sz, GFP_NOIO);
if (!blocks)
return -ENOMEM;
@@ -1220,7 +1179,7 @@ static void mmc_blk_issue_erase_rq(struct mmc_queue *mq, struct request *req,
int err = 0;
blk_status_t status = BLK_STS_OK;
- if (!mmc_can_erase(card)) {
+ if (!mmc_card_can_erase(card)) {
status = BLK_STS_NOTSUPP;
goto fail;
}
@@ -1276,7 +1235,7 @@ static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
int err = 0, type = MMC_BLK_SECDISCARD;
blk_status_t status = BLK_STS_OK;
- if (!(mmc_can_secure_erase_trim(card))) {
+ if (!(mmc_card_can_secure_erase_trim(card))) {
status = BLK_STS_NOTSUPP;
goto out;
}
@@ -1284,7 +1243,7 @@ static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
from = blk_rq_pos(req);
nr = blk_rq_sectors(req);
- if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+ if (mmc_card_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
arg = MMC_SECURE_TRIM1_ARG;
else
arg = MMC_SECURE_ERASE_ARG;
@@ -1768,8 +1727,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
* these, while retaining features like reliable writes.
*/
if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) &&
- (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) ||
- do_data_tag)) {
+ (do_rel_wr || !mmc_card_blk_no_cmd23(card) || do_data_tag)) {
brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
brq->sbc.arg = brq->data.blocks |
(do_rel_wr ? (1 << 31) : 0) |
@@ -2278,7 +2236,7 @@ void mmc_blk_mq_recovery(struct mmc_queue *mq)
static void mmc_blk_mq_complete_prev_req(struct mmc_queue *mq,
struct request **prev_req)
{
- if (mmc_host_done_complete(mq->card->host))
+ if (mmc_host_can_done_complete(mq->card->host))
return;
mutex_lock(&mq->complete_lock);
@@ -2317,7 +2275,7 @@ static void mmc_blk_mq_req_done(struct mmc_request *mrq)
struct mmc_host *host = mq->card->host;
unsigned long flags;
- if (!mmc_host_done_complete(host)) {
+ if (!mmc_host_can_done_complete(host)) {
bool waiting;
/*
@@ -2430,7 +2388,7 @@ static int mmc_blk_mq_issue_rw_rq(struct mmc_queue *mq,
mq->rw_wait = false;
/* Release re-tuning here where there is no synchronization required */
- if (err || mmc_host_done_complete(host))
+ if (err || mmc_host_can_done_complete(host))
mmc_retune_release(host);
out_post_req:
@@ -2618,13 +2576,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
*/
md->read_only = mmc_blk_readonly(card);
- if (mmc_host_cmd23(card->host)) {
- if ((mmc_card_mmc(card) &&
- card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
- (mmc_card_sd(card) && !mmc_card_ult_capacity(card) &&
- card->scr.cmds & SD_SCR_CMD23_SUPPORT))
- md->flags |= MMC_BLK_CMD23;
- }
+ if (mmc_host_can_cmd23(card->host) && mmc_card_can_cmd23(card))
+ md->flags |= MMC_BLK_CMD23;
if (md->flags & MMC_BLK_CMD23 &&
((card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) ||
@@ -2655,7 +2608,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
md->disk->private_data = md;
md->parent = parent;
set_disk_ro(md->disk, md->read_only || default_ro);
- if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
+ if (area_type & MMC_BLK_DATA_AREA_RPMB)
md->disk->flags |= GENHD_FL_NO_PART;
/*
@@ -2864,12 +2817,12 @@ static void set_idata(struct mmc_blk_ioc_data *idata, u32 opcode,
* The size of an RPMB frame must match what's expected by the
* hardware.
*/
- BUILD_BUG_ON(sizeof(struct rpmb_frame) != 512);
+ static_assert(!CHECK_SIZE_NEQ(512), "RPMB frame size must be 512 bytes");
idata->ic.opcode = opcode;
idata->ic.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
idata->ic.write_flag = write_flag;
- idata->ic.blksz = sizeof(struct rpmb_frame);
+ idata->ic.blksz = RPMB_FRAME_SIZE;
idata->ic.blocks = buf_bytes / idata->ic.blksz;
idata->buf = buf;
idata->buf_bytes = buf_bytes;
@@ -2893,32 +2846,28 @@ static int mmc_route_rpmb_frames(struct device *dev, u8 *req,
if (IS_ERR(md->queue.card))
return PTR_ERR(md->queue.card);
- if (req_len < sizeof(*frm))
+ if (req_len < RPMB_FRAME_SIZE)
return -EINVAL;
req_type = be16_to_cpu(frm->req_resp);
switch (req_type) {
case RPMB_PROGRAM_KEY:
- if (req_len != sizeof(struct rpmb_frame) ||
- resp_len != sizeof(struct rpmb_frame))
+ if (CHECK_SIZE_NEQ(req_len) || CHECK_SIZE_NEQ(resp_len))
return -EINVAL;
write = true;
break;
case RPMB_GET_WRITE_COUNTER:
- if (req_len != sizeof(struct rpmb_frame) ||
- resp_len != sizeof(struct rpmb_frame))
+ if (CHECK_SIZE_NEQ(req_len) || CHECK_SIZE_NEQ(resp_len))
return -EINVAL;
write = false;
break;
case RPMB_WRITE_DATA:
- if (req_len % sizeof(struct rpmb_frame) ||
- resp_len != sizeof(struct rpmb_frame))
+ if (!CHECK_SIZE_ALIGNED(req_len) || CHECK_SIZE_NEQ(resp_len))
return -EINVAL;
write = true;
break;
case RPMB_READ_DATA:
- if (req_len != sizeof(struct rpmb_frame) ||
- resp_len % sizeof(struct rpmb_frame))
+ if (CHECK_SIZE_NEQ(req_len) || !CHECK_SIZE_ALIGNED(resp_len))
return -EINVAL;
write = false;
break;
@@ -2926,25 +2875,23 @@ static int mmc_route_rpmb_frames(struct device *dev, u8 *req,
return -EINVAL;
}
- if (write)
- cmd_count = 3;
- else
- cmd_count = 2;
+ /* Write operations require 3 commands, read operations require 2 */
+ cmd_count = write ? 3 : 2;
idata = alloc_idata(rpmb, cmd_count);
if (!idata)
return -ENOMEM;
if (write) {
- struct rpmb_frame *frm = (struct rpmb_frame *)resp;
+ struct rpmb_frame *resp_frm = (struct rpmb_frame *)resp;
/* Send write request frame(s) */
set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK,
1 | MMC_CMD23_ARG_REL_WR, req, req_len);
/* Send result request frame */
- memset(frm, 0, sizeof(*frm));
- frm->req_resp = cpu_to_be16(RPMB_RESULT_READ);
+ memset(resp_frm, 0, RPMB_FRAME_SIZE);
+ resp_frm->req_resp = cpu_to_be16(RPMB_RESULT_READ);
set_idata(idata[1], MMC_WRITE_MULTIPLE_BLOCK, 1, resp,
resp_len);
@@ -3244,7 +3191,7 @@ static void mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
if (mmc_card_mmc(card)) {
md->ext_csd_dentry =
- debugfs_create_file("ext_csd", S_IRUSR, root, card,
+ debugfs_create_file("ext_csd", 0400, root, card,
&mmc_dbg_ext_csd_fops);
}
}
@@ -3325,7 +3272,8 @@ static int mmc_blk_probe(struct mmc_card *card)
mmc_fixup_device(card, mmc_blk_fixups);
card->complete_wq = alloc_workqueue("mmc_complete",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
+ 0);
if (!card->complete_wq) {
pr_err("Failed to create mmc completion workqueue");
return -ENOMEM;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 9283b28bc69f..ec4f3462bf80 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -19,6 +19,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
#include "core.h"
#include "card.h"
@@ -149,6 +150,8 @@ static void mmc_bus_shutdown(struct device *dev)
if (dev->driver && drv->shutdown)
drv->shutdown(card);
+ __mmc_stop_host(host);
+
if (host->bus_ops->shutdown) {
ret = host->bus_ops->shutdown(host);
if (ret)
@@ -381,6 +384,14 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_set_present(card);
+ /*
+ * Register for undervoltage notification if the card supports
+ * power-off notification, enabling emergency shutdowns.
+ */
+ if (mmc_card_mmc(card) &&
+ card->ext_csd.power_off_notification == EXT_CSD_POWER_ON)
+ mmc_regulator_register_undervoltage_notifier(card->host);
+
return 0;
}
@@ -392,6 +403,9 @@ void mmc_remove_card(struct mmc_card *card)
{
struct mmc_host *host = card->host;
+ if (mmc_card_present(card))
+ mmc_regulator_unregister_undervoltage_notifier(host);
+
mmc_remove_card_debugfs(card);
if (mmc_card_present(card)) {
diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h
index cfd0d02d3420..8b69624fa46e 100644
--- a/drivers/mmc/core/bus.h
+++ b/drivers/mmc/core/bus.h
@@ -20,7 +20,7 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a
struct mmc_card *card = mmc_dev_to_card(dev); \
return sysfs_emit(buf, fmt, args); \
} \
-static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+static DEVICE_ATTR(name, 0444, mmc_##name##_show, NULL)
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
const struct device_type *type);
diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h
index 3205feb1e8ff..1200951bab08 100644
--- a/drivers/mmc/core/card.h
+++ b/drivers/mmc/core/card.h
@@ -89,6 +89,7 @@ struct mmc_fixup {
#define CID_MANFID_MICRON 0x13
#define CID_MANFID_SAMSUNG 0x15
#define CID_MANFID_APACER 0x27
+#define CID_MANFID_SWISSBIT 0x5D
#define CID_MANFID_KINGSTON 0x70
#define CID_MANFID_HYNIX 0x90
#define CID_MANFID_KINGSTON_SD 0x9F
@@ -244,14 +245,19 @@ 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_nonstd_func_interface(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
+}
+
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)
+static inline int mmc_card_blk_no_cmd23(const struct mmc_card *c)
{
- return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
+ return c->quirks & MMC_QUIRK_BLK_NO_CMD23;
}
static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c)
@@ -294,4 +300,9 @@ static inline int mmc_card_broken_sd_poweroff_notify(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY;
}
+static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c)
+{
+ return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING;
+}
+
#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index a499f3c59de5..860378bea557 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -19,7 +19,6 @@
#include <linux/scatterlist.h>
#include <linux/log2.h>
#include <linux/pm_runtime.h>
-#include <linux/pm_wakeup.h>
#include <linux/suspend.h>
#include <linux/fault-inject.h>
#include <linux/random.h>
@@ -336,7 +335,7 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
- if (mrq->cmd && mrq->cmd->has_ext_addr)
+ if (mrq->cmd->has_ext_addr)
mmc_send_ext_addr(host, mrq->cmd->ext_addr);
init_completion(&mrq->cmd_completion);
@@ -557,8 +556,7 @@ int mmc_cqe_recovery(struct mmc_host *host)
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_STOP_TRANSMISSION;
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
- cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
+ cmd.flags = MMC_RSP_R1B_NO_CRC | MMC_CMD_AC; /* Ignore CRC */
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
@@ -567,8 +565,7 @@ int mmc_cqe_recovery(struct mmc_host *host)
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_CMDQ_TASK_MGMT;
cmd.arg = 1; /* Discard entire queue */
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
- cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
+ cmd.flags = MMC_RSP_R1B_NO_CRC | MMC_CMD_AC; /* Ignore CRC */
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
@@ -885,7 +882,6 @@ void mmc_put_card(struct mmc_card *card, struct mmc_ctx *ctx)
WARN_ON(ctx && host->claimer != ctx);
mmc_release_host(host);
- pm_runtime_mark_last_busy(&card->dev);
pm_runtime_put_autosuspend(&card->dev);
}
EXPORT_SYMBOL(mmc_put_card);
@@ -1402,6 +1398,29 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr)
mmc_power_up(host, ocr);
}
+/**
+ * mmc_handle_undervoltage - Handle an undervoltage event on the MMC bus
+ * @host: The MMC host that detected the undervoltage condition
+ *
+ * This function is called when an undervoltage event is detected on one of
+ * the MMC regulators.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int mmc_handle_undervoltage(struct mmc_host *host)
+{
+ /* Stop the host to prevent races with card removal */
+ __mmc_stop_host(host);
+
+ if (!host->bus_ops || !host->bus_ops->handle_undervoltage)
+ return 0;
+
+ dev_warn(mmc_dev(host), "%s: Undervoltage detected, initiating emergency stop\n",
+ mmc_hostname(host));
+
+ return host->bus_ops->handle_undervoltage(host);
+}
+
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
@@ -1840,52 +1859,53 @@ int mmc_erase(struct mmc_card *card, sector_t from, unsigned int nr,
}
EXPORT_SYMBOL(mmc_erase);
-int mmc_can_erase(struct mmc_card *card)
+bool mmc_card_can_erase(struct mmc_card *card)
{
- if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
- return 1;
- return 0;
+ return (card->csd.cmdclass & CCC_ERASE && card->erase_size);
}
-EXPORT_SYMBOL(mmc_can_erase);
+EXPORT_SYMBOL(mmc_card_can_erase);
-int mmc_can_trim(struct mmc_card *card)
+bool mmc_card_can_trim(struct mmc_card *card)
{
- if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) &&
- (!(card->quirks & MMC_QUIRK_TRIM_BROKEN)))
- return 1;
- return 0;
+ return ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) &&
+ (!(card->quirks & MMC_QUIRK_TRIM_BROKEN)));
}
-EXPORT_SYMBOL(mmc_can_trim);
+EXPORT_SYMBOL(mmc_card_can_trim);
-int mmc_can_discard(struct mmc_card *card)
+bool mmc_card_can_discard(struct mmc_card *card)
{
/*
* As there's no way to detect the discard support bit at v4.5
* use the s/w feature support filed.
*/
- if (card->ext_csd.feature_support & MMC_DISCARD_FEATURE)
- return 1;
- return 0;
+ return (card->ext_csd.feature_support & MMC_DISCARD_FEATURE);
}
-EXPORT_SYMBOL(mmc_can_discard);
+EXPORT_SYMBOL(mmc_card_can_discard);
-int mmc_can_sanitize(struct mmc_card *card)
+bool mmc_card_can_sanitize(struct mmc_card *card)
{
- if (!mmc_can_trim(card) && !mmc_can_erase(card))
- return 0;
+ if (!mmc_card_can_trim(card) && !mmc_card_can_erase(card))
+ return false;
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_SANITIZE)
- return 1;
- return 0;
+ return true;
+ return false;
}
-int mmc_can_secure_erase_trim(struct mmc_card *card)
+bool mmc_card_can_secure_erase_trim(struct mmc_card *card)
{
- if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) &&
- !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
- return 1;
- return 0;
+ return ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) &&
+ !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN));
+}
+EXPORT_SYMBOL(mmc_card_can_secure_erase_trim);
+
+bool mmc_card_can_cmd23(struct mmc_card *card)
+{
+ return ((mmc_card_mmc(card) &&
+ card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
+ (mmc_card_sd(card) && !mmc_card_ult_capacity(card) &&
+ card->scr.cmds & SD_SCR_CMD23_SUPPORT));
}
-EXPORT_SYMBOL(mmc_can_secure_erase_trim);
+EXPORT_SYMBOL(mmc_card_can_cmd23);
int mmc_erase_group_aligned(struct mmc_card *card, sector_t from,
unsigned int nr)
@@ -1990,7 +2010,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
return card->pref_erase;
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
- if (mmc_can_trim(card)) {
+ if (mmc_card_can_trim(card)) {
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
if (max_trim < max_discard || max_discard == 0)
max_discard = max_trim;
@@ -2335,6 +2355,9 @@ void mmc_start_host(struct mmc_host *host)
void __mmc_stop_host(struct mmc_host *host)
{
+ if (host->rescan_disable)
+ return;
+
if (host->slot.cd_irq >= 0) {
mmc_gpio_set_cd_wake(host, false);
disable_irq(host->slot.cd_irq);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index fc9c066e6468..a028b48be164 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -31,6 +31,7 @@ struct mmc_bus_ops {
int (*sw_reset)(struct mmc_host *);
bool (*cache_enabled)(struct mmc_host *);
int (*flush_cache)(struct mmc_host *);
+ int (*handle_undervoltage)(struct mmc_host *host);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -59,6 +60,10 @@ void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
void mmc_set_initial_state(struct mmc_host *host);
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
+int mmc_handle_undervoltage(struct mmc_host *host);
+void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host);
+void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host);
+void mmc_undervoltage_workfn(struct work_struct *work);
static inline void mmc_delay(unsigned int ms)
{
@@ -118,11 +123,12 @@ bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq);
int mmc_erase(struct mmc_card *card, sector_t 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);
+bool mmc_card_can_erase(struct mmc_card *card);
+bool mmc_card_can_trim(struct mmc_card *card);
+bool mmc_card_can_discard(struct mmc_card *card);
+bool mmc_card_can_sanitize(struct mmc_card *card);
+bool mmc_card_can_secure_erase_trim(struct mmc_card *card);
+bool mmc_card_can_cmd23(struct mmc_card *card);
int mmc_erase_group_aligned(struct mmc_card *card, sector_t from, unsigned int nr);
unsigned int mmc_calc_max_discard(struct mmc_card *card);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index f10a4dcf1f95..91ea00a0f61d 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -315,7 +315,10 @@ static int mmc_caps_set(void *data, u64 val)
MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_UHS |
- MMC_CAP_DDR;
+ MMC_CAP_DDR |
+ MMC_CAP_4_BIT_DATA |
+ MMC_CAP_8_BIT_DATA |
+ MMC_CAP_CMD23;
if (diff & ~allowed)
return -EINVAL;
@@ -327,7 +330,10 @@ static int mmc_caps_set(void *data, u64 val)
static int mmc_caps2_set(void *data, u64 val)
{
- u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V;
+ u32 allowed = MMC_CAP2_HSX00_1_8V |
+ MMC_CAP2_HSX00_1_2V |
+ MMC_CAP2_CQE |
+ MMC_CAP2_CQE_DCMD;
u32 *caps = data;
u32 diff = *caps ^ val;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 48bda70145ee..88c95dbfd9cf 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -14,7 +14,6 @@
#include <linux/idr.h>
#include <linux/of.h>
#include <linux/pagemap.h>
-#include <linux/pm_wakeup.h>
#include <linux/export.h>
#include <linux/leds.h>
#include <linux/slab.h>
@@ -148,13 +147,13 @@ void mmc_retune_disable(struct mmc_host *host)
{
mmc_retune_unpause(host);
host->can_retune = 0;
- del_timer_sync(&host->retune_timer);
+ timer_delete_sync(&host->retune_timer);
mmc_retune_clear(host);
}
void mmc_retune_timer_stop(struct mmc_host *host)
{
- del_timer_sync(&host->retune_timer);
+ timer_delete_sync(&host->retune_timer);
}
EXPORT_SYMBOL(mmc_retune_timer_stop);
@@ -213,7 +212,7 @@ out:
static void mmc_retune_timer(struct timer_list *t)
{
- struct mmc_host *host = from_timer(host, t, retune_timer);
+ struct mmc_host *host = timer_container_of(host, t, retune_timer);
mmc_retune_needed(host);
}
@@ -303,6 +302,8 @@ int mmc_of_parse(struct mmc_host *host)
/* f_max is obtained from the optional "max-frequency" property */
device_property_read_u32(dev, "max-frequency", &host->f_max);
+ device_property_read_u32(dev, "max-sd-hs-hz", &host->max_sd_hs_hz);
+
/*
* Configure CD and WP pins. They are both by default active low to
* match the SDHCI spec. If GPIOs are provided for CD and / or WP, the
@@ -565,6 +566,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
INIT_WORK(&host->sdio_irq_work, sdio_irq_work);
timer_setup(&host->retune_timer, mmc_retune_timer, 0);
+ INIT_WORK(&host->supply.uv_work, mmc_undervoltage_workfn);
+
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index 48c4952512a5..5941d68ff989 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -39,22 +39,22 @@ static inline void mmc_retune_recheck(struct mmc_host *host)
host->retune_now = 1;
}
-static inline int mmc_host_cmd23(struct mmc_host *host)
+static inline int mmc_host_can_cmd23(struct mmc_host *host)
{
return host->caps & MMC_CAP_CMD23;
}
-static inline bool mmc_host_done_complete(struct mmc_host *host)
+static inline bool mmc_host_can_done_complete(struct mmc_host *host)
{
return host->caps & MMC_CAP_DONE_COMPLETE;
}
-static inline int mmc_boot_partition_access(struct mmc_host *host)
+static inline int mmc_host_can_access_boot(struct mmc_host *host)
{
return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC);
}
-static inline int mmc_host_uhs(struct mmc_host *host)
+static inline int mmc_host_can_uhs(struct mmc_host *host)
{
return host->caps &
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6a23be214543..7c86efb1044a 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/string.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/sysfs.h>
@@ -32,6 +33,13 @@
#define MIN_CACHE_EN_TIMEOUT_MS 1600
#define CACHE_FLUSH_TIMEOUT_MS 30000 /* 30s */
+enum mmc_poweroff_type {
+ MMC_POWEROFF_SUSPEND,
+ MMC_POWEROFF_SHUTDOWN,
+ MMC_POWEROFF_UNDERVOLTAGE,
+ MMC_POWEROFF_UNBIND,
+};
+
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
@@ -66,7 +74,7 @@ static int mmc_decode_cid(struct mmc_card *card)
/*
* The selection of the format here is based upon published
- * specs from sandisk and from what people have reported.
+ * specs from SanDisk and from what people have reported.
*/
switch (card->csd.mmca_vsn) {
case 0: /* MMC v1.0 - v1.2 */
@@ -109,6 +117,9 @@ static int mmc_decode_cid(struct mmc_card *card)
return -EINVAL;
}
+ /* some product names include trailing whitespace */
+ strim(card->cid.prod_name);
+
return 0;
}
@@ -449,7 +460,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
* There are two boot regions of equal size, defined in
* multiples of 128K.
*/
- if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) {
+ if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_host_can_access_boot(card->host)) {
for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) {
part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
mmc_part_add(card, part_size,
@@ -568,7 +579,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
* RPMB regions are defined in multiples of 128K.
*/
card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT];
- if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) {
+ if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_can_cmd23(card->host)) {
mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17,
EXT_CSD_PART_CONFIG_ACC_RPMB,
"rpmb", 0, false,
@@ -670,7 +681,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
u8 *ext_csd;
int err;
- if (!mmc_can_ext_csd(card))
+ if (!mmc_card_can_ext_csd(card))
return 0;
err = mmc_get_ext_csd(card, &ext_csd);
@@ -820,7 +831,7 @@ static ssize_t mmc_fwrev_show(struct device *dev,
card->ext_csd.fwrev);
}
-static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
+static DEVICE_ATTR(fwrev, 0444, mmc_fwrev_show, NULL);
static ssize_t mmc_dsr_show(struct device *dev,
struct device_attribute *attr,
@@ -836,7 +847,7 @@ static ssize_t mmc_dsr_show(struct device *dev,
return sysfs_emit(buf, "0x%x\n", 0x404);
}
-static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
+static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
@@ -949,7 +960,7 @@ static int mmc_select_powerclass(struct mmc_card *card)
int err, ddr;
/* Power class selection is supported for versions >= 4.0 */
- if (!mmc_can_ext_csd(card))
+ if (!mmc_card_can_ext_csd(card))
return 0;
bus_width = host->ios.bus_width;
@@ -1012,7 +1023,7 @@ static int mmc_select_bus_width(struct mmc_card *card)
unsigned idx, bus_width = 0;
int err = 0;
- if (!mmc_can_ext_csd(card) ||
+ if (!mmc_card_can_ext_csd(card) ||
!(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
return 0;
@@ -1533,7 +1544,7 @@ static int mmc_select_timing(struct mmc_card *card)
{
int err = 0;
- if (!mmc_can_ext_csd(card))
+ if (!mmc_card_can_ext_csd(card))
goto bus_speed;
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
@@ -1794,9 +1805,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/* set erase_arg */
- if (mmc_can_discard(card))
+ if (mmc_card_can_discard(card))
card->erase_arg = MMC_DISCARD_ARG;
- else if (mmc_can_trim(card))
+ else if (mmc_card_can_trim(card))
card->erase_arg = MMC_TRIM_ARG;
else
card->erase_arg = MMC_ERASE_ARG;
@@ -1945,7 +1956,7 @@ err:
return err;
}
-static int mmc_can_sleep(struct mmc_card *card)
+static bool mmc_card_can_sleep(struct mmc_card *card)
{
return card->ext_csd.rev >= 3;
}
@@ -2003,13 +2014,26 @@ out_release:
return err;
}
-static int mmc_can_poweroff_notify(const struct mmc_card *card)
+static bool mmc_card_can_poweroff_notify(const struct mmc_card *card)
{
return card &&
mmc_card_mmc(card) &&
(card->ext_csd.power_off_notification == EXT_CSD_POWER_ON);
}
+static bool mmc_host_can_poweroff_notify(const struct mmc_host *host,
+ enum mmc_poweroff_type pm_type)
+{
+ if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)
+ return true;
+
+ if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND &&
+ pm_type == MMC_POWEROFF_SUSPEND)
+ return true;
+
+ return pm_type == MMC_POWEROFF_SHUTDOWN;
+}
+
static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
{
unsigned int timeout = card->ext_csd.generic_cmd6_time;
@@ -2033,15 +2057,6 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
}
/*
- * Host is being removed. Free up the current card.
- */
-static void mmc_remove(struct mmc_host *host)
-{
- mmc_remove_card(host->card);
- host->card = NULL;
-}
-
-/*
* Card detection - card is alive.
*/
static int mmc_alive(struct mmc_host *host)
@@ -2066,7 +2081,8 @@ static void mmc_detect(struct mmc_host *host)
mmc_put_card(host->card, NULL);
if (err) {
- mmc_remove(host);
+ mmc_remove_card(host->card);
+ host->card = NULL;
mmc_claim_host(host);
mmc_detach_bus(host);
@@ -2104,26 +2120,33 @@ static int _mmc_flush_cache(struct mmc_host *host)
return err;
}
-static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
+static int _mmc_suspend(struct mmc_host *host, enum mmc_poweroff_type pm_type)
{
+ unsigned int notify_type = EXT_CSD_POWER_OFF_SHORT;
int err = 0;
- unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
- EXT_CSD_POWER_OFF_LONG;
+
+ if (pm_type == MMC_POWEROFF_SHUTDOWN)
+ notify_type = EXT_CSD_POWER_OFF_LONG;
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
- err = _mmc_flush_cache(host);
- if (err)
- goto out;
+ /*
+ * For the undervoltage case, we care more about device integrity.
+ * Avoid cache flush and notify the device to power off quickly.
+ */
+ if (pm_type != MMC_POWEROFF_UNDERVOLTAGE) {
+ err = _mmc_flush_cache(host);
+ if (err)
+ goto out;
+ }
- if (mmc_can_poweroff_notify(host->card) &&
- ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend ||
- (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND)))
+ if (mmc_card_can_poweroff_notify(host->card) &&
+ mmc_host_can_poweroff_notify(host, pm_type))
err = mmc_poweroff_notify(host->card, notify_type);
- else if (mmc_can_sleep(host->card))
+ else if (mmc_card_can_sleep(host->card))
err = mmc_sleep(host);
else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
@@ -2138,13 +2161,27 @@ out:
}
/*
+ * Host is being removed. Free up the current card and do a graceful power-off.
+ */
+static void mmc_remove(struct mmc_host *host)
+{
+ get_device(&host->card->dev);
+ mmc_remove_card(host->card);
+
+ _mmc_suspend(host, MMC_POWEROFF_UNBIND);
+
+ put_device(&host->card->dev);
+ host->card = NULL;
+}
+
+/*
* Suspend callback
*/
static int mmc_suspend(struct mmc_host *host)
{
int err;
- err = _mmc_suspend(host, true);
+ err = _mmc_suspend(host, MMC_POWEROFF_SUSPEND);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
@@ -2183,15 +2220,23 @@ static int mmc_shutdown(struct mmc_host *host)
int err = 0;
/*
- * In a specific case for poweroff notify, we need to resume the card
- * before we can shutdown it properly.
+ * In case of undervoltage, the card will be powered off (removed) by
+ * _mmc_handle_undervoltage()
*/
- if (mmc_can_poweroff_notify(host->card) &&
- !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
+ if (mmc_card_removed(host->card))
+ return 0;
+
+ /*
+ * If the card remains suspended at this point and it was done by using
+ * the sleep-cmd (CMD5), we may need to re-initialize it first, to allow
+ * us to send the preferred poweroff-notification cmd at shutdown.
+ */
+ if (mmc_card_can_poweroff_notify(host->card) &&
+ !mmc_host_can_poweroff_notify(host, MMC_POWEROFF_SUSPEND))
err = _mmc_resume(host);
if (!err)
- err = _mmc_suspend(host, false);
+ err = _mmc_suspend(host, MMC_POWEROFF_SHUTDOWN);
return err;
}
@@ -2215,7 +2260,7 @@ static int mmc_runtime_suspend(struct mmc_host *host)
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0;
- err = _mmc_suspend(host, true);
+ err = _mmc_suspend(host, MMC_POWEROFF_SUSPEND);
if (err)
pr_err("%s: error %d doing aggressive suspend\n",
mmc_hostname(host), err);
@@ -2238,14 +2283,12 @@ static int mmc_runtime_resume(struct mmc_host *host)
return 0;
}
-static int mmc_can_reset(struct mmc_card *card)
+static bool mmc_card_can_reset(struct mmc_card *card)
{
u8 rst_n_function;
rst_n_function = card->ext_csd.rst_n_function;
- if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
- return 0;
- return 1;
+ return ((rst_n_function & EXT_CSD_RST_N_EN_MASK) == EXT_CSD_RST_N_ENABLED);
}
static int _mmc_hw_reset(struct mmc_host *host)
@@ -2259,7 +2302,7 @@ static int _mmc_hw_reset(struct mmc_host *host)
_mmc_flush_cache(host);
if ((host->caps & MMC_CAP_HW_RESET) && host->ops->card_hw_reset &&
- mmc_can_reset(card)) {
+ mmc_card_can_reset(card)) {
/* If the card accept RST_n signal, send it. */
mmc_set_clock(host, host->f_init);
host->ops->card_hw_reset(host);
@@ -2273,6 +2316,55 @@ static int _mmc_hw_reset(struct mmc_host *host)
return mmc_init_card(host, card->ocr, card);
}
+/**
+ * _mmc_handle_undervoltage - Handle an undervoltage event for MMC/eMMC devices
+ * @host: MMC host structure
+ *
+ * This function is triggered when an undervoltage condition is detected.
+ * It attempts to transition the device into a low-power or safe state to
+ * prevent data corruption.
+ *
+ * Steps performed:
+ * - Perform an emergency suspend using EXT_CSD_POWER_OFF_SHORT if possible.
+ * - If power-off notify is not supported, fallback mechanisms like sleep or
+ * deselecting the card are attempted.
+ * - Cache flushing is skipped to reduce execution time.
+ * - Mark the card as removed to prevent further interactions after
+ * undervoltage.
+ *
+ * Note: This function does not handle host claiming or releasing. The caller
+ * must ensure that the host is properly claimed before calling this
+ * function and released afterward.
+ *
+ * Returns: 0 on success, or a negative error code if any step fails.
+ */
+static int _mmc_handle_undervoltage(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ int err;
+
+ /*
+ * Perform an emergency suspend to power off the eMMC quickly.
+ * This ensures the device enters a safe state before power is lost.
+ * We first attempt EXT_CSD_POWER_OFF_SHORT, but if power-off notify
+ * is not supported, we fall back to sleep mode or deselecting the card.
+ * Cache flushing is skipped to minimize delay.
+ */
+ err = _mmc_suspend(host, MMC_POWEROFF_UNDERVOLTAGE);
+ if (err)
+ pr_err("%s: undervoltage suspend failed: %pe\n",
+ mmc_hostname(host), ERR_PTR(err));
+
+ /*
+ * Mark the card as removed to prevent further operations.
+ * This ensures the system does not attempt to access the device
+ * after an undervoltage event, avoiding potential corruption.
+ */
+ mmc_card_set_removed(card);
+
+ return err;
+}
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -2285,6 +2377,7 @@ static const struct mmc_bus_ops mmc_ops = {
.hw_reset = _mmc_hw_reset,
.cache_enabled = _mmc_cache_enabled,
.flush_cache = _mmc_flush_cache,
+ .handle_undervoltage = _mmc_handle_undervoltage,
};
/*
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 5c8e62e8f331..a952cc8265af 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -383,7 +383,7 @@ int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
if (!card || !new_ext_csd)
return -EINVAL;
- if (!mmc_can_ext_csd(card))
+ if (!mmc_card_can_ext_csd(card))
return -EOPNOTSUPP;
/*
@@ -944,7 +944,7 @@ out:
return err;
}
-int mmc_can_ext_csd(struct mmc_card *card)
+bool mmc_card_can_ext_csd(struct mmc_card *card)
{
return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
}
@@ -1046,7 +1046,7 @@ int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms)
struct mmc_host *host = card->host;
int err;
- if (!mmc_can_sanitize(card)) {
+ if (!mmc_card_can_sanitize(card)) {
pr_warn("%s: Sanitize not supported\n", mmc_hostname(host));
return -EOPNOTSUPP;
}
@@ -1077,3 +1077,75 @@ int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms)
return err;
}
EXPORT_SYMBOL_GPL(mmc_sanitize);
+
+/**
+ * mmc_read_tuning() - read data blocks from the mmc
+ * @host: mmc host doing the read
+ * @blksz: data block size
+ * @blocks: number of blocks to read
+ *
+ * Read one or more blocks of data from the beginning of the mmc. This is a
+ * low-level helper for tuning operation. It is assumed that CMD23 can be used
+ * for multi-block read if the host supports it.
+ *
+ * Note: Allocate and free a temporary buffer to store the data read. The data
+ * is not available outside of the function, only the status of the read
+ * operation.
+ *
+ * Return: 0 in case of success, otherwise -EIO / -ENOMEM / -E2BIG
+ */
+int mmc_read_tuning(struct mmc_host *host, unsigned int blksz, unsigned int blocks)
+{
+ struct mmc_request mrq = {};
+ struct mmc_command sbc = {};
+ struct mmc_command cmd = {};
+ struct mmc_command stop = {};
+ struct mmc_data data = {};
+ struct scatterlist sg;
+ void *buf;
+ unsigned int len;
+
+ if (blocks > 1) {
+ if (mmc_host_can_cmd23(host)) {
+ mrq.sbc = &sbc;
+ sbc.opcode = MMC_SET_BLOCK_COUNT;
+ sbc.arg = blocks;
+ sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ }
+ cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
+ mrq.stop = &stop;
+ stop.opcode = MMC_STOP_TRANSMISSION;
+ stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ } else {
+ cmd.opcode = MMC_READ_SINGLE_BLOCK;
+ }
+
+ mrq.cmd = &cmd;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ mrq.data = &data;
+ data.flags = MMC_DATA_READ;
+ data.blksz = blksz;
+ data.blocks = blocks;
+ data.blk_addr = 0;
+ data.sg = &sg;
+ data.sg_len = 1;
+ data.timeout_ns = 1000000000;
+
+ if (check_mul_overflow(blksz, blocks, &len))
+ return -E2BIG;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ sg_init_one(&sg, buf, len);
+
+ mmc_wait_for_req(host, &mrq);
+ kfree(buf);
+
+ if (sbc.error || cmd.error || data.error)
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mmc_read_tuning);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 0df3ebd900d1..514c40ff4b4e 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -37,7 +37,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid);
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_can_ext_csd(struct mmc_card *card);
+bool mmc_card_can_ext_csd(struct mmc_card *card);
int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal);
bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
unsigned int timeout_ms);
diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c
index 4f4286b8e0f2..01d1e62c2ce7 100644
--- a/drivers/mmc/core/mmc_test.c
+++ b/drivers/mmc/core/mmc_test.c
@@ -180,20 +180,14 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
return mmc_set_blocklen(test->card, size);
}
-static bool mmc_test_card_cmd23(struct mmc_card *card)
-{
- return mmc_card_mmc(card) ||
- (mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT);
-}
-
static void mmc_test_prepare_sbc(struct mmc_test_card *test,
struct mmc_request *mrq, unsigned int blocks)
{
struct mmc_card *card = test->card;
- if (!mrq->sbc || !mmc_host_cmd23(card->host) ||
- !mmc_test_card_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
- (card->quirks & MMC_QUIRK_BLK_NO_CMD23)) {
+ if (!mrq->sbc || !mmc_host_can_cmd23(card->host) ||
+ !mmc_card_can_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) ||
+ mmc_card_blk_no_cmd23(card)) {
mrq->sbc = NULL;
return;
}
@@ -592,14 +586,11 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
rate = mmc_test_rate(tot, &ts);
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
- pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
- "%llu.%09u seconds (%u kB/s, %u KiB/s, "
- "%u.%02u IOPS, sg_len %d)\n",
- mmc_hostname(test->card->host), count, sectors, count,
- sectors >> 1, (sectors & 1 ? ".5" : ""),
- (u64)ts.tv_sec, (u32)ts.tv_nsec,
- rate / 1000, rate / 1024, iops / 100, iops % 100,
- test->area.sg_len);
+ pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took %ptSp seconds (%u kB/s, %u KiB/s, %u.%02u IOPS, sg_len %d)\n",
+ mmc_hostname(test->card->host), count, sectors, count,
+ sectors >> 1, (sectors & 1 ? ".5" : ""), &ts,
+ rate / 1000, rate / 1024, iops / 100, iops % 100,
+ test->area.sg_len);
mmc_test_save_transfer_result(test, count, sectors, ts, rate, iops);
}
@@ -1510,7 +1501,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test)
{
struct mmc_test_area *t = &test->area;
- if (!mmc_can_erase(test->card))
+ if (!mmc_card_can_erase(test->card))
return 0;
return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9,
@@ -1746,10 +1737,10 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
struct timespec64 ts1, ts2;
int ret;
- if (!mmc_can_trim(test->card))
+ if (!mmc_card_can_trim(test->card))
return RESULT_UNSUP_CARD;
- if (!mmc_can_erase(test->card))
+ if (!mmc_card_can_erase(test->card))
return RESULT_UNSUP_HOST;
for (sz = 512; sz < t->max_sz; sz <<= 1) {
@@ -1863,10 +1854,10 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
struct timespec64 ts1, ts2;
int ret;
- if (!mmc_can_trim(test->card))
+ if (!mmc_card_can_trim(test->card))
return RESULT_UNSUP_CARD;
- if (!mmc_can_erase(test->card))
+ if (!mmc_card_can_erase(test->card))
return RESULT_UNSUP_HOST;
for (sz = 512; sz <= t->max_sz; sz <<= 1) {
@@ -2114,7 +2105,7 @@ static int mmc_test_rw_multiple(struct mmc_test_card *test,
return 0;
/* prepare test area */
- if (mmc_can_erase(test->card) &&
+ if (mmc_card_can_erase(test->card) &&
tdata->prepare & MMC_TEST_PREP_ERASE) {
ret = mmc_erase(test->card, dev_addr,
size / 512, test->card->erase_arg);
@@ -2390,7 +2381,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
512, write);
if (use_sbc && t->blocks > 1 && !mrq->sbc) {
- ret = mmc_host_cmd23(host) ?
+ ret = mmc_host_can_cmd23(host) ?
RESULT_UNSUP_CARD :
RESULT_UNSUP_HOST;
goto out_free;
@@ -3080,10 +3071,9 @@ static int mtf_test_show(struct seq_file *sf, void *data)
seq_printf(sf, "Test %d: %d\n", gr->testcase + 1, gr->result);
list_for_each_entry(tr, &gr->tr_lst, link) {
- seq_printf(sf, "%u %d %llu.%09u %u %u.%02u\n",
- tr->count, tr->sectors,
- (u64)tr->ts.tv_sec, (u32)tr->ts.tv_nsec,
- tr->rate, tr->iops / 100, tr->iops % 100);
+ seq_printf(sf, "%u %d %ptSp %u %u.%02u\n",
+ tr->count, tr->sectors, &tr->ts, tr->rate,
+ tr->iops / 100, tr->iops % 100);
}
}
@@ -3218,12 +3208,12 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card)
mutex_lock(&mmc_test_lock);
- ret = __mmc_test_register_dbgfs_file(card, "test", S_IWUSR | S_IRUGO,
+ ret = __mmc_test_register_dbgfs_file(card, "test", 0644,
&mmc_test_fops_test);
if (ret)
goto err;
- ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
+ ret = __mmc_test_register_dbgfs_file(card, "testlist", 0444,
&mtf_testlist_fops);
if (ret)
goto err;
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 37cd858df0f4..4b47e6c3b04b 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -54,8 +54,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
else
bitmap_zero(values, nvalues);
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- reset_gpios->info, values);
+ gpiod_multi_set_value_cansleep(reset_gpios, values);
bitmap_free(values);
}
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 4d6844261912..284856c8f655 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -184,9 +184,9 @@ static void mmc_queue_setup_discard(struct mmc_card *card,
return;
lim->max_hw_discard_sectors = max_discard;
- if (mmc_can_secure_erase_trim(card))
+ if (mmc_card_can_secure_erase_trim(card))
lim->max_secure_erase_sectors = max_discard;
- if (mmc_can_trim(card) && card->erased_byte == 0)
+ if (mmc_card_can_trim(card) && card->erased_byte == 0)
lim->max_write_zeroes_sectors = max_discard;
/* granularity must not be greater than max. discard */
@@ -352,7 +352,7 @@ static struct gendisk *mmc_alloc_disk(struct mmc_queue *mq,
};
struct gendisk *disk;
- if (mmc_can_erase(card))
+ if (mmc_card_can_erase(card))
mmc_queue_setup_discard(card, &lim);
lim.max_hw_sectors = min(host->max_blk_count, host->max_req_size / 512);
@@ -441,7 +441,7 @@ struct gendisk *mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
else
mq->tag_set.queue_depth = MMC_QUEUE_DEPTH;
mq->tag_set.numa_node = NUMA_NO_NODE;
- mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING;
+ mq->tag_set.flags = BLK_MQ_F_BLOCKING;
mq->tag_set.nr_hw_queues = 1;
mq->tag_set.cmd_size = sizeof(struct mmc_queue_req);
mq->tag_set.driver_data = mq;
@@ -523,5 +523,5 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq)
{
struct request *req = mmc_queue_req_to_req(mqrq);
- return blk_rq_map_sg(mq->queue, req, mqrq->sg);
+ return blk_rq_map_sg(req, mqrq->sg);
}
diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h
index 89b512905be1..c417ed34c057 100644
--- a/drivers/mmc/core/quirks.h
+++ b/drivers/mmc/core/quirks.h
@@ -34,6 +34,22 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = {
MMC_QUIRK_BROKEN_SD_CACHE | MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY,
EXT_CSD_REV_ANY),
+ /*
+ * Swissbit series S46-u cards throw I/O errors during tuning requests
+ * after the initial tuning request expectedly times out. This has
+ * only been observed on cards manufactured on 01/2019 that are using
+ * Bay Trail host controllers.
+ */
+ _FIXUP_EXT("0016G", CID_MANFID_SWISSBIT, 0x5342, 2019, 1,
+ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
+ MMC_QUIRK_NO_UHS_DDR50_TUNING, EXT_CSD_REV_ANY),
+
+ /*
+ * Some SD cards reports discard support while they don't
+ */
+ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd,
+ MMC_QUIRK_BROKEN_SD_DISCARD),
+
END_FIXUP
};
@@ -137,12 +153,6 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = {
MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc,
MMC_QUIRK_TRIM_BROKEN),
- /*
- * Some SD cards reports discard support while they don't
- */
- MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, add_quirk_sd,
- MMC_QUIRK_BROKEN_SD_DISCARD),
-
END_FIXUP
};
diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c
index 3dae2e9b7978..a85179f1a4de 100644
--- a/drivers/mmc/core/regulator.c
+++ b/drivers/mmc/core/regulator.c
@@ -7,6 +7,7 @@
#include <linux/err.h>
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include <linux/mmc/host.h>
@@ -262,6 +263,82 @@ static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
#endif /* CONFIG_REGULATOR */
+/* To be called from a high-priority workqueue */
+void mmc_undervoltage_workfn(struct work_struct *work)
+{
+ struct mmc_supply *supply;
+ struct mmc_host *host;
+
+ supply = container_of(work, struct mmc_supply, uv_work);
+ host = container_of(supply, struct mmc_host, supply);
+
+ mmc_handle_undervoltage(host);
+}
+
+static int mmc_handle_regulator_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct mmc_supply *supply = container_of(nb, struct mmc_supply,
+ vmmc_nb);
+ struct mmc_host *host = container_of(supply, struct mmc_host, supply);
+ unsigned long flags;
+
+ switch (event) {
+ case REGULATOR_EVENT_UNDER_VOLTAGE:
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->undervoltage) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return NOTIFY_OK;
+ }
+
+ host->undervoltage = true;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ queue_work(system_highpri_wq, &host->supply.uv_work);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+/**
+ * mmc_regulator_register_undervoltage_notifier - Register for undervoltage
+ * events
+ * @host: MMC host
+ *
+ * To be called by a bus driver when a card supporting graceful shutdown
+ * is attached.
+ */
+void mmc_regulator_register_undervoltage_notifier(struct mmc_host *host)
+{
+ int ret;
+
+ if (IS_ERR_OR_NULL(host->supply.vmmc))
+ return;
+
+ host->supply.vmmc_nb.notifier_call = mmc_handle_regulator_event;
+ ret = regulator_register_notifier(host->supply.vmmc,
+ &host->supply.vmmc_nb);
+ if (ret)
+ dev_warn(mmc_dev(host), "Failed to register vmmc notifier: %d\n", ret);
+}
+
+/**
+ * mmc_regulator_unregister_undervoltage_notifier - Unregister undervoltage
+ * notifier
+ * @host: MMC host
+ */
+void mmc_regulator_unregister_undervoltage_notifier(struct mmc_host *host)
+{
+ if (IS_ERR_OR_NULL(host->supply.vmmc))
+ return;
+
+ regulator_unregister_notifier(host->supply.vmmc, &host->supply.vmmc_nb);
+ cancel_work_sync(&host->supply.uv_work);
+}
+
/**
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
* @mmc: the host to regulate
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index cc757b850e79..948948ca9b4a 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -11,6 +11,7 @@
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/string.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
@@ -95,6 +96,9 @@ void mmc_decode_cid(struct mmc_card *card)
card->cid.month = unstuff_bits(resp, 8, 4);
card->cid.year += 2000; /* SD cards year offset */
+
+ /* some product names may include trailing whitespace */
+ strim(card->cid.prod_name);
}
/*
@@ -355,7 +359,7 @@ static int mmc_read_switch(struct mmc_card *card)
}
if (status[13] & SD_MODE_HIGH_SPEED)
- card->sw_caps.hs_max_dtr = HIGH_SPEED_MAX_DTR;
+ card->sw_caps.hs_max_dtr = card->host->max_sd_hs_hz ?: HIGH_SPEED_MAX_DTR;
if (card->scr.sda_spec3) {
card->sw_caps.sd3_bus_mode = status[13];
@@ -451,7 +455,7 @@ static void sd_update_bus_speed_mode(struct mmc_card *card)
* If the host doesn't support any of the UHS-I modes, fallback on
* default speed.
*/
- if (!mmc_host_uhs(card->host)) {
+ if (!mmc_host_can_uhs(card->host)) {
card->sd_bus_speed = 0;
return;
}
@@ -550,7 +554,7 @@ static u32 sd_get_host_max_current(struct mmc_host *host)
static int sd_set_current_limit(struct mmc_card *card, u8 *status)
{
- int current_limit = SD_SET_CURRENT_NO_CHANGE;
+ int current_limit = SD_SET_CURRENT_LIMIT_200;
int err;
u32 max_current;
@@ -594,11 +598,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
else if (max_current >= 400 &&
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
current_limit = SD_SET_CURRENT_LIMIT_400;
- else if (max_current >= 200 &&
- card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
- current_limit = SD_SET_CURRENT_LIMIT_200;
- if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
+ if (current_limit != SD_SET_CURRENT_LIMIT_200) {
err = mmc_sd_switch(card, SD_SWITCH_SET, 3,
current_limit, status);
if (err)
@@ -614,6 +615,29 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
}
/*
+ * Determine if the card should tune or not.
+ */
+static bool mmc_sd_use_tuning(struct mmc_card *card)
+{
+ /*
+ * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
+ * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
+ */
+ if (mmc_host_is_spi(card->host))
+ return false;
+
+ switch (card->host->ios.timing) {
+ case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_SDR104:
+ return true;
+ case MMC_TIMING_UHS_DDR50:
+ return !mmc_card_no_uhs_ddr50_tuning(card);
+ }
+
+ return false;
+}
+
+/*
* UHS-I specific initialization procedure
*/
static int mmc_sd_init_uhs_card(struct mmc_card *card)
@@ -656,14 +680,7 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
if (err)
goto out;
- /*
- * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
- * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
- */
- if (!mmc_host_is_spi(card->host) &&
- (card->host->ios.timing == MMC_TIMING_UHS_SDR50 ||
- card->host->ios.timing == MMC_TIMING_UHS_DDR50 ||
- card->host->ios.timing == MMC_TIMING_UHS_SDR104)) {
+ if (mmc_sd_use_tuning(card)) {
err = mmc_execute_tuning(card);
/*
@@ -724,7 +741,7 @@ static ssize_t mmc_dsr_show(struct device *dev, struct device_attribute *attr,
return sysfs_emit(buf, "0x%x\n", 0x404);
}
-static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
+static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
@@ -847,7 +864,7 @@ try_again:
* to switch to 1.8V signaling level. If the card has failed
* repeatedly to switch however, skip this.
*/
- if (retries && mmc_host_uhs(host))
+ if (retries && mmc_host_can_uhs(host))
ocr |= SD_OCR_S18R;
/*
@@ -1489,7 +1506,7 @@ retry:
* signaling. Detect that situation and try to initialize a UHS-I (1.8V)
* transfer mode.
*/
- if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) &&
+ if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_can_uhs(host) &&
mmc_sd_card_using_v18(card) &&
host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
if (mmc_host_set_uhs_voltage(host) ||
@@ -1504,7 +1521,7 @@ retry:
}
/* Initialization sequence for UHS-I cards */
- if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
+ if (rocr & SD_ROCR_S18A && mmc_host_can_uhs(host)) {
err = mmc_sd_init_uhs_card(card);
if (err)
goto free_card;
@@ -1593,15 +1610,6 @@ free_card:
}
/*
- * Host is being removed. Free up the current card.
- */
-static void mmc_sd_remove(struct mmc_host *host)
-{
- mmc_remove_card(host->card);
- host->card = NULL;
-}
-
-/*
* Card detection - card is alive.
*/
static int mmc_sd_alive(struct mmc_host *host)
@@ -1626,7 +1634,8 @@ static void mmc_sd_detect(struct mmc_host *host)
mmc_put_card(host->card, NULL);
if (err) {
- mmc_sd_remove(host);
+ mmc_remove_card(host->card);
+ host->card = NULL;
mmc_claim_host(host);
mmc_detach_bus(host);
@@ -1727,6 +1736,19 @@ out:
}
/*
+ * Host is being removed. Free up the current card and do a graceful power-off.
+ */
+static void mmc_sd_remove(struct mmc_host *host)
+{
+ get_device(&host->card->dev);
+ mmc_remove_card(host->card);
+
+ _mmc_sd_suspend(host);
+
+ put_device(&host->card->dev);
+ host->card = NULL;
+}
+/*
* Callback for suspend
*/
static int mmc_sd_suspend(struct mmc_host *host)
diff --git a/drivers/mmc/core/sd_uhs2.c b/drivers/mmc/core/sd_uhs2.c
index 1c31d0dfa961..de17d1611290 100644
--- a/drivers/mmc/core/sd_uhs2.c
+++ b/drivers/mmc/core/sd_uhs2.c
@@ -91,8 +91,8 @@ static int sd_uhs2_phy_init(struct mmc_host *host)
err = host->ops->uhs2_control(host, UHS2_PHY_INIT);
if (err) {
- pr_err("%s: failed to initial phy for UHS-II!\n",
- mmc_hostname(host));
+ pr_debug("%s: failed to initial phy for UHS-II!\n",
+ mmc_hostname(host));
}
return err;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 9566837c9848..83085e76486a 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -198,7 +198,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
if (ret)
goto out;
- if (mmc_host_uhs(card->host)) {
+ if (mmc_host_can_uhs(card->host)) {
if (data & SDIO_UHS_DDR50)
card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_DDR50 | SD_MODE_UHS_SDR50
@@ -458,6 +458,8 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
if (mmc_card_sd_combo(card))
max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
+ max_dtr = min_not_zero(max_dtr, card->quirk_max_rate);
+
return max_dtr;
}
@@ -525,7 +527,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
* If the host doesn't support any of the UHS-I modes, fallback on
* default speed.
*/
- if (!mmc_host_uhs(card->host))
+ if (!mmc_host_can_uhs(card->host))
return 0;
bus_speed = SDIO_SPEED_SDR12;
@@ -667,7 +669,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
WARN_ON(!host->claimed);
/* to query card if 1.8V signalling is supported */
- if (mmc_host_uhs(host))
+ if (mmc_host_can_uhs(host))
ocr |= R4_18V_PRESENT;
try_again:
@@ -943,7 +945,11 @@ static void mmc_sdio_remove(struct mmc_host *host)
*/
static int mmc_sdio_alive(struct mmc_host *host)
{
- return mmc_select_card(host->card);
+ if (!mmc_host_is_spi(host))
+ return mmc_select_card(host->card);
+ else
+ return mmc_io_rw_direct(host->card, 0, 0, SDIO_CCCR_CCCR, 0,
+ NULL);
}
/*
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index b66b637e2d57..10799772494a 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -161,7 +161,7 @@ static int sdio_bus_probe(struct device *dev)
if (!id)
return -ENODEV;
- ret = dev_pm_domain_attach(dev, false);
+ ret = dev_pm_domain_attach(dev, 0);
if (ret)
return ret;
@@ -200,7 +200,6 @@ disable_runtimepm:
atomic_dec(&func->card->sdio_funcs_probed);
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_noidle(dev);
- dev_pm_domain_detach(dev, false);
return ret;
}
@@ -231,8 +230,6 @@ static void sdio_bus_remove(struct device *dev)
/* Then undo the runtime PM settings in sdio_bus_probe() */
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_sync(dev);
-
- dev_pm_domain_detach(dev, false);
}
static const struct dev_pm_ops sdio_bus_pm_ops = {
diff --git a/drivers/mmc/core/sdio_uart.c b/drivers/mmc/core/sdio_uart.c
index 6b7471dba3bf..7423a601e1e5 100644
--- a/drivers/mmc/core/sdio_uart.c
+++ b/drivers/mmc/core/sdio_uart.c
@@ -471,7 +471,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
port->icount.cts++;
tty = tty_port_tty_get(&port->port);
if (tty && C_CRTSCTS(tty)) {
- int cts = (status & UART_MSR_CTS);
+ bool cts = status & UART_MSR_CTS;
if (tty->hw_stopped) {
if (cts) {
tty->hw_stopped = false;
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 12247219e1c2..c5bc6268803e 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -159,18 +159,6 @@ int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
}
EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
-/* Register an alternate interrupt service routine for
- * the card-detect GPIO.
- */
-void mmc_gpio_set_cd_isr(struct mmc_host *host, irq_handler_t isr)
-{
- struct mmc_gpio *ctx = host->slot.handler_priv;
-
- WARN_ON(ctx->cd_gpio_isr);
- ctx->cd_gpio_isr = isr;
-}
-EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
-
/**
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
* @host: mmc host
@@ -240,13 +228,13 @@ int mmc_gpiod_set_cd_config(struct mmc_host *host, unsigned long config)
}
EXPORT_SYMBOL(mmc_gpiod_set_cd_config);
-bool mmc_can_gpio_cd(struct mmc_host *host)
+bool mmc_host_can_gpio_cd(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
return ctx->cd_gpio ? true : false;
}
-EXPORT_SYMBOL(mmc_can_gpio_cd);
+EXPORT_SYMBOL(mmc_host_can_gpio_cd);
/**
* mmc_gpiod_request_ro - request a gpio descriptor for write protection
@@ -287,10 +275,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
}
EXPORT_SYMBOL(mmc_gpiod_request_ro);
-bool mmc_can_gpio_ro(struct mmc_host *host)
+bool mmc_host_can_gpio_ro(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
return ctx->ro_gpio ? true : false;
}
-EXPORT_SYMBOL(mmc_can_gpio_ro);
+EXPORT_SYMBOL(mmc_host_can_gpio_ro);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 6824131b69b1..24f07df32a1a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -56,7 +56,7 @@ config MMC_STM32_SDMMC
config MMC_PXA
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
- depends on ARCH_PXA
+ depends on ARCH_PXA || COMPILE_TEST
help
This selects the Intel(R) PXA(R) Multimedia card Interface.
If you have a PXA(R) platform with a Multimedia Card slot,
@@ -250,6 +250,20 @@ config MMC_SDHCI_OF_DWCMSHC
If you have a controller with this interface, say Y or M here.
If unsure, say N.
+config MMC_SDHCI_OF_K1
+ tristate "SDHCI OF support for the SpacemiT K1 SoC"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ depends on COMMON_CLK
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ found in the SpacemiT K1 SoC.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDHCI_OF_SPARX5
tristate "SDHCI OF support for the MCHP Sparx5 SoC"
depends on MMC_SDHCI_PLTFM
@@ -345,7 +359,7 @@ config MMC_SDHCI_S3C
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
help
This selects the Secure Digital Host Controller Interface (SDHCI)
- often referrered to as the HSMMC block in some of the Samsung
+ often referred to as the HSMMC block in some of the Samsung
S3C6410, S5Pv210 and Exynos (Exynso4210, Exynos4412) SoCs.
If you have a controller with this interface (thereforeyou build for
@@ -387,7 +401,7 @@ config MMC_SDHCI_SPEAR
depends on OF
help
This selects the Secure Digital Host Controller Interface (SDHCI)
- often referrered to as the HSMMC block in some of the ST SPEAR range
+ often referred to as the HSMMC block in some of the ST SPEAR range
of SoC
If you have a controller with this interface, say Y or M here.
@@ -490,6 +504,7 @@ config MMC_MESON_MX_SDIO
depends on ARCH_MESON || COMPILE_TEST
depends on COMMON_CLK
depends on OF_ADDRESS
+ select REGMAP_MMIO
help
This selects support for the SD/MMC Host Controller on
Amlogic Meson6, Meson8 and Meson8b SoCs.
@@ -594,7 +609,7 @@ config MMC_SDHCI_MSM
config MMC_MXC
tristate "Freescale i.MX21/27/31 or MPC512x Multimedia Card support"
- depends on ARCH_MXC || PPC_MPC512x
+ depends on ARCH_MXC || PPC_MPC512x || COMPILE_TEST
help
This selects the Freescale i.MX21, i.MX27, i.MX31 or MPC512x
Multimedia Card Interface. If you have an i.MX or MPC512x platform
@@ -691,8 +706,8 @@ config MMC_TMIO_CORE
config MMC_SDHI
tristate "Renesas SDHI SD/SDIO controller support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
+ depends on (RESET_CONTROLLER && REGULATOR) || !OF
select MMC_TMIO_CORE
- select RESET_CONTROLLER if ARCH_RENESAS
help
This provides support for the SDHI SD/SDIO controller found in
Renesas SuperH, ARM and ARM64 based SoCs
@@ -852,7 +867,8 @@ config MMC_DW_PCI
config MMC_DW_ROCKCHIP
tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface"
- depends on MMC_DW && ARCH_ROCKCHIP
+ depends on MMC_DW
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
select MMC_DW_PLTFM
help
This selects support for Rockchip SoC specific extensions to the
@@ -934,8 +950,8 @@ config MMC_USHC
config MMC_WMT
tristate "Wondermedia SD/MMC Host Controller support"
- depends on ARCH_VT8500
- default y
+ depends on ARCH_VT8500 || COMPILE_TEST
+ default ARCH_VT8500
help
This selects support for the SD/MMC Host Controller on
Wondermedia WM8505/WM8650 based SoCs.
@@ -1097,6 +1113,20 @@ config MMC_OWL
This selects support for the SD/MMC Host Controller on
Actions Semi Owl SoCs.
+config MMC_LOONGSON2
+ tristate "Loongson-2K SD/SDIO/eMMC Host Interface support"
+ depends on LOONGARCH || COMPILE_TEST
+ depends on HAS_DMA
+ select REGMAP_MMIO
+ help
+ This selects support for the SD/SDIO/eMMC Host Controller on
+ Loongson-2K series CPUs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mmc_loongson2.
+
+ If unsure, say N.
+
config MMC_SDHCI_EXTERNAL_DMA
bool
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5147467ec825..5057fea8afb6 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o
obj-$(CONFIG_MMC_BCM2835) += bcm2835.o
obj-$(CONFIG_MMC_OWL) += owl-mmc.o
+obj-$(CONFIG_MMC_LOONGSON2) += loongson2-mmc.o
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o
+obj-$(CONFIG_MMC_SDHCI_OF_K1) += sdhci-of-k1.o
obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o
obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c
index b6b6dd677ae5..721db54739c1 100644
--- a/drivers/mmc/host/alcor.c
+++ b/drivers/mmc/host/alcor.c
@@ -20,6 +20,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/string_choices.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -208,7 +209,7 @@ static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read)
len = min(host->sg_miter.length, blksize);
dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n",
- read ? "read" : "write", blksize);
+ str_read_write(read), blksize);
host->sg_miter.consumed = len;
host->blocks--;
@@ -1083,7 +1084,7 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
struct alcor_sdmmc_host *host;
int ret;
- mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc) {
dev_err(&pdev->dev, "Can't allocate MMC\n");
return -ENOMEM;
@@ -1101,11 +1102,9 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(&pdev->dev, priv->irq,
alcor_irq, alcor_irq_thread, IRQF_SHARED,
DRV_NAME_ALCOR_PCI_SDMMC, host);
-
- if (ret) {
- dev_err(&pdev->dev, "Failed to get irq for data line\n");
- goto free_host;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to get irq for data line\n");
mutex_init(&host->cmd_mutex);
INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer);
@@ -1114,15 +1113,8 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
alcor_hw_init(host);
dev_set_drvdata(&pdev->dev, host);
- ret = mmc_add_host(mmc);
- if (ret)
- goto free_host;
- return 0;
-
-free_host:
- mmc_free_host(mmc);
- return ret;
+ return mmc_add_host(mmc);
}
static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev)
@@ -1135,10 +1127,8 @@ static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev)
alcor_hw_uninit(host);
mmc_remove_host(mmc);
- mmc_free_host(mmc);
}
-#ifdef CONFIG_PM_SLEEP
static int alcor_pci_sdmmc_suspend(struct device *dev)
{
struct alcor_sdmmc_host *host = dev_get_drvdata(dev);
@@ -1159,10 +1149,9 @@ static int alcor_pci_sdmmc_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend,
- alcor_pci_sdmmc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend,
+ alcor_pci_sdmmc_resume);
static const struct platform_device_id alcor_pci_sdmmc_ids[] = {
{
@@ -1180,7 +1169,7 @@ static struct platform_driver alcor_pci_sdmmc_driver = {
.driver = {
.name = DRV_NAME_ALCOR_PCI_SDMMC,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &alcor_mmc_pm_ops
+ .pm = pm_sleep_ptr(&alcor_mmc_pm_ops),
},
};
module_platform_driver(alcor_pci_sdmmc_driver);
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index fc360902729d..fdf6926ea468 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -38,6 +38,7 @@
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <linux/unaligned.h>
+#include <linux/string_choices.h>
#define ATMCI_MAX_NR_SLOTS 2
@@ -541,7 +542,6 @@ static int atmci_regs_show(struct seq_file *s, void *v)
memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
spin_unlock_bh(&host->lock);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
seq_printf(s, "MR:\t0x%08x%s%s ",
@@ -609,12 +609,12 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
if (!root)
return;
- debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops);
- debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
- debugfs_create_u32("state", S_IRUSR, root, &host->state);
- debugfs_create_xul("pending_events", S_IRUSR, root,
+ debugfs_create_file("regs", 0400, root, host, &atmci_regs_fops);
+ debugfs_create_file("req", 0400, root, slot, &atmci_req_fops);
+ debugfs_create_u32("state", 0400, root, &host->state);
+ debugfs_create_xul("pending_events", 0400, root,
&host->pending_events);
- debugfs_create_xul("completed_events", S_IRUSR, root,
+ debugfs_create_xul("completed_events", 0400, root,
&host->completed_events);
}
@@ -714,7 +714,7 @@ static inline unsigned int atmci_convert_chksize(struct atmel_mci *host,
static void atmci_timeout_timer(struct timer_list *t)
{
- struct atmel_mci *host = from_timer(host, t, timer);
+ struct atmel_mci *host = timer_container_of(host, t, timer);
struct device *dev = host->dev;
dev_dbg(dev, "software timeout\n");
@@ -1592,7 +1592,7 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
WARN_ON(host->cmd || host->data);
- del_timer(&host->timer);
+ timer_delete(&host->timer);
/*
* Update the MMC clock rate if necessary. This may be
@@ -1652,7 +1652,8 @@ static void atmci_command_complete(struct atmel_mci *host,
static void atmci_detect_change(struct timer_list *t)
{
- struct atmel_mci_slot *slot = from_timer(slot, t, detect_timer);
+ struct atmel_mci_slot *slot = timer_container_of(slot, t,
+ detect_timer);
bool present;
bool present_old;
@@ -2247,7 +2248,7 @@ static int atmci_init_slot(struct atmel_mci *host,
struct atmel_mci_slot *slot;
int ret;
- mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*slot));
if (!mmc)
return -ENOMEM;
@@ -2263,7 +2264,7 @@ static int atmci_init_slot(struct atmel_mci *host,
"slot[%u]: bus_width=%u, detect_pin=%d, "
"detect_is_active_high=%s, wp_pin=%d\n",
id, slot_data->bus_width, desc_to_gpio(slot_data->detect_pin),
- !gpiod_is_active_low(slot_data->detect_pin) ? "true" : "false",
+ str_true_false(!gpiod_is_active_low(slot_data->detect_pin)),
desc_to_gpio(slot_data->wp_pin));
mmc->ops = &atmci_ops;
@@ -2320,10 +2321,8 @@ static int atmci_init_slot(struct atmel_mci *host,
host->slot[id] = slot;
mmc_regulator_get_supply(mmc);
ret = mmc_add_host(mmc);
- if (ret) {
- mmc_free_host(mmc);
+ if (ret)
return ret;
- }
if (slot->detect_pin) {
timer_setup(&slot->detect_timer, atmci_detect_change, 0);
@@ -2357,11 +2356,10 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot,
if (slot->detect_pin) {
free_irq(gpiod_to_irq(slot->detect_pin), slot);
- del_timer_sync(&slot->detect_timer);
+ timer_delete_sync(&slot->detect_timer);
}
slot->host->slot[id] = NULL;
- mmc_free_host(slot->mmc);
}
static int atmci_configure_dma(struct atmel_mci *host)
@@ -2499,8 +2497,10 @@ static int atmci_probe(struct platform_device *pdev)
/* Get MCI capabilities and set operations according to it */
atmci_get_cap(host);
ret = atmci_configure_dma(host);
- if (ret == -EPROBE_DEFER)
+ if (ret == -EPROBE_DEFER) {
+ clk_disable_unprepare(host->mck);
goto err_dma_probe_defer;
+ }
if (ret == 0) {
host->prepare_data = &atmci_prepare_data_dma;
host->submit_data = &atmci_submit_data_dma;
@@ -2567,7 +2567,6 @@ static int atmci_probe(struct platform_device *pdev)
dev_info(dev, "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
host->mapbase, irq, nr_slots);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -2583,7 +2582,7 @@ err_init_slot:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
- del_timer_sync(&host->timer);
+ timer_delete_sync(&host->timer);
if (!IS_ERR(host->dma.chan))
dma_release_channel(host->dma.chan);
err_dma_probe_defer:
@@ -2611,7 +2610,7 @@ static void atmci_remove(struct platform_device *pdev)
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
atmci_readl(host, ATMCI_SR);
- del_timer_sync(&host->timer);
+ timer_delete_sync(&host->timer);
if (!IS_ERR(host->dma.chan))
dma_release_channel(host->dma.chan);
@@ -2623,7 +2622,6 @@ static void atmci_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev);
}
-#ifdef CONFIG_PM
static int atmci_runtime_suspend(struct device *dev)
{
struct atmel_mci *host = dev_get_drvdata(dev);
@@ -2643,12 +2641,10 @@ static int atmci_runtime_resume(struct device *dev)
return clk_prepare_enable(host->mck);
}
-#endif
static const struct dev_pm_ops atmci_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
};
static struct platform_driver atmci_driver = {
@@ -2658,7 +2654,7 @@ static struct platform_driver atmci_driver = {
.name = "atmel_mci",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = atmci_dt_ids,
- .pm = &atmci_dev_pm_ops,
+ .pm = pm_ptr(&atmci_dev_pm_ops),
},
};
module_platform_driver(atmci_driver);
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index 057d42307832..cc6e05f9b96f 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -937,11 +937,10 @@ static int au1xmmc_probe(struct platform_device *pdev)
struct resource *r;
int ret, iflag;
- mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc) {
dev_err(&pdev->dev, "no memory for mmc_host\n");
- ret = -ENOMEM;
- goto out0;
+ return -ENOMEM;
}
host = mmc_priv(mmc);
@@ -953,14 +952,14 @@ static int au1xmmc_probe(struct platform_device *pdev)
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "no mmio defined\n");
- goto out1;
+ return ret;
}
host->ioarea = request_mem_region(r->start, resource_size(r),
pdev->name);
if (!host->ioarea) {
dev_err(&pdev->dev, "mmio already in use\n");
- goto out1;
+ return ret;
}
host->iobase = ioremap(r->start, 0x3c);
@@ -1109,9 +1108,6 @@ out3:
out2:
release_resource(host->ioarea);
kfree(host->ioarea);
-out1:
- mmc_free_host(mmc);
-out0:
return ret;
}
@@ -1151,15 +1147,12 @@ static void au1xmmc_remove(struct platform_device *pdev)
iounmap((void *)host->iobase);
release_resource(host->ioarea);
kfree(host->ioarea);
-
- mmc_free_host(host->mmc);
}
}
-#ifdef CONFIG_PM
-static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
+static int au1xmmc_suspend(struct device *dev)
{
- struct au1xmmc_host *host = platform_get_drvdata(pdev);
+ struct au1xmmc_host *host = dev_get_drvdata(dev);
__raw_writel(0, HOST_CONFIG2(host));
__raw_writel(0, HOST_CONFIG(host));
@@ -1170,27 +1163,24 @@ static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int au1xmmc_resume(struct platform_device *pdev)
+static int au1xmmc_resume(struct device *dev)
{
- struct au1xmmc_host *host = platform_get_drvdata(pdev);
+ struct au1xmmc_host *host = dev_get_drvdata(dev);
au1xmmc_reset_controller(host);
return 0;
}
-#else
-#define au1xmmc_suspend NULL
-#define au1xmmc_resume NULL
-#endif
+
+static DEFINE_SIMPLE_DEV_PM_OPS(au1xmmc_pmops, au1xmmc_suspend, au1xmmc_resume);
static struct platform_driver au1xmmc_driver = {
.probe = au1xmmc_probe,
.remove = au1xmmc_remove,
- .suspend = au1xmmc_suspend,
- .resume = au1xmmc_resume,
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .pm = pm_sleep_ptr(&au1xmmc_pmops),
},
};
diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c
index 7847f0c8b465..ee63835b3ca0 100644
--- a/drivers/mmc/host/bcm2835.c
+++ b/drivers/mmc/host/bcm2835.c
@@ -44,6 +44,7 @@
#include <linux/scatterlist.h>
#include <linux/time.h>
#include <linux/workqueue.h>
+#include <linux/string_choices.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -391,8 +392,7 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
if (time_after(jiffies, wait_max)) {
dev_err(dev, "PIO %s timeout - EDM %08x\n",
- is_read ? "read" : "write",
- edm);
+ str_read_write(is_read), edm);
hsts = SDHSTS_REW_TIME_OUT;
break;
}
@@ -435,12 +435,12 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host)
SDHSTS_CRC7_ERROR |
SDHSTS_FIFO_ERROR)) {
dev_err(dev, "%s transfer error - HSTS %08x\n",
- is_read ? "read" : "write", sdhsts);
+ str_read_write(is_read), sdhsts);
host->data->error = -EILSEQ;
} else if ((sdhsts & (SDHSTS_CMD_TIME_OUT |
SDHSTS_REW_TIME_OUT))) {
dev_err(dev, "%s timeout error - HSTS %08x\n",
- is_read ? "read" : "write", sdhsts);
+ str_read_write(is_read), sdhsts);
host->data->error = -ETIMEDOUT;
}
}
@@ -503,7 +503,8 @@ void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data)
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
- dma_unmap_sg(dma_chan->device->dev, data->sg, sg_len, dir_data);
+ dma_unmap_sg(dma_chan->device->dev, data->sg, data->sg_len,
+ dir_data);
return;
}
@@ -1343,6 +1344,25 @@ static int bcm2835_add_host(struct bcm2835_host *host)
return 0;
}
+static int bcm2835_suspend(struct device *dev)
+{
+ struct bcm2835_host *host = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(host->clk);
+
+ return 0;
+}
+
+static int bcm2835_resume(struct device *dev)
+{
+ struct bcm2835_host *host = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(host->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835_pm_ops, bcm2835_suspend,
+ bcm2835_resume);
+
static int bcm2835_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1352,7 +1372,7 @@ static int bcm2835_probe(struct platform_device *pdev)
int ret;
dev_dbg(dev, "%s\n", __func__);
- mmc = mmc_alloc_host(sizeof(*host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -1431,7 +1451,6 @@ err:
dev_dbg(dev, "%s -> err %d\n", __func__, ret);
if (host->dma_chan_rxtx)
dma_release_channel(host->dma_chan_rxtx);
- mmc_free_host(mmc);
return ret;
}
@@ -1454,8 +1473,6 @@ static void bcm2835_remove(struct platform_device *pdev)
if (host->dma_chan_rxtx)
dma_release_channel(host->dma_chan_rxtx);
-
- mmc_free_host(mmc);
}
static const struct of_device_id bcm2835_match[] = {
@@ -1471,6 +1488,7 @@ static struct platform_driver bcm2835_driver = {
.name = "sdhost-bcm2835",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = bcm2835_match,
+ .pm = pm_ptr(&bcm2835_pm_ops),
},
};
module_platform_driver(bcm2835_driver);
diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c
index 2e2ff984f0b3..1373deb3f531 100644
--- a/drivers/mmc/host/cavium-thunderx.c
+++ b/drivers/mmc/host/cavium-thunderx.c
@@ -72,7 +72,7 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
if (ret)
return ret;
- ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ ret = pcim_request_all_regions(pdev, KBUILD_MODNAME);
if (ret)
return ret;
@@ -164,7 +164,6 @@ error:
}
}
clk_disable_unprepare(host->clk);
- pci_release_regions(pdev);
return ret;
}
@@ -183,7 +182,6 @@ static void thunder_mmc_remove(struct pci_dev *pdev)
writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host));
clk_disable_unprepare(host->clk);
- pci_release_regions(pdev);
}
static const struct pci_device_id thunder_mmc_id_table[] = {
diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c
index 95a41983c6c0..9a55db0e657c 100644
--- a/drivers/mmc/host/cavium.c
+++ b/drivers/mmc/host/cavium.c
@@ -1012,7 +1012,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
struct mmc_host *mmc;
int ret, id;
- mmc = mmc_alloc_host(sizeof(struct cvm_mmc_slot), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*slot));
if (!mmc)
return -ENOMEM;
@@ -1022,7 +1022,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
ret = cvm_mmc_of_parse(dev, slot);
if (ret < 0)
- goto error;
+ return ret;
id = ret;
/* Set up host parameters */
@@ -1066,12 +1066,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
if (ret) {
dev_err(dev, "mmc_add_host() returned %d\n", ret);
slot->host->slot[id] = NULL;
- goto error;
}
- return 0;
-
-error:
- mmc_free_host(slot->mmc);
return ret;
}
@@ -1079,6 +1074,5 @@ int cvm_mmc_of_slot_remove(struct cvm_mmc_slot *slot)
{
mmc_remove_host(slot->mmc);
slot->host->slot[slot->bus_id] = NULL;
- mmc_free_host(slot->mmc);
return 0;
}
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index d741c1f9cf87..31daec787495 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/string_choices.h>
#include "cb710-mmc.h"
#define CB710_MMC_REQ_TIMEOUT_MS 2000
@@ -215,7 +216,7 @@ static void cb710_mmc_set_transfer_size(struct cb710_slot *slot,
((count - 1) << 16)|(blocksize - 1));
dev_vdbg(cb710_slot_dev(slot), "set up for %zu block%s of %zu bytes\n",
- count, count == 1 ? "" : "s", blocksize);
+ count, str_plural(count), blocksize);
}
static void cb710_mmc_fifo_hack(struct cb710_slot *slot)
@@ -663,25 +664,25 @@ static const struct mmc_host_ops cb710_mmc_host = {
.get_cd = cb710_mmc_get_cd,
};
-#ifdef CONFIG_PM
-
-static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
+static int cb710_mmc_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
cb710_mmc_enable_irq(slot, 0, ~0);
return 0;
}
-static int cb710_mmc_resume(struct platform_device *pdev)
+static int cb710_mmc_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
cb710_mmc_enable_irq(slot, 0, ~0);
return 0;
}
-#endif /* CONFIG_PM */
+static DEFINE_SIMPLE_DEV_PM_OPS(cb710_mmc_pmops, cb710_mmc_suspend, cb710_mmc_resume);
static int cb710_mmc_init(struct platform_device *pdev)
{
@@ -692,7 +693,7 @@ static int cb710_mmc_init(struct platform_device *pdev)
int err;
u32 val;
- mmc = mmc_alloc_host(sizeof(*reader), cb710_slot_dev(slot));
+ mmc = devm_mmc_alloc_host(cb710_slot_dev(slot), sizeof(*reader));
if (!mmc)
return -ENOMEM;
@@ -741,7 +742,6 @@ err_free_mmc:
dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err);
cb710_set_irq_handler(slot, NULL);
- mmc_free_host(mmc);
return err;
}
@@ -764,18 +764,15 @@ static void cb710_mmc_exit(struct platform_device *pdev)
cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0);
cancel_work_sync(&reader->finish_req_bh_work);
-
- mmc_free_host(mmc);
}
static struct platform_driver cb710_mmc_driver = {
- .driver.name = "cb710-mmc",
+ .driver = {
+ .name = "cb710-mmc",
+ .pm = pm_sleep_ptr(&cb710_mmc_pmops),
+ },
.probe = cb710_mmc_init,
.remove = cb710_mmc_exit,
-#ifdef CONFIG_PM
- .suspend = cb710_mmc_suspend,
- .resume = cb710_mmc_resume,
-#endif
};
module_platform_driver(cb710_mmc_driver);
diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c
index d5f4b6972f63..5a467098a0d6 100644
--- a/drivers/mmc/host/cqhci-crypto.c
+++ b/drivers/mmc/host/cqhci-crypto.c
@@ -25,22 +25,16 @@ static const struct cqhci_crypto_alg_entry {
static inline struct cqhci_host *
cqhci_host_from_crypto_profile(struct blk_crypto_profile *profile)
{
- struct mmc_host *mmc =
- container_of(profile, struct mmc_host, crypto_profile);
-
- return mmc->cqe_private;
+ return mmc_from_crypto_profile(profile)->cqe_private;
}
-static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
- const union cqhci_crypto_cfg_entry *cfg,
- int slot)
+static void cqhci_crypto_program_key(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg,
+ int slot)
{
u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg);
int i;
- if (cq_host->ops->program_key)
- return cq_host->ops->program_key(cq_host, cfg, slot);
-
/* Clear CFGE */
cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0]));
@@ -55,7 +49,6 @@ static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
/* Write dword 16, which includes the new value of CFGE */
cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]),
slot_offset + 16 * sizeof(cfg->reg_val[0]));
- return 0;
}
static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,
@@ -72,7 +65,6 @@ static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,
int i;
int cap_idx = -1;
union cqhci_crypto_cfg_entry cfg = {};
- int err;
BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) {
@@ -92,17 +84,17 @@ static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,
if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) {
/* In XTS mode, the blk_crypto_key's size is already doubled */
- memcpy(cfg.crypto_key, key->raw, key->size/2);
+ memcpy(cfg.crypto_key, key->bytes, key->size/2);
memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2,
- key->raw + key->size/2, key->size/2);
+ key->bytes + key->size/2, key->size/2);
} else {
- memcpy(cfg.crypto_key, key->raw, key->size);
+ memcpy(cfg.crypto_key, key->bytes, key->size);
}
- err = cqhci_crypto_program_key(cq_host, &cfg, slot);
+ cqhci_crypto_program_key(cq_host, &cfg, slot);
memzero_explicit(&cfg, sizeof(cfg));
- return err;
+ return 0;
}
static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
@@ -113,7 +105,8 @@ static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
*/
union cqhci_crypto_cfg_entry cfg = {};
- return cqhci_crypto_program_key(cq_host, &cfg, slot);
+ cqhci_crypto_program_key(cq_host, &cfg, slot);
+ return 0;
}
static int cqhci_crypto_keyslot_evict(struct blk_crypto_profile *profile,
@@ -170,7 +163,6 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
struct mmc_host *mmc = cq_host->mmc;
struct device *dev = mmc_dev(mmc);
struct blk_crypto_profile *profile = &mmc->crypto_profile;
- unsigned int num_keyslots;
unsigned int cap_idx;
enum blk_crypto_mode_num blk_mode_num;
unsigned int slot;
@@ -180,6 +172,9 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
goto out;
+ if (cq_host->ops->uses_custom_crypto_profile)
+ goto profile_initialized;
+
cq_host->crypto_capabilities.reg_val =
cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
@@ -198,9 +193,8 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
* CCAP.CFGC is off by one, so the actual number of crypto
* configurations (a.k.a. keyslots) is CCAP.CFGC + 1.
*/
- num_keyslots = cq_host->crypto_capabilities.config_count + 1;
-
- err = devm_blk_crypto_profile_init(dev, profile, num_keyslots);
+ err = devm_blk_crypto_profile_init(
+ dev, profile, cq_host->crypto_capabilities.config_count + 1);
if (err)
goto out;
@@ -210,6 +204,8 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
/* Unfortunately, CQHCI crypto only supports 32 DUN bits. */
profile->max_dun_bytes_supported = 4;
+ profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
+
/*
* Cache all the crypto capabilities and advertise the supported crypto
* modes and data unit sizes to the block layer.
@@ -228,9 +224,11 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
cq_host->crypto_cap_array[cap_idx].sdus_mask * 512;
}
+profile_initialized:
+
/* Clear all the keyslots so that we start in a known state. */
- for (slot = 0; slot < num_keyslots; slot++)
- cqhci_crypto_clear_keyslot(cq_host, slot);
+ for (slot = 0; slot < profile->num_slots; slot++)
+ profile->ll_ops.keyslot_evict(profile, NULL, slot);
/* CQHCI crypto requires the use of 128-bit task descriptors. */
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index fab9d74445ba..3668856531c1 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -93,6 +93,7 @@
/* send status config 1 */
#define CQHCI_SSC1 0x40
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
+#define CQHCI_SSC1_CIT_MASK GENMASK(15, 0)
/* send status config 2 */
#define CQHCI_SSC2 0x44
@@ -289,13 +290,11 @@ struct cqhci_host_ops {
u64 *data);
void (*pre_enable)(struct mmc_host *mmc);
void (*post_disable)(struct mmc_host *mmc);
-#ifdef CONFIG_MMC_CRYPTO
- int (*program_key)(struct cqhci_host *cq_host,
- const union cqhci_crypto_cfg_entry *cfg, int slot);
-#endif
void (*set_tran_desc)(struct cqhci_host *cq_host, u8 **desc,
dma_addr_t addr, int len, bool end, bool dma64);
-
+#ifdef CONFIG_MMC_CRYPTO
+ bool uses_custom_crypto_profile;
+#endif
};
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index cde4c4339ab7..42b0118a45a8 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -145,17 +145,17 @@
#define MAX_NR_SG 16
static unsigned rw_threshold = 32;
-module_param(rw_threshold, uint, S_IRUGO);
+module_param(rw_threshold, uint, 0444);
MODULE_PARM_DESC(rw_threshold,
"Read/Write threshold. Default = 32");
static unsigned poll_threshold = 128;
-module_param(poll_threshold, uint, S_IRUGO);
+module_param(poll_threshold, uint, 0444);
MODULE_PARM_DESC(poll_threshold,
"Polling transaction size threshold. Default = 128");
static unsigned poll_loopcount = 32;
-module_param(poll_loopcount, uint, S_IRUGO);
+module_param(poll_loopcount, uint, 0444);
MODULE_PARM_DESC(poll_loopcount,
"Maximum polling loop count. Default = 32");
@@ -588,7 +588,7 @@ static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req)
cpu_relax();
}
if (mmcst1 & MMCST1_BUSY) {
- dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n");
+ dev_err(mmc_dev(host->mmc), "still BUSY? bad ...\n");
req->cmd->error = -ETIMEDOUT;
mmc_request_done(mmc, req);
return;
@@ -1203,7 +1203,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
if (!mem)
return -EBUSY;
- mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -1212,19 +1212,16 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
host->mem_res = mem;
host->base = devm_ioremap(&pdev->dev, mem->start, mem_size);
- if (!host->base) {
- ret = -ENOMEM;
- goto ioremap_fail;
- }
+ if (!host->base)
+ return -ENOMEM;
host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- goto clk_get_fail;
- }
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
ret = clk_prepare_enable(host->clk);
if (ret)
- goto clk_prepare_enable_fail;
+ return ret;
host->mmc_input_clk = clk_get_rate(host->clk);
@@ -1336,10 +1333,6 @@ cpu_freq_fail:
parse_fail:
dma_probe_defer:
clk_disable_unprepare(host->clk);
-clk_prepare_enable_fail:
-clk_get_fail:
-ioremap_fail:
- mmc_free_host(mmc);
return ret;
}
@@ -1352,10 +1345,8 @@ static void davinci_mmcsd_remove(struct platform_device *pdev)
mmc_davinci_cpufreq_deregister(host);
davinci_release_dma_channels(host);
clk_disable_unprepare(host->clk);
- mmc_free_host(host->mmc);
}
-#ifdef CONFIG_PM
static int davinci_mmcsd_suspend(struct device *dev)
{
struct mmc_davinci_host *host = dev_get_drvdata(dev);
@@ -1381,21 +1372,14 @@ static int davinci_mmcsd_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops davinci_mmcsd_pm = {
- .suspend = davinci_mmcsd_suspend,
- .resume = davinci_mmcsd_resume,
-};
-
-#define davinci_mmcsd_pm_ops (&davinci_mmcsd_pm)
-#else
-#define davinci_mmcsd_pm_ops NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(davinci_mmcsd_pm_ops,
+ davinci_mmcsd_suspend, davinci_mmcsd_resume);
static struct platform_driver davinci_mmcsd_driver = {
.driver = {
.name = "davinci_mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = davinci_mmcsd_pm_ops,
+ .pm = pm_sleep_ptr(&davinci_mmcsd_pm_ops),
.of_match_table = davinci_mmc_dt_ids,
},
.probe = davinci_mmcsd_probe,
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 53d32d0f2709..384609671a9a 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -27,6 +27,8 @@ enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS5420_SMU,
DW_MCI_TYPE_EXYNOS7,
DW_MCI_TYPE_EXYNOS7_SMU,
+ DW_MCI_TYPE_EXYNOS7870,
+ DW_MCI_TYPE_EXYNOS7870_SMU,
DW_MCI_TYPE_ARTPEC8,
};
@@ -70,6 +72,12 @@ static struct dw_mci_exynos_compatible {
.compatible = "samsung,exynos7-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU,
}, {
+ .compatible = "samsung,exynos7870-dw-mshc",
+ .ctrl_type = DW_MCI_TYPE_EXYNOS7870,
+ }, {
+ .compatible = "samsung,exynos7870-dw-mshc-smu",
+ .ctrl_type = DW_MCI_TYPE_EXYNOS7870_SMU,
+ }, {
.compatible = "axis,artpec8-dw-mshc",
.ctrl_type = DW_MCI_TYPE_ARTPEC8,
},
@@ -85,6 +93,8 @@ static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
return EXYNOS4210_FIXED_CIU_CLK_DIV;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
else
@@ -100,7 +110,8 @@ static void dw_mci_exynos_config_smu(struct dw_mci *host)
* set for non-ecryption mode at this time.
*/
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
- priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
@@ -126,6 +137,12 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
}
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
+ /* Quirk needed for certain Exynos SoCs */
+ host->quirks |= DW_MMC_QUIRK_FIFO64_32;
+ }
+
if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) {
/* Quirk needed for the ARTPEC-8 SoC */
host->quirks |= DW_MMC_QUIRK_EXTENDED_TMOUT;
@@ -143,6 +160,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -152,6 +171,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -168,7 +189,6 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
}
-#ifdef CONFIG_PM
static int dw_mci_exynos_runtime_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
@@ -182,9 +202,7 @@ static int dw_mci_exynos_runtime_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PM_SLEEP
/**
* dw_mci_exynos_suspend_noirq - Exynos-specific suspend code
* @dev: Device to suspend (this device)
@@ -222,6 +240,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -230,6 +250,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -240,7 +262,6 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
{
@@ -409,6 +430,8 @@ static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
else
@@ -422,6 +445,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -429,6 +454,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -443,6 +470,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -453,6 +482,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -632,6 +663,10 @@ static const struct of_device_id dw_mci_exynos_match[] = {
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7-dw-mshc-smu",
.data = &exynos_drv_data, },
+ { .compatible = "samsung,exynos7870-dw-mshc",
+ .data = &exynos_drv_data, },
+ { .compatible = "samsung,exynos7870-dw-mshc-smu",
+ .data = &exynos_drv_data, },
{ .compatible = "axis,artpec8-dw-mshc",
.data = &artpec_drv_data, },
{},
@@ -673,11 +708,8 @@ static void dw_mci_exynos_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_exynos_pmops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
- dw_mci_exynos_resume_noirq)
- SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
- dw_mci_exynos_runtime_resume,
- NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, dw_mci_exynos_resume_noirq)
+ RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_exynos_runtime_resume, NULL)
};
static struct platform_driver dw_mci_exynos_pltfm_driver = {
@@ -687,7 +719,7 @@ static struct platform_driver dw_mci_exynos_pltfm_driver = {
.name = "dwmmc_exynos",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_exynos_match,
- .pm = &dw_mci_exynos_pmops,
+ .pm = pm_ptr(&dw_mci_exynos_pmops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c
index cce174b5249b..5791a975a944 100644
--- a/drivers/mmc/host/dw_mmc-hi3798mv200.c
+++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c
@@ -181,7 +181,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host)
{
struct dw_mci_hi3798mv200_priv *priv;
struct device_node *np = host->dev->of_node;
- int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -199,15 +198,12 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host)
return dev_err_probe(host->dev, PTR_ERR(priv->drive_clk),
"failed to get enabled ciu-drive clock\n");
- priv->crg_reg = syscon_regmap_lookup_by_phandle(np, "hisilicon,sap-dll-reg");
+ priv->crg_reg = syscon_regmap_lookup_by_phandle_args(np, "hisilicon,sap-dll-reg",
+ 1, &priv->sap_dll_offset);
if (IS_ERR(priv->crg_reg))
return dev_err_probe(host->dev, PTR_ERR(priv->crg_reg),
"failed to get CRG reg\n");
- ret = of_property_read_u32_index(np, "hisilicon,sap-dll-reg", 1, &priv->sap_dll_offset);
- if (ret)
- return dev_err_probe(host->dev, ret, "failed to get sample DLL register offset\n");
-
host->priv = priv;
return 0;
}
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index 0311a37dd4ab..ad6aa1aea549 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -461,11 +461,8 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_k3_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)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
};
static struct platform_driver dw_mci_k3_pltfm_driver = {
@@ -475,7 +472,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = {
.name = "dwmmc_k3",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_k3_match,
- .pm = &dw_mci_k3_dev_pm_ops,
+ .pm = pm_ptr(&dw_mci_k3_dev_pm_ops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index e7ab699f488e..092cc99175af 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -75,11 +75,8 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
}
static const struct dev_pm_ops dw_mci_pci_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)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
};
static const struct pci_device_id dw_mci_pci_id[] = {
@@ -94,7 +91,7 @@ static struct pci_driver dw_mci_pci_driver = {
.probe = dw_mci_pci_probe,
.remove = dw_mci_pci_remove,
.driver = {
- .pm = &dw_mci_pci_dev_pm_ops,
+ .pm = pm_ptr(&dw_mci_pci_dev_pm_ops),
},
};
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index baa23b517731..62c68cda1e21 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/hw_bitfield.h>
#include <linux/mmc/host.h>
#include <linux/of_address.h>
#include <linux/mmc/slot-gpio.h>
@@ -18,14 +19,14 @@
#define RK3288_CLKGEN_DIV 2
#define SDMMC_TIMING_CON0 0x130
#define SDMMC_TIMING_CON1 0x134
+#define SDMMC_MISC_CON 0x138
+#define MEM_CLK_AUTOGATE_ENABLE BIT(5)
#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DEGREE_OFFSET 1
#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
-#define HIWORD_UPDATE(val, mask, shift) \
- ((val) << (shift) | (mask) << ((shift) + 16))
static const unsigned int freqs[] = { 100000, 200000, 300000, 400000 };
@@ -43,7 +44,7 @@ struct dw_mci_rockchip_priv_data {
*/
static int rockchip_mmc_get_internal_phase(struct dw_mci *host, bool sample)
{
- unsigned long rate = clk_get_rate(host->ciu_clk);
+ unsigned long rate = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
u32 raw_value;
u16 degrees;
u32 delay_num = 0;
@@ -86,7 +87,7 @@ static int rockchip_mmc_get_phase(struct dw_mci *host, bool sample)
static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int degrees)
{
- unsigned long rate = clk_get_rate(host->ciu_clk);
+ unsigned long rate = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
u8 nineties, remainder;
u8 delay_num;
u32 raw_value;
@@ -148,9 +149,11 @@ static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int
raw_value |= nineties;
if (sample)
- mci_writel(host, TIMING_CON1, HIWORD_UPDATE(raw_value, 0x07ff, 1));
+ mci_writel(host, TIMING_CON1,
+ FIELD_PREP_WM16(GENMASK(11, 1), raw_value));
else
- mci_writel(host, TIMING_CON0, HIWORD_UPDATE(raw_value, 0x07ff, 1));
+ mci_writel(host, TIMING_CON0,
+ FIELD_PREP_WM16(GENMASK(11, 1), raw_value));
dev_dbg(host->dev, "set %s_phase(%d) delay_nums=%u actual_degrees=%d\n",
sample ? "sample" : "drv", degrees, delay_num,
@@ -469,6 +472,7 @@ static int dw_mci_rk3576_parse_dt(struct dw_mci *host)
static int dw_mci_rockchip_init(struct dw_mci *host)
{
+ struct dw_mci_rockchip_priv_data *priv = host->priv;
int ret, i;
/* It is slot 8 on Rockchip SoCs */
@@ -493,6 +497,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
dev_warn(host->dev, "no valid minimum freq: %d\n", ret);
}
+ if (priv->internal_phase)
+ mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE);
+
return 0;
}
@@ -568,11 +575,8 @@ static void dw_mci_rockchip_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_rockchip_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)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL)
};
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
@@ -582,7 +586,7 @@ static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.name = "dwmmc_rockchip",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_rockchip_match,
- .pm = &dw_mci_rockchip_dev_pm_ops,
+ .pm = pm_ptr(&dw_mci_rockchip_dev_pm_ops),
},
};
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 3cbda98d08d2..9e74b675e92d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -175,12 +175,12 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
if (!root)
return;
- debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops);
- debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops);
- debugfs_create_u32("state", S_IRUSR, root, &host->state);
- debugfs_create_xul("pending_events", S_IRUSR, root,
+ debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops);
+ debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops);
+ debugfs_create_u32("state", 0400, root, &host->state);
+ debugfs_create_xul("pending_events", 0400, root,
&host->pending_events);
- debugfs_create_xul("completed_events", S_IRUSR, root,
+ debugfs_create_xul("completed_events", 0400, root,
&host->completed_events);
#ifdef CONFIG_FAULT_INJECTION
fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc);
@@ -1875,8 +1875,7 @@ static void dw_mci_init_fault(struct dw_mci *host)
{
host->fail_data_crc = (struct fault_attr) FAULT_ATTR_INITIALIZER;
- hrtimer_init(&host->fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- host->fault_timer.function = dw_mci_fault_timer;
+ hrtimer_setup(&host->fault_timer, dw_mci_fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
}
#else
static void dw_mci_init_fault(struct dw_mci *host)
@@ -2041,10 +2040,10 @@ static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
* Really be certain that the timer has stopped. This is a bit of
* paranoia and could only really happen if we had really bad
* interrupt latency and the interrupt routine and timeout were
- * running concurrently so that the del_timer() in the interrupt
+ * running concurrently so that the timer_delete() in the interrupt
* handler couldn't run.
*/
- WARN_ON(del_timer_sync(&host->cto_timer));
+ WARN_ON(timer_delete_sync(&host->cto_timer));
clear_bit(EVENT_CMD_COMPLETE, &host->pending_events);
return true;
@@ -2056,7 +2055,7 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
return false;
/* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */
- WARN_ON(del_timer_sync(&host->dto_timer));
+ WARN_ON(timer_delete_sync(&host->dto_timer));
clear_bit(EVENT_DATA_COMPLETE, &host->pending_events);
return true;
@@ -2579,6 +2578,91 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
}
}
+static void dw_mci_push_data64_32(struct dw_mci *host, void *buf, int cnt)
+{
+ struct mmc_data *data = host->data;
+ int init_cnt = cnt;
+
+ /* try and push anything in the part_buf */
+ if (unlikely(host->part_buf_count)) {
+ int len = dw_mci_push_part_bytes(host, buf, cnt);
+
+ buf += len;
+ cnt -= len;
+
+ if (host->part_buf_count == 8) {
+ mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
+ host->part_buf_count = 0;
+ }
+ }
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x7)) {
+ while (cnt >= 8) {
+ u64 aligned_buf[16];
+ int len = min(cnt & -8, (int)sizeof(aligned_buf));
+ int items = len >> 3;
+ int i;
+ /* memcpy from input buffer into aligned buffer */
+ memcpy(aligned_buf, buf, len);
+ buf += len;
+ cnt -= len;
+ /* push data from aligned buffer into fifo */
+ for (i = 0; i < items; ++i)
+ mci_fifo_l_writeq(host->fifo_reg, aligned_buf[i]);
+ }
+ } else
+#endif
+ {
+ u64 *pdata = buf;
+
+ for (; cnt >= 8; cnt -= 8)
+ mci_fifo_l_writeq(host->fifo_reg, *pdata++);
+ buf = pdata;
+ }
+ /* put anything remaining in the part_buf */
+ if (cnt) {
+ dw_mci_set_part_bytes(host, buf, cnt);
+ /* Push data if we have reached the expected data length */
+ if ((data->bytes_xfered + init_cnt) ==
+ (data->blksz * data->blocks))
+ mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
+ }
+}
+
+static void dw_mci_pull_data64_32(struct dw_mci *host, void *buf, int cnt)
+{
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x7)) {
+ while (cnt >= 8) {
+ /* pull data from fifo into aligned buffer */
+ u64 aligned_buf[16];
+ int len = min(cnt & -8, (int)sizeof(aligned_buf));
+ int items = len >> 3;
+ int i;
+
+ for (i = 0; i < items; ++i)
+ aligned_buf[i] = mci_fifo_l_readq(host->fifo_reg);
+
+ /* memcpy from aligned buffer into output buffer */
+ memcpy(buf, aligned_buf, len);
+ buf += len;
+ cnt -= len;
+ }
+ } else
+#endif
+ {
+ u64 *pdata = buf;
+
+ for (; cnt >= 8; cnt -= 8)
+ *pdata++ = mci_fifo_l_readq(host->fifo_reg);
+ buf = pdata;
+ }
+ if (cnt) {
+ host->part_buf = mci_fifo_l_readq(host->fifo_reg);
+ dw_mci_pull_final_bytes(host, buf, cnt);
+ }
+}
+
static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
{
int len;
@@ -2704,7 +2788,7 @@ done:
static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
{
- del_timer(&host->cto_timer);
+ timer_delete(&host->cto_timer);
if (!host->cmd_status)
host->cmd_status = status;
@@ -2748,13 +2832,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
dw_mci_cmd_interrupt(host, pending);
spin_unlock(&host->irq_lock);
- del_timer(&host->cmd11_timer);
+ timer_delete(&host->cmd11_timer);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
spin_lock(&host->irq_lock);
- del_timer(&host->cto_timer);
+ timer_delete(&host->cto_timer);
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending;
smp_wmb(); /* drain writebuffer */
@@ -2767,7 +2851,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
spin_lock(&host->irq_lock);
if (host->quirks & DW_MMC_QUIRK_EXTENDED_TMOUT)
- del_timer(&host->dto_timer);
+ timer_delete(&host->dto_timer);
/* if there is an error report DATA_ERROR */
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
@@ -2788,7 +2872,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_DATA_OVER) {
spin_lock(&host->irq_lock);
- del_timer(&host->dto_timer);
+ timer_delete(&host->dto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
@@ -2924,7 +3008,7 @@ static int dw_mci_init_slot(struct dw_mci *host)
struct dw_mci_slot *slot;
int ret;
- mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
+ mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot));
if (!mmc)
return -ENOMEM;
@@ -2940,18 +3024,18 @@ static int dw_mci_init_slot(struct dw_mci *host)
/*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
if (ret)
- goto err_host_allocated;
+ return ret;
if (!mmc->ocr_avail)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
ret = mmc_of_parse(mmc);
if (ret)
- goto err_host_allocated;
+ return ret;
ret = dw_mci_init_slot_caps(slot);
if (ret)
- goto err_host_allocated;
+ return ret;
/* Useful defaults if platform data is unset. */
if (host->use_dma == TRANS_MODE_IDMAC) {
@@ -2981,17 +3065,13 @@ static int dw_mci_init_slot(struct dw_mci *host)
ret = mmc_add_host(mmc);
if (ret)
- goto err_host_allocated;
+ return ret;
#if defined(CONFIG_DEBUG_FS)
dw_mci_init_debugfs(slot);
#endif
return 0;
-
-err_host_allocated:
- mmc_free_host(mmc);
- return ret;
}
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot)
@@ -2999,7 +3079,6 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot)
/* Debugfs stuff is cleaned up by mmc core */
mmc_remove_host(slot->mmc);
slot->host->slot = NULL;
- mmc_free_host(slot->mmc);
}
static void dw_mci_init_dma(struct dw_mci *host)
@@ -3041,9 +3120,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
host->dma_64bit_address = 1;
dev_info(host->dev,
"IDMAC supports 64-bit address mode.\n");
- if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
- dma_set_coherent_mask(host->dev,
- DMA_BIT_MASK(64));
+ if (dma_set_mask_and_coherent(host->dev, DMA_BIT_MASK(64)))
+ dev_info(host->dev, "Fail to set 64-bit DMA mask");
} else {
/* host supports IDMAC in 32-bit address mode */
host->dma_64bit_address = 0;
@@ -3095,7 +3173,7 @@ no_dma:
static void dw_mci_cmd11_timer(struct timer_list *t)
{
- struct dw_mci *host = from_timer(host, t, cmd11_timer);
+ struct dw_mci *host = timer_container_of(host, t, cmd11_timer);
if (host->state != STATE_SENDING_CMD11) {
dev_warn(host->dev, "Unexpected CMD11 timeout\n");
@@ -3109,7 +3187,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t)
static void dw_mci_cto_timer(struct timer_list *t)
{
- struct dw_mci *host = from_timer(host, t, cto_timer);
+ struct dw_mci *host = timer_container_of(host, t, cto_timer);
unsigned long irqflags;
u32 pending;
@@ -3164,7 +3242,7 @@ exit:
static void dw_mci_dto_timer(struct timer_list *t)
{
- struct dw_mci *host = from_timer(host, t, dto_timer);
+ struct dw_mci *host = timer_container_of(host, t, dto_timer);
unsigned long irqflags;
u32 pending;
@@ -3379,8 +3457,13 @@ int dw_mci_probe(struct dw_mci *host)
width = 16;
host->data_shift = 1;
} else if (i == 2) {
- host->push_data = dw_mci_push_data64;
- host->pull_data = dw_mci_pull_data64;
+ if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) {
+ host->push_data = dw_mci_push_data64_32;
+ host->pull_data = dw_mci_pull_data64_32;
+ } else {
+ host->push_data = dw_mci_push_data64;
+ host->pull_data = dw_mci_pull_data64;
+ }
width = 64;
host->data_shift = 3;
} else {
@@ -3533,7 +3616,7 @@ int dw_mci_runtime_suspend(struct device *dev)
clk_disable_unprepare(host->ciu_clk);
if (host->slot &&
- (mmc_can_gpio_cd(host->slot->mmc) ||
+ (mmc_host_can_gpio_cd(host->slot->mmc) ||
!mmc_card_is_removable(host->slot->mmc)))
clk_disable_unprepare(host->biu_clk);
@@ -3547,7 +3630,7 @@ int dw_mci_runtime_resume(struct device *dev)
struct dw_mci *host = dev_get_drvdata(dev);
if (host->slot &&
- (mmc_can_gpio_cd(host->slot->mmc) ||
+ (mmc_host_can_gpio_cd(host->slot->mmc) ||
!mmc_card_is_removable(host->slot->mmc))) {
ret = clk_prepare_enable(host->biu_clk);
if (ret)
@@ -3601,7 +3684,7 @@ int dw_mci_runtime_resume(struct device *dev)
err:
if (host->slot &&
- (mmc_can_gpio_cd(host->slot->mmc) ||
+ (mmc_host_can_gpio_cd(host->slot->mmc) ||
!mmc_card_is_removable(host->slot->mmc)))
clk_disable_unprepare(host->biu_clk);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 6447b916990d..648b4a5641bf 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -281,6 +281,8 @@ struct dw_mci_board {
/* Support for longer data read timeout */
#define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0)
+/* Force 32-bit access to the FIFO */
+#define DW_MMC_QUIRK_FIFO64_32 BIT(1)
#define DW_MMC_240A 0x240a
#define DW_MMC_280A 0x280a
@@ -472,6 +474,31 @@ struct dw_mci_board {
#define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value)
#define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value)
+/*
+ * Some dw_mmc devices have 64-bit FIFOs, but expect them to be
+ * accessed using two 32-bit accesses. If such controller is used
+ * with a 64-bit kernel, this has to be done explicitly.
+ */
+static inline u64 mci_fifo_l_readq(void __iomem *addr)
+{
+ u64 ans;
+ u32 proxy[2];
+
+ proxy[0] = mci_fifo_readl(addr);
+ proxy[1] = mci_fifo_readl(addr + 4);
+ memcpy(&ans, proxy, 8);
+ return ans;
+}
+
+static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
+{
+ u32 proxy[2];
+
+ memcpy(proxy, &value, 8);
+ mci_fifo_writel(addr, proxy[0]);
+ mci_fifo_writel(addr + 4, proxy[1]);
+}
+
/* Register access macros */
#define mci_readl(dev, reg) \
readl_relaxed((dev)->regs + SDMMC_##reg)
@@ -514,6 +541,9 @@ extern void dw_mci_remove(struct dw_mci *host);
#ifdef CONFIG_PM
extern int dw_mci_runtime_suspend(struct device *device);
extern int dw_mci_runtime_resume(struct device *device);
+#else
+static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOTSUPP; }
+static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; }
#endif
/**
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 596012d5afac..6a0d0250d47b 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -641,7 +641,8 @@ poll_timeout:
static void jz4740_mmc_timeout(struct timer_list *t)
{
- struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer);
+ struct jz4740_mmc_host *host = timer_container_of(host, t,
+ timeout_timer);
if (!test_and_clear_bit(0, &host->waiting))
return;
@@ -862,7 +863,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
if (host->req && cmd && irq_reg) {
if (test_and_clear_bit(0, &host->waiting)) {
- del_timer(&host->timeout_timer);
+ timer_delete(&host->timeout_timer);
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
cmd->error = -ETIMEDOUT;
@@ -1042,7 +1043,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
struct mmc_host *mmc;
struct jz4740_mmc_host *host;
- mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc) {
dev_err(&pdev->dev, "Failed to alloc mmc host structure\n");
return -ENOMEM;
@@ -1054,31 +1055,24 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev);
ret = mmc_of_parse(mmc);
- if (ret) {
- dev_err_probe(&pdev->dev, ret, "could not parse device properties\n");
- goto err_free_host;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "could not parse device properties\n");
mmc_regulator_get_supply(mmc);
host->irq = platform_get_irq(pdev, 0);
- if (host->irq < 0) {
- ret = host->irq;
- goto err_free_host;
- }
+ if (host->irq < 0)
+ return host->irq;
host->clk = devm_clk_get(&pdev->dev, "mmc");
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- dev_err(&pdev->dev, "Failed to get mmc clock\n");
- goto err_free_host;
- }
+ if (IS_ERR(host->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->clk),
+ "Failed to get mmc clock\n");
host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->mem_res);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto err_free_host;
- }
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
mmc->ops = &jz4740_mmc_ops;
if (!mmc->f_max)
@@ -1118,10 +1112,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
dev_name(&pdev->dev), host);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
- goto err_free_host;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n");
jz4740_mmc_clock_disable(host);
timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
@@ -1152,9 +1144,6 @@ err_release_dma:
jz4740_mmc_release_dma_channels(host);
err_free_irq:
free_irq(host->irq, host);
-err_free_host:
- mmc_free_host(mmc);
-
return ret;
}
@@ -1162,7 +1151,7 @@ static void jz4740_mmc_remove(struct platform_device *pdev)
{
struct jz4740_mmc_host *host = platform_get_drvdata(pdev);
- del_timer_sync(&host->timeout_timer);
+ timer_delete_sync(&host->timeout_timer);
jz4740_mmc_set_irq_enabled(host, 0xff, false);
jz4740_mmc_reset(host);
@@ -1172,8 +1161,6 @@ static void jz4740_mmc_remove(struct platform_device *pdev)
if (host->use_dma)
jz4740_mmc_release_dma_channels(host);
-
- mmc_free_host(host->mmc);
}
static int jz4740_mmc_suspend(struct device *dev)
diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c
index b338ccfa8f33..d2f19c2dc673 100644
--- a/drivers/mmc/host/litex_mmc.c
+++ b/drivers/mmc/host/litex_mmc.c
@@ -506,11 +506,6 @@ use_polling:
return 0;
}
-static void litex_mmc_free_host_wrapper(void *mmc)
-{
- mmc_free_host(mmc);
-}
-
static int litex_mmc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -525,15 +520,10 @@ static int litex_mmc_probe(struct platform_device *pdev)
* If for some reason we need to modify max_blk_count, we must also
* re-calculate `max_[req,seg]_size = max_blk_size * max_blk_count;`
*/
- mmc = mmc_alloc_host(sizeof(struct litex_mmc_host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
- ret = devm_add_action_or_reset(dev, litex_mmc_free_host_wrapper, mmc);
- if (ret)
- return dev_err_probe(dev, ret,
- "Can't register mmc_free_host action\n");
-
host = mmc_priv(mmc);
host->mmc = mmc;
diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c
new file mode 100644
index 000000000000..da3daab5f3d6
--- /dev/null
+++ b/drivers/mmc/host/loongson2-mmc.c
@@ -0,0 +1,1030 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Loongson-2K MMC/SDIO controller driver
+ *
+ * Copyright (C) 2018-2025 Loongson Technology Corporation Limited.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitrev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define LOONGSON2_MMC_REG_CTL 0x00 /* Control Register */
+#define LOONGSON2_MMC_REG_PRE 0x04 /* Prescaler Register */
+#define LOONGSON2_MMC_REG_CARG 0x08 /* Command Register */
+#define LOONGSON2_MMC_REG_CCTL 0x0c /* Command Control Register */
+#define LOONGSON2_MMC_REG_CSTS 0x10 /* Command Status Register */
+#define LOONGSON2_MMC_REG_RSP0 0x14 /* Command Response Register 0 */
+#define LOONGSON2_MMC_REG_RSP1 0x18 /* Command Response Register 1 */
+#define LOONGSON2_MMC_REG_RSP2 0x1c /* Command Response Register 2 */
+#define LOONGSON2_MMC_REG_RSP3 0x20 /* Command Response Register 3 */
+#define LOONGSON2_MMC_REG_TIMER 0x24 /* Data Timeout Register */
+#define LOONGSON2_MMC_REG_BSIZE 0x28 /* Block Size Register */
+#define LOONGSON2_MMC_REG_DCTL 0x2c /* Data Control Register */
+#define LOONGSON2_MMC_REG_DCNT 0x30 /* Data Counter Register */
+#define LOONGSON2_MMC_REG_DSTS 0x34 /* Data Status Register */
+#define LOONGSON2_MMC_REG_FSTS 0x38 /* FIFO Status Register */
+#define LOONGSON2_MMC_REG_INT 0x3c /* Interrupt Register */
+#define LOONGSON2_MMC_REG_DATA 0x40 /* Data Register */
+#define LOONGSON2_MMC_REG_IEN 0x64 /* Interrupt Enable Register */
+
+/* EMMC DLL Mode Registers */
+#define LOONGSON2_MMC_REG_DLLVAL 0xf0 /* DLL Master Lock-value Register */
+#define LOONGSON2_MMC_REG_DLLCTL 0xf4 /* DLL Control Register */
+#define LOONGSON2_MMC_REG_DELAY 0xf8 /* DLL Delayed Parameter Register */
+#define LOONGSON2_MMC_REG_SEL 0xfc /* Bus Mode Selection Register */
+
+/* Exclusive DMA R/W Registers */
+#define LOONGSON2_MMC_REG_WDMA_LO 0x400
+#define LOONGSON2_MMC_REG_WDMA_HI 0x404
+#define LOONGSON2_MMC_REG_RDMA_LO 0x800
+#define LOONGSON2_MMC_REG_RDMA_HI 0x804
+
+/* Bitfields of control register */
+#define LOONGSON2_MMC_CTL_ENCLK BIT(0)
+#define LOONGSON2_MMC_CTL_EXTCLK BIT(1)
+#define LOONGSON2_MMC_CTL_RESET BIT(8)
+
+/* Bitfields of prescaler register */
+#define LOONGSON2_MMC_PRE GENMASK(9, 0)
+#define LOONGSON2_MMC_PRE_EN BIT(31)
+
+/* Bitfields of command control register */
+#define LOONGSON2_MMC_CCTL_INDEX GENMASK(5, 0)
+#define LOONGSON2_MMC_CCTL_HOST BIT(6)
+#define LOONGSON2_MMC_CCTL_START BIT(8)
+#define LOONGSON2_MMC_CCTL_WAIT_RSP BIT(9)
+#define LOONGSON2_MMC_CCTL_LONG_RSP BIT(10)
+#define LOONGSON2_MMC_CCTL_ABORT BIT(12)
+#define LOONGSON2_MMC_CCTL_CHECK BIT(13)
+#define LOONGSON2_MMC_CCTL_SDIO BIT(14)
+#define LOONGSON2_MMC_CCTL_CMD6 BIT(18)
+
+/* Bitfields of command status register */
+#define LOONGSON2_MMC_CSTS_INDEX GENMASK(7, 0)
+#define LOONGSON2_MMC_CSTS_ON BIT(8)
+#define LOONGSON2_MMC_CSTS_RSP BIT(9)
+#define LOONGSON2_MMC_CSTS_TIMEOUT BIT(10)
+#define LOONGSON2_MMC_CSTS_END BIT(11)
+#define LOONGSON2_MMC_CSTS_CRC_ERR BIT(12)
+#define LOONGSON2_MMC_CSTS_AUTO_STOP BIT(13)
+#define LOONGSON2_MMC_CSTS_FIN BIT(14)
+
+/* Bitfields of data timeout register */
+#define LOONGSON2_MMC_DTIMR GENMASK(23, 0)
+
+/* Bitfields of block size register */
+#define LOONGSON2_MMC_BSIZE GENMASK(11, 0)
+
+/* Bitfields of data control register */
+#define LOONGSON2_MMC_DCTL_BNUM GENMASK(11, 0)
+#define LOONGSON2_MMC_DCTL_START BIT(14)
+#define LOONGSON2_MMC_DCTL_ENDMA BIT(15)
+#define LOONGSON2_MMC_DCTL_WIDE BIT(16)
+#define LOONGSON2_MMC_DCTL_RWAIT BIT(17)
+#define LOONGSON2_MMC_DCTL_IO_SUSPEND BIT(18)
+#define LOONGSON2_MMC_DCTL_IO_RESUME BIT(19)
+#define LOONGSON2_MMC_DCTL_RW_RESUME BIT(20)
+#define LOONGSON2_MMC_DCTL_8BIT_BUS BIT(26)
+
+/* Bitfields of sata counter register */
+#define LOONGSON2_MMC_DCNT_BNUM GENMASK(11, 0)
+#define LOONGSON2_MMC_DCNT_BYTE GENMASK(23, 12)
+
+/* Bitfields of command status register */
+#define LOONGSON2_MMC_DSTS_RXON BIT(0)
+#define LOONGSON2_MMC_DSTS_TXON BIT(1)
+#define LOONGSON2_MMC_DSTS_SBITERR BIT(2)
+#define LOONGSON2_MMC_DSTS_BUSYFIN BIT(3)
+#define LOONGSON2_MMC_DSTS_XFERFIN BIT(4)
+#define LOONGSON2_MMC_DSTS_DTIMEOUT BIT(5)
+#define LOONGSON2_MMC_DSTS_RXCRC BIT(6)
+#define LOONGSON2_MMC_DSTS_TXCRC BIT(7)
+#define LOONGSON2_MMC_DSTS_IRQ BIT(8)
+#define LOONGSON2_MMC_DSTS_START BIT(13)
+#define LOONGSON2_MMC_DSTS_RESUME BIT(15)
+#define LOONGSON2_MMC_DSTS_SUSPEND BIT(16)
+
+/* Bitfields of FIFO Status Register */
+#define LOONGSON2_MMC_FSTS_TXFULL BIT(11)
+
+/* Bitfields of interrupt register */
+#define LOONGSON2_MMC_INT_DFIN BIT(0)
+#define LOONGSON2_MMC_INT_DTIMEOUT BIT(1)
+#define LOONGSON2_MMC_INT_RXCRC BIT(2)
+#define LOONGSON2_MMC_INT_TXCRC BIT(3)
+#define LOONGSON2_MMC_INT_PROGERR BIT(4)
+#define LOONGSON2_MMC_INT_SDIOIRQ BIT(5)
+#define LOONGSON2_MMC_INT_CSENT BIT(6)
+#define LOONGSON2_MMC_INT_CTIMEOUT BIT(7)
+#define LOONGSON2_MMC_INT_RESPCRC BIT(8)
+#define LOONGSON2_MMC_INT_BUSYEND BIT(9)
+
+/* Bitfields of interrupt enable register */
+#define LOONGSON2_MMC_IEN_DFIN BIT(0)
+#define LOONGSON2_MMC_IEN_DTIMEOUT BIT(1)
+#define LOONGSON2_MMC_IEN_RXCRC BIT(2)
+#define LOONGSON2_MMC_IEN_TXCRC BIT(3)
+#define LOONGSON2_MMC_IEN_PROGERR BIT(4)
+#define LOONGSON2_MMC_IEN_SDIOIRQ BIT(5)
+#define LOONGSON2_MMC_IEN_CSENT BIT(6)
+#define LOONGSON2_MMC_IEN_CTIMEOUT BIT(7)
+#define LOONGSON2_MMC_IEN_RESPCRC BIT(8)
+#define LOONGSON2_MMC_IEN_BUSYEND BIT(9)
+
+#define LOONGSON2_MMC_IEN_ALL GENMASK(9, 0)
+#define LOONGSON2_MMC_INT_CLEAR GENMASK(9, 0)
+
+/* Bitfields of DLL master lock-value register */
+#define LOONGSON2_MMC_DLLVAL_DONE BIT(8)
+
+/* Bitfields of DLL control register */
+#define LOONGSON2_MMC_DLLCTL_TIME GENMASK(7, 0)
+#define LOONGSON2_MMC_DLLCTL_INCRE GENMASK(15, 8)
+#define LOONGSON2_MMC_DLLCTL_START GENMASK(23, 16)
+#define LOONGSON2_MMC_DLLCTL_CLK_MODE BIT(24)
+#define LOONGSON2_MMC_DLLCTL_START_BIT BIT(25)
+#define LOONGSON2_MMC_DLLCTL_TIME_BPASS GENMASK(29, 26)
+
+#define LOONGSON2_MMC_DELAY_PAD GENMASK(7, 0)
+#define LOONGSON2_MMC_DELAY_RD GENMASK(15, 8)
+
+#define LOONGSON2_MMC_SEL_DATA BIT(0) /* 0: SDR, 1: DDR */
+#define LOONGSON2_MMC_SEL_BUS BIT(0) /* 0: EMMC, 1: SDIO */
+
+/* Internal dma controller registers */
+
+/* Bitfields of Global Configuration Register */
+#define LOONGSON2_MMC_DMA_64BIT_EN BIT(0) /* 1: 64 bit support */
+#define LOONGSON2_MMC_DMA_UNCOHERENT_EN BIT(1) /* 0: cache, 1: uncache */
+#define LOONGSON2_MMC_DMA_ASK_VALID BIT(2)
+#define LOONGSON2_MMC_DMA_START BIT(3) /* DMA start operation */
+#define LOONGSON2_MMC_DMA_STOP BIT(4) /* DMA stop operation */
+#define LOONGSON2_MMC_DMA_CONFIG_MASK GENMASK_ULL(4, 0) /* DMA controller config bits mask */
+
+/* Bitfields of ndesc_addr field of HW descriptor */
+#define LOONGSON2_MMC_DMA_DESC_EN BIT(0) /*1: The next descriptor is valid */
+#define LOONGSON2_MMC_DMA_DESC_ADDR_LOW GENMASK(31, 1)
+
+/* Bitfields of cmd field of HW descriptor */
+#define LOONGSON2_MMC_DMA_INT BIT(1) /* Enable DMA interrupts */
+#define LOONGSON2_MMC_DMA_DATA_DIR BIT(12) /* 1: write to device, 0: read from device */
+
+#define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000
+#define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500
+
+/* Loongson-2K1000 SDIO2 DMA routing register */
+#define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15)
+#define LS2K1000_DMA0_CONF 0x0
+#define LS2K1000_DMA1_CONF 0x1
+#define LS2K1000_DMA2_CONF 0x2
+#define LS2K1000_DMA3_CONF 0x3
+#define LS2K1000_DMA4_CONF 0x4
+
+/* Loongson-2K0500 SDIO2 DMA routing register */
+#define LS2K0500_SDIO_DMA_MASK GENMASK(15, 14)
+#define LS2K0500_DMA0_CONF 0x1
+#define LS2K0500_DMA1_CONF 0x2
+#define LS2K0500_DMA2_CONF 0x3
+
+enum loongson2_mmc_state {
+ STATE_NONE,
+ STATE_FINALIZE,
+ STATE_CMDSENT,
+ STATE_RSPFIN,
+ STATE_XFERFINISH,
+ STATE_XFERFINISH_RSPFIN,
+};
+
+struct loongson2_dma_desc {
+ u32 ndesc_addr;
+ u32 mem_addr;
+ u32 apb_addr;
+ u32 len;
+ u32 step_len;
+ u32 step_times;
+ u32 cmd;
+ u32 stats;
+ u32 high_ndesc_addr;
+ u32 high_mem_addr;
+ u32 reserved[2];
+} __packed;
+
+struct loongson2_mmc_host {
+ struct device *dev;
+ struct mmc_request *mrq;
+ struct regmap *regmap;
+ struct resource *res;
+ struct clk *clk;
+ u32 current_clk;
+ void *sg_cpu;
+ dma_addr_t sg_dma;
+ int dma_complete;
+ struct dma_chan *chan;
+ int cmd_is_stop;
+ int bus_width;
+ spinlock_t lock; /* Prevent races with irq handler */
+ enum loongson2_mmc_state state;
+ const struct loongson2_mmc_pdata *pdata;
+};
+
+struct loongson2_mmc_pdata {
+ const struct regmap_config *regmap_config;
+ void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
+ void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
+ int (*setting_dma)(struct loongson2_mmc_host *host, struct platform_device *pdev);
+ int (*prepare_dma)(struct loongson2_mmc_host *host, struct mmc_data *data);
+ void (*release_dma)(struct loongson2_mmc_host *host, struct device *dev);
+};
+
+static void loongson2_mmc_send_command(struct loongson2_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ u32 cctrl;
+
+ if (cmd->data)
+ host->state = STATE_XFERFINISH_RSPFIN;
+ else if (cmd->flags & MMC_RSP_PRESENT)
+ host->state = STATE_RSPFIN;
+ else
+ host->state = STATE_CMDSENT;
+
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, cmd->arg);
+
+ cctrl = FIELD_PREP(LOONGSON2_MMC_CCTL_INDEX, cmd->opcode);
+ cctrl |= LOONGSON2_MMC_CCTL_HOST | LOONGSON2_MMC_CCTL_START;
+
+ if (cmd->opcode == SD_SWITCH && cmd->data)
+ cctrl |= LOONGSON2_MMC_CCTL_CMD6;
+
+ if (cmd->flags & MMC_RSP_PRESENT)
+ cctrl |= LOONGSON2_MMC_CCTL_WAIT_RSP;
+
+ if (cmd->flags & MMC_RSP_136)
+ cctrl |= LOONGSON2_MMC_CCTL_LONG_RSP;
+
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, cctrl);
+}
+
+static int loongson2_mmc_setup_data(struct loongson2_mmc_host *host,
+ struct mmc_data *data)
+{
+ u32 dctrl;
+
+ if ((data->blksz & 3) != 0)
+ return -EINVAL;
+
+ dctrl = FIELD_PREP(LOONGSON2_MMC_DCTL_BNUM, data->blocks);
+ dctrl |= LOONGSON2_MMC_DCTL_START | LOONGSON2_MMC_DCTL_ENDMA;
+
+ if (host->bus_width == MMC_BUS_WIDTH_4)
+ dctrl |= LOONGSON2_MMC_DCTL_WIDE;
+ else if (host->bus_width == MMC_BUS_WIDTH_8)
+ dctrl |= LOONGSON2_MMC_DCTL_8BIT_BUS;
+
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_DCTL, dctrl);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_BSIZE, data->blksz);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_TIMER, U32_MAX);
+
+ return 0;
+}
+
+static int loongson2_mmc_prepare_dma(struct loongson2_mmc_host *host,
+ struct mmc_data *data)
+{
+ int ret;
+
+ if (!data)
+ return 0;
+
+ ret = loongson2_mmc_setup_data(host, data);
+ if (ret)
+ return ret;
+
+ host->dma_complete = 0;
+
+ return host->pdata->prepare_dma(host, data);
+}
+
+static void loongson2_mmc_send_request(struct mmc_host *mmc)
+{
+ int ret;
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+
+ ret = loongson2_mmc_prepare_dma(host, cmd->data);
+ if (ret) {
+ dev_err(host->dev, "DMA data prepared failed with %d\n", ret);
+ cmd->error = ret;
+ cmd->data->error = ret;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ if (host->pdata->fix_data_timeout)
+ host->pdata->fix_data_timeout(host, cmd);
+
+ loongson2_mmc_send_command(host, cmd);
+
+ /* Fix deselect card */
+ if (cmd->opcode == MMC_SELECT_CARD && cmd->arg == 0) {
+ cmd->error = 0;
+ mmc_request_done(mmc, mrq);
+ }
+}
+
+static irqreturn_t loongson2_mmc_irq_worker(int irq, void *devid)
+{
+ struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid;
+ struct mmc_host *mmc = mmc_from_priv(host);
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+
+ if (cmd->data)
+ dma_unmap_sg(mmc_dev(mmc), cmd->data->sg, cmd->data->sg_len,
+ mmc_get_dma_dir(cmd->data));
+
+ if (cmd->data && !cmd->error &&
+ !cmd->data->error && !host->dma_complete)
+ return IRQ_HANDLED;
+
+ /* Read response from controller. */
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP0, &cmd->resp[0]);
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP1, &cmd->resp[1]);
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP2, &cmd->resp[2]);
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP3, &cmd->resp[3]);
+
+ /* Cleanup controller */
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, 0);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, 0);
+
+ if (cmd->data && cmd->error)
+ cmd->data->error = cmd->error;
+
+ if (cmd->data && cmd->data->stop && !host->cmd_is_stop) {
+ host->cmd_is_stop = 1;
+ loongson2_mmc_send_request(mmc);
+ return IRQ_HANDLED;
+ }
+
+ /* If we have no data transfer we are finished here */
+ if (!mrq->data)
+ goto request_done;
+
+ /* Calculate the amount of bytes transfer if there was no error */
+ if (mrq->data->error == 0) {
+ mrq->data->bytes_xfered =
+ (mrq->data->blocks * mrq->data->blksz);
+ } else {
+ mrq->data->bytes_xfered = 0;
+ }
+
+request_done:
+ host->state = STATE_NONE;
+ host->mrq = NULL;
+ mmc_request_done(mmc, mrq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t loongson2_mmc_irq(int irq, void *devid)
+{
+ struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid;
+ struct mmc_host *mmc = mmc_from_priv(host);
+ struct mmc_command *cmd;
+ unsigned long iflags;
+ u32 dsts, imsk;
+
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk);
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts);
+
+ if ((dsts & LOONGSON2_MMC_DSTS_IRQ) &&
+ (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) {
+ regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT,
+ LOONGSON2_MMC_INT_SDIOIRQ, LOONGSON2_MMC_INT_SDIOIRQ);
+
+ sdio_signal_irq(mmc);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ if (host->state == STATE_NONE || host->state == STATE_FINALIZE || !host->mrq)
+ goto irq_out;
+
+ cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
+ if (!cmd)
+ goto irq_out;
+
+ cmd->error = 0;
+
+ if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) {
+ cmd->error = -ETIMEDOUT;
+ goto close_transfer;
+ }
+
+ if (imsk & LOONGSON2_MMC_INT_CSENT) {
+ if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT)
+ goto close_transfer;
+
+ if (host->state == STATE_XFERFINISH_RSPFIN)
+ host->state = STATE_XFERFINISH;
+ }
+
+ if (!cmd->data)
+ goto irq_out;
+
+ if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) {
+ cmd->data->error = -EILSEQ;
+ goto close_transfer;
+ }
+
+ if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) {
+ cmd->data->error = -ETIMEDOUT;
+ goto close_transfer;
+ }
+
+ if (imsk & LOONGSON2_MMC_INT_DFIN) {
+ if (host->state == STATE_XFERFINISH) {
+ host->dma_complete = 1;
+ goto close_transfer;
+ }
+
+ if (host->state == STATE_XFERFINISH_RSPFIN)
+ host->state = STATE_RSPFIN;
+ }
+
+irq_out:
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
+ spin_unlock_irqrestore(&host->lock, iflags);
+ return IRQ_HANDLED;
+
+close_transfer:
+ host->state = STATE_FINALIZE;
+ host->pdata->reorder_cmd_data(host, cmd);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
+ spin_unlock_irqrestore(&host->lock, iflags);
+ return IRQ_WAKE_THREAD;
+}
+
+static void loongson2_mmc_dll_mode_init(struct loongson2_mmc_host *host)
+{
+ u32 val, pad_delay, delay;
+ int ret;
+
+ regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_SEL,
+ LOONGSON2_MMC_SEL_DATA, LOONGSON2_MMC_SEL_DATA);
+
+ val = FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME, 0xc8)
+ | FIELD_PREP(LOONGSON2_MMC_DLLCTL_INCRE, 0x1)
+ | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START, 0x1)
+ | FIELD_PREP(LOONGSON2_MMC_DLLCTL_CLK_MODE, 0x1)
+ | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START_BIT, 0x1)
+ | FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME_BPASS, 0xf);
+
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_DLLCTL, val);
+
+ ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_DLLVAL, val,
+ (val & LOONGSON2_MMC_DLLVAL_DONE), 0,
+ LOONGSON2_MMC_DLLVAL_TIMEOUT_US);
+ if (ret < 0)
+ return;
+
+ regmap_read(host->regmap, LOONGSON2_MMC_REG_DLLVAL, &val);
+ pad_delay = FIELD_GET(GENMASK(7, 1), val);
+
+ delay = FIELD_PREP(LOONGSON2_MMC_DELAY_PAD, pad_delay)
+ | FIELD_PREP(LOONGSON2_MMC_DELAY_RD, pad_delay + 1);
+
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_DELAY, delay);
+}
+
+static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_ios *ios)
+{
+ u32 pre;
+
+ pre = DIV_ROUND_UP(host->current_clk, ios->clock);
+ if (pre > 255)
+ pre = 255;
+
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN);
+
+ regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL,
+ LOONGSON2_MMC_CTL_ENCLK, LOONGSON2_MMC_CTL_ENCLK);
+
+ /* EMMC DLL mode setting */
+ if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52)
+ loongson2_mmc_dll_mode_init(host);
+}
+
+static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+ int ret;
+
+ if (ios->power_mode == MMC_POWER_UP) {
+ if (!IS_ERR(mmc->supply.vmmc)) {
+ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
+ if (ret) {
+ dev_err(host->dev, "failed to enable vmmc regulator\n");
+ return; /* return, if failed turn on vmmc */
+ }
+ }
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_RESET);
+ mdelay(10);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_EXTCLK);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, LOONGSON2_MMC_IEN_ALL);
+ regmap_write(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_CLEAR);
+ } else if (ios->power_mode == MMC_POWER_OFF) {
+ regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL,
+ LOONGSON2_MMC_CTL_RESET, LOONGSON2_MMC_CTL_RESET);
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ return;
+ }
+
+ loongson2_mmc_set_clk(host, ios);
+
+ host->bus_width = ios->bus_width;
+}
+
+static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+ host->cmd_is_stop = 0;
+ host->mrq = mrq;
+ loongson2_mmc_send_request(mmc);
+}
+
+static void loongson2_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+ regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_SDIOIRQ, enable);
+}
+
+static void loongson2_mmc_ack_sdio_irq(struct mmc_host *mmc)
+{
+ loongson2_mmc_enable_sdio_irq(mmc, 1);
+}
+
+static struct mmc_host_ops loongson2_mmc_ops = {
+ .request = loongson2_mmc_request,
+ .set_ios = loongson2_mmc_set_ios,
+ .get_ro = mmc_gpio_get_ro,
+ .get_cd = mmc_gpio_get_cd,
+ .enable_sdio_irq = loongson2_mmc_enable_sdio_irq,
+ .ack_sdio_irq = loongson2_mmc_ack_sdio_irq,
+};
+
+static const struct regmap_config ls2k0500_mmc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = LOONGSON2_MMC_REG_IEN,
+};
+
+static int loongson2_reorder_cmd_list[] = { SD_APP_SEND_SCR, SD_APP_SEND_NUM_WR_BLKS,
+ SD_APP_SD_STATUS, MMC_SEND_WRITE_PROT, SD_SWITCH };
+
+/*
+ * According to SD spec, ACMD13, ACMD22, ACMD51 and CMD30
+ * response datas has different byte order with usual data packets.
+ * However sdio controller will send these datas in usual data format,
+ * so we need to adjust these datas to a protocol consistent byte order.
+ */
+static void ls2k0500_mmc_reorder_cmd_data(struct loongson2_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ struct scatterlist *sg;
+ u32 *data;
+ int i, j;
+
+ if (mmc_cmd_type(cmd) != MMC_CMD_ADTC)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(loongson2_reorder_cmd_list); i++)
+ if (cmd->opcode == loongson2_reorder_cmd_list[i])
+ break;
+
+ if (i == ARRAY_SIZE(loongson2_reorder_cmd_list))
+ return;
+
+ for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) {
+ data = sg_virt(&sg[i]);
+ for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++)
+ if (cmd->opcode == SD_SWITCH)
+ data[j] = bitrev8x4(data[j]);
+ else
+ data[j] = (__force u32)cpu_to_be32(data[j]);
+ }
+}
+
+static int loongson2_mmc_prepare_external_dma(struct loongson2_mmc_host *host,
+ struct mmc_data *data)
+{
+ struct mmc_host *mmc = mmc_from_priv(host);
+ struct dma_slave_config dma_conf = { };
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
+ mmc_get_dma_dir(data));
+ if (!ret)
+ return -ENOMEM;
+
+ dma_conf.src_addr = host->res->start + LOONGSON2_MMC_REG_DATA,
+ dma_conf.dst_addr = host->res->start + LOONGSON2_MMC_REG_DATA,
+ dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ dma_conf.direction = !(data->flags & MMC_DATA_WRITE) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+
+ dmaengine_slave_config(host->chan, &dma_conf);
+ desc = dmaengine_prep_slave_sg(host->chan, data->sg, data->sg_len,
+ dma_conf.direction,
+ DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+ if (!desc)
+ goto unmap_exit;
+
+ dmaengine_submit(desc);
+ dma_async_issue_pending(host->chan);
+
+ return 0;
+
+unmap_exit:
+ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, mmc_get_dma_dir(data));
+ return -ENOMEM;
+}
+
+static void loongson2_mmc_release_external_dma(struct loongson2_mmc_host *host,
+ struct device *dev)
+{
+ dma_release_channel(host->chan);
+}
+
+static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host,
+ struct platform_device *pdev)
+{
+ int ret, val;
+ void __iomem *regs;
+
+ regs = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ val = readl(regs);
+ val |= FIELD_PREP(LS2K0500_SDIO_DMA_MASK, LS2K0500_DMA2_CONF);
+ writel(val, regs);
+
+ host->chan = dma_request_chan(&pdev->dev, "rx-tx");
+ ret = PTR_ERR_OR_ZERO(host->chan);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot get DMA channel.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
+ .regmap_config = &ls2k0500_mmc_regmap_config,
+ .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
+ .setting_dma = ls2k0500_mmc_set_external_dma,
+ .prepare_dma = loongson2_mmc_prepare_external_dma,
+ .release_dma = loongson2_mmc_release_external_dma,
+};
+
+static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
+ struct platform_device *pdev)
+{
+ int ret, val;
+ void __iomem *regs;
+
+ regs = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ val = readl(regs);
+ val |= FIELD_PREP(LS2K1000_SDIO_DMA_MASK, LS2K1000_DMA1_CONF);
+ writel(val, regs);
+
+ host->chan = dma_request_chan(&pdev->dev, "rx-tx");
+ ret = PTR_ERR_OR_ZERO(host->chan);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot get DMA channel.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
+ .regmap_config = &ls2k0500_mmc_regmap_config,
+ .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data,
+ .setting_dma = ls2k1000_mmc_set_external_dma,
+ .prepare_dma = loongson2_mmc_prepare_external_dma,
+ .release_dma = loongson2_mmc_release_external_dma,
+};
+
+static const struct regmap_config ls2k2000_mmc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = LOONGSON2_MMC_REG_RDMA_HI,
+};
+
+static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ struct scatterlist *sg;
+ u32 *data;
+ int i, j;
+
+ if (cmd->opcode != SD_SWITCH || mmc_cmd_type(cmd) != MMC_CMD_ADTC)
+ return;
+
+ for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) {
+ data = sg_virt(&sg[i]);
+ for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++)
+ data[j] = bitrev8x4(data[j]);
+ }
+}
+
+/*
+ * This is a controller hardware defect. Single/multiple block write commands
+ * must be sent after the TX FULL flag is set, otherwise a data timeout interrupt
+ * will occur.
+ */
+static void ls2k2000_mmc_fix_data_timeout(struct loongson2_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ int val;
+
+ if (cmd->opcode != MMC_WRITE_BLOCK && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK)
+ return;
+
+ regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_FSTS, val,
+ (val & LOONGSON2_MMC_FSTS_TXFULL), 0,
+ LOONGSON2_MMC_TXFULL_TIMEOUT_US);
+}
+
+static int loongson2_mmc_prepare_internal_dma(struct loongson2_mmc_host *host,
+ struct mmc_data *data)
+{
+ struct loongson2_dma_desc *pdes = (struct loongson2_dma_desc *)host->sg_cpu;
+ struct mmc_host *mmc = mmc_from_priv(host);
+ dma_addr_t next_desc = host->sg_dma;
+ struct scatterlist *sg;
+ int reg_lo, reg_hi;
+ u64 dma_order;
+ int i, ret;
+
+ ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
+ mmc_get_dma_dir(data));
+ if (!ret)
+ return -ENOMEM;
+
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ pdes[i].len = sg_dma_len(&sg[i]) / 4;
+ pdes[i].step_len = 0;
+ pdes[i].step_times = 1;
+ pdes[i].mem_addr = lower_32_bits(sg_dma_address(&sg[i]));
+ pdes[i].high_mem_addr = upper_32_bits(sg_dma_address(&sg[i]));
+ pdes[i].apb_addr = host->res->start + LOONGSON2_MMC_REG_DATA;
+ pdes[i].cmd = LOONGSON2_MMC_DMA_INT;
+
+ if (data->flags & MMC_DATA_READ) {
+ reg_lo = LOONGSON2_MMC_REG_RDMA_LO;
+ reg_hi = LOONGSON2_MMC_REG_RDMA_HI;
+ } else {
+ pdes[i].cmd |= LOONGSON2_MMC_DMA_DATA_DIR;
+ reg_lo = LOONGSON2_MMC_REG_WDMA_LO;
+ reg_hi = LOONGSON2_MMC_REG_WDMA_HI;
+ }
+
+ next_desc += sizeof(struct loongson2_dma_desc);
+ pdes[i].ndesc_addr = lower_32_bits(next_desc) |
+ LOONGSON2_MMC_DMA_DESC_EN;
+ pdes[i].high_ndesc_addr = upper_32_bits(next_desc);
+ }
+
+ /* Setting the last descriptor enable bit */
+ pdes[i - 1].ndesc_addr &= ~LOONGSON2_MMC_DMA_DESC_EN;
+
+ dma_order = (host->sg_dma & ~LOONGSON2_MMC_DMA_CONFIG_MASK) |
+ LOONGSON2_MMC_DMA_64BIT_EN |
+ LOONGSON2_MMC_DMA_START;
+
+ regmap_write(host->regmap, reg_hi, upper_32_bits(dma_order));
+ regmap_write(host->regmap, reg_lo, lower_32_bits(dma_order));
+
+ return 0;
+}
+
+static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host,
+ struct platform_device *pdev)
+{
+ host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+ &host->sg_dma, GFP_KERNEL);
+ if (!host->sg_cpu)
+ return -ENOMEM;
+
+ memset(host->sg_cpu, 0, PAGE_SIZE);
+ return 0;
+}
+
+static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host,
+ struct device *dev)
+{
+ dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+}
+
+static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = {
+ .regmap_config = &ls2k2000_mmc_regmap_config,
+ .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data,
+ .fix_data_timeout = ls2k2000_mmc_fix_data_timeout,
+ .setting_dma = ls2k2000_mmc_set_internal_dma,
+ .prepare_dma = loongson2_mmc_prepare_internal_dma,
+ .release_dma = loongson2_mmc_release_internal_dma,
+};
+
+static int loongson2_mmc_resource_request(struct platform_device *pdev,
+ struct loongson2_mmc_host *host)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ int ret, irq;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ host->regmap = devm_regmap_init_mmio(dev, base, host->pdata->regmap_config);
+ if (IS_ERR(host->regmap))
+ return PTR_ERR(host->regmap);
+
+ host->clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
+
+ if (host->clk) {
+ ret = devm_clk_rate_exclusive_get(dev, host->clk);
+ if (ret)
+ return ret;
+
+ host->current_clk = clk_get_rate(host->clk);
+ } else {
+ /* For ACPI, the clock is accessed via the clock-frequency attribute. */
+ device_property_read_u32(dev, "clock-frequency", &host->current_clk);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq,
+ loongson2_mmc_irq_worker,
+ IRQF_ONESHOT, "loongson2-mmc", host);
+ if (ret)
+ return ret;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (ret)
+ return ret;
+
+ return host->pdata->setting_dma(host, pdev);
+}
+
+static int loongson2_mmc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct loongson2_mmc_host *host;
+ struct mmc_host *mmc;
+ int ret;
+
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
+ if (!mmc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mmc);
+
+ host = mmc_priv(mmc);
+ host->state = STATE_NONE;
+ spin_lock_init(&host->lock);
+
+ host->pdata = device_get_match_data(dev);
+ if (!host->pdata)
+ return dev_err_probe(dev, -EINVAL, "Failed to get match data\n");
+
+ ret = loongson2_mmc_resource_request(pdev, host);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request resource\n");
+
+ mmc->ops = &loongson2_mmc_ops;
+ mmc->f_min = DIV_ROUND_UP(host->current_clk, 256);
+ mmc->f_max = host->current_clk;
+ mmc->max_blk_count = 4095;
+ mmc->max_blk_size = 4095;
+ mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
+ mmc->max_segs = 1;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ /* Process SDIO IRQs through the sdio_irq_work. */
+ if (mmc->caps & MMC_CAP_SDIO_IRQ)
+ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret || mmc->ocr_avail == 0) {
+ dev_warn(dev, "Can't get voltage, defaulting to 3.3V\n");
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ }
+
+ ret = mmc_of_parse(mmc);
+ if (ret) {
+ dev_err(dev, "Failed to parse device node\n");
+ goto free_dma;
+ }
+
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ dev_err(dev, "Failed to add mmc host\n");
+ goto free_dma;
+ }
+
+ return 0;
+
+free_dma:
+ host->pdata->release_dma(host, dev);
+ return ret;
+}
+
+static void loongson2_mmc_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+ mmc_remove_host(mmc);
+ host->pdata->release_dma(host, &pdev->dev);
+}
+
+static const struct of_device_id loongson2_mmc_of_ids[] = {
+ { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
+ { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
+ { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata },
+ { },
+};
+MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids);
+
+static int loongson2_mmc_suspend(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+ clk_disable_unprepare(host->clk);
+
+ return 0;
+}
+
+static int loongson2_mmc_resume(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+ return clk_prepare_enable(host->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(loongson2_mmc_pm_ops, loongson2_mmc_suspend, loongson2_mmc_resume);
+
+static struct platform_driver loongson2_mmc_driver = {
+ .driver = {
+ .name = "loongson2-mmc",
+ .of_match_table = loongson2_mmc_of_ids,
+ .pm = pm_ptr(&loongson2_mmc_pm_ops),
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = loongson2_mmc_probe,
+ .remove = loongson2_mmc_remove,
+};
+
+module_platform_driver(loongson2_mmc_driver);
+
+MODULE_DESCRIPTION("Loongson-2K SD/SDIO/eMMC Interface driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/meson-mx-sdhc-clkc.c b/drivers/mmc/host/meson-mx-sdhc-clkc.c
index cbd17a596cd2..6d619bd0a8dc 100644
--- a/drivers/mmc/host/meson-mx-sdhc-clkc.c
+++ b/drivers/mmc/host/meson-mx-sdhc-clkc.c
@@ -84,10 +84,8 @@ static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
return ret;
clk_bulk_data[bulk_index].clk = devm_clk_hw_get_clk(dev, hw, name_suffix);
- if (IS_ERR(clk_bulk_data[bulk_index].clk))
- return PTR_ERR(clk_bulk_data[bulk_index].clk);
- return 0;
+ return PTR_ERR_OR_ZERO(clk_bulk_data[bulk_index].clk);
}
int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c
index b4e56ccffca2..fb49ea71289e 100644
--- a/drivers/mmc/host/meson-mx-sdhc-mmc.c
+++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c
@@ -757,11 +757,6 @@ static void meson_mx_sdhc_init_hw(struct mmc_host *mmc)
regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
}
-static void meason_mx_mmc_free_host(void *data)
-{
- mmc_free_host(data);
-}
-
static int meson_mx_sdhc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -770,16 +765,10 @@ static int meson_mx_sdhc_probe(struct platform_device *pdev)
void __iomem *base;
int ret, irq;
- mmc = mmc_alloc_host(sizeof(*host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
- ret = devm_add_action_or_reset(dev, meason_mx_mmc_free_host, mmc);
- if (ret) {
- dev_err(dev, "Failed to register mmc_free_host action\n");
- return ret;
- }
-
host = mmc_priv(mmc);
host->mmc = mmc;
diff --git a/drivers/mmc/host/meson-mx-sdio.c b/drivers/mmc/host/meson-mx-sdio.c
index ad351805eed4..5921e2cb2180 100644
--- a/drivers/mmc/host/meson-mx-sdio.c
+++ b/drivers/mmc/host/meson-mx-sdio.c
@@ -19,6 +19,7 @@
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
+#include <linux/regmap.h>
#include <linux/timer.h>
#include <linux/types.h>
@@ -98,17 +99,16 @@
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
#define MESON_MX_SDIO_MAX_SLOTS 3
+struct meson_mx_mmc_host_clkc {
+ struct clk_divider cfg_div;
+ struct clk_fixed_factor fixed_div2;
+};
+
struct meson_mx_mmc_host {
struct device *controller_dev;
- struct clk *parent_clk;
- struct clk *core_clk;
- struct clk_divider cfg_div;
struct clk *cfg_div_clk;
- struct clk_fixed_factor fixed_factor;
- struct clk *fixed_factor_clk;
-
- void __iomem *base;
+ struct regmap *regmap;
int irq;
spinlock_t irq_lock;
@@ -122,22 +122,10 @@ struct meson_mx_mmc_host {
int error;
};
-static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask,
- u32 val)
-{
- struct meson_mx_mmc_host *host = mmc_priv(mmc);
- u32 regval;
-
- regval = readl(host->base + reg);
- regval &= ~mask;
- regval |= (val & mask);
-
- writel(regval, host->base + reg);
-}
-
static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host)
{
- writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC);
+ regmap_write(host->regmap, MESON_MX_SDIO_IRQC,
+ MESON_MX_SDIO_IRQC_SOFT_RESET);
udelay(2);
}
@@ -158,7 +146,7 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
struct meson_mx_mmc_host *host = mmc_priv(mmc);
unsigned int pack_size;
unsigned long irqflags, timeout;
- u32 mult, send = 0, ext = 0;
+ u32 send = 0, ext = 0;
host->cmd = cmd;
@@ -215,25 +203,22 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
spin_lock_irqsave(&host->irq_lock, irqflags);
- mult = readl(host->base + MESON_MX_SDIO_MULT);
- mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK;
- mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id);
- mult |= BIT(31);
- writel(mult, host->base + MESON_MX_SDIO_MULT);
+ regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
+ MESON_MX_SDIO_MULT_PORT_SEL_MASK | BIT(31),
+ FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK,
+ host->slot_id) | BIT(31));
/* enable the CMD done interrupt */
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC,
- MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN,
- MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
+ regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQC,
+ MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
/* clear pending interrupts */
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS,
- MESON_MX_SDIO_IRQS_CMD_INT,
- MESON_MX_SDIO_IRQS_CMD_INT);
+ regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQS,
+ MESON_MX_SDIO_IRQS_CMD_INT);
- writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU);
- writel(ext, host->base + MESON_MX_SDIO_EXT);
- writel(send, host->base + MESON_MX_SDIO_SEND);
+ regmap_write(host->regmap, MESON_MX_SDIO_ARGU, cmd->arg);
+ regmap_write(host->regmap, MESON_MX_SDIO_EXT, ext);
+ regmap_write(host->regmap, MESON_MX_SDIO_SEND, send);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
@@ -263,14 +248,13 @@ static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
- MESON_MX_SDIO_CONF_BUS_WIDTH, 0);
+ regmap_clear_bits(host->regmap, MESON_MX_SDIO_CONF,
+ MESON_MX_SDIO_CONF_BUS_WIDTH);
break;
case MMC_BUS_WIDTH_4:
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
- MESON_MX_SDIO_CONF_BUS_WIDTH,
- MESON_MX_SDIO_CONF_BUS_WIDTH);
+ regmap_set_bits(host->regmap, MESON_MX_SDIO_CONF,
+ MESON_MX_SDIO_CONF_BUS_WIDTH);
break;
case MMC_BUS_WIDTH_8:
@@ -351,8 +335,8 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
if (mrq->data)
- writel(sg_dma_address(mrq->data->sg),
- host->base + MESON_MX_SDIO_ADDR);
+ regmap_write(host->regmap, MESON_MX_SDIO_ADDR,
+ sg_dma_address(mrq->data->sg));
if (mrq->sbc)
meson_mx_mmc_start_cmd(mmc, mrq->sbc);
@@ -364,24 +348,26 @@ static void meson_mx_mmc_read_response(struct mmc_host *mmc,
struct mmc_command *cmd)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
- u32 mult;
- int i, resp[4];
+ unsigned int i, resp[4];
- mult = readl(host->base + MESON_MX_SDIO_MULT);
- mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX;
- mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK;
- mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0);
- writel(mult, host->base + MESON_MX_SDIO_MULT);
+ regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
+ MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
+ MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
+ MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
+ FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
+ 0));
if (cmd->flags & MMC_RSP_136) {
for (i = 0; i <= 3; i++)
- resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU);
+ regmap_read(host->regmap, MESON_MX_SDIO_ARGU,
+ &resp[3 - i]);
+
cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff);
cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
cmd->resp[3] = (resp[3] << 8);
} else if (cmd->flags & MMC_RSP_PRESENT) {
- cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU);
+ regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &cmd->resp[0]);
}
}
@@ -422,8 +408,8 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
spin_lock(&host->irq_lock);
- irqs = readl(host->base + MESON_MX_SDIO_IRQS);
- send = readl(host->base + MESON_MX_SDIO_SEND);
+ regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
+ regmap_read(host->regmap, MESON_MX_SDIO_SEND, &send);
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
ret = meson_mx_mmc_process_cmd_irq(host, irqs, send);
@@ -431,7 +417,7 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
ret = IRQ_HANDLED;
/* finally ACK all pending interrupts */
- writel(irqs, host->base + MESON_MX_SDIO_IRQS);
+ regmap_write(host->regmap, MESON_MX_SDIO_IRQS, irqs);
spin_unlock(&host->irq_lock);
@@ -446,12 +432,11 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
if (WARN_ON(!cmd))
return IRQ_HANDLED;
- del_timer_sync(&host->cmd_timeout);
+ timer_delete_sync(&host->cmd_timeout);
if (cmd->data) {
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
- cmd->data->sg_len,
- mmc_get_dma_dir(cmd->data));
+ cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
}
@@ -467,16 +452,16 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
static void meson_mx_mmc_timeout(struct timer_list *t)
{
- struct meson_mx_mmc_host *host = from_timer(host, t, cmd_timeout);
+ struct meson_mx_mmc_host *host = timer_container_of(host, t,
+ cmd_timeout);
unsigned long irqflags;
- u32 irqc;
+ u32 irqs, argu;
spin_lock_irqsave(&host->irq_lock, irqflags);
/* disable the CMD interrupt */
- irqc = readl(host->base + MESON_MX_SDIO_IRQC);
- irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN;
- writel(irqc, host->base + MESON_MX_SDIO_IRQC);
+ regmap_clear_bits(host->regmap, MESON_MX_SDIO_IRQC,
+ MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
@@ -487,10 +472,12 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
if (!host->cmd)
return;
+ regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
+ regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &argu);
+
dev_dbg(mmc_dev(host->mmc),
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
- host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS),
- readl(host->base + MESON_MX_SDIO_ARGU));
+ host->cmd->opcode, irqs, argu);
host->cmd->error = -ETIMEDOUT;
@@ -506,23 +493,30 @@ static struct mmc_host_ops meson_mx_mmc_ops = {
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
{
- struct device_node *slot_node;
- struct platform_device *pdev;
+ struct platform_device *pdev = NULL;
+
+ for_each_available_child_of_node_scoped(parent->of_node, slot_node) {
+ if (!of_device_is_compatible(slot_node, "mmc-slot"))
+ continue;
+
+ /*
+ * TODO: the MMC core framework currently does not support
+ * controllers with multiple slots properly. So we only
+ * register the first slot for now.
+ */
+ if (pdev) {
+ dev_warn(parent,
+ "more than one 'mmc-slot' compatible child found - using the first one and ignoring all subsequent ones\n");
+ break;
+ }
- /*
- * TODO: the MMC core framework currently does not support
- * controllers with multiple slots properly. So we only register
- * the first slot for now
- */
- slot_node = of_get_compatible_child(parent->of_node, "mmc-slot");
- if (!slot_node) {
- dev_warn(parent, "no 'mmc-slot' sub-node found\n");
- return ERR_PTR(-ENOENT);
+ pdev = of_platform_device_create(slot_node, NULL, parent);
+ if (!pdev)
+ dev_err(parent,
+ "Failed to create platform device for mmc-slot node '%pOF'\n",
+ slot_node);
}
- pdev = of_platform_device_create(slot_node, NULL, parent);
- of_node_put(slot_node);
-
return pdev;
}
@@ -532,16 +526,14 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
struct device *slot_dev = mmc_dev(mmc);
int ret;
- if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) {
- dev_err(slot_dev, "missing 'reg' property\n");
- return -EINVAL;
- }
+ if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id))
+ return dev_err_probe(slot_dev, -EINVAL,
+ "missing 'reg' property\n");
- if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) {
- dev_err(slot_dev, "invalid 'reg' property value %d\n",
- host->slot_id);
- return -EINVAL;
- }
+ if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS)
+ return dev_err_probe(slot_dev, -EINVAL,
+ "invalid 'reg' property value %d\n",
+ host->slot_id);
/* Get regulators and the supported OCR mask */
ret = mmc_regulator_get_supply(mmc);
@@ -560,8 +552,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
/* Get the min and max supported clock rates */
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
- mmc->f_max = clk_round_rate(host->cfg_div_clk,
- clk_get_rate(host->parent_clk));
+ mmc->f_max = clk_round_rate(host->cfg_div_clk, ULONG_MAX);
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
mmc->ops = &meson_mx_mmc_ops;
@@ -577,70 +568,89 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
return 0;
}
-static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host)
+static struct clk *meson_mx_mmc_register_clk(struct device *dev,
+ void __iomem *base)
{
- struct clk_init_data init;
- const char *clk_div_parent, *clk_fixed_factor_parent;
-
- clk_fixed_factor_parent = __clk_get_name(host->parent_clk);
- init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
- "%s#fixed_factor",
- dev_name(host->controller_dev));
- if (!init.name)
- return -ENOMEM;
+ const char *fixed_div2_name, *cfg_div_name;
+ struct meson_mx_mmc_host_clkc *host_clkc;
+ struct clk *clk;
+ int ret;
- init.ops = &clk_fixed_factor_ops;
- init.flags = 0;
- init.parent_names = &clk_fixed_factor_parent;
- init.num_parents = 1;
- host->fixed_factor.div = 2;
- host->fixed_factor.mult = 1;
- host->fixed_factor.hw.init = &init;
-
- host->fixed_factor_clk = devm_clk_register(host->controller_dev,
- &host->fixed_factor.hw);
- if (WARN_ON(IS_ERR(host->fixed_factor_clk)))
- return PTR_ERR(host->fixed_factor_clk);
-
- clk_div_parent = __clk_get_name(host->fixed_factor_clk);
- init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
- "%s#div", dev_name(host->controller_dev));
- if (!init.name)
- return -ENOMEM;
+ /* use a dedicated memory allocation for the clock controller to
+ * prevent use-after-free as meson_mx_mmc_host is free'd before
+ * dev (controller dev, not mmc_host->dev) is free'd.
+ */
+ host_clkc = devm_kzalloc(dev, sizeof(*host_clkc), GFP_KERNEL);
+ if (!host_clkc)
+ return ERR_PTR(-ENOMEM);
+
+ fixed_div2_name = devm_kasprintf(dev, GFP_KERNEL, "%s#fixed_div2",
+ dev_name(dev));
+ if (!fixed_div2_name)
+ return ERR_PTR(-ENOMEM);
+
+ host_clkc->fixed_div2.div = 2;
+ host_clkc->fixed_div2.mult = 1;
+ host_clkc->fixed_div2.hw.init = CLK_HW_INIT_FW_NAME(fixed_div2_name,
+ "clkin",
+ &clk_fixed_factor_ops,
+ 0);
+ ret = devm_clk_hw_register(dev, &host_clkc->fixed_div2.hw);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Failed to register %s clock\n",
+ fixed_div2_name);
+
+ cfg_div_name = devm_kasprintf(dev, GFP_KERNEL, "%s#div", dev_name(dev));
+ if (!cfg_div_name)
+ return ERR_PTR(-ENOMEM);
+
+ host_clkc->cfg_div.reg = base + MESON_MX_SDIO_CONF;
+ host_clkc->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
+ host_clkc->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
+ host_clkc->cfg_div.hw.init = CLK_HW_INIT_HW(cfg_div_name,
+ &host_clkc->fixed_div2.hw,
+ &clk_divider_ops,
+ CLK_DIVIDER_ALLOW_ZERO);
+ ret = devm_clk_hw_register(dev, &host_clkc->cfg_div.hw);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Failed to register %s clock\n",
+ cfg_div_name);
- init.ops = &clk_divider_ops;
- init.flags = CLK_SET_RATE_PARENT;
- init.parent_names = &clk_div_parent;
- init.num_parents = 1;
- host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF;
- host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
- host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
- host->cfg_div.hw.init = &init;
- host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO;
-
- host->cfg_div_clk = devm_clk_register(host->controller_dev,
- &host->cfg_div.hw);
- if (WARN_ON(IS_ERR(host->cfg_div_clk)))
- return PTR_ERR(host->cfg_div_clk);
+ clk = devm_clk_hw_get_clk(dev, &host_clkc->cfg_div.hw, "cfg_div_clk");
+ if (IS_ERR(clk))
+ return dev_err_ptr_probe(dev, PTR_ERR(clk),
+ "Failed to get the cfg_div clock\n");
- return 0;
+ return clk;
}
static int meson_mx_mmc_probe(struct platform_device *pdev)
{
+ const struct regmap_config meson_mx_sdio_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = MESON_MX_SDIO_EXT,
+ };
struct platform_device *slot_pdev;
struct mmc_host *mmc;
struct meson_mx_mmc_host *host;
+ struct clk *core_clk;
+ void __iomem *base;
int ret, irq;
u32 conf;
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev);
if (!slot_pdev)
return -ENODEV;
- else if (IS_ERR(slot_pdev))
- return PTR_ERR(slot_pdev);
- mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev);
+ mmc = devm_mmc_alloc_host(&slot_pdev->dev, sizeof(*host));
if (!mmc) {
ret = -ENOMEM;
goto error_unregister_slot_pdev;
@@ -655,51 +665,48 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
- host->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto error_free_mmc;
+ host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &meson_mx_sdio_regmap_config);
+ if (IS_ERR(host->regmap)) {
+ ret = dev_err_probe(host->controller_dev, PTR_ERR(host->regmap),
+ "Failed to initialize regmap\n");
+ goto error_unregister_slot_pdev;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
- goto error_free_mmc;
+ goto error_unregister_slot_pdev;
}
ret = devm_request_threaded_irq(host->controller_dev, irq,
meson_mx_mmc_irq,
meson_mx_mmc_irq_thread, IRQF_ONESHOT,
NULL, host);
- if (ret)
- goto error_free_mmc;
-
- host->core_clk = devm_clk_get(host->controller_dev, "core");
- if (IS_ERR(host->core_clk)) {
- ret = PTR_ERR(host->core_clk);
- goto error_free_mmc;
+ if (ret) {
+ dev_err_probe(host->controller_dev, ret,
+ "Failed to request IRQ\n");
+ goto error_unregister_slot_pdev;
}
- host->parent_clk = devm_clk_get(host->controller_dev, "clkin");
- if (IS_ERR(host->parent_clk)) {
- ret = PTR_ERR(host->parent_clk);
- goto error_free_mmc;
+ core_clk = devm_clk_get_enabled(host->controller_dev, "core");
+ if (IS_ERR(core_clk)) {
+ ret = dev_err_probe(host->controller_dev, PTR_ERR(core_clk),
+ "Failed to get and enable 'core' clock\n");
+ goto error_unregister_slot_pdev;
}
- ret = meson_mx_mmc_register_clks(host);
- if (ret)
- goto error_free_mmc;
-
- ret = clk_prepare_enable(host->core_clk);
- if (ret) {
- dev_err(host->controller_dev, "Failed to enable core clock\n");
- goto error_free_mmc;
+ host->cfg_div_clk = meson_mx_mmc_register_clk(&pdev->dev, base);
+ if (IS_ERR(host->cfg_div_clk)) {
+ ret = PTR_ERR(host->cfg_div_clk);
+ goto error_unregister_slot_pdev;
}
ret = clk_prepare_enable(host->cfg_div_clk);
if (ret) {
- dev_err(host->controller_dev, "Failed to enable MMC clock\n");
- goto error_disable_core_clk;
+ dev_err_probe(host->controller_dev, ret,
+ "Failed to enable MMC (cfg div) clock\n");
+ goto error_unregister_slot_pdev;
}
conf = 0;
@@ -707,22 +714,18 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2);
- writel(conf, host->base + MESON_MX_SDIO_CONF);
+ regmap_write(host->regmap, MESON_MX_SDIO_CONF, conf);
meson_mx_mmc_soft_reset(host);
ret = meson_mx_mmc_add_host(host);
if (ret)
- goto error_disable_clks;
+ goto error_disable_div_clk;
return 0;
-error_disable_clks:
+error_disable_div_clk:
clk_disable_unprepare(host->cfg_div_clk);
-error_disable_core_clk:
- clk_disable_unprepare(host->core_clk);
-error_free_mmc:
- mmc_free_host(mmc);
error_unregister_slot_pdev:
of_platform_device_destroy(&slot_pdev->dev, NULL);
return ret;
@@ -733,16 +736,13 @@ static void meson_mx_mmc_remove(struct platform_device *pdev)
struct meson_mx_mmc_host *host = platform_get_drvdata(pdev);
struct device *slot_dev = mmc_dev(host->mmc);
- del_timer_sync(&host->cmd_timeout);
+ timer_delete_sync(&host->cmd_timeout);
mmc_remove_host(host->mmc);
of_platform_device_destroy(slot_dev, NULL);
clk_disable_unprepare(host->cfg_div_clk);
- clk_disable_unprepare(host->core_clk);
-
- mmc_free_host(host->mmc);
}
static const struct of_device_id meson_mx_mmc_of_match[] = {
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 47443fb5eb33..42936e248c55 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -563,10 +563,10 @@ mmc_spi_setup_data_message(struct mmc_spi_host *host, bool multiple, bool write)
* the next token (next data block, or STOP_TRAN). We can try to
* minimize I/O ops by using a single read to collect end-of-busy.
*/
- if (multiple || write) {
+ if (write) {
t = &host->early_status;
memset(t, 0, sizeof(*t));
- t->len = write ? sizeof(scratch->status) : 1;
+ t->len = sizeof(scratch->status);
t->tx_buf = host->ones;
t->rx_buf = scratch->status;
t->cs_change = 1;
@@ -1185,7 +1185,7 @@ static int mmc_spi_probe(struct spi_device *spi)
goto nomem;
memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
- mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
+ mmc = devm_mmc_alloc_host(&spi->dev, sizeof(*host));
if (!mmc)
goto nomem;
@@ -1305,7 +1305,6 @@ fail_glue_init:
kfree(host->data);
fail_nobuf1:
mmc_spi_put_pdata(spi);
- mmc_free_host(mmc);
nomem:
kfree(ones);
return status;
@@ -1328,7 +1327,6 @@ static void mmc_spi_remove(struct spi_device *spi)
spi->max_speed_hz = mmc->f_max;
mmc_spi_put_pdata(spi);
- mmc_free_host(mmc);
}
static const struct spi_device_id mmc_spi_dev_ids[] = {
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b790c3c3c8f9..e500051bd572 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -2082,7 +2082,6 @@ static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
if (!enable) {
- pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
}
}
@@ -2223,7 +2222,7 @@ static int mmci_probe(struct amba_device *dev,
return -ENOMEM;
}
- mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
+ mmc = devm_mmc_alloc_host(&dev->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -2234,7 +2233,7 @@ static int mmci_probe(struct amba_device *dev,
ret = mmci_of_parse(np, mmc);
if (ret)
- goto host_free;
+ return ret;
/*
* Some variant (STM32) doesn't have opendrain bit, nevertheless
@@ -2242,19 +2241,15 @@ static int mmci_probe(struct amba_device *dev,
*/
if (!variant->opendrain) {
host->pinctrl = devm_pinctrl_get(&dev->dev);
- if (IS_ERR(host->pinctrl)) {
- dev_err(&dev->dev, "failed to get pinctrl");
- ret = PTR_ERR(host->pinctrl);
- goto host_free;
- }
+ if (IS_ERR(host->pinctrl))
+ return dev_err_probe(&dev->dev, PTR_ERR(host->pinctrl),
+ "failed to get pinctrl\n");
host->pins_opendrain = pinctrl_lookup_state(host->pinctrl,
MMCI_PINCTRL_STATE_OPENDRAIN);
- if (IS_ERR(host->pins_opendrain)) {
- dev_err(mmc_dev(mmc), "Can't select opendrain pins\n");
- ret = PTR_ERR(host->pins_opendrain);
- goto host_free;
- }
+ if (IS_ERR(host->pins_opendrain))
+ return dev_err_probe(&dev->dev, PTR_ERR(host->pins_opendrain),
+ "Can't select opendrain pins\n");
}
host->hw_designer = amba_manf(dev);
@@ -2263,14 +2258,12 @@ static int mmci_probe(struct amba_device *dev,
dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision);
host->clk = devm_clk_get(&dev->dev, NULL);
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- goto host_free;
- }
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
ret = clk_prepare_enable(host->clk);
if (ret)
- goto host_free;
+ return ret;
if (variant->qcom_fifo)
host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt;
@@ -2491,8 +2484,6 @@ static int mmci_probe(struct amba_device *dev,
clk_disable:
clk_disable_unprepare(host->clk);
- host_free:
- mmc_free_host(mmc);
return ret;
}
@@ -2522,11 +2513,9 @@ static void mmci_remove(struct amba_device *dev)
mmci_dma_release(host);
clk_disable_unprepare(host->clk);
- mmc_free_host(mmc);
}
}
-#ifdef CONFIG_PM
static void mmci_save(struct mmci_host *host)
{
unsigned long flags;
@@ -2591,12 +2580,10 @@ static int mmci_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops mmci_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)
};
static const struct amba_id mmci_ids[] = {
@@ -2685,7 +2672,7 @@ MODULE_DEVICE_TABLE(amba, mmci_ids);
static struct amba_driver mmci_driver = {
.drv = {
.name = DRIVER_NAME,
- .pm = &mmci_dev_pm_ops,
+ .pm = pm_ptr(&mmci_dev_pm_ops),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = mmci_probe,
diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c
index a12048e5de63..3dd8f232052f 100644
--- a/drivers/mmc/host/moxart-mmc.c
+++ b/drivers/mmc/host/moxart-mmc.c
@@ -558,41 +558,33 @@ static int moxart_probe(struct platform_device *pdev)
int irq, ret;
u32 i;
- mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc) {
- dev_err(dev, "mmc_alloc_host failed\n");
- ret = -ENOMEM;
- goto out_mmc;
+ dev_err(dev, "devm_mmc_alloc_host failed\n");
+ return -ENOMEM;
}
ret = of_address_to_resource(node, 0, &res_mmc);
- if (ret) {
- dev_err(dev, "of_address_to_resource failed\n");
- goto out_mmc;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "of_address_to_resource failed\n");
irq = irq_of_parse_and_map(node, 0);
- if (irq <= 0) {
- dev_err(dev, "irq_of_parse_and_map failed\n");
- ret = -EINVAL;
- goto out_mmc;
- }
+ if (irq <= 0)
+ return dev_err_probe(dev, -EINVAL,
+ "irq_of_parse_and_map failed\n");
clk = devm_clk_get(dev, NULL);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto out_mmc;
- }
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
reg_mmc = devm_ioremap_resource(dev, &res_mmc);
- if (IS_ERR(reg_mmc)) {
- ret = PTR_ERR(reg_mmc);
- goto out_mmc;
- }
+ if (IS_ERR(reg_mmc))
+ return PTR_ERR(reg_mmc);
ret = mmc_of_parse(mmc);
if (ret)
- goto out_mmc;
+ return ret;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -686,9 +678,6 @@ out:
dma_release_channel(host->dma_chan_tx);
if (!IS_ERR_OR_NULL(host->dma_chan_rx))
dma_release_channel(host->dma_chan_rx);
-out_mmc:
- if (mmc)
- mmc_free_host(mmc);
return ret;
}
@@ -707,7 +696,6 @@ static void moxart_remove(struct platform_device *pdev)
writel(0, host->base + REG_POWER_CONTROL);
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
host->base + REG_CLOCK_CONTROL);
- mmc_free_host(mmc);
}
static const struct of_device_id moxart_mmc_match[] = {
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index efb0d2d5716b..daed659f63f6 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -5,6 +5,7 @@
*/
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -83,6 +84,7 @@
#define EMMC51_CFG0 0x204
#define EMMC50_CFG0 0x208
#define EMMC50_CFG1 0x20c
+#define EMMC50_CFG2 0x21c
#define EMMC50_CFG3 0x220
#define SDC_FIFO_CFG 0x228
#define CQHCI_SETTING 0x7fc
@@ -233,7 +235,9 @@
/* MSDC_PATCH_BIT mask */
#define MSDC_PATCH_BIT_ODDSUPP BIT(1) /* RW */
+#define MSDC_PATCH_BIT_DIS_WRMON BIT(2) /* RW */
#define MSDC_PATCH_BIT_RD_DAT_SEL BIT(3) /* RW */
+#define MSDC_PATCH_BIT_DESCUP_SEL BIT(6) /* RW */
#define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7)
#define MSDC_CKGEN_MSDC_DLY_SEL GENMASK(14, 10)
#define MSDC_PATCH_BIT_IODSSEL BIT(16) /* RW */
@@ -246,10 +250,22 @@
#define MSDC_PATCH_BIT_SPCPUSH BIT(29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO BIT(30) /* RW */
-#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PB1_WRDAT_CRC_TACNTR GENMASK(2, 0) /* RW */
+#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
#define MSDC_PB1_BUSY_CHECK_SEL BIT(7) /* RW */
#define MSDC_PATCH_BIT1_STOP_DLY GENMASK(11, 8) /* RW */
-
+#define MSDC_PB1_DDR_CMD_FIX_SEL BIT(14) /* RW */
+#define MSDC_PB1_SINGLE_BURST BIT(16) /* RW */
+#define MSDC_PB1_RSVD20 GENMASK(18, 17) /* RW */
+#define MSDC_PB1_AUTO_SYNCST_CLR BIT(19) /* RW */
+#define MSDC_PB1_MARK_POP_WATER BIT(20) /* RW */
+#define MSDC_PB1_LP_DCM_EN BIT(21) /* RW */
+#define MSDC_PB1_RSVD3 BIT(22) /* RW */
+#define MSDC_PB1_AHB_GDMA_HCLK BIT(23) /* RW */
+#define MSDC_PB1_MSDC_CLK_ENFEAT GENMASK(31, 24) /* RW */
+
+/* MSDC_PATCH_BIT2 mask */
#define MSDC_PATCH_BIT2_CFGRESP BIT(15) /* RW */
#define MSDC_PATCH_BIT2_CFGCRCSTS BIT(28) /* RW */
#define MSDC_PB2_SUPPORT_64G BIT(1) /* RW */
@@ -273,6 +289,7 @@
#define MSDC_PAD_TUNE_CMD2_SEL BIT(21) /* RW */
#define PAD_DS_TUNE_DLY_SEL BIT(0) /* RW */
+#define PAD_DS_TUNE_DLY2_SEL BIT(1) /* RW */
#define PAD_DS_TUNE_DLY1 GENMASK(6, 2) /* RW */
#define PAD_DS_TUNE_DLY2 GENMASK(11, 7) /* RW */
#define PAD_DS_TUNE_DLY3 GENMASK(16, 12) /* RW */
@@ -290,7 +307,10 @@
/* EMMC50_CFG1 mask */
#define EMMC50_CFG1_DS_CFG BIT(28) /* RW */
-#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
+/* EMMC50_CFG2 mask */
+#define EMMC50_CFG2_AXI_SET_LEN GENMASK(27, 24) /* RW */
+
+#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
#define SDC_FIFO_CFG_WRVALIDSEL BIT(24) /* RW */
#define SDC_FIFO_CFG_RDVALIDSEL BIT(25) /* RW */
@@ -318,6 +338,7 @@
/* EMMC50_PAD_DS_TUNE mask */
#define PAD_DS_DLY_SEL BIT(16) /* RW */
+#define PAD_DS_DLY2_SEL BIT(15) /* RW */
#define PAD_DS_DLY1 GENMASK(14, 10) /* RW */
#define PAD_DS_DLY3 GENMASK(4, 0) /* RW */
@@ -414,6 +435,7 @@ struct mtk_mmc_compatible {
u8 clk_div_bits;
bool recheck_sdio_irq;
bool hs400_tune; /* only used for MT8173 */
+ bool needs_top_base;
u32 pad_tune_reg;
bool async_fifo;
bool data_tune;
@@ -587,6 +609,7 @@ static const struct mtk_mmc_compatible mt7986_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = true,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
@@ -627,6 +650,7 @@ static const struct mtk_mmc_compatible mt8183_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = false,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
@@ -653,6 +677,7 @@ static const struct mtk_mmc_compatible mt8196_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = false,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
@@ -821,12 +846,18 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
static void msdc_prepare_data(struct msdc_host *host, struct mmc_data *data)
{
if (!(data->host_cookie & MSDC_PREPARE_FLAG)) {
- data->host_cookie |= MSDC_PREPARE_FLAG;
data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len,
mmc_get_dma_dir(data));
+ if (data->sg_count)
+ data->host_cookie |= MSDC_PREPARE_FLAG;
}
}
+static bool msdc_data_prepared(struct mmc_data *data)
+{
+ return data->host_cookie & MSDC_PREPARE_FLAG;
+}
+
static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data)
{
if (data->host_cookie & MSDC_ASYNC_FLAG)
@@ -921,15 +952,15 @@ static int msdc_ungate_clock(struct msdc_host *host)
static void msdc_new_tx_setting(struct msdc_host *host)
{
+ u32 val;
+
if (!host->top_base)
return;
- sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
- TEST_LOOP_DSCLK_MUX_SEL);
- sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
- TEST_LOOP_LATCH_MUX_SEL);
- sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
- TEST_HS400_CMD_LOOP_MUX_SEL);
+ val = readl(host->top_base + LOOP_TEST_CONTROL);
+ val |= TEST_LOOP_DSCLK_MUX_SEL;
+ val |= TEST_LOOP_LATCH_MUX_SEL;
+ val &= ~TEST_HS400_CMD_LOOP_MUX_SEL;
switch (host->timing) {
case MMC_TIMING_LEGACY:
@@ -939,19 +970,18 @@ static void msdc_new_tx_setting(struct msdc_host *host)
case MMC_TIMING_UHS_SDR25:
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
- sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
- LOOP_EN_SEL_CLK);
+ val &= ~LOOP_EN_SEL_CLK;
break;
case MMC_TIMING_UHS_SDR50:
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
case MMC_TIMING_MMC_HS400:
- sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
- LOOP_EN_SEL_CLK);
+ val |= LOOP_EN_SEL_CLK;
break;
default:
break;
}
+ writel(val, host->top_base + LOOP_TEST_CONTROL);
}
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
@@ -1097,11 +1127,12 @@ static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
u32 resp;
switch (mmc_resp_type(cmd)) {
- /* Actually, R1, R5, R6, R7 are the same */
+ /* Actually, R1, R5, R6, R7 are the same */
case MMC_RSP_R1:
resp = 0x1;
break;
case MMC_RSP_R1B:
+ case MMC_RSP_R1B_NO_CRC:
resp = 0x7;
break;
case MMC_RSP_R2:
@@ -1183,7 +1214,7 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
host->data = data;
read = data->flags & MMC_DATA_READ;
- mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+ mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
msdc_dma_setup(host, &host->dma, data);
sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
@@ -1351,7 +1382,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
* CRC error.
*/
msdc_reset_hw(host);
- if (events & MSDC_INT_RSPCRCERR) {
+ if (events & MSDC_INT_RSPCRCERR &&
+ mmc_resp_type(cmd) != MMC_RSP_R1B_NO_CRC) {
cmd->error = -EILSEQ;
host->error |= REQ_CMD_EIO;
} else if (events & MSDC_INT_CMDTMO) {
@@ -1412,7 +1444,7 @@ static void msdc_start_command(struct msdc_host *host,
WARN_ON(host->cmd);
host->cmd = cmd;
- mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+ mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
if (!msdc_cmd_is_ready(host, mrq, cmd))
return;
@@ -1457,8 +1489,19 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(!host->hsq_en && host->mrq);
host->mrq = mrq;
- if (mrq->data)
+ if (mrq->data) {
msdc_prepare_data(host, mrq->data);
+ if (!msdc_data_prepared(mrq->data)) {
+ host->mrq = NULL;
+ /*
+ * Failed to prepare DMA area, fail fast before
+ * starting any commands.
+ */
+ mrq->cmd->error = -ENOSPC;
+ mmc_request_done(mmc_from_priv(host), mrq);
+ return;
+ }
+ }
/* if SBC is required, we have HW option and SW option.
* if HW option is enabled, and SBC does not have "special" flags,
@@ -1808,7 +1851,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
static void msdc_init_hw(struct msdc_host *host)
{
- u32 val;
+ u32 val, pb1_val, pb2_val;
u32 tune_reg = host->dev_comp->pad_tune_reg;
struct mmc_host *mmc = mmc_from_priv(host);
@@ -1861,71 +1904,115 @@ static void msdc_init_hw(struct msdc_host *host)
}
writel(0, host->base + MSDC_IOCON);
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
- writel(0x403c0046, host->base + MSDC_PATCH_BIT);
- sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
- writel(0xffff4089, host->base + MSDC_PATCH_BIT1);
- sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
+ /*
+ * Patch bit 0 and 1 are completely rewritten, but for patch bit 2
+ * defaults are retained and, if necessary, only some bits are fixed
+ * up: read the PB2 register here for later usage in this function.
+ */
+ pb2_val = readl(host->base + MSDC_PATCH_BIT2);
+
+ /* Enable odd number support for 8-bit data bus */
+ val = MSDC_PATCH_BIT_ODDSUPP;
+
+ /* Disable SD command register write monitor */
+ val |= MSDC_PATCH_BIT_DIS_WRMON;
+
+ /* Issue transfer done interrupt after GPD update */
+ val |= MSDC_PATCH_BIT_DESCUP_SEL;
+
+ /* Extend R1B busy detection delay (in clock cycles) */
+ val |= FIELD_PREP(MSDC_PATCH_BIT_BUSYDLY, 15);
+
+ /* Enable CRC phase timeout during data write operation */
+ val |= MSDC_PATCH_BIT_DECRCTMO;
+
+ /* Set CKGEN delay to one stage */
+ val |= FIELD_PREP(MSDC_CKGEN_MSDC_DLY_SEL, 1);
+
+ /* First MSDC_PATCH_BIT setup is done: pull the trigger! */
+ writel(val, host->base + MSDC_PATCH_BIT);
+
+ /* Set wr data, crc status, cmd response turnaround period for UHS104 */
+ pb1_val = FIELD_PREP(MSDC_PB1_WRDAT_CRC_TACNTR, 1);
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_CMDTA, 1);
+ pb1_val |= MSDC_PB1_DDR_CMD_FIX_SEL;
+
+ /* Support 'single' burst type only when AXI_LEN is 0 */
+ sdr_get_field(host->base + EMMC50_CFG2, EMMC50_CFG2_AXI_SET_LEN, &val);
+ if (!val)
+ pb1_val |= MSDC_PB1_SINGLE_BURST;
+
+ /* Set auto sync state clear, block gap stop clk */
+ pb1_val |= MSDC_PB1_RSVD20 | MSDC_PB1_AUTO_SYNCST_CLR | MSDC_PB1_MARK_POP_WATER;
+
+ /* Set low power DCM, use HCLK for GDMA, use MSDC CLK for everything else */
+ pb1_val |= MSDC_PB1_LP_DCM_EN | MSDC_PB1_RSVD3 |
+ MSDC_PB1_AHB_GDMA_HCLK | MSDC_PB1_MSDC_CLK_ENFEAT;
+
+ /* If needed, enable R1b command busy check at controller init time */
+ if (!host->dev_comp->busy_check)
+ pb1_val |= MSDC_PB1_BUSY_CHECK_SEL;
if (host->dev_comp->stop_clk_fix) {
if (host->dev_comp->stop_dly_sel)
- sdr_set_field(host->base + MSDC_PATCH_BIT1,
- MSDC_PATCH_BIT1_STOP_DLY,
- host->dev_comp->stop_dly_sel);
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_STOP_DLY,
+ host->dev_comp->stop_dly_sel);
- if (host->dev_comp->pop_en_cnt)
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_POP_EN_CNT,
- host->dev_comp->pop_en_cnt);
+ if (host->dev_comp->pop_en_cnt) {
+ pb2_val &= ~MSDC_PB2_POP_EN_CNT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_POP_EN_CNT,
+ host->dev_comp->pop_en_cnt);
+ }
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_WRVALIDSEL);
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_RDVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL);
}
- if (host->dev_comp->busy_check)
- sdr_clr_bits(host->base + MSDC_PATCH_BIT1, BIT(7));
-
if (host->dev_comp->async_fifo) {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPWAIT, 3);
- if (host->dev_comp->enhance_rx) {
- if (host->top_base)
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- SDC_RX_ENH_EN);
- else
- sdr_set_bits(host->base + SDC_ADV_CFG0,
- SDC_RX_ENHANCE_EN);
+ /* Set CMD response timeout multiplier to 65 + (16 * 3) cycles */
+ pb2_val &= ~MSDC_PB2_RESPWAIT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPWAIT, 3);
+
+ /* eMMC4.5: Select async FIFO path for CMD resp and CRC status */
+ pb2_val &= ~MSDC_PATCH_BIT2_CFGRESP;
+ pb2_val |= MSDC_PATCH_BIT2_CFGCRCSTS;
+
+ if (!host->dev_comp->enhance_rx) {
+ /* eMMC4.5: Delay 2T for CMD resp and CRC status EN signals */
+ pb2_val &= ~(MSDC_PB2_RESPSTSENSEL | MSDC_PB2_CRCSTSENSEL);
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPSTSENSEL, 2);
+ pb2_val |= FIELD_PREP(MSDC_PB2_CRCSTSENSEL, 2);
+ } else if (host->top_base) {
+ sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, SDC_RX_ENH_EN);
} else {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPSTSENSEL, 2);
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_CRCSTSENSEL, 2);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_RX_ENHANCE_EN);
}
- /* use async fifo, then no need tune internal delay */
- sdr_clr_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGRESP);
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGCRCSTS);
}
if (host->dev_comp->support_64g)
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_SUPPORT_64G);
+ pb2_val |= MSDC_PB2_SUPPORT_64G;
+
+ /* Patch Bit 1/2 setup is done: pull the trigger! */
+ writel(pb1_val, host->base + MSDC_PATCH_BIT1);
+ writel(pb2_val, host->base + MSDC_PATCH_BIT2);
+ sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
if (host->dev_comp->data_tune) {
if (host->top_base) {
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY_SEL);
- sdr_clr_bits(host->top_base + EMMC_TOP_CONTROL,
- DATA_K_VALUE_SEL);
- sdr_set_bits(host->top_base + EMMC_TOP_CMD,
- PAD_CMD_RD_RXDLY_SEL);
+ u32 top_ctl_val = readl(host->top_base + EMMC_TOP_CONTROL);
+ u32 top_cmd_val = readl(host->top_base + EMMC_TOP_CMD);
+
+ top_cmd_val |= PAD_CMD_RD_RXDLY_SEL;
+ top_ctl_val |= PAD_DAT_RD_RXDLY_SEL;
+ top_ctl_val &= ~DATA_K_VALUE_SEL;
if (host->tuning_step > PAD_DELAY_HALF) {
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2_SEL);
- sdr_set_bits(host->top_base + EMMC_TOP_CMD,
- PAD_CMD_RD_RXDLY2_SEL);
+ top_cmd_val |= PAD_CMD_RD_RXDLY2_SEL;
+ top_ctl_val |= PAD_DAT_RD_RXDLY2_SEL;
}
+
+ writel(top_ctl_val, host->top_base + EMMC_TOP_CONTROL);
+ writel(top_cmd_val, host->top_base + EMMC_TOP_CMD);
} else {
sdr_set_bits(host->base + tune_reg,
MSDC_PAD_TUNE_RD_SEL |
@@ -2135,15 +2222,17 @@ static inline void msdc_set_cmd_delay(struct msdc_host *host, u32 value)
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->top_base) {
+ u32 regval = readl(host->top_base + EMMC_TOP_CMD);
+
+ regval &= ~(PAD_CMD_RXDLY | PAD_CMD_RXDLY2);
+
if (value < PAD_DELAY_HALF) {
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY, value);
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2, 0);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY, value);
} else {
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY,
- PAD_DELAY_HALF - 1);
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2,
- value - PAD_DELAY_HALF);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY, PAD_DELAY_HALF - 1);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY2, value - PAD_DELAY_HALF);
}
+ writel(regval, host->top_base + EMMC_TOP_CMD);
} else {
if (value < PAD_DELAY_HALF) {
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY, value);
@@ -2163,17 +2252,18 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value)
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->top_base) {
+ u32 regval = readl(host->top_base + EMMC_TOP_CONTROL);
+
+ regval &= ~(PAD_DAT_RD_RXDLY | PAD_DAT_RD_RXDLY2);
+
if (value < PAD_DELAY_HALF) {
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY, value);
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2, 0);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, value);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value);
} else {
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
}
+ writel(regval, host->top_base + EMMC_TOP_CONTROL);
} else {
if (value < PAD_DELAY_HALF) {
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_DATRRDLY, value);
@@ -2498,13 +2588,23 @@ tune_done:
static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct msdc_host *host = mmc_priv(mmc);
+
host->hs400_mode = true;
- if (host->top_base)
- writel(host->hs400_ds_delay,
- host->top_base + EMMC50_PAD_DS_TUNE);
- else
- writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+ if (host->top_base) {
+ if (host->hs400_ds_dly3)
+ sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY3, host->hs400_ds_dly3);
+ if (host->hs400_ds_delay)
+ writel(host->hs400_ds_delay,
+ host->top_base + EMMC50_PAD_DS_TUNE);
+ } else {
+ if (host->hs400_ds_dly3)
+ sdr_set_field(host->base + PAD_DS_TUNE,
+ PAD_DS_TUNE_DLY3, host->hs400_ds_dly3);
+ if (host->hs400_ds_delay)
+ writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+ }
/* hs400 mode must set it to 0 */
sdr_clr_bits(host->base + MSDC_PATCH_BIT2, MSDC_PATCH_BIT2_CFGCRCSTS);
/* to improve read performance, set outstanding to 2 */
@@ -2524,14 +2624,11 @@ static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card
if (host->top_base) {
sdr_set_bits(host->top_base + EMMC50_PAD_DS_TUNE,
PAD_DS_DLY_SEL);
- if (host->hs400_ds_dly3)
- sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
- PAD_DS_DLY3, host->hs400_ds_dly3);
+ sdr_clr_bits(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY2_SEL);
} else {
sdr_set_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY_SEL);
- if (host->hs400_ds_dly3)
- sdr_set_field(host->base + PAD_DS_TUNE,
- PAD_DS_TUNE_DLY3, host->hs400_ds_dly3);
+ sdr_clr_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY2_SEL);
}
host->hs400_tuning = true;
@@ -2885,9 +2982,13 @@ static int msdc_drv_probe(struct platform_device *pdev)
if (IS_ERR(host->base))
return PTR_ERR(host->base);
- host->top_base = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(host->top_base))
- host->top_base = NULL;
+ host->dev_comp = of_device_get_match_data(&pdev->dev);
+
+ if (host->dev_comp->needs_top_base) {
+ host->top_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(host->top_base))
+ return PTR_ERR(host->top_base);
+ }
ret = mmc_regulator_get_supply(mmc);
if (ret)
@@ -2949,7 +3050,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
- host->dev_comp = of_device_get_match_data(&pdev->dev);
host->src_clk_freq = clk_get_rate(host->src_clk);
/* Set host parameters to mmc */
mmc->ops = &mt_msdc_ops;
@@ -2959,7 +3059,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
if (!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
- !mmc_can_gpio_cd(mmc) &&
+ !mmc_host_can_gpio_cd(mmc) &&
host->dev_comp->use_internal_cd) {
/*
* Is removable but no GPIO declared, so
@@ -3070,6 +3170,7 @@ release_clk:
msdc_gate_clock(host);
platform_set_drvdata(pdev, NULL);
release_mem:
+ device_init_wakeup(&pdev->dev, false);
if (host->dma.gpd)
dma_free_coherent(&pdev->dev,
2 * sizeof(struct mt_gpdma_desc),
@@ -3103,6 +3204,7 @@ static void msdc_drv_remove(struct platform_device *pdev)
host->dma.gpd, host->dma.gpd_addr);
dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
host->dma.bd, host->dma.bd_addr);
+ device_init_wakeup(&pdev->dev, false);
}
static void msdc_save_reg(struct msdc_host *host)
@@ -3176,7 +3278,7 @@ static void msdc_restore_reg(struct msdc_host *host)
__msdc_enable_sdio_irq(host, 1);
}
-static int __maybe_unused msdc_runtime_suspend(struct device *dev)
+static int msdc_runtime_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@@ -3198,7 +3300,7 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused msdc_runtime_resume(struct device *dev)
+static int msdc_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@@ -3221,7 +3323,7 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev)
return 0;
}
-static int __maybe_unused msdc_suspend(struct device *dev)
+static int msdc_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@@ -3246,7 +3348,7 @@ static int __maybe_unused msdc_suspend(struct device *dev)
return pm_runtime_force_suspend(dev);
}
-static int __maybe_unused msdc_resume(struct device *dev)
+static int msdc_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@@ -3258,8 +3360,8 @@ static int __maybe_unused msdc_resume(struct device *dev)
}
static const struct dev_pm_ops msdc_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume)
- SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume)
+ RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
};
static struct platform_driver mt_msdc_driver = {
@@ -3269,7 +3371,7 @@ static struct platform_driver mt_msdc_driver = {
.name = "mtk-msdc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = msdc_of_ids,
- .pm = &msdc_dev_pm_ops,
+ .pm = pm_ptr(&msdc_dev_pm_ops),
},
};
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index b92f3ba38663..79df2fa89a3f 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -292,7 +292,7 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
host->pio_ptr = NULL;
host->pio_size = 0;
} else {
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
mmc_get_dma_dir(data));
}
@@ -464,7 +464,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
struct mmc_command *cmd = mrq->cmd;
u32 err_status = 0;
- del_timer(&host->timer);
+ timer_delete(&host->timer);
host->mrq = NULL;
host->intr_en &= MVSD_NOR_CARD_INT;
@@ -509,7 +509,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
static void mvsd_timeout_timer(struct timer_list *t)
{
- struct mvsd_host *host = from_timer(host, t, timer);
+ struct mvsd_host *host = timer_container_of(host, t, timer);
void __iomem *iobase = host->base;
struct mmc_request *mrq;
unsigned long flags;
@@ -706,11 +706,9 @@ static int mvsd_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto out;
- }
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
+ if (!mmc)
+ return -ENOMEM;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -724,11 +722,9 @@ static int mvsd_probe(struct platform_device *pdev)
* fixed rate clock).
*/
host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->clk)) {
- dev_err(&pdev->dev, "no clock associated\n");
- ret = -EINVAL;
- goto out;
- }
+ if (IS_ERR(host->clk))
+ return dev_err_probe(&pdev->dev, -EINVAL, "no clock associated\n");
+
clk_prepare_enable(host->clk);
mmc->ops = &mvsd_ops;
@@ -787,12 +783,7 @@ static int mvsd_probe(struct platform_device *pdev)
return 0;
out:
- if (mmc) {
- if (!IS_ERR(host->clk))
- clk_disable_unprepare(host->clk);
- mmc_free_host(mmc);
- }
-
+ clk_disable_unprepare(host->clk);
return ret;
}
@@ -803,12 +794,11 @@ static void mvsd_remove(struct platform_device *pdev)
struct mvsd_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
- del_timer_sync(&host->timer);
+ timer_delete_sync(&host->timer);
mvsd_power_down(host);
if (!IS_ERR(host->clk))
clk_disable_unprepare(host->clk);
- mmc_free_host(mmc);
}
static const struct of_device_id mvsdio_dt_ids[] = {
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index e7a286c3216f..c405cfb8b269 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -352,7 +352,7 @@ static void mxcmci_dma_callback(void *data)
struct mxcmci_host *host = data;
u32 stat;
- del_timer(&host->watchdog);
+ timer_delete(&host->watchdog);
stat = mxcmci_readl(host, MMC_REG_STATUS);
@@ -737,7 +737,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
mxcmci_cmd_done(host, stat);
if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) {
- del_timer(&host->watchdog);
+ timer_delete(&host->watchdog);
mxcmci_data_done(host, stat);
}
@@ -955,7 +955,7 @@ static bool filter(struct dma_chan *chan, void *param)
static void mxcmci_watchdog(struct timer_list *t)
{
- struct mxcmci_host *host = from_timer(host, t, watchdog);
+ struct mxcmci_host *host = timer_container_of(host, t, watchdog);
struct mmc_request *req = host->req;
unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS);
@@ -995,7 +995,7 @@ static int mxcmci_probe(struct platform_device *pdev)
struct mxcmci_host *host;
struct resource *res;
int ret = 0, irq;
- bool dat3_card_detect = false;
+ bool dat3_card_detect;
dma_cap_mask_t mask;
struct imxmmc_platform_data *pdata = pdev->dev.platform_data;
@@ -1005,23 +1005,21 @@ static int mxcmci_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
host = mmc_priv(mmc);
host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto out_free;
- }
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
host->phys_base = res->start;
ret = mmc_of_parse(mmc);
if (ret)
- goto out_free;
+ return ret;
mmc->ops = &mxcmci_ops;
/* For devicetree parsing, the bus width is read from devicetree */
@@ -1048,13 +1046,13 @@ static int mxcmci_probe(struct platform_device *pdev)
if (pdata)
dat3_card_detect = pdata->dat3_card_detect;
- else if (mmc_card_is_removable(mmc)
- && !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
- dat3_card_detect = true;
+ else
+ dat3_card_detect = mmc_card_is_removable(mmc) &&
+ !of_property_present(pdev->dev.of_node, "cd-gpios");
ret = mmc_regulator_get_supply(mmc);
if (ret)
- goto out_free;
+ return ret;
if (!mmc->ocr_avail) {
if (pdata && pdata->ocr_avail)
@@ -1070,20 +1068,16 @@ static int mxcmci_probe(struct platform_device *pdev)
host->default_irq_mask = 0;
host->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(host->clk_ipg)) {
- ret = PTR_ERR(host->clk_ipg);
- goto out_free;
- }
+ if (IS_ERR(host->clk_ipg))
+ return PTR_ERR(host->clk_ipg);
host->clk_per = devm_clk_get(&pdev->dev, "per");
- if (IS_ERR(host->clk_per)) {
- ret = PTR_ERR(host->clk_per);
- goto out_free;
- }
+ if (IS_ERR(host->clk_per))
+ return PTR_ERR(host->clk_per);
ret = clk_prepare_enable(host->clk_per);
if (ret)
- goto out_free;
+ return ret;
ret = clk_prepare_enable(host->clk_ipg);
if (ret)
@@ -1169,9 +1163,6 @@ out_clk_put:
out_clk_per_put:
clk_disable_unprepare(host->clk_per);
-out_free:
- mmc_free_host(mmc);
-
return ret;
}
@@ -1190,8 +1181,6 @@ static void mxcmci_remove(struct platform_device *pdev)
clk_disable_unprepare(host->clk_per);
clk_disable_unprepare(host->clk_ipg);
-
- mmc_free_host(mmc);
}
static int mxcmci_suspend(struct device *dev)
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 80e6f48c83aa..7c7c52d9e8e7 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -569,7 +569,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
if (irq_err < 0)
return irq_err;
- mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -577,10 +577,8 @@ static int mxs_mmc_probe(struct platform_device *pdev)
ssp = &host->ssp;
ssp->dev = &pdev->dev;
ssp->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(ssp->base)) {
- ret = PTR_ERR(ssp->base);
- goto out_mmc_free;
- }
+ if (IS_ERR(ssp->base))
+ return PTR_ERR(ssp->base);
ssp->devid = (enum mxs_ssp_id)of_device_get_match_data(&pdev->dev);
@@ -590,26 +588,23 @@ static int mxs_mmc_probe(struct platform_device *pdev)
reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc");
if (!IS_ERR(reg_vmmc)) {
ret = regulator_enable(reg_vmmc);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to enable vmmc regulator: %d\n", ret);
- goto out_mmc_free;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to enable vmmc regulator\n");
ret = devm_add_action_or_reset(&pdev->dev, mxs_mmc_regulator_disable,
reg_vmmc);
if (ret)
- goto out_mmc_free;
+ return ret;
}
ssp->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(ssp->clk)) {
- ret = PTR_ERR(ssp->clk);
- goto out_mmc_free;
- }
+ if (IS_ERR(ssp->clk))
+ return PTR_ERR(ssp->clk);
+
ret = clk_prepare_enable(ssp->clk);
if (ret)
- goto out_mmc_free;
+ return ret;
ret = mxs_mmc_reset(host);
if (ret) {
@@ -668,8 +663,6 @@ out_free_dma:
dma_release_channel(ssp->dmach);
out_clk_disable:
clk_disable_unprepare(ssp->clk);
-out_mmc_free:
- mmc_free_host(mmc);
return ret;
}
@@ -685,11 +678,8 @@ static void mxs_mmc_remove(struct platform_device *pdev)
dma_release_channel(ssp->dmach);
clk_disable_unprepare(ssp->clk);
-
- mmc_free_host(mmc);
}
-#ifdef CONFIG_PM_SLEEP
static int mxs_mmc_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@@ -708,9 +698,8 @@ static int mxs_mmc_resume(struct device *dev)
return clk_prepare_enable(ssp->clk);
}
-#endif
-static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
static struct platform_driver mxs_mmc_driver = {
.probe = mxs_mmc_probe,
@@ -718,7 +707,7 @@ static struct platform_driver mxs_mmc_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &mxs_mmc_pm_ops,
+ .pm = pm_sleep_ptr(&mxs_mmc_pm_ops),
.of_match_table = mxs_mmc_dt_ids,
},
};
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 62252ad4e20d..527b89a5ed70 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -214,7 +214,7 @@ static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
host->mmc = slot->mmc;
spin_unlock_irqrestore(&host->slot_lock, flags);
no_claim:
- del_timer(&host->clk_timer);
+ timer_delete(&host->clk_timer);
if (host->current_slot != slot || !claimed)
mmc_omap_fclk_offdelay(host->current_slot);
@@ -273,7 +273,7 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
/* Keeps clock running for at least 8 cycles on valid freq */
mod_timer(&host->clk_timer, jiffies + HZ/10);
else {
- del_timer(&host->clk_timer);
+ timer_delete(&host->clk_timer);
mmc_omap_fclk_offdelay(slot);
mmc_omap_fclk_enable(host, 0);
}
@@ -326,7 +326,7 @@ mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
"closed");
}
-static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+static DEVICE_ATTR(cover_switch, 0444, mmc_omap_show_cover_switch, NULL);
static ssize_t
mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
@@ -338,7 +338,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", slot->pdata->name);
}
-static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
+static DEVICE_ATTR(slot_name, 0444, mmc_omap_show_slot_name, NULL);
static void
mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
@@ -564,7 +564,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
{
host->cmd = NULL;
- del_timer(&host->cmd_abort_timer);
+ timer_delete(&host->cmd_abort_timer);
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
@@ -639,7 +639,8 @@ static void mmc_omap_abort_command(struct work_struct *work)
static void
mmc_omap_cmd_timer(struct timer_list *t)
{
- struct mmc_omap_host *host = from_timer(host, t, cmd_abort_timer);
+ struct mmc_omap_host *host = timer_container_of(host, t,
+ cmd_abort_timer);
unsigned long flags;
spin_lock_irqsave(&host->slot_lock, flags);
@@ -655,7 +656,7 @@ mmc_omap_cmd_timer(struct timer_list *t)
static void
mmc_omap_clk_timer(struct timer_list *t)
{
- struct mmc_omap_host *host = from_timer(host, t, clk_timer);
+ struct mmc_omap_host *host = timer_container_of(host, t, clk_timer);
mmc_omap_fclk_enable(host, 0);
}
@@ -836,7 +837,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
}
if (cmd_error && host->data) {
- del_timer(&host->cmd_abort_timer);
+ timer_delete(&host->cmd_abort_timer);
host->abort = 1;
OMAP_MMC_WRITE(host, IE, 0);
disable_irq_nosync(host->irq);
@@ -879,7 +880,7 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
static void mmc_omap_cover_timer(struct timer_list *t)
{
- struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
+ struct mmc_omap_slot *slot = timer_container_of(slot, t, cover_timer);
queue_work(system_bh_wq, &slot->cover_bh_work);
}
@@ -1258,7 +1259,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
struct mmc_host *mmc;
int r;
- mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev);
+ mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot));
if (mmc == NULL)
return -ENOMEM;
@@ -1275,11 +1276,13 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
if (IS_ERR(slot->vsd))
return dev_err_probe(host->dev, PTR_ERR(slot->vsd),
"error looking up VSD GPIO\n");
+
slot->vio = devm_gpiod_get_index_optional(host->dev, "vio",
id, GPIOD_OUT_LOW);
if (IS_ERR(slot->vio))
return dev_err_probe(host->dev, PTR_ERR(slot->vio),
"error looking up VIO GPIO\n");
+
slot->cover = devm_gpiod_get_index_optional(host->dev, "cover",
id, GPIOD_IN);
if (IS_ERR(slot->cover))
@@ -1344,7 +1347,6 @@ err_remove_slot_name:
device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
err_remove_host:
mmc_remove_host(mmc);
- mmc_free_host(mmc);
return r;
}
@@ -1358,11 +1360,10 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
cancel_work_sync(&slot->cover_bh_work);
- del_timer_sync(&slot->cover_timer);
+ timer_delete_sync(&slot->cover_timer);
flush_workqueue(slot->host->mmc_omap_wq);
mmc_remove_host(mmc);
- mmc_free_host(mmc);
}
static int mmc_omap_probe(struct platform_device *pdev)
@@ -1476,7 +1477,7 @@ static int mmc_omap_probe(struct platform_device *pdev)
host->nr_slots = pdata->nr_slots;
host->reg_shift = (mmc_omap7xx() ? 1 : 2);
- host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
+ host->mmc_omap_wq = alloc_workqueue("mmc_omap", WQ_PERCPU, 0);
if (!host->mmc_omap_wq) {
ret = -ENOMEM;
goto err_plat_cleanup;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 59e36e0ebbbf..58c881f2725b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -620,8 +620,6 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
}
-#ifdef CONFIG_PM
-
/*
* Restore the MMC host context, if it was lost as result of a
* power state change.
@@ -689,6 +687,7 @@ out:
return 0;
}
+#ifdef CONFIG_PM
/*
* Save the MMC host context (store the number of power state changes so far).
*/
@@ -747,7 +746,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", mmc_pdata(host)->name);
}
-static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
+static DEVICE_ATTR(slot_name, 0444, omap_hsmmc_show_slot_name, NULL);
/*
* Configure the response type and send the cmd.
@@ -1663,7 +1662,6 @@ static int mmc_regs_show(struct seq_file *s, void *data)
seq_printf(s, "CAPA:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CAPA));
- pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
@@ -1674,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(mmc_regs);
static void omap_hsmmc_debugfs(struct mmc_host *mmc)
{
if (mmc->debugfs_root)
- debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root,
+ debugfs_create_file("regs", 0400, mmc->debugfs_root,
mmc, &mmc_regs_fops);
}
@@ -1798,15 +1796,13 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto err;
- }
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
+ if (!mmc)
+ return -ENOMEM;
ret = mmc_of_parse(mmc);
if (ret)
- goto err1;
+ return ret;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -1842,7 +1838,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (IS_ERR(host->fclk)) {
ret = PTR_ERR(host->fclk);
host->fclk = NULL;
- goto err1;
+ return ret;
}
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
@@ -1956,7 +1952,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
}
omap_hsmmc_debugfs(mmc);
- pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
@@ -1973,9 +1968,6 @@ err_irq:
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_disable_unprepare(host->dbclk);
-err1:
- mmc_free_host(mmc);
-err:
return ret;
}
@@ -1995,11 +1987,8 @@ static void omap_hsmmc_remove(struct platform_device *pdev)
pm_runtime_disable(host->dev);
device_init_wakeup(&pdev->dev, false);
clk_disable_unprepare(host->dbclk);
-
- mmc_free_host(host->mmc);
}
-#ifdef CONFIG_PM_SLEEP
static int omap_hsmmc_suspend(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
@@ -2038,13 +2027,10 @@ static int omap_hsmmc_resume(struct device *dev)
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
omap_hsmmc_conf_bus_power(host);
- pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
}
-#endif
-#ifdef CONFIG_PM
static int omap_hsmmc_runtime_suspend(struct device *dev)
{
struct omap_hsmmc_host *host;
@@ -2112,11 +2098,10 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
spin_unlock_irqrestore(&host->irq_lock, flags);
return 0;
}
-#endif
static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
- SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
+ RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL)
};
static struct platform_driver omap_hsmmc_driver = {
@@ -2125,7 +2110,7 @@ static struct platform_driver omap_hsmmc_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &omap_hsmmc_dev_pm_ops,
+ .pm = pm_ptr(&omap_hsmmc_dev_pm_ops),
.of_match_table = of_match_ptr(omap_mmc_of_match),
},
};
diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c
index 797ef48d9204..dc585726b66e 100644
--- a/drivers/mmc/host/owl-mmc.c
+++ b/drivers/mmc/host/owl-mmc.c
@@ -567,7 +567,7 @@ static int owl_mmc_probe(struct platform_device *pdev)
struct resource *res;
int ret;
- mmc = mmc_alloc_host(sizeof(struct owl_mmc_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*owl_host));
if (!mmc) {
dev_err(&pdev->dev, "mmc alloc host failed\n");
return -ENOMEM;
@@ -580,24 +580,18 @@ static int owl_mmc_probe(struct platform_device *pdev)
spin_lock_init(&owl_host->lock);
owl_host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(owl_host->base)) {
- ret = PTR_ERR(owl_host->base);
- goto err_free_host;
- }
+ if (IS_ERR(owl_host->base))
+ return PTR_ERR(owl_host->base);
owl_host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(owl_host->clk)) {
- dev_err(&pdev->dev, "No clock defined\n");
- ret = PTR_ERR(owl_host->clk);
- goto err_free_host;
- }
+ if (IS_ERR(owl_host->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->clk),
+ "No clock defined\n");
owl_host->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
- if (IS_ERR(owl_host->reset)) {
- dev_err(&pdev->dev, "Could not get reset control\n");
- ret = PTR_ERR(owl_host->reset);
- goto err_free_host;
- }
+ if (IS_ERR(owl_host->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->reset),
+ "Could not get reset control\n");
mmc->ops = &owl_mmc_ops;
mmc->max_blk_count = 512;
@@ -616,16 +610,14 @@ static int owl_mmc_probe(struct platform_device *pdev)
ret = mmc_of_parse(mmc);
if (ret)
- goto err_free_host;
+ return ret;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
owl_host->dma = dma_request_chan(&pdev->dev, "mmc");
- if (IS_ERR(owl_host->dma)) {
- dev_err(owl_host->dev, "Failed to get external DMA channel.\n");
- ret = PTR_ERR(owl_host->dma);
- goto err_free_host;
- }
+ if (IS_ERR(owl_host->dma))
+ return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->dma),
+ "Failed to get external DMA channel.\n");
dev_info(&pdev->dev, "Using %s for DMA transfers\n",
dma_chan_name(owl_host->dma));
@@ -662,8 +654,6 @@ static int owl_mmc_probe(struct platform_device *pdev)
err_release_channel:
dma_release_channel(owl_host->dma);
-err_free_host:
- mmc_free_host(mmc);
return ret;
}
@@ -676,7 +666,6 @@ static void owl_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
disable_irq(owl_host->irq);
dma_release_channel(owl_host->dma);
- mmc_free_host(mmc);
}
static const struct of_device_id owl_mmc_of_match[] = {
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 2d0ad006913d..b5ea058ed467 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -615,11 +615,9 @@ static int pxamci_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto out;
- }
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
+ if (!mmc)
+ return -ENOMEM;
mmc->ops = &pxamci_ops;
@@ -646,7 +644,7 @@ static int pxamci_probe(struct platform_device *pdev)
ret = pxamci_of_init(pdev, mmc);
if (ret)
- goto out;
+ return ret;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -654,11 +652,9 @@ static int pxamci_probe(struct platform_device *pdev)
host->clkrt = CLKRT_OFF;
host->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- host->clk = NULL;
- goto out;
- }
+ if (IS_ERR(host->clk))
+ return dev_err_probe(dev, PTR_ERR(host->clk),
+ "Failed to acquire clock\n");
host->clkrate = clk_get_rate(host->clk);
@@ -670,7 +666,7 @@ static int pxamci_probe(struct platform_device *pdev)
ret = pxamci_init_ocr(host);
if (ret < 0)
- goto out;
+ return ret;
mmc->caps = 0;
host->cmdat = 0;
@@ -686,10 +682,8 @@ static int pxamci_probe(struct platform_device *pdev)
host->imask = MMC_I_MASK_ALL;
host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto out;
- }
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
host->res = r;
/*
@@ -704,51 +698,41 @@ static int pxamci_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, pxamci_irq, 0,
DRIVER_NAME, host);
if (ret)
- goto out;
+ return ret;
platform_set_drvdata(pdev, mmc);
- host->dma_chan_rx = dma_request_chan(dev, "rx");
- if (IS_ERR(host->dma_chan_rx)) {
- dev_err(dev, "unable to request rx dma channel\n");
- ret = PTR_ERR(host->dma_chan_rx);
- host->dma_chan_rx = NULL;
- goto out;
- }
+ host->dma_chan_rx = devm_dma_request_chan(dev, "rx");
+ if (IS_ERR(host->dma_chan_rx))
+ return dev_err_probe(dev, PTR_ERR(host->dma_chan_rx),
+ "unable to request rx dma channel\n");
- host->dma_chan_tx = dma_request_chan(dev, "tx");
- if (IS_ERR(host->dma_chan_tx)) {
- dev_err(dev, "unable to request tx dma channel\n");
- ret = PTR_ERR(host->dma_chan_tx);
- host->dma_chan_tx = NULL;
- goto out;
- }
+
+ host->dma_chan_tx = devm_dma_request_chan(dev, "tx");
+ if (IS_ERR(host->dma_chan_tx))
+ return dev_err_probe(dev, PTR_ERR(host->dma_chan_tx),
+ "unable to request tx dma channel\n");
if (host->pdata) {
host->detect_delay_ms = host->pdata->detect_delay_ms;
host->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
- if (IS_ERR(host->power)) {
- ret = PTR_ERR(host->power);
- dev_err(dev, "Failed requesting gpio_power\n");
- goto out;
- }
+ if (IS_ERR(host->power))
+ return dev_err_probe(dev, PTR_ERR(host->power),
+ "Failed requesting gpio_power\n");
/* FIXME: should we pass detection delay to debounce? */
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
- if (ret && ret != -ENOENT) {
- dev_err(dev, "Failed requesting gpio_cd\n");
- goto out;
- }
+ if (ret && ret != -ENOENT)
+ return dev_err_probe(dev, ret, "Failed requesting gpio_cd\n");
if (!host->pdata->gpio_card_ro_invert)
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0);
- if (ret && ret != -ENOENT) {
- dev_err(dev, "Failed requesting gpio_ro\n");
- goto out;
- }
+ if (ret && ret != -ENOENT)
+ return dev_err_probe(dev, ret, "Failed requesting gpio_ro\n");
+
if (!ret)
host->use_ro_gpio = true;
@@ -765,20 +749,8 @@ static int pxamci_probe(struct platform_device *pdev)
if (ret) {
if (host->pdata && host->pdata->exit)
host->pdata->exit(dev, mmc);
- goto out;
}
- return 0;
-
-out:
- if (host) {
- if (host->dma_chan_rx)
- dma_release_channel(host->dma_chan_rx);
- if (host->dma_chan_tx)
- dma_release_channel(host->dma_chan_tx);
- }
- if (mmc)
- mmc_free_host(mmc);
return ret;
}
@@ -801,10 +773,6 @@ static void pxamci_remove(struct platform_device *pdev)
dmaengine_terminate_all(host->dma_chan_rx);
dmaengine_terminate_all(host->dma_chan_tx);
- dma_release_channel(host->dma_chan_rx);
- dma_release_channel(host->dma_chan_tx);
-
- mmc_free_host(mmc);
}
}
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index f12a87442338..afc36a407c2c 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -9,6 +9,7 @@
#ifndef RENESAS_SDHI_H
#define RENESAS_SDHI_H
+#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
@@ -85,6 +86,7 @@ struct renesas_sdhi {
u32 scc_tappos_hs400;
const u8 *adjust_hs400_calib_table;
bool needs_adjust_hs400;
+ bool card_is_sdio;
/* Tuning values: 1 for success, 0 for failure */
DECLARE_BITMAP(taps, BITS_PER_LONG);
@@ -95,6 +97,7 @@ struct renesas_sdhi {
struct reset_control *rstc;
struct tmio_mmc_host *host;
+ struct regulator_dev *rdev;
};
#define host_to_priv(host) \
@@ -105,4 +108,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
const struct renesas_sdhi_of_data *of_data,
const struct renesas_sdhi_quirks *quirks);
void renesas_sdhi_remove(struct platform_device *pdev);
+int renesas_sdhi_suspend(struct device *dev);
+int renesas_sdhi_resume(struct device *dev);
#endif
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index f73b84bae0c4..2a310a145785 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -31,7 +31,10 @@
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/reset.h>
#include <linux/sh_dma.h>
#include <linux/slab.h>
@@ -220,7 +223,11 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host,
clk &= ~0xff;
}
- sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK);
+ clock = clk & CLK_CTL_DIV_MASK;
+ if (clock != CLK_CTL_DIV_MASK)
+ host->mmc->actual_clock /= (1 << (ffs(clock) + 1));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clock);
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
usleep_range(10000, 11000);
@@ -581,12 +588,24 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
if (!preserve) {
if (priv->rstc) {
+ u32 sd_status;
+ /*
+ * HW reset might have toggled the regulator state in
+ * HW which regulator core might be unaware of so save
+ * and restore the regulator state during HW reset.
+ */
+ if (priv->rdev)
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+
reset_control_reset(priv->rstc);
/* Unknown why but without polling reset status, it will hang */
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
false, priv->rstc);
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+ if (priv->rdev)
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
priv->needs_adjust_hs400 = false;
renesas_sdhi_set_clock(host, host->clk_cache);
@@ -672,9 +691,8 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
/* Set SCC */
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, priv->tap_set);
- /* Enable auto re-tuning */
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
- SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN |
+ (priv->card_is_sdio ? 0 : SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) |
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
return 0;
@@ -764,6 +782,14 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
if (bad_taps & BIT(new_tap % priv->tap_num))
return test_bit(error_tap % priv->tap_num, priv->smpcmp);
} else {
+ if (!priv->card_is_sdio &&
+ !(val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)) {
+ u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP);
+
+ /* DAT1 is unmatched because of an SDIO irq */
+ if (smpcmp & (BIT(17) | BIT(1)))
+ return false;
+ }
if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)
return true; /* need retune */
else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP)
@@ -814,11 +840,14 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host,
if (mmc_doing_tune(host->mmc))
return false;
- if (((mrq->cmd->error == -ETIMEDOUT) ||
- (mrq->data && mrq->data->error == -ETIMEDOUT)) &&
- ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
- (host->ops.get_cd && host->ops.get_cd(host->mmc))))
- ret |= true;
+ /* mrq can be NULL to check SCC error on SDIO irq without any request */
+ if (mrq) {
+ if (((mrq->cmd->error == -ETIMEDOUT) ||
+ (mrq->data && mrq->data->error == -ETIMEDOUT)) &&
+ ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ (host->ops.get_cd && host->ops.get_cd(host->mmc))))
+ ret |= true;
+ }
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN)
@@ -829,6 +858,28 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host,
return ret;
}
+static void renesas_sdhi_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct renesas_sdhi *priv = host_to_priv(host);
+
+ /*
+ * This controller cannot do auto-retune with SDIO irqs, so we
+ * then need to enforce manual correction. However, when tuning,
+ * mmc->card is not populated yet, so we don't know if the card
+ * is SDIO. init_card provides this information earlier, so we
+ * keep a copy of it.
+ */
+ priv->card_is_sdio = mmc_card_sdio(card);
+}
+
+static void renesas_sdhi_sdio_irq(struct tmio_mmc_host *host)
+{
+ /* This controller requires retune when an SDIO irq occurs */
+ if (renesas_sdhi_check_scc_error(host, NULL))
+ mmc_retune_needed(host->mmc);
+}
+
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
{
int timeout = 1000;
@@ -904,6 +955,102 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
renesas_sdhi_sdbuf_width(host, enable ? width : 16);
}
+static const unsigned int renesas_sdhi_vqmmc_voltages[] = {
+ 3300000, 1800000
+};
+
+static int renesas_sdhi_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+ sd_status &= ~SD_STATUS_PWEN;
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+static int renesas_sdhi_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+ sd_status |= SD_STATUS_PWEN;
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+static int renesas_sdhi_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+
+ return (sd_status & SD_STATUS_PWEN) ? 1 : 0;
+}
+
+static int renesas_sdhi_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+
+ return (sd_status & SD_STATUS_IOVS) ? 1800000 : 3300000;
+}
+
+static int renesas_sdhi_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV,
+ unsigned int *selector)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+ if (min_uV >= 1700000 && max_uV <= 1950000) {
+ sd_status |= SD_STATUS_IOVS;
+ *selector = 1;
+ } else {
+ sd_status &= ~SD_STATUS_IOVS;
+ *selector = 0;
+ }
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+static int renesas_sdhi_regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages))
+ return -EINVAL;
+
+ return renesas_sdhi_vqmmc_voltages[selector];
+}
+
+static const struct regulator_ops renesas_sdhi_regulator_voltage_ops = {
+ .enable = renesas_sdhi_regulator_enable,
+ .disable = renesas_sdhi_regulator_disable,
+ .is_enabled = renesas_sdhi_regulator_is_enabled,
+ .list_voltage = renesas_sdhi_regulator_list_voltage,
+ .get_voltage = renesas_sdhi_regulator_get_voltage,
+ .set_voltage = renesas_sdhi_regulator_set_voltage,
+};
+
+static const struct regulator_desc renesas_sdhi_vqmmc_regulator = {
+ .name = "sdhi-vqmmc-regulator",
+ .of_match = of_match_ptr("vqmmc-regulator"),
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &renesas_sdhi_regulator_voltage_ops,
+ .volt_table = renesas_sdhi_vqmmc_voltages,
+ .n_voltages = ARRAY_SIZE(renesas_sdhi_vqmmc_voltages),
+};
+
int renesas_sdhi_probe(struct platform_device *pdev,
const struct tmio_mmc_dma_ops *dma_ops,
const struct renesas_sdhi_of_data *of_data,
@@ -911,7 +1058,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
{
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
struct tmio_mmc_data *mmc_data;
+ struct regulator_config rcfg = { .dev = &pdev->dev, };
+ struct regulator_dev *rdev;
struct renesas_sdhi_dma *dma_priv;
+ struct device *dev = &pdev->dev;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@@ -954,7 +1104,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (IS_ERR(priv->clk_cd))
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock");
- priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
+ priv->rstc = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
if (IS_ERR(priv->rstc))
return PTR_ERR(priv->rstc);
@@ -999,7 +1149,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
/* For some SoC, we disable internal WP. GPIO may override this */
- if (mmc_can_gpio_ro(host->mmc))
+ if (mmc_host_can_gpio_ro(host->mmc))
mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
/* SDR speeds are only available on Gen2+ */
@@ -1051,7 +1201,20 @@ int renesas_sdhi_probe(struct platform_device *pdev,
ret = renesas_sdhi_clk_enable(host);
if (ret)
- goto efree;
+ return ret;
+
+ rcfg.of_node = of_get_available_child_by_name(dev->of_node, "vqmmc-regulator");
+ if (rcfg.of_node) {
+ rcfg.driver_data = priv->host;
+ rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
+ of_node_put(rcfg.of_node);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev));
+ ret = PTR_ERR(rdev);
+ goto edisclk;
+ }
+ priv->rdev = rdev;
+ }
ver = sd_ctrl_read16(host, CTL_VERSION);
/* GEN2_SDR104 is first known SDHI to use 32bit block count */
@@ -1101,6 +1264,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n");
host->check_retune = renesas_sdhi_check_scc_error;
+ host->sdio_irq = renesas_sdhi_sdio_irq;
+ host->ops.init_card = renesas_sdhi_init_card;
host->ops.execute_tuning = renesas_sdhi_execute_tuning;
host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning;
host->ops.hs400_downgrade = renesas_sdhi_disable_scc;
@@ -1109,29 +1274,24 @@ int renesas_sdhi_probe(struct platform_device *pdev,
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all);
- num_irqs = platform_irq_count(pdev);
- if (num_irqs < 0) {
- ret = num_irqs;
- goto eirq;
- }
-
/* There must be at least one IRQ source */
- if (!num_irqs) {
- ret = -ENXIO;
- goto eirq;
+ num_irqs = platform_irq_count(pdev);
+ if (num_irqs <= 0) {
+ ret = num_irqs ?: -ENOENT;
+ goto edisclk;
}
for (i = 0; i < num_irqs; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0) {
ret = irq;
- goto eirq;
+ goto edisclk;
}
ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
- goto eirq;
+ goto edisclk;
}
ret = tmio_mmc_host_probe(host);
@@ -1143,13 +1303,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
return ret;
-eirq:
- tmio_mmc_host_remove(host);
edisclk:
renesas_sdhi_clk_disable(host);
-efree:
- tmio_mmc_host_free(host);
-
return ret;
}
EXPORT_SYMBOL_GPL(renesas_sdhi_probe);
@@ -1160,9 +1315,44 @@ void renesas_sdhi_remove(struct platform_device *pdev)
tmio_mmc_host_remove(host);
renesas_sdhi_clk_disable(host);
- tmio_mmc_host_free(host);
}
EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
+int renesas_sdhi_suspend(struct device *dev)
+{
+ struct tmio_mmc_host *host = dev_get_drvdata(dev);
+ struct renesas_sdhi *priv = host_to_priv(host);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ ret = reset_control_assert(priv->rstc);
+ if (ret)
+ pm_runtime_force_resume(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(renesas_sdhi_suspend);
+
+int renesas_sdhi_resume(struct device *dev)
+{
+ struct tmio_mmc_host *host = dev_get_drvdata(dev);
+ struct renesas_sdhi *priv = host_to_priv(host);
+ int ret;
+
+ ret = reset_control_deassert(priv->rstc);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ reset_control_assert(priv->rstc);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(renesas_sdhi_resume);
+
MODULE_DESCRIPTION("Renesas SDHI core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 4b389e92399e..f6ebb7bc7ede 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -18,7 +18,6 @@
#include <linux/pagemap.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/sys_soc.h>
@@ -107,7 +106,8 @@ static const struct renesas_sdhi_of_data of_data_rza2 = {
static const struct renesas_sdhi_of_data of_data_rcar_gen3 = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
- TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
+ TMIO_MMC_64BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
@@ -123,7 +123,8 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3 = {
static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
- TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
+ TMIO_MMC_64BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
@@ -598,18 +599,17 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
}
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
- tmio_mmc_host_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(renesas_sdhi_suspend, renesas_sdhi_resume)
+ RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+ tmio_mmc_host_runtime_resume,
+ NULL)
};
static struct platform_driver renesas_internal_dmac_sdhi_driver = {
.driver = {
.name = "renesas_sdhi_internal_dmac",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &renesas_sdhi_internal_dmac_dev_pm_ops,
+ .pm = pm_ptr(&renesas_sdhi_internal_dmac_dev_pm_ops),
.of_match_table = renesas_sdhi_internal_dmac_of_match,
},
.probe = renesas_sdhi_internal_dmac_probe,
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 822a310c9bba..543ad1d0ed1c 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -60,7 +60,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
- TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
+ TMIO_MMC_32BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 48d3b0aae5a0..dc2587ff8519 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -115,8 +115,6 @@ static int sd_response_type(struct mmc_command *cmd)
return SD_RSP_TYPE_R0;
case MMC_RSP_R1:
return SD_RSP_TYPE_R1;
- case MMC_RSP_R1_NO_CRC:
- return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
case MMC_RSP_R1B:
return SD_RSP_TYPE_R1b;
case MMC_RSP_R2:
@@ -1500,7 +1498,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n");
- mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -1531,7 +1529,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
if (ret) {
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- mmc_free_host(mmc);
return ret;
}
@@ -1574,8 +1571,6 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- mmc_free_host(mmc);
-
dev_dbg(&(pdev->dev),
": Realtek PCI-E SDMMC controller has been removed\n");
}
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 107c78df53cf..84674659a84d 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -48,7 +48,7 @@ struct rtsx_usb_sdmmc {
bool ddr_mode;
unsigned char power_mode;
-
+ u16 ocp_stat;
#ifdef RTSX_USB_USE_LEDS_CLASS
struct led_classdev led;
char led_name[32];
@@ -313,9 +313,6 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
case MMC_RSP_R1:
rsp_type = SD_RSP_TYPE_R1;
break;
- case MMC_RSP_R1_NO_CRC:
- rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
- break;
case MMC_RSP_R1B:
rsp_type = SD_RSP_TYPE_R1b;
break;
@@ -792,12 +789,20 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
if (err)
goto no_card;
+ /* get OCP status */
+ host->ocp_stat = (val >> 4) & 0x03;
+
if (val & SD_CD) {
host->card_exist = true;
return 1;
}
no_card:
+ /* clear OCP status */
+ if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
+ rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR);
+ host->ocp_stat = 0;
+ }
host->card_exist = false;
return 0;
}
@@ -821,7 +826,11 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
cmd->error = -ENOMEDIUM;
goto finish_detect_card;
}
-
+ /* check OCP stat */
+ if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
+ cmd->error = -ENOMEDIUM;
+ goto finish_detect_card;
+ }
mutex_lock(&ucr->dev_mutex);
mutex_lock(&host->host_mutex);
@@ -955,6 +964,10 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
struct rtsx_ucr *ucr = host->ucr;
int err;
+ if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
+ dev_dbg(sdmmc_dev(host), "over current\n");
+ return -EIO;
+ }
dev_dbg(sdmmc_dev(host), "%s\n", __func__);
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
@@ -981,8 +994,18 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
usleep_range(800, 1000);
rtsx_usb_init_cmd(ucr);
+ /* WA OCP issue: after OCP, there were problems with reopen card power */
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, POWER_MASK, POWER_ON);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, FPDCTL, SSC_POWER_MASK, SSC_POWER_DOWN);
+ err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
+ if (err)
+ return err;
+ msleep(20);
+ rtsx_usb_write_register(ucr, FPDCTL, SSC_POWER_MASK, SSC_POWER_ON);
+ usleep_range(180, 200);
+ rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
- POWER_MASK|LDO3318_PWR_MASK, POWER_ON|LDO_ON);
+ LDO3318_PWR_MASK, LDO_ON);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
SD_OUTPUT_EN, SD_OUTPUT_EN);
@@ -1013,29 +1036,45 @@ static int sd_power_off(struct rtsx_usb_sdmmc *host)
return sd_pull_ctl_disable_qfn24(ucr);
}
-static int sd_set_power_mode(struct rtsx_usb_sdmmc *host,
+static void sd_set_power_mode(struct rtsx_usb_sdmmc *host,
unsigned char power_mode)
{
int err;
-
- if (power_mode != MMC_POWER_OFF)
- power_mode = MMC_POWER_ON;
+ struct rtsx_ucr *ucr = host->ucr;
if (power_mode == host->power_mode)
- return 0;
+ return;
- if (power_mode == MMC_POWER_OFF) {
+ switch (power_mode) {
+ case MMC_POWER_OFF:
err = sd_power_off(host);
+ if (err)
+ dev_dbg(sdmmc_dev(host), "power-off (err = %d)\n", err);
pm_runtime_put_noidle(sdmmc_dev(host));
- } else {
+ break;
+
+ case MMC_POWER_UP:
pm_runtime_get_noresume(sdmmc_dev(host));
err = sd_power_on(host);
- }
+ if (err)
+ dev_dbg(sdmmc_dev(host), "power-on (err = %d)\n", err);
+ /* issue the clock signals to card at least 74 clocks */
+ rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, SD_CLK_TOGGLE_EN);
+ break;
+
+ case MMC_POWER_ON:
+ /* stop to send the clock signals */
+ rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, 0x00);
+ break;
- if (!err)
- host->power_mode = power_mode;
+ case MMC_POWER_UNDEFINED:
+ break;
- return err;
+ default:
+ break;
+ }
+
+ host->power_mode = power_mode;
}
static int sd_set_timing(struct rtsx_usb_sdmmc *host,
@@ -1319,6 +1358,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
mmc->max_req_size = 524288;
host->power_mode = MMC_POWER_OFF;
+ host->ocp_stat = 0;
}
static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
@@ -1337,7 +1377,7 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
dev_dbg(&(pdev->dev), ": Realtek USB SD/MMC controller found\n");
- mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -1371,7 +1411,6 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
#ifdef RTSX_USB_USE_LEDS_CLASS
led_classdev_unregister(&host->led);
#endif
- mmc_free_host(mmc);
pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -1409,7 +1448,6 @@ static void rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
led_classdev_unregister(&host->led);
#endif
- mmc_free_host(mmc);
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
@@ -1417,7 +1455,6 @@ static void rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
": Realtek USB SD/MMC module has been removed\n");
}
-#ifdef CONFIG_PM
static int rtsx_usb_sdmmc_runtime_suspend(struct device *dev)
{
struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev);
@@ -1435,11 +1472,9 @@ static int rtsx_usb_sdmmc_runtime_resume(struct device *dev)
mmc_detect_change(host->mmc, 0);
return 0;
}
-#endif
static const struct dev_pm_ops rtsx_usb_sdmmc_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend,
- rtsx_usb_sdmmc_runtime_resume, NULL)
+ RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend, rtsx_usb_sdmmc_runtime_resume, NULL)
};
static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
@@ -1458,7 +1493,7 @@ static struct platform_driver rtsx_usb_sdmmc_driver = {
.driver = {
.name = "rtsx_usb_sdmmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &rtsx_usb_sdmmc_dev_pm_ops,
+ .pm = pm_ptr(&rtsx_usb_sdmmc_dev_pm_ops),
},
};
module_platform_driver(rtsx_usb_sdmmc_driver);
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index d1ce9193ece9..84c7054607fc 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -822,8 +822,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
struct acpi_device *device;
struct sdhci_acpi_host *c;
struct sdhci_host *host;
- struct resource *iomem;
- resource_size_t len;
size_t priv_size;
int quirks = 0;
int err;
@@ -844,17 +842,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (sdhci_acpi_byt_defer(dev))
return -EPROBE_DEFER;
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iomem)
- return -ENOMEM;
-
- len = resource_size(iomem);
- if (len < 0x100)
- dev_err(dev, "Invalid iomem size!\n");
-
- if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev)))
- return -ENOMEM;
-
priv_size = slot ? slot->priv_size : 0;
host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host) + priv_size);
if (IS_ERR(host))
@@ -876,10 +863,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
goto err_free;
}
- host->ioaddr = devm_ioremap(dev, iomem->start,
- resource_size(iomem));
- if (host->ioaddr == NULL) {
- err = -ENOMEM;
+ host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(host->ioaddr)) {
+ err = PTR_ERR(host->ioaddr);
goto err_free;
}
@@ -962,7 +948,6 @@ err_free:
if (c->slot && c->slot->free_slot)
c->slot->free_slot(pdev);
- sdhci_free_host(c->host);
return err;
}
@@ -986,12 +971,9 @@ static void sdhci_acpi_remove(struct platform_device *pdev)
if (c->slot && c->slot->free_slot)
c->slot->free_slot(pdev);
-
- sdhci_free_host(c->host);
}
-static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed(
- struct device *dev)
+static void sdhci_acpi_reset_signal_voltage_if_needed(struct device *dev)
{
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
struct sdhci_host *host = c->host;
@@ -1006,8 +988,6 @@ static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed(
}
}
-#ifdef CONFIG_PM_SLEEP
-
static int sdhci_acpi_suspend(struct device *dev)
{
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
@@ -1034,22 +1014,15 @@ static int sdhci_acpi_resume(struct device *dev)
return sdhci_resume_host(c->host);
}
-#endif
-
-#ifdef CONFIG_PM
-
static int sdhci_acpi_runtime_suspend(struct device *dev)
{
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
struct sdhci_host *host = c->host;
- int ret;
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
- ret = sdhci_runtime_suspend_host(host);
- if (ret)
- return ret;
+ sdhci_runtime_suspend_host(host);
sdhci_acpi_reset_signal_voltage_if_needed(dev);
return 0;
@@ -1061,15 +1034,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
sdhci_acpi_byt_setting(&c->pdev->dev);
- return sdhci_runtime_resume_host(c->host, 0);
+ sdhci_runtime_resume_host(c->host, 0);
+ return 0;
}
-#endif
-
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
- SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
- sdhci_acpi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
+ RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, sdhci_acpi_runtime_resume, NULL)
};
static struct platform_driver sdhci_acpi_driver = {
@@ -1077,7 +1048,7 @@ static struct platform_driver sdhci_acpi_driver = {
.name = "sdhci-acpi",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.acpi_match_table = sdhci_acpi_ids,
- .pm = &sdhci_acpi_pm_ops,
+ .pm = pm_ptr(&sdhci_acpi_pm_ops),
},
.probe = sdhci_acpi_probe,
.remove = sdhci_acpi_remove,
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index fda911fb28e5..0f2a84f769b6 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -304,8 +304,6 @@ err_clk_disable:
clk_disable_unprepare(pltfm_priv->clk);
err_pltfm_free:
- sdhci_pltfm_free(pdev);
-
dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret);
return ret;
}
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index 0ef4d578ade8..c9442499876c 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -31,35 +31,116 @@
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
-#define SDIO_CFG_CQ_CAPABILITY 0x4c
-#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
-
#define SDIO_CFG_CTRL 0x0
#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
-
+#define SDIO_CFG_OP_DLY 0x34
+#define SDIO_CFG_OP_DLY_DEFAULT 0x80000003
+#define SDIO_CFG_CQ_CAPABILITY 0x4c
+#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
+#define SDIO_CFG_SD_PIN_SEL 0x44
+#define SDIO_CFG_V1_SD_PIN_SEL 0x54
+#define SDIO_CFG_PHY_SW_MODE_0_RX_CTRL 0x7C
#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
+#define SDIO_BOOT_MAIN_CTL 0x0
+
#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
/* Select all SD UHS type I SDR speed above 50MB/s */
#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
-struct sdhci_brcmstb_priv {
- void __iomem *cfg_regs;
- unsigned int flags;
- struct clk *base_clk;
- u32 base_freq_hz;
+enum cfg_core_ver {
+ SDIO_CFG_CORE_V1 = 1,
+ SDIO_CFG_CORE_V2,
+};
+
+struct sdhci_brcmstb_saved_regs {
+ u32 sd_pin_sel;
+ u32 phy_sw_mode0_rxctrl;
+ u32 max_50mhz_mode;
+ u32 boot_main_ctl;
};
struct brcmstb_match_priv {
void (*cfginit)(struct sdhci_host *host);
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
+ void (*save_restore_regs)(struct mmc_host *mmc, int save);
struct sdhci_ops *ops;
const unsigned int flags;
};
+struct sdhci_brcmstb_priv {
+ void __iomem *cfg_regs;
+ void __iomem *boot_regs;
+ struct sdhci_brcmstb_saved_regs saved_regs;
+ unsigned int flags;
+ struct clk *base_clk;
+ u32 base_freq_hz;
+ const struct brcmstb_match_priv *match_priv;
+};
+
+static void sdhci_brcmstb_save_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
+ void __iomem *cr = priv->cfg_regs;
+ bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
+
+ if (is_emmc && priv->boot_regs)
+ sr->boot_main_ctl = readl(priv->boot_regs + SDIO_BOOT_MAIN_CTL);
+
+ if (ver == SDIO_CFG_CORE_V1) {
+ sr->sd_pin_sel = readl(cr + SDIO_CFG_V1_SD_PIN_SEL);
+ return;
+ }
+
+ sr->sd_pin_sel = readl(cr + SDIO_CFG_SD_PIN_SEL);
+ sr->phy_sw_mode0_rxctrl = readl(cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
+ sr->max_50mhz_mode = readl(cr + SDIO_CFG_MAX_50MHZ_MODE);
+}
+
+static void sdhci_brcmstb_restore_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
+ void __iomem *cr = priv->cfg_regs;
+ bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
+
+ if (is_emmc && priv->boot_regs)
+ writel(sr->boot_main_ctl, priv->boot_regs + SDIO_BOOT_MAIN_CTL);
+
+ if (ver == SDIO_CFG_CORE_V1) {
+ writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
+ return;
+ }
+
+ writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
+ writel(sr->phy_sw_mode0_rxctrl, cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
+ writel(sr->max_50mhz_mode, cr + SDIO_CFG_MAX_50MHZ_MODE);
+}
+
+static void sdhci_brcmstb_save_restore_regs_v1(struct mmc_host *mmc, int save)
+{
+ if (save)
+ sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V1);
+ else
+ sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V1);
+}
+
+static void sdhci_brcmstb_save_restore_regs_v2(struct mmc_host *mmc, int save)
+{
+ if (save)
+ sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V2);
+ else
+ sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V2);
+}
+
static inline void enable_clock_gating(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -212,6 +293,21 @@ static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
}
}
+static void sdhci_brcmstb_set_72116_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /* no change to SDIO_CFG_OP_DLY_DEFAULT when using preset clk rate */
+ if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
+ return;
+
+ reg = (timing == MMC_TIMING_MMC_HS200) ? 0 : SDIO_CFG_OP_DLY_DEFAULT;
+ writel(reg, priv->cfg_regs + SDIO_CFG_OP_DLY);
+ sdhci_set_uhs_signaling(host, timing);
+}
+
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{
sdhci_dumpregs(mmc_priv(mmc));
@@ -252,6 +348,13 @@ static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
+static struct sdhci_ops sdhci_brcmstb_ops_72116 = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_brcmstb_set_72116_uhs_signaling,
+};
+
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width,
@@ -277,19 +380,33 @@ static struct brcmstb_match_priv match_priv_7425 = {
.ops = &sdhci_brcmstb_ops,
};
+static struct brcmstb_match_priv match_priv_74371 = {
+ .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
+ .ops = &sdhci_brcmstb_ops,
+};
+
static struct brcmstb_match_priv match_priv_7445 = {
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
.ops = &sdhci_brcmstb_ops,
};
+static struct brcmstb_match_priv match_priv_72116 = {
+ .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
+ .ops = &sdhci_brcmstb_ops_72116,
+};
+
static const struct brcmstb_match_priv match_priv_7216 = {
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
.hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_7216,
};
static struct brcmstb_match_priv match_priv_74165b0 = {
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
.hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_74165b0,
};
@@ -297,7 +414,9 @@ static struct brcmstb_match_priv match_priv_74165b0 = {
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
+ { .compatible = "brcm,bcm74371-sdhci", .data = &match_priv_74371 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
+ { .compatible = "brcm,bcm72116-sdhci", .data = &match_priv_72116 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
{ .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 },
{},
@@ -395,6 +514,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
priv = sdhci_pltfm_priv(pltfm_host);
+ priv->match_priv = match->data;
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE;
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
@@ -412,6 +532,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
if (res)
goto err;
+ /* map non-standard BOOT registers if present */
+ if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
+ priv->boot_regs = devm_platform_get_and_ioremap_resource(pdev, 2, NULL);
+ if (IS_ERR(priv->boot_regs))
+ priv->boot_regs = NULL;
+ }
+
/*
* Automatic clock gating does not work for SD cards that may
* voltage switch so only enable it for non-removable devices.
@@ -485,7 +612,6 @@ add_host:
return res;
err:
- sdhci_pltfm_free(pdev);
clk_disable_unprepare(base_clk);
return res;
}
@@ -497,14 +623,25 @@ static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
-#ifdef CONFIG_PM_SLEEP
static int sdhci_brcmstb_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ const struct brcmstb_match_priv *match_priv = priv->match_priv;
+
+ int ret;
+
+ if (match_priv->save_restore_regs)
+ match_priv->save_restore_regs(host->mmc, 1);
clk_disable_unprepare(priv->base_clk);
+ if (host->mmc->caps2 & MMC_CAP2_CQE) {
+ ret = cqhci_suspend(host->mmc);
+ if (ret)
+ return ret;
+ }
+
return sdhci_pltfm_suspend(dev);
}
@@ -513,6 +650,7 @@ static int sdhci_brcmstb_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ const struct brcmstb_match_priv *match_priv = priv->match_priv;
int ret;
ret = sdhci_pltfm_resume(dev);
@@ -529,19 +667,22 @@ static int sdhci_brcmstb_resume(struct device *dev)
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
}
+ if (match_priv->save_restore_regs)
+ match_priv->save_restore_regs(host->mmc, 0);
+
+ if (host->mmc->caps2 & MMC_CAP2_CQE)
+ ret = cqhci_resume(host->mmc);
+
return ret;
}
-#endif
-static const struct dev_pm_ops sdhci_brcmstb_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend, sdhci_brcmstb_resume);
static struct platform_driver sdhci_brcmstb_driver = {
.driver = {
.name = "sdhci-brcmstb",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_brcmstb_pmops,
+ .pm = pm_sleep_ptr(&sdhci_brcmstb_pmops),
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
},
.probe = sdhci_brcmstb_probe,
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index a94b297fcf2a..435603c8c00b 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -36,6 +36,24 @@
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
+/* Read block gap */
+#define SDHCI_CDNS_HRS37 0x94 /* interface mode select */
+#define SDHCI_CDNS_HRS37_MODE_DS 0x0
+#define SDHCI_CDNS_HRS37_MODE_HS 0x1
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR12 0x8
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR25 0x9
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR50 0xa
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR104 0xb
+#define SDHCI_CDNS_HRS37_MODE_UDS_DDR50 0xc
+#define SDHCI_CDNS_HRS37_MODE_MMC_LEGACY 0x20
+#define SDHCI_CDNS_HRS37_MODE_MMC_SDR 0x21
+#define SDHCI_CDNS_HRS37_MODE_MMC_DDR 0x22
+#define SDHCI_CDNS_HRS37_MODE_MMC_HS200 0x23
+#define SDHCI_CDNS_HRS37_MODE_MMC_HS400 0x24
+#define SDHCI_CDNS_HRS37_MODE_MMC_HS400ES 0x25
+#define SDHCI_CDNS_HRS38 0x98 /* Read block gap coefficient */
+#define SDHCI_CDNS_HRS38_BLKGAP_MAX 0xf
+
/* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200
@@ -144,7 +162,7 @@ static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
int i;
for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
- if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
+ if (of_property_present(np, sdhci_cdns_phy_cfgs[i].property))
count++;
return count;
@@ -251,6 +269,43 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
return 0;
}
+/**
+ * sdhci_cdns_tune_blkgap() - tune multi-block read gap
+ * @mmc: MMC host
+ *
+ * Tune delay used in multi block read. To do so,
+ * try sending multi-block read command with incremented gap, unless
+ * it succeeds.
+ *
+ * Return: error code
+ */
+static int sdhci_cdns_tune_blkgap(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ void __iomem *hrs37_reg = priv->hrs_addr + SDHCI_CDNS_HRS37;
+ void __iomem *hrs38_reg = priv->hrs_addr + SDHCI_CDNS_HRS38;
+ int ret;
+ u32 gap;
+
+ /* Currently only needed in HS200 mode */
+ if (host->timing != MMC_TIMING_MMC_HS200)
+ return 0;
+
+ writel(SDHCI_CDNS_HRS37_MODE_MMC_HS200, hrs37_reg);
+
+ for (gap = 0; gap <= SDHCI_CDNS_HRS38_BLKGAP_MAX; gap++) {
+ writel(gap, hrs38_reg);
+ ret = mmc_read_tuning(mmc, 512, 32);
+ if (!ret)
+ break;
+ }
+
+ dev_dbg(mmc_dev(mmc), "read block gap tune %s, gap %d\n", ret ? "failed" : "OK", gap);
+ return ret;
+}
+
/*
* In SD mode, software must not use the hardware tuning and instead perform
* an almost identical procedure to eMMC.
@@ -261,6 +316,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
int max_streak = 0;
int end_of_streak = 0;
int i;
+ int ret;
/*
* Do not execute tuning for UHS_SDR50 or UHS_DDR50.
@@ -288,7 +344,11 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
return -EIO;
}
- return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+ ret = sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+ if (ret)
+ return ret;
+
+ return sdhci_cdns_tune_blkgap(host->mmc);
}
static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
@@ -433,6 +493,13 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = {
},
};
+static const struct sdhci_cdns_drv_data sdhci_eyeq_drv_data = {
+ .pltfm_data = {
+ .ops = &sdhci_cdns_ops,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+};
+
static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = {
.pltfm_data = {
.ops = &sdhci_cdns_ops,
@@ -515,7 +582,7 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
if (data->init) {
ret = data->init(pdev);
if (ret)
- goto free;
+ return ret;
}
sdhci_enable_v4_mode(host);
__sdhci_read_caps(host, &version, NULL, NULL);
@@ -524,36 +591,26 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto free;
+ return ret;
sdhci_cdns_phy_param_parse(dev->of_node, priv);
ret = sdhci_cdns_phy_init(priv);
if (ret)
- goto free;
+ return ret;
if (host->mmc->caps & MMC_CAP_HW_RESET) {
priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL);
- if (IS_ERR(priv->rst_hw)) {
- ret = dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw),
+ if (IS_ERR(priv->rst_hw))
+ return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw),
"reset controller error\n");
- goto free;
- }
if (priv->rst_hw)
host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset;
}
- ret = sdhci_add_host(host);
- if (ret)
- goto free;
-
- return 0;
-free:
- sdhci_pltfm_free(pdev);
- return ret;
+ return sdhci_add_host(host);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_cdns_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -580,11 +637,8 @@ disable_clk:
return ret;
}
-#endif
-static const struct dev_pm_ops sdhci_cdns_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_cdns_pm_ops, sdhci_pltfm_suspend, sdhci_cdns_resume);
static const struct of_device_id sdhci_cdns_match[] = {
{
@@ -595,6 +649,10 @@ static const struct of_device_id sdhci_cdns_match[] = {
.compatible = "amd,pensando-elba-sd4hc",
.data = &sdhci_elba_drv_data,
},
+ {
+ .compatible = "mobileye,eyeq-sd4hc",
+ .data = &sdhci_eyeq_drv_data,
+ },
{ .compatible = "cdns,sd4hc" },
{ /* sentinel */ }
};
@@ -604,7 +662,7 @@ static struct platform_driver sdhci_cdns_driver = {
.driver = {
.name = "sdhci-cdns",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_cdns_pm_ops,
+ .pm = pm_sleep_ptr(&sdhci_cdns_pm_ops),
.of_match_table = sdhci_cdns_match,
},
.probe = sdhci_cdns_probe,
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 77034b13fa66..dbfaee4a5ada 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -79,17 +79,9 @@ static int sdhci_dove_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto err_sdhci_add;
+ return ret;
- ret = sdhci_add_host(host);
- if (ret)
- goto err_sdhci_add;
-
- return 0;
-
-err_sdhci_add:
- sdhci_pltfm_free(pdev);
- return ret;
+ return sdhci_add_host(host);
}
static const struct of_device_id sdhci_dove_of_match_table[] = {
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index d55d045ef236..a7a5df673b0f 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -31,7 +31,9 @@
#include "cqhci.h"
#define ESDHC_SYS_CTRL_DTOCV_MASK GENMASK(19, 16)
+#define ESDHC_SYS_CTRL_RST_FIFO BIT(22)
#define ESDHC_SYS_CTRL_IPP_RST_N BIT(23)
+#define ESDHC_SYS_CTRL_RESET_TUNING BIT(28)
#define ESDHC_CTRL_D3CD 0x08
#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
/* VENDOR SPEC register */
@@ -81,7 +83,11 @@
#define ESDHC_TUNE_CTRL_STEP 1
#define ESDHC_TUNE_CTRL_MIN 0
#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
-
+#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK GENMASK(30, 16)
+#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK GENMASK(30, 24)
+#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK GENMASK(14, 8)
+#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK GENMASK(7, 4)
+#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK GENMASK(3, 0)
/* strobe dll register */
#define ESDHC_STROBE_DLL_CTRL 0x70
#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
@@ -104,6 +110,7 @@
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_STD_TUNING_EN (1 << 24)
+#define ESDHC_TUNING_WINDOW_MASK GENMASK(22, 20)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
#define ESDHC_TUNING_START_TAP_MASK 0x7f
@@ -205,6 +212,11 @@
/* The IP does not have GPIO CD wake capabilities */
#define ESDHC_FLAG_SKIP_CD_WAKE BIT(18)
+/* the controller has dummy pad for clock loopback */
+#define ESDHC_FLAG_DUMMY_PAD BIT(19)
+
+#define ESDHC_AUTO_TUNING_WINDOW 3
+
enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
@@ -235,6 +247,8 @@ struct esdhc_platform_data {
unsigned int tuning_step; /* The delay cell steps in tuning procedure */
unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */
unsigned int strobe_dll_delay_target; /* The delay cell for strobe pad (read clock) */
+ unsigned int saved_tuning_delay_cell; /* save the value of tuning delay cell */
+ unsigned int saved_auto_tuning_window; /* save the auto tuning window width */
};
struct esdhc_soc_data {
@@ -264,35 +278,35 @@ static const struct esdhc_soc_data usdhc_imx6q_data = {
};
static const struct esdhc_soc_data usdhc_imx6sl_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
| ESDHC_FLAG_HS200
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
};
static const struct esdhc_soc_data usdhc_imx6sll_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
};
static const struct esdhc_soc_data usdhc_imx6sx_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
};
static const struct esdhc_soc_data usdhc_imx6ull_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_ERR010450
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
};
static const struct esdhc_soc_data usdhc_imx7d_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
@@ -304,10 +318,11 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
| ESDHC_FLAG_SKIP_ERR004536 | ESDHC_FLAG_SKIP_CD_WAKE,
+ .quirks = SDHCI_QUIRK_NO_LED,
};
static struct esdhc_soc_data usdhc_imx7ulp_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
@@ -320,7 +335,7 @@ static struct esdhc_soc_data usdhc_imxrt1050_data = {
};
static struct esdhc_soc_data usdhc_imx8qxp_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
@@ -329,13 +344,22 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = {
};
static struct esdhc_soc_data usdhc_imx8mm_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
.quirks = SDHCI_QUIRK_NO_LED,
};
+static struct esdhc_soc_data usdhc_imx95_data = {
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
+ | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
+ | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
+ | ESDHC_FLAG_STATE_LOST_IN_LPMODE
+ | ESDHC_FLAG_DUMMY_PAD,
+ .quirks = SDHCI_QUIRK_NO_LED,
+};
+
struct pltfm_imx_data {
u32 scratchpad;
struct pinctrl *pinctrl;
@@ -380,6 +404,8 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
+ { .compatible = "fsl,imx94-usdhc", .data = &usdhc_imx95_data, },
+ { .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, },
{ .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, },
{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
{ /* sentinel */ }
@@ -716,23 +742,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
- u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
- if (val & SDHCI_CTRL_TUNED_CLK) {
+ if (val & SDHCI_CTRL_TUNED_CLK)
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
- } else {
+ else
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
- m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
- }
- if (val & SDHCI_CTRL_EXEC_TUNING) {
+ if (val & SDHCI_CTRL_EXEC_TUNING)
v |= ESDHC_MIX_CTRL_EXE_TUNE;
- m |= ESDHC_MIX_CTRL_FBCLK_SEL;
- } else {
+ else
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
- }
writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
- writel(m, host->ioaddr + ESDHC_MIX_CTRL);
}
return;
case SDHCI_TRANSFER_MODE:
@@ -869,6 +889,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
esdhc_clrset_le(host, mask, new_val, reg);
return;
+ case SDHCI_TIMEOUT_CONTROL:
+ esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
+ FIELD_PREP(ESDHC_SYS_CTRL_DTOCV_MASK, val),
+ ESDHC_SYSTEM_CONTROL);
+ return;
case SDHCI_SOFTWARE_RESET:
if (val & SDHCI_RESET_DATA)
new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
@@ -1056,7 +1081,7 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
- u32 ctrl;
+ u32 ctrl, tuning_ctrl, sys_ctrl;
int ret;
/* Reset the tuning circuit */
@@ -1065,11 +1090,25 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
- ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
+ /*
+ * enable the std tuning just in case it cleared in
+ * sdhc_esdhc_tuning_restore.
+ */
+ tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+ if (!(tuning_ctrl & ESDHC_STD_TUNING_EN)) {
+ tuning_ctrl |= ESDHC_STD_TUNING_EN;
+ writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
+ }
+
+ /* set the reset tuning bit */
+ sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
+ sys_ctrl |= ESDHC_SYS_CTRL_RESET_TUNING;
+ writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL);
+
ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
@@ -1129,7 +1168,7 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
{
- u32 reg;
+ u32 reg, sys_ctrl;
u8 sw_rst;
int ret;
@@ -1145,13 +1184,23 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
"warning! RESET_ALL never complete before sending tuning command\n");
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
- reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
- ESDHC_MIX_CTRL_FBCLK_SEL;
+ reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
- writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, val),
+ host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
dev_dbg(mmc_dev(host->mmc),
"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
+
+ /* set RST_FIFO to reset the async FIFO, and wat it to self-clear */
+ sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
+ sys_ctrl |= ESDHC_SYS_CTRL_RST_FIFO;
+ writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL);
+ ret = readl_poll_timeout(host->ioaddr + ESDHC_SYSTEM_CONTROL, sys_ctrl,
+ !(sys_ctrl & ESDHC_SYS_CTRL_RST_FIFO), 10, 100);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "warning! RST_FIFO not clear in 100us\n");
}
static void esdhc_post_tuning(struct sdhci_host *host)
@@ -1171,9 +1220,10 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
{
int min, max, avg, ret;
int win_length, target_min, target_max, target_win_length;
+ u32 clk_tune_ctrl_status, temp;
- min = ESDHC_TUNE_CTRL_MIN;
- max = ESDHC_TUNE_CTRL_MIN;
+ min = target_min = ESDHC_TUNE_CTRL_MIN;
+ max = target_max = ESDHC_TUNE_CTRL_MIN;
target_win_length = 0;
while (max < ESDHC_TUNE_CTRL_MAX) {
/* find the mininum delay first which can pass tuning */
@@ -1210,6 +1260,30 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
/* use average delay to get the best timing */
avg = (target_min + target_max) / 2;
esdhc_prepare_tuning(host, avg);
+
+ /*
+ * adjust the delay according to tuning window, make preparation
+ * for the auto-tuning logic. According to hardware suggest, need
+ * to config the auto tuning window width to 3, to make the auto
+ * tuning logic have enough space to handle the sample point shift
+ * caused by temperature change.
+ */
+ clk_tune_ctrl_status = FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK,
+ avg - ESDHC_AUTO_TUNING_WINDOW) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK,
+ ESDHC_AUTO_TUNING_WINDOW) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK,
+ ESDHC_AUTO_TUNING_WINDOW);
+
+ writel(clk_tune_ctrl_status, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ ret = readl_poll_timeout(host->ioaddr + ESDHC_TUNE_CTRL_STATUS, temp,
+ clk_tune_ctrl_status ==
+ FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK, temp),
+ 1, 10);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "clock tuning control status not set in 10us\n");
+
ret = mmc_send_tuning(host->mmc, opcode, NULL);
esdhc_post_tuning(host);
@@ -1364,6 +1438,16 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
break;
}
+ if (!(imx_data->socdata->flags & ESDHC_FLAG_DUMMY_PAD) &&
+ (timing == MMC_TIMING_UHS_SDR104 ||
+ timing == MMC_TIMING_MMC_HS200 ||
+ timing == MMC_TIMING_MMC_HS400))
+ m |= ESDHC_MIX_CTRL_FBCLK_SEL;
+ else
+ m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+
+ writel(m, host->ioaddr + ESDHC_MIX_CTRL);
+
esdhc_change_pinstate(host, timing);
}
@@ -1384,17 +1468,6 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
}
-static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
-
- /* use maximum timeout counter */
- esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
- esdhc_is_usdhc(imx_data) ? 0xF0000 : 0xE0000,
- ESDHC_SYSTEM_CONTROL);
-}
-
static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
{
int cmd_error = 0;
@@ -1431,7 +1504,6 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.get_min_clock = esdhc_pltfm_get_min_clock,
.get_max_timeout_count = esdhc_get_max_timeout_count,
.get_ro = esdhc_pltfm_get_ro,
- .set_timeout = esdhc_set_timeout,
.set_bus_width = esdhc_pltfm_set_bus_width,
.set_uhs_signaling = esdhc_set_uhs_signaling,
.reset = esdhc_reset,
@@ -1528,6 +1600,16 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
<< ESDHC_TUNING_STEP_SHIFT;
}
+ /*
+ * Config the tuning window to the hardware suggested value 3.
+ * This tuning window is used for auto tuning logic. The default
+ * tuning window is 2, here change to 3 make the window a bit
+ * wider, give auto tuning enough space to handle the sample
+ * point shift cause by temperature change.
+ */
+ tmp &= ~ESDHC_TUNING_WINDOW_MASK;
+ tmp |= FIELD_PREP(ESDHC_TUNING_WINDOW_MASK, ESDHC_AUTO_TUNING_WINDOW);
+
/* Disable the CMD CRC check for tuning, if not, need to
* add some delay after every tuning command, because
* hardware standard tuning logic will directly go to next
@@ -1568,6 +1650,63 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
}
}
+static void sdhc_esdhc_tuning_save(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /*
+ * SD/eMMC do not need this tuning save because it will re-init
+ * after system resume back.
+ * Here save the tuning delay value for SDIO device since it may
+ * keep power during system PM. And for usdhc, only SDR50 and
+ * SDR104 mode for SDIO device need to do tuning, and need to
+ * save/restore.
+ */
+ if (host->timing == MMC_TIMING_UHS_SDR50 ||
+ host->timing == MMC_TIMING_UHS_SDR104) {
+ reg = readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ reg = FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK, reg);
+ imx_data->boarddata.saved_tuning_delay_cell = reg;
+ }
+}
+
+static void sdhc_esdhc_tuning_restore(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ if (host->timing == MMC_TIMING_UHS_SDR50 ||
+ host->timing == MMC_TIMING_UHS_SDR104) {
+ /*
+ * restore the tuning delay value actually is a
+ * manual tuning method, so clear the standard
+ * tuning enable bit here. Will set back this
+ * ESDHC_STD_TUNING_EN in esdhc_reset_tuning()
+ * when trigger re-tuning.
+ */
+ reg = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+ reg &= ~ESDHC_STD_TUNING_EN;
+ writel(reg, host->ioaddr + ESDHC_TUNING_CTRL);
+
+ reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ reg |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+ if (!(imx_data->socdata->flags & ESDHC_FLAG_DUMMY_PAD))
+ reg |= ESDHC_MIX_CTRL_FBCLK_SEL;
+ writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
+
+ writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK,
+ imx_data->boarddata.saved_tuning_delay_cell) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK,
+ ESDHC_AUTO_TUNING_WINDOW) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK,
+ ESDHC_AUTO_TUNING_WINDOW),
+ host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ }
+}
+
static void esdhc_cqe_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -1647,7 +1786,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
* Retrieving and requesting the actual WP GPIO will happen
* in the call to mmc_of_parse().
*/
- if (of_property_read_bool(np, "wp-gpios"))
+ if (of_property_present(np, "wp-gpios"))
boarddata->wp_type = ESDHC_WP_GPIO;
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
@@ -1776,6 +1915,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
* to distinguish the card type.
*/
host->mmc_host_ops.init_card = usdhc_init_card;
+
+ host->max_timeout_count = 0xF;
}
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
@@ -1848,7 +1989,6 @@ disable_per_clk:
free_sdhci:
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
- sdhci_pltfm_free(pdev);
return err;
}
@@ -1872,11 +2012,8 @@ static void sdhci_esdhc_imx_remove(struct platform_device *pdev)
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
-
- sdhci_pltfm_free(pdev);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_esdhc_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -1884,11 +2021,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
int ret;
- if (host->mmc->caps2 & MMC_CAP2_CQE) {
- ret = cqhci_suspend(host->mmc);
- if (ret)
- return ret;
- }
+ /*
+ * Switch to runtime resume for two reasons:
+ * 1, there is register access (e.g., wakeup control register), so
+ * need to make sure gate on ipg clock.
+ * 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really
+ * invoke its ->runtime_resume callback (needs_force_resume = 1).
+ */
+ pm_runtime_get_sync(dev);
if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
@@ -1896,49 +2036,80 @@ static int sdhci_esdhc_suspend(struct device *dev)
mmc_retune_needed(host->mmc);
}
- if (host->tuning_mode != SDHCI_TUNING_MODE_3)
- mmc_retune_needed(host->mmc);
-
- ret = sdhci_suspend_host(host);
- if (ret)
- return ret;
-
- ret = pinctrl_pm_select_sleep_state(dev);
- if (ret)
- return ret;
+ /*
+ * For the device need to keep power during system PM, need
+ * to save the tuning delay value just in case the usdhc
+ * lost power during system PM.
+ */
+ if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
+ esdhc_is_usdhc(imx_data))
+ sdhc_esdhc_tuning_save(host);
+
+ if (device_may_wakeup(dev)) {
+ /* The irqs of imx are not shared. It is safe to disable */
+ disable_irq(host->irq);
+ ret = sdhci_enable_irq_wakeups(host);
+ if (!ret)
+ dev_warn(dev, "Failed to enable irq wakeup\n");
+ } else {
+ /*
+ * For the device which works as wakeup source, no need
+ * to change the pinctrl to sleep state.
+ * e.g. For SDIO device, the interrupt share with data pin,
+ * but the pinctrl sleep state may config the data pin to
+ * other function like GPIO function to save power in PM,
+ * which finally block the SDIO wakeup function.
+ */
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret)
+ return ret;
+ }
ret = mmc_gpio_set_cd_wake(host->mmc, true);
+ /*
+ * Make sure invoke runtime_suspend to gate off clock.
+ * uSDHC IP supports in-band SDIO wakeup even without clock.
+ */
+ pm_runtime_force_suspend(dev);
+
return ret;
}
static int sdhci_esdhc_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
int ret;
- ret = pinctrl_pm_select_default_state(dev);
+ pm_runtime_force_resume(dev);
+
+ ret = mmc_gpio_set_cd_wake(host->mmc, false);
if (ret)
return ret;
/* re-initialize hw state in case it's lost in low power mode */
sdhci_esdhc_imx_hwinit(host);
- ret = sdhci_resume_host(host);
- if (ret)
- return ret;
+ if (host->irq_wake_enabled) {
+ sdhci_disable_irq_wakeups(host);
+ enable_irq(host->irq);
+ }
- if (host->mmc->caps2 & MMC_CAP2_CQE)
- ret = cqhci_resume(host->mmc);
+ /*
+ * restore the saved tuning delay value for the device which keep
+ * power during system PM.
+ */
+ if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
+ esdhc_is_usdhc(imx_data))
+ sdhc_esdhc_tuning_restore(host);
- if (!ret)
- ret = mmc_gpio_set_cd_wake(host->mmc, false);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
-#endif
-#ifdef CONFIG_PM
static int sdhci_esdhc_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -1952,9 +2123,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
return ret;
}
- ret = sdhci_runtime_suspend_host(host);
- if (ret)
- return ret;
+ sdhci_runtime_suspend_host(host);
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
@@ -1968,7 +2137,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
- return ret;
+ return 0;
}
static int sdhci_esdhc_runtime_resume(struct device *dev)
@@ -1998,17 +2167,13 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
esdhc_pltfm_set_clock(host, imx_data->actual_clock);
- err = sdhci_runtime_resume_host(host, 0);
- if (err)
- goto disable_ipg_clk;
+ sdhci_runtime_resume_host(host, 0);
if (host->mmc->caps2 & MMC_CAP2_CQE)
err = cqhci_resume(host->mmc);
return err;
-disable_ipg_clk:
- clk_disable_unprepare(imx_data->clk_ipg);
disable_per_clk:
clk_disable_unprepare(imx_data->clk_per);
disable_ahb_clk:
@@ -2018,12 +2183,10 @@ remove_pm_qos_request:
cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
return err;
}
-#endif
static const struct dev_pm_ops sdhci_esdhc_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
- SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
- sdhci_esdhc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
+ RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, sdhci_esdhc_runtime_resume, NULL)
};
static struct platform_driver sdhci_esdhc_imx_driver = {
@@ -2031,7 +2194,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
.name = "sdhci-esdhc-imx",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = imx_esdhc_dt_ids,
- .pm = &sdhci_esdhc_pmops,
+ .pm = pm_ptr(&sdhci_esdhc_pmops),
},
.probe = sdhci_esdhc_imx_probe,
.remove = sdhci_esdhc_imx_remove,
diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c
index 327662ba5bd9..375fce5639d7 100644
--- a/drivers/mmc/host/sdhci-esdhc-mcf.c
+++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
@@ -426,28 +426,22 @@ static int sdhci_esdhc_mcf_probe(struct platform_device *pdev)
host->flags |= SDHCI_AUTO_CMD12;
mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(mcf_data->clk_ipg)) {
- err = PTR_ERR(mcf_data->clk_ipg);
- goto err_exit;
- }
+ if (IS_ERR(mcf_data->clk_ipg))
+ return PTR_ERR(mcf_data->clk_ipg);
mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
- if (IS_ERR(mcf_data->clk_ahb)) {
- err = PTR_ERR(mcf_data->clk_ahb);
- goto err_exit;
- }
+ if (IS_ERR(mcf_data->clk_ahb))
+ return PTR_ERR(mcf_data->clk_ahb);
mcf_data->clk_per = devm_clk_get(&pdev->dev, "per");
- if (IS_ERR(mcf_data->clk_per)) {
- err = PTR_ERR(mcf_data->clk_per);
- goto err_exit;
- }
+ if (IS_ERR(mcf_data->clk_per))
+ return PTR_ERR(mcf_data->clk_per);
pltfm_host->clk = mcf_data->clk_per;
pltfm_host->clock = clk_get_rate(pltfm_host->clk);
err = clk_prepare_enable(mcf_data->clk_per);
if (err)
- goto err_exit;
+ return err;
err = clk_prepare_enable(mcf_data->clk_ipg);
if (err)
@@ -485,9 +479,6 @@ unprep_ipg:
clk_disable_unprepare(mcf_data->clk_ipg);
unprep_per:
clk_disable_unprepare(mcf_data->clk_per);
-err_exit:
- sdhci_pltfm_free(pdev);
-
return err;
}
@@ -502,8 +493,6 @@ static void sdhci_esdhc_mcf_remove(struct platform_device *pdev)
clk_disable_unprepare(mcf_data->clk_ipg);
clk_disable_unprepare(mcf_data->clk_ahb);
clk_disable_unprepare(mcf_data->clk_per);
-
- sdhci_pltfm_free(pdev);
}
static struct platform_driver sdhci_esdhc_mcf_driver = {
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 80b2567a488b..35ef5c5f5146 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -379,7 +379,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto err;
+ return ret;
sdhci_get_property(pdev);
@@ -387,10 +387,8 @@ static int sdhci_iproc_probe(struct platform_device *pdev)
if (dev->of_node) {
pltfm_host->clk = devm_clk_get_enabled(dev, NULL);
- if (IS_ERR(pltfm_host->clk)) {
- ret = PTR_ERR(pltfm_host->clk);
- goto err;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return PTR_ERR(pltfm_host->clk);
}
if (iproc_host->data->missing_caps) {
@@ -399,15 +397,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev)
&iproc_host->data->caps1);
}
- ret = sdhci_add_host(host);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- sdhci_pltfm_free(pdev);
- return ret;
+ return sdhci_add_host(host);
}
static void sdhci_iproc_shutdown(struct platform_device *pdev)
diff --git a/drivers/mmc/host/sdhci-milbeaut.c b/drivers/mmc/host/sdhci-milbeaut.c
index a4675456f9c7..bda71d5966dc 100644
--- a/drivers/mmc/host/sdhci-milbeaut.c
+++ b/drivers/mmc/host/sdhci-milbeaut.c
@@ -258,7 +258,7 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto err;
+ return ret;
platform_set_drvdata(pdev, host);
@@ -267,23 +267,19 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev)
host->irq = irq;
host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(host->ioaddr)) {
- ret = PTR_ERR(host->ioaddr);
- goto err;
- }
+ if (IS_ERR(host->ioaddr))
+ return PTR_ERR(host->ioaddr);
if (dev_of_node(dev)) {
sdhci_get_of_property(pdev);
priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
- if (IS_ERR(priv->clk_iface)) {
- ret = PTR_ERR(priv->clk_iface);
- goto err;
- }
+ if (IS_ERR(priv->clk_iface))
+ return PTR_ERR(priv->clk_iface);
ret = clk_prepare_enable(priv->clk_iface);
if (ret)
- goto err;
+ return ret;
priv->clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(priv->clk)) {
@@ -308,8 +304,6 @@ err_add_host:
clk_disable_unprepare(priv->clk);
err_clk:
clk_disable_unprepare(priv->clk_iface);
-err:
- sdhci_free_host(host);
return ret;
}
@@ -324,7 +318,6 @@ static void sdhci_milbeaut_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk_iface);
clk_disable_unprepare(priv->clk);
- sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
}
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index e00208535bd1..3b85233131b3 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -81,6 +81,7 @@
#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15)
#define CORE_IO_PAD_PWR_SWITCH BIT(16)
#define CORE_HC_SELECT_IN_EN BIT(18)
+#define CORE_HC_SELECT_IN_SDR50 (4 << 19)
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
#define CORE_HC_SELECT_IN_MASK (7 << 19)
@@ -134,9 +135,18 @@
/* Timeout value to avoid infinite waiting for pwr_irq */
#define MSM_PWR_IRQ_TIMEOUT_MS 5000
+/* Max load for eMMC Vdd supply */
+#define MMC_VMMC_MAX_LOAD_UA 570000
+
/* Max load for eMMC Vdd-io supply */
#define MMC_VQMMC_MAX_LOAD_UA 325000
+/* Max load for SD Vdd supply */
+#define SD_VMMC_MAX_LOAD_UA 800000
+
+/* Max load for SD Vdd-io supply */
+#define SD_VQMMC_MAX_LOAD_UA 22000
+
#define msm_host_readl(msm_host, host, offset) \
msm_host->var_ops->msm_readl_relaxed(host, offset)
@@ -334,41 +344,43 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val,
writel_relaxed(val, host->ioaddr + offset);
}
-static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host)
+static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host,
+ unsigned int clock,
+ unsigned int timing)
{
- 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 ||
+ if (timing == MMC_TIMING_UHS_DDR50 ||
+ timing == MMC_TIMING_MMC_DDR52 ||
+ (timing == MMC_TIMING_MMC_HS400 &&
+ clock == MMC_HS200_MAX_DTR) ||
host->flags & SDHCI_HS400_TUNING)
return 2;
return 1;
}
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
- unsigned int clock)
+ unsigned int clock,
+ unsigned int timing)
{
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;
struct clk *core_clk = msm_host->bulk_clks[0].clk;
unsigned long achieved_rate;
unsigned int desired_rate;
unsigned int mult;
int rc;
- mult = msm_get_clock_mult_for_bus_mode(host);
+ mult = msm_get_clock_mult_for_bus_mode(host, clock, timing);
desired_rate = clock * mult;
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
- mmc_hostname(host->mmc), desired_rate, curr_ios.timing);
+ mmc_hostname(host->mmc), desired_rate, timing);
return;
}
@@ -387,7 +399,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
msm_host->clk_rate = desired_rate;
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
- mmc_hostname(host->mmc), achieved_rate, curr_ios.timing);
+ mmc_hostname(host->mmc), achieved_rate, timing);
}
/* Platform specific tuning */
@@ -1124,6 +1136,10 @@ static bool sdhci_msm_is_tuning_needed(struct sdhci_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
+ if (ios->timing == MMC_TIMING_UHS_SDR50 &&
+ host->flags & SDHCI_SDR50_NEEDS_TUNING)
+ return true;
+
/*
* Tuning is required for SDR104, HS200 and HS400 cards and
* if clock frequency is greater than 100MHz in these modes.
@@ -1192,6 +1208,8 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
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);
+ const struct sdhci_msm_offset *msm_offset = msm_host->offset;
+ u32 config;
if (!sdhci_msm_is_tuning_needed(host)) {
msm_host->use_cdr = false;
@@ -1208,6 +1226,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/
msm_host->tuning_done = 0;
+ if (ios.timing == MMC_TIMING_UHS_SDR50 &&
+ host->flags & SDHCI_SDR50_NEEDS_TUNING) {
+ config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec);
+ config &= ~CORE_HC_SELECT_IN_MASK;
+ config |= CORE_HC_SELECT_IN_EN | CORE_HC_SELECT_IN_SDR50;
+ writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec);
+ }
+
/*
* For HS400 tuning in HS200 timing requires:
* - select MCLK/2 in VENDOR_SPEC
@@ -1215,7 +1241,7 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/
if (host->flags & SDHCI_HS400_TUNING) {
sdhci_msm_hc_select_mode(host);
- msm_set_clock_rate_for_bus_mode(host, ios.clock);
+ msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
host->flags &= ~SDHCI_HS400_TUNING;
}
@@ -1403,11 +1429,48 @@ static int sdhci_msm_set_pincfg(struct sdhci_msm_host *msm_host, bool level)
return ret;
}
-static int sdhci_msm_set_vmmc(struct mmc_host *mmc)
+static void msm_config_vmmc_regulator(struct mmc_host *mmc, bool hpm)
+{
+ int load;
+
+ if (!hpm)
+ load = 0;
+ else if (!mmc->card)
+ load = max(MMC_VMMC_MAX_LOAD_UA, SD_VMMC_MAX_LOAD_UA);
+ else if (mmc_card_mmc(mmc->card))
+ load = MMC_VMMC_MAX_LOAD_UA;
+ else if (mmc_card_sd(mmc->card))
+ load = SD_VMMC_MAX_LOAD_UA;
+ else
+ return;
+
+ regulator_set_load(mmc->supply.vmmc, load);
+}
+
+static void msm_config_vqmmc_regulator(struct mmc_host *mmc, bool hpm)
+{
+ int load;
+
+ if (!hpm)
+ load = 0;
+ else if (!mmc->card)
+ load = max(MMC_VQMMC_MAX_LOAD_UA, SD_VQMMC_MAX_LOAD_UA);
+ else if (mmc_card_sd(mmc->card))
+ load = SD_VQMMC_MAX_LOAD_UA;
+ else
+ return;
+
+ regulator_set_load(mmc->supply.vqmmc, load);
+}
+
+static int sdhci_msm_set_vmmc(struct sdhci_msm_host *msm_host,
+ struct mmc_host *mmc, bool hpm)
{
if (IS_ERR(mmc->supply.vmmc))
return 0;
+ msm_config_vmmc_regulator(mmc, hpm);
+
return mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, mmc->ios.vdd);
}
@@ -1420,6 +1483,8 @@ static int msm_toggle_vqmmc(struct sdhci_msm_host *msm_host,
if (msm_host->vqmmc_enabled == level)
return 0;
+ msm_config_vqmmc_regulator(mmc, level);
+
if (level) {
/* Set the IO voltage regulator to default voltage level */
if (msm_host->caps_0 & CORE_3_0V_SUPPORT)
@@ -1516,6 +1581,7 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
{
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;
bool done = false;
u32 val = SWITCHABLE_SIGNALING_VOLTAGE;
const struct sdhci_msm_offset *msm_offset =
@@ -1573,6 +1639,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
"%s: pwr_irq for req: (%d) timed out\n",
mmc_hostname(host->mmc), req_type);
}
+
+ if ((req_type & REQ_BUS_ON) && mmc->card && !mmc->ops->get_cd(mmc)) {
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ host->pwr = 0;
+ }
+
pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc),
__func__, req_type);
}
@@ -1631,6 +1703,13 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
udelay(10);
}
+ if ((irq_status & CORE_PWRCTL_BUS_ON) && mmc->card &&
+ !mmc->ops->get_cd(mmc)) {
+ msm_host_writel(msm_host, CORE_PWRCTL_BUS_FAIL, host,
+ msm_offset->core_pwrctl_ctl);
+ return;
+ }
+
/* Handle BUS ON/OFF*/
if (irq_status & CORE_PWRCTL_BUS_ON) {
pwr_state = REQ_BUS_ON;
@@ -1642,7 +1721,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
}
if (pwr_state) {
- ret = sdhci_msm_set_vmmc(mmc);
+ ret = sdhci_msm_set_vmmc(msm_host, mmc,
+ pwr_state & REQ_BUS_ON);
if (!ret)
ret = sdhci_msm_set_vqmmc(msm_host, mmc,
pwr_state & REQ_BUS_ON);
@@ -1786,6 +1866,7 @@ 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 ios = host->mmc->ios;
if (!clock) {
host->mmc->actual_clock = msm_host->clk_rate = 0;
@@ -1794,7 +1875,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_msm_hc_select_mode(host);
- msm_set_clock_rate_for_bus_mode(host, clock);
+ msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
out:
__sdhci_msm_set_clock(host, clock);
}
@@ -1807,17 +1888,24 @@ out:
#ifdef CONFIG_MMC_CRYPTO
+static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops; /* forward decl */
+
static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
struct cqhci_host *cq_host)
{
struct mmc_host *mmc = msm_host->mmc;
+ struct blk_crypto_profile *profile = &mmc->crypto_profile;
struct device *dev = mmc_dev(mmc);
struct qcom_ice *ice;
+ union cqhci_crypto_capabilities caps;
+ union cqhci_crypto_cap_entry cap;
+ int err;
+ int i;
if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
return 0;
- ice = of_qcom_ice_get(dev);
+ ice = devm_of_qcom_ice_get(dev);
if (ice == ERR_PTR(-EOPNOTSUPP)) {
dev_warn(dev, "Disabling inline encryption support\n");
ice = NULL;
@@ -1826,9 +1914,44 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
if (IS_ERR_OR_NULL(ice))
return PTR_ERR_OR_ZERO(ice);
+ if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) {
+ dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n");
+ return 0;
+ }
+
msm_host->ice = ice;
- mmc->caps2 |= MMC_CAP2_CRYPTO;
+ /* Initialize the blk_crypto_profile */
+
+ caps.reg_val = cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
+
+ /* The number of keyslots supported is (CFGC+1) */
+ err = devm_blk_crypto_profile_init(dev, profile, caps.config_count + 1);
+ if (err)
+ return err;
+
+ profile->ll_ops = sdhci_msm_crypto_ops;
+ profile->max_dun_bytes_supported = 4;
+ profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
+ profile->dev = dev;
+
+ /*
+ * Currently this driver only supports AES-256-XTS. All known versions
+ * of ICE support it, but to be safe make sure it is really declared in
+ * the crypto capability registers. The crypto capability registers
+ * also give the supported data unit size(s).
+ */
+ for (i = 0; i < caps.num_crypto_cap; i++) {
+ cap.reg_val = cpu_to_le32(cqhci_readl(cq_host,
+ CQHCI_CRYPTOCAP +
+ i * sizeof(__le32)));
+ if (cap.algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS &&
+ cap.key_size == CQHCI_CRYPTO_KEY_SIZE_256)
+ profile->modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] |=
+ cap.sdus_mask * 512;
+ }
+
+ mmc->caps2 |= MMC_CAP2_CRYPTO;
return 0;
}
@@ -1838,7 +1961,7 @@ static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
qcom_ice_enable(msm_host->ice);
}
-static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
+static int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{
if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return qcom_ice_resume(msm_host->ice);
@@ -1846,7 +1969,7 @@ static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
return 0;
}
-static __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
+static int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
{
if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)
return qcom_ice_suspend(msm_host->ice);
@@ -1854,35 +1977,46 @@ static __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
return 0;
}
-/*
- * Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires
- * vendor-specific SCM calls for this; it doesn't support the standard way.
- */
-static int sdhci_msm_program_key(struct cqhci_host *cq_host,
- const union cqhci_crypto_cfg_entry *cfg,
- int slot)
+static inline struct sdhci_msm_host *
+sdhci_msm_host_from_crypto_profile(struct blk_crypto_profile *profile)
{
- struct sdhci_host *host = mmc_priv(cq_host->mmc);
+ struct mmc_host *mmc = mmc_from_crypto_profile(profile);
+ struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
- union cqhci_crypto_cap_entry cap;
- /* Only AES-256-XTS has been tested so far. */
- cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx];
- if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS ||
- cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256)
- return -EINVAL;
+ return msm_host;
+}
- if (cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE)
- return qcom_ice_program_key(msm_host->ice,
- QCOM_ICE_CRYPTO_ALG_AES_XTS,
- QCOM_ICE_CRYPTO_KEY_SIZE_256,
- cfg->crypto_key,
- cfg->data_unit_size, slot);
- else
- return qcom_ice_evict_key(msm_host->ice, slot);
+/*
+ * Program a key into a QC ICE keyslot. QC ICE requires a QC-specific SCM call
+ * for this; it doesn't support the standard way.
+ */
+static int sdhci_msm_ice_keyslot_program(struct blk_crypto_profile *profile,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct sdhci_msm_host *msm_host =
+ sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_program_key(msm_host->ice, slot, key);
}
+static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct sdhci_msm_host *msm_host =
+ sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_evict_key(msm_host->ice, slot);
+}
+
+static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {
+ .keyslot_program = sdhci_msm_ice_keyslot_program,
+ .keyslot_evict = sdhci_msm_ice_keyslot_evict,
+};
+
#else /* CONFIG_MMC_CRYPTO */
static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
@@ -1895,13 +2029,13 @@ static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host)
{
}
-static inline __maybe_unused int
+static inline int
sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host)
{
return 0;
}
-static inline __maybe_unused int
+static inline int
sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
{
return 0;
@@ -1988,7 +2122,7 @@ static const struct cqhci_host_ops sdhci_msm_cqhci_ops = {
.enable = sdhci_msm_cqe_enable,
.disable = sdhci_msm_cqe_disable,
#ifdef CONFIG_MMC_CRYPTO
- .program_key = sdhci_msm_program_key,
+ .uses_custom_crypto_profile = true,
#endif
};
@@ -2424,7 +2558,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto pltfm_free;
+ return ret;
/*
* Based on the compatible string, load the required msm host info from
@@ -2446,7 +2580,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
ret = sdhci_msm_gcc_reset(&pdev->dev, host);
if (ret)
- goto pltfm_free;
+ return ret;
/* Setup SDCC bus voter clock. */
msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
@@ -2454,10 +2588,10 @@ static int sdhci_msm_probe(struct platform_device *pdev)
/* Vote for max. clk rate for max. performance */
ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
if (ret)
- goto pltfm_free;
+ return ret;
ret = clk_prepare_enable(msm_host->bus_clk);
if (ret)
- goto pltfm_free;
+ return ret;
}
/* Setup main peripheral bus clock */
@@ -2648,7 +2782,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
if (ret)
goto pm_runtime_disable;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
@@ -2663,8 +2796,6 @@ clk_disable:
bus_clk_disable:
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
-pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -2686,10 +2817,9 @@ static void sdhci_msm_remove(struct platform_device *pdev)
msm_host->bulk_clks);
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
- sdhci_pltfm_free(pdev);
}
-static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
+static int sdhci_msm_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -2708,7 +2838,7 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
return sdhci_msm_ice_suspend(msm_host);
}
-static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
+static int sdhci_msm_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -2744,11 +2874,8 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops sdhci_msm_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend,
- sdhci_msm_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL)
};
static struct platform_driver sdhci_msm_driver = {
@@ -2757,7 +2884,7 @@ static struct platform_driver sdhci_msm_driver = {
.driver = {
.name = "sdhci_msm",
.of_match_table = sdhci_msm_dt_match,
- .pm = &sdhci_msm_pm_ops,
+ .pm = pm_ptr(&sdhci_msm_pm_ops),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
diff --git a/drivers/mmc/host/sdhci-npcm.c b/drivers/mmc/host/sdhci-npcm.c
index bee0585ba5c1..71b635dfdf1d 100644
--- a/drivers/mmc/host/sdhci-npcm.c
+++ b/drivers/mmc/host/sdhci-npcm.c
@@ -48,8 +48,7 @@ static int npcm_sdhci_probe(struct platform_device *pdev)
pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(pltfm_host->clk)) {
- ret = PTR_ERR(pltfm_host->clk);
- goto err_sdhci;
+ return PTR_ERR(pltfm_host->clk);
}
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
@@ -58,17 +57,9 @@ static int npcm_sdhci_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto err_sdhci;
+ return ret;
- ret = sdhci_add_host(host);
- if (ret)
- goto err_sdhci;
-
- return 0;
-
-err_sdhci:
- sdhci_pltfm_free(pdev);
- return ret;
+ return sdhci_add_host(host);
}
static const struct of_device_id npcm_sdhci_of_match[] = {
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 8c29676ab662..b97d042897ad 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -99,6 +99,9 @@
#define HIWORD_UPDATE(val, mask, shift) \
((val) << (shift) | (mask) << ((shift) + 16))
+#define CD_STABLE_TIMEOUT_US 1000000
+#define CD_STABLE_MAX_SLEEP_US 10
+
/**
* struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map
*
@@ -206,12 +209,15 @@ struct sdhci_arasan_data {
* 19MHz instead
*/
#define SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN BIT(2)
+/* Enable CD stable check before power-up */
+#define SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE BIT(3)
};
struct sdhci_arasan_of_data {
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
const struct sdhci_pltfm_data *pdata;
const struct sdhci_arasan_clk_ops *clk_ops;
+ u32 quirks;
};
static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
@@ -514,6 +520,24 @@ static int sdhci_arasan_voltage_switch(struct mmc_host *mmc,
return -EINVAL;
}
+static void sdhci_arasan_set_power_and_bus_voltage(struct sdhci_host *host, unsigned char mode,
+ unsigned short vdd)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /*
+ * Ensure that the card detect logic has stabilized before powering up, this is
+ * necessary after a host controller reset.
+ */
+ if (mode == MMC_POWER_UP && sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE)
+ read_poll_timeout(sdhci_readl, reg, reg & SDHCI_CD_STABLE, CD_STABLE_MAX_SLEEP_US,
+ CD_STABLE_TIMEOUT_US, false, host, SDHCI_PRESENT_STATE);
+
+ sdhci_set_power_and_bus_voltage(host, mode, vdd);
+}
+
static const struct sdhci_ops sdhci_arasan_ops = {
.set_clock = sdhci_arasan_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
@@ -521,7 +545,7 @@ static const struct sdhci_ops sdhci_arasan_ops = {
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_arasan_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
- .set_power = sdhci_set_power_and_bus_voltage,
+ .set_power = sdhci_arasan_set_power_and_bus_voltage,
.hw_reset = sdhci_arasan_hw_reset,
};
@@ -570,7 +594,7 @@ static const struct sdhci_ops sdhci_arasan_cqe_ops = {
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_arasan_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
- .set_power = sdhci_set_power_and_bus_voltage,
+ .set_power = sdhci_arasan_set_power_and_bus_voltage,
.irq = sdhci_arasan_cqhci_irq,
};
@@ -581,7 +605,6 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
};
-#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
* @dev: Address of the device structure
@@ -675,10 +698,9 @@ static int sdhci_arasan_resume(struct device *dev)
return 0;
}
-#endif /* ! CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
- sdhci_arasan_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
+ sdhci_arasan_resume);
/**
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
@@ -1447,6 +1469,7 @@ static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = {
static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
.pdata = &sdhci_arasan_zynqmp_pdata,
.clk_ops = &zynqmp_clk_ops,
+ .quirks = SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE,
};
static const struct sdhci_arasan_clk_ops versal_clk_ops = {
@@ -1457,6 +1480,7 @@ static const struct sdhci_arasan_clk_ops versal_clk_ops = {
static struct sdhci_arasan_of_data sdhci_arasan_versal_data = {
.pdata = &sdhci_arasan_zynqmp_pdata,
.clk_ops = &versal_clk_ops,
+ .quirks = SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE,
};
static const struct sdhci_arasan_clk_ops versal_net_clk_ops = {
@@ -1467,6 +1491,7 @@ static const struct sdhci_arasan_clk_ops versal_net_clk_ops = {
static struct sdhci_arasan_of_data sdhci_arasan_versal_net_data = {
.pdata = &sdhci_arasan_versal_net_pdata,
.clk_ops = &versal_net_clk_ops,
+ .quirks = SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE,
};
static struct sdhci_arasan_of_data intel_keembay_emmc_data = {
@@ -1883,34 +1908,26 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
of_node_put(node);
- if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
- ret = dev_err_probe(dev,
+ if (IS_ERR(sdhci_arasan->soc_ctl_base))
+ return dev_err_probe(dev,
PTR_ERR(sdhci_arasan->soc_ctl_base),
"Can't get syscon\n");
- goto err_pltfm_free;
- }
}
sdhci_get_of_property(pdev);
sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb");
- if (IS_ERR(sdhci_arasan->clk_ahb)) {
- ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb),
+ if (IS_ERR(sdhci_arasan->clk_ahb))
+ return dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb),
"clk_ahb clock not found.\n");
- goto err_pltfm_free;
- }
clk_xin = devm_clk_get(dev, "clk_xin");
- if (IS_ERR(clk_xin)) {
- ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n");
- goto err_pltfm_free;
- }
+ if (IS_ERR(clk_xin))
+ return dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n");
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
- if (ret) {
- dev_err(dev, "Unable to enable AHB clock.\n");
- goto err_pltfm_free;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to enable AHB clock.\n");
/* If clock-frequency property is set, use the provided value */
if (pltfm_host->clock &&
@@ -1945,6 +1962,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1"))
sdhci_arasan_update_clockmultiplier(host, 0x0);
+ sdhci_arasan->quirks |= data->quirks;
+
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
@@ -1972,7 +1991,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret) {
- ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
+ dev_err_probe(dev, ret, "parsing dt failed.\n");
goto unreg_clk;
}
@@ -2029,8 +2048,6 @@ clk_disable_all:
clk_disable_unprepare(clk_xin);
clk_dis_ahb:
clk_disable_unprepare(sdhci_arasan->clk_ahb);
-err_pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -2061,7 +2078,7 @@ static struct platform_driver sdhci_arasan_driver = {
.name = "sdhci-arasan",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_arasan_of_match,
- .pm = &sdhci_arasan_dev_pm_ops,
+ .pm = pm_sleep_ptr(&sdhci_arasan_dev_pm_ops),
},
.probe = sdhci_arasan_probe,
.remove = sdhci_arasan_remove,
diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c
index d6de010551b9..ca97b01996b1 100644
--- a/drivers/mmc/host/sdhci-of-aspeed.c
+++ b/drivers/mmc/host/sdhci-of-aspeed.c
@@ -425,10 +425,8 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
return PTR_ERR(pltfm_host->clk);
ret = clk_prepare_enable(pltfm_host->clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to enable SDIO clock\n");
- goto err_pltfm_free;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Unable to enable SDIO clock\n");
ret = mmc_of_parse(host->mmc);
if (ret)
@@ -445,8 +443,6 @@ static int aspeed_sdhci_probe(struct platform_device *pdev)
err_sdhci_add:
clk_disable_unprepare(pltfm_host->clk);
-err_pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -461,8 +457,6 @@ static void aspeed_sdhci_remove(struct platform_device *pdev)
sdhci_remove_host(host, 0);
clk_disable_unprepare(pltfm_host->clk);
-
- sdhci_pltfm_free(pdev);
}
static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = {
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index 97988ed37467..7c4ac65f247d 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -229,7 +229,6 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_at91_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -243,17 +242,14 @@ static int sdhci_at91_suspend(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int sdhci_at91_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
- int ret;
- ret = sdhci_runtime_suspend_host(host);
+ sdhci_runtime_suspend_host(host);
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
@@ -262,7 +258,7 @@ static int sdhci_at91_runtime_suspend(struct device *dev)
clk_disable_unprepare(priv->hclock);
clk_disable_unprepare(priv->mainck);
- return ret;
+ return 0;
}
static int sdhci_at91_runtime_resume(struct device *dev)
@@ -300,15 +296,13 @@ static int sdhci_at91_runtime_resume(struct device *dev)
}
out:
- return sdhci_runtime_resume_host(host, 0);
+ sdhci_runtime_resume_host(host, 0);
+ return 0;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops sdhci_at91_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend,
- sdhci_at91_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, sdhci_at91_runtime_resume, NULL)
};
static int sdhci_at91_probe(struct platform_device *pdev)
@@ -333,32 +327,26 @@ static int sdhci_at91_probe(struct platform_device *pdev)
priv->mainck = devm_clk_get(&pdev->dev, "baseclk");
if (IS_ERR(priv->mainck)) {
- if (soc_data->baseclk_is_generated_internally) {
+ if (soc_data->baseclk_is_generated_internally)
priv->mainck = NULL;
- } else {
- dev_err(&pdev->dev, "failed to get baseclk\n");
- ret = PTR_ERR(priv->mainck);
- goto sdhci_pltfm_free;
- }
+ else
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->mainck),
+ "failed to get baseclk\n");
}
priv->hclock = devm_clk_get(&pdev->dev, "hclock");
- if (IS_ERR(priv->hclock)) {
- dev_err(&pdev->dev, "failed to get hclock\n");
- ret = PTR_ERR(priv->hclock);
- goto sdhci_pltfm_free;
- }
+ if (IS_ERR(priv->hclock))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->hclock),
+ "failed to get hclock\n");
priv->gck = devm_clk_get(&pdev->dev, "multclk");
- if (IS_ERR(priv->gck)) {
- dev_err(&pdev->dev, "failed to get multclk\n");
- ret = PTR_ERR(priv->gck);
- goto sdhci_pltfm_free;
- }
+ if (IS_ERR(priv->gck))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->gck),
+ "failed to get multclk\n");
ret = sdhci_at91_set_clks_presets(&pdev->dev);
if (ret)
- goto sdhci_pltfm_free;
+ return ret;
priv->restore_needed = false;
@@ -438,8 +426,6 @@ clocks_disable_unprepare:
clk_disable_unprepare(priv->gck);
clk_disable_unprepare(priv->mainck);
clk_disable_unprepare(priv->hclock);
-sdhci_pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -468,7 +454,7 @@ static struct platform_driver sdhci_at91_driver = {
.name = "sdhci-at91",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_at91_dt_match,
- .pm = &sdhci_at91_dev_pm_ops,
+ .pm = pm_ptr(&sdhci_at91_dev_pm_ops),
},
.probe = sdhci_at91_probe,
.remove = sdhci_at91_remove,
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 7ea3da45db32..51949cde0958 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -11,18 +11,24 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sizes.h>
+#include <linux/mfd/syscon.h>
+#include <linux/units.h>
#include "sdhci-pltfm.h"
#include "cqhci.h"
+#include "sdhci-cqhci.h"
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
@@ -38,6 +44,7 @@
#define DWCMSHC_CARD_IS_EMMC BIT(0)
#define DWCMSHC_ENHANCED_STROBE BIT(8)
#define DWCMSHC_EMMC_ATCTRL 0x40
+#define DWCMSHC_AT_STAT 0x44
/* Tuning and auto-tuning fields in AT_CTRL_R control register */
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
#define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */
@@ -81,6 +88,8 @@
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
+#define DECMSHC_EMMC_MISC_CON 0x81C
+#define MISC_INTCLK_EN BIT(1)
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
#define DWCMSHC_EMMC_DLL_START BIT(0)
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
@@ -93,7 +102,7 @@
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
-#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
+#define DLL_STRBIN_TAPNUM_DEFAULT 0x4
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
#define DLL_STRBIN_DELAY_NUM_OFFSET 16
@@ -193,6 +202,19 @@
#define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */
#define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */
+/* PHY DLL offset setting register */
+#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29)
+/* DLL LBT setting register */
+#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c)
+/* DLL Status register */
+#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e)
+#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */
+/*
+ * Captures the value of DLL's lock error status information. Value is valid
+ * only when LOCK_STS is set.
+ */
+#define DLL_ERROR_STS BIT(1)
+
#define FLAG_IO_FIXED_1V8 BIT(0)
#define BOUNDARY_OK(addr, len) \
@@ -205,6 +227,31 @@
/* SMC call for BlueField-3 eMMC RST_N */
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
+/* Eswin specific Registers */
+#define EIC7700_CARD_CLK_STABLE BIT(28)
+#define EIC7700_INT_BCLK_STABLE BIT(16)
+#define EIC7700_INT_ACLK_STABLE BIT(8)
+#define EIC7700_INT_TMCLK_STABLE BIT(0)
+#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \
+ EIC7700_INT_ACLK_STABLE | \
+ EIC7700_INT_BCLK_STABLE | \
+ EIC7700_INT_TMCLK_STABLE)
+#define EIC7700_HOST_VAL_STABLE BIT(0)
+
+/* strength definition */
+#define PHYCTRL_DR_33OHM 0xee
+#define PHYCTRL_DR_40OHM 0xcc
+#define PHYCTRL_DR_50OHM 0x88
+#define PHYCTRL_DR_66OHM 0x44
+#define PHYCTRL_DR_100OHM 0x00
+
+#define MAX_PHASE_CODE 0xff
+#define TUNING_RANGE_THRESHOLD 40
+#define PHY_CLK_MAX_DELAY_MASK 0x7f
+#define PHY_DELAY_CODE_MAX 0x7f
+#define PHY_DELAY_CODE_EMMC 0x17
+#define PHY_DELAY_CODE_SD 0x55
+
enum dwcmshc_rk_type {
DWCMSHC_RK3568,
DWCMSHC_RK3588,
@@ -216,6 +263,11 @@ struct rk35xx_priv {
u8 txclk_tapnum;
};
+struct eic7700_priv {
+ struct reset_control *reset;
+ unsigned int drive_impedance;
+};
+
#define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv {
@@ -233,10 +285,22 @@ struct dwcmshc_priv {
struct dwcmshc_pltfm_data {
const struct sdhci_pltfm_data pdata;
+ const struct cqhci_host_ops *cqhci_host_ops;
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
};
+static void dwcmshc_enable_card_clk(struct sdhci_host *host)
+{
+ u16 ctrl;
+
+ ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
+ ctrl |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
+ }
+}
+
static int dwcmshc_get_enable_other_clks(struct device *dev,
struct dwcmshc_priv *priv,
int num_clks,
@@ -288,6 +352,19 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
+static void dwcmshc_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ /* The dwcmshc does not comply with the SDHCI specification
+ * regarding the "Software Reset for CMD line should clear 'Command
+ * Complete' in the Normal Interrupt Status Register." Clear the bit
+ * here to compensate for this quirk.
+ */
+ if (mask & SDHCI_RESET_CMD)
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+}
+
static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -328,12 +405,17 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_request(mmc, mrq);
}
-static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
+static void dwcmshc_phy_init(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 rxsel = PHY_PAD_RXSEL_3V3;
u32 val;
+ if (priv->flags & FLAG_IO_FIXED_1V8 ||
+ host->mmc->ios.timing & MMC_SIGNAL_VOLTAGE_180)
+ rxsel = PHY_PAD_RXSEL_1V8;
+
/* deassert phy reset & set tx drive strength */
val = PHY_CNFG_RSTN_DEASSERT;
val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
@@ -353,7 +435,7 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* configure phy pads */
- val = PHY_PAD_RXSEL_1V8;
+ val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
@@ -365,65 +447,22 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
- val = PHY_PAD_RXSEL_1V8;
+ val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
/* enable data strobe mode */
- sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL),
- PHY_DLLDL_CNFG_R);
-
- /* enable phy dll */
- sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
-}
-
-static void dwcmshc_phy_3_3v_init(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
- u32 val;
-
- /* deassert phy reset & set tx drive strength */
- val = PHY_CNFG_RSTN_DEASSERT;
- val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
- val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN);
- sdhci_writel(host, val, PHY_CNFG_R);
+ if (rxsel == PHY_PAD_RXSEL_1V8) {
+ u8 sel = FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL);
- /* disable delay line */
- sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
-
- /* set delay line */
- sdhci_writeb(host, priv->delay_line, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R);
-
- /* enable delay lane */
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
-
- /* configure phy pads */
- val = PHY_PAD_RXSEL_3V3;
- val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
-
- val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
-
- val = PHY_PAD_RXSEL_3V3;
- val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+ sdhci_writeb(host, sel, PHY_DLLDL_CNFG_R);
+ }
/* enable phy dll */
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
+
}
static void th1520_sdhci_set_phy(struct sdhci_host *host)
@@ -433,11 +472,7 @@ static void th1520_sdhci_set_phy(struct sdhci_host *host)
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
u16 emmc_ctrl;
- /* Before power on, set PHY configs */
- if (priv->flags & FLAG_IO_FIXED_1V8)
- dwcmshc_phy_1_8v_init(host);
- else
- dwcmshc_phy_3_3v_init(host);
+ dwcmshc_phy_init(host);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
emmc_ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
@@ -602,6 +637,73 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc)
sdhci_dumpregs(mmc_priv(mmc));
}
+static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /* Set Send Status Command Idle Timer to 10.66us (256 * 1 / 24) */
+ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
+ reg = (reg & ~CQHCI_SSC1_CIT_MASK) | 0x0100;
+ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
+
+ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+ reg |= CQHCI_ENABLE;
+ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+}
+
+static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ while (reg & SDHCI_DATA_AVAILABLE) {
+ sdhci_readl(host, SDHCI_BUFFER);
+ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ }
+
+ sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
+
+ sdhci_cqe_enable(mmc);
+}
+
+static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ u32 ctrl;
+
+ /*
+ * During CQE command transfers, command complete bit gets latched.
+ * So s/w should clear command complete interrupt status when CQE is
+ * either halted or disabled. Otherwise unexpected SDCHI legacy
+ * interrupt gets triggered when CQE is halted/disabled.
+ */
+ spin_lock_irqsave(&host->lock, flags);
+ ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
+ ctrl |= SDHCI_INT_RESPONSE;
+ sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ sdhci_cqe_disable(mmc, recovery);
+}
+
+static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 ctrl;
+
+ ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+ ctrl &= ~CQHCI_ENABLE;
+ sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+}
+
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -629,10 +731,11 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_set_clock(host, clock);
- /* Disable cmd conflict check */
+ /* Disable cmd conflict check and internal clock gate */
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
extra = sdhci_readl(host, reg);
extra &= ~BIT(0);
+ extra |= BIT(4);
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
@@ -720,6 +823,10 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *priv = dwc_priv->priv;
+ u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
+
+ if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
+ cqhci_deactivate(host->mmc);
if (mask & SDHCI_RESET_ALL && priv->reset) {
reset_control_assert(priv->reset);
@@ -728,6 +835,9 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
}
sdhci_reset(host, mask);
+
+ /* Enable INTERNAL CLOCK */
+ sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
}
static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
@@ -787,6 +897,29 @@ static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv
}
}
+static void dwcmshc_rk3576_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+ struct device *dev = mmc_dev(host->mmc);
+ int ret;
+
+ /*
+ * This works around the design of the RK3576's power domains, which
+ * makes the PD_NVM power domain, which the sdhci controller on the
+ * RK3576 is in, never come back the same way once it's run-time
+ * suspended once. This can happen during early kernel boot if no driver
+ * is using either PD_NVM or its child power domain PD_SDGMAC for a
+ * short moment, leading to it being turned off to save power. By
+ * keeping it on, sdhci suspending won't lead to PD_NVM becoming a
+ * candidate for getting turned off.
+ */
+ ret = dev_pm_genpd_rpm_always_on(dev, true);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_warn(dev, "failed to set PD rpm always on, SoC may hang later: %pe\n",
+ ERR_PTR(ret));
+
+ dwcmshc_rk35xx_postinit(host, dwc_priv);
+}
+
static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -850,15 +983,7 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask)
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u16 ctrl_2;
- sdhci_reset(host, mask);
-
- /* The T-Head 1520 SoC does not comply with the SDHCI specification
- * regarding the "Software Reset for CMD line should clear 'Command
- * Complete' in the Normal Interrupt Status Register." Clear the bit
- * here to compensate for this quirk.
- */
- if (mask & SDHCI_RESET_CMD)
- sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+ dwcmshc_reset(host, mask);
if (priv->flags & FLAG_IO_FIXED_1V8) {
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
@@ -904,7 +1029,7 @@ static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 val, emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
- sdhci_reset(host, mask);
+ dwcmshc_reset(host, mask);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
@@ -976,7 +1101,7 @@ static void cv18xx_sdhci_post_tuning(struct sdhci_host *host)
val |= SDHCI_INT_DATA_AVAIL;
sdhci_writel(host, val, SDHCI_INT_STATUS);
- sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ dwcmshc_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
}
static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
@@ -1113,12 +1238,417 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
ARRAY_SIZE(clk_ids), clk_ids);
}
+static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ u16 clk;
+
+ host->mmc->actual_clock = clock;
+
+ if (clock == 0) {
+ sdhci_set_clock(host, clock);
+ return;
+ }
+
+ clk_set_rate(pltfm_host->clk, clock);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ dwcmshc_enable_card_clk(host);
+}
+
+static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay)
+{
+ delay &= PHY_CLK_MAX_DELAY_MASK;
+
+ /* phy clk delay line config */
+ sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
+ sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
+ sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
+}
+
+static void sdhci_eic7700_config_phy(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ struct eic7700_priv *priv = dwc_priv->priv;
+ unsigned int val, drv;
+
+ drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF);
+ drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF);
+
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+ val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+ val |= DWCMSHC_CARD_IS_EMMC;
+ sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+ }
+
+ /* reset phy, config phy's pad */
+ sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
+
+ /* configure phy pads */
+ val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
+ val |= PHY_PAD_RXSEL_1V8;
+ sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+
+ /* Clock PAD Setting */
+ val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+
+ /* PHY strobe PAD setting (EMMC only) */
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+ val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= PHY_PAD_RXSEL_1V8;
+ sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+ }
+ usleep_range(2000, 3000);
+ sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
+ sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
+}
+
+static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ /* after reset all, the phy's config will be clear */
+ if (mask == SDHCI_RESET_ALL)
+ sdhci_eic7700_config_phy(host);
+}
+
+static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv)
+{
+ int ret;
+
+ priv->reset = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(priv->reset)) {
+ ret = PTR_ERR(priv->reset);
+ dev_err(dev, "failed to get reset control %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_assert(priv->reset);
+ if (ret) {
+ dev_err(dev, "Failed to assert reset signals: %d\n", ret);
+ return ret;
+ }
+ usleep_range(2000, 2100);
+ ret = reset_control_deassert(priv->reset);
+ if (ret) {
+ dev_err(dev, "Failed to deassert reset signals: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm)
+{
+ switch (dr_ohm) {
+ case 100:
+ return PHYCTRL_DR_100OHM;
+ case 66:
+ return PHYCTRL_DR_66OHM;
+ case 50:
+ return PHYCTRL_DR_50OHM;
+ case 40:
+ return PHYCTRL_DR_40OHM;
+ case 33:
+ return PHYCTRL_DR_33OHM;
+ }
+
+ dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm);
+ return PHYCTRL_DR_50OHM;
+}
+
+static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ int delay_min = -1;
+ int delay_max = -1;
+ int cmd_error = 0;
+ int delay = 0;
+ int i = 0;
+ int ret;
+
+ for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
+ sdhci_eic7700_config_phy_delay(host, i);
+ ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+ if (ret) {
+ host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ usleep_range(200, 210);
+ if (delay_min != -1 && delay_max != -1)
+ break;
+ } else {
+ if (delay_min == -1) {
+ delay_min = i;
+ continue;
+ } else {
+ delay_max = i;
+ continue;
+ }
+ }
+ }
+ if (delay_min == -1 && delay_max == -1) {
+ pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc));
+ sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
+ return ret;
+ }
+
+ delay = (delay_min + delay_max) / 2;
+ sdhci_eic7700_config_phy_delay(host, delay);
+
+ return 0;
+}
+
+static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
+ int phase_code = -1;
+ int code_range = -1;
+ bool is_sd = false;
+ int code_min = -1;
+ int code_max = -1;
+ int cmd_error = 0;
+ int ret = 0;
+ int i = 0;
+
+ if ((host->mmc->caps2 & sd_caps) == sd_caps)
+ is_sd = true;
+
+ for (i = 0; i <= MAX_PHASE_CODE; i++) {
+ /* Centered Phase code */
+ sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+ ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+ host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ if (ret) {
+ /* SD specific range tracking */
+ if (is_sd && code_min != -1 && code_max != -1) {
+ if (code_max - code_min > code_range) {
+ code_range = code_max - code_min;
+ phase_code = (code_min + code_max) / 2;
+ if (code_range > TUNING_RANGE_THRESHOLD)
+ break;
+ }
+ code_min = -1;
+ code_max = -1;
+ }
+ /* EMMC breaks after first valid range */
+ if (!is_sd && code_min != -1 && code_max != -1)
+ break;
+ } else {
+ /* Track valid phase code range */
+ if (code_min == -1) {
+ code_min = i;
+ if (!is_sd)
+ continue;
+ }
+ code_max = i;
+ if (is_sd && i == MAX_PHASE_CODE) {
+ if (code_max - code_min > code_range) {
+ code_range = code_max - code_min;
+ phase_code = (code_min + code_max) / 2;
+ }
+ }
+ }
+ }
+
+ /* Handle tuning failure case */
+ if ((is_sd && phase_code == -1) ||
+ (!is_sd && code_min == -1 && code_max == -1)) {
+ pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc));
+ sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+ return -EIO;
+ }
+ if (!is_sd)
+ phase_code = (code_min + code_max) / 2;
+
+ sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+
+ /* SD specific final verification */
+ if (is_sd) {
+ ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+ host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ if (ret) {
+ pr_err("%s: Final phase code 0x%x verification failed!\n",
+ mmc_hostname(host->mmc), phase_code);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ int ret = 0;
+ u16 ctrl;
+ u32 val;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+ val |= AT_CTRL_SW_TUNE_EN;
+ sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+
+ sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+ sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
+
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+ ret = sdhci_eic7700_delay_tuning(host, opcode);
+ if (ret)
+ return ret;
+ }
+
+ ret = sdhci_eic7700_phase_code_tuning(host, opcode);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u8 status;
+ u32 val;
+ int ret;
+
+ dwcmshc_set_uhs_signaling(host, timing);
+
+ /* here need make dll locked when in hs400 at 200MHz */
+ if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) {
+ val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+ val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY));
+ /* 2-cycle latency */
+ val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2);
+ sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+
+ sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) |
+ 0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */
+ /* DLL jump step input */
+ sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
+ sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK,
+ PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R);
+ /* Sets the value of DLL's offset input */
+ sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
+ /*
+ * Sets the value of DLL's olbt loadval input. Controls the Ibt
+ * timer's timeout value at which DLL runs a revalidation cycle.
+ */
+ sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
+ sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
+ usleep_range(100, 110);
+
+ ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000,
+ false, host, PHY_DLL_STATUS_R);
+ if (ret) {
+ pr_err("%s: DLL lock timeout! status: 0x%x\n",
+ mmc_hostname(host->mmc), status);
+ return;
+ }
+
+ status = sdhci_readb(host, PHY_DLL_STATUS_R);
+ if (status & DLL_ERROR_STS) {
+ pr_err("%s: DLL lock failed!err_status:0x%x\n",
+ mmc_hostname(host->mmc), status);
+ }
+ }
+}
+
+static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing)
+{
+ u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
+
+ if ((host->mmc->caps2 & sd_caps) == sd_caps)
+ sdhci_set_uhs_signaling(host, timing);
+ else
+ sdhci_eic7700_set_uhs_signaling(host, timing);
+}
+
+static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+ u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ unsigned int val, hsp_int_status, hsp_pwr_ctrl;
+ struct of_phandle_args args;
+ struct eic7700_priv *priv;
+ struct regmap *hsp_regmap;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dwc_priv->priv = priv;
+
+ ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
+ if (ret) {
+ dev_err(dev, "failed to reset\n");
+ return ret;
+ }
+
+ ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
+ if (ret) {
+ dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
+ return ret;
+ }
+
+ hsp_regmap = syscon_node_to_regmap(args.np);
+ if (IS_ERR(hsp_regmap)) {
+ dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
+ of_node_put(args.np);
+ return PTR_ERR(hsp_regmap);
+ }
+ hsp_int_status = args.args[0];
+ hsp_pwr_ctrl = args.args[1];
+ of_node_put(args.np);
+ /*
+ * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
+ * This signals to the eMMC controller that platform clocks (card, ACLK,
+ * BCLK, TMCLK) are enabled and stable.
+ */
+ regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
+ /*
+ * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
+ * This signals that VDD is stable and permits transition to high-speed
+ * modes (e.g., UHS-I).
+ */
+ regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
+
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
+ dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
+ else
+ dwc_priv->delay_line = PHY_DELAY_CODE_SD;
+
+ if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
+ priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
+ return 0;
+}
+
static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = dwcmshc_get_max_clock,
- .reset = sdhci_reset,
+ .reset = dwcmshc_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.irq = dwcmshc_cqe_irq_handler,
};
@@ -1163,7 +1693,7 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
.get_max_clock = dwcmshc_get_max_clock,
.reset = th1520_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
- .voltage_switch = dwcmshc_phy_1_8v_init,
+ .voltage_switch = dwcmshc_phy_init,
.platform_execute_tuning = th1520_execute_tuning,
};
@@ -1187,6 +1717,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
.platform_execute_tuning = th1520_execute_tuning,
};
+static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
+ .set_clock = sdhci_eic7700_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_eic7700_reset,
+ .set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
+ .set_power = sdhci_set_power_and_bus_voltage,
+ .irq = dwcmshc_cqe_irq_handler,
+ .platform_execute_tuning = sdhci_eic7700_executing_tuning,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_ops,
@@ -1206,6 +1748,15 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
};
#endif
+static const struct cqhci_host_ops rk35xx_cqhci_ops = {
+ .pre_enable = rk35xx_sdhci_cqe_pre_enable,
+ .enable = rk35xx_sdhci_cqe_enable,
+ .disable = rk35xx_sdhci_cqe_disable,
+ .post_disable = rk35xx_sdhci_cqe_post_disable,
+ .dumpregs = dwcmshc_cqhci_dumpregs,
+ .set_tran_desc = dwcmshc_set_tran_desc,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_rk35xx_ops,
@@ -1214,10 +1765,24 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
},
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
.init = dwcmshc_rk35xx_init,
.postinit = dwcmshc_rk35xx_postinit,
};
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk3576_postinit,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_th1520_ops,
@@ -1244,6 +1809,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
.init = sg2042_init,
};
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_eic7700_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .init = eic7700_init,
+};
+
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.enable = dwcmshc_sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
@@ -1251,7 +1827,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.set_tran_desc = dwcmshc_set_tran_desc,
};
-static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
+static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
+ const struct dwcmshc_pltfm_data *pltfm_data)
{
struct cqhci_host *cq_host;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1281,7 +1858,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
}
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
- cq_host->ops = &dwcmshc_cqhci_ops;
+ if (pltfm_data->cqhci_host_ops)
+ cq_host->ops = pltfm_data->cqhci_host_ops;
+ else
+ cq_host->ops = &dwcmshc_cqhci_ops;
/* Enable using of 128-bit task descriptors */
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
@@ -1317,6 +1897,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.data = &sdhci_dwcmshc_rk35xx_pdata,
},
{
+ .compatible = "rockchip,rk3576-dwcmshc",
+ .data = &sdhci_dwcmshc_rk3576_pdata,
+ },
+ {
.compatible = "rockchip,rk3568-dwcmshc",
.data = &sdhci_dwcmshc_rk35xx_pdata,
},
@@ -1340,6 +1924,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "sophgo,sg2042-dwcmshc",
.data = &sdhci_dwcmshc_sg2042_pdata,
},
+ {
+ .compatible = "eswin,eic7700-dwcmshc",
+ .data = &sdhci_dwcmshc_eic7700_pdata,
+ },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@@ -1389,14 +1977,13 @@ static int dwcmshc_probe(struct platform_device *pdev)
if (dev->of_node) {
pltfm_host->clk = devm_clk_get(dev, "core");
- if (IS_ERR(pltfm_host->clk)) {
- err = PTR_ERR(pltfm_host->clk);
- dev_err(dev, "failed to get core clk: %d\n", err);
- goto free_pltfm;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return dev_err_probe(dev, PTR_ERR(pltfm_host->clk),
+ "failed to get core clk\n");
+
err = clk_prepare_enable(pltfm_host->clk);
if (err)
- goto free_pltfm;
+ return err;
priv->bus_clk = devm_clk_get(dev, "bus");
if (!IS_ERR(priv->bus_clk))
@@ -1446,7 +2033,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
priv->vendor_specific_area2 =
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
- dwcmshc_cqhci_init(host, pdev);
+ dwcmshc_cqhci_init(host, pdev, pltfm_data);
}
if (pltfm_data->postinit)
@@ -1469,8 +2056,6 @@ err_clk:
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
-free_pltfm:
- sdhci_pltfm_free(pdev);
return err;
}
@@ -1502,10 +2087,8 @@ static void dwcmshc_remove(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
- sdhci_pltfm_free(pdev);
}
-#ifdef CONFIG_PM_SLEEP
static int dwcmshc_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -1576,20 +2159,6 @@ disable_clk:
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
-#endif
-
-#ifdef CONFIG_PM
-
-static void dwcmshc_enable_card_clk(struct sdhci_host *host)
-{
- u16 ctrl;
-
- ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
- ctrl |= SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
- }
-}
static int dwcmshc_runtime_suspend(struct device *dev)
{
@@ -1609,12 +2178,9 @@ static int dwcmshc_runtime_resume(struct device *dev)
return 0;
}
-#endif
-
static const struct dev_pm_ops dwcmshc_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
- SET_RUNTIME_PM_OPS(dwcmshc_runtime_suspend,
- dwcmshc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
+ RUNTIME_PM_OPS(dwcmshc_runtime_suspend, dwcmshc_runtime_resume, NULL)
};
static struct platform_driver sdhci_dwcmshc_driver = {
@@ -1623,7 +2189,7 @@ static struct platform_driver sdhci_dwcmshc_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_dwcmshc_dt_ids,
.acpi_match_table = ACPI_PTR(sdhci_dwcmshc_acpi_ids),
- .pm = &dwcmshc_pmops,
+ .pm = pm_ptr(&dwcmshc_pmops),
},
.probe = dwcmshc_probe,
.remove = dwcmshc_remove,
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 002d0d59b992..8345e2c5a034 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -1234,7 +1234,6 @@ static u32 esdhc_irq(struct sdhci_host *host, u32 intmask)
return intmask;
}
-#ifdef CONFIG_PM_SLEEP
static u32 esdhc_proctl;
static int esdhc_of_suspend(struct device *dev)
{
@@ -1260,11 +1259,8 @@ static int esdhc_of_resume(struct device *dev)
}
return ret;
}
-#endif
-static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops,
- esdhc_of_suspend,
- esdhc_of_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, esdhc_of_suspend, esdhc_of_resume);
static const struct sdhci_ops sdhci_esdhc_be_ops = {
.read_l = esdhc_be_readl,
@@ -1499,18 +1495,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
/* call to generic mmc_of_parse to support additional capabilities */
ret = mmc_of_parse(host->mmc);
if (ret)
- goto err;
+ return ret;
mmc_of_parse_voltage(host->mmc, &host->ocr_mask);
- ret = sdhci_add_host(host);
- if (ret)
- goto err;
-
- return 0;
- err:
- sdhci_pltfm_free(pdev);
- return ret;
+ return sdhci_add_host(host);
}
static struct platform_driver sdhci_esdhc_driver = {
@@ -1518,7 +1507,7 @@ static struct platform_driver sdhci_esdhc_driver = {
.name = "sdhci-esdhc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_esdhc_of_match,
- .pm = &esdhc_of_dev_pm_ops,
+ .pm = pm_sleep_ptr(&esdhc_of_dev_pm_ops),
},
.probe = sdhci_esdhc_probe,
.remove = sdhci_pltfm_remove,
diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c
new file mode 100644
index 000000000000..0cc97e23a2f9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-k1.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
+ * Copyright (c) 2025 Yixun Lan <dlan@gentoo.org>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/init.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+#define SPACEMIT_SDHC_MMC_CTRL_REG 0x114
+#define SDHC_MISC_INT_EN BIT(1)
+#define SDHC_MISC_INT BIT(2)
+#define SDHC_ENHANCE_STROBE_EN BIT(8)
+#define SDHC_MMC_HS400 BIT(9)
+#define SDHC_MMC_HS200 BIT(10)
+#define SDHC_MMC_CARD_MODE BIT(12)
+
+#define SPACEMIT_SDHC_TX_CFG_REG 0x11C
+#define SDHC_TX_INT_CLK_SEL BIT(30)
+#define SDHC_TX_MUX_SEL BIT(31)
+
+#define SPACEMIT_SDHC_PHY_CTRL_REG 0x160
+#define SDHC_PHY_FUNC_EN BIT(0)
+#define SDHC_PHY_PLL_LOCK BIT(1)
+#define SDHC_HOST_LEGACY_MODE BIT(31)
+
+#define SPACEMIT_SDHC_PHY_FUNC_REG 0x164
+#define SDHC_PHY_TEST_EN BIT(7)
+#define SDHC_HS200_USE_RFIFO BIT(15)
+
+#define SPACEMIT_SDHC_PHY_DLLCFG 0x168
+#define SDHC_DLL_PREDLY_NUM GENMASK(3, 2)
+#define SDHC_DLL_FULLDLY_RANGE GENMASK(5, 4)
+#define SDHC_DLL_VREG_CTRL GENMASK(7, 6)
+#define SDHC_DLL_ENABLE BIT(31)
+
+#define SPACEMIT_SDHC_PHY_DLLCFG1 0x16C
+#define SDHC_DLL_REG1_CTRL GENMASK(7, 0)
+#define SDHC_DLL_REG2_CTRL GENMASK(15, 8)
+#define SDHC_DLL_REG3_CTRL GENMASK(23, 16)
+#define SDHC_DLL_REG4_CTRL GENMASK(31, 24)
+
+#define SPACEMIT_SDHC_PHY_DLLSTS 0x170
+#define SDHC_DLL_LOCK_STATE BIT(0)
+
+#define SPACEMIT_SDHC_PHY_PADCFG_REG 0x178
+#define SDHC_PHY_DRIVE_SEL GENMASK(2, 0)
+#define SDHC_RX_BIAS_CTRL BIT(5)
+
+struct spacemit_sdhci_host {
+ struct clk *clk_core;
+ struct clk *clk_io;
+};
+
+/* All helper functions will update clr/set while preserve rest bits */
+static inline void spacemit_sdhci_setbits(struct sdhci_host *host, u32 val, int reg)
+{
+ sdhci_writel(host, sdhci_readl(host, reg) | val, reg);
+}
+
+static inline void spacemit_sdhci_clrbits(struct sdhci_host *host, u32 val, int reg)
+{
+ sdhci_writel(host, sdhci_readl(host, reg) & ~val, reg);
+}
+
+static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u32 set, int reg)
+{
+ u32 val = sdhci_readl(host, reg);
+
+ val = (val & ~clr) | set;
+ sdhci_writel(host, val, reg);
+}
+
+static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ if (mask != SDHCI_RESET_ALL)
+ return;
+
+ spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK,
+ SPACEMIT_SDHC_PHY_CTRL_REG);
+
+ spacemit_sdhci_clrsetbits(host, SDHC_PHY_DRIVE_SEL,
+ SDHC_RX_BIAS_CTRL | FIELD_PREP(SDHC_PHY_DRIVE_SEL, 4),
+ SPACEMIT_SDHC_PHY_PADCFG_REG);
+
+ if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC))
+ spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG);
+}
+
+static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ if (timing == MMC_TIMING_MMC_HS200)
+ spacemit_sdhci_setbits(host, SDHC_MMC_HS200, SPACEMIT_SDHC_MMC_CTRL_REG);
+
+ if (timing == MMC_TIMING_MMC_HS400)
+ spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG);
+
+ sdhci_set_uhs_signaling(host, timing);
+
+ if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO))
+ spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2);
+}
+
+static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50)
+ spacemit_sdhci_setbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG);
+ else
+ spacemit_sdhci_clrbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG);
+
+ sdhci_set_clock(host, clock);
+};
+
+static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host)
+{
+ u32 state;
+ int ret;
+
+ spacemit_sdhci_clrsetbits(host, SDHC_DLL_PREDLY_NUM |
+ SDHC_DLL_FULLDLY_RANGE |
+ SDHC_DLL_VREG_CTRL,
+ FIELD_PREP(SDHC_DLL_PREDLY_NUM, 1) |
+ FIELD_PREP(SDHC_DLL_FULLDLY_RANGE, 1) |
+ FIELD_PREP(SDHC_DLL_VREG_CTRL, 1),
+ SPACEMIT_SDHC_PHY_DLLCFG);
+
+ spacemit_sdhci_clrsetbits(host, SDHC_DLL_REG1_CTRL,
+ FIELD_PREP(SDHC_DLL_REG1_CTRL, 0x92),
+ SPACEMIT_SDHC_PHY_DLLCFG1);
+
+ spacemit_sdhci_setbits(host, SDHC_DLL_ENABLE, SPACEMIT_SDHC_PHY_DLLCFG);
+
+ ret = readl_poll_timeout(host->ioaddr + SPACEMIT_SDHC_PHY_DLLSTS, state,
+ state & SDHC_DLL_LOCK_STATE, 2, 100);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n");
+}
+
+static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (!ios->enhanced_strobe) {
+ spacemit_sdhci_clrbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG);
+ return;
+ }
+
+ spacemit_sdhci_setbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG);
+ spacemit_sdhci_phy_dll_init(host);
+}
+
+static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ return clk_get_rate(pltfm_host->clk);
+}
+
+static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG);
+
+ return 0;
+}
+
+static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ spacemit_sdhci_phy_dll_init(host);
+}
+
+static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ spacemit_sdhci_clrbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK,
+ SPACEMIT_SDHC_PHY_CTRL_REG);
+ spacemit_sdhci_clrbits(host, SDHC_MMC_HS400 | SDHC_MMC_HS200 | SDHC_ENHANCE_STROBE_EN,
+ SPACEMIT_SDHC_MMC_CTRL_REG);
+ spacemit_sdhci_clrbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG);
+
+ udelay(5);
+
+ spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK,
+ SPACEMIT_SDHC_PHY_CTRL_REG);
+}
+
+static inline int spacemit_sdhci_get_clocks(struct device *dev,
+ struct sdhci_pltfm_host *pltfm_host)
+{
+ struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host);
+
+ sdhst->clk_core = devm_clk_get_enabled(dev, "core");
+ if (IS_ERR(sdhst->clk_core))
+ return -EINVAL;
+
+ sdhst->clk_io = devm_clk_get_enabled(dev, "io");
+ if (IS_ERR(sdhst->clk_io))
+ return -EINVAL;
+
+ pltfm_host->clk = sdhst->clk_io;
+
+ return 0;
+}
+
+static const struct sdhci_ops spacemit_sdhci_ops = {
+ .get_max_clock = spacemit_sdhci_clk_get_max_clock,
+ .reset = spacemit_sdhci_reset,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_clock = spacemit_sdhci_set_clock,
+ .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
+ .ops = &spacemit_sdhci_ops,
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+ SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA |
+ SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
+static const struct of_device_id spacemit_sdhci_of_match[] = {
+ { .compatible = "spacemit,k1-sdhci" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match);
+
+static int spacemit_sdhci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spacemit_sdhci_host *sdhst;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_host *host;
+ struct mmc_host_ops *mops;
+ int ret;
+
+ host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err_pltfm;
+
+ sdhci_get_of_property(pdev);
+
+ if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) {
+ mops = &host->mmc_host_ops;
+ mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400;
+ mops->hs400_complete = spacemit_sdhci_post_select_hs400;
+ mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200;
+ mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe;
+ }
+
+ host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
+
+ ret = spacemit_sdhci_get_clocks(dev, pltfm_host);
+ if (ret)
+ goto err_pltfm;
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_pltfm;
+
+ return 0;
+
+err_pltfm:
+ return ret;
+}
+
+static struct platform_driver spacemit_sdhci_driver = {
+ .driver = {
+ .name = "sdhci-spacemit",
+ .of_match_table = spacemit_sdhci_of_match,
+ },
+ .probe = spacemit_sdhci_probe,
+ .remove = sdhci_pltfm_remove,
+};
+module_platform_driver(spacemit_sdhci_driver);
+
+MODULE_DESCRIPTION("SpacemiT SDHCI platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of-ma35d1.c b/drivers/mmc/host/sdhci-of-ma35d1.c
index 1e6d180100ad..287026422616 100644
--- a/drivers/mmc/host/sdhci-of-ma35d1.c
+++ b/drivers/mmc/host/sdhci-of-ma35d1.c
@@ -211,20 +211,18 @@ static int ma35_probe(struct platform_device *pdev)
priv = sdhci_pltfm_priv(pltfm_host);
pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL);
- if (IS_ERR(pltfm_host->clk)) {
- err = dev_err_probe(dev, PTR_ERR(pltfm_host->clk), "failed to get clk\n");
- goto err_sdhci;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return dev_err_probe(dev, PTR_ERR(pltfm_host->clk),
+ "failed to get clk\n");
err = mmc_of_parse(host->mmc);
if (err)
- goto err_sdhci;
+ return err;
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
- if (IS_ERR(priv->rst)) {
- err = dev_err_probe(dev, PTR_ERR(priv->rst), "failed to get reset control\n");
- goto err_sdhci;
- }
+ if (IS_ERR(priv->rst))
+ return dev_err_probe(dev, PTR_ERR(priv->rst),
+ "failed to get reset control\n");
sdhci_get_of_property(pdev);
@@ -255,7 +253,7 @@ static int ma35_probe(struct platform_device *pdev)
err = sdhci_add_host(host);
if (err)
- goto err_sdhci;
+ return err;
/*
* Split data into chunks of 16 or 8 bytes for transmission.
@@ -268,10 +266,6 @@ static int ma35_probe(struct platform_device *pdev)
sdhci_writew(host, ctl, MA35_SDHCI_MBIUCTL);
return 0;
-
-err_sdhci:
- sdhci_pltfm_free(pdev);
- return err;
}
static void ma35_disable_card_clk(struct sdhci_host *host)
@@ -291,7 +285,6 @@ static void ma35_remove(struct platform_device *pdev)
sdhci_remove_host(host, 0);
ma35_disable_card_clk(host);
- sdhci_pltfm_free(pdev);
}
static const struct of_device_id sdhci_ma35_dt_ids[] = {
diff --git a/drivers/mmc/host/sdhci-of-sparx5.c b/drivers/mmc/host/sdhci-of-sparx5.c
index d2aa684e786f..b3db1e2c4c0e 100644
--- a/drivers/mmc/host/sdhci-of-sparx5.c
+++ b/drivers/mmc/host/sdhci-of-sparx5.c
@@ -185,11 +185,9 @@ static int sdhci_sparx5_probe(struct platform_device *pdev)
sdhci_sparx5->host = host;
pltfm_host->clk = devm_clk_get_enabled(&pdev->dev, "core");
- if (IS_ERR(pltfm_host->clk)) {
- ret = PTR_ERR(pltfm_host->clk);
- dev_err(&pdev->dev, "failed to get and enable core clk: %d\n", ret);
- goto free_pltfm;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pltfm_host->clk),
+ "failed to get and enable core clk\n");
if (!of_property_read_u32(np, "microchip,clock-delay", &value) &&
(value > 0 && value <= MSHC_DLY_CC_MAX))
@@ -199,14 +197,12 @@ static int sdhci_sparx5_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto free_pltfm;
+ return ret;
sdhci_sparx5->cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon);
- if (IS_ERR(sdhci_sparx5->cpu_ctrl)) {
- dev_err(&pdev->dev, "No CPU syscon regmap !\n");
- ret = PTR_ERR(sdhci_sparx5->cpu_ctrl);
- goto free_pltfm;
- }
+ if (IS_ERR(sdhci_sparx5->cpu_ctrl))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sdhci_sparx5->cpu_ctrl),
+ "No CPU syscon regmap !\n");
if (sdhci_sparx5->delay_clock >= 0)
sparx5_set_delay(host, sdhci_sparx5->delay_clock);
@@ -222,7 +218,7 @@ static int sdhci_sparx5_probe(struct platform_device *pdev)
ret = sdhci_add_host(host);
if (ret)
- goto free_pltfm;
+ return ret;
/* Set AXI bus master to use un-cached access (for DMA) */
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA) &&
@@ -235,10 +231,6 @@ static int sdhci_sparx5_probe(struct platform_device *pdev)
mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_TYPE));
return ret;
-
-free_pltfm:
- sdhci_pltfm_free(pdev);
- return ret;
}
static const struct of_device_id sdhci_sparx5_of_match[] = {
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 54d795205fb4..b5d7c1a80a92 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -1257,7 +1257,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
ret = mmc_of_parse(mmc);
if (ret)
- goto err_pltfm_free;
+ return ret;
soc = soc_device_match(sdhci_omap_soc_devices);
if (soc) {
@@ -1270,26 +1270,23 @@ static int sdhci_omap_probe(struct platform_device *pdev)
mmc->f_max = 48000000;
}
- if (!mmc_can_gpio_ro(mmc))
+ if (!mmc_host_can_gpio_ro(mmc))
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
pltfm_host->clk = devm_clk_get(dev, "fck");
- if (IS_ERR(pltfm_host->clk)) {
- ret = PTR_ERR(pltfm_host->clk);
- goto err_pltfm_free;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return PTR_ERR(pltfm_host->clk);
ret = clk_set_rate(pltfm_host->clk, mmc->f_max);
- if (ret) {
- dev_err(dev, "failed to set clock to %d\n", mmc->f_max);
- goto err_pltfm_free;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set clock to %d\n", mmc->f_max);
omap_host->pbias = devm_regulator_get_optional(dev, "pbias");
if (IS_ERR(omap_host->pbias)) {
ret = PTR_ERR(omap_host->pbias);
if (ret != -ENODEV)
- goto err_pltfm_free;
+ return ret;
dev_dbg(dev, "unable to get pbias regulator %d\n", ret);
}
omap_host->pbias_enabled = false;
@@ -1339,8 +1336,8 @@ static int sdhci_omap_probe(struct platform_device *pdev)
/* R1B responses is required to properly manage HW busy detection. */
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
- /* Allow card power off and runtime PM for eMMC/SD card devices */
- mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM;
+ /* Enable SDIO card power off. */
+ mmc->caps |= MMC_CAP_POWER_OFF_CARD;
ret = sdhci_setup_host(host);
if (ret)
@@ -1373,7 +1370,6 @@ static int sdhci_omap_probe(struct platform_device *pdev)
host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
}
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -1382,14 +1378,10 @@ err_cleanup_host:
sdhci_cleanup_host(host);
err_rpm_put:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
err_rpm_disable:
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
-
-err_pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -1406,11 +1398,9 @@ static void sdhci_omap_remove(struct platform_device *pdev)
pm_runtime_put_sync(dev);
/* Ensure device gets disabled despite userspace sysfs config */
pm_runtime_force_suspend(dev);
- sdhci_pltfm_free(pdev);
}
-#ifdef CONFIG_PM
-static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_host)
+static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host)
{
omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
@@ -1421,7 +1411,7 @@ static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_
}
/* Order matters here, HCTL must be restored in two phases */
-static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
+static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host)
{
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl);
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa);
@@ -1433,7 +1423,7 @@ static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *om
sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise);
}
-static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev)
+static int sdhci_omap_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1452,7 +1442,7 @@ static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev)
+static int sdhci_omap_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1467,13 +1457,10 @@ static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops sdhci_omap_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(sdhci_omap_runtime_suspend,
- sdhci_omap_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(sdhci_omap_runtime_suspend, sdhci_omap_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver sdhci_omap_driver = {
@@ -1482,7 +1469,7 @@ static struct platform_driver sdhci_omap_driver = {
.driver = {
.name = "sdhci-omap",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_omap_dev_pm_ops,
+ .pm = pm_ptr(&sdhci_omap_dev_pm_ops),
.of_match_table = omap_sdhci_match,
},
};
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 2b300bc4a701..47a0a738862b 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/gpio.h>
+#include <linux/gpio/machine.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/debugfs.h>
@@ -151,18 +152,15 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip)
{
struct sdhci_pci_slot *slot;
struct sdhci_host *host;
- int i, ret;
- for (i = 0; i < chip->num_slots; i++) {
+ for (int i = 0; i < chip->num_slots; i++) {
slot = chip->slots[i];
if (!slot)
continue;
host = slot->host;
- ret = sdhci_runtime_suspend_host(host);
- if (ret)
- goto err_pci_runtime_suspend;
+ sdhci_runtime_suspend_host(host);
if (chip->rpm_retune &&
host->tuning_mode != SDHCI_TUNING_MODE_3)
@@ -170,26 +168,18 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip)
}
return 0;
-
-err_pci_runtime_suspend:
- while (--i >= 0)
- sdhci_runtime_resume_host(chip->slots[i]->host, 0);
- return ret;
}
static int sdhci_pci_runtime_resume_host(struct sdhci_pci_chip *chip)
{
struct sdhci_pci_slot *slot;
- int i, ret;
- for (i = 0; i < chip->num_slots; i++) {
+ for (int i = 0; i < chip->num_slots; i++) {
slot = chip->slots[i];
if (!slot)
continue;
- ret = sdhci_runtime_resume_host(slot->host, 0);
- if (ret)
- return ret;
+ sdhci_runtime_resume_host(slot->host, 0);
}
return 0;
@@ -609,8 +599,12 @@ static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power(host, mode, vdd);
- if (mode == MMC_POWER_OFF)
+ if (mode == MMC_POWER_OFF) {
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD ||
+ slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BYT_SD)
+ usleep_range(15000, 17500);
return;
+ }
/*
* Bus power might not enable after D3 -> D0 transition due to the
@@ -685,8 +679,19 @@ static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
+static void sdhci_intel_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+ /* Stop card clock separately to avoid glitches on clock line */
+ if (clk & SDHCI_CLOCK_CARD_EN)
+ sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL);
+
+ sdhci_set_clock(host, clock);
+}
+
static const struct sdhci_ops sdhci_intel_byt_ops = {
- .set_clock = sdhci_set_clock,
+ .set_clock = sdhci_intel_set_clock,
.set_power = sdhci_intel_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
@@ -696,7 +701,7 @@ static const struct sdhci_ops sdhci_intel_byt_ops = {
};
static const struct sdhci_ops sdhci_intel_glk_ops = {
- .set_clock = sdhci_set_clock,
+ .set_clock = sdhci_intel_set_clock,
.set_power = sdhci_intel_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
@@ -908,7 +913,8 @@ static bool glk_broken_cqhci(struct sdhci_pci_slot *slot)
{
return slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC &&
(dmi_match(DMI_BIOS_VENDOR, "LENOVO") ||
- dmi_match(DMI_SYS_VENDOR, "IRBIS"));
+ dmi_match(DMI_SYS_VENDOR, "IRBIS") ||
+ dmi_match(DMI_SYS_VENDOR, "Positivo Tecnologia SA"));
}
static bool jsl_broken_hs400es(struct sdhci_pci_slot *slot)
@@ -1236,6 +1242,29 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
.priv_size = sizeof(struct intel_host),
};
+/* DMI quirks for devices with missing or broken CD GPIO info */
+static const struct gpiod_lookup_table vexia_edu_atla10_cd_gpios = {
+ .dev_id = "0000:00:12.0",
+ .table = {
+ GPIO_LOOKUP("INT33FC:00", 38, "cd", GPIO_ACTIVE_HIGH),
+ { }
+ },
+};
+
+static const struct dmi_system_id sdhci_intel_byt_cd_gpio_override[] = {
+ {
+ /* Vexia Edu Atla 10 tablet 9V version */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
+ },
+ .driver_data = (void *)&vexia_edu_atla10_cd_gpios,
+ },
+ { }
+};
+
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
#ifdef CONFIG_PM_SLEEP
.resume = byt_resume,
@@ -1254,6 +1283,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
.add_host = byt_add_host,
.remove_slot = byt_remove_slot,
.ops = &sdhci_intel_byt_ops,
+ .cd_gpio_override = sdhci_intel_byt_cd_gpio_override,
.priv_size = sizeof(struct intel_host),
};
@@ -2055,6 +2085,42 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
* *
\*****************************************************************************/
+static struct gpiod_lookup_table *sdhci_pci_add_gpio_lookup_table(
+ struct sdhci_pci_chip *chip)
+{
+ struct gpiod_lookup_table *cd_gpio_lookup_table;
+ const struct dmi_system_id *dmi_id = NULL;
+ size_t count;
+
+ if (chip->fixes && chip->fixes->cd_gpio_override)
+ dmi_id = dmi_first_match(chip->fixes->cd_gpio_override);
+
+ if (!dmi_id)
+ return NULL;
+
+ cd_gpio_lookup_table = dmi_id->driver_data;
+ for (count = 0; cd_gpio_lookup_table->table[count].key; count++)
+ ;
+
+ cd_gpio_lookup_table = kmemdup(dmi_id->driver_data,
+ /* count + 1 terminating entry */
+ struct_size(cd_gpio_lookup_table, table, count + 1),
+ GFP_KERNEL);
+ if (!cd_gpio_lookup_table)
+ return ERR_PTR(-ENOMEM);
+
+ gpiod_add_lookup_table(cd_gpio_lookup_table);
+ return cd_gpio_lookup_table;
+}
+
+static void sdhci_pci_remove_gpio_lookup_table(struct gpiod_lookup_table *lookup_table)
+{
+ if (lookup_table) {
+ gpiod_remove_lookup_table(lookup_table);
+ kfree(lookup_table);
+ }
+}
+
static struct sdhci_pci_slot *sdhci_pci_probe_slot(
struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
int slotno)
@@ -2108,7 +2174,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
ret = pcim_iomap_regions(pdev, BIT(bar), mmc_hostname(host->mmc));
if (ret) {
dev_err(&pdev->dev, "cannot request region\n");
- goto cleanup;
+ return ERR_PTR(ret);
}
host->ioaddr = pcim_iomap_table(pdev)[bar];
@@ -2116,7 +2182,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
if (chip->fixes && chip->fixes->probe_slot) {
ret = chip->fixes->probe_slot(slot);
if (ret)
- goto cleanup;
+ return ERR_PTR(ret);
}
host->mmc->pm_caps = MMC_PM_KEEP_POWER;
@@ -2130,8 +2196,19 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
device_init_wakeup(&pdev->dev, true);
if (slot->cd_idx >= 0) {
+ struct gpiod_lookup_table *cd_gpio_lookup_table;
+
+ cd_gpio_lookup_table = sdhci_pci_add_gpio_lookup_table(chip);
+ if (IS_ERR(cd_gpio_lookup_table)) {
+ ret = PTR_ERR(cd_gpio_lookup_table);
+ goto remove;
+ }
+
ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,
slot->cd_override_level, 0);
+
+ sdhci_pci_remove_gpio_lookup_table(cd_gpio_lookup_table);
+
if (ret && ret != -EPROBE_DEFER)
ret = mmc_gpiod_request_cd(host->mmc, NULL,
slot->cd_idx,
@@ -2166,9 +2243,6 @@ remove:
if (chip->fixes && chip->fixes->remove_slot)
chip->fixes->remove_slot(slot, 0);
-cleanup:
- sdhci_free_host(host);
-
return ERR_PTR(ret);
}
@@ -2189,8 +2263,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
slot->chip->fixes->remove_slot(slot, dead);
-
- sdhci_free_host(slot->host);
}
int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot)
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 4c2ae71770f7..b0f91cc9e40e 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -283,10 +283,26 @@
#define PCIE_GLI_9767_UHS2_CTL2_ZC_VALUE 0xb
#define PCIE_GLI_9767_UHS2_CTL2_ZC_CTL BIT(6)
#define PCIE_GLI_9767_UHS2_CTL2_ZC_CTL_VALUE 0x1
+#define PCIE_GLI_9767_UHS2_CTL2_FORCE_PHY_RESETN BIT(13)
+#define PCIE_GLI_9767_UHS2_CTL2_FORCE_RESETN_VALUE BIT(14)
#define GLI_MAX_TUNING_LOOP 40
/* Genesys Logic chipset */
+static void sdhci_gli_mask_replay_timer_timeout(struct pci_dev *pdev)
+{
+ int aer;
+ u32 value;
+
+ /* mask the replay timer timeout of AER */
+ aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
+ if (aer) {
+ pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value);
+ value |= PCI_ERR_COR_REP_TIMER;
+ pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value);
+ }
+}
+
static inline void gl9750_wt_on(struct sdhci_host *host)
{
u32 wt_value;
@@ -607,7 +623,6 @@ static void gl9750_hw_setting(struct sdhci_host *host)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
struct pci_dev *pdev;
- int aer;
u32 value;
pdev = slot->chip->pdev;
@@ -626,12 +641,7 @@ static void gl9750_hw_setting(struct sdhci_host *host)
pci_set_power_state(pdev, PCI_D0);
/* mask the replay timer timeout of AER */
- aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
- if (aer) {
- pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value);
- value |= PCI_ERR_COR_REP_TIMER;
- pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value);
- }
+ sdhci_gli_mask_replay_timer_timeout(pdev);
gl9750_wt_off(host);
}
@@ -806,7 +816,6 @@ static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock)
static void gl9755_hw_setting(struct sdhci_pci_slot *slot)
{
struct pci_dev *pdev = slot->chip->pdev;
- int aer;
u32 value;
gl9755_wt_on(pdev);
@@ -841,12 +850,7 @@ static void gl9755_hw_setting(struct sdhci_pci_slot *slot)
pci_set_power_state(pdev, PCI_D0);
/* mask the replay timer timeout of AER */
- aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
- if (aer) {
- pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value);
- value |= PCI_ERR_COR_REP_TIMER;
- pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value);
- }
+ sdhci_gli_mask_replay_timer_timeout(pdev);
gl9755_wt_off(pdev);
}
@@ -1177,6 +1181,65 @@ static void gl9767_set_low_power_negotiation(struct pci_dev *pdev, bool enable)
gl9767_vhs_read(pdev);
}
+static void sdhci_gl9767_uhs2_phy_reset(struct sdhci_host *host, bool assert)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ u32 value, set, clr;
+
+ if (assert) {
+ /* Assert reset, set RESETN and clean RESETN_VALUE */
+ set = PCIE_GLI_9767_UHS2_CTL2_FORCE_PHY_RESETN;
+ clr = PCIE_GLI_9767_UHS2_CTL2_FORCE_RESETN_VALUE;
+ } else {
+ /* De-assert reset, clean RESETN and set RESETN_VALUE */
+ set = PCIE_GLI_9767_UHS2_CTL2_FORCE_RESETN_VALUE;
+ clr = PCIE_GLI_9767_UHS2_CTL2_FORCE_PHY_RESETN;
+ }
+
+ gl9767_vhs_write(pdev);
+ pci_read_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, &value);
+ value |= set;
+ pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, value);
+ value &= ~clr;
+ pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, value);
+ gl9767_vhs_read(pdev);
+}
+
+static void __gl9767_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)
+{
+ u8 pwr = 0;
+
+ if (mode != MMC_POWER_OFF) {
+ pwr = sdhci_get_vdd_value(vdd);
+ if (!pwr)
+ WARN(1, "%s: Invalid vdd %#x\n",
+ mmc_hostname(host->mmc), vdd);
+ pwr |= SDHCI_VDD2_POWER_180;
+ }
+
+ if (host->pwr == pwr)
+ return;
+
+ host->pwr = pwr;
+
+ if (pwr == 0) {
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ } else {
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+
+ pwr |= SDHCI_POWER_ON;
+ sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL);
+ usleep_range(5000, 6250);
+
+ /* Assert reset */
+ sdhci_gl9767_uhs2_phy_reset(host, true);
+ pwr |= SDHCI_VDD2_POWER_ON;
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+ usleep_range(5000, 6250);
+ }
+}
+
static void sdhci_gl9767_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pci_slot *slot = sdhci_priv(host);
@@ -1203,6 +1266,11 @@ static void sdhci_gl9767_set_clock(struct sdhci_host *host, unsigned int clock)
}
sdhci_enable_clk(host, clk);
+
+ if (mmc_card_uhs2(host->mmc))
+ /* De-assert reset */
+ sdhci_gl9767_uhs2_phy_reset(host, false);
+
gl9767_set_low_power_negotiation(pdev, true);
}
@@ -1474,7 +1542,7 @@ static void sdhci_gl9767_set_power(struct sdhci_host *host, unsigned char mode,
gl9767_vhs_read(pdev);
sdhci_gli_overcurrent_event_enable(host, false);
- sdhci_uhs2_set_power(host, mode, vdd);
+ __gl9767_uhs2_set_power(host, mode, vdd);
sdhci_gli_overcurrent_event_enable(host, true);
} else {
gl9767_vhs_write(pdev);
@@ -1751,7 +1819,7 @@ cleanup:
return ret;
}
-static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
+static void gl9763e_hw_setting(struct sdhci_pci_slot *slot)
{
struct pci_dev *pdev = slot->chip->pdev;
u32 value;
@@ -1780,6 +1848,9 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5);
pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value);
+ /* mask the replay timer timeout of AER */
+ sdhci_gli_mask_replay_timer_timeout(pdev);
+
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
@@ -1923,7 +1994,7 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
gli_pcie_enable_msi(slot);
host->mmc_host_ops.hs400_enhanced_strobe =
gl9763e_hs400_enhanced_strobe;
- gli_set_gl9763e(slot);
+ gl9763e_hw_setting(slot);
sdhci_enable_v4_mode(host);
return 0;
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index e807c039a8b1..f38f0bd4165c 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -157,6 +157,7 @@ struct sdhci_pci_fixes {
#endif
const struct sdhci_ops *ops;
+ const struct dmi_system_id *cd_gpio_override;
size_t priv_size;
};
diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c
index d6a299f49900..7ddac0befed8 100644
--- a/drivers/mmc/host/sdhci-pic32.c
+++ b/drivers/mmc/host/sdhci-pic32.c
@@ -157,20 +157,20 @@ static int pic32_sdhci_probe(struct platform_device *pdev)
ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD,
ADMA_FIFO_WR_THSHLD);
if (ret)
- goto err_host;
+ goto err;
}
sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
if (IS_ERR(sdhci_pdata->sys_clk)) {
ret = PTR_ERR(sdhci_pdata->sys_clk);
dev_err(&pdev->dev, "Error getting clock\n");
- goto err_host;
+ goto err;
}
ret = clk_prepare_enable(sdhci_pdata->sys_clk);
if (ret) {
dev_err(&pdev->dev, "Error enabling clock\n");
- goto err_host;
+ goto err;
}
sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk");
@@ -203,8 +203,6 @@ err_base_clk:
clk_disable_unprepare(sdhci_pdata->base_clk);
err_sys_clk:
clk_disable_unprepare(sdhci_pdata->sys_clk);
-err_host:
- sdhci_pltfm_free(pdev);
err:
dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret);
return ret;
@@ -220,7 +218,6 @@ static void pic32_sdhci_remove(struct platform_device *pdev)
sdhci_remove_host(host, scratch == (u32)~0);
clk_disable_unprepare(sdhci_pdata->base_clk);
clk_disable_unprepare(sdhci_pdata->sys_clk);
- sdhci_pltfm_free(pdev);
}
static const struct of_device_id pic32_sdhci_id_table[] = {
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 62753d72198a..7f6ac636f040 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -146,20 +146,11 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_init);
-void sdhci_pltfm_free(struct platform_device *pdev)
-{
- struct sdhci_host *host = platform_get_drvdata(pdev);
-
- sdhci_free_host(host);
-}
-EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
-
int sdhci_pltfm_init_and_add_host(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size)
{
struct sdhci_host *host;
- int ret = 0;
host = sdhci_pltfm_init(pdev, pdata, priv_size);
if (IS_ERR(host))
@@ -167,11 +158,7 @@ int sdhci_pltfm_init_and_add_host(struct platform_device *pdev,
sdhci_get_property(pdev);
- ret = sdhci_add_host(host);
- if (ret)
- sdhci_pltfm_free(pdev);
-
- return ret;
+ return sdhci_add_host(host);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_init_and_add_host);
@@ -181,7 +168,6 @@ void sdhci_pltfm_remove(struct platform_device *pdev)
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
- sdhci_pltfm_free(pdev);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_remove);
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index b81d5b0fd616..9c32e8a289d6 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -97,7 +97,6 @@ static inline void sdhci_get_of_property(struct platform_device *pdev)
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size);
-extern void sdhci_pltfm_free(struct platform_device *pdev);
extern int sdhci_pltfm_init_and_add_host(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index 45b6f0891c47..76346353dc55 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -258,7 +258,6 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
struct sdhci_host *host = NULL;
const struct sdhci_pxa_variant *variant;
- int ret;
struct clk *clk, *clk_core;
host = sdhci_pltfm_init(pdev, NULL, sizeof(*pxav2_host));
@@ -271,19 +270,14 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
clk = devm_clk_get_optional_enabled(dev, "io");
if (!clk)
clk = devm_clk_get_enabled(dev, NULL);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- dev_err_probe(dev, ret, "failed to get io clock\n");
- goto free;
- }
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to get io clock\n");
pltfm_host->clk = clk;
clk_core = devm_clk_get_optional_enabled(dev, "core");
- if (IS_ERR(clk_core)) {
- ret = PTR_ERR(clk_core);
- dev_err_probe(dev, ret, "failed to enable core clock\n");
- goto free;
- }
+ if (IS_ERR(clk_core))
+ return dev_err_probe(dev, PTR_ERR(clk_core),
+ "failed to enable core clock\n");
host->quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
@@ -332,15 +326,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
pxav2_host->pinctrl = NULL;
}
- ret = sdhci_add_host(host);
- if (ret)
- goto free;
-
- return 0;
-
-free:
- sdhci_pltfm_free(pdev);
- return ret;
+ return sdhci_add_host(host);
}
static struct platform_driver sdhci_pxav2_driver = {
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 990723a008ae..d082c4e21aa9 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -20,9 +20,11 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/mbus.h>
+#include <linux/units.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
@@ -51,6 +53,9 @@ struct sdhci_pxa {
struct clk *clk_io;
u8 power_mode;
void __iomem *sdio3_conf_reg;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
+ struct pinctrl_state *pins_uhs;
};
/*
@@ -313,8 +318,20 @@ static void pxav3_set_power(struct sdhci_host *host, unsigned char mode,
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
}
+static void pxav3_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct sdhci_pltfm_host *phost = sdhci_priv(host);
+ struct sdhci_pxa *pxa = sdhci_pltfm_priv(phost);
+ struct pinctrl_state *pins = clock < 100 * HZ_PER_MHZ ? pxa->pins_default : pxa->pins_uhs;
+
+ if (pins)
+ pinctrl_select_state(pxa->pinctrl, pins);
+
+ sdhci_set_clock(host, clock);
+}
+
static const struct sdhci_ops pxav3_sdhci_ops = {
- .set_clock = sdhci_set_clock,
+ .set_clock = pxav3_set_clock,
.set_power = pxav3_set_power,
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
@@ -366,6 +383,19 @@ static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
}
#endif
+static struct pinctrl_state *pxav3_lookup_pinstate(struct device *dev, struct pinctrl *pinctrl,
+ const char *name)
+{
+ struct pinctrl_state *pins = pinctrl_lookup_state(pinctrl, name);
+
+ if (IS_ERR(pins)) {
+ dev_dbg(dev, "could not get pinstate '%s': %ld\n", name, PTR_ERR(pins));
+ return NULL;
+ }
+
+ return pins;
+}
+
static int sdhci_pxav3_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_host *pltfm_host;
@@ -389,8 +419,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
pxa->clk_io = devm_clk_get(dev, NULL);
if (IS_ERR(pxa->clk_io)) {
dev_err(dev, "failed to get io clock\n");
- ret = PTR_ERR(pxa->clk_io);
- goto err_clk_get;
+ return PTR_ERR(pxa->clk_io);
}
pltfm_host->clk = pxa->clk_io;
clk_prepare_enable(pxa->clk_io);
@@ -399,6 +428,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
if (!IS_ERR(pxa->clk_core))
clk_prepare_enable(pxa->clk_core);
+ host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
@@ -440,6 +470,15 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
host->mmc->pm_caps |= pdata->pm_caps;
}
+ pxa->pinctrl = devm_pinctrl_get(dev);
+ if (!IS_ERR(pxa->pinctrl)) {
+ pxa->pins_default = pxav3_lookup_pinstate(dev, pxa->pinctrl, "default");
+ if (pxa->pins_default)
+ pxa->pins_uhs = pxav3_lookup_pinstate(dev, pxa->pinctrl, "state_uhs");
+ } else {
+ dev_dbg(dev, "could not get pinctrl handle: %ld\n", PTR_ERR(pxa->pinctrl));
+ }
+
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
@@ -465,8 +504,6 @@ err_of_parse:
err_mbus_win:
clk_disable_unprepare(pxa->clk_io);
clk_disable_unprepare(pxa->clk_core);
-err_clk_get:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -484,11 +521,8 @@ static void sdhci_pxav3_remove(struct platform_device *pdev)
clk_disable_unprepare(pxa->clk_io);
clk_disable_unprepare(pxa->clk_core);
-
- sdhci_pltfm_free(pdev);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_pxav3_suspend(struct device *dev)
{
int ret;
@@ -498,7 +532,6 @@ static int sdhci_pxav3_suspend(struct device *dev)
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
ret = sdhci_suspend_host(host);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
@@ -511,24 +544,18 @@ static int sdhci_pxav3_resume(struct device *dev)
pm_runtime_get_sync(dev);
ret = sdhci_resume_host(host);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
-#endif
-#ifdef CONFIG_PM
static int sdhci_pxav3_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host);
- int ret;
- ret = sdhci_runtime_suspend_host(host);
- if (ret)
- return ret;
+ sdhci_runtime_suspend_host(host);
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
@@ -550,14 +577,13 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
if (!IS_ERR(pxa->clk_core))
clk_prepare_enable(pxa->clk_core);
- return sdhci_runtime_resume_host(host, 0);
+ sdhci_runtime_resume_host(host, 0);
+ return 0;
}
-#endif
static const struct dev_pm_ops sdhci_pxav3_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
- SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
- sdhci_pxav3_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
+ RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, sdhci_pxav3_runtime_resume, NULL)
};
static struct platform_driver sdhci_pxav3_driver = {
@@ -565,7 +591,7 @@ static struct platform_driver sdhci_pxav3_driver = {
.name = "sdhci-pxav3",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
- .pm = &sdhci_pxav3_pmops,
+ .pm = pm_ptr(&sdhci_pxav3_pmops),
},
.probe = sdhci_pxav3_probe,
.remove = sdhci_pxav3_remove,
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index bdf4dc0d6b77..6bf66aaa86a6 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -507,15 +507,13 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
sc = sdhci_priv(host);
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- ret = -ENOMEM;
- goto err_pdata_io_clk;
- }
+ if (!pdata)
+ return -ENOMEM;
if (pdev->dev.of_node) {
ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
if (ret)
- goto err_pdata_io_clk;
+ return ret;
} else {
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
}
@@ -532,8 +530,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
sc->clk_io = devm_clk_get(dev, "hsmmc");
if (IS_ERR(sc->clk_io)) {
dev_err(dev, "failed to get io clock\n");
- ret = PTR_ERR(sc->clk_io);
- goto err_pdata_io_clk;
+ return PTR_ERR(sc->clk_io);
}
/* enable the local io clock and keep it running for the moment. */
@@ -661,9 +658,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
err_no_busclks:
clk_disable_unprepare(sc->clk_io);
- err_pdata_io_clk:
- sdhci_free_host(host);
-
return ret;
}
@@ -685,11 +679,8 @@ static void sdhci_s3c_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(sc->clk_io);
-
- sdhci_free_host(host);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_s3c_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -706,17 +697,14 @@ static int sdhci_s3c_resume(struct device *dev)
return sdhci_resume_host(host);
}
-#endif
-#ifdef CONFIG_PM
static int sdhci_s3c_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_s3c *ourhost = to_s3c(host);
struct clk *busclk = ourhost->clk_io;
- int ret;
- ret = sdhci_runtime_suspend_host(host);
+ sdhci_runtime_suspend_host(host);
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
@@ -724,7 +712,7 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
if (ourhost->cur_clk >= 0)
clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
clk_disable_unprepare(busclk);
- return ret;
+ return 0;
}
static int sdhci_s3c_runtime_resume(struct device *dev)
@@ -732,20 +720,17 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_s3c *ourhost = to_s3c(host);
struct clk *busclk = ourhost->clk_io;
- int ret;
clk_prepare_enable(busclk);
if (ourhost->cur_clk >= 0)
clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
- ret = sdhci_runtime_resume_host(host, 0);
- return ret;
+ sdhci_runtime_resume_host(host, 0);
+ return 0;
}
-#endif
static const struct dev_pm_ops sdhci_s3c_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
- SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
+ RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume, NULL)
};
static const struct platform_device_id sdhci_s3c_driver_ids[] = {
@@ -780,7 +765,7 @@ static struct platform_driver sdhci_s3c_driver = {
.name = "s3c-sdhci",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
- .pm = &sdhci_s3c_pmops,
+ .pm = pm_ptr(&sdhci_s3c_pmops),
},
};
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 770dc12b9ae9..72d21dc0cb69 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -59,7 +59,7 @@ static int sdhci_probe(struct platform_device *pdev)
if (IS_ERR(host->ioaddr)) {
ret = PTR_ERR(host->ioaddr);
dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
- goto err_host;
+ goto err;
}
host->hw_name = "sdhci";
@@ -67,7 +67,7 @@ static int sdhci_probe(struct platform_device *pdev)
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
ret = host->irq;
- goto err_host;
+ goto err;
}
host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
@@ -78,13 +78,13 @@ static int sdhci_probe(struct platform_device *pdev)
if (IS_ERR(sdhci->clk)) {
ret = PTR_ERR(sdhci->clk);
dev_dbg(&pdev->dev, "Error getting clock\n");
- goto err_host;
+ goto err;
}
ret = clk_prepare_enable(sdhci->clk);
if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n");
- goto err_host;
+ goto err;
}
ret = clk_set_rate(sdhci->clk, 50000000);
@@ -110,8 +110,6 @@ static int sdhci_probe(struct platform_device *pdev)
disable_clk:
clk_disable_unprepare(sdhci->clk);
-err_host:
- sdhci_free_host(host);
err:
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
return ret;
@@ -130,10 +128,8 @@ static void sdhci_remove(struct platform_device *pdev)
sdhci_remove_host(host, dead);
clk_disable_unprepare(sdhci->clk);
- sdhci_free_host(host);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -164,9 +160,8 @@ static int sdhci_resume(struct device *dev)
return sdhci_resume_host(host);
}
-#endif
-static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume);
static const struct of_device_id sdhci_spear_id_table[] = {
{ .compatible = "st,spear300-sdhci" },
@@ -178,7 +173,7 @@ static struct platform_driver sdhci_driver = {
.driver = {
.name = "sdhci",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_pm_ops,
+ .pm = pm_sleep_ptr(&sdhci_pm_ops),
.of_match_table = sdhci_spear_id_table,
},
.probe = sdhci_probe,
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
index db5e253b0f79..3584a2b314a9 100644
--- a/drivers/mmc/host/sdhci-sprd.c
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -764,7 +764,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto pltfm_free;
+ return ret;
if (!mmc_card_is_removable(host->mmc))
host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic;
@@ -778,34 +778,26 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
if (!IS_ERR(sprd_host->pinctrl)) {
sprd_host->pins_uhs =
pinctrl_lookup_state(sprd_host->pinctrl, "state_uhs");
- if (IS_ERR(sprd_host->pins_uhs)) {
- ret = PTR_ERR(sprd_host->pins_uhs);
- goto pltfm_free;
- }
+ if (IS_ERR(sprd_host->pins_uhs))
+ return PTR_ERR(sprd_host->pins_uhs);
sprd_host->pins_default =
pinctrl_lookup_state(sprd_host->pinctrl, "default");
- if (IS_ERR(sprd_host->pins_default)) {
- ret = PTR_ERR(sprd_host->pins_default);
- goto pltfm_free;
- }
+ if (IS_ERR(sprd_host->pins_default))
+ return PTR_ERR(sprd_host->pins_default);
}
clk = devm_clk_get(&pdev->dev, "sdio");
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto pltfm_free;
- }
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
sprd_host->clk_sdio = clk;
sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio);
if (!sprd_host->base_rate)
sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE;
clk = devm_clk_get(&pdev->dev, "enable");
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- goto pltfm_free;
- }
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
sprd_host->clk_enable = clk;
clk = devm_clk_get(&pdev->dev, "2x_enable");
@@ -814,7 +806,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
ret = clk_prepare_enable(sprd_host->clk_sdio);
if (ret)
- goto pltfm_free;
+ return ret;
ret = clk_prepare_enable(sprd_host->clk_enable);
if (ret)
@@ -871,7 +863,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup_host;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
@@ -891,9 +882,6 @@ clk_disable2:
clk_disable:
clk_disable_unprepare(sprd_host->clk_sdio);
-
-pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -907,8 +895,6 @@ static void sdhci_sprd_remove(struct platform_device *pdev)
clk_disable_unprepare(sprd_host->clk_sdio);
clk_disable_unprepare(sprd_host->clk_enable);
clk_disable_unprepare(sprd_host->clk_2x_enable);
-
- sdhci_pltfm_free(pdev);
}
static const struct of_device_id sdhci_sprd_of_match[] = {
@@ -917,7 +903,6 @@ static const struct of_device_id sdhci_sprd_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match);
-#ifdef CONFIG_PM
static int sdhci_sprd_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -964,13 +949,10 @@ clk_2x_disable:
return ret;
}
-#endif
static const struct dev_pm_ops sdhci_sprd_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend,
- sdhci_sprd_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend, sdhci_sprd_runtime_resume, NULL)
};
static struct platform_driver sdhci_sprd_driver = {
@@ -980,7 +962,7 @@ static struct platform_driver sdhci_sprd_driver = {
.name = "sdhci_sprd_r11",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_sprd_of_match,
- .pm = &sdhci_sprd_pm_ops,
+ .pm = pm_ptr(&sdhci_sprd_pm_ops),
},
};
module_platform_driver(sdhci_sprd_driver);
diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c
index 4973e08a98f8..bf6685805137 100644
--- a/drivers/mmc/host/sdhci-st.c
+++ b/drivers/mmc/host/sdhci-st.c
@@ -380,13 +380,13 @@ static int sdhci_st_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret) {
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
- goto err_of;
+ goto err_pltfm_init;
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare clock\n");
- goto err_of;
+ goto err_pltfm_init;
}
ret = clk_prepare_enable(icnclk);
@@ -423,8 +423,6 @@ err_out:
clk_disable_unprepare(icnclk);
err_icnclk:
clk_disable_unprepare(clk);
-err_of:
- sdhci_pltfm_free(pdev);
err_pltfm_init:
reset_control_assert(rstc);
@@ -447,7 +445,6 @@ static void sdhci_st_remove(struct platform_device *pdev)
reset_control_assert(rstc);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_st_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -494,9 +491,8 @@ static int sdhci_st_resume(struct device *dev)
return sdhci_resume_host(host);
}
-#endif
-static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume);
static const struct of_device_id st_sdhci_match[] = {
{ .compatible = "st,sdhci" },
@@ -511,7 +507,7 @@ static struct platform_driver sdhci_st_driver = {
.driver = {
.name = "sdhci-st",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_st_pmops,
+ .pm = pm_sleep_ptr(&sdhci_st_pmops),
.of_match_table = st_sdhci_match,
},
};
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 4d402b601883..820ce4dae58b 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -1525,7 +1525,6 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
- SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER,
@@ -1694,7 +1693,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
rc = mmc_of_parse(host->mmc);
if (rc)
- goto err_parse_dt;
+ return rc;
if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
host->mmc->caps |= MMC_CAP_1_8V_DDR;
@@ -1740,7 +1739,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
if (IS_ERR(clk)) {
rc = PTR_ERR(clk);
if (rc == -EPROBE_DEFER)
- goto err_power_req;
+ return rc;
dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc);
clk = NULL;
@@ -1751,7 +1750,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
if (rc) {
dev_err(&pdev->dev,
"failed to enable tmclk: %d\n", rc);
- goto err_power_req;
+ return rc;
}
tegra_host->tmclk = clk;
@@ -1812,8 +1811,6 @@ err_rst_get:
err_clk_get:
clk_disable_unprepare(tegra_host->tmclk);
err_power_req:
-err_parse_dt:
- sdhci_pltfm_free(pdev);
return rc;
}
@@ -1832,10 +1829,9 @@ static void sdhci_tegra_remove(struct platform_device *pdev)
pm_runtime_force_suspend(&pdev->dev);
clk_disable_unprepare(tegra_host->tmclk);
- sdhci_pltfm_free(pdev);
}
-static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
+static int sdhci_tegra_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1845,7 +1841,7 @@ static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
+static int sdhci_tegra_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1853,7 +1849,6 @@ static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
return clk_prepare_enable(pltfm_host->clk);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_tegra_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -1914,12 +1909,10 @@ disable_clk:
pm_runtime_force_suspend(dev);
return ret;
}
-#endif
static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
+ RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
};
static struct platform_driver sdhci_tegra_driver = {
@@ -1927,7 +1920,7 @@ static struct platform_driver sdhci_tegra_driver = {
.name = "sdhci-tegra",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_tegra_dt_match,
- .pm = &sdhci_tegra_dev_pm_ops,
+ .pm = pm_ptr(&sdhci_tegra_dev_pm_ops),
},
.probe = sdhci_tegra_probe,
.remove = sdhci_tegra_remove,
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index c53b64d50c0d..c459a08d01da 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -99,8 +99,8 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
/* hw clears the bit when it's done */
if (read_poll_timeout_atomic(sdhci_readw, val, !(val & mask), 10,
UHS2_RESET_TIMEOUT_100MS, true, host, SDHCI_UHS2_SW_RESET)) {
- pr_warn("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__,
- mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc));
+ pr_debug("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__,
+ mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc));
sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
return;
}
@@ -295,7 +295,8 @@ static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
sdhci_uhs2_set_power(host, ios->power_mode, ios->vdd);
- sdhci_set_clock(host, host->clock);
+ host->ops->set_clock(host, ios->clock);
+ host->clock = ios->clock;
}
static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -335,8 +336,8 @@ static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT),
100, UHS2_INTERFACE_DETECT_TIMEOUT_100MS, true,
host, SDHCI_PRESENT_STATE)) {
- pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ pr_debug("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dbg_dumpregs(host, "UHS2 interface detect timeout in 100ms");
return -EIO;
}
@@ -345,8 +346,8 @@ static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC),
100, UHS2_LANE_SYNC_TIMEOUT_150MS, true, host, SDHCI_PRESENT_STATE)) {
- pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ pr_debug("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
+ sdhci_dbg_dumpregs(host, "UHS2 Lane sync fail in 150ms");
return -EIO;
}
@@ -417,12 +418,12 @@ static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
host->ops->uhs2_pre_detect_init(host);
if (sdhci_uhs2_interface_detect(host)) {
- pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
+ pr_debug("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
return -EIO;
}
if (sdhci_uhs2_init(host)) {
- pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
+ pr_debug("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
return -EIO;
}
@@ -504,8 +505,8 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IN_DORMANT_STATE),
100, UHS2_CHECK_DORMANT_TIMEOUT_100MS, true, host,
SDHCI_PRESENT_STATE)) {
- pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ pr_debug("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dbg_dumpregs(host, "UHS2 IN_DORMANT fail in 100ms");
return -EIO;
}
return 0;
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
index 098f0ea45cbe..046e8100dd08 100644
--- a/drivers/mmc/host/sdhci-xenon.c
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -532,14 +532,13 @@ static int xenon_probe(struct platform_device *pdev)
if (dev->of_node) {
pltfm_host->clk = devm_clk_get(&pdev->dev, "core");
- if (IS_ERR(pltfm_host->clk)) {
- err = PTR_ERR(pltfm_host->clk);
- dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err);
- goto free_pltfm;
- }
+ if (IS_ERR(pltfm_host->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pltfm_host->clk),
+ "Failed to setup input clk.\n");
+
err = clk_prepare_enable(pltfm_host->clk);
if (err)
- goto free_pltfm;
+ return err;
priv->axi_clk = devm_clk_get(&pdev->dev, "axi");
if (IS_ERR(priv->axi_clk)) {
@@ -603,8 +602,6 @@ err_clk_axi:
clk_disable_unprepare(priv->axi_clk);
err_clk:
clk_disable_unprepare(pltfm_host->clk);
-free_pltfm:
- sdhci_pltfm_free(pdev);
return err;
}
@@ -623,11 +620,8 @@ static void xenon_remove(struct platform_device *pdev)
xenon_sdhc_unprepare(host);
clk_disable_unprepare(priv->axi_clk);
clk_disable_unprepare(pltfm_host->clk);
-
- sdhci_pltfm_free(pdev);
}
-#ifdef CONFIG_PM_SLEEP
static int xenon_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -640,19 +634,14 @@ static int xenon_suspend(struct device *dev)
priv->restore_needed = true;
return ret;
}
-#endif
-#ifdef CONFIG_PM
static int xenon_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
- int ret;
- ret = sdhci_runtime_suspend_host(host);
- if (ret)
- return ret;
+ sdhci_runtime_suspend_host(host);
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
@@ -687,22 +676,16 @@ static int xenon_runtime_resume(struct device *dev)
priv->restore_needed = false;
}
- ret = sdhci_runtime_resume_host(host, 0);
- if (ret)
- goto out;
+ sdhci_runtime_resume_host(host, 0);
return 0;
out:
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(xenon_runtime_suspend,
- xenon_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(xenon_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(xenon_runtime_suspend, xenon_runtime_resume, NULL)
};
static const struct of_device_id sdhci_xenon_dt_ids[] = {
@@ -731,7 +714,7 @@ static struct platform_driver sdhci_xenon_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sdhci_xenon_dt_ids,
.acpi_match_table = ACPI_PTR(sdhci_xenon_acpi_ids),
- .pm = &sdhci_xenon_dev_pm_ops,
+ .pm = pm_ptr(&sdhci_xenon_dev_pm_ops),
},
.probe = xenon_probe,
.remove = xenon_remove,
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f4a7733a8ad2..ac7e11f37af7 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -158,7 +158,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
u32 present;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
- !mmc_card_is_removable(host->mmc) || mmc_can_gpio_cd(host->mmc))
+ !mmc_card_is_removable(host->mmc) || mmc_host_can_gpio_cd(host->mmc))
return;
if (enable) {
@@ -517,9 +517,9 @@ EXPORT_SYMBOL_GPL(sdhci_mod_timer);
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
if (sdhci_data_line_cmd(mrq->cmd))
- del_timer(&host->data_timer);
+ timer_delete(&host->data_timer);
else
- del_timer(&host->timer);
+ timer_delete(&host->timer);
}
static inline bool sdhci_has_requests(struct sdhci_host *host)
@@ -2367,23 +2367,6 @@ void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios)
(ios->power_mode == MMC_POWER_UP) &&
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
sdhci_enable_preset_value(host, false);
-
- if (!ios->clock || ios->clock != host->clock) {
- host->ops->set_clock(host, ios->clock);
- host->clock = ios->clock;
-
- if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
- host->clock) {
- host->timeout_clk = mmc->actual_clock ?
- mmc->actual_clock / 1000 :
- host->clock / 1000;
- mmc->max_busy_timeout =
- host->ops->get_max_timeout_count ?
- host->ops->get_max_timeout_count(host) :
- 1 << 27;
- mmc->max_busy_timeout /= host->timeout_clk;
- }
- }
}
EXPORT_SYMBOL_GPL(sdhci_set_ios_common);
@@ -2410,6 +2393,23 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_set_ios_common(mmc, ios);
+ if (!ios->clock || ios->clock != host->clock) {
+ host->ops->set_clock(host, ios->clock);
+ host->clock = ios->clock;
+
+ if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
+ host->clock) {
+ host->timeout_clk = mmc->actual_clock ?
+ mmc->actual_clock / 1000 :
+ host->clock / 1000;
+ mmc->max_busy_timeout =
+ host->ops->get_max_timeout_count ?
+ host->ops->get_max_timeout_count(host) :
+ 1 << 27;
+ mmc->max_busy_timeout /= host->timeout_clk;
+ }
+ }
+
if (host->ops->set_power)
host->ops->set_power(host, ios->power_mode, ios->vdd);
else
@@ -2566,7 +2566,7 @@ int sdhci_get_ro(struct mmc_host *mmc)
is_readonly = 0;
} else if (host->ops->get_ro) {
is_readonly = host->ops->get_ro(host);
- } else if (mmc_can_gpio_ro(mmc)) {
+ } else if (mmc_host_can_gpio_ro(mmc)) {
is_readonly = mmc_gpio_get_ro(mmc);
/* Do not invert twice */
allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
@@ -3240,7 +3240,7 @@ static void sdhci_timeout_timer(struct timer_list *t)
struct sdhci_host *host;
unsigned long flags;
- host = from_timer(host, t, timer);
+ host = timer_container_of(host, t, timer);
spin_lock_irqsave(&host->lock, flags);
@@ -3262,7 +3262,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
struct sdhci_host *host;
unsigned long flags;
- host = from_timer(host, t, data_timer);
+ host = timer_container_of(host, t, data_timer);
spin_lock_irqsave(&host->lock, flags);
@@ -3739,7 +3739,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
{
return mmc_card_is_removable(host->mmc) &&
!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
- !mmc_can_gpio_cd(host->mmc);
+ !mmc_host_can_gpio_cd(host->mmc);
}
/*
@@ -3750,7 +3750,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
* sdhci_disable_irq_wakeups() since it will be set by
* sdhci_enable_card_detection() or sdhci_init().
*/
-static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
+bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
{
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE |
SDHCI_WAKE_ON_INT;
@@ -3782,8 +3782,9 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
return host->irq_wake_enabled;
}
+EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
-static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
+void sdhci_disable_irq_wakeups(struct sdhci_host *host)
{
u8 val;
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
@@ -3797,6 +3798,7 @@ static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
host->irq_wake_enabled = false;
}
+EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
int sdhci_suspend_host(struct sdhci_host *host)
{
@@ -3856,7 +3858,7 @@ int sdhci_resume_host(struct sdhci_host *host)
EXPORT_SYMBOL_GPL(sdhci_resume_host);
-int sdhci_runtime_suspend_host(struct sdhci_host *host)
+void sdhci_runtime_suspend_host(struct sdhci_host *host)
{
unsigned long flags;
@@ -3873,12 +3875,10 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
spin_lock_irqsave(&host->lock, flags);
host->runtime_suspended = true;
spin_unlock_irqrestore(&host->lock, flags);
-
- return 0;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
-int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset)
+void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset)
{
struct mmc_host *mmc = host->mmc;
unsigned long flags;
@@ -3924,8 +3924,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset)
sdhci_enable_card_detection(host);
spin_unlock_irqrestore(&host->lock, flags);
-
- return 0;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
@@ -4069,7 +4067,7 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
WARN_ON(dev == NULL);
- mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(struct sdhci_host) + priv_size);
if (!mmc)
return ERR_PTR(-ENOMEM);
@@ -4971,8 +4969,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
free_irq(host->irq, host);
- del_timer_sync(&host->timer);
- del_timer_sync(&host->data_timer);
+ timer_delete_sync(&host->timer);
+ timer_delete_sync(&host->data_timer);
destroy_workqueue(host->complete_wq);
@@ -4993,13 +4991,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
EXPORT_SYMBOL_GPL(sdhci_remove_host);
-void sdhci_free_host(struct sdhci_host *host)
-{
- mmc_free_host(host->mmc);
-}
-
-EXPORT_SYMBOL_GPL(sdhci_free_host);
-
/*****************************************************************************\
* *
* Driver init/exit *
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index cd0e35a80542..b6a571d866fa 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -811,7 +811,6 @@ static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size);
-void sdhci_free_host(struct sdhci_host *host);
static inline void *sdhci_priv(struct sdhci_host *host)
{
@@ -875,10 +874,19 @@ void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
#ifdef CONFIG_PM
+bool sdhci_enable_irq_wakeups(struct sdhci_host *host);
+void sdhci_disable_irq_wakeups(struct sdhci_host *host);
int sdhci_suspend_host(struct sdhci_host *host);
int sdhci_resume_host(struct sdhci_host *host);
-int sdhci_runtime_suspend_host(struct sdhci_host *host);
-int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset);
+void sdhci_runtime_suspend_host(struct sdhci_host *host);
+void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset);
+#else
+static inline bool sdhci_enable_irq_wakeups(struct sdhci_host *host) { return false; }
+static inline void sdhci_disable_irq_wakeups(struct sdhci_host *host) {}
+static inline int sdhci_suspend_host(struct sdhci_host *host) { return -EOPNOTSUPP; }
+static inline int sdhci_resume_host(struct sdhci_host *host) { return -EOPNOTSUPP; }
+static inline void sdhci_runtime_suspend_host(struct sdhci_host *host) {}
+static inline void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) {}
#endif
void sdhci_cqe_enable(struct mmc_host *mmc);
@@ -898,4 +906,20 @@ void sdhci_switch_external_dma(struct sdhci_host *host, bool en);
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable);
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
+#if defined(CONFIG_DYNAMIC_DEBUG) || \
+ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
+#define SDHCI_DBG_ANYWAY 0
+#elif defined(DEBUG)
+#define SDHCI_DBG_ANYWAY 1
+#else
+#define SDHCI_DBG_ANYWAY 0
+#endif
+
+#define sdhci_dbg_dumpregs(host, fmt) \
+do { \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
+ if (DYNAMIC_DEBUG_BRANCH(descriptor) || SDHCI_DBG_ANYWAY) \
+ sdhci_dumpregs(host); \
+} while (0)
+
#endif /* __SDHCI_HW_H */
diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index b73f673db92b..d235b0aecfdb 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -95,7 +95,6 @@ static const struct regmap_config sdhci_am654_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
- .fast_io = true,
};
struct timing_data {
@@ -156,6 +155,7 @@ struct sdhci_am654_data {
#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
#define SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA BIT(1)
+#define SDHCI_AM654_QUIRK_DISABLE_HS400 BIT(2)
};
struct window {
@@ -167,6 +167,7 @@ struct window {
struct sdhci_am654_driver_data {
const struct sdhci_pltfm_data *pdata;
u32 flags;
+ u32 quirks;
#define IOMUX_PRESENT (1 << 0)
#define FREQSEL_2_BIT (1 << 1)
#define STRBSEL_4_BIT (1 << 2)
@@ -612,7 +613,8 @@ static const struct sdhci_ops sdhci_am654_ops = {
static const struct sdhci_pltfm_data sdhci_am654_pdata = {
.ops = &sdhci_am654_ops,
.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
};
static const struct sdhci_am654_driver_data sdhci_am654_sr1_drvdata = {
@@ -642,7 +644,8 @@ static const struct sdhci_ops sdhci_j721e_8bit_ops = {
static const struct sdhci_pltfm_data sdhci_j721e_8bit_pdata = {
.ops = &sdhci_j721e_8bit_ops,
.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
};
static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = {
@@ -666,7 +669,8 @@ static const struct sdhci_ops sdhci_j721e_4bit_ops = {
static const struct sdhci_pltfm_data sdhci_j721e_4bit_pdata = {
.ops = &sdhci_j721e_4bit_ops,
.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
};
static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
@@ -674,6 +678,12 @@ static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
.flags = IOMUX_PRESENT,
};
+static const struct sdhci_am654_driver_data sdhci_am62_4bit_drvdata = {
+ .pdata = &sdhci_j721e_4bit_pdata,
+ .flags = IOMUX_PRESENT,
+ .quirks = SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA,
+};
+
static const struct soc_device_attribute sdhci_am654_devices[] = {
{ .family = "AM65X",
.revision = "SR1.0",
@@ -755,6 +765,7 @@ static int sdhci_am654_init(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
+ struct device *dev = mmc_dev(host->mmc);
u32 ctl_cfg_2 = 0;
u32 mask;
u32 val;
@@ -810,6 +821,12 @@ static int sdhci_am654_init(struct sdhci_host *host)
if (ret)
goto err_cleanup_host;
+ if (sdhci_am654->quirks & SDHCI_AM654_QUIRK_DISABLE_HS400 &&
+ host->mmc->caps2 & (MMC_CAP2_HS400 | MMC_CAP2_HS400_ES)) {
+ dev_info(dev, "HS400 mode not supported on this silicon revision, disabling it\n");
+ host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
+ }
+
ret = __sdhci_add_host(host);
if (ret)
goto err_cleanup_host;
@@ -868,16 +885,17 @@ static int sdhci_am654_get_of_property(struct platform_device *pdev,
if (device_property_read_bool(dev, "ti,fails-without-test-cd"))
sdhci_am654->quirks |= SDHCI_AM654_QUIRK_FORCE_CDTEST;
- /* Suppress v1p8 ena for eMMC and SD with vqmmc supply */
- if (!!of_parse_phandle(dev->of_node, "vmmc-supply", 0) ==
- !!of_parse_phandle(dev->of_node, "vqmmc-supply", 0))
- sdhci_am654->quirks |= SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA;
-
sdhci_get_of_property(pdev);
return 0;
}
+static const struct soc_device_attribute sdhci_am654_descope_hs400[] = {
+ { .family = "AM62PX", .revision = "SR1.0" },
+ { .family = "AM62PX", .revision = "SR1.1" },
+ { /* sentinel */ }
+};
+
static const struct of_device_id sdhci_am654_of_match[] = {
{
.compatible = "ti,am654-sdhci-5.1",
@@ -901,7 +919,7 @@ static const struct of_device_id sdhci_am654_of_match[] = {
},
{
.compatible = "ti,am62-sdhci",
- .data = &sdhci_j721e_4bit_drvdata,
+ .data = &sdhci_am62_4bit_drvdata,
},
{ /* sentinel */ }
};
@@ -935,39 +953,39 @@ static int sdhci_am654_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
sdhci_am654->flags = drvdata->flags;
+ sdhci_am654->quirks = drvdata->quirks;
clk_xin = devm_clk_get(dev, "clk_xin");
if (IS_ERR(clk_xin)) {
dev_err(dev, "clk_xin clock not found.\n");
- ret = PTR_ERR(clk_xin);
- goto err_pltfm_free;
+ return PTR_ERR(clk_xin);
}
pltfm_host->clk = clk_xin;
base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(base)) {
- ret = PTR_ERR(base);
- goto err_pltfm_free;
+ return PTR_ERR(base);
}
sdhci_am654->base = devm_regmap_init_mmio(dev, base,
&sdhci_am654_regmap_config);
if (IS_ERR(sdhci_am654->base)) {
dev_err(dev, "Failed to initialize regmap\n");
- ret = PTR_ERR(sdhci_am654->base);
- goto err_pltfm_free;
+ return PTR_ERR(sdhci_am654->base);
}
ret = sdhci_am654_get_of_property(pdev, sdhci_am654);
if (ret)
- goto err_pltfm_free;
+ return ret;
ret = mmc_of_parse(host->mmc);
- if (ret) {
- dev_err_probe(dev, ret, "parsing dt failed\n");
- goto err_pltfm_free;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "parsing dt failed\n");
+
+ soc = soc_device_match(sdhci_am654_descope_hs400);
+ if (soc)
+ sdhci_am654->quirks |= SDHCI_AM654_QUIRK_DISABLE_HS400;
host->mmc_host_ops.start_signal_voltage_switch = sdhci_am654_start_signal_voltage_switch;
host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning;
@@ -988,7 +1006,6 @@ static int sdhci_am654_probe(struct platform_device *pdev)
/* Setting up autosuspend */
pm_runtime_set_autosuspend_delay(dev, SDHCI_AM654_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(dev);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -998,8 +1015,6 @@ pm_disable:
pm_runtime_disable(dev);
pm_put:
pm_runtime_put_noidle(dev);
-err_pltfm_free:
- sdhci_pltfm_free(pdev);
return ret;
}
@@ -1018,10 +1033,8 @@ static void sdhci_am654_remove(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk);
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
- sdhci_pltfm_free(pdev);
}
-#ifdef CONFIG_PM
static int sdhci_am654_restore(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1079,9 +1092,7 @@ static int sdhci_am654_runtime_suspend(struct device *dev)
if (ret)
return ret;
- ret = sdhci_runtime_suspend_host(host);
- if (ret)
- return ret;
+ sdhci_runtime_suspend_host(host);
/* disable the clock */
clk_disable_unprepare(pltfm_host->clk);
@@ -1103,9 +1114,7 @@ static int sdhci_am654_runtime_resume(struct device *dev)
if (ret)
return ret;
- ret = sdhci_runtime_resume_host(host, 0);
- if (ret)
- return ret;
+ sdhci_runtime_resume_host(host, 0);
ret = cqhci_resume(host->mmc);
if (ret)
@@ -1113,20 +1122,17 @@ static int sdhci_am654_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops sdhci_am654_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(sdhci_am654_runtime_suspend,
- sdhci_am654_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ RUNTIME_PM_OPS(sdhci_am654_runtime_suspend, sdhci_am654_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver sdhci_am654_driver = {
.driver = {
.name = "sdhci-am654",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_am654_dev_pm_ops,
+ .pm = pm_ptr(&sdhci_am654_dev_pm_ops),
.of_match_table = sdhci_am654_of_match,
},
.probe = sdhci_am654_probe,
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
index ee66e4f3683d..e9b934e159ad 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -133,20 +133,18 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto err;
+ return ret;
if (dev_of_node(dev)) {
sdhci_get_of_property(pdev);
priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
- if (IS_ERR(priv->clk_iface)) {
- ret = PTR_ERR(priv->clk_iface);
- goto err;
- }
+ if (IS_ERR(priv->clk_iface))
+ return PTR_ERR(priv->clk_iface);
ret = clk_prepare_enable(priv->clk_iface);
if (ret)
- goto err;
+ return ret;
priv->clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(priv->clk)) {
@@ -200,9 +198,6 @@ err_rst:
clk_disable_unprepare(priv->clk);
err_clk:
clk_disable_unprepare(priv->clk_iface);
-err:
- sdhci_pltfm_free(pdev);
-
return ret;
}
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
index 57b8c1a96756..481cb552c2b4 100644
--- a/drivers/mmc/host/sdricoh_cs.c
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -403,9 +403,9 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
}
/* allocate privdata */
mmc = pcmcia_dev->priv =
- mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev);
+ devm_mmc_alloc_host(&pcmcia_dev->dev, sizeof(*host));
if (!mmc) {
- dev_err(dev, "mmc_alloc_host failed\n");
+ dev_err(dev, "devm_mmc_alloc_host failed\n");
result = -ENOMEM;
goto unmap_io;
}
@@ -431,7 +431,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
if (sdricoh_reset(host)) {
dev_dbg(dev, "could not reset\n");
result = -EIO;
- goto free_host;
+ goto unmap_io;
}
result = mmc_add_host(mmc);
@@ -440,8 +440,6 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
dev_dbg(dev, "mmc host registered\n");
return 0;
}
-free_host:
- mmc_free_host(mmc);
unmap_io:
pci_iounmap(pci_dev, iobase);
return result;
@@ -483,10 +481,8 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link)
mmc_remove_host(mmc);
pci_iounmap(host->pci_dev, host->iobase);
pci_dev_put(host->pci_dev);
- mmc_free_host(mmc);
}
pcmcia_disable_device(link);
-
}
#ifdef CONFIG_PM
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index ce60cec26b98..bf899c8e38f5 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -1444,13 +1444,13 @@ static int sh_mmcif_probe(struct platform_device *pdev)
if (IS_ERR(reg))
return PTR_ERR(reg);
- mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
ret = mmc_of_parse(mmc);
if (ret < 0)
- goto err_host;
+ return ret;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -1481,15 +1481,13 @@ static int sh_mmcif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
host->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- dev_err(dev, "cannot get clock: %d\n", ret);
- goto err_host;
- }
+ if (IS_ERR(host->clk))
+ return dev_err_probe(dev, PTR_ERR(host->clk),
+ "cannot get clock\n");
ret = clk_prepare_enable(host->clk);
if (ret < 0)
- goto err_host;
+ return ret;
sh_mmcif_clk_setup(host);
@@ -1542,8 +1540,6 @@ err_clk:
clk_disable_unprepare(host->clk);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
-err_host:
- mmc_free_host(mmc);
return ret;
}
@@ -1568,12 +1564,10 @@ static void sh_mmcif_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&host->timeout_work);
clk_disable_unprepare(host->clk);
- mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int sh_mmcif_suspend(struct device *dev)
{
struct sh_mmcif_host *host = dev_get_drvdata(dev);
@@ -1585,15 +1579,7 @@ static int sh_mmcif_suspend(struct device *dev)
return 0;
}
-static int sh_mmcif_resume(struct device *dev)
-{
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(sh_mmcif_dev_pm_ops, sh_mmcif_suspend, NULL);
static struct platform_driver sh_mmcif_driver = {
.probe = sh_mmcif_probe,
@@ -1601,7 +1587,7 @@ static struct platform_driver sh_mmcif_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sh_mmcif_dev_pm_ops,
+ .pm = pm_sleep_ptr(&sh_mmcif_dev_pm_ops),
.of_match_table = sh_mmcif_of_match,
},
};
diff --git a/drivers/mmc/host/sunplus-mmc.c b/drivers/mmc/host/sunplus-mmc.c
index 1cddea615a27..63279760239c 100644
--- a/drivers/mmc/host/sunplus-mmc.c
+++ b/drivers/mmc/host/sunplus-mmc.c
@@ -791,7 +791,7 @@ static int spmmc_get_cd(struct mmc_host *mmc)
{
int ret = 0;
- if (mmc_can_gpio_cd(mmc))
+ if (mmc_host_can_gpio_cd(mmc))
ret = mmc_gpio_get_cd(mmc);
if (ret < 0)
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 1508eead5d01..8dbcff53a631 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -1369,11 +1369,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
struct mmc_host *mmc;
int ret;
- mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);
- if (!mmc) {
- dev_err(&pdev->dev, "mmc alloc host failed\n");
- return -ENOMEM;
- }
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
+ if (!mmc)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "mmc alloc host failed\n");
platform_set_drvdata(pdev, mmc);
host = mmc_priv(mmc);
@@ -1383,15 +1382,13 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
ret = sunxi_mmc_resource_request(host, pdev);
if (ret)
- goto error_free_host;
+ return ret;
host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL);
- if (!host->sg_cpu) {
- dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n");
- ret = -ENOMEM;
- goto error_free_host;
- }
+ if (!host->sg_cpu)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "Failed to allocate DMA descriptor mem\n");
if (host->cfg->ccu_has_timings_switch) {
/*
@@ -1481,8 +1478,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
error_free_dma:
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
-error_free_host:
- mmc_free_host(mmc);
return ret;
}
@@ -1498,10 +1493,8 @@ static void sunxi_mmc_remove(struct platform_device *pdev)
sunxi_mmc_disable(host);
}
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
- mmc_free_host(mmc);
}
-#ifdef CONFIG_PM
static int sunxi_mmc_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@@ -1536,14 +1529,10 @@ static int sunxi_mmc_runtime_suspend(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops sunxi_mmc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
- sunxi_mmc_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, sunxi_mmc_runtime_resume, NULL)
};
static struct platform_driver sunxi_mmc_driver = {
@@ -1551,7 +1540,7 @@ static struct platform_driver sunxi_mmc_driver = {
.name = "sunxi-mmc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = sunxi_mmc_of_match,
- .pm = &sunxi_mmc_pm_ops,
+ .pm = pm_ptr(&sunxi_mmc_pm_ops),
},
.probe = sunxi_mmc_probe,
.remove = sunxi_mmc_remove,
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index aea14bf3e2e8..2cd69c9e9571 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -191,7 +191,7 @@ static void tifm_sd_transfer_data(struct tifm_sd *host)
}
off = sg[host->sg_pos].offset + host->block_pos;
- pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT);
+ pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT);
p_off = offset_in_page(off);
p_cnt = PAGE_SIZE - p_off;
p_cnt = min(p_cnt, cnt);
@@ -240,7 +240,7 @@ static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
}
off = sg[host->sg_pos].offset + host->block_pos;
- pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT);
+ pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT);
p_off = offset_in_page(off);
p_cnt = PAGE_SIZE - p_off;
p_cnt = min(p_cnt, cnt);
@@ -735,7 +735,7 @@ static void tifm_sd_end_cmd(struct work_struct *t)
spin_lock_irqsave(&sock->lock, flags);
- del_timer(&host->timer);
+ timer_delete(&host->timer);
mrq = host->req;
host->req = NULL;
@@ -777,7 +777,7 @@ static void tifm_sd_end_cmd(struct work_struct *t)
static void tifm_sd_abort(struct timer_list *t)
{
- struct tifm_sd *host = from_timer(host, t, timer);
+ struct tifm_sd *host = timer_container_of(host, t, timer);
pr_err("%s : card failed to respond for a long period of time "
"(%x, %x)\n",
@@ -947,7 +947,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
return rc;
}
- mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev);
+ mmc = devm_mmc_alloc_host(&sock->dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -982,10 +982,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
if (!rc)
rc = mmc_add_host(mmc);
- if (!rc)
- return 0;
- mmc_free_host(mmc);
return rc;
}
@@ -1015,8 +1012,6 @@ static void tifm_sd_remove(struct tifm_dev *sock)
spin_unlock_irqrestore(&sock->lock, flags);
mmc_remove_host(mmc);
dev_dbg(&sock->dev, "after remove\n");
-
- mmc_free_host(mmc);
}
#ifdef CONFIG_PM
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index a75755f31d31..b9de03325c58 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -16,6 +16,7 @@
#include <linux/dmaengine.h>
#include <linux/highmem.h>
+#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/scatterlist.h>
@@ -44,6 +45,7 @@
#define CTL_RESET_SD 0xe0
#define CTL_VERSION 0xe2
#define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
+#define CTL_SD_STATUS 0xf2 /* only known on RZ/{G2L,G3E,V2H} */
/* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
#define TMIO_STOP_STP BIT(0)
@@ -103,6 +105,10 @@
/* Definitions for values the CTL_SDIF_MODE register can take */
#define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */
+/* Definitions for values the CTL_SD_STATUS register can take */
+#define SD_STATUS_PWEN BIT(0) /* only known on RZ/{G3E,V2H} */
+#define SD_STATUS_IOVS BIT(16) /* only known on RZ/{G3E,V2H} */
+
/* Define some IRQ masks */
/* This is the mask used at reset by the chip */
#define TMIO_MASK_ALL 0x837f031d
@@ -188,13 +194,13 @@ struct tmio_mmc_host {
bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq);
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host);
+ void (*sdio_irq)(struct tmio_mmc_host *host);
const struct tmio_mmc_dma_ops *dma_ops;
};
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
struct tmio_mmc_data *pdata);
-void tmio_mmc_host_free(struct tmio_mmc_host *host);
int tmio_mmc_host_probe(struct tmio_mmc_host *host);
void tmio_mmc_host_remove(struct tmio_mmc_host *host);
void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
@@ -203,10 +209,8 @@ void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid);
-#ifdef CONFIG_PM
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
-#endif
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{
@@ -226,12 +230,31 @@ static inline u32 sd_ctrl_read16_and_16_as_32(struct tmio_mmc_host *host,
ioread16(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
}
+static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
+{
+ return ioread32(host->ctl + (addr << host->bus_shift));
+}
+
static inline void sd_ctrl_read32_rep(struct tmio_mmc_host *host, int addr,
u32 *buf, int count)
{
ioread32_rep(host->ctl + (addr << host->bus_shift), buf, count);
}
+#ifdef CONFIG_64BIT
+static inline void sd_ctrl_read64_rep(struct tmio_mmc_host *host, int addr,
+ u64 *buf, int count)
+{
+ readsq(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
+static inline void sd_ctrl_write64_rep(struct tmio_mmc_host *host, int addr,
+ const u64 *buf, int count)
+{
+ writesq(host->ctl + (addr << host->bus_shift), buf, count);
+}
+#endif
+
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr,
u16 val)
{
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 45a474ccab1c..775e0d9353d5 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -160,7 +160,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
host->sdio_irq_enabled = false;
- pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
}
}
@@ -297,7 +296,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
switch (mmc_resp_type(cmd)) {
case MMC_RSP_NONE: c |= RESP_NONE; break;
case MMC_RSP_R1:
- case MMC_RSP_R1_NO_CRC:
c |= RESP_R1; break;
case MMC_RSP_R1B: c |= RESP_R1B; break;
case MMC_RSP_R2: c |= RESP_R2; break;
@@ -351,6 +349,39 @@ static void tmio_mmc_transfer_data(struct tmio_mmc_host *host,
/*
* Transfer the data
*/
+#ifdef CONFIG_64BIT
+ if (host->pdata->flags & TMIO_MMC_64BIT_DATA_PORT) {
+ u64 *buf64 = (u64 *)buf;
+ u64 data = 0;
+
+ if (count >= 8) {
+ if (is_read)
+ sd_ctrl_read64_rep(host, CTL_SD_DATA_PORT,
+ buf64, count >> 3);
+ else
+ sd_ctrl_write64_rep(host, CTL_SD_DATA_PORT,
+ buf64, count >> 3);
+ }
+
+ /* if count was multiple of 8 */
+ if (!(count & 0x7))
+ return;
+
+ buf64 += count >> 3;
+ count %= 8;
+
+ if (is_read) {
+ sd_ctrl_read64_rep(host, CTL_SD_DATA_PORT, &data, 1);
+ memcpy(buf64, &data, count);
+ } else {
+ memcpy(&data, buf64, count);
+ sd_ctrl_write64_rep(host, CTL_SD_DATA_PORT, &data, 1);
+ }
+
+ return;
+ }
+#endif
+
if (host->pdata->flags & TMIO_MMC_32BIT_DATA_PORT) {
u32 data = 0;
u32 *buf32 = (u32 *)buf;
@@ -697,8 +728,11 @@ static bool __tmio_mmc_sdio_irq(struct tmio_mmc_host *host)
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
- if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ)
+ if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) {
+ if (host->sdio_irq)
+ host->sdio_irq(host);
mmc_signal_sdio_irq(mmc);
+ }
return ireg;
}
@@ -1098,7 +1132,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
if (IS_ERR(ctl))
return ERR_CAST(ctl);
- mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc)
return ERR_PTR(-ENOMEM);
@@ -1111,29 +1145,17 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
mmc->ops = &host->ops;
ret = mmc_of_parse(host->mmc);
- if (ret) {
- host = ERR_PTR(ret);
- goto free;
- }
+ if (ret)
+ return ERR_PTR(ret);
tmio_mmc_of_parse(pdev, mmc);
platform_set_drvdata(pdev, host);
return host;
-free:
- mmc_free_host(mmc);
-
- return host;
}
EXPORT_SYMBOL_GPL(tmio_mmc_host_alloc);
-void tmio_mmc_host_free(struct tmio_mmc_host *host)
-{
- mmc_free_host(host->mmc);
-}
-EXPORT_SYMBOL_GPL(tmio_mmc_host_free);
-
int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
{
struct platform_device *pdev = _host->pdev;
@@ -1177,14 +1199,14 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
dma_max_mapping_size(&pdev->dev));
mmc->max_seg_size = mmc->max_req_size;
- if (mmc_can_gpio_ro(mmc))
+ if (mmc_host_can_gpio_ro(mmc))
_host->ops.get_ro = mmc_gpio_get_ro;
- if (mmc_can_gpio_cd(mmc))
+ if (mmc_host_can_gpio_cd(mmc))
_host->ops.get_cd = mmc_gpio_get_cd;
/* must be set before tmio_mmc_reset() */
- _host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
+ _host->native_hotplug = !(mmc_host_can_gpio_cd(mmc) ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
!mmc_card_is_removable(mmc));
diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c
index 497791ffada6..aa5d2511a62b 100644
--- a/drivers/mmc/host/toshsd.c
+++ b/drivers/mmc/host/toshsd.c
@@ -567,7 +567,6 @@ static void toshsd_powerdown(struct toshsd_host *host)
pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0);
}
-#ifdef CONFIG_PM_SLEEP
static int toshsd_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -599,7 +598,6 @@ static int toshsd_pm_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -612,7 +610,7 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
return ret;
- mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host));
if (!mmc) {
ret = -ENOMEM;
goto err;
@@ -669,7 +667,6 @@ unmap:
release:
pci_release_regions(pdev);
free:
- mmc_free_host(mmc);
pci_set_drvdata(pdev, NULL);
err:
pci_disable_device(pdev);
@@ -685,21 +682,18 @@ static void toshsd_remove(struct pci_dev *pdev)
free_irq(pdev->irq, host);
pci_iounmap(pdev, host->ioaddr);
pci_release_regions(pdev);
- mmc_free_host(host->mmc);
pci_set_drvdata(pdev, NULL);
pci_disable_device(pdev);
}
-static const struct dev_pm_ops toshsd_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(toshsd_pm_ops, toshsd_pm_suspend, toshsd_pm_resume);
static struct pci_driver toshsd_driver = {
.name = DRIVER_NAME,
.id_table = pci_ids,
.probe = toshsd_probe,
.remove = toshsd_remove,
- .driver.pm = &toshsd_pm_ops,
+ .driver.pm = pm_sleep_ptr(&toshsd_pm_ops),
};
module_pci_driver(toshsd_driver);
diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c
index 4ad02cfdc238..1eae2f4b6c1f 100644
--- a/drivers/mmc/host/uniphier-sd.c
+++ b/drivers/mmc/host/uniphier-sd.c
@@ -663,8 +663,7 @@ static int uniphier_sd_probe(struct platform_device *pdev)
priv->rst_hw = devm_reset_control_get_exclusive(dev, "hw");
if (IS_ERR(priv->rst_hw)) {
dev_err(dev, "failed to get hw reset\n");
- ret = PTR_ERR(priv->rst_hw);
- goto free_host;
+ return PTR_ERR(priv->rst_hw);
}
host->ops.card_hw_reset = uniphier_sd_hw_reset;
}
@@ -694,7 +693,7 @@ static int uniphier_sd_probe(struct platform_device *pdev)
ret = uniphier_sd_clk_enable(host);
if (ret)
- goto free_host;
+ return ret;
uniphier_sd_host_init(host);
@@ -720,8 +719,6 @@ static int uniphier_sd_probe(struct platform_device *pdev)
disable_clk:
uniphier_sd_clk_disable(host);
-free_host:
- tmio_mmc_host_free(host);
return ret;
}
@@ -732,7 +729,6 @@ static void uniphier_sd_remove(struct platform_device *pdev)
tmio_mmc_host_remove(host);
uniphier_sd_clk_disable(host);
- tmio_mmc_host_free(host);
}
static const struct of_device_id uniphier_sd_match[] = {
diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c
index 49efb960a052..3bccf800339b 100644
--- a/drivers/mmc/host/usdhi6rol0.c
+++ b/drivers/mmc/host/usdhi6rol0.c
@@ -323,7 +323,7 @@ static void usdhi6_blk_bounce(struct usdhi6_host *host,
host->head_pg.page = host->pg.page;
host->head_pg.mapped = host->pg.mapped;
- host->pg.page = nth_page(host->pg.page, 1);
+ host->pg.page = host->pg.page + 1;
host->pg.mapped = kmap(host->pg.page);
host->blk_page = host->bounce_buf;
@@ -503,7 +503,7 @@ static void usdhi6_sg_advance(struct usdhi6_host *host)
/* We cannot get here after crossing a page border */
/* Next page in the same SG */
- host->pg.page = nth_page(sg_page(host->sg), host->page_idx);
+ host->pg.page = sg_page(host->sg) + host->page_idx;
host->pg.mapped = kmap(host->pg.page);
host->blk_page = host->pg.mapped;
@@ -1762,17 +1762,17 @@ static int usdhi6_probe(struct platform_device *pdev)
if (irq_sdio < 0)
return irq_sdio;
- mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
ret = mmc_regulator_get_supply(mmc);
if (ret)
- goto e_free_mmc;
+ return ret;
ret = mmc_of_parse(mmc);
if (ret < 0)
- goto e_free_mmc;
+ return ret;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -1785,30 +1785,24 @@ static int usdhi6_probe(struct platform_device *pdev)
mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS;
host->pinctrl = devm_pinctrl_get(&pdev->dev);
- if (IS_ERR(host->pinctrl)) {
- ret = PTR_ERR(host->pinctrl);
- goto e_free_mmc;
- }
+ if (IS_ERR(host->pinctrl))
+ return PTR_ERR(host->pinctrl);
host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs");
host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto e_free_mmc;
- }
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
host->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- goto e_free_mmc;
- }
+ if (IS_ERR(host->clk))
+ return PTR_ERR(host->clk);
host->imclk = clk_get_rate(host->clk);
ret = clk_prepare_enable(host->clk);
if (ret < 0)
- goto e_free_mmc;
+ return ret;
version = usdhi6_read(host, USDHI6_VERSION);
if ((version & 0xfff) != 0xa0d) {
@@ -1878,9 +1872,6 @@ e_release_dma:
usdhi6_dma_release(host);
e_clk_off:
clk_disable_unprepare(host->clk);
-e_free_mmc:
- mmc_free_host(mmc);
-
return ret;
}
@@ -1894,7 +1885,6 @@ static void usdhi6_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&host->timeout_work);
usdhi6_dma_release(host);
clk_disable_unprepare(host->clk);
- mmc_free_host(host->mmc);
}
static struct platform_driver usdhi6_driver = {
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index 9a6358fd9512..2b7456e942f7 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -404,8 +404,6 @@ static void ushc_clean_up(struct ushc_data *ushc)
kfree(ushc->int_data);
kfree(ushc->cbw);
kfree(ushc->csw);
-
- mmc_free_host(ushc->mmc);
}
static const struct mmc_host_ops ushc_ops = {
@@ -425,7 +423,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id
if (intf->cur_altsetting->desc.bNumEndpoints < 1)
return -ENODEV;
- mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev);
+ mmc = devm_mmc_alloc_host(&intf->dev, sizeof(*ushc));
if (mmc == NULL)
return -ENOMEM;
ushc = mmc_priv(mmc);
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index f77457105ec3..c628b3bbfd7a 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -937,7 +937,7 @@ static void via_sdc_timeout(struct timer_list *t)
struct via_crdr_mmc_host *sdhost;
unsigned long flags;
- sdhost = from_timer(sdhost, t, timer);
+ sdhost = timer_container_of(sdhost, t, timer);
spin_lock_irqsave(&sdhost->lock, flags);
@@ -971,7 +971,7 @@ static void via_sdc_finish_bh_work(struct work_struct *t)
spin_lock_irqsave(&host->lock, flags);
- del_timer(&host->timer);
+ timer_delete(&host->timer);
mrq = host->mrq;
host->mrq = NULL;
host->cmd = NULL;
@@ -1100,7 +1100,7 @@ static int via_sd_probe(struct pci_dev *pcidev,
pci_write_config_byte(pcidev, VIA_CRDR_PCI_WORK_MODE, 0);
pci_write_config_byte(pcidev, VIA_CRDR_PCI_DBG_MODE, 0);
- mmc = mmc_alloc_host(sizeof(struct via_crdr_mmc_host), &pcidev->dev);
+ mmc = devm_mmc_alloc_host(&pcidev->dev, sizeof(*sdhost));
if (!mmc) {
ret = -ENOMEM;
goto release;
@@ -1115,7 +1115,7 @@ static int via_sd_probe(struct pci_dev *pcidev,
sdhost->mmiobase = ioremap(base, len);
if (!sdhost->mmiobase) {
ret = -ENOMEM;
- goto free_mmc_host;
+ goto release;
}
sdhost->sdhc_mmiobase =
@@ -1160,8 +1160,6 @@ static int via_sd_probe(struct pci_dev *pcidev,
unmap:
iounmap(sdhost->mmiobase);
-free_mmc_host:
- mmc_free_host(mmc);
release:
pci_release_regions(pcidev);
disable:
@@ -1202,7 +1200,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
free_irq(pcidev->irq, sdhost);
- del_timer_sync(&sdhost->timer);
+ timer_delete_sync(&sdhost->timer);
cancel_work_sync(&sdhost->finish_bh_work);
@@ -1212,7 +1210,6 @@ static void via_sd_remove(struct pci_dev *pcidev)
writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT);
iounmap(sdhost->mmiobase);
- mmc_free_host(sdhost->mmc);
pci_release_regions(pcidev);
pci_disable_device(pcidev);
@@ -1221,7 +1218,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device);
}
-static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host)
+static void via_init_sdc_pm(struct via_crdr_mmc_host *host)
{
struct sdhcreg *pm_sdhcreg;
void __iomem *addrbase;
@@ -1255,7 +1252,7 @@ static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host)
via_print_sdchc(host);
}
-static int __maybe_unused via_sd_suspend(struct device *dev)
+static int via_sd_suspend(struct device *dev)
{
struct via_crdr_mmc_host *host;
unsigned long flags;
@@ -1272,7 +1269,7 @@ static int __maybe_unused via_sd_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused via_sd_resume(struct device *dev)
+static int via_sd_resume(struct device *dev)
{
struct via_crdr_mmc_host *sdhost;
u8 gatt;
@@ -1298,14 +1295,14 @@ static int __maybe_unused via_sd_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume);
static struct pci_driver via_sd_driver = {
.name = DRV_NAME,
.id_table = via_ids,
.probe = via_sd_probe,
.remove = via_sd_remove,
- .driver.pm = &via_sd_pm_ops,
+ .driver.pm = pm_sleep_ptr(&via_sd_pm_ops),
};
module_pci_driver(via_sd_driver);
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index fd67c0682b38..ff49d0770506 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -369,13 +369,11 @@ struct vub300_mmc_host {
static void vub300_delete(struct kref *kref)
{ /* kref callback - softirq */
struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref);
- struct mmc_host *mmc = vub300->mmc;
usb_free_urb(vub300->command_out_urb);
vub300->command_out_urb = NULL;
usb_free_urb(vub300->command_res_urb);
vub300->command_res_urb = NULL;
usb_put_dev(vub300->udev);
- mmc_free_host(mmc);
/*
* and hence also frees vub300
* which is contained at the end of struct mmc
@@ -740,8 +738,8 @@ static void vub300_deadwork_thread(struct work_struct *work)
static void vub300_inactivity_timer_expired(struct timer_list *t)
{ /* softirq */
- struct vub300_mmc_host *vub300 = from_timer(vub300, t,
- inactivity_timer);
+ struct vub300_mmc_host *vub300 = timer_container_of(vub300, t,
+ inactivity_timer);
if (!vub300->interface) {
kref_put(&vub300->kref, vub300_delete);
} else if (vub300->cmd) {
@@ -1180,8 +1178,8 @@ static void send_command(struct vub300_mmc_host *vub300)
*/
static void vub300_sg_timed_out(struct timer_list *t)
{
- struct vub300_mmc_host *vub300 = from_timer(vub300, t,
- sg_transfer_timer);
+ struct vub300_mmc_host *vub300 = timer_container_of(vub300, t,
+ sg_transfer_timer);
vub300->usb_timed_out = 1;
usb_sg_cancel(&vub300->sg_request);
usb_unlink_urb(vub300->command_out_urb);
@@ -1452,7 +1450,7 @@ static int __command_read_data(struct vub300_mmc_host *vub300,
(linear_length / 16384));
add_timer(&vub300->sg_transfer_timer);
usb_sg_wait(&vub300->sg_request);
- del_timer(&vub300->sg_transfer_timer);
+ timer_delete(&vub300->sg_transfer_timer);
if (vub300->sg_request.status < 0) {
cmd->error = vub300->sg_request.status;
data->bytes_xfered = 0;
@@ -1572,7 +1570,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300,
if (cmd->error) {
data->bytes_xfered = 0;
} else {
- del_timer(&vub300->sg_transfer_timer);
+ timer_delete(&vub300->sg_transfer_timer);
if (vub300->sg_request.status < 0) {
cmd->error = vub300->sg_request.status;
data->bytes_xfered = 0;
@@ -2114,7 +2112,7 @@ static int vub300_probe(struct usb_interface *interface,
goto error1;
}
/* this also allocates memory for our VUB300 mmc host device */
- mmc = mmc_alloc_host(sizeof(struct vub300_mmc_host), &udev->dev);
+ mmc = devm_mmc_alloc_host(&udev->dev, sizeof(*vub300));
if (!mmc) {
retval = -ENOMEM;
dev_err(&udev->dev, "not enough memory for the mmc_host\n");
@@ -2271,7 +2269,7 @@ static int vub300_probe(struct usb_interface *interface,
dev_err(&vub300->udev->dev,
"Could not find two sets of bulk-in/out endpoint pairs\n");
retval = -EINVAL;
- goto error5;
+ goto error4;
}
retval =
usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
@@ -2280,14 +2278,14 @@ static int vub300_probe(struct usb_interface *interface,
0x0000, 0x0000, &vub300->hc_info,
sizeof(vub300->hc_info), 1000);
if (retval < 0)
- goto error5;
+ goto error4;
retval =
usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0),
SET_ROM_WAIT_STATES,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
firmware_rom_wait_states, 0x0000, NULL, 0, 1000);
if (retval < 0)
- goto error5;
+ goto error4;
dev_info(&vub300->udev->dev,
"operating_mode = %s %s %d MHz %s %d byte USB packets\n",
(mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL",
@@ -2302,7 +2300,7 @@ static int vub300_probe(struct usb_interface *interface,
0x0000, 0x0000, &vub300->system_port_status,
sizeof(vub300->system_port_status), 1000);
if (retval < 0) {
- goto error5;
+ goto error4;
} else if (sizeof(vub300->system_port_status) == retval) {
vub300->card_present =
(0x0001 & vub300->system_port_status.port_flags) ? 1 : 0;
@@ -2310,7 +2308,7 @@ static int vub300_probe(struct usb_interface *interface,
(0x0010 & vub300->system_port_status.port_flags) ? 1 : 0;
} else {
retval = -EINVAL;
- goto error5;
+ goto error4;
}
usb_set_intfdata(interface, vub300);
INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread);
@@ -2339,9 +2337,7 @@ static int vub300_probe(struct usb_interface *interface,
return 0;
error6:
- del_timer_sync(&vub300->inactivity_timer);
-error5:
- mmc_free_host(mmc);
+ timer_delete_sync(&vub300->inactivity_timer);
/*
* and hence also frees vub300
* which is contained at the end of struct mmc
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 8b268e8a0ec9..c33a0223ce7f 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -947,7 +947,7 @@ static const struct mmc_host_ops wbsd_ops = {
static void wbsd_reset_ignore(struct timer_list *t)
{
- struct wbsd_host *host = from_timer(host, t, ignore_timer);
+ struct wbsd_host *host = timer_container_of(host, t, ignore_timer);
BUG_ON(host == NULL);
@@ -1190,7 +1190,7 @@ static int wbsd_alloc_mmc(struct device *dev)
/*
* Allocate MMC structure.
*/
- mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
+ mmc = devm_mmc_alloc_host(dev, sizeof(*host));
if (!mmc)
return -ENOMEM;
@@ -1261,9 +1261,7 @@ static void wbsd_free_mmc(struct device *dev)
host = mmc_priv(mmc);
BUG_ON(host == NULL);
- del_timer_sync(&host->ignore_timer);
-
- mmc_free_host(mmc);
+ timer_delete_sync(&host->ignore_timer);
}
/*
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index cdb36a9f9e38..1b1d691e19fc 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -774,7 +774,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
goto fail1;
}
- mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*priv));
if (!mmc) {
dev_err(&pdev->dev, "Failed to allocate mmc_host\n");
ret = -ENOMEM;
@@ -808,7 +808,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
if (!priv->sdmmc_base) {
dev_err(&pdev->dev, "Failed to map IO space\n");
ret = -ENOMEM;
- goto fail2;
+ goto fail1;
}
priv->irq_regular = regular_irq;
@@ -873,8 +873,6 @@ fail4:
free_irq(regular_irq, priv);
fail3:
iounmap(priv->sdmmc_base);
-fail2:
- mmc_free_host(mmc);
fail1:
return ret;
}
@@ -910,12 +908,9 @@ static void wmt_mci_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk_sdmmc);
clk_put(priv->clk_sdmmc);
- mmc_free_host(mmc);
-
dev_info(&pdev->dev, "WMT MCI device removed\n");
}
-#ifdef CONFIG_PM
static int wmt_mci_suspend(struct device *dev)
{
u32 reg_tmp;
@@ -967,18 +962,7 @@ static int wmt_mci_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops wmt_mci_pm = {
- .suspend = wmt_mci_suspend,
- .resume = wmt_mci_resume,
-};
-
-#define wmt_mci_pm_ops (&wmt_mci_pm)
-
-#else /* !CONFIG_PM */
-
-#define wmt_mci_pm_ops NULL
-
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(wmt_mci_pm_ops, wmt_mci_suspend, wmt_mci_resume);
static struct platform_driver wmt_mci_driver = {
.probe = wmt_mci_probe,
@@ -986,7 +970,7 @@ static struct platform_driver wmt_mci_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = wmt_mci_pm_ops,
+ .pm = pm_sleep_ptr(&wmt_mci_pm_ops),
.of_match_table = wmt_mci_dt_ids,
},
};