From patchwork Mon Oct 8 12:58:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630685 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 264E314DB for ; Mon, 8 Oct 2018 12:58:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1640D28591 for ; Mon, 8 Oct 2018 12:58:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 09C7F285D8; Mon, 8 Oct 2018 12:58:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9257228591 for ; Mon, 8 Oct 2018 12:58:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726523AbeJHUKK (ORCPT ); Mon, 8 Oct 2018 16:10:10 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:48612 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726014AbeJHUKK (ORCPT ); Mon, 8 Oct 2018 16:10:10 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 3BAC360BFA; Mon, 8 Oct 2018 12:58:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003514; bh=5cYhOuesC706GUAgOUXoSmE00Bwr9g+9IrGNf3kUexE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N0dOFLJgTrzUwXh7LApPPEDA4xFNHMqE1VNBGEq76EVB/0kN0TPhZQkDRyb6GFZzw BaXrh5U8ThkTWXfUPxj5M3r4fM5/P01xfPAaDibRfJujlGp/EMaCti84dCo8Iwj8LL w8DYnl1is8cgeb2WIiBsWILl94mIo2XAAgurJFiQ= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id EDE4160C6A; Mon, 8 Oct 2018 12:58:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003513; bh=5cYhOuesC706GUAgOUXoSmE00Bwr9g+9IrGNf3kUexE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F8inf5hmv5PFbRP2oB+fJZmfDaVrjXzEqwM0UYtV6zOXWsWoLGHyH6s72S+lmT7SJ 6BKrPZ6BnUPVtghBquBelnto4Dw6Zo/KjZWgLz4FpC6Xv4+zeuAaDKC7NFXCVkKtEK 7TLO9hnEgoA0LDDPgFR8FFE1QxTEIQ9yXBukzeIA= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org EDE4160C6A Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org Subject: [PATCH V1 1/7] devfreq: Add new flag to do simple clock scaling Date: Mon, 8 Oct 2018 18:28:00 +0530 Message-Id: <1539003486-24087-2-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sahitya Tummala Add new flag "simple_scaling" to on demand governor so that the clocks can be scaled up only when the load is more than up threshold and can be scaled down only when the load is less than down differential data as provided within struct devfreq_simple_ondemand_data. Signed-off-by: Sahitya Tummala Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Sayali Lokhande --- drivers/devfreq/governor_simpleondemand.c | 25 +++++++++++++++++++------ include/linux/devfreq.h | 4 ++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 28e0f2d..532801b 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -28,6 +28,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; struct devfreq_simple_ondemand_data *data = df->data; unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX; + unsigned long min = (df->min_freq) ? df->min_freq : 0; err = devfreq_update_stats(df); if (err) @@ -45,18 +46,30 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, dfso_upthreshold < dfso_downdifferential) return -EINVAL; - /* Assume MAX if it is going to be divided by zero */ - if (stat->total_time == 0) { - *freq = max; - return 0; - } - /* Prevent overflow */ if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) { stat->busy_time >>= 7; stat->total_time >>= 7; } + if (data && data->simple_scaling) { + if (stat->busy_time * 100 > + stat->total_time * dfso_upthreshold) + *freq = max; + else if (stat->busy_time * 100 < + stat->total_time * dfso_downdifferential) + *freq = min; + else + *freq = df->previous_freq; + return 0; + } + + /* Assume MAX if it is going to be divided by zero */ + if (stat->total_time == 0) { + *freq = max; + return 0; + } + /* Set MAX if it's busy enough */ if (stat->busy_time * 100 > stat->total_time * dfso_upthreshold) { diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 3aae5b3..2957fd8 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -236,6 +236,9 @@ extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, * the governor may consider slowing the frequency down. * Specify 0 to use the default. Valid value = 0 to 100. * downdifferential < upthreshold must hold. + * @simple_scaling: Setting this flag will scale the clocks up only if the + * load is above @upthreshold and will scale the clocks + * down only if the load is below @downdifferential. * * If the fed devfreq_simple_ondemand_data pointer is NULL to the governor, * the governor uses the default values. @@ -243,6 +246,7 @@ extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, struct devfreq_simple_ondemand_data { unsigned int upthreshold; unsigned int downdifferential; + unsigned int simple_scaling; }; #endif From patchwork Mon Oct 8 12:58:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630689 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 70EC314DB for ; Mon, 8 Oct 2018 12:58:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5D40A28E7D for ; Mon, 8 Oct 2018 12:58:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5074128E9A; Mon, 8 Oct 2018 12:58:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D686E28E7D for ; Mon, 8 Oct 2018 12:58:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726275AbeJHUK0 (ORCPT ); Mon, 8 Oct 2018 16:10:26 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:48846 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726014AbeJHUK0 (ORCPT ); Mon, 8 Oct 2018 16:10:26 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id D890760316; Mon, 8 Oct 2018 12:58:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003527; bh=b2UFf2oRbbMffOCowzJTDegr8RrHwMVuodDtTlYklMo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Awg/oVjvTyEwOq5J7uJE8LQ/pcdobEUAZYx66dJE8KJTwD0CUz8+hTKVOma+Y3Woq pYV0p8Fdd2+8uABvPsXmiX9ddUeAxmowwjIFv2qrGNNmDo+h9IoaUdANpTh3BKefTZ tfWAxKL8opkAP90xjXYSOsxARzhuBquxX4T9SVKM= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 00D8F60C64; Mon, 8 Oct 2018 12:58:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003523; bh=b2UFf2oRbbMffOCowzJTDegr8RrHwMVuodDtTlYklMo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EwyI9b0ha47n79bU8q2j5EBRyRO6SmUkprTazEJ12mq82xsa8Gse8/qGcsoJEktST eL46bgxAK8yNbaOneR7CNfxWfuqqJeRJvY/O2gM2XCbA++dfWikc7j7Mj5j0JRVB7c eSTwO2k+iGYFsurY6GmEZfZnwemxiP64tq+vKVXg= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 00D8F60C64 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org, Talel Shenhar Subject: [PATCH V1 2/7] mmc: core: devfreq: Add devfreq based clock scaling support Date: Mon, 8 Oct 2018 18:28:01 +0530 Message-Id: <1539003486-24087-3-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This change adds the use of devfreq to MMC. Both eMMC and SD card will use it. For some workloads, such as video playback, it isn't necessary for these cards to run at high speed. Running at lower frequency, for example 52MHz, in such cases can still meet the deadlines for data transfers. Scaling down the clock frequency dynamically has power savings not only because the bus is running at lower frequency but also has an advantage of scaling down the system core voltage, if supported. Provide an ondemand clock scaling support similar to the cpufreq ondemand governor having two thresholds, up_threshold and down_threshold to decide whether to increase the frequency or scale it down respectively. The sampling interval is in the order of milliseconds. If sampling interval is too low, frequent switching of frequencies can lead to high power consumption and if sampling interval is too high, the clock scaling logic would take long time to realize that the underlying hardware (controller and card) is busy and scale up the clocks. Signed-off-by: Talel Shenhar Signed-off-by: Sayali Lokhande --- .../devicetree/bindings/mmc/sdhci-msm.txt | 10 + drivers/mmc/core/core.c | 556 +++++++++++++++++++++ drivers/mmc/core/core.h | 7 + drivers/mmc/core/debugfs.c | 46 ++ drivers/mmc/core/host.c | 8 + drivers/mmc/core/mmc.c | 200 +++++++- drivers/mmc/core/sd.c | 72 ++- drivers/mmc/host/sdhci-msm.c | 37 ++ drivers/mmc/host/sdhci-pltfm.c | 11 + drivers/mmc/host/sdhci.c | 27 + drivers/mmc/host/sdhci.h | 8 + include/linux/mmc/card.h | 5 + include/linux/mmc/host.h | 70 +++ 13 files changed, 1055 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 502b3b8..bd8470a 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -26,6 +26,15 @@ Required properties: "cal" - reference clock for RCLK delay calibration (optional) "sleep" - sleep clock for RCLK delay calibration (optional) +Optional Properties: +- qcom,devfreq,freq-table - specifies supported frequencies for clock scaling. + Clock scaling logic shall toggle between these frequencies based + on card load. In case the defined frequencies are over or below + the supported card frequencies, they will be overridden + during card init. In case this entry is not supplied, + the driver will construct one based on the card + supported max and min frequencies. + The frequencies must be ordered from lowest to highest. Example: sdhc_1: sdhci@f9824900 { @@ -43,6 +52,7 @@ Example: clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; clock-names = "core", "iface"; + qcom,devfreq,freq-table = <52000000 200000000>; }; sdhc_2: sdhci@f98a4900 { diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 281826d..a3d4a92 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,552 @@ static inline void mmc_should_fail_request(struct mmc_host *host, #endif /* CONFIG_FAIL_MMC_REQUEST */ +static bool mmc_is_data_request(struct mmc_request *mmc_request) +{ + switch (mmc_request->cmd->opcode) { + case MMC_READ_SINGLE_BLOCK: + case MMC_READ_MULTIPLE_BLOCK: + case MMC_WRITE_BLOCK: + case MMC_WRITE_MULTIPLE_BLOCK: + return true; + default: + return false; + } +} + +static void mmc_clk_scaling_start_busy(struct mmc_host *host, bool lock_needed) +{ + struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling; + + if (!clk_scaling->enable) + return; + + if (lock_needed) + spin_lock_bh(&clk_scaling->lock); + + clk_scaling->start_busy = ktime_get(); + clk_scaling->is_busy_started = true; + + if (lock_needed) + spin_unlock_bh(&clk_scaling->lock); +} + +static void mmc_clk_scaling_stop_busy(struct mmc_host *host, bool lock_needed) +{ + struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling; + + if (!clk_scaling->enable) + return; + + if (lock_needed) + spin_lock_bh(&clk_scaling->lock); + + if (!clk_scaling->is_busy_started) { + WARN_ON(1); + goto out; + } + + clk_scaling->total_busy_time_us += + ktime_to_us(ktime_sub(ktime_get(), + clk_scaling->start_busy)); + pr_debug("%s: accumulated busy time is %lu usec\n", + mmc_hostname(host), clk_scaling->total_busy_time_us); + clk_scaling->is_busy_started = false; + +out: + if (lock_needed) + spin_unlock_bh(&clk_scaling->lock); +} + +/** + * mmc_can_scale_clk() - Check clock scaling capability + * @host: pointer to mmc host structure + */ +bool mmc_can_scale_clk(struct mmc_host *host) +{ + if (!host) { + pr_err("bad host parameter\n"); + WARN_ON(1); + return false; + } + + return host->caps2 & MMC_CAP2_CLK_SCALE; +} +EXPORT_SYMBOL(mmc_can_scale_clk); + +static int mmc_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *status) +{ + struct mmc_host *host = container_of(dev, struct mmc_host, class_dev); + struct mmc_devfeq_clk_scaling *clk_scaling; + + if (!host) { + pr_err("bad host parameter\n"); + WARN_ON(1); + return -EINVAL; + } + + clk_scaling = &host->clk_scaling; + + if (!clk_scaling->enable) + return 0; + + spin_lock_bh(&clk_scaling->lock); + + /* accumulate the busy time of ongoing work */ + memset(status, 0, sizeof(*status)); + if (clk_scaling->is_busy_started) { + mmc_clk_scaling_stop_busy(host, false); + mmc_clk_scaling_start_busy(host, false); + } + + status->busy_time = clk_scaling->total_busy_time_us; + status->total_time = ktime_to_us(ktime_sub(ktime_get(), + clk_scaling->measure_interval_start)); + clk_scaling->total_busy_time_us = 0; + status->current_frequency = clk_scaling->curr_freq; + clk_scaling->measure_interval_start = ktime_get(); + + pr_debug("%s: status: load = %lu%% - total_time=%lu busy_time = %lu, clk=%lu\n", + mmc_hostname(host), + (status->busy_time*100)/status->total_time, + status->total_time, status->busy_time, + status->current_frequency); + + spin_unlock_bh(&clk_scaling->lock); + + return 0; +} + +static bool mmc_is_valid_state_for_clk_scaling(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + u32 status; + + /* + * If the current partition type is RPMB, clock switching may not + * work properly as sending tuning command (CMD21) is illegal in + * this mode. + */ + if (!card || (mmc_card_mmc(card) && + (card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB || + mmc_card_doing_bkops(card)))) + return false; + + if (mmc_send_status(card, &status)) { + pr_err("%s: Get card status fail\n", mmc_hostname(card->host)); + return false; + } + + return R1_CURRENT_STATE(status) == R1_STATE_TRAN; +} + +int mmc_clk_update_freq(struct mmc_host *host, + unsigned long freq, enum mmc_load state) +{ + int err = 0; + + if (!host) { + pr_err("bad host parameter\n"); + WARN_ON(1); + return -EINVAL; + } + + /* make sure the card supports the frequency we want */ + if (unlikely(freq > host->card->clk_scaling_highest)) { + freq = host->card->clk_scaling_highest; + pr_warn("%s: %s: frequency was overridden to %lu\n", + mmc_hostname(host), __func__, + host->card->clk_scaling_highest); + } + + if (unlikely(freq < host->card->clk_scaling_lowest)) { + freq = host->card->clk_scaling_lowest; + pr_warn("%s: %s: frequency was overridden to %lu\n", + mmc_hostname(host), __func__, + host->card->clk_scaling_lowest); + } + + if (freq == host->clk_scaling.curr_freq) + goto out; + + if (host->ops->notify_load) { + err = host->ops->notify_load(host, state); + if (err) { + pr_err("%s: %s: fail on notify_load\n", + mmc_hostname(host), __func__); + goto out; + } + } + + if (!mmc_is_valid_state_for_clk_scaling(host)) { + pr_debug("%s: invalid state for clock scaling - skipping", + mmc_hostname(host)); + goto invalid_state; + } + + err = host->bus_ops->change_bus_speed(host, &freq); + if (!err) + host->clk_scaling.curr_freq = freq; + else + pr_err("%s: %s: failed (%d) at freq=%lu\n", + mmc_hostname(host), __func__, err, freq); + +invalid_state: + if (err) { + /* restore previous state */ + if (host->ops->notify_load) + if (host->ops->notify_load(host, + host->clk_scaling.state)) + pr_err("%s: %s: fail on notify_load restore\n", + mmc_hostname(host), __func__); + } +out: + return err; +} +EXPORT_SYMBOL(mmc_clk_update_freq); + +static int mmc_devfreq_set_target(struct device *dev, + unsigned long *freq, u32 devfreq_flags) +{ + struct mmc_host *host = container_of(dev, struct mmc_host, class_dev); + struct mmc_devfeq_clk_scaling *clk_scaling; + int err = 0; + int abort; + unsigned long pflags = current->flags; + + /* Ensure scaling would happen even in memory pressure conditions */ + current->flags |= PF_MEMALLOC; + + if (!(host && freq)) { + pr_err("%s: unexpected host/freq parameter\n", __func__); + err = -EINVAL; + goto out; + } + + clk_scaling = &host->clk_scaling; + + if (!clk_scaling->enable) + goto out; + + pr_debug("%s: target freq = %lu (%s)\n", mmc_hostname(host), + *freq, current->comm); + + /* No need to scale the clocks if they are gated */ + if (!host->ios.clock) + goto out; + + spin_lock_bh(&clk_scaling->lock); + if (clk_scaling->target_freq == *freq || + clk_scaling->skip_clk_scale_freq_update || + clk_scaling->clk_scaling_in_progress) { + spin_unlock_bh(&clk_scaling->lock); + goto out; + } + clk_scaling->need_freq_change = true; + clk_scaling->target_freq = *freq; + clk_scaling->state = *freq < clk_scaling->curr_freq ? + MMC_LOAD_LOW : MMC_LOAD_HIGH; + spin_unlock_bh(&clk_scaling->lock); + + abort = __mmc_claim_host(host, NULL, &clk_scaling->devfreq_abort); + if (abort) + goto out; + + /* + * In case we were able to claim host there is no need to + * defer the frequency change. It will be done now + */ + clk_scaling->need_freq_change = false; + + err = mmc_clk_update_freq(host, *freq, clk_scaling->state); + if (err && err != -EAGAIN) { + pr_err("%s: clock scale to %lu failed with error %d\n", + mmc_hostname(host), *freq, err); + } else { + pr_debug("%s: clock change to %lu finished successfully (%s)\n", + mmc_hostname(host), *freq, current->comm); + } + + mmc_release_host(host); +out: + current->flags &= ~PF_MEMALLOC; + current->flags |= pflags & PF_MEMALLOC; + return err; +} + +/** + * mmc_deferred_scaling() - scale clocks from data path (mmc thread context) + * @host: pointer to mmc host structure + * + * This function does clock scaling in case "need_freq_change" flag was set + * by the clock scaling logic. + */ +void mmc_deferred_scaling(struct mmc_host *host) +{ + unsigned long target_freq; + int err; + + if (!host->clk_scaling.enable) + return; + + spin_lock_bh(&host->clk_scaling.lock); + + if (host->clk_scaling.clk_scaling_in_progress || + !(host->clk_scaling.need_freq_change)) { + spin_unlock_bh(&host->clk_scaling.lock); + return; + } + + + atomic_inc(&host->clk_scaling.devfreq_abort); + target_freq = host->clk_scaling.target_freq; + host->clk_scaling.clk_scaling_in_progress = true; + host->clk_scaling.need_freq_change = false; + spin_unlock_bh(&host->clk_scaling.lock); + pr_debug("%s: doing deferred frequency change (%lu) (%s)\n", + mmc_hostname(host), + target_freq, current->comm); + + err = mmc_clk_update_freq(host, target_freq, + host->clk_scaling.state); + if (err && err != -EAGAIN) { + pr_err("%s: failed on deferred scale clocks (%d)\n", + mmc_hostname(host), err); + } else { + pr_debug("%s: clocks were successfully scaled to %lu (%s)\n", + mmc_hostname(host), + target_freq, current->comm); + } + host->clk_scaling.clk_scaling_in_progress = false; + atomic_dec(&host->clk_scaling.devfreq_abort); +} +EXPORT_SYMBOL(mmc_deferred_scaling); + +static int mmc_devfreq_create_freq_table(struct mmc_host *host) +{ + int i; + struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling; + + pr_debug("%s: supported: lowest=%lu, highest=%lu\n", + mmc_hostname(host), + host->card->clk_scaling_lowest, + host->card->clk_scaling_highest); + + /* + * Create the frequency table and initialize it with default values. + * Initialize it with platform specific frequencies if the frequency + * table supplied by platform driver is present, otherwise initialize + * it with min and max frequencies supported by the card. + */ + if (!clk_scaling->freq_table) { + if (clk_scaling->pltfm_freq_table_sz) + clk_scaling->freq_table_sz = + clk_scaling->pltfm_freq_table_sz; + else + clk_scaling->freq_table_sz = 2; + + clk_scaling->freq_table = kzalloc( + (clk_scaling->freq_table_sz * + sizeof(*(clk_scaling->freq_table))), GFP_KERNEL); + if (!clk_scaling->freq_table) + return -ENOMEM; + + if (clk_scaling->pltfm_freq_table) { + memcpy(clk_scaling->freq_table, + clk_scaling->pltfm_freq_table, + (clk_scaling->pltfm_freq_table_sz * + sizeof(*(clk_scaling->pltfm_freq_table)))); + } else { + pr_debug("%s: no frequency table defined - setting default\n", + mmc_hostname(host)); + clk_scaling->freq_table[0] = + host->card->clk_scaling_lowest; + clk_scaling->freq_table[1] = + host->card->clk_scaling_highest; + goto out; + } + } + + if (host->card->clk_scaling_lowest > + clk_scaling->freq_table[0]) + pr_debug("%s: frequency table undershot possible freq\n", + mmc_hostname(host)); + + for (i = 0; i < clk_scaling->freq_table_sz; i++) { + if (clk_scaling->freq_table[i] <= + host->card->clk_scaling_highest) + continue; + clk_scaling->freq_table[i] = + host->card->clk_scaling_highest; + clk_scaling->freq_table_sz = i + 1; + pr_debug("%s: frequency table overshot possible freq (%d)\n", + mmc_hostname(host), clk_scaling->freq_table[i]); + break; + } + +out: + /** + * devfreq requires unsigned long type freq_table while the + * freq_table in clk_scaling is un32. Here allocates an individual + * memory space for it and release it when exit clock scaling. + */ + clk_scaling->devfreq_profile.freq_table = kzalloc( + clk_scaling->freq_table_sz * + sizeof(*(clk_scaling->devfreq_profile.freq_table)), + GFP_KERNEL); + if (!clk_scaling->devfreq_profile.freq_table) + return -ENOMEM; + clk_scaling->devfreq_profile.max_state = clk_scaling->freq_table_sz; + + for (i = 0; i < clk_scaling->freq_table_sz; i++) { + clk_scaling->devfreq_profile.freq_table[i] = + clk_scaling->freq_table[i]; + pr_debug("%s: freq[%d] = %u\n", + mmc_hostname(host), i, clk_scaling->freq_table[i]); + } + + return 0; +} + +/** + * mmc_init_devfreq_clk_scaling() - Initialize clock scaling + * @host: pointer to mmc host structure + * + * Initialize clock scaling for supported hosts. It is assumed that the caller + * ensure clock is running at maximum possible frequency before calling this + * function. Shall use struct devfreq_simple_ondemand_data to configure + * governor. + */ +int mmc_init_clk_scaling(struct mmc_host *host) +{ + int err; + + if (!host || !host->card) { + pr_err("%s: unexpected host/card parameters\n", + __func__); + return -EINVAL; + } + + if (!mmc_can_scale_clk(host) || + !host->bus_ops->change_bus_speed) { + pr_debug("%s: clock scaling is not supported\n", + mmc_hostname(host)); + return 0; + } + + pr_debug("registering %s dev (%p) to devfreq", + mmc_hostname(host), + mmc_classdev(host)); + + if (host->clk_scaling.devfreq) { + pr_err("%s: dev is already registered for dev %p\n", + mmc_hostname(host), + mmc_dev(host)); + return -EPERM; + } + spin_lock_init(&host->clk_scaling.lock); + atomic_set(&host->clk_scaling.devfreq_abort, 0); + host->clk_scaling.curr_freq = host->ios.clock; + host->clk_scaling.clk_scaling_in_progress = false; + host->clk_scaling.need_freq_change = false; + host->clk_scaling.is_busy_started = false; + + host->clk_scaling.devfreq_profile.polling_ms = + host->clk_scaling.polling_delay_ms; + host->clk_scaling.devfreq_profile.get_dev_status = + mmc_devfreq_get_dev_status; + host->clk_scaling.devfreq_profile.target = mmc_devfreq_set_target; + host->clk_scaling.devfreq_profile.initial_freq = host->ios.clock; + + host->clk_scaling.ondemand_gov_data.simple_scaling = true; + host->clk_scaling.ondemand_gov_data.upthreshold = + host->clk_scaling.upthreshold; + host->clk_scaling.ondemand_gov_data.downdifferential = + host->clk_scaling.upthreshold - host->clk_scaling.downthreshold; + + err = mmc_devfreq_create_freq_table(host); + if (err) { + pr_err("%s: fail to create devfreq frequency table\n", + mmc_hostname(host)); + return err; + } + + pr_debug("%s: adding devfreq with: upthreshold=%u downthreshold=%u polling=%u\n", + mmc_hostname(host), + host->clk_scaling.ondemand_gov_data.upthreshold, + host->clk_scaling.ondemand_gov_data.downdifferential, + host->clk_scaling.devfreq_profile.polling_ms); + host->clk_scaling.devfreq = devfreq_add_device( + mmc_classdev(host), + &host->clk_scaling.devfreq_profile, + "simple_ondemand", + &host->clk_scaling.ondemand_gov_data); + if (!host->clk_scaling.devfreq) { + pr_err("%s: unable to register with devfreq\n", + mmc_hostname(host)); + return -EPERM; + } + + pr_debug("%s: clk scaling is enabled for device %s (%p) with devfreq %p (clock = %uHz)\n", + mmc_hostname(host), + dev_name(mmc_classdev(host)), + mmc_classdev(host), + host->clk_scaling.devfreq, + host->ios.clock); + + host->clk_scaling.enable = true; + + return err; +} +EXPORT_SYMBOL(mmc_init_clk_scaling); + +/** + * mmc_exit_devfreq_clk_scaling() - Disable clock scaling + * @host: pointer to mmc host structure + * + * Disable clock scaling permanently. + */ +int mmc_exit_clk_scaling(struct mmc_host *host) +{ + int err; + + if (!host) { + pr_err("%s: bad host parameter\n", __func__); + WARN_ON(1); + return -EINVAL; + } + + if (!mmc_can_scale_clk(host)) + return 0; + + if (!host->clk_scaling.devfreq) { + pr_err("%s: %s: no devfreq is assosiated with this device\n", + mmc_hostname(host), __func__); + return -EPERM; + } + + err = devfreq_remove_device(host->clk_scaling.devfreq); + if (err) { + pr_err("%s: remove devfreq failed (%d)\n", + mmc_hostname(host), err); + return err; + } + + kfree(host->clk_scaling.devfreq_profile.freq_table); + + host->clk_scaling.devfreq = NULL; + atomic_set(&host->clk_scaling.devfreq_abort, 1); + + kfree(host->clk_scaling.freq_table); + host->clk_scaling.freq_table = NULL; + + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); + + return 0; +} +EXPORT_SYMBOL(mmc_exit_clk_scaling); + static inline void mmc_complete_cmd(struct mmc_request *mrq) { if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion)) @@ -143,6 +690,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; int err = cmd->error; + if (host->clk_scaling.is_busy_started) + mmc_clk_scaling_stop_busy(host, true); + /* Flag re-tuning needed on CRC errors */ if ((cmd->opcode != MMC_SEND_TUNING_BLOCK && cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) && @@ -354,6 +904,12 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) return err; led_trigger_event(host->led, LED_FULL); + + if (mmc_is_data_request(mrq)) { + mmc_deferred_scaling(host); + mmc_clk_scaling_start_busy(host, true); + } + __mmc_start_request(host, mrq); return 0; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 9d8f09a..fc0a9b7 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -34,6 +34,7 @@ struct mmc_bus_ops { int (*shutdown)(struct mmc_host *); int (*hw_reset)(struct mmc_host *); int (*sw_reset)(struct mmc_host *); + int (*change_bus_speed)(struct mmc_host *, unsigned long *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); @@ -46,6 +47,8 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host, void mmc_set_chip_select(struct mmc_host *host, int mode); void mmc_set_clock(struct mmc_host *host, unsigned int hz); +int mmc_clk_update_freq(struct mmc_host *host, + unsigned long freq, enum mmc_load state); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); @@ -91,6 +94,10 @@ static inline void mmc_delay(unsigned int ms) void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); +extern bool mmc_can_scale_clk(struct mmc_host *host); +extern int mmc_init_clk_scaling(struct mmc_host *host); +extern int mmc_exit_clk_scaling(struct mmc_host *host); + int mmc_execute_tuning(struct mmc_card *card); int mmc_hs200_to_hs400(struct mmc_card *card); int mmc_hs400_to_hs200(struct mmc_card *card); diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index d2275c5..630ca8e 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -225,6 +225,43 @@ static int mmc_clock_opt_set(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set, "%llu\n"); +#include + +static int mmc_scale_get(void *data, u64 *val) +{ + struct mmc_host *host = data; + + *val = host->clk_scaling.curr_freq; + + return 0; +} + +static int mmc_scale_set(void *data, u64 val) +{ + int err = 0; + struct mmc_host *host = data; + + mmc_claim_host(host); + + /* change frequency from sysfs manually */ + err = mmc_clk_update_freq(host, val, host->clk_scaling.state); + if (err == -EAGAIN) + err = 0; + else if (err) + pr_err("%s: clock scale to %llu failed with error %d\n", + mmc_hostname(host), val, err); + else + pr_debug("%s: clock change to %llu finished successfully (%s)\n", + mmc_hostname(host), val, current->comm); + + mmc_release_host(host); + + return err; +} + +DEFINE_SIMPLE_ATTRIBUTE(mmc_scale_fops, mmc_scale_get, mmc_scale_set, + "%llu\n"); + void mmc_add_host_debugfs(struct mmc_host *host) { struct dentry *root; @@ -253,6 +290,15 @@ void mmc_add_host_debugfs(struct mmc_host *host) &mmc_clock_fops)) goto err_node; + if (!debugfs_create_file("scale", 0600, root, host, + &mmc_scale_fops)) + goto err_node; + + if (!debugfs_create_bool("skip_clk_scale_freq_update", + 0600, root, + &host->clk_scaling.skip_clk_scale_freq_update)) + goto err_node; + #ifdef CONFIG_FAIL_MMC_REQUEST if (fail_request) setup_fault_attr(&fail_default_attr, fail_request); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index abf9e88..1e46aa4 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -32,6 +32,10 @@ #include "pwrseq.h" #include "sdio_ops.h" +#define MMC_DEVFRQ_DEFAULT_UP_THRESHOLD 35 +#define MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD 5 +#define MMC_DEVFRQ_DEFAULT_POLLING_MSEC 100 + #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) static DEFINE_IDA(mmc_host_ida); @@ -435,6 +439,10 @@ int mmc_add_host(struct mmc_host *host) return err; led_trigger_register_simple(dev_name(&host->class_dev), &host->led); + host->clk_scaling.upthreshold = MMC_DEVFRQ_DEFAULT_UP_THRESHOLD; + host->clk_scaling.downthreshold = MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD; + host->clk_scaling.polling_delay_ms = MMC_DEVFRQ_DEFAULT_POLLING_MSEC; + host->clk_scaling.skip_clk_scale_freq_update = false; #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4466f5d..c8aedf3 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1526,6 +1526,170 @@ static int mmc_hs200_tuning(struct mmc_card *card) } /* + * Scale down from HS400 to HS in order to allow frequency change. + * This is needed for cards that doesn't support changing frequency in HS400 + */ +static int mmc_scale_low(struct mmc_host *host, unsigned long freq) +{ + int err = 0; + + mmc_set_timing(host, MMC_TIMING_LEGACY); + mmc_set_clock(host, MMC_HIGH_26_MAX_DTR); + + err = mmc_select_hs(host->card); + if (err) { + pr_err("%s: %s: scaling low: failed (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + err = mmc_select_bus_width(host->card); + if (err < 0) { + pr_err("%s: %s: select_bus_width failed(%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + mmc_set_clock(host, freq); + + return 0; +} + +/* + * Scale UP from HS to HS200/H400 + */ +static int mmc_scale_high(struct mmc_host *host) +{ + int err = 0; + + if (mmc_card_ddr52(host->card)) { + mmc_set_timing(host, MMC_TIMING_LEGACY); + mmc_set_clock(host, MMC_HIGH_26_MAX_DTR); + } + + if (!host->card->ext_csd.strobe_support) { + if (!(host->card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)) { + pr_err("%s: %s: card does not support HS200\n", + mmc_hostname(host), __func__); + WARN_ON(1); + return -EPERM; + } + + err = mmc_select_hs200(host->card); + if (err) { + pr_err("%s: %s: selecting HS200 failed (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + mmc_set_bus_speed(host->card); + + err = mmc_hs200_tuning(host->card); + if (err) { + pr_err("%s: %s: hs200 tuning failed (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + if (!(host->card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400)) { + pr_debug("%s: card does not support HS400\n", + mmc_hostname(host)); + return 0; + } + } + + err = mmc_select_hs400(host->card); + if (err) { + pr_err("%s: %s: select hs400 failed (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + + return err; +} + +static int mmc_set_clock_bus_speed(struct mmc_card *card, unsigned long freq) +{ + int err = 0; + + if (freq == MMC_HS200_MAX_DTR) + err = mmc_scale_high(card->host); + else + err = mmc_scale_low(card->host, freq); + + return err; +} + +static inline unsigned long mmc_ddr_freq_accommodation(unsigned long freq) +{ + if (freq == MMC_HIGH_DDR_MAX_DTR) + return freq; + + return freq/2; +} + +/** + * mmc_change_bus_speed() - Change MMC card bus frequency at runtime + * @host: pointer to mmc host structure + * @freq: pointer to desired frequency to be set + * + * Change the MMC card bus frequency at runtime after the card is + * initialized. Callers are expected to make sure of the card's + * state (DATA/RCV/TRANSFER) before changing the frequency at runtime. + * + * If the frequency to change is greater than max. supported by card, + * *freq is changed to max. supported by card. If it is less than min. + * supported by host, *freq is changed to min. supported by host. + * Host is assumed to be calimed while calling this funciton. + */ +static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq) +{ + int err = 0; + struct mmc_card *card; + unsigned long actual_freq; + + card = host->card; + + if (!card || !freq) { + err = -EINVAL; + goto out; + } + actual_freq = *freq; + + WARN_ON(!host->claimed); + + /* + * For scaling up/down HS400 we'll need special handling, + * for other timings we can simply do clock frequency change + */ + if (mmc_card_hs400(card) || + (!mmc_card_hs200(host->card) && *freq == MMC_HS200_MAX_DTR)) { + err = mmc_set_clock_bus_speed(card, *freq); + if (err) { + pr_err("%s: %s: failed (%d)to set bus and clock speed (freq=%lu)\n", + mmc_hostname(host), __func__, err, *freq); + goto out; + } + } else if (mmc_card_hs200(host->card)) { + mmc_set_clock(host, *freq); + err = mmc_hs200_tuning(host->card); + if (err) { + pr_warn("%s: %s: tuning execution failed %d\n", + mmc_hostname(card->host), + __func__, err); + mmc_set_clock(host, host->clk_scaling.curr_freq); + } + } else { + if (mmc_card_ddr52(host->card)) + actual_freq = mmc_ddr_freq_accommodation(*freq); + mmc_set_clock(host, actual_freq); + } + +out: + return err; +} + +/* * Handle the detection and initialisation of a card. * * In the case of a resume, "oldcard" will contain the card @@ -1751,6 +1915,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + card->clk_scaling_lowest = host->f_min; + if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) || + (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)) + card->clk_scaling_highest = card->ext_csd.hs200_max_dtr; + else if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) || + (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52)) + card->clk_scaling_highest = card->ext_csd.hs_max_dtr; + else + card->clk_scaling_highest = card->csd.max_dtr; + /* * Choose the power class with selected bus interface */ @@ -1942,6 +2116,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) */ static void mmc_remove(struct mmc_host *host) { + mmc_exit_clk_scaling(host); mmc_remove_card(host->card); host->card = NULL; } @@ -2064,6 +2239,13 @@ static int mmc_shutdown(struct mmc_host *host) int err = 0; /* + * Exit clock scaling so that it doesn't kick in after + * power off notification is sent + */ + if (host->caps2 & MMC_CAP2_CLK_SCALE) + mmc_exit_clk_scaling(host); + + /* * In a specific case for poweroff notify, we need to resume the card * before we can shutdown it properly. */ @@ -2132,6 +2314,7 @@ static int mmc_can_reset(struct mmc_card *card) static int _mmc_hw_reset(struct mmc_host *host) { struct mmc_card *card = host->card; + int ret; /* * In the case of recovery, we can't expect flushing the cache to work @@ -2151,7 +2334,15 @@ static int _mmc_hw_reset(struct mmc_host *host) mmc_power_cycle(host, card->ocr); mmc_pwrseq_reset(host); } - return mmc_init_card(host, card->ocr, card); + + ret = mmc_init_card(host, card->ocr, card); + if (ret) { + pr_err("%s: %s: mmc_init_card failed (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } + + return ret; } static const struct mmc_bus_ops mmc_ops = { @@ -2164,6 +2355,7 @@ static int _mmc_hw_reset(struct mmc_host *host) .alive = mmc_alive, .shutdown = mmc_shutdown, .hw_reset = _mmc_hw_reset, + .change_bus_speed = mmc_change_bus_speed, }; /* @@ -2220,6 +2412,12 @@ int mmc_attach_mmc(struct mmc_host *host) goto remove_card; mmc_claim_host(host); + err = mmc_init_clk_scaling(host); + if (err) { + mmc_release_host(host); + goto remove_card; + } + return 0; remove_card: diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index d0d9f90..40144c1 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -892,7 +892,10 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card) { unsigned max_dtr = (unsigned int)-1; - if (mmc_card_hs(card)) { + if (mmc_card_uhs(card)) { + if (max_dtr > card->sw_caps.uhs_max_dtr) + max_dtr = card->sw_caps.uhs_max_dtr; + } else if (mmc_card_hs(card)) { if (max_dtr > card->sw_caps.hs_max_dtr) max_dtr = card->sw_caps.hs_max_dtr; } else if (max_dtr > card->csd.max_dtr) { @@ -1059,6 +1062,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, } } + card->clk_scaling_highest = mmc_sd_get_max_clock(card); + card->clk_scaling_lowest = host->f_min; + if (host->caps2 & MMC_CAP2_AVOID_3_3V && host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { pr_err("%s: Host failed to negotiate down from 3.3V\n", @@ -1082,6 +1088,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, */ static void mmc_sd_remove(struct mmc_host *host) { + mmc_exit_clk_scaling(host); mmc_remove_card(host->card); host->card = NULL; } @@ -1228,6 +1235,62 @@ static int mmc_sd_hw_reset(struct mmc_host *host) return mmc_sd_init_card(host, host->card->ocr, host->card); } +/** + * mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime + * @host: pointer to mmc host structure + * @freq: pointer to desired frequency to be set + * + * Change the SD card bus frequency at runtime after the card is + * initialized. Callers are expected to make sure of the card's + * state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime. + * + * If the frequency to change is greater than max. supported by card, + * *freq is changed to max. supported by card and if it is less than min. + * supported by host, *freq is changed to min. supported by host. + */ +static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq) +{ + int err = 0; + struct mmc_card *card; + + mmc_claim_host(host); + /* + * Assign card pointer after claiming host to avoid race + * conditions that may arise during removal of the card. + */ + card = host->card; + + /* sanity checks */ + if (!card || !freq) { + err = -EINVAL; + goto out; + } + + mmc_set_clock(host, (unsigned int) (*freq)); + + if (!mmc_host_is_spi(card->host) && mmc_card_uhs(card) + && card->host->ops->execute_tuning) { + /* + * We try to probe host driver for tuning for any + * frequency, it is host driver responsibility to + * perform actual tuning only when required. + */ + err = card->host->ops->execute_tuning(card->host, + MMC_SEND_TUNING_BLOCK); + + if (err) { + pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n", + mmc_hostname(card->host), __func__, err, + host->clk_scaling.curr_freq); + mmc_set_clock(host, host->clk_scaling.curr_freq); + } + } + +out: + mmc_release_host(host); + return err; +} + static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, @@ -1238,6 +1301,7 @@ static int mmc_sd_hw_reset(struct mmc_host *host) .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, .hw_reset = mmc_sd_hw_reset, + .change_bus_speed = mmc_sd_change_bus_speed, }; /* @@ -1292,6 +1356,12 @@ int mmc_attach_sd(struct mmc_host *host) goto remove_card; mmc_claim_host(host); + err = mmc_init_clk_scaling(host); + if (err) { + mmc_release_host(host); + goto remove_card; + } + return 0; remove_card: diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index b5519a5..e9fe8c6 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1705,6 +1705,43 @@ static int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host) MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); +int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name, + u32 **out, int *len, u32 size) +{ + int ret = 0; + struct device_node *np = dev->of_node; + size_t sz; + u32 *arr = NULL; + + if (!of_get_property(np, prop_name, len)) { + ret = -EINVAL; + goto out; + } + sz = *len = *len / sizeof(*arr); + if (sz <= 0 || (size > 0 && (sz > size))) { + dev_err(dev, "%s invalid size\n", prop_name); + ret = -EINVAL; + goto out; + } + + arr = devm_kzalloc(dev, sz * sizeof(*arr), GFP_KERNEL); + if (!arr) { + ret = -ENOMEM; + goto out; + } + + ret = of_property_read_u32_array(np, prop_name, arr, sz); + if (ret < 0) { + dev_err(dev, "%s failed reading array %d\n", prop_name, ret); + goto out; + } + *out = arr; +out: + if (ret) + *len = 0; + return ret; +} + static const struct sdhci_ops sdhci_msm_ops = { .reset = sdhci_reset, .set_clock = sdhci_msm_set_clock, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 02bea61..354fc68 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -36,6 +36,9 @@ #endif #include "sdhci-pltfm.h" +int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name, + u32 **out, int *len, u32 size); + unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -101,6 +104,14 @@ void sdhci_get_of_property(struct platform_device *pdev) of_property_read_u32(np, "clock-frequency", &pltfm_host->clock); + if (sdhci_msm_dt_get_array(&pdev->dev, "qcom,devfreq,freq-table", + &host->mmc->clk_scaling.pltfm_freq_table, + &host->mmc->clk_scaling.pltfm_freq_table_sz, 0)) + pr_debug("no clock scaling frequencies were supplied\n"); + else if (!host->mmc->clk_scaling.pltfm_freq_table || + !host->mmc->clk_scaling.pltfm_freq_table_sz) + pr_err("bad dts clock scaling frequencies\n"); + if (of_find_property(np, "keep-power-in-suspend", NULL)) host->mmc->pm_caps |= MMC_PM_KEEP_POWER; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 162b9af..f0aafab 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2427,6 +2427,32 @@ static void sdhci_card_event(struct mmc_host *mmc) spin_unlock_irqrestore(&host->lock, flags); } +static inline void sdhci_update_power_policy(struct sdhci_host *host, + enum sdhci_power_policy policy) +{ + host->power_policy = policy; +} + +static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state) +{ + int err = 0; + struct sdhci_host *host = mmc_priv(mmc); + + switch (state) { + case MMC_LOAD_HIGH: + sdhci_update_power_policy(host, SDHCI_PERFORMANCE_MODE); + break; + case MMC_LOAD_LOW: + sdhci_update_power_policy(host, SDHCI_POWER_SAVE_MODE); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .post_req = sdhci_post_req, @@ -2441,6 +2467,7 @@ static void sdhci_card_event(struct mmc_host *mmc) .execute_tuning = sdhci_execute_tuning, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, + .notify_load = sdhci_notify_load, }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 3b0c97a..740471f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -346,6 +346,12 @@ enum sdhci_cookie { COOKIE_MAPPED, /* mapped by sdhci_prepare_data() */ }; +enum sdhci_power_policy { + SDHCI_PERFORMANCE_MODE, + SDHCI_POWER_SAVE_MODE, + SDHCI_POWER_POLICY_NUM /* Always keep this one last */ +}; + struct sdhci_host { /* Data set by hardware interface driver */ const char *hw_name; /* Hardware bus name */ @@ -562,6 +568,8 @@ struct sdhci_host { /* Delay (ms) between tuning commands */ int tuning_delay; + enum sdhci_power_policy power_policy; + /* Host SDMA buffer boundary. */ u32 sdma_boundary; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index de73778..c713581 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -245,6 +245,10 @@ struct mmc_card { struct mmc_host *host; /* the host this device belongs to */ struct device dev; /* the device */ u32 ocr; /* the current OCR setting */ + unsigned long clk_scaling_lowest; /* lowest scaleable*/ + /* frequency */ + unsigned long clk_scaling_highest; /* highest scaleable */ + unsigned int rca; /* relative card address of device */ unsigned int type; /* card type */ #define MMC_TYPE_MMC 0 /* MMC card */ @@ -308,6 +312,7 @@ struct mmc_card { unsigned int nr_parts; unsigned int bouncesz; /* Bounce buffer size */ + unsigned int part_curr; }; static inline bool mmc_large_sector(struct mmc_card *card) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 64300a4..321ab39 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -82,6 +83,12 @@ struct mmc_ios { struct mmc_host; +/* states to represent load on the host */ +enum mmc_load { + MMC_LOAD_HIGH, + MMC_LOAD_LOW, +}; + struct mmc_host_ops { /* * It is optional for the host to implement pre_req and post_req in @@ -161,6 +168,7 @@ struct mmc_host_ops { */ int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); + int (*notify_load)(struct mmc_host *, enum mmc_load); }; struct mmc_cqe_ops { @@ -260,9 +268,60 @@ struct mmc_ctx { struct task_struct *task; }; +/** + * struct mmc_devfeq_clk_scaling - main context for MMC clock scaling logic + * + * @lock: spinlock to protect statistics + * @devfreq: struct that represent mmc-host as a client for devfreq + * @devfreq_profile: MMC device profile, mostly polling interval and callbacks + * @ondemand_gov_data: struct supplied to ondemmand governor (thresholds) + * @state: load state, can be HIGH or LOW. used to notify mmc_host_ops callback + * @start_busy: timestamped armed once a data request is started + * @measure_interval_start: timestamped armed once a measure interval started + * @devfreq_abort: flag to sync between different contexts relevant to devfreq + * @skip_clk_scale_freq_update: flag that enable/disable frequency change + * @freq_table_sz: table size of frequencies supplied to devfreq + * @freq_table: frequencies table supplied to devfreq + * @curr_freq: current frequency + * @polling_delay_ms: polling interval for status collection used by devfreq + * @upthreshold: up-threshold supplied to ondemand governor + * @downthreshold: down-threshold supplied to ondemand governor + * @need_freq_change: flag indicating if a frequency change is required + * @clk_scaling_in_progress: flag indicating if there's ongoing frequency change + * @is_busy_started: flag indicating if a request is handled by the HW + * @enable: flag indicating if the clock scaling logic is enabled for this host + */ +struct mmc_devfeq_clk_scaling { + spinlock_t lock; + struct devfreq *devfreq; + struct devfreq_dev_profile devfreq_profile; + struct devfreq_simple_ondemand_data ondemand_gov_data; + enum mmc_load state; + ktime_t start_busy; + ktime_t measure_interval_start; + atomic_t devfreq_abort; + bool skip_clk_scale_freq_update; + int freq_table_sz; + int pltfm_freq_table_sz; + u32 *freq_table; + u32 *pltfm_freq_table; + unsigned long total_busy_time_us; + unsigned long target_freq; + unsigned long curr_freq; + unsigned long polling_delay_ms; + unsigned int upthreshold; + unsigned int downthreshold; + bool need_freq_change; + bool clk_scaling_in_progress; + bool is_busy_started; + bool enable; +}; + + struct mmc_host { struct device *parent; struct device class_dev; + struct mmc_devfeq_clk_scaling clk_scaling; int index; const struct mmc_host_ops *ops; struct mmc_pwrseq *pwrseq; @@ -360,6 +419,7 @@ struct mmc_host { #define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */ #define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */ #define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */ +#define MMC_CAP2_CLK_SCALE (1 << 26) /* Allow dynamic clk scaling */ int fixed_drv_type; /* fixed driver type for non-removable media */ @@ -523,6 +583,16 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc, u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max); int mmc_regulator_get_supply(struct mmc_host *mmc); +static inline void mmc_host_clear_sdr104(struct mmc_host *host) +{ + host->caps &= ~MMC_CAP_UHS_SDR104; +} + +static inline void mmc_host_set_sdr104(struct mmc_host *host) +{ + host->caps |= MMC_CAP_UHS_SDR104; +} + static inline int mmc_card_is_removable(struct mmc_host *host) { return !(host->caps & MMC_CAP_NONREMOVABLE); From patchwork Mon Oct 8 12:58:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630693 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 409FE16B1 for ; Mon, 8 Oct 2018 12:59:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2D79528E7D for ; Mon, 8 Oct 2018 12:59:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 20DCC28E98; Mon, 8 Oct 2018 12:59:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6406028E7D for ; Mon, 8 Oct 2018 12:58:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726159AbeJHUKe (ORCPT ); Mon, 8 Oct 2018 16:10:34 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:49132 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726014AbeJHUKe (ORCPT ); Mon, 8 Oct 2018 16:10:34 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id C5C9560B7F; Mon, 8 Oct 2018 12:58:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003537; bh=WrICnaO66H8LrFR+sb3Y5sGjTTFNRJduLthwZ28gJEk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SDVOgss/huO1gs6aHjEzAuklolRo1xl+94v6YXmxuIKlbrU1XVySu7fT7af2ENEBc EWtxvX6UyDOfYruetCTvAMCn94fgiGzAeWlczJ7BTmQ6uqTXkX5LhVArKovHEO7beY ua8pUf+L7c0mSY+nyeBDbJ4HEb8MXY2tfH114gF8= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id EE00F60C1D; Mon, 8 Oct 2018 12:58:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003534; bh=WrICnaO66H8LrFR+sb3Y5sGjTTFNRJduLthwZ28gJEk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bCml3t17X1DA+vmAZerB33gxpyC01n9nE6k10U04WJg1LJY9kQDtBsTSuoVGwYpX3 NH8tfzYeLcqBVpLaa6x+VIyaJAo8uuyEvsX7Ly/MHJqnd3nfBpCvTcsJbabr46YVCq blpqYvAo0UofhujQeSBURpDVB6qL/DMmp0jwnX7I= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org EE00F60C1D Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org, Sujit Reddy Thumma , Subhash Jadavani Subject: [PATCH V1 3/7] mmc: core: Add sysfs entries for dynamic control of clock scaling Date: Mon, 8 Oct 2018 18:28:02 +0530 Message-Id: <1539003486-24087-4-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add sysfs attributes to allow dynamic control of clock scaling parameters. These attributes are used to enable/disable clock scaling at runtime and change the up_threshold, down_threshold, and polling_interval values. Complete documentation for these sysfs entries are provided at "Documentation/mmc/mmc-dev-attrs.txt". Signed-off-by: Sujit Reddy Thumma Signed-off-by: Subhash Jadavani Signed-off-by: Sayali Lokhande --- Documentation/mmc/mmc-dev-attrs.txt | 38 +++++++++ drivers/mmc/core/host.c | 151 +++++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 1 deletion(-) diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt index 4ad0bb1..b02b2b4 100644 --- a/Documentation/mmc/mmc-dev-attrs.txt +++ b/Documentation/mmc/mmc-dev-attrs.txt @@ -75,3 +75,41 @@ Note on raw_rpmb_size_mult: "raw_rpmb_size_mult" is a multiple of 128kB block. RPMB size in byte is calculated by using the following equation: RPMB partition size = 128kB x raw_rpmb_size_mult + +SD/MMC Clock Scaling Attributes +==================================== + +Read and write accesses are provided to following attributes. + + polling_interval Measured in milliseconds, this attribute + defines how often we need to check the card + usage and make decisions on frequency scaling. + + up_threshold This attribute defines what should be the + average card usage between the polling + interval for the mmc core to make a decision + on whether it should increase the frequency. + For example when it is set to '35' it means + that between the checking intervals the card + needs to be on average more than 35% in use to + scale up the frequency. The value should be + between 0 - 100 so that it can be compared + against load percentage. + + down_threshold Similar to up_threshold, but on lowering the + frequency. For example, when it is set to '2' + it means that between the checking intervals + the card needs to be on average less than 2% + in use to scale down the clocks to minimum + frequency. The value should be between 0 - 100 + so that it can be compared against load + percentage. + + enable Enable clock scaling for hosts (and cards) + that support ultrahigh speed modes + (SDR104, DDR50, HS200). + +echo > /sys/class/mmc_host/mmcX/clk_scaling/polling_interval +echo > /sys/class/mmc_host/mmcX/clk_scaling/up_threshold +echo > /sys/class/mmc_host/mmcX/clk_scaling/down_threshold +echo > /sys/class/mmc_host/mmcX/clk_scaling/enable diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1e46aa4..0504610 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -419,6 +419,151 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) EXPORT_SYMBOL(mmc_alloc_host); +static ssize_t show_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%d\n", mmc_can_scale_clk(host)); +} + +static ssize_t store_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || !host->card || kstrtoul(buf, 0, &value)) + return -EINVAL; + + mmc_get_card(host->card, NULL); + + if (!value) { + /* mask host capability */ + host->caps2 &= ~MMC_CAP2_CLK_SCALE; + host->clk_scaling.state = MMC_LOAD_HIGH; + /* Set to max. frequency when disabling */ + mmc_clk_update_freq(host, host->card->clk_scaling_highest, + host->clk_scaling.state); + } else if (value) { + /* Unmask host capability*/ + host->caps2 |= MMC_CAP2_CLK_SCALE; + } + + mmc_put_card(host->card, NULL); + + return count; +} + +static ssize_t show_up_threshold(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%d\n", host->clk_scaling.upthreshold); +} + +#define MAX_PERCENTAGE 100 +static ssize_t store_up_threshold(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE)) + return -EINVAL; + + host->clk_scaling.upthreshold = value; + + pr_debug("%s: clkscale_up_thresh set to %lu\n", + mmc_hostname(host), value); + return count; +} + +static ssize_t show_down_threshold(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%d\n", + host->clk_scaling.downthreshold); +} + +static ssize_t store_down_threshold(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE)) + return -EINVAL; + + host->clk_scaling.downthreshold = value; + + pr_debug("%s: clkscale_down_thresh set to %lu\n", + mmc_hostname(host), value); + return count; +} + +static ssize_t show_polling(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + + if (!host) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%lu milliseconds\n", + host->clk_scaling.polling_delay_ms); +} + +static ssize_t store_polling(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long value; + + if (!host || kstrtoul(buf, 0, &value)) + return -EINVAL; + + host->clk_scaling.polling_delay_ms = value; + + pr_debug("%s: clkscale_polling_delay_ms set to %lu\n", + mmc_hostname(host), value); + return count; +} + +DEVICE_ATTR(enable, 0644, + show_enable, store_enable); +DEVICE_ATTR(polling_interval, 0644, + show_polling, store_polling); +DEVICE_ATTR(up_threshold, 0644, + show_up_threshold, store_up_threshold); +DEVICE_ATTR(down_threshold, 0644, + show_down_threshold, store_down_threshold); + +static struct attribute *clk_scaling_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_up_threshold.attr, + &dev_attr_down_threshold.attr, + &dev_attr_polling_interval.attr, + NULL, +}; + +static struct attribute_group clk_scaling_attr_grp = { + .name = "clk_scaling", + .attrs = clk_scaling_attrs, +}; + /** * mmc_add_host - initialise host hardware * @host: mmc host @@ -447,6 +592,10 @@ int mmc_add_host(struct mmc_host *host) #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); #endif + err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp); + if (err) + pr_err("%s: failed to create clk scale sysfs group with err %d\n", + __func__, err); mmc_start_host(host); mmc_register_pm_notifier(host); @@ -472,7 +621,7 @@ void mmc_remove_host(struct mmc_host *host) #ifdef CONFIG_DEBUG_FS mmc_remove_host_debugfs(host); #endif - + sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp); device_del(&host->class_dev); led_trigger_unregister_simple(host->led); From patchwork Mon Oct 8 12:58:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630697 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 706F416B1 for ; Mon, 8 Oct 2018 12:59:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5FF5328E7D for ; Mon, 8 Oct 2018 12:59:09 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 532C128E98; Mon, 8 Oct 2018 12:59:09 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9104228E7D for ; Mon, 8 Oct 2018 12:59:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726700AbeJHUKn (ORCPT ); Mon, 8 Oct 2018 16:10:43 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:49366 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726014AbeJHUKn (ORCPT ); Mon, 8 Oct 2018 16:10:43 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 3CE43605A4; Mon, 8 Oct 2018 12:59:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003546; bh=I8ui9intrn4owoqMqMTW+yRPtp0UugnA25wBOoZZuRU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bvSEMxWasyB5sQ5vQP6j/WcK6sEU6Hegp+8t6s7ChFTJQiUd+7HHZU9jxEkTlsO+9 LxrCqrLq5SnEgTam7SCmyW9i4JQJW15vFpQwK9eqSAw7kS8d0EbPJwPMG8nWl6KimB l3jqZ2R7eD7cGGAHhLMjL2yU6W3WlDYHOQF1zmqk= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 41C5360769; Mon, 8 Oct 2018 12:58:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003543; bh=I8ui9intrn4owoqMqMTW+yRPtp0UugnA25wBOoZZuRU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o7JwL8iARcqrMQFptYUVFxBhf1qkXnqqCIYzICptNf9RGmp7aBIvAOwh+s5Z6VpF6 MkknOaCp8EKTGmw3pkAmwmBxzEXi93rKN/vsmW6QjgsE78JZW+ZNTRkHOqN44JgR9d kpVlECK+D91iyPDlvhGowxnjCParojlO2HJDtZcQ= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 41C5360769 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org, Talel Shenhar , Subhash Jadavani Subject: [PATCH V1 4/7] mmc: core: add support for devfreq suspend/resume Date: Mon, 8 Oct 2018 18:28:03 +0530 Message-Id: <1539003486-24087-5-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This change adds support for devfreq suspend/resume to be called on each system suspend/resume, runtime suspend/resume, power restore. Signed-off-by: Talel Shenhar Signed-off-by: Subhash Jadavani Signed-off-by: Sayali Lokhande --- drivers/mmc/core/core.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 2 + drivers/mmc/core/host.c | 8 +++- drivers/mmc/core/mmc.c | 27 ++++++++++++ drivers/mmc/core/sd.c | 13 ++++++ 5 files changed, 160 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a3d4a92..78be523 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -614,6 +614,111 @@ int mmc_init_clk_scaling(struct mmc_host *host) EXPORT_SYMBOL(mmc_init_clk_scaling); /** + * mmc_suspend_clk_scaling() - suspend clock scaling + * @host: pointer to mmc host structure + * + * This API will suspend devfreq feature for the specific host. + * The statistics collected by mmc will be cleared. + * This function is intended to be called by the pm callbacks + * (e.g. runtime_suspend, suspend) of the mmc device + */ +int mmc_suspend_clk_scaling(struct mmc_host *host) +{ + int err; + + if (!host) { + WARN(1, "bad host parameter\n"); + return -EINVAL; + } + + if (!mmc_can_scale_clk(host) || !host->clk_scaling.enable) + return 0; + + if (!host->clk_scaling.devfreq) { + pr_err("%s: %s: no devfreq is assosiated with this device\n", + mmc_hostname(host), __func__); + return -EPERM; + } + + atomic_inc(&host->clk_scaling.devfreq_abort); + wake_up(&host->wq); + err = devfreq_suspend_device(host->clk_scaling.devfreq); + if (err) { + pr_err("%s: %s: failed to suspend devfreq\n", + mmc_hostname(host), __func__); + return err; + } + host->clk_scaling.enable = false; + + host->clk_scaling.total_busy_time_us = 0; + + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); + + return 0; +} +EXPORT_SYMBOL(mmc_suspend_clk_scaling); + +/** + * mmc_resume_clk_scaling() - resume clock scaling + * @host: pointer to mmc host structure + * + * This API will resume devfreq feature for the specific host. + * This API is intended to be called by the pm callbacks + * (e.g. runtime_suspend, suspend) of the mmc device + */ +int mmc_resume_clk_scaling(struct mmc_host *host) +{ + int err = 0; + u32 max_clk_idx = 0; + u32 devfreq_max_clk = 0; + u32 devfreq_min_clk = 0; + + if (!host) { + WARN(1, "bad host parameter\n"); + return -EINVAL; + } + + if (!mmc_can_scale_clk(host)) + return 0; + + /* + * If clock scaling is already exited when resume is called, like + * during mmc shutdown, it is not an error and should not fail the + * API calling this. + */ + if (!host->clk_scaling.devfreq) { + pr_warn("%s: %s: no devfreq is assosiated with this device\n", + mmc_hostname(host), __func__); + return 0; + } + + atomic_set(&host->clk_scaling.devfreq_abort, 0); + + max_clk_idx = host->clk_scaling.freq_table_sz - 1; + devfreq_max_clk = host->clk_scaling.freq_table[max_clk_idx]; + devfreq_min_clk = host->clk_scaling.freq_table[0]; + + host->clk_scaling.curr_freq = devfreq_max_clk; + if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx]) + host->clk_scaling.curr_freq = devfreq_min_clk; + + host->clk_scaling.clk_scaling_in_progress = false; + host->clk_scaling.need_freq_change = false; + + err = devfreq_resume_device(host->clk_scaling.devfreq); + if (err) { + pr_err("%s: %s: failed to resume devfreq (%d)\n", + mmc_hostname(host), __func__, err); + } else { + host->clk_scaling.enable = true; + pr_debug("%s: devfreq resumed\n", mmc_hostname(host)); + } + + return err; +} +EXPORT_SYMBOL(mmc_resume_clk_scaling); + +/** * mmc_exit_devfreq_clk_scaling() - Disable clock scaling * @host: pointer to mmc host structure * @@ -638,6 +743,13 @@ int mmc_exit_clk_scaling(struct mmc_host *host) return -EPERM; } + err = mmc_suspend_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + err = devfreq_remove_device(host->clk_scaling.devfreq); if (err) { pr_err("%s: remove devfreq failed (%d)\n", diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index fc0a9b7..249c20d 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -97,6 +97,8 @@ static inline void mmc_delay(unsigned int ms) extern bool mmc_can_scale_clk(struct mmc_host *host); extern int mmc_init_clk_scaling(struct mmc_host *host); extern int mmc_exit_clk_scaling(struct mmc_host *host); +extern int mmc_suspend_clk_scaling(struct mmc_host *host); +extern int mmc_resume_clk_scaling(struct mmc_host *host); int mmc_execute_tuning(struct mmc_card *card); int mmc_hs200_to_hs400(struct mmc_card *card); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 0504610..c3a71a5 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -442,15 +442,19 @@ static ssize_t store_enable(struct device *dev, mmc_get_card(host->card, NULL); if (!value) { - /* mask host capability */ + /* Suspend the clock scaling and mask host capability */ + if (host->clk_scaling.enable) + mmc_suspend_clk_scaling(host); host->caps2 &= ~MMC_CAP2_CLK_SCALE; host->clk_scaling.state = MMC_LOAD_HIGH; /* Set to max. frequency when disabling */ mmc_clk_update_freq(host, host->card->clk_scaling_highest, host->clk_scaling.state); } else if (value) { - /* Unmask host capability*/ + /* Unmask host capability and resume scaling */ host->caps2 |= MMC_CAP2_CLK_SCALE; + if (!host->clk_scaling.enable) + mmc_resume_clk_scaling(host); } mmc_put_card(host->card, NULL); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index c8aedf3..1d286af 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2161,6 +2161,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT : EXT_CSD_POWER_OFF_LONG; + err = mmc_suspend_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + mmc_claim_host(host); if (mmc_card_suspended(host->card)) @@ -2190,6 +2197,8 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) } out: mmc_release_host(host); + if (err) + mmc_resume_clk_scaling(host); return err; } @@ -2228,6 +2237,10 @@ static int _mmc_resume(struct mmc_host *host) out: mmc_release_host(host); + err = mmc_resume_clk_scaling(host); + if (err) + pr_err("%s: %s: fail to resume clock scaling (%d)\n", + mmc_hostname(host), __func__, err); return err; } @@ -2335,6 +2348,15 @@ static int _mmc_hw_reset(struct mmc_host *host) mmc_pwrseq_reset(host); } + /* Suspend clk scaling to avoid switching frequencies intermittently */ + + ret = mmc_suspend_clk_scaling(host); + if (ret) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; + } + ret = mmc_init_card(host, card->ocr, card); if (ret) { pr_err("%s: %s: mmc_init_card failed (%d)\n", @@ -2342,6 +2364,11 @@ static int _mmc_hw_reset(struct mmc_host *host) return ret; } + ret = mmc_resume_clk_scaling(host); + if (ret) + pr_err("%s: %s: fail to resume clock scaling (%d)\n", + mmc_hostname(host), __func__, ret); + return ret; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 40144c1..5ae2916 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1131,6 +1131,13 @@ static int _mmc_sd_suspend(struct mmc_host *host) { int err = 0; + err = mmc_suspend_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to suspend clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + return err; + } + mmc_claim_host(host); if (mmc_card_suspended(host->card)) @@ -1182,6 +1189,12 @@ static int _mmc_sd_resume(struct mmc_host *host) err = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_card_clr_suspended(host->card); + err = mmc_resume_clk_scaling(host); + if (err) { + pr_err("%s: %s: fail to resume clock scaling (%d)\n", + mmc_hostname(host), __func__, err); + goto out; + } out: mmc_release_host(host); return err; From patchwork Mon Oct 8 12:58:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630703 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C29B314DB for ; Mon, 8 Oct 2018 12:59:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B4A9B28E9A for ; Mon, 8 Oct 2018 12:59:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A926A28EB5; Mon, 8 Oct 2018 12:59:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 644CB28E99 for ; Mon, 8 Oct 2018 12:59:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726741AbeJHUKv (ORCPT ); Mon, 8 Oct 2018 16:10:51 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:49550 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726014AbeJHUKv (ORCPT ); Mon, 8 Oct 2018 16:10:51 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 9B7E560C81; Mon, 8 Oct 2018 12:59:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003554; bh=rtZFfpeeP3sYJt+dblteHR45TRoa/BXNVupV6RRD5j8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TgrDb2apHu7y2o04gEw3cudxaCXWtUT0JU/IXQ/4ROLll9+1HQ+o0suVbEfgZ9VWE Kj4oQOnJHXP97G4XhT5SHtidFQ3Jf0Ov+8ymGnDBIFNU7EgLgWNcKQIRCD88aavLmv KYH4NuXUogD1Rxr2om8m37+s1nolU4BALmAevhqc= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 53B5360881; Mon, 8 Oct 2018 12:59:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003552; bh=rtZFfpeeP3sYJt+dblteHR45TRoa/BXNVupV6RRD5j8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k606hbrehkjmS2IDrHRp6ZGiMF3TSSCAA+X98/X+TW16dLcYGNvA7ecThkNk0hy/1 qVzUCub9PsbsuK7BmnXdceQTUnZo6YeJprc1TvhEjb/9Gu3Fc/4t16vdaALmGiTXHa j+22EvgaXdsMlq0igwV/B9wYbuLFZj0p1qIC5DD4= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 53B5360881 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org Subject: [PATCH V1 5/7] mmc: sdhci-msm: Kconfig: select devfreq ondemand for sdhci-msm Date: Mon, 8 Oct 2018 18:28:04 +0530 Message-Id: <1539003486-24087-6-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP SDHCI-MSM platform is using devfreq ondemand governor. Select devfreq governor for SDHCI-MSM platform. Signed-off-by: Ritesh Harjani Signed-off-by: Sayali Lokhande --- drivers/mmc/host/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 0581c19..2ba2c62 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -442,6 +442,8 @@ config MMC_SDHCI_MSM tristate "Qualcomm SDHCI Controller Support" depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on MMC_SDHCI_PLTFM + select PM_DEVFREQ + select DEVFREQ_GOV_SIMPLE_ONDEMAND select MMC_SDHCI_IO_ACCESSORS help This selects the Secure Digital Host Controller Interface (SDHCI) From patchwork Mon Oct 8 12:58:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630705 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BC9BF16B1 for ; Mon, 8 Oct 2018 12:59:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ACEAA28E88 for ; Mon, 8 Oct 2018 12:59:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9E73228E99; Mon, 8 Oct 2018 12:59:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4331128E88 for ; Mon, 8 Oct 2018 12:59:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726006AbeJHULG (ORCPT ); Mon, 8 Oct 2018 16:11:06 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:49878 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725941AbeJHULG (ORCPT ); Mon, 8 Oct 2018 16:11:06 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 4CD5C60C7D; Mon, 8 Oct 2018 12:59:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003569; bh=71fUGI73NBkpOHwuXaLS9PGk+98bEPMw5LTMDCWvXXw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=b3/gMtu73tCHHD9wpr6fPrHeVo4Qh9dAMGeW69ts4H/AajbSAP28tPuUMOIOvu2JX hVy7XNdBfRdMEhCMN/e4cLsUOCJLvhKMobL8MOOCsrK/ra3hHa1q4jFTu0dSQiB5gP an68y4vo+KciN8CHhVkAAYi2dShTB5BC1Gsk7/UY= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id BE10960C82; Mon, 8 Oct 2018 12:59:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003565; bh=71fUGI73NBkpOHwuXaLS9PGk+98bEPMw5LTMDCWvXXw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DvFwf2UtNXdUvD86ddXb6Tpen6Q3gbopGHQrrZeYHOAEBPun1Lywb84RyABcIhaF5 u8ibdoWfL2cSfj4+kDDQJrRx5NYVk593l6U8HEptond6mWzmBHDOMk1RrKmQW8IXfy 3L63Ov4cbi47XYU12DwYwa7zBnj1zdKAn5BEe/vA= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org BE10960C82 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org Subject: [PATCH V1 6/7] mmc: sdhci-msm: Enable clock scaling property Date: Mon, 8 Oct 2018 18:28:05 +0530 Message-Id: <1539003486-24087-7-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This change enables clock scaling capability for sdhci-msm platform driver. Signed-off-by: Sayali Lokhande --- drivers/mmc/host/sdhci-msm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index e9fe8c6..8846a76 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1803,6 +1803,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_offset = msm_host->offset; sdhci_get_of_property(pdev); + msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE; msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; From patchwork Mon Oct 8 12:58:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sayali Lokhande X-Patchwork-Id: 10630709 X-Patchwork-Delegate: agross@codeaurora.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A267B14DB for ; Mon, 8 Oct 2018 12:59:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9131428EB5 for ; Mon, 8 Oct 2018 12:59:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 859D028EC4; Mon, 8 Oct 2018 12:59:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 225C828EB5 for ; Mon, 8 Oct 2018 12:59:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726486AbeJHULR (ORCPT ); Mon, 8 Oct 2018 16:11:17 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:50050 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725914AbeJHULR (ORCPT ); Mon, 8 Oct 2018 16:11:17 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 86C0160BF4; Mon, 8 Oct 2018 12:59:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003579; bh=A4vqSNmkJ6M0okyY7xZNgzUoUCkAhEFtwShfK4XsboI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ND9me6Cs1+sSt8KPf3+F8B86M51FBwTX6zBONyLgZ4fZW4U5SshwdDoWl5gR5vVCj OExNff1QMWhcH4hItJA9Htda2cJIxzkQszoGR8heJbc5g50rXgnj76wgEfqX2jB/8Y Bg8uS+mL1o4EPrHjPVrYOE+wz/khVP+kGV/4wvC4= Received: from sayalil-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: sayalil@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 0280C6086B; Mon, 8 Oct 2018 12:59:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1539003577; bh=A4vqSNmkJ6M0okyY7xZNgzUoUCkAhEFtwShfK4XsboI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PMzHOEAEjZh+ElGvKebx8rRFIdqS92JPvhhKKktEX4GpAWG/NEOi96HhVZLDqLvG1 42Rjvrdzza2fsP2aLPEgCt9w3rbpB4P8wq2C+b2pXRAQXyVM/nniUaOezrxxIWaRfC 5dff2JWPFC1mPP2QQok8R8sbS6HHa5hE3xVEGsJ0= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 0280C6086B Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=sayalil@codeaurora.org From: Sayali Lokhande To: adrian.hunter@intel.com, ulf.hansson@linaro.org, robh+dt@kernel.org, robh@kernel.org, mark.rutland@arm.com Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, shawn.lin@rock-chips.com, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org, devicetree@vger.kernel.org, asutoshd@codeaurora.org, stummala@codeaurora.org, venkatg@codeaurora.org, vviswana@codeaurora.org, bjorn.andersson@linaro.org, riteshh@codeaurora.org, vbadigan@codeaurora.org, dianders@google.com, sayalil@codeaurora.org, Sujit Reddy Thumma Subject: [PATCH V1 7/7] mmc: core: Add a debugfs entry to set max clock rate Date: Mon, 8 Oct 2018 18:28:06 +0530 Message-Id: <1539003486-24087-8-git-send-email-sayalil@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> References: <1539003486-24087-1-git-send-email-sayalil@codeaurora.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Limiting the max frequency supported by host controller to a certain value can be useful for testing power consumption at various frequencies that are supported by the host. Note: If the card supports less than desired value then the frequency of operation would be limited to that frequency. Usage: mount -t debugfs none /sys/kernel/debug echo > /sys/kernel/debug/mmcX/max_clock cat /sys/kernel/debug/mmcX/max_clock Signed-off-by: Sujit Reddy Thumma Signed-off-by: Sayali Lokhande --- drivers/mmc/core/debugfs.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 630ca8e..26499f7 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -262,6 +262,46 @@ static int mmc_scale_set(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(mmc_scale_fops, mmc_scale_get, mmc_scale_set, "%llu\n"); +static int mmc_max_clock_get(void *data, u64 *val) +{ + struct mmc_host *host = data; + + if (!host) + return -EINVAL; + + *val = host->f_max; + + return 0; +} + +static int mmc_max_clock_set(void *data, u64 val) +{ + struct mmc_host *host = data; + int err = -EINVAL; + unsigned long freq = val; + unsigned int old_freq; + + if (!host || (val < host->f_min)) + goto out; + + mmc_claim_host(host); + if (host->bus_ops && host->bus_ops->change_bus_speed) { + old_freq = host->f_max; + host->f_max = freq; + + err = host->bus_ops->change_bus_speed(host, &freq); + + if (err) + host->f_max = old_freq; + } + mmc_release_host(host); +out: + return err; +} + +DEFINE_SIMPLE_ATTRIBUTE(mmc_max_clock_fops, mmc_max_clock_get, + mmc_max_clock_set, "%llu\n"); + void mmc_add_host_debugfs(struct mmc_host *host) { struct dentry *root; @@ -290,6 +330,10 @@ void mmc_add_host_debugfs(struct mmc_host *host) &mmc_clock_fops)) goto err_node; + if (!debugfs_create_file("max_clock", 0600, root, host, + &mmc_max_clock_fops)) + goto err_node; + if (!debugfs_create_file("scale", 0600, root, host, &mmc_scale_fops)) goto err_node;