From patchwork Fri Apr 18 13:37:01 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seungwon Jeon X-Patchwork-Id: 4016401 Return-Path: X-Original-To: patchwork-linux-mmc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id C02CABFF02 for ; Fri, 18 Apr 2014 13:37:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9B14A203AA for ; Fri, 18 Apr 2014 13:37:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3F4FC2038D for ; Fri, 18 Apr 2014 13:37:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751885AbaDRNhF (ORCPT ); Fri, 18 Apr 2014 09:37:05 -0400 Received: from mailout2.samsung.com ([203.254.224.25]:64751 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751159AbaDRNhD (ORCPT ); Fri, 18 Apr 2014 09:37:03 -0400 Received: from epcpsbgr2.samsung.com (u142.gpu120.samsung.co.kr [203.254.230.142]) by mailout2.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0N48000BEB5QR390@mailout2.samsung.com> for linux-mmc@vger.kernel.org; Fri, 18 Apr 2014 22:37:02 +0900 (KST) Received: from epcpsbgm1.samsung.com ( [203.254.230.51]) by epcpsbgr2.samsung.com (EPCPMTA) with SMTP id 5E.9D.09028.E7A21535; Fri, 18 Apr 2014 22:37:02 +0900 (KST) X-AuditID: cbfee68e-b7f566d000002344-9b-53512a7ea4d6 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id C2.D1.29263.D7A21535; Fri, 18 Apr 2014 22:37:02 +0900 (KST) Received: from DOTGIHJUN01 ([12.36.185.168]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0N4800A6RB5P2DA0@mmp1.samsung.com>; Fri, 18 Apr 2014 22:37:01 +0900 (KST) From: Seungwon Jeon To: linux-mmc@vger.kernel.org Cc: 'Chris Ball' , 'Ulf Hansson' , 'Jaehoon Chung' , 'Jackey Shen' , 'Alim Akhtar' References: <1383653403-10049-1-git-send-email-ulf.hansson@linaro.org> <006701cf2a58$c628e050$527aa0f0$%jun@samsung.com> In-reply-to: Subject: [PATCH v6 5/6] mmc: add support for HS400 mode of eMMC5.0 Date: Fri, 18 Apr 2014 22:37:01 +0900 Message-id: <001801cf5b0b$4783c6a0$d68b53e0$%jun@samsung.com> MIME-version: 1.0 Content-type: text/plain; charset=UTF-8 Content-transfer-encoding: 7bit X-Mailer: Microsoft Office Outlook 12.0 Thread-index: Ac7aH/uUm1J6MxFOSyaXb4Fx/nx/rAABnFmgDfJZ22AGGfSo8APt897AAVsylFACWLK74AMkkqCgAWQT7DA= Content-language: ko X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrDIsWRmVeSWpSXmKPExsVy+t8zY906rcBgg2tPOSwezNvGZjHh8nZG iwn3JrJa3PjVxmpx5H8/o8XxteEObB6tl/6yedy5tofN48arhUwefVtWMXp83iQXwBrFZZOS mpNZllqkb5fAlTGl9zZTwfKoirnfbzA3ML707GLk5JAQMJFYfeEBE4QtJnHh3nq2LkYuDiGB ZYwSX99vYoIper12MxNEYhGjxKZHz5lBEkICfxgl3k7LArHZBLQk/r55AxYXEZCV+PnnAtgk ZoHjjBLXthxggWioklh1bhtYEacAj8S6P51gtrCAk8SLLcvBbBYBVYkvrxrANvMK2ErMnPwG yhaU+DH5HtgcZgF1iUnzFjFD2PISm9e8BbI5gC5Vl3j0VxfihhSJNwf72SFKRCT2vXjHCHKP hMA9dokXxzuhdglIfJt8iAWiV1Zi0wFmiIclJQ6uuMEygVFiFpLNs5BsnoVk8ywkKxYwsqxi FE0tSC4oTkovMtIrTswtLs1L10vOz93ECInZvh2MNw9YH2JMBlo/kVlKNDkfGPN5JfGGxmZG FqYmpsZG5pZmpAkrifMuepgUJCSQnliSmp2aWpBaFF9UmpNafIiRiYNTqoHR7PGMnLpjC8UY uBta/8X0bTj8YIOate2Di7tvPJ743Miigf9lS/2RQz3nmAK2b/m4c0YaW2jhtkNft97ojxda dUZvufRd13WCS+4uKl0tYStp6WK3z+/cqw+lz/9cfHZnzZV+vo/f9eIY08If6+uzfLY66WNz xvXe19Cd+ZMm6Jzb8595cX/MIiWW4oxEQy3mouJEAHHxs9TvAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrJKsWRmVeSWpSXmKPExsVy+t9jAd06rcBgg38vmCwezNvGZjHh8nZG iwn3JrJa3PjVxmpx5H8/o8XxteEObB6tl/6yedy5tofN48arhUwefVtWMXp83iQXwBrVwGiT kZqYklqkkJqXnJ+SmZduq+QdHO8cb2pmYKhraGlhrqSQl5ibaqvk4hOg65aZA3SBkkJZYk4p UCggsbhYSd8O04TQEDddC5jGCF3fkCC4HiMDNJCwjjFjSu9tpoLlURVzv99gbmB86dnFyMkh IWAi8XrtZiYIW0ziwr31bF2MXBxCAosYJTY9es4MkhAS+MMo8XZaFojNJqAl8ffNG7C4iICs xM8/F8AamAWOM0pc23KABaKhSmLVuW1gRZwCPBLr/nSC2cICThIvtiwHs1kEVCW+vGoA28wr YCsxc/IbKFtQ4sfke2BzmAXUJSbNW8QMYctLbF7zFsjmALpUXeLRX12IG1Ik3hzsZ4coEZHY 9+Id4wRGoVlIJs1CMmkWkkmzkLQsYGRZxSiaWpBcUJyUnmuoV5yYW1yal66XnJ+7iRGcEJ5J 7WBc2WBxiFGAg1GJh1dALyBYiDWxrLgy9xCjBAezkgjv7r9AId6UxMqq1KL8+KLSnNTiQ4zJ QI9OZJYSTc4HJqu8knhDYxMzI0sjMwsjE3Nz0oSVxHkPtFoHCgmkJ5akZqemFqQWwWxh4uCU amCcuzLH9ZXo7nidUn2+bx3L5BKk/78z0F8loP6gas562YgJvUWRr/ZZssiIH1l4KLPhvPWy fV+yutsTF+plLDSKrzzlVLpc/Lb2O94pf/bfj+O5+33umvkXmV5cnhqaX2hw+UZboo/MlNgr M8//u+DbUiSixjNpgs9CpjtenE5LrwrYM/EI7HmuxFKckWioxVxUnAgAnGymv0wDAAA= DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-CFilter-Loop: Reflected Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds HS400 mode support for eMMC5.0 device. HS400 mode is high speed DDR interface timing from HS200. Clock frequency is up to 200MHz and only 8-bit bus width is supported. In addition, tuning process of HS200 is required to synchronize the command response on the CMD line because CMD input timing for HS400 mode is the same as HS200 mode. Signed-off-by: Seungwon Jeon Tested-by: Jaehoon Chung Acked-by: Jaehoon Chung Acked-by: Ulf Hansson --- drivers/mmc/core/bus.c | 1 + drivers/mmc/core/debugfs.c | 3 + drivers/mmc/core/mmc.c | 98 +++++++++++++++++++++++++++++++++++++++++-- include/linux/mmc/card.h | 1 + include/linux/mmc/host.h | 14 ++++++ include/linux/mmc/mmc.h | 7 +++- 6 files changed, 118 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index f37e9d6..d2dbf02 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -349,6 +349,7 @@ int mmc_add_card(struct mmc_card *card) mmc_hostname(card->host), mmc_card_uhs(card) ? "ultra high speed " : (mmc_card_hs(card) ? "high speed " : ""), + mmc_card_hs400(card) ? "HS400 " : (mmc_card_hs200(card) ? "HS200 " : ""), mmc_card_ddr52(card) ? "DDR " : "", uhs_bus_speed_mode, type, card->rca); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 1f730db..91eb162 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -141,6 +141,9 @@ static int mmc_ios_show(struct seq_file *s, void *data) case MMC_TIMING_MMC_HS200: str = "mmc HS200"; break; + case MMC_TIMING_MMC_HS400: + str = "mmc HS400"; + break; default: str = "invalid"; break; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7e9b424..c12e247 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -240,7 +240,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) static void mmc_select_card_type(struct mmc_card *card) { struct mmc_host *host = card->host; - u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK; + u8 card_type = card->ext_csd.raw_card_type; u32 caps = host->caps, caps2 = host->caps2; unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; unsigned int avail_type = 0; @@ -281,6 +281,18 @@ static void mmc_select_card_type(struct mmc_card *card) avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V; } + if (caps2 & MMC_CAP2_HS400_1_8V && + card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) { + hs200_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V; + } + + if (caps2 & MMC_CAP2_HS400_1_2V && + card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) { + hs200_max_dtr = MMC_HS200_MAX_DTR; + avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; + } + card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; @@ -499,6 +511,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_PWR_CL_DDR_52_195]; card->ext_csd.raw_pwr_cl_ddr_52_360 = ext_csd[EXT_CSD_PWR_CL_DDR_52_360]; + card->ext_csd.raw_pwr_cl_ddr_200_360 = + ext_csd[EXT_CSD_PWR_CL_DDR_200_360]; } if (card->ext_csd.rev >= 5) { @@ -665,7 +679,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) (card->ext_csd.raw_pwr_cl_ddr_52_195 == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) && (card->ext_csd.raw_pwr_cl_ddr_52_360 == - bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360])); + bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) && + (card->ext_csd.raw_pwr_cl_ddr_200_360 == + bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360])); + if (err) err = -EINVAL; @@ -776,7 +793,9 @@ static int __mmc_select_powerclass(struct mmc_card *card, ext_csd->raw_pwr_cl_52_360 : ext_csd->raw_pwr_cl_ddr_52_360; else if (host->ios.clock <= MMC_HS200_MAX_DTR) - pwrclass_val = ext_csd->raw_pwr_cl_200_360; + pwrclass_val = (bus_width == EXT_CSD_DDR_BUS_WIDTH_8) ? + ext_csd->raw_pwr_cl_ddr_200_360 : + ext_csd->raw_pwr_cl_200_360; break; default: pr_warning("%s: Voltage range not supported " @@ -840,7 +859,8 @@ static void mmc_set_bus_speed(struct mmc_card *card) { unsigned int max_dtr = (unsigned int)-1; - if (mmc_card_hs200(card) && max_dtr > card->ext_csd.hs200_max_dtr) + if ((mmc_card_hs200(card) || mmc_card_hs400(card)) && + max_dtr > card->ext_csd.hs200_max_dtr) max_dtr = card->ext_csd.hs200_max_dtr; else if (mmc_card_hs(card) && max_dtr > card->ext_csd.hs_max_dtr) max_dtr = card->ext_csd.hs_max_dtr; @@ -993,6 +1013,61 @@ static int mmc_select_hs_ddr(struct mmc_card *card) return err; } +static int mmc_select_hs400(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + + /* + * HS400 mode requires 8-bit bus width + */ + if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && + host->ios.bus_width == MMC_BUS_WIDTH_8)) + return 0; + + /* + * Before switching to dual data rate operation for HS400, + * it is required to convert from HS200 mode to HS mode. + */ + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + mmc_set_bus_speed(card); + + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, + card->ext_csd.generic_cmd6_time, + true, true, true); + if (err) { + pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n", + mmc_hostname(host), err); + return err; + } + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + EXT_CSD_DDR_BUS_WIDTH_8, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_warn("%s: switch to bus width for hs400 failed, err:%d\n", + mmc_hostname(host), err); + return err; + } + + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, + card->ext_csd.generic_cmd6_time, + true, true, true); + if (err) { + pr_warn("%s: switch to hs400 failed, err:%d\n", + mmc_hostname(host), err); + return err; + } + + mmc_set_timing(host, MMC_TIMING_MMC_HS400); + mmc_set_bus_speed(card); + + return 0; +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. @@ -1070,13 +1145,22 @@ bus_speed: /* * Execute tuning sequence to seek the proper bus operating - * conditions for HS200, which sends CMD21 to the device. + * conditions for HS200 and HS400, which sends CMD21 to the device. */ static int mmc_hs200_tuning(struct mmc_card *card) { struct mmc_host *host = card->host; int err = 0; + /* + * Timing should be adjusted to the HS400 target + * operation frequency for tuning process + */ + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && + host->ios.bus_width == MMC_BUS_WIDTH_8) + if (host->ops->prepare_hs400_tuning) + host->ops->prepare_hs400_tuning(host, &host->ios); + if (host->ops->execute_tuning) { mmc_host_clk_hold(host); err = host->ops->execute_tuning(host, @@ -1304,6 +1388,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_hs200_tuning(card); if (err) goto err; + + err = mmc_select_hs400(card); + if (err) + goto err; } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index c422349..e68bbeb 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -110,6 +110,7 @@ struct mmc_ext_csd { u8 raw_pwr_cl_200_360; /* 237 */ u8 raw_pwr_cl_ddr_52_195; /* 238 */ u8 raw_pwr_cl_ddr_52_360; /* 239 */ + u8 raw_pwr_cl_ddr_200_360; /* 253 */ u8 raw_bkops_status; /* 246 */ u8 raw_sectors[4]; /* 212 - 4 bytes */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1ee3c10..5664d57 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -61,6 +61,7 @@ struct mmc_ios { #define MMC_TIMING_UHS_DDR50 7 #define MMC_TIMING_MMC_DDR52 8 #define MMC_TIMING_MMC_HS200 9 +#define MMC_TIMING_MMC_HS400 10 unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */ @@ -132,6 +133,9 @@ struct mmc_host_ops { /* The tuning command opcode value is different for SD and eMMC cards */ int (*execute_tuning)(struct mmc_host *host, u32 opcode); + + /* Prepare HS400 target operating frequency depending host driver */ + void (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); @@ -274,6 +278,10 @@ struct mmc_host { #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \ MMC_CAP2_PACKED_WR) #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ +#define MMC_CAP2_HS400_1_8V (1 << 15) /* Can support HS400 1.8V */ +#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ +#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ + MMC_CAP2_HS400_1_2V) mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -493,4 +501,10 @@ static inline bool mmc_card_ddr52(struct mmc_card *card) { return card->host->ios.timing == MMC_TIMING_MMC_DDR52; } + +static inline bool mmc_card_hs400(struct mmc_card *card) +{ + return card->host->ios.timing == MMC_TIMING_MMC_HS400; +} + #endif /* LINUX_MMC_HOST_H */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index f429f13..64ec963 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -325,6 +325,7 @@ struct _mmc_csd { #define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ +#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ #define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ #define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ #define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ @@ -354,7 +355,6 @@ struct _mmc_csd { #define EXT_CSD_CMD_SET_SECURE (1<<1) #define EXT_CSD_CMD_SET_CPSECURE (1<<2) -#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */ #define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */ #define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */ #define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \ @@ -370,6 +370,10 @@ struct _mmc_csd { /* SDR mode @1.2V I/O */ #define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ EXT_CSD_CARD_TYPE_HS200_1_2V) +#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz DDR, 1.8V */ +#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */ +#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ + EXT_CSD_CARD_TYPE_HS400_1_2V) #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ @@ -380,6 +384,7 @@ struct _mmc_csd { #define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ #define EXT_CSD_TIMING_HS 1 /* High speed */ #define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ #define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_BD_BLK_EN BIT(2)