From patchwork Wed Mar 26 11:31:44 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Seungwon Jeon X-Patchwork-Id: 3892901 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 75C90BF540 for ; Wed, 26 Mar 2014 11:32:07 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2847F2021A for ; Wed, 26 Mar 2014 11:32:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E0D4C20172 for ; Wed, 26 Mar 2014 11:32:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751614AbaCZLcB (ORCPT ); Wed, 26 Mar 2014 07:32:01 -0400 Received: from mailout2.samsung.com ([203.254.224.25]:48477 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751147AbaCZLcA (ORCPT ); Wed, 26 Mar 2014 07:32:00 -0400 Received: from epcpsbgr5.samsung.com (u145.gpu120.samsung.co.kr [203.254.230.145]) by mailout2.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0N310017NK0XJI30@mailout2.samsung.com>; Wed, 26 Mar 2014 20:31:45 +0900 (KST) Received: from epcpsbgm1.samsung.com ( [203.254.230.51]) by epcpsbgr5.samsung.com (EPCPMTA) with SMTP id 4B.1F.14803.1AAB2335; Wed, 26 Mar 2014 20:31:45 +0900 (KST) X-AuditID: cbfee691-b7efc6d0000039d3-b2-5332baa104d9 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 52.5E.29263.1AAB2335; Wed, 26 Mar 2014 20:31:45 +0900 (KST) Received: from DOTGIHJUN01 ([12.36.185.168]) by mmp2.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0N3100AWRK0XHF60@mmp2.samsung.com>; Wed, 26 Mar 2014 20:31:45 +0900 (KST) From: Seungwon Jeon To: linux-samsung-soc@vger.kernel.org, linux-mmc@vger.kernel.org Cc: 'Chris Ball' , 'Kukjin Kim' , 'Jaehoon Chung' , 'Ulf Hansson' , 'Alim Akhtar' References: In-reply-to: Subject: [PATCH v2 5/7] mmc: dw_mmc: exynos: support eMMC's HS400 mode Date: Wed, 26 Mar 2014 20:31:44 +0900 Message-id: <000501cf48e6$f714b0c0$e53e1240$%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: Ac9FDjtZo1aMTMCOStGdx+bxiO4TmwD1P6hw Content-language: ko X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrBIsWRmVeSWpSXmKPExsVy+t8zY92Fu4yCDf6dl7d4MG8bm8WEy9sZ LW78amO16F1wlc3iyP9+RosZ5/cxWRxfG+7A7nHn2h42jxuvFjJ59G1ZxejxeZNcAEsUl01K ak5mWWqRvl0CV0bfp5XsBU88KzY+PsjSwLjHpouRk0NCwETixba7TBC2mMSFe+vZuhi5OIQE ljFKNPbcYIcper92DTtEYjqjxML+XijnD6PE2Rvr2ECq2AS0JP6+ecMMYosIOEi8u34BbCyz wAlGiXmLjbsYOYAauCVWNweBhDkFeCTW/ekEKxcWcJO4+vYkC4jNIqAqsf/MZrCRvAK2Egv+ /2eHsAUlfky+xwIyhllAXWLKlFyI6fISm9e8ZQYJSwCFH/3VhTjASKK9YSIzRImIxL4X7xgh XnnJLvHllSbEJgGJb5MPsUC0ykpsOsAMUSIpcXDFDZYJjBKzkOydhbB3FpK9s5AsWMDIsopR NLUguaA4Kb3IVK84Mbe4NC9dLzk/dxMjJFYn7mC8f8D6EGMy0PaJzFKiyfnAWM8riTc0NjOy MDUxNTYytzQjTVhJnDf9UVKQkEB6YklqdmpqQWpRfFFpTmrxIUYmDk6pBsYOW3mdrkNc+lLu N14bKGwyld8XbR14tfFd2bSwEoXybna2/OrOdTNDWz0KdFo4ti79zXt4eu0UdvFTNQ7Rtjte LE1ku6u7uOer4RlP/fW+bfOufC6Vtj/KYq/Z+8qW4z5z/XO9vOuvnduNtn3+YniAO/r+SsWT Mjcyz7in60/Ncrje561jqsRSnJFoqMVcVJwIANysiBvrAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrGKsWRmVeSWpSXmKPExsVy+t9jQd2Fu4yCDX5tlLJ4MG8bm8WEy9sZ LW78amO16F1wlc3iyP9+RosZ5/cxWRxfG+7A7nHn2h42jxuvFjJ59G1ZxejxeZNcAEtUA6NN RmpiSmqRQmpecn5KZl66rZJ3cLxzvKmZgaGuoaWFuZJCXmJuqq2Si0+ArltmDtAFSgpliTml QKGAxOJiJX07TBNCQ9x0LWAaI3R9Q4LgeowM0EDCOsaMvk8r2QueeFZsfHyQpYFxj00XIyeH hICJxPu1a9ghbDGJC/fWs3UxcnEICUxnlFjY38sO4fxhlDh7Yx0bSBWbgJbE3zdvmEFsEQEH iXfXLzCB2MwCJxgl5i027mLkAGrglljdHAQS5hTgkVj3pxOsXFjATeLq25MsIDaLgKrE/jOb wUbyCthKLPj/nx3CFpT4MfkeC8gYZgF1iSlTciGmy0tsXvOWGSQsARR+9FcX4gAjifaGicwQ JSIS+168Y5zAKDQLyaBZCINmIRk0C0nHAkaWVYyiqQXJBcVJ6bmGesWJucWleel6yfm5mxjB qeCZ1A7GlQ0WhxgFOBiVeHgnTDAMFmJNLCuuzD3EKMHBrCTCu2OtUbAQb0piZVVqUX58UWlO avEhxmSgNycyS4km5wPTVF5JvKGxiZmRpZGZhZGJuTlpwkrivAdarQOFBNITS1KzU1MLUotg tjBxcEo1MDbub8rzyvMWdU29sCNykVnVo9Qlpxsu1Dx8wWn9UHDJ99ov52S6VQx/rxR9vr+s V3wpe2PFEaEWafuFjnNrXi7ezLNM5PTRzYuF/upkeVzs6X7Z8NpW/MR+W0aBrLL3qmxaSccv 71j5dnVZ60ndErWTFWpHTGc+ObLkTcUbRantJyo6wmcf6lViKc5INNRiLipOBADNnF6fSQMA AA== 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.3 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 Implements HS400 support for exynos host driver. And this patch includes some updates as new mode is added. Signed-off-by: Seungwon Jeon --- drivers/mmc/host/dw_mmc-exynos.c | 140 +++++++++++++++++++++++++++++++------- drivers/mmc/host/dw_mmc-exynos.h | 14 ++++ drivers/mmc/host/dw_mmc.c | 3 +- 3 files changed, 130 insertions(+), 27 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 39f9114..78f3b07 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -40,7 +40,12 @@ struct dw_mci_exynos_priv_data { u32 sdr_timing; u32 ddr_timing; u32 hs200_timing; + u32 hs400_timing; + u32 tuned_sample; u32 cur_speed; + u32 dqs_delay; + u32 saved_dqs_en; + u32 saved_strobe_ctrl; }; static struct dw_mci_exynos_compatible { @@ -90,6 +95,16 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT); } + if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) { + priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL); + priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN); + priv->saved_dqs_en |= AXI_NON_BLOCKING_WR; + mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en); + if (!priv->dqs_delay) + priv->dqs_delay = + DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl); + } + priv->ciu_div = dw_mci_exynos_get_ciu_div(host); return 0; @@ -104,6 +119,14 @@ static int dw_mci_exynos_setup_clock(struct dw_mci *host) return 0; } +static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) +{ + u32 clksel; + clksel = mci_readl(host, CLKSEL); + clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing; + mci_writel(host, CLKSEL, clksel); +} + #ifdef CONFIG_PM_SLEEP static int dw_mci_exynos_suspend(struct device *dev) { @@ -161,23 +184,39 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) +static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) { struct dw_mci_exynos_priv_data *priv = host->priv; - unsigned int wanted = ios->clock; - unsigned long actual; + u32 dqs, strobe; - if (ios->timing == MMC_TIMING_MMC_HS200) { - mci_writel(host, CLKSEL, priv->hs200_timing); - } else if (ios->timing == MMC_TIMING_MMC_DDR52) { - mci_writel(host, CLKSEL, priv->ddr_timing); - /* Should be double rate for DDR mode */ - if (ios->bus_width == MMC_BUS_WIDTH_8) - wanted <<= 1; + /* + * Not suppported to configure register + * related to HS400 + */ + if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) + return; + + dqs = priv->saved_dqs_en; + strobe = priv->saved_strobe_ctrl; + + if (timing == MMC_TIMING_MMC_HS400) { + dqs |= DATA_STROBE_EN; + strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay); } else { - mci_writel(host, CLKSEL, priv->sdr_timing); + dqs &= ~DATA_STROBE_EN; } + mci_writel(host, HS400_DQS_EN, dqs); + mci_writel(host, HS400_DLINE_CTRL, strobe); +} + +static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned long actual; + u8 div; + int ret; + /* * Don't care if wanted clock is zero or * ciu clock is unavailable @@ -189,18 +228,57 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) if (wanted < EXYNOS_CCLKIN_MIN) wanted = EXYNOS_CCLKIN_MIN; - if (wanted != priv->cur_speed) { - u8 div = dw_mci_exynos_get_ciu_div(host); - int ret = clk_set_rate(host->ciu_clk, wanted * div); - if (ret) - dev_warn(host->dev, - "failed to set clk-rate %u error: %d\n", - wanted * div, ret); - actual = clk_get_rate(host->ciu_clk); - host->bus_hz = actual / div; - priv->cur_speed = wanted; - host->current_speed = 0; + if (wanted == priv->cur_speed) + return; + + div = dw_mci_exynos_get_ciu_div(host); + ret = clk_set_rate(host->ciu_clk, wanted * div); + if (ret) + dev_warn(host->dev, + "failed to set clk-rate %u error: %d\n", + wanted * div, ret); + actual = clk_get_rate(host->ciu_clk); + host->bus_hz = actual / div; + priv->cur_speed = wanted; + host->current_speed = 0; +} + +static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned int wanted = ios->clock; + u32 timing = ios->timing, clksel; + + switch (timing) { + case MMC_TIMING_MMC_HS400: + /* Update tuned sample timing */ + priv->hs400_timing = SDMMC_CLKSEL_UP_SAMPLE( + priv->hs400_timing, priv->tuned_sample); + case MMC_TIMING_MMC_HS400_TUNING: + clksel = priv->hs400_timing; + wanted <<= 1; + break; + case MMC_TIMING_MMC_HS200: + clksel = priv->hs200_timing; + break; + case MMC_TIMING_MMC_DDR52: + clksel = priv->ddr_timing; + /* Should be double rate for DDR mode */ + if (ios->bus_width == MMC_BUS_WIDTH_8) + wanted <<= 1; + break; + default: + clksel = priv->sdr_timing; } + + /* Set clock timing for the requested speed mode*/ + dw_mci_exynos_set_clksel_timing(host, clksel); + + /* Configure setting for HS400 */ + dw_mci_exynos_config_hs400(host, timing); + + /* Configure clock rate */ + dw_mci_exynos_adjust_clock(host, wanted); } static int dw_mci_exynos_dt_populate_timing(struct dw_mci *host, @@ -256,6 +334,13 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) dw_mci_exynos_dt_populate_timing(host, priv->ctrl_type, "samsung,dw-mshc-hs200-timing", &priv->hs200_timing); + ret = dw_mci_exynos_dt_populate_timing(host, priv->ctrl_type, + "samsung,dw-mshc-hs400-timing", &priv->hs400_timing); + if (!ret && of_property_read_u32(np, + "read-strobe-delay", &priv->dqs_delay)) + dev_info(host->dev, + "read-strobe-delay is not found, assuming usage of default value\n"); + host->priv = priv; return 0; @@ -270,7 +355,7 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) { u32 clksel; clksel = mci_readl(host, CLKSEL); - clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); + clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample); mci_writel(host, CLKSEL, clksel); } @@ -281,7 +366,7 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) clksel = mci_readl(host, CLKSEL); sample = (clksel + 1) & 0x7; - clksel = (clksel & ~0x7) | sample; + clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample); mci_writel(host, CLKSEL, clksel); return sample; } @@ -316,6 +401,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, struct dw_mci_tuning_data *tuning_data) { struct dw_mci *host = slot->host; + struct dw_mci_exynos_priv_data *priv = host->priv; struct mmc_host *mmc = slot->mmc; const u8 *blk_pattern = tuning_data->blk_pattern; u8 *blk_test; @@ -373,10 +459,12 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, } while (start_smpl != smpl); found = dw_mci_exynos_get_best_clksmpl(candiates); - if (found >= 0) + if (found >= 0) { dw_mci_exynos_set_clksmpl(host, found); - else + priv->tuned_sample = found; + } else { ret = -EIO; + } kfree(blk_test); return ret; diff --git a/drivers/mmc/host/dw_mmc-exynos.h b/drivers/mmc/host/dw_mmc-exynos.h index 2c8c228..ccf1ba1 100644 --- a/drivers/mmc/host/dw_mmc-exynos.h +++ b/drivers/mmc/host/dw_mmc-exynos.h @@ -14,6 +14,9 @@ /* Extended Register's Offset */ #define SDMMC_CLKSEL 0x09C +#define SDMMC_HS400_DQS_EN 0x180 +#define SDMMC_HS400_ASYNC_FIFO_CTRL 0x184 +#define SDMMC_HS400_DLINE_CTRL 0x188 /* CLKSEL register defines */ #define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) @@ -21,11 +24,22 @@ #define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) #define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7) #define SDMMC_CLKSEL_GET_DIV(x) (((x) >> 24) & 0x7) +#define SDMMC_CLKSEL_UP_SAMPLE(x, y) (((x) & ~SDMMC_CLKSEL_CCLK_SAMPLE(7)) |\ + SDMMC_CLKSEL_CCLK_SAMPLE(y)) #define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ SDMMC_CLKSEL_CCLK_DRIVE(y) | \ SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_TIMING_MASK SDMMC_CLKSEL_TIMING(0x7, 0x7, 0x7) #define SDMMC_CLKSEL_WAKEUP_INT BIT(11) +/* HS400 control defines */ +#define DATA_STROBE_EN BIT(0) +#define AXI_NON_BLOCKING_WR BIT(7) + +/* Delay Line Control defines */ +#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF)) +#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF) + /* Protector Register */ #define SDMMC_EMMCP_BASE 0x1000 #define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index aeb38f9..32dd81d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -953,7 +953,8 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) regs = mci_readl(slot->host, UHS_REG); /* DDR mode set */ - if (ios->timing == MMC_TIMING_MMC_DDR52) + if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400) regs |= ((0x1 << slot->id) << 16); else regs &= ~((0x1 << slot->id) << 16);