From patchwork Fri Apr 15 23:51:42 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrei Warkentin X-Patchwork-Id: 711791 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p3FNDHeZ006404 for ; Fri, 15 Apr 2011 23:13:19 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752233Ab1DOXNQ (ORCPT ); Fri, 15 Apr 2011 19:13:16 -0400 Received: from exprod5og108.obsmtp.com ([64.18.0.186]:38985 "EHLO exprod5og108.obsmtp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751283Ab1DOXNQ (ORCPT ); Fri, 15 Apr 2011 19:13:16 -0400 Received: from il93mgrg01.am.mot-mobility.com ([144.188.21.13]) (using TLSv1) by exprod5ob108.postini.com ([64.18.4.12]) with SMTP ID DSNKTajRC9FFwA6smQwRQkX+BRIH//T7jN4f@postini.com; Fri, 15 Apr 2011 16:13:15 PDT Received: from il93mgrg01.am.mot-mobility.com ([10.22.94.168]) by il93mgrg01.am.mot-mobility.com (8.14.3/8.14.3) with ESMTP id p3FNBON4016400 for ; Fri, 15 Apr 2011 19:11:24 -0400 (EDT) Received: from mail-yx0-f170.google.com (mail-yx0-f170.google.com [209.85.213.170]) by il93mgrg01.am.mot-mobility.com (8.14.3/8.14.3) with ESMTP id p3FNBOE0016387 (version=TLSv1/SSLv3 cipher=RC4-MD5 bits=128 verify=OK) for ; Fri, 15 Apr 2011 19:11:24 -0400 (EDT) Received: by yxi11 with SMTP id 11so2197034yxi.15 for ; Fri, 15 Apr 2011 16:13:13 -0700 (PDT) Received: by 10.236.80.71 with SMTP id j47mr1373118yhe.45.1302909193766; Fri, 15 Apr 2011 16:13:13 -0700 (PDT) Received: from localhost.localdomain (dyngate-ca119-13.motorola.com [144.189.96.13]) by mx.google.com with ESMTPS id f28sm1458306yhc.44.2011.04.15.16.13.11 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 15 Apr 2011 16:13:12 -0700 (PDT) From: Andrei Warkentin To: linux-mmc@vger.kernel.org Cc: Andrei Warkentin , arindam.nath@amd.com, cjb@laptop.org, arnd@arndb.de Subject: [patchv2 1/3] MMC: Use CMD23 for multiblock transfers when we can. Date: Fri, 15 Apr 2011 18:51:42 -0500 Message-Id: <1302911504-18287-1-git-send-email-andreiw@motorola.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1302741523-22276-1-git-send-email-andreiw@motorola.com> References: <1302741523-22276-1-git-send-email-andreiw@motorola.com> X-CFilter-Loop: Reflected Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Fri, 15 Apr 2011 23:13:20 +0000 (UTC) CMD23-prefixed instead of open-ended multiblock transfers have a performance advantage on some MMC cards. Cc: arindam.nath@amd.com Cc: cjb@laptop.org Cc: arnd@arndb.de Signed-off-by: Andrei Warkentin --- drivers/mmc/card/block.c | 112 +++++++++++++++++++++++++++++++++------------ include/linux/mmc/card.h | 1 + include/linux/mmc/core.h | 1 + include/linux/mmc/host.h | 6 +++ include/linux/mmc/mmc.h | 6 +++ 5 files changed, 96 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c209953..bba0888 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -55,10 +55,6 @@ MODULE_ALIAS("mmc:block"); #define INAND_CMD38_ARG_SECTRIM1 0x81 #define INAND_CMD38_ARG_SECTRIM2 0x88 -#define REL_WRITES_SUPPORTED(card) (mmc_card_mmc((card)) && \ - (((card)->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) || \ - ((card)->ext_csd.rel_sectors))) - static DEFINE_MUTEX(block_mutex); /* @@ -85,6 +81,10 @@ struct mmc_blk_data { 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; @@ -225,6 +225,7 @@ static const struct block_device_operations mmc_bdops = { struct mmc_blk_request { struct mmc_request mrq; + struct mmc_command sbc; struct mmc_command cmd; struct mmc_command stop; struct mmc_data data; @@ -455,13 +456,10 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req) * reliable write can handle, thus finish the request in * partial completions. */ -static inline int mmc_apply_rel_rw(struct mmc_blk_request *brq, - struct mmc_card *card, - struct request *req) +static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq, + struct mmc_card *card, + struct request *req) { - int err; - struct mmc_command set_count; - 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)) @@ -472,16 +470,6 @@ static inline int mmc_apply_rel_rw(struct mmc_blk_request *brq, else if (brq->data.blocks < card->ext_csd.rel_sectors) brq->data.blocks = 1; } - - memset(&set_count, 0, sizeof(struct mmc_command)); - set_count.opcode = MMC_SET_BLOCK_COUNT; - set_count.arg = brq->data.blocks | (1 << 31); - set_count.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &set_count, 0); - if (err) - printk(KERN_ERR "%s: error %d SET_BLOCK_COUNT\n", - req->rq_disk->disk_name, err); - return err; } static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) @@ -498,7 +486,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) bool do_rel_wr = ((req->cmd_flags & REQ_FUA) || (req->cmd_flags & REQ_META)) && (rq_data_dir(req) == WRITE) && - REL_WRITES_SUPPORTED(card); + (md->flags & MMC_BLK_REL_WR); do { struct mmc_command cmd; @@ -536,11 +524,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) if (brq.data.blocks > 1 || do_rel_wr) { /* SPI multiblock writes terminate using a special - * token, not a STOP_TRANSMISSION request. Reliable - * writes use SET_BLOCK_COUNT and do not use a - * STOP_TRANSMISSION request either. + * token, not a STOP_TRANSMISSION request. */ - if ((!mmc_host_is_spi(card->host) && !do_rel_wr) || + if (!mmc_host_is_spi(card->host) || rq_data_dir(req) == READ) brq.mrq.stop = &brq.stop; readcmd = MMC_READ_MULTIPLE_BLOCK; @@ -558,8 +544,37 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) brq.data.flags |= MMC_DATA_WRITE; } - if (do_rel_wr && mmc_apply_rel_rw(&brq, card, req)) - goto cmd_err; + if (do_rel_wr) + mmc_apply_rel_rw(&brq, card, req); + + /* + * 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))) { + brq.sbc.opcode = MMC_SET_BLOCK_COUNT; + brq.sbc.arg = brq.data.blocks | + (do_rel_wr ? (1 << 31) : 0); + brq.sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; + brq.mrq.sbc = &brq.sbc; + } mmc_set_data_timeout(&brq.data, card); @@ -596,7 +611,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) * until later as we need to wait for the card to leave * programming mode even when things go wrong. */ - if (brq.cmd.error || brq.data.error || brq.stop.error) { + if (brq.sbc.error || brq.cmd.error || + brq.data.error || brq.stop.error) { if (brq.data.blocks > 1 && rq_data_dir(req) == READ) { /* Redo read one sector at a time */ printk(KERN_WARNING "%s: retrying using single " @@ -607,6 +623,13 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) status = get_card_status(card, req); } + if (brq.sbc.error) { + printk(KERN_ERR "%s: error %d sending SET_BLOCK_COUNT " + "command, response %#x, card status %#x\n", + req->rq_disk->disk_name, brq.sbc.error, + brq.sbc.resp[0], status); + } + if (brq.cmd.error) { printk(KERN_ERR "%s: error %d sending read/write " "command, response %#x, card status %#x\n", @@ -804,8 +827,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, md->disk->queue = md->queue.queue; md->disk->driverfs_dev = parent; set_disk_ro(md->disk, md->read_only || default_ro); - if (REL_WRITES_SUPPORTED(card)) - blk_queue_flush(md->queue.queue, REQ_FLUSH | REQ_FUA); /* * As discussed on lkml, GENHD_FL_REMOVABLE should: @@ -829,6 +850,19 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, blk_queue_logical_block_size(md->queue.queue, 512); set_capacity(md->disk, size); + + if (mmc_host_cmd23(card->host) && + mmc_card_mmc(card)) + 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_flush(md->queue.queue, REQ_FLUSH | REQ_FUA); + } + return md; err_putdisk: @@ -982,6 +1016,24 @@ static const struct mmc_fixup blk_fixups[] = MMC_FIXUP("SEM08G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), MMC_FIXUP("SEM16G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), MMC_FIXUP("SEM32G", 0x2, 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 + * white list what's good. + */ + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_ANY, CID_OEMID_ANY, add_quirk, + MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM02G", 0x2, 0x100, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM04G", 0x2, 0x100, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM08G", 0x2, 0x100, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM16G", 0x2, 0x100, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM32G", 0x2, 0x100, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM02G", 0x45, 0x0, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM04G", 0x45, 0x0, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM08G", 0x45, 0x0, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM16G", 0x45, 0x0, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SEM32G", 0x45, 0x0, remove_quirk, MMC_QUIRK_BLK_NO_CMD23), END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 72a9868..1e6bd6c 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -134,6 +134,7 @@ struct mmc_card { #define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */ #define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ #define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */ +#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */ unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index f8e4bcb..55d7fde 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -120,6 +120,7 @@ struct mmc_data { }; struct mmc_request { + struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd; struct mmc_data *data; struct mmc_command *stop; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 4f705eb..ba34fc5 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -173,6 +173,7 @@ struct mmc_host { /* DDR mode at 1.2V */ #define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */ #define MMC_CAP_BUS_WIDTH_TEST (1 << 14) /* CMD14/CMD19 bus width ok */ +#define MMC_CAP_CMD23 (1 << 15) /* CMD23 supported */ mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -330,5 +331,10 @@ static inline int mmc_card_wake_sdio_irq(struct mmc_host *host) { return host->pm_flags & MMC_PM_WAKE_SDIO_IRQ; } + +static inline int mmc_host_cmd23(struct mmc_host *host) +{ + return host->caps & MMC_CAP_CMD23; +} #endif diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 373b2bf..ea74168 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -82,6 +82,12 @@ #define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ +static inline bool mmc_op_multi(u32 opcode) +{ + return opcode == MMC_WRITE_MULTIPLE_BLOCK || + opcode == MMC_READ_MULTIPLE_BLOCK; +} + /* * MMC_SWITCH argument format: *