Merge tag 'mmc-v4.10-2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Dec 2016 18:55:56 +0000 (10:55 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 14 Dec 2016 18:55:56 +0000 (10:55 -0800)
Pull another MMC update from Ulf Hansson:
 "Here's a second pull request for MMC for v4.10.

  As a matter of fact it's only one change that moves some mmc files
  around. I thought it was a good idea to get this into v4.10, as it
  gives us a nice and fresh base for v4.11. Summary:

  MMC core:

   - Move files from the card directory to the core directory to enable
     future clean-ups of the generic mmc header files and interfaces"

* tag 'mmc-v4.10-2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc:
  mmc: block: Move files to core

1  2 
drivers/mmc/core/block.c
drivers/mmc/core/queue.c

index 0000000000000000000000000000000000000000,646d1a1fa6ca8d47e2984824f602433385da91a1..bab3f07b1117ea819e74758a50b15b54155087f4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2336 +1,2336 @@@
 -              req->cmd_flags |= REQ_QUIET;
+ /*
+  * Block driver for media (i.e., flash cards)
+  *
+  * Copyright 2002 Hewlett-Packard Company
+  * Copyright 2005-2008 Pierre Ossman
+  *
+  * Use consistent with the GNU GPL is permitted,
+  * provided that this copyright notice is
+  * preserved in its entirety in all copies and derived works.
+  *
+  * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+  * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+  * FITNESS FOR ANY PARTICULAR PURPOSE.
+  *
+  * Many thanks to Alessandro Rubini and Jonathan Corbet!
+  *
+  * Author:  Andrew Christian
+  *          28 May 2002
+  */
+ #include <linux/moduleparam.h>
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/kernel.h>
+ #include <linux/fs.h>
+ #include <linux/slab.h>
+ #include <linux/errno.h>
+ #include <linux/hdreg.h>
+ #include <linux/kdev_t.h>
+ #include <linux/blkdev.h>
+ #include <linux/mutex.h>
+ #include <linux/scatterlist.h>
+ #include <linux/string_helpers.h>
+ #include <linux/delay.h>
+ #include <linux/capability.h>
+ #include <linux/compat.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/idr.h>
+ #include <linux/mmc/ioctl.h>
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+ #include <linux/mmc/mmc.h>
+ #include <linux/mmc/sd.h>
+ #include <asm/uaccess.h>
+ #include "queue.h"
+ #include "block.h"
+ MODULE_ALIAS("mmc:block");
+ #ifdef MODULE_PARAM_PREFIX
+ #undef MODULE_PARAM_PREFIX
+ #endif
+ #define MODULE_PARAM_PREFIX "mmcblk."
+ #define INAND_CMD38_ARG_EXT_CSD  113
+ #define INAND_CMD38_ARG_ERASE    0x00
+ #define INAND_CMD38_ARG_TRIM     0x01
+ #define INAND_CMD38_ARG_SECERASE 0x80
+ #define INAND_CMD38_ARG_SECTRIM1 0x81
+ #define INAND_CMD38_ARG_SECTRIM2 0x88
+ #define MMC_BLK_TIMEOUT_MS  (10 * 60 * 1000)        /* 10 minute timeout */
+ #define MMC_SANITIZE_REQ_TIMEOUT 240000
+ #define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
+ #define mmc_req_rel_wr(req)   ((req->cmd_flags & REQ_FUA) && \
+                                 (rq_data_dir(req) == WRITE))
+ static DEFINE_MUTEX(block_mutex);
+ /*
+  * The defaults come from config options but can be overriden by module
+  * or bootarg options.
+  */
+ static int perdev_minors = CONFIG_MMC_BLOCK_MINORS;
+ /*
+  * We've only got one major, so number of mmcblk devices is
+  * limited to (1 << 20) / number of minors per device.  It is also
+  * limited by the MAX_DEVICES below.
+  */
+ static int max_devices;
+ #define MAX_DEVICES 256
+ static DEFINE_IDA(mmc_blk_ida);
+ static DEFINE_SPINLOCK(mmc_blk_lock);
+ /*
+  * There is one mmc_blk_data per slot.
+  */
+ struct mmc_blk_data {
+       spinlock_t      lock;
+       struct device   *parent;
+       struct gendisk  *disk;
+       struct mmc_queue queue;
+       struct list_head part;
+       unsigned int    flags;
+ #define MMC_BLK_CMD23 (1 << 0)        /* Can do SET_BLOCK_COUNT for multiblock */
+ #define MMC_BLK_REL_WR        (1 << 1)        /* MMC Reliable write support */
+       unsigned int    usage;
+       unsigned int    read_only;
+       unsigned int    part_type;
+       unsigned int    reset_done;
+ #define MMC_BLK_READ          BIT(0)
+ #define MMC_BLK_WRITE         BIT(1)
+ #define MMC_BLK_DISCARD               BIT(2)
+ #define MMC_BLK_SECDISCARD    BIT(3)
+       /*
+        * Only set in main mmc_blk_data associated
+        * with mmc_card with dev_set_drvdata, and keeps
+        * track of the current selected device partition.
+        */
+       unsigned int    part_curr;
+       struct device_attribute force_ro;
+       struct device_attribute power_ro_lock;
+       int     area_type;
+ };
+ static DEFINE_MUTEX(open_lock);
+ module_param(perdev_minors, int, 0444);
+ MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
+ static inline int mmc_blk_part_switch(struct mmc_card *card,
+                                     struct mmc_blk_data *md);
+ static int get_card_status(struct mmc_card *card, u32 *status, int retries);
+ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
+ {
+       struct mmc_blk_data *md;
+       mutex_lock(&open_lock);
+       md = disk->private_data;
+       if (md && md->usage == 0)
+               md = NULL;
+       if (md)
+               md->usage++;
+       mutex_unlock(&open_lock);
+       return md;
+ }
+ static inline int mmc_get_devidx(struct gendisk *disk)
+ {
+       int devidx = disk->first_minor / perdev_minors;
+       return devidx;
+ }
+ static void mmc_blk_put(struct mmc_blk_data *md)
+ {
+       mutex_lock(&open_lock);
+       md->usage--;
+       if (md->usage == 0) {
+               int devidx = mmc_get_devidx(md->disk);
+               blk_cleanup_queue(md->queue.queue);
+               spin_lock(&mmc_blk_lock);
+               ida_remove(&mmc_blk_ida, devidx);
+               spin_unlock(&mmc_blk_lock);
+               put_disk(md->disk);
+               kfree(md);
+       }
+       mutex_unlock(&open_lock);
+ }
+ static ssize_t power_ro_lock_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+ {
+       int ret;
+       struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+       struct mmc_card *card = md->queue.card;
+       int locked = 0;
+       if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
+               locked = 2;
+       else if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
+               locked = 1;
+       ret = snprintf(buf, PAGE_SIZE, "%d\n", locked);
+       mmc_blk_put(md);
+       return ret;
+ }
+ static ssize_t power_ro_lock_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+ {
+       int ret;
+       struct mmc_blk_data *md, *part_md;
+       struct mmc_card *card;
+       unsigned long set;
+       if (kstrtoul(buf, 0, &set))
+               return -EINVAL;
+       if (set != 1)
+               return count;
+       md = mmc_blk_get(dev_to_disk(dev));
+       card = md->queue.card;
+       mmc_get_card(card);
+       ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
+                               card->ext_csd.boot_ro_lock |
+                               EXT_CSD_BOOT_WP_B_PWR_WP_EN,
+                               card->ext_csd.part_time);
+       if (ret)
+               pr_err("%s: Locking boot partition ro until next power on failed: %d\n", md->disk->disk_name, ret);
+       else
+               card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN;
+       mmc_put_card(card);
+       if (!ret) {
+               pr_info("%s: Locking boot partition ro until next power on\n",
+                       md->disk->disk_name);
+               set_disk_ro(md->disk, 1);
+               list_for_each_entry(part_md, &md->part, part)
+                       if (part_md->area_type == MMC_BLK_DATA_AREA_BOOT) {
+                               pr_info("%s: Locking boot partition ro until next power on\n", part_md->disk->disk_name);
+                               set_disk_ro(part_md->disk, 1);
+                       }
+       }
+       mmc_blk_put(md);
+       return count;
+ }
+ static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+ {
+       int ret;
+       struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+       ret = snprintf(buf, PAGE_SIZE, "%d\n",
+                      get_disk_ro(dev_to_disk(dev)) ^
+                      md->read_only);
+       mmc_blk_put(md);
+       return ret;
+ }
+ static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
+ {
+       int ret;
+       char *end;
+       struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+       unsigned long set = simple_strtoul(buf, &end, 0);
+       if (end == buf) {
+               ret = -EINVAL;
+               goto out;
+       }
+       set_disk_ro(dev_to_disk(dev), set || md->read_only);
+       ret = count;
+ out:
+       mmc_blk_put(md);
+       return ret;
+ }
+ static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
+ {
+       struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
+       int ret = -ENXIO;
+       mutex_lock(&block_mutex);
+       if (md) {
+               if (md->usage == 2)
+                       check_disk_change(bdev);
+               ret = 0;
+               if ((mode & FMODE_WRITE) && md->read_only) {
+                       mmc_blk_put(md);
+                       ret = -EROFS;
+               }
+       }
+       mutex_unlock(&block_mutex);
+       return ret;
+ }
+ static void mmc_blk_release(struct gendisk *disk, fmode_t mode)
+ {
+       struct mmc_blk_data *md = disk->private_data;
+       mutex_lock(&block_mutex);
+       mmc_blk_put(md);
+       mutex_unlock(&block_mutex);
+ }
+ static int
+ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+ {
+       geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16);
+       geo->heads = 4;
+       geo->sectors = 16;
+       return 0;
+ }
+ struct mmc_blk_ioc_data {
+       struct mmc_ioc_cmd ic;
+       unsigned char *buf;
+       u64 buf_bytes;
+ };
+ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
+       struct mmc_ioc_cmd __user *user)
+ {
+       struct mmc_blk_ioc_data *idata;
+       int err;
+       idata = kmalloc(sizeof(*idata), GFP_KERNEL);
+       if (!idata) {
+               err = -ENOMEM;
+               goto out;
+       }
+       if (copy_from_user(&idata->ic, user, sizeof(idata->ic))) {
+               err = -EFAULT;
+               goto idata_err;
+       }
+       idata->buf_bytes = (u64) idata->ic.blksz * idata->ic.blocks;
+       if (idata->buf_bytes > MMC_IOC_MAX_BYTES) {
+               err = -EOVERFLOW;
+               goto idata_err;
+       }
+       if (!idata->buf_bytes) {
+               idata->buf = NULL;
+               return idata;
+       }
+       idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
+       if (!idata->buf) {
+               err = -ENOMEM;
+               goto idata_err;
+       }
+       if (copy_from_user(idata->buf, (void __user *)(unsigned long)
+                                       idata->ic.data_ptr, idata->buf_bytes)) {
+               err = -EFAULT;
+               goto copy_err;
+       }
+       return idata;
+ copy_err:
+       kfree(idata->buf);
+ idata_err:
+       kfree(idata);
+ out:
+       return ERR_PTR(err);
+ }
+ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr,
+                                     struct mmc_blk_ioc_data *idata)
+ {
+       struct mmc_ioc_cmd *ic = &idata->ic;
+       if (copy_to_user(&(ic_ptr->response), ic->response,
+                        sizeof(ic->response)))
+               return -EFAULT;
+       if (!idata->ic.write_flag) {
+               if (copy_to_user((void __user *)(unsigned long)ic->data_ptr,
+                                idata->buf, idata->buf_bytes))
+                       return -EFAULT;
+       }
+       return 0;
+ }
+ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
+                                      u32 retries_max)
+ {
+       int err;
+       u32 retry_count = 0;
+       if (!status || !retries_max)
+               return -EINVAL;
+       do {
+               err = get_card_status(card, status, 5);
+               if (err)
+                       break;
+               if (!R1_STATUS(*status) &&
+                               (R1_CURRENT_STATE(*status) != R1_STATE_PRG))
+                       break; /* RPMB programming operation complete */
+               /*
+                * Rechedule to give the MMC device a chance to continue
+                * processing the previous command without being polled too
+                * frequently.
+                */
+               usleep_range(1000, 5000);
+       } while (++retry_count < retries_max);
+       if (retry_count == retries_max)
+               err = -EPERM;
+       return err;
+ }
+ static int ioctl_do_sanitize(struct mmc_card *card)
+ {
+       int err;
+       if (!mmc_can_sanitize(card)) {
+                       pr_warn("%s: %s - SANITIZE is not supported\n",
+                               mmc_hostname(card->host), __func__);
+                       err = -EOPNOTSUPP;
+                       goto out;
+       }
+       pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
+               mmc_hostname(card->host), __func__);
+       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                       EXT_CSD_SANITIZE_START, 1,
+                                       MMC_SANITIZE_REQ_TIMEOUT);
+       if (err)
+               pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n",
+                      mmc_hostname(card->host), __func__, err);
+       pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host),
+                                            __func__);
+ out:
+       return err;
+ }
+ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
+                              struct mmc_blk_ioc_data *idata)
+ {
+       struct mmc_command cmd = {0};
+       struct mmc_data data = {0};
+       struct mmc_request mrq = {NULL};
+       struct scatterlist sg;
+       int err;
+       int is_rpmb = false;
+       u32 status = 0;
+       if (!card || !md || !idata)
+               return -EINVAL;
+       if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
+               is_rpmb = true;
+       cmd.opcode = idata->ic.opcode;
+       cmd.arg = idata->ic.arg;
+       cmd.flags = idata->ic.flags;
+       if (idata->buf_bytes) {
+               data.sg = &sg;
+               data.sg_len = 1;
+               data.blksz = idata->ic.blksz;
+               data.blocks = idata->ic.blocks;
+               sg_init_one(data.sg, idata->buf, idata->buf_bytes);
+               if (idata->ic.write_flag)
+                       data.flags = MMC_DATA_WRITE;
+               else
+                       data.flags = MMC_DATA_READ;
+               /* data.flags must already be set before doing this. */
+               mmc_set_data_timeout(&data, card);
+               /* Allow overriding the timeout_ns for empirical tuning. */
+               if (idata->ic.data_timeout_ns)
+                       data.timeout_ns = idata->ic.data_timeout_ns;
+               if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
+                       /*
+                        * Pretend this is a data transfer and rely on the
+                        * host driver to compute timeout.  When all host
+                        * drivers support cmd.cmd_timeout for R1B, this
+                        * can be changed to:
+                        *
+                        *     mrq.data = NULL;
+                        *     cmd.cmd_timeout = idata->ic.cmd_timeout_ms;
+                        */
+                       data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000;
+               }
+               mrq.data = &data;
+       }
+       mrq.cmd = &cmd;
+       err = mmc_blk_part_switch(card, md);
+       if (err)
+               return err;
+       if (idata->ic.is_acmd) {
+               err = mmc_app_cmd(card->host, card);
+               if (err)
+                       return err;
+       }
+       if (is_rpmb) {
+               err = mmc_set_blockcount(card, data.blocks,
+                       idata->ic.write_flag & (1 << 31));
+               if (err)
+                       return err;
+       }
+       if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
+           (cmd.opcode == MMC_SWITCH)) {
+               err = ioctl_do_sanitize(card);
+               if (err)
+                       pr_err("%s: ioctl_do_sanitize() failed. err = %d",
+                              __func__, err);
+               return err;
+       }
+       mmc_wait_for_req(card->host, &mrq);
+       if (cmd.error) {
+               dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
+                                               __func__, cmd.error);
+               return cmd.error;
+       }
+       if (data.error) {
+               dev_err(mmc_dev(card->host), "%s: data error %d\n",
+                                               __func__, data.error);
+               return data.error;
+       }
+       /*
+        * According to the SD specs, some commands require a delay after
+        * issuing the command.
+        */
+       if (idata->ic.postsleep_min_us)
+               usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
+       memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
+       if (is_rpmb) {
+               /*
+                * Ensure RPMB command has completed by polling CMD13
+                * "Send Status".
+                */
+               err = ioctl_rpmb_card_status_poll(card, &status, 5);
+               if (err)
+                       dev_err(mmc_dev(card->host),
+                                       "%s: Card Status=0x%08X, error %d\n",
+                                       __func__, status, err);
+       }
+       return err;
+ }
+ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
+                            struct mmc_ioc_cmd __user *ic_ptr)
+ {
+       struct mmc_blk_ioc_data *idata;
+       struct mmc_blk_data *md;
+       struct mmc_card *card;
+       int err = 0, ioc_err = 0;
+       /*
+        * The caller must have CAP_SYS_RAWIO, and must be calling this on the
+        * whole block device, not on a partition.  This prevents overspray
+        * between sibling partitions.
+        */
+       if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
+               return -EPERM;
+       idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
+       if (IS_ERR(idata))
+               return PTR_ERR(idata);
+       md = mmc_blk_get(bdev->bd_disk);
+       if (!md) {
+               err = -EINVAL;
+               goto cmd_err;
+       }
+       card = md->queue.card;
+       if (IS_ERR(card)) {
+               err = PTR_ERR(card);
+               goto cmd_done;
+       }
+       mmc_get_card(card);
+       ioc_err = __mmc_blk_ioctl_cmd(card, md, idata);
+       /* Always switch back to main area after RPMB access */
+       if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
+               mmc_blk_part_switch(card, dev_get_drvdata(&card->dev));
+       mmc_put_card(card);
+       err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
+ cmd_done:
+       mmc_blk_put(md);
+ cmd_err:
+       kfree(idata->buf);
+       kfree(idata);
+       return ioc_err ? ioc_err : err;
+ }
+ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
+                                  struct mmc_ioc_multi_cmd __user *user)
+ {
+       struct mmc_blk_ioc_data **idata = NULL;
+       struct mmc_ioc_cmd __user *cmds = user->cmds;
+       struct mmc_card *card;
+       struct mmc_blk_data *md;
+       int i, err = 0, ioc_err = 0;
+       __u64 num_of_cmds;
+       /*
+        * The caller must have CAP_SYS_RAWIO, and must be calling this on the
+        * whole block device, not on a partition.  This prevents overspray
+        * between sibling partitions.
+        */
+       if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
+               return -EPERM;
+       if (copy_from_user(&num_of_cmds, &user->num_of_cmds,
+                          sizeof(num_of_cmds)))
+               return -EFAULT;
+       if (num_of_cmds > MMC_IOC_MAX_CMDS)
+               return -EINVAL;
+       idata = kcalloc(num_of_cmds, sizeof(*idata), GFP_KERNEL);
+       if (!idata)
+               return -ENOMEM;
+       for (i = 0; i < num_of_cmds; i++) {
+               idata[i] = mmc_blk_ioctl_copy_from_user(&cmds[i]);
+               if (IS_ERR(idata[i])) {
+                       err = PTR_ERR(idata[i]);
+                       num_of_cmds = i;
+                       goto cmd_err;
+               }
+       }
+       md = mmc_blk_get(bdev->bd_disk);
+       if (!md) {
+               err = -EINVAL;
+               goto cmd_err;
+       }
+       card = md->queue.card;
+       if (IS_ERR(card)) {
+               err = PTR_ERR(card);
+               goto cmd_done;
+       }
+       mmc_get_card(card);
+       for (i = 0; i < num_of_cmds && !ioc_err; i++)
+               ioc_err = __mmc_blk_ioctl_cmd(card, md, idata[i]);
+       /* Always switch back to main area after RPMB access */
+       if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
+               mmc_blk_part_switch(card, dev_get_drvdata(&card->dev));
+       mmc_put_card(card);
+       /* copy to user if data and response */
+       for (i = 0; i < num_of_cmds && !err; i++)
+               err = mmc_blk_ioctl_copy_to_user(&cmds[i], idata[i]);
+ cmd_done:
+       mmc_blk_put(md);
+ cmd_err:
+       for (i = 0; i < num_of_cmds; i++) {
+               kfree(idata[i]->buf);
+               kfree(idata[i]);
+       }
+       kfree(idata);
+       return ioc_err ? ioc_err : err;
+ }
+ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
+       unsigned int cmd, unsigned long arg)
+ {
+       switch (cmd) {
+       case MMC_IOC_CMD:
+               return mmc_blk_ioctl_cmd(bdev,
+                               (struct mmc_ioc_cmd __user *)arg);
+       case MMC_IOC_MULTI_CMD:
+               return mmc_blk_ioctl_multi_cmd(bdev,
+                               (struct mmc_ioc_multi_cmd __user *)arg);
+       default:
+               return -EINVAL;
+       }
+ }
+ #ifdef CONFIG_COMPAT
+ static int mmc_blk_compat_ioctl(struct block_device *bdev, fmode_t mode,
+       unsigned int cmd, unsigned long arg)
+ {
+       return mmc_blk_ioctl(bdev, mode, cmd, (unsigned long) compat_ptr(arg));
+ }
+ #endif
+ static const struct block_device_operations mmc_bdops = {
+       .open                   = mmc_blk_open,
+       .release                = mmc_blk_release,
+       .getgeo                 = mmc_blk_getgeo,
+       .owner                  = THIS_MODULE,
+       .ioctl                  = mmc_blk_ioctl,
+ #ifdef CONFIG_COMPAT
+       .compat_ioctl           = mmc_blk_compat_ioctl,
+ #endif
+ };
+ static inline int mmc_blk_part_switch(struct mmc_card *card,
+                                     struct mmc_blk_data *md)
+ {
+       int ret;
+       struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
+       if (main_md->part_curr == md->part_type)
+               return 0;
+       if (mmc_card_mmc(card)) {
+               u8 part_config = card->ext_csd.part_config;
+               if (md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
+                       mmc_retune_pause(card->host);
+               part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
+               part_config |= md->part_type;
+               ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                EXT_CSD_PART_CONFIG, part_config,
+                                card->ext_csd.part_time);
+               if (ret) {
+                       if (md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
+                               mmc_retune_unpause(card->host);
+                       return ret;
+               }
+               card->ext_csd.part_config = part_config;
+               if (main_md->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB)
+                       mmc_retune_unpause(card->host);
+       }
+       main_md->part_curr = md->part_type;
+       return 0;
+ }
+ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
+ {
+       int err;
+       u32 result;
+       __be32 *blocks;
+       struct mmc_request mrq = {NULL};
+       struct mmc_command cmd = {0};
+       struct mmc_data data = {0};
+       struct scatterlist sg;
+       cmd.opcode = MMC_APP_CMD;
+       cmd.arg = card->rca << 16;
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, 0);
+       if (err)
+               return (u32)-1;
+       if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
+               return (u32)-1;
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
+       cmd.arg = 0;
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+       data.blksz = 4;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+       mmc_set_data_timeout(&data, card);
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+       blocks = kmalloc(4, GFP_KERNEL);
+       if (!blocks)
+               return (u32)-1;
+       sg_init_one(&sg, blocks, 4);
+       mmc_wait_for_req(card->host, &mrq);
+       result = ntohl(*blocks);
+       kfree(blocks);
+       if (cmd.error || data.error)
+               result = (u32)-1;
+       return result;
+ }
+ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
+ {
+       struct mmc_command cmd = {0};
+       int err;
+       cmd.opcode = MMC_SEND_STATUS;
+       if (!mmc_host_is_spi(card->host))
+               cmd.arg = card->rca << 16;
+       cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, retries);
+       if (err == 0)
+               *status = cmd.resp[0];
+       return err;
+ }
+ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
+               bool hw_busy_detect, struct request *req, bool *gen_err)
+ {
+       unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+       int err = 0;
+       u32 status;
+       do {
+               err = get_card_status(card, &status, 5);
+               if (err) {
+                       pr_err("%s: error %d requesting status\n",
+                              req->rq_disk->disk_name, err);
+                       return err;
+               }
+               if (status & R1_ERROR) {
+                       pr_err("%s: %s: error sending status cmd, status %#x\n",
+                               req->rq_disk->disk_name, __func__, status);
+                       *gen_err = true;
+               }
+               /* We may rely on the host hw to handle busy detection.*/
+               if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) &&
+                       hw_busy_detect)
+                       break;
+               /*
+                * Timeout if the device never becomes ready for data and never
+                * leaves the program state.
+                */
+               if (time_after(jiffies, timeout)) {
+                       pr_err("%s: Card stuck in programming state! %s %s\n",
+                               mmc_hostname(card->host),
+                               req->rq_disk->disk_name, __func__);
+                       return -ETIMEDOUT;
+               }
+               /*
+                * Some cards mishandle the status bits,
+                * so make sure to check both the busy
+                * indication and the card state.
+                */
+       } while (!(status & R1_READY_FOR_DATA) ||
+                (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+       return err;
+ }
+ static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
+               struct request *req, bool *gen_err, u32 *stop_status)
+ {
+       struct mmc_host *host = card->host;
+       struct mmc_command cmd = {0};
+       int err;
+       bool use_r1b_resp = rq_data_dir(req) == WRITE;
+       /*
+        * Normally we use R1B responses for WRITE, but in cases where the host
+        * has specified a max_busy_timeout we need to validate it. A failure
+        * means we need to prevent the host from doing hw busy detection, which
+        * is done by converting to a R1 response instead.
+        */
+       if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout))
+               use_r1b_resp = false;
+       cmd.opcode = MMC_STOP_TRANSMISSION;
+       if (use_r1b_resp) {
+               cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+               cmd.busy_timeout = timeout_ms;
+       } else {
+               cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+       }
+       err = mmc_wait_for_cmd(host, &cmd, 5);
+       if (err)
+               return err;
+       *stop_status = cmd.resp[0];
+       /* No need to check card status in case of READ. */
+       if (rq_data_dir(req) == READ)
+               return 0;
+       if (!mmc_host_is_spi(host) &&
+               (*stop_status & R1_ERROR)) {
+               pr_err("%s: %s: general error sending stop command, resp %#x\n",
+                       req->rq_disk->disk_name, __func__, *stop_status);
+               *gen_err = true;
+       }
+       return card_busy_detect(card, timeout_ms, use_r1b_resp, req, gen_err);
+ }
+ #define ERR_NOMEDIUM  3
+ #define ERR_RETRY     2
+ #define ERR_ABORT     1
+ #define ERR_CONTINUE  0
+ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
+       bool status_valid, u32 status)
+ {
+       switch (error) {
+       case -EILSEQ:
+               /* response crc error, retry the r/w cmd */
+               pr_err("%s: %s sending %s command, card status %#x\n",
+                       req->rq_disk->disk_name, "response CRC error",
+                       name, status);
+               return ERR_RETRY;
+       case -ETIMEDOUT:
+               pr_err("%s: %s sending %s command, card status %#x\n",
+                       req->rq_disk->disk_name, "timed out", name, status);
+               /* If the status cmd initially failed, retry the r/w cmd */
+               if (!status_valid) {
+                       pr_err("%s: status not valid, retrying timeout\n",
+                               req->rq_disk->disk_name);
+                       return ERR_RETRY;
+               }
+               /*
+                * If it was a r/w cmd crc error, or illegal command
+                * (eg, issued in wrong state) then retry - we should
+                * have corrected the state problem above.
+                */
+               if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) {
+                       pr_err("%s: command error, retrying timeout\n",
+                               req->rq_disk->disk_name);
+                       return ERR_RETRY;
+               }
+               /* Otherwise abort the command */
+               return ERR_ABORT;
+       default:
+               /* We don't understand the error code the driver gave us */
+               pr_err("%s: unknown error %d sending read/write command, card status %#x\n",
+                      req->rq_disk->disk_name, error, status);
+               return ERR_ABORT;
+       }
+ }
+ /*
+  * Initial r/w and stop cmd error recovery.
+  * We don't know whether the card received the r/w cmd or not, so try to
+  * restore things back to a sane state.  Essentially, we do this as follows:
+  * - Obtain card status.  If the first attempt to obtain card status fails,
+  *   the status word will reflect the failed status cmd, not the failed
+  *   r/w cmd.  If we fail to obtain card status, it suggests we can no
+  *   longer communicate with the card.
+  * - Check the card state.  If the card received the cmd but there was a
+  *   transient problem with the response, it might still be in a data transfer
+  *   mode.  Try to send it a stop command.  If this fails, we can't recover.
+  * - If the r/w cmd failed due to a response CRC error, it was probably
+  *   transient, so retry the cmd.
+  * - If the r/w cmd timed out, but we didn't get the r/w cmd status, retry.
+  * - If the r/w cmd timed out, and the r/w cmd failed due to CRC error or
+  *   illegal cmd, retry.
+  * Otherwise we don't understand what happened, so abort.
+  */
+ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
+       struct mmc_blk_request *brq, bool *ecc_err, bool *gen_err)
+ {
+       bool prev_cmd_status_valid = true;
+       u32 status, stop_status = 0;
+       int err, retry;
+       if (mmc_card_removed(card))
+               return ERR_NOMEDIUM;
+       /*
+        * Try to get card status which indicates both the card state
+        * and why there was no response.  If the first attempt fails,
+        * we can't be sure the returned status is for the r/w command.
+        */
+       for (retry = 2; retry >= 0; retry--) {
+               err = get_card_status(card, &status, 0);
+               if (!err)
+                       break;
+               /* Re-tune if needed */
+               mmc_retune_recheck(card->host);
+               prev_cmd_status_valid = false;
+               pr_err("%s: error %d sending status command, %sing\n",
+                      req->rq_disk->disk_name, err, retry ? "retry" : "abort");
+       }
+       /* We couldn't get a response from the card.  Give up. */
+       if (err) {
+               /* Check if the card is removed */
+               if (mmc_detect_card_removed(card->host))
+                       return ERR_NOMEDIUM;
+               return ERR_ABORT;
+       }
+       /* Flag ECC errors */
+       if ((status & R1_CARD_ECC_FAILED) ||
+           (brq->stop.resp[0] & R1_CARD_ECC_FAILED) ||
+           (brq->cmd.resp[0] & R1_CARD_ECC_FAILED))
+               *ecc_err = true;
+       /* Flag General errors */
+       if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ)
+               if ((status & R1_ERROR) ||
+                       (brq->stop.resp[0] & R1_ERROR)) {
+                       pr_err("%s: %s: general error sending stop or status command, stop cmd response %#x, card status %#x\n",
+                              req->rq_disk->disk_name, __func__,
+                              brq->stop.resp[0], status);
+                       *gen_err = true;
+               }
+       /*
+        * Check the current card state.  If it is in some data transfer
+        * mode, tell it to stop (and hopefully transition back to TRAN.)
+        */
+       if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||
+           R1_CURRENT_STATE(status) == R1_STATE_RCV) {
+               err = send_stop(card,
+                       DIV_ROUND_UP(brq->data.timeout_ns, 1000000),
+                       req, gen_err, &stop_status);
+               if (err) {
+                       pr_err("%s: error %d sending stop command\n",
+                              req->rq_disk->disk_name, err);
+                       /*
+                        * If the stop cmd also timed out, the card is probably
+                        * not present, so abort. Other errors are bad news too.
+                        */
+                       return ERR_ABORT;
+               }
+               if (stop_status & R1_CARD_ECC_FAILED)
+                       *ecc_err = true;
+       }
+       /* Check for set block count errors */
+       if (brq->sbc.error)
+               return mmc_blk_cmd_error(req, "SET_BLOCK_COUNT", brq->sbc.error,
+                               prev_cmd_status_valid, status);
+       /* Check for r/w command errors */
+       if (brq->cmd.error)
+               return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
+                               prev_cmd_status_valid, status);
+       /* Data errors */
+       if (!brq->stop.error)
+               return ERR_CONTINUE;
+       /* Now for stop errors.  These aren't fatal to the transfer. */
+       pr_info("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
+              req->rq_disk->disk_name, brq->stop.error,
+              brq->cmd.resp[0], status);
+       /*
+        * Subsitute in our own stop status as this will give the error
+        * state which happened during the execution of the r/w command.
+        */
+       if (stop_status) {
+               brq->stop.resp[0] = stop_status;
+               brq->stop.error = 0;
+       }
+       return ERR_CONTINUE;
+ }
+ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
+                        int type)
+ {
+       int err;
+       if (md->reset_done & type)
+               return -EEXIST;
+       md->reset_done |= type;
+       err = mmc_hw_reset(host);
+       /* Ensure we switch back to the correct partition */
+       if (err != -EOPNOTSUPP) {
+               struct mmc_blk_data *main_md =
+                       dev_get_drvdata(&host->card->dev);
+               int part_err;
+               main_md->part_curr = main_md->part_type;
+               part_err = mmc_blk_part_switch(host->card, md);
+               if (part_err) {
+                       /*
+                        * We have failed to get back into the correct
+                        * partition, so we need to abort the whole request.
+                        */
+                       return -ENODEV;
+               }
+       }
+       return err;
+ }
+ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
+ {
+       md->reset_done &= ~type;
+ }
+ int mmc_access_rpmb(struct mmc_queue *mq)
+ {
+       struct mmc_blk_data *md = mq->blkdata;
+       /*
+        * If this is a RPMB partition access, return ture
+        */
+       if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
+               return true;
+       return false;
+ }
+ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
+ {
+       struct mmc_blk_data *md = mq->blkdata;
+       struct mmc_card *card = md->queue.card;
+       unsigned int from, nr, arg;
+       int err = 0, type = MMC_BLK_DISCARD;
+       if (!mmc_can_erase(card)) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+       from = blk_rq_pos(req);
+       nr = blk_rq_sectors(req);
+       if (mmc_can_discard(card))
+               arg = MMC_DISCARD_ARG;
+       else if (mmc_can_trim(card))
+               arg = MMC_TRIM_ARG;
+       else
+               arg = MMC_ERASE_ARG;
+ retry:
+       if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                INAND_CMD38_ARG_EXT_CSD,
+                                arg == MMC_TRIM_ARG ?
+                                INAND_CMD38_ARG_TRIM :
+                                INAND_CMD38_ARG_ERASE,
+                                0);
+               if (err)
+                       goto out;
+       }
+       err = mmc_erase(card, from, nr, arg);
+ out:
+       if (err == -EIO && !mmc_blk_reset(md, card->host, type))
+               goto retry;
+       if (!err)
+               mmc_blk_reset_success(md, type);
+       blk_end_request(req, err, blk_rq_bytes(req));
+       return err ? 0 : 1;
+ }
+ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
+                                      struct request *req)
+ {
+       struct mmc_blk_data *md = mq->blkdata;
+       struct mmc_card *card = md->queue.card;
+       unsigned int from, nr, arg;
+       int err = 0, type = MMC_BLK_SECDISCARD;
+       if (!(mmc_can_secure_erase_trim(card))) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+       from = blk_rq_pos(req);
+       nr = blk_rq_sectors(req);
+       if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+               arg = MMC_SECURE_TRIM1_ARG;
+       else
+               arg = MMC_SECURE_ERASE_ARG;
+ retry:
+       if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                INAND_CMD38_ARG_EXT_CSD,
+                                arg == MMC_SECURE_TRIM1_ARG ?
+                                INAND_CMD38_ARG_SECTRIM1 :
+                                INAND_CMD38_ARG_SECERASE,
+                                0);
+               if (err)
+                       goto out_retry;
+       }
+       err = mmc_erase(card, from, nr, arg);
+       if (err == -EIO)
+               goto out_retry;
+       if (err)
+               goto out;
+       if (arg == MMC_SECURE_TRIM1_ARG) {
+               if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+                       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                        INAND_CMD38_ARG_EXT_CSD,
+                                        INAND_CMD38_ARG_SECTRIM2,
+                                        0);
+                       if (err)
+                               goto out_retry;
+               }
+               err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
+               if (err == -EIO)
+                       goto out_retry;
+               if (err)
+                       goto out;
+       }
+ out_retry:
+       if (err && !mmc_blk_reset(md, card->host, type))
+               goto retry;
+       if (!err)
+               mmc_blk_reset_success(md, type);
+ out:
+       blk_end_request(req, err, blk_rq_bytes(req));
+       return err ? 0 : 1;
+ }
+ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
+ {
+       struct mmc_blk_data *md = mq->blkdata;
+       struct mmc_card *card = md->queue.card;
+       int ret = 0;
+       ret = mmc_flush_cache(card);
+       if (ret)
+               ret = -EIO;
+       blk_end_request_all(req, ret);
+       return ret ? 0 : 1;
+ }
+ /*
+  * Reformat current write as a reliable write, supporting
+  * both legacy and the enhanced reliable write MMC cards.
+  * In each transfer we'll handle only as much as a single
+  * reliable write can handle, thus finish the request in
+  * partial completions.
+  */
+ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
+                                   struct mmc_card *card,
+                                   struct request *req)
+ {
+       if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) {
+               /* Legacy mode imposes restrictions on transfers. */
+               if (!IS_ALIGNED(brq->cmd.arg, card->ext_csd.rel_sectors))
+                       brq->data.blocks = 1;
+               if (brq->data.blocks > card->ext_csd.rel_sectors)
+                       brq->data.blocks = card->ext_csd.rel_sectors;
+               else if (brq->data.blocks < card->ext_csd.rel_sectors)
+                       brq->data.blocks = 1;
+       }
+ }
+ #define CMD_ERRORS                                                    \
+       (R1_OUT_OF_RANGE |      /* Command argument out of range */     \
+        R1_ADDRESS_ERROR |     /* Misaligned address */                \
+        R1_BLOCK_LEN_ERROR |   /* Transferred block length incorrect */\
+        R1_WP_VIOLATION |      /* Tried to write to protected block */ \
+        R1_CC_ERROR |          /* Card controller error */             \
+        R1_ERROR)              /* General/unknown error */
+ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card,
+                                            struct mmc_async_req *areq)
+ {
+       struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
+                                                   mmc_active);
+       struct mmc_blk_request *brq = &mq_mrq->brq;
+       struct request *req = mq_mrq->req;
+       int need_retune = card->host->need_retune;
+       bool ecc_err = false;
+       bool gen_err = false;
+       /*
+        * sbc.error indicates a problem with the set block count
+        * command.  No data will have been transferred.
+        *
+        * cmd.error indicates a problem with the r/w command.  No
+        * data will have been transferred.
+        *
+        * stop.error indicates a problem with the stop command.  Data
+        * may have been transferred, or may still be transferring.
+        */
+       if (brq->sbc.error || brq->cmd.error || brq->stop.error ||
+           brq->data.error) {
+               switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err, &gen_err)) {
+               case ERR_RETRY:
+                       return MMC_BLK_RETRY;
+               case ERR_ABORT:
+                       return MMC_BLK_ABORT;
+               case ERR_NOMEDIUM:
+                       return MMC_BLK_NOMEDIUM;
+               case ERR_CONTINUE:
+                       break;
+               }
+       }
+       /*
+        * Check for errors relating to the execution of the
+        * initial command - such as address errors.  No data
+        * has been transferred.
+        */
+       if (brq->cmd.resp[0] & CMD_ERRORS) {
+               pr_err("%s: r/w command failed, status = %#x\n",
+                      req->rq_disk->disk_name, brq->cmd.resp[0]);
+               return MMC_BLK_ABORT;
+       }
+       /*
+        * Everything else is either success, or a data error of some
+        * kind.  If it was a write, we may have transitioned to
+        * program mode, which we have to wait for it to complete.
+        */
+       if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
+               int err;
+               /* Check stop command response */
+               if (brq->stop.resp[0] & R1_ERROR) {
+                       pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n",
+                              req->rq_disk->disk_name, __func__,
+                              brq->stop.resp[0]);
+                       gen_err = true;
+               }
+               err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, false, req,
+                                       &gen_err);
+               if (err)
+                       return MMC_BLK_CMD_ERR;
+       }
+       /* if general error occurs, retry the write operation. */
+       if (gen_err) {
+               pr_warn("%s: retrying write for general error\n",
+                               req->rq_disk->disk_name);
+               return MMC_BLK_RETRY;
+       }
+       if (brq->data.error) {
+               if (need_retune && !brq->retune_retry_done) {
+                       pr_debug("%s: retrying because a re-tune was needed\n",
+                                req->rq_disk->disk_name);
+                       brq->retune_retry_done = 1;
+                       return MMC_BLK_RETRY;
+               }
+               pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
+                      req->rq_disk->disk_name, brq->data.error,
+                      (unsigned)blk_rq_pos(req),
+                      (unsigned)blk_rq_sectors(req),
+                      brq->cmd.resp[0], brq->stop.resp[0]);
+               if (rq_data_dir(req) == READ) {
+                       if (ecc_err)
+                               return MMC_BLK_ECC_ERR;
+                       return MMC_BLK_DATA_ERR;
+               } else {
+                       return MMC_BLK_CMD_ERR;
+               }
+       }
+       if (!brq->data.bytes_xfered)
+               return MMC_BLK_RETRY;
+       if (blk_rq_bytes(req) != brq->data.bytes_xfered)
+               return MMC_BLK_PARTIAL;
+       return MMC_BLK_SUCCESS;
+ }
+ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
+                              struct mmc_card *card,
+                              int disable_multi,
+                              struct mmc_queue *mq)
+ {
+       u32 readcmd, writecmd;
+       struct mmc_blk_request *brq = &mqrq->brq;
+       struct request *req = mqrq->req;
+       struct mmc_blk_data *md = mq->blkdata;
+       bool do_data_tag;
+       /*
+        * Reliable writes are used to implement Forced Unit Access and
+        * are supported only on MMCs.
+        */
+       bool do_rel_wr = (req->cmd_flags & REQ_FUA) &&
+               (rq_data_dir(req) == WRITE) &&
+               (md->flags & MMC_BLK_REL_WR);
+       memset(brq, 0, sizeof(struct mmc_blk_request));
+       brq->mrq.cmd = &brq->cmd;
+       brq->mrq.data = &brq->data;
+       brq->cmd.arg = blk_rq_pos(req);
+       if (!mmc_card_blockaddr(card))
+               brq->cmd.arg <<= 9;
+       brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+       brq->data.blksz = 512;
+       brq->stop.opcode = MMC_STOP_TRANSMISSION;
+       brq->stop.arg = 0;
+       brq->data.blocks = blk_rq_sectors(req);
+       /*
+        * The block layer doesn't support all sector count
+        * restrictions, so we need to be prepared for too big
+        * requests.
+        */
+       if (brq->data.blocks > card->host->max_blk_count)
+               brq->data.blocks = card->host->max_blk_count;
+       if (brq->data.blocks > 1) {
+               /*
+                * After a read error, we redo the request one sector
+                * at a time in order to accurately determine which
+                * sectors can be read successfully.
+                */
+               if (disable_multi)
+                       brq->data.blocks = 1;
+               /*
+                * Some controllers have HW issues while operating
+                * in multiple I/O mode
+                */
+               if (card->host->ops->multi_io_quirk)
+                       brq->data.blocks = card->host->ops->multi_io_quirk(card,
+                                               (rq_data_dir(req) == READ) ?
+                                               MMC_DATA_READ : MMC_DATA_WRITE,
+                                               brq->data.blocks);
+       }
+       if (brq->data.blocks > 1 || do_rel_wr) {
+               /* SPI multiblock writes terminate using a special
+                * token, not a STOP_TRANSMISSION request.
+                */
+               if (!mmc_host_is_spi(card->host) ||
+                   rq_data_dir(req) == READ)
+                       brq->mrq.stop = &brq->stop;
+               readcmd = MMC_READ_MULTIPLE_BLOCK;
+               writecmd = MMC_WRITE_MULTIPLE_BLOCK;
+       } else {
+               brq->mrq.stop = NULL;
+               readcmd = MMC_READ_SINGLE_BLOCK;
+               writecmd = MMC_WRITE_BLOCK;
+       }
+       if (rq_data_dir(req) == READ) {
+               brq->cmd.opcode = readcmd;
+               brq->data.flags = MMC_DATA_READ;
+               if (brq->mrq.stop)
+                       brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |
+                                       MMC_CMD_AC;
+       } else {
+               brq->cmd.opcode = writecmd;
+               brq->data.flags = MMC_DATA_WRITE;
+               if (brq->mrq.stop)
+                       brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B |
+                                       MMC_CMD_AC;
+       }
+       if (do_rel_wr)
+               mmc_apply_rel_rw(brq, card, req);
+       /*
+        * Data tag is used only during writing meta data to speed
+        * up write and any subsequent read of this meta data
+        */
+       do_data_tag = (card->ext_csd.data_tag_unit_size) &&
+               (req->cmd_flags & REQ_META) &&
+               (rq_data_dir(req) == WRITE) &&
+               ((brq->data.blocks * brq->data.blksz) >=
+                card->ext_csd.data_tag_unit_size);
+       /*
+        * Pre-defined multi-block transfers are preferable to
+        * open ended-ones (and necessary for reliable writes).
+        * However, it is not sufficient to just send CMD23,
+        * and avoid the final CMD12, as on an error condition
+        * CMD12 (stop) needs to be sent anyway. This, coupled
+        * with Auto-CMD23 enhancements provided by some
+        * hosts, means that the complexity of dealing
+        * with this is best left to the host. If CMD23 is
+        * supported by card and host, we'll fill sbc in and let
+        * the host deal with handling it correctly. This means
+        * that for hosts that don't expose MMC_CAP_CMD23, no
+        * change of behavior will be observed.
+        *
+        * N.B: Some MMC cards experience perf degradation.
+        * We'll avoid using CMD23-bounded multiblock writes for
+        * 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)) {
+               brq->sbc.opcode = MMC_SET_BLOCK_COUNT;
+               brq->sbc.arg = brq->data.blocks |
+                       (do_rel_wr ? (1 << 31) : 0) |
+                       (do_data_tag ? (1 << 29) : 0);
+               brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
+               brq->mrq.sbc = &brq->sbc;
+       }
+       mmc_set_data_timeout(&brq->data, card);
+       brq->data.sg = mqrq->sg;
+       brq->data.sg_len = mmc_queue_map_sg(mq, mqrq);
+       /*
+        * Adjust the sg list so it is the same size as the
+        * request.
+        */
+       if (brq->data.blocks != blk_rq_sectors(req)) {
+               int i, data_size = brq->data.blocks << 9;
+               struct scatterlist *sg;
+               for_each_sg(brq->data.sg, sg, brq->data.sg_len, i) {
+                       data_size -= sg->length;
+                       if (data_size <= 0) {
+                               sg->length += data_size;
+                               i++;
+                               break;
+                       }
+               }
+               brq->data.sg_len = i;
+       }
+       mqrq->mmc_active.mrq = &brq->mrq;
+       mqrq->mmc_active.err_check = mmc_blk_err_check;
+       mmc_queue_bounce_pre(mqrq);
+ }
+ static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
+                          struct mmc_blk_request *brq, struct request *req,
+                          int ret)
+ {
+       struct mmc_queue_req *mq_rq;
+       mq_rq = container_of(brq, struct mmc_queue_req, brq);
+       /*
+        * If this is an SD card and we're writing, we can first
+        * mark the known good sectors as ok.
+        *
+        * If the card is not SD, we can still ok written sectors
+        * as reported by the controller (which might be less than
+        * the real number of written sectors, but never more).
+        */
+       if (mmc_card_sd(card)) {
+               u32 blocks;
+               blocks = mmc_sd_num_wr_blocks(card);
+               if (blocks != (u32)-1) {
+                       ret = blk_end_request(req, 0, blocks << 9);
+               }
+       } else {
+               ret = blk_end_request(req, 0, brq->data.bytes_xfered);
+       }
+       return ret;
+ }
+ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
+ {
+       struct mmc_blk_data *md = mq->blkdata;
+       struct mmc_card *card = md->queue.card;
+       struct mmc_blk_request *brq;
+       int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
+       enum mmc_blk_status status;
+       struct mmc_queue_req *mq_rq;
+       struct request *req;
+       struct mmc_async_req *areq;
+       if (!rqc && !mq->mqrq_prev->req)
+               return 0;
+       do {
+               if (rqc) {
+                       /*
+                        * When 4KB native sector is enabled, only 8 blocks
+                        * multiple read or write is allowed
+                        */
+                       if (mmc_large_sector(card) &&
+                               !IS_ALIGNED(blk_rq_sectors(rqc), 8)) {
+                               pr_err("%s: Transfer size is not 4KB sector size aligned\n",
+                                       rqc->rq_disk->disk_name);
+                               mq_rq = mq->mqrq_cur;
+                               req = rqc;
+                               rqc = NULL;
+                               goto cmd_abort;
+                       }
+                       mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
+                       areq = &mq->mqrq_cur->mmc_active;
+               } else
+                       areq = NULL;
+               areq = mmc_start_req(card->host, areq, &status);
+               if (!areq) {
+                       if (status == MMC_BLK_NEW_REQUEST)
+                               mq->flags |= MMC_QUEUE_NEW_REQUEST;
+                       return 0;
+               }
+               mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
+               brq = &mq_rq->brq;
+               req = mq_rq->req;
+               type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
+               mmc_queue_bounce_post(mq_rq);
+               switch (status) {
+               case MMC_BLK_SUCCESS:
+               case MMC_BLK_PARTIAL:
+                       /*
+                        * A block was successfully transferred.
+                        */
+                       mmc_blk_reset_success(md, type);
+                       ret = blk_end_request(req, 0,
+                                       brq->data.bytes_xfered);
+                       /*
+                        * If the blk_end_request function returns non-zero even
+                        * though all data has been transferred and no errors
+                        * were returned by the host controller, it's a bug.
+                        */
+                       if (status == MMC_BLK_SUCCESS && ret) {
+                               pr_err("%s BUG rq_tot %d d_xfer %d\n",
+                                      __func__, blk_rq_bytes(req),
+                                      brq->data.bytes_xfered);
+                               rqc = NULL;
+                               goto cmd_abort;
+                       }
+                       break;
+               case MMC_BLK_CMD_ERR:
+                       ret = mmc_blk_cmd_err(md, card, brq, req, ret);
+                       if (mmc_blk_reset(md, card->host, type))
+                               goto cmd_abort;
+                       if (!ret)
+                               goto start_new_req;
+                       break;
+               case MMC_BLK_RETRY:
+                       retune_retry_done = brq->retune_retry_done;
+                       if (retry++ < 5)
+                               break;
+                       /* Fall through */
+               case MMC_BLK_ABORT:
+                       if (!mmc_blk_reset(md, card->host, type))
+                               break;
+                       goto cmd_abort;
+               case MMC_BLK_DATA_ERR: {
+                       int err;
+                       err = mmc_blk_reset(md, card->host, type);
+                       if (!err)
+                               break;
+                       if (err == -ENODEV)
+                               goto cmd_abort;
+                       /* Fall through */
+               }
+               case MMC_BLK_ECC_ERR:
+                       if (brq->data.blocks > 1) {
+                               /* Redo read one sector at a time */
+                               pr_warn("%s: retrying using single block read\n",
+                                       req->rq_disk->disk_name);
+                               disable_multi = 1;
+                               break;
+                       }
+                       /*
+                        * After an error, we redo I/O one sector at a
+                        * time, so we only reach here after trying to
+                        * read a single sector.
+                        */
+                       ret = blk_end_request(req, -EIO,
+                                               brq->data.blksz);
+                       if (!ret)
+                               goto start_new_req;
+                       break;
+               case MMC_BLK_NOMEDIUM:
+                       goto cmd_abort;
+               default:
+                       pr_err("%s: Unhandled return value (%d)",
+                                       req->rq_disk->disk_name, status);
+                       goto cmd_abort;
+               }
+               if (ret) {
+                       /*
+                        * In case of a incomplete request
+                        * prepare it again and resend.
+                        */
+                       mmc_blk_rw_rq_prep(mq_rq, card,
+                                       disable_multi, mq);
+                       mmc_start_req(card->host,
+                                       &mq_rq->mmc_active, NULL);
+                       mq_rq->brq.retune_retry_done = retune_retry_done;
+               }
+       } while (ret);
+       return 1;
+  cmd_abort:
+       if (mmc_card_removed(card))
 -                      rqc->cmd_flags |= REQ_QUIET;
++              req->rq_flags |= RQF_QUIET;
+       while (ret)
+               ret = blk_end_request(req, -EIO,
+                               blk_rq_cur_bytes(req));
+  start_new_req:
+       if (rqc) {
+               if (mmc_card_removed(card)) {
++                      rqc->rq_flags |= RQF_QUIET;
+                       blk_end_request_all(rqc, -EIO);
+               } else {
+                       mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
+                       mmc_start_req(card->host,
+                                     &mq->mqrq_cur->mmc_active, NULL);
+               }
+       }
+       return 0;
+ }
+ int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+ {
+       int ret;
+       struct mmc_blk_data *md = mq->blkdata;
+       struct mmc_card *card = md->queue.card;
+       bool req_is_special = mmc_req_is_special(req);
+       if (req && !mq->mqrq_prev->req)
+               /* claim host only for the first request */
+               mmc_get_card(card);
+       ret = mmc_blk_part_switch(card, md);
+       if (ret) {
+               if (req) {
+                       blk_end_request_all(req, -EIO);
+               }
+               ret = 0;
+               goto out;
+       }
+       mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
+       if (req && req_op(req) == REQ_OP_DISCARD) {
+               /* complete ongoing async transfer before issuing discard */
+               if (card->host->areq)
+                       mmc_blk_issue_rw_rq(mq, NULL);
+               ret = mmc_blk_issue_discard_rq(mq, req);
+       } else if (req && req_op(req) == REQ_OP_SECURE_ERASE) {
+               /* complete ongoing async transfer before issuing secure erase*/
+               if (card->host->areq)
+                       mmc_blk_issue_rw_rq(mq, NULL);
+               ret = mmc_blk_issue_secdiscard_rq(mq, req);
+       } else if (req && req_op(req) == REQ_OP_FLUSH) {
+               /* complete ongoing async transfer before issuing flush */
+               if (card->host->areq)
+                       mmc_blk_issue_rw_rq(mq, NULL);
+               ret = mmc_blk_issue_flush(mq, req);
+       } else {
+               ret = mmc_blk_issue_rw_rq(mq, req);
+       }
+ out:
+       if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || req_is_special)
+               /*
+                * Release host when there are no more requests
+                * and after special request(discard, flush) is done.
+                * In case sepecial request, there is no reentry to
+                * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
+                */
+               mmc_put_card(card);
+       return ret;
+ }
+ static inline int mmc_blk_readonly(struct mmc_card *card)
+ {
+       return mmc_card_readonly(card) ||
+              !(card->csd.cmdclass & CCC_BLOCK_WRITE);
+ }
+ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
+                                             struct device *parent,
+                                             sector_t size,
+                                             bool default_ro,
+                                             const char *subname,
+                                             int area_type)
+ {
+       struct mmc_blk_data *md;
+       int devidx, ret;
+ again:
+       if (!ida_pre_get(&mmc_blk_ida, GFP_KERNEL))
+               return ERR_PTR(-ENOMEM);
+       spin_lock(&mmc_blk_lock);
+       ret = ida_get_new(&mmc_blk_ida, &devidx);
+       spin_unlock(&mmc_blk_lock);
+       if (ret == -EAGAIN)
+               goto again;
+       else if (ret)
+               return ERR_PTR(ret);
+       if (devidx >= max_devices) {
+               ret = -ENOSPC;
+               goto out;
+       }
+       md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
+       if (!md) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       md->area_type = area_type;
+       /*
+        * Set the read-only status based on the supported commands
+        * and the write protect switch.
+        */
+       md->read_only = mmc_blk_readonly(card);
+       md->disk = alloc_disk(perdev_minors);
+       if (md->disk == NULL) {
+               ret = -ENOMEM;
+               goto err_kfree;
+       }
+       spin_lock_init(&md->lock);
+       INIT_LIST_HEAD(&md->part);
+       md->usage = 1;
+       ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
+       if (ret)
+               goto err_putdisk;
+       md->queue.blkdata = md;
+       md->disk->major = MMC_BLOCK_MAJOR;
+       md->disk->first_minor = devidx * perdev_minors;
+       md->disk->fops = &mmc_bdops;
+       md->disk->private_data = md;
+       md->disk->queue = md->queue.queue;
+       md->parent = parent;
+       set_disk_ro(md->disk, md->read_only || default_ro);
+       md->disk->flags = GENHD_FL_EXT_DEVT;
+       if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
+               md->disk->flags |= GENHD_FL_NO_PART_SCAN;
+       /*
+        * As discussed on lkml, GENHD_FL_REMOVABLE should:
+        *
+        * - be set for removable media with permanent block devices
+        * - be unset for removable block devices with permanent media
+        *
+        * Since MMC block devices clearly fall under the second
+        * case, we do not set GENHD_FL_REMOVABLE.  Userspace
+        * should use the block device creation/destruction hotplug
+        * messages to tell when the card is present.
+        */
+       snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
+                "mmcblk%u%s", card->host->index, subname ? subname : "");
+       if (mmc_card_mmc(card))
+               blk_queue_logical_block_size(md->queue.queue,
+                                            card->ext_csd.data_sector_size);
+       else
+               blk_queue_logical_block_size(md->queue.queue, 512);
+       set_capacity(md->disk, size);
+       if (mmc_host_cmd23(card->host)) {
+               if ((mmc_card_mmc(card) &&
+                    card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
+                   (mmc_card_sd(card) &&
+                    card->scr.cmds & SD_SCR_CMD23_SUPPORT))
+                       md->flags |= MMC_BLK_CMD23;
+       }
+       if (mmc_card_mmc(card) &&
+           md->flags & MMC_BLK_CMD23 &&
+           ((card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) ||
+            card->ext_csd.rel_sectors)) {
+               md->flags |= MMC_BLK_REL_WR;
+               blk_queue_write_cache(md->queue.queue, true, true);
+       }
+       return md;
+  err_putdisk:
+       put_disk(md->disk);
+  err_kfree:
+       kfree(md);
+  out:
+       spin_lock(&mmc_blk_lock);
+       ida_remove(&mmc_blk_ida, devidx);
+       spin_unlock(&mmc_blk_lock);
+       return ERR_PTR(ret);
+ }
+ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
+ {
+       sector_t size;
+       if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+               /*
+                * The EXT_CSD sector count is in number or 512 byte
+                * sectors.
+                */
+               size = card->ext_csd.sectors;
+       } else {
+               /*
+                * The CSD capacity field is in units of read_blkbits.
+                * set_capacity takes units of 512 bytes.
+                */
+               size = (typeof(sector_t))card->csd.capacity
+                       << (card->csd.read_blkbits - 9);
+       }
+       return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
+                                       MMC_BLK_DATA_AREA_MAIN);
+ }
+ static int mmc_blk_alloc_part(struct mmc_card *card,
+                             struct mmc_blk_data *md,
+                             unsigned int part_type,
+                             sector_t size,
+                             bool default_ro,
+                             const char *subname,
+                             int area_type)
+ {
+       char cap_str[10];
+       struct mmc_blk_data *part_md;
+       part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro,
+                                   subname, area_type);
+       if (IS_ERR(part_md))
+               return PTR_ERR(part_md);
+       part_md->part_type = part_type;
+       list_add(&part_md->part, &md->part);
+       string_get_size((u64)get_capacity(part_md->disk), 512, STRING_UNITS_2,
+                       cap_str, sizeof(cap_str));
+       pr_info("%s: %s %s partition %u %s\n",
+              part_md->disk->disk_name, mmc_card_id(card),
+              mmc_card_name(card), part_md->part_type, cap_str);
+       return 0;
+ }
+ /* MMC Physical partitions consist of two boot partitions and
+  * up to four general purpose partitions.
+  * For each partition enabled in EXT_CSD a block device will be allocatedi
+  * to provide access to the partition.
+  */
+ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
+ {
+       int idx, ret = 0;
+       if (!mmc_card_mmc(card))
+               return 0;
+       for (idx = 0; idx < card->nr_parts; idx++) {
+               if (card->part[idx].size) {
+                       ret = mmc_blk_alloc_part(card, md,
+                               card->part[idx].part_cfg,
+                               card->part[idx].size >> 9,
+                               card->part[idx].force_ro,
+                               card->part[idx].name,
+                               card->part[idx].area_type);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return ret;
+ }
+ static void mmc_blk_remove_req(struct mmc_blk_data *md)
+ {
+       struct mmc_card *card;
+       if (md) {
+               /*
+                * Flush remaining requests and free queues. It
+                * is freeing the queue that stops new requests
+                * from being accepted.
+                */
+               card = md->queue.card;
+               mmc_cleanup_queue(&md->queue);
+               if (md->disk->flags & GENHD_FL_UP) {
+                       device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+                       if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
+                                       card->ext_csd.boot_ro_lockable)
+                               device_remove_file(disk_to_dev(md->disk),
+                                       &md->power_ro_lock);
+                       del_gendisk(md->disk);
+               }
+               mmc_blk_put(md);
+       }
+ }
+ static void mmc_blk_remove_parts(struct mmc_card *card,
+                                struct mmc_blk_data *md)
+ {
+       struct list_head *pos, *q;
+       struct mmc_blk_data *part_md;
+       list_for_each_safe(pos, q, &md->part) {
+               part_md = list_entry(pos, struct mmc_blk_data, part);
+               list_del(pos);
+               mmc_blk_remove_req(part_md);
+       }
+ }
+ static int mmc_add_disk(struct mmc_blk_data *md)
+ {
+       int ret;
+       struct mmc_card *card = md->queue.card;
+       device_add_disk(md->parent, md->disk);
+       md->force_ro.show = force_ro_show;
+       md->force_ro.store = force_ro_store;
+       sysfs_attr_init(&md->force_ro.attr);
+       md->force_ro.attr.name = "force_ro";
+       md->force_ro.attr.mode = S_IRUGO | S_IWUSR;
+       ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
+       if (ret)
+               goto force_ro_fail;
+       if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
+            card->ext_csd.boot_ro_lockable) {
+               umode_t mode;
+               if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
+                       mode = S_IRUGO;
+               else
+                       mode = S_IRUGO | S_IWUSR;
+               md->power_ro_lock.show = power_ro_lock_show;
+               md->power_ro_lock.store = power_ro_lock_store;
+               sysfs_attr_init(&md->power_ro_lock.attr);
+               md->power_ro_lock.attr.mode = mode;
+               md->power_ro_lock.attr.name =
+                                       "ro_lock_until_next_power_on";
+               ret = device_create_file(disk_to_dev(md->disk),
+                               &md->power_ro_lock);
+               if (ret)
+                       goto power_ro_lock_fail;
+       }
+       return ret;
+ power_ro_lock_fail:
+       device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+ force_ro_fail:
+       del_gendisk(md->disk);
+       return ret;
+ }
+ static const struct mmc_fixup blk_fixups[] =
+ {
+       MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       /*
+        * Some MMC cards experience performance degradation with CMD23
+        * instead of CMD12-bounded multiblock transfers. For now we'll
+        * black list what's bad...
+        * - Certain Toshiba cards.
+        *
+        * N.B. This doesn't affect SD cards.
+        */
+       MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_BLK_NO_CMD23),
+       MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_BLK_NO_CMD23),
+       MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_BLK_NO_CMD23),
+       MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_BLK_NO_CMD23),
+       MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_BLK_NO_CMD23),
+       /*
+        * Some MMC cards need longer data read timeout than indicated in CSD.
+        */
+       MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
+                 MMC_QUIRK_LONG_READ_TIME),
+       MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_LONG_READ_TIME),
+       /*
+        * On these Samsung MoviNAND parts, performing secure erase or
+        * secure trim can result in unrecoverable corruption due to a
+        * firmware bug.
+        */
+       MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+       /*
+        *  On Some Kingston eMMCs, performing trim can result in
+        *  unrecoverable data conrruption occasionally due to a firmware bug.
+        */
+       MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_TRIM_BROKEN),
+       MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
+                 MMC_QUIRK_TRIM_BROKEN),
+       END_FIXUP
+ };
+ static int mmc_blk_probe(struct mmc_card *card)
+ {
+       struct mmc_blk_data *md, *part_md;
+       char cap_str[10];
+       /*
+        * Check that the card supports the command class(es) we need.
+        */
+       if (!(card->csd.cmdclass & CCC_BLOCK_READ))
+               return -ENODEV;
+       mmc_fixup_device(card, blk_fixups);
+       md = mmc_blk_alloc(card);
+       if (IS_ERR(md))
+               return PTR_ERR(md);
+       string_get_size((u64)get_capacity(md->disk), 512, STRING_UNITS_2,
+                       cap_str, sizeof(cap_str));
+       pr_info("%s: %s %s %s %s\n",
+               md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
+               cap_str, md->read_only ? "(ro)" : "");
+       if (mmc_blk_alloc_parts(card, md))
+               goto out;
+       dev_set_drvdata(&card->dev, md);
+       if (mmc_add_disk(md))
+               goto out;
+       list_for_each_entry(part_md, &md->part, part) {
+               if (mmc_add_disk(part_md))
+                       goto out;
+       }
+       pm_runtime_set_autosuspend_delay(&card->dev, 3000);
+       pm_runtime_use_autosuspend(&card->dev);
+       /*
+        * Don't enable runtime PM for SD-combo cards here. Leave that
+        * decision to be taken during the SDIO init sequence instead.
+        */
+       if (card->type != MMC_TYPE_SD_COMBO) {
+               pm_runtime_set_active(&card->dev);
+               pm_runtime_enable(&card->dev);
+       }
+       return 0;
+  out:
+       mmc_blk_remove_parts(card, md);
+       mmc_blk_remove_req(md);
+       return 0;
+ }
+ static void mmc_blk_remove(struct mmc_card *card)
+ {
+       struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
+       mmc_blk_remove_parts(card, md);
+       pm_runtime_get_sync(&card->dev);
+       mmc_claim_host(card->host);
+       mmc_blk_part_switch(card, md);
+       mmc_release_host(card->host);
+       if (card->type != MMC_TYPE_SD_COMBO)
+               pm_runtime_disable(&card->dev);
+       pm_runtime_put_noidle(&card->dev);
+       mmc_blk_remove_req(md);
+       dev_set_drvdata(&card->dev, NULL);
+ }
+ static int _mmc_blk_suspend(struct mmc_card *card)
+ {
+       struct mmc_blk_data *part_md;
+       struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
+       if (md) {
+               mmc_queue_suspend(&md->queue);
+               list_for_each_entry(part_md, &md->part, part) {
+                       mmc_queue_suspend(&part_md->queue);
+               }
+       }
+       return 0;
+ }
+ static void mmc_blk_shutdown(struct mmc_card *card)
+ {
+       _mmc_blk_suspend(card);
+ }
+ #ifdef CONFIG_PM_SLEEP
+ static int mmc_blk_suspend(struct device *dev)
+ {
+       struct mmc_card *card = mmc_dev_to_card(dev);
+       return _mmc_blk_suspend(card);
+ }
+ static int mmc_blk_resume(struct device *dev)
+ {
+       struct mmc_blk_data *part_md;
+       struct mmc_blk_data *md = dev_get_drvdata(dev);
+       if (md) {
+               /*
+                * Resume involves the card going into idle state,
+                * so current partition is always the main one.
+                */
+               md->part_curr = md->part_type;
+               mmc_queue_resume(&md->queue);
+               list_for_each_entry(part_md, &md->part, part) {
+                       mmc_queue_resume(&part_md->queue);
+               }
+       }
+       return 0;
+ }
+ #endif
+ static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
+ static struct mmc_driver mmc_driver = {
+       .drv            = {
+               .name   = "mmcblk",
+               .pm     = &mmc_blk_pm_ops,
+       },
+       .probe          = mmc_blk_probe,
+       .remove         = mmc_blk_remove,
+       .shutdown       = mmc_blk_shutdown,
+ };
+ static int __init mmc_blk_init(void)
+ {
+       int res;
+       if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
+               pr_info("mmcblk: using %d minors per device\n", perdev_minors);
+       max_devices = min(MAX_DEVICES, (1 << MINORBITS) / perdev_minors);
+       res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
+       if (res)
+               goto out;
+       res = mmc_register_driver(&mmc_driver);
+       if (res)
+               goto out2;
+       return 0;
+  out2:
+       unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
+  out:
+       return res;
+ }
+ static void __exit mmc_blk_exit(void)
+ {
+       mmc_unregister_driver(&mmc_driver);
+       unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
+ }
+ module_init(mmc_blk_init);
+ module_exit(mmc_blk_exit);
+ MODULE_LICENSE("GPL");
+ MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
index 0000000000000000000000000000000000000000,f4e3d76792f330c144d94f6cc6939db642b58f1a..a6496d8027bce0b4914c2ced3cf6bada3c540b8c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,489 +1,489 @@@
 -      req->cmd_flags |= REQ_DONTPREP;
+ /*
+  *  Copyright (C) 2003 Russell King, All Rights Reserved.
+  *  Copyright 2006-2007 Pierre Ossman
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  *
+  */
+ #include <linux/slab.h>
+ #include <linux/module.h>
+ #include <linux/blkdev.h>
+ #include <linux/freezer.h>
+ #include <linux/kthread.h>
+ #include <linux/scatterlist.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+ #include "queue.h"
+ #include "block.h"
+ #define MMC_QUEUE_BOUNCESZ    65536
+ /*
+  * Prepare a MMC request. This just filters out odd stuff.
+  */
+ static int mmc_prep_request(struct request_queue *q, struct request *req)
+ {
+       struct mmc_queue *mq = q->queuedata;
+       /*
+        * We only like normal block requests and discards.
+        */
+       if (req->cmd_type != REQ_TYPE_FS && req_op(req) != REQ_OP_DISCARD &&
+           req_op(req) != REQ_OP_SECURE_ERASE) {
+               blk_dump_rq_flags(req, "MMC bad request");
+               return BLKPREP_KILL;
+       }
+       if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq)))
+               return BLKPREP_KILL;
 -                      req->cmd_flags |= REQ_QUIET;
++      req->rq_flags |= RQF_DONTPREP;
+       return BLKPREP_OK;
+ }
+ static int mmc_queue_thread(void *d)
+ {
+       struct mmc_queue *mq = d;
+       struct request_queue *q = mq->queue;
+       struct mmc_context_info *cntx = &mq->card->host->context_info;
+       current->flags |= PF_MEMALLOC;
+       down(&mq->thread_sem);
+       do {
+               struct request *req = NULL;
+               spin_lock_irq(q->queue_lock);
+               set_current_state(TASK_INTERRUPTIBLE);
+               req = blk_fetch_request(q);
+               mq->asleep = false;
+               cntx->is_waiting_last_req = false;
+               cntx->is_new_req = false;
+               if (!req) {
+                       /*
+                        * Dispatch queue is empty so set flags for
+                        * mmc_request_fn() to wake us up.
+                        */
+                       if (mq->mqrq_prev->req)
+                               cntx->is_waiting_last_req = true;
+                       else
+                               mq->asleep = true;
+               }
+               mq->mqrq_cur->req = req;
+               spin_unlock_irq(q->queue_lock);
+               if (req || mq->mqrq_prev->req) {
+                       bool req_is_special = mmc_req_is_special(req);
+                       set_current_state(TASK_RUNNING);
+                       mmc_blk_issue_rq(mq, req);
+                       cond_resched();
+                       if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
+                               mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
+                               continue; /* fetch again */
+                       }
+                       /*
+                        * Current request becomes previous request
+                        * and vice versa.
+                        * In case of special requests, current request
+                        * has been finished. Do not assign it to previous
+                        * request.
+                        */
+                       if (req_is_special)
+                               mq->mqrq_cur->req = NULL;
+                       mq->mqrq_prev->brq.mrq.data = NULL;
+                       mq->mqrq_prev->req = NULL;
+                       swap(mq->mqrq_prev, mq->mqrq_cur);
+               } else {
+                       if (kthread_should_stop()) {
+                               set_current_state(TASK_RUNNING);
+                               break;
+                       }
+                       up(&mq->thread_sem);
+                       schedule();
+                       down(&mq->thread_sem);
+               }
+       } while (1);
+       up(&mq->thread_sem);
+       return 0;
+ }
+ /*
+  * Generic MMC request handler.  This is called for any queue on a
+  * particular host.  When the host is not busy, we look for a request
+  * on any queue on this host, and attempt to issue it.  This may
+  * not be the queue we were asked to process.
+  */
+ static void mmc_request_fn(struct request_queue *q)
+ {
+       struct mmc_queue *mq = q->queuedata;
+       struct request *req;
+       struct mmc_context_info *cntx;
+       if (!mq) {
+               while ((req = blk_fetch_request(q)) != NULL) {
++                      req->rq_flags |= RQF_QUIET;
+                       __blk_end_request_all(req, -EIO);
+               }
+               return;
+       }
+       cntx = &mq->card->host->context_info;
+       if (cntx->is_waiting_last_req) {
+               cntx->is_new_req = true;
+               wake_up_interruptible(&cntx->wait);
+       }
+       if (mq->asleep)
+               wake_up_process(mq->thread);
+ }
+ static struct scatterlist *mmc_alloc_sg(int sg_len, int *err)
+ {
+       struct scatterlist *sg;
+       sg = kmalloc(sizeof(struct scatterlist)*sg_len, GFP_KERNEL);
+       if (!sg)
+               *err = -ENOMEM;
+       else {
+               *err = 0;
+               sg_init_table(sg, sg_len);
+       }
+       return sg;
+ }
+ static void mmc_queue_setup_discard(struct request_queue *q,
+                                   struct mmc_card *card)
+ {
+       unsigned max_discard;
+       max_discard = mmc_calc_max_discard(card);
+       if (!max_discard)
+               return;
+       queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+       blk_queue_max_discard_sectors(q, max_discard);
+       if (card->erased_byte == 0 && !mmc_can_discard(card))
+               q->limits.discard_zeroes_data = 1;
+       q->limits.discard_granularity = card->pref_erase << 9;
+       /* granularity must not be greater than max. discard */
+       if (card->pref_erase > max_discard)
+               q->limits.discard_granularity = 0;
+       if (mmc_can_secure_erase_trim(card))
+               queue_flag_set_unlocked(QUEUE_FLAG_SECERASE, q);
+ }
+ #ifdef CONFIG_MMC_BLOCK_BOUNCE
+ static bool mmc_queue_alloc_bounce_bufs(struct mmc_queue *mq,
+                                       unsigned int bouncesz)
+ {
+       int i;
+       for (i = 0; i < mq->qdepth; i++) {
+               mq->mqrq[i].bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+               if (!mq->mqrq[i].bounce_buf)
+                       goto out_err;
+       }
+       return true;
+ out_err:
+       while (--i >= 0) {
+               kfree(mq->mqrq[i].bounce_buf);
+               mq->mqrq[i].bounce_buf = NULL;
+       }
+       pr_warn("%s: unable to allocate bounce buffers\n",
+               mmc_card_name(mq->card));
+       return false;
+ }
+ static int mmc_queue_alloc_bounce_sgs(struct mmc_queue *mq,
+                                     unsigned int bouncesz)
+ {
+       int i, ret;
+       for (i = 0; i < mq->qdepth; i++) {
+               mq->mqrq[i].sg = mmc_alloc_sg(1, &ret);
+               if (ret)
+                       return ret;
+               mq->mqrq[i].bounce_sg = mmc_alloc_sg(bouncesz / 512, &ret);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+ }
+ #endif
+ static int mmc_queue_alloc_sgs(struct mmc_queue *mq, int max_segs)
+ {
+       int i, ret;
+       for (i = 0; i < mq->qdepth; i++) {
+               mq->mqrq[i].sg = mmc_alloc_sg(max_segs, &ret);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+ }
+ static void mmc_queue_req_free_bufs(struct mmc_queue_req *mqrq)
+ {
+       kfree(mqrq->bounce_sg);
+       mqrq->bounce_sg = NULL;
+       kfree(mqrq->sg);
+       mqrq->sg = NULL;
+       kfree(mqrq->bounce_buf);
+       mqrq->bounce_buf = NULL;
+ }
+ static void mmc_queue_reqs_free_bufs(struct mmc_queue *mq)
+ {
+       int i;
+       for (i = 0; i < mq->qdepth; i++)
+               mmc_queue_req_free_bufs(&mq->mqrq[i]);
+ }
+ /**
+  * mmc_init_queue - initialise a queue structure.
+  * @mq: mmc queue
+  * @card: mmc card to attach this queue
+  * @lock: queue lock
+  * @subname: partition subname
+  *
+  * Initialise a MMC card request queue.
+  */
+ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
+                  spinlock_t *lock, const char *subname)
+ {
+       struct mmc_host *host = card->host;
+       u64 limit = BLK_BOUNCE_HIGH;
+       bool bounce = false;
+       int ret = -ENOMEM;
+       if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
+               limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;
+       mq->card = card;
+       mq->queue = blk_init_queue(mmc_request_fn, lock);
+       if (!mq->queue)
+               return -ENOMEM;
+       mq->qdepth = 2;
+       mq->mqrq = kcalloc(mq->qdepth, sizeof(struct mmc_queue_req),
+                          GFP_KERNEL);
+       if (!mq->mqrq)
+               goto blk_cleanup;
+       mq->mqrq_cur = &mq->mqrq[0];
+       mq->mqrq_prev = &mq->mqrq[1];
+       mq->queue->queuedata = mq;
+       blk_queue_prep_rq(mq->queue, mmc_prep_request);
+       queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
+       queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, mq->queue);
+       if (mmc_can_erase(card))
+               mmc_queue_setup_discard(mq->queue, card);
+ #ifdef CONFIG_MMC_BLOCK_BOUNCE
+       if (host->max_segs == 1) {
+               unsigned int bouncesz;
+               bouncesz = MMC_QUEUE_BOUNCESZ;
+               if (bouncesz > host->max_req_size)
+                       bouncesz = host->max_req_size;
+               if (bouncesz > host->max_seg_size)
+                       bouncesz = host->max_seg_size;
+               if (bouncesz > (host->max_blk_count * 512))
+                       bouncesz = host->max_blk_count * 512;
+               if (bouncesz > 512 &&
+                   mmc_queue_alloc_bounce_bufs(mq, bouncesz)) {
+                       blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
+                       blk_queue_max_hw_sectors(mq->queue, bouncesz / 512);
+                       blk_queue_max_segments(mq->queue, bouncesz / 512);
+                       blk_queue_max_segment_size(mq->queue, bouncesz);
+                       ret = mmc_queue_alloc_bounce_sgs(mq, bouncesz);
+                       if (ret)
+                               goto cleanup_queue;
+                       bounce = true;
+               }
+       }
+ #endif
+       if (!bounce) {
+               blk_queue_bounce_limit(mq->queue, limit);
+               blk_queue_max_hw_sectors(mq->queue,
+                       min(host->max_blk_count, host->max_req_size / 512));
+               blk_queue_max_segments(mq->queue, host->max_segs);
+               blk_queue_max_segment_size(mq->queue, host->max_seg_size);
+               ret = mmc_queue_alloc_sgs(mq, host->max_segs);
+               if (ret)
+                       goto cleanup_queue;
+       }
+       sema_init(&mq->thread_sem, 1);
+       mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",
+               host->index, subname ? subname : "");
+       if (IS_ERR(mq->thread)) {
+               ret = PTR_ERR(mq->thread);
+               goto cleanup_queue;
+       }
+       return 0;
+  cleanup_queue:
+       mmc_queue_reqs_free_bufs(mq);
+       kfree(mq->mqrq);
+       mq->mqrq = NULL;
+ blk_cleanup:
+       blk_cleanup_queue(mq->queue);
+       return ret;
+ }
+ void mmc_cleanup_queue(struct mmc_queue *mq)
+ {
+       struct request_queue *q = mq->queue;
+       unsigned long flags;
+       /* Make sure the queue isn't suspended, as that will deadlock */
+       mmc_queue_resume(mq);
+       /* Then terminate our worker thread */
+       kthread_stop(mq->thread);
+       /* Empty the queue */
+       spin_lock_irqsave(q->queue_lock, flags);
+       q->queuedata = NULL;
+       blk_start_queue(q);
+       spin_unlock_irqrestore(q->queue_lock, flags);
+       mmc_queue_reqs_free_bufs(mq);
+       kfree(mq->mqrq);
+       mq->mqrq = NULL;
+       mq->card = NULL;
+ }
+ EXPORT_SYMBOL(mmc_cleanup_queue);
+ /**
+  * mmc_queue_suspend - suspend a MMC request queue
+  * @mq: MMC queue to suspend
+  *
+  * Stop the block request queue, and wait for our thread to
+  * complete any outstanding requests.  This ensures that we
+  * won't suspend while a request is being processed.
+  */
+ void mmc_queue_suspend(struct mmc_queue *mq)
+ {
+       struct request_queue *q = mq->queue;
+       unsigned long flags;
+       if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
+               mq->flags |= MMC_QUEUE_SUSPENDED;
+               spin_lock_irqsave(q->queue_lock, flags);
+               blk_stop_queue(q);
+               spin_unlock_irqrestore(q->queue_lock, flags);
+               down(&mq->thread_sem);
+       }
+ }
+ /**
+  * mmc_queue_resume - resume a previously suspended MMC request queue
+  * @mq: MMC queue to resume
+  */
+ void mmc_queue_resume(struct mmc_queue *mq)
+ {
+       struct request_queue *q = mq->queue;
+       unsigned long flags;
+       if (mq->flags & MMC_QUEUE_SUSPENDED) {
+               mq->flags &= ~MMC_QUEUE_SUSPENDED;
+               up(&mq->thread_sem);
+               spin_lock_irqsave(q->queue_lock, flags);
+               blk_start_queue(q);
+               spin_unlock_irqrestore(q->queue_lock, flags);
+       }
+ }
+ /*
+  * Prepare the sg list(s) to be handed of to the host driver
+  */
+ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq)
+ {
+       unsigned int sg_len;
+       size_t buflen;
+       struct scatterlist *sg;
+       int i;
+       if (!mqrq->bounce_buf)
+               return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
+       sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
+       mqrq->bounce_sg_len = sg_len;
+       buflen = 0;
+       for_each_sg(mqrq->bounce_sg, sg, sg_len, i)
+               buflen += sg->length;
+       sg_init_one(mqrq->sg, mqrq->bounce_buf, buflen);
+       return 1;
+ }
+ /*
+  * If writing, bounce the data to the buffer before the request
+  * is sent to the host driver
+  */
+ void mmc_queue_bounce_pre(struct mmc_queue_req *mqrq)
+ {
+       if (!mqrq->bounce_buf)
+               return;
+       if (rq_data_dir(mqrq->req) != WRITE)
+               return;
+       sg_copy_to_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
+               mqrq->bounce_buf, mqrq->sg[0].length);
+ }
+ /*
+  * If reading, bounce the data from the buffer after the request
+  * has been handled by the host driver
+  */
+ void mmc_queue_bounce_post(struct mmc_queue_req *mqrq)
+ {
+       if (!mqrq->bounce_buf)
+               return;
+       if (rq_data_dir(mqrq->req) != READ)
+               return;
+       sg_copy_from_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len,
+               mqrq->bounce_buf, mqrq->sg[0].length);
+ }