From patchwork Fri Apr 10 17:58:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartlomiej Zolnierkiewicz X-Patchwork-Id: 6198321 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 482B7BF4A6 for ; Fri, 10 Apr 2015 18:01:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0CF0A20430 for ; Fri, 10 Apr 2015 18:01:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 989BD20437 for ; Fri, 10 Apr 2015 18:01:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756461AbbDJSBF (ORCPT ); Fri, 10 Apr 2015 14:01:05 -0400 Received: from mailout3.samsung.com ([203.254.224.33]:19937 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756201AbbDJSAJ (ORCPT ); Fri, 10 Apr 2015 14:00:09 -0400 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout3.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0NML00APTRBLD130@mailout3.samsung.com>; Sat, 11 Apr 2015 02:59:45 +0900 (KST) X-AuditID: cbfee61a-f79516d000006302-34-55280f91273a Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 9B.49.25346.19F08255; Sat, 11 Apr 2015 02:59:45 +0900 (KST) Received: from amdc1032.digital.local ([106.116.147.136]) by mmp1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0NML004B7RB00E40@mmp1.samsung.com>; Sat, 11 Apr 2015 02:59:45 +0900 (KST) From: Bartlomiej Zolnierkiewicz To: Thomas Abraham , Sylwester Nawrocki , Mike Turquette , Kukjin Kim , Kukjin Kim , Viresh Kumar Cc: Tomasz Figa , Lukasz Majewski , Heiko Stuebner , Chanwoo Choi , Kevin Hilman , Javier Martinez Canillas , linux-samsung-soc@vger.kernel.org, linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, b.zolnierkie@samsung.com Subject: [PATCH 2/6] cpufreq-dt: add 'boost' mode frequencies support Date: Fri, 10 Apr 2015 19:58:58 +0200 Message-id: <1428688742-15578-3-git-send-email-b.zolnierkie@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1428688742-15578-1-git-send-email-b.zolnierkie@samsung.com> References: <1428688742-15578-1-git-send-email-b.zolnierkie@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrALMWRmVeSWpSXmKPExsVy+t9jAd2J/BqhBlcmaVhsnLGe1eL6l+es Fv8fvWa1OPq7wKJ3wVU2i/7Hr5ktvh5ewWjx5uFmRotNj6+xWlzeNYfN4nPvEUaLGef3MVk8 nXCRzeLwm3ZWi45ljBardv1htNj41cNB0OPv8+ssHjtn3WX32LSqk83jzrU9bB6bl9R79G1Z xeix/do8Zo/Pm+QCOKK4bFJSczLLUov07RK4Mo6uPcJUcMOt4tu0uUwNjBMtuhg5OCQETCQ2 ftTsYuQEMsUkLtxbz9bFyMUhJLCIUeJO1y8o5zejRG/7HXaQKjYBK4mJ7asYQRIiAh8YJV6/ +wZWxSwwn1liwtNTLCBVwgKuEhOXHmADsVkEVCWu/m8Gi/MKeEg8eX+XCWK1gsScSTYgYU4B T4k9R/vYQcJCQCW728MmMPIuYGRYxSiaWpBcUJyUnmuoV5yYW1yal66XnJ+7iREczs+kdjCu bLA4xCjAwajEw3sjXj1UiDWxrLgy9xCjBAezkghvzRegEG9KYmVValF+fFFpTmrxIUZpDhYl cd45unKhQgLpiSWp2ampBalFMFkmDk6pBsbcCT2KhdaNC3apa/zc53ChPJ/BxzP99bU3zJsr Qze+cHiqYMFq8kdUQmr+5vRrlnsNA36ec/vE99Z9tmPW+zWfzbNP2MhkdPj9EVlgPTF2lrLn zGDXw7sjUluCngtk2UqlrHsYZflu3h6GTb1sbVufrNh76e3klGNiv34s9fxwv4T3kODtHgkl luKMREMt5qLiRABcYMfGYwIAAA== Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_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 Add 'boost' mode frequencies support: - add boost-opps binding to cpufreq-dt driver bindings - make cpufreq_init() adjust freq_table accordingly - fix set_target() to handle boost frequencies - add boost_supported field to struct cpufreq_dt_platform_data - set dt_cpufreq_driver.boost_supported in dt_cpufreq_probe() This patch makes cpufreq-dt driver aware of 'boost' mode frequencies and prepares it for adding support for Exynos4x12 'boost' support. boost-opps binding is currently limited to cpufreq-dt but once there is a need for cpufreq wide and/or generic Linux device support for 'boost' mode cpufreq-dt can be updated to handle the new code without changing the binding itself. The decision to make 'boost' mode support limited to cpufreq-dt driver for now was taken because 'boost' mode is currently a niche feature and code needed for parsing boost-opps binding is minimal and simple. More generic (i.e. separate 'boost' OPPs list in struct device and generic cpufreq convertion of them to freq_table format) support would need far more code and effort to make it work. Doing it without a demonstrated real need would be on overengineering IMHO. Cc: Tomasz Figa Cc: Mike Turquette Cc: Javier Martinez Canillas Cc: Thomas Abraham Signed-off-by: Bartlomiej Zolnierkiewicz --- .../devicetree/bindings/cpufreq/cpufreq-dt.txt | 14 +++ drivers/cpufreq/cpufreq-dt.c | 114 +++++++++++++++++--- include/linux/cpufreq-dt.h | 1 + 3 files changed, 116 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt index e41c98f..98572d8 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt @@ -14,6 +14,16 @@ Optional properties: - operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for details. OPPs *must* be supplied either via DT, i.e. this property, or populated at runtime. +- boost-opps: Certain CPUs can be operated in optional 'boost' mode (sometimes + also referred as overclocking) in which the CPU can operate at frequencies + which are not specified by the manufacturer as CPU's operating frequency. + CPU usually can operate in 'boost' mode for limited amount of time which + depends on thermal conditions. This makes the boost operating points + separate from normal ones which can be used at any time. This property + consists of an array of 2-tuples items, and each item consists of frequency + and voltage like . + freq: clock frequency in kHz + vol: voltage in microvolt - clock-latency: Specify the possible maximum transition latency for clock, in unit of nanoseconds. - voltage-tolerance: Specify the CPU voltage tolerance in percentage. @@ -38,6 +48,10 @@ cpus { 396000 950000 198000 850000 >; + boost-opps = < + /* kHz uV */ + 891000 1150000 + >; clock-latency = <61036>; /* two CLK32 periods */ #cooling-cells = <2>; cooling-min-level = <0>; diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index bab67db..e5aaf3a 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -60,17 +60,22 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index) if (!IS_ERR(cpu_reg)) { unsigned long opp_freq; - rcu_read_lock(); - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); - if (IS_ERR(opp)) { + if (freq_table[index].flags & CPUFREQ_BOOST_FREQ) { + volt = freq_table[index].driver_data; + opp_freq = freq_table[index].frequency * 1000; + } else { + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(cpu_dev, "failed to find OPP for %ld\n", + freq_Hz); + return PTR_ERR(opp); + } + volt = dev_pm_opp_get_voltage(opp); + opp_freq = dev_pm_opp_get_freq(opp); rcu_read_unlock(); - dev_err(cpu_dev, "failed to find OPP for %ld\n", - freq_Hz); - return PTR_ERR(opp); } - volt = dev_pm_opp_get_voltage(opp); - opp_freq = dev_pm_opp_get_freq(opp); - rcu_read_unlock(); tol = volt * priv->voltage_tolerance / 100; volt_old = regulator_get_voltage(cpu_reg); dev_dbg(cpu_dev, "Found OPP: %ld kHz, %ld uV\n", @@ -182,6 +187,11 @@ try_again: return ret; } +struct boost_opp { + unsigned long rate; + unsigned long u_volt; +}; + static int cpufreq_init(struct cpufreq_policy *policy) { struct cpufreq_dt_platform_data *pd; @@ -191,9 +201,11 @@ static int cpufreq_init(struct cpufreq_policy *policy) struct device *cpu_dev; struct regulator *cpu_reg; struct clk *cpu_clk; + struct boost_opp *boost_opps = NULL; unsigned long min_uV = ~0, max_uV = 0; + unsigned int extra_opps = 0; unsigned int transition_latency; - int ret; + int nr, i, ret; ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk); if (ret) { @@ -234,7 +246,10 @@ static int cpufreq_init(struct cpufreq_policy *policy) transition_latency = CPUFREQ_ETERNAL; if (!IS_ERR(cpu_reg)) { + const struct property *prop; + unsigned long opp_uV, tol_uV; unsigned long opp_freq = 0; + const __be32 *val; /* * Disable any OPPs where the connected regulator isn't able to @@ -243,7 +258,6 @@ static int cpufreq_init(struct cpufreq_policy *policy) */ while (1) { struct dev_pm_opp *opp; - unsigned long opp_uV, tol_uV; rcu_read_lock(); opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq); @@ -268,17 +282,86 @@ static int cpufreq_init(struct cpufreq_policy *policy) opp_freq++; } + prop = of_find_property(np, "boost-opps", NULL); + if (!prop || !prop->value) + goto set_cpu_reg; + + nr = prop->length / sizeof(u32); + if (nr % 2) { + dev_err(cpu_dev, "%s: Invalid boost-opps list\n", + __func__); + goto set_cpu_reg; + } + + boost_opps = kzalloc(nr / 2 * sizeof(*boost_opps), + GFP_KERNEL); + if (!boost_opps) + goto set_cpu_reg; + + val = prop->value; + while (nr) { + unsigned long rate = be32_to_cpup(val++) * 1000; + unsigned long u_volt = be32_to_cpup(val++); + + if (rate < opp_freq) { + nr -= 2; + continue; + } else { + opp_freq = rate + 1; + opp_uV = u_volt; + } + + tol_uV = opp_uV * priv->voltage_tolerance / 100; + if (regulator_is_supported_voltage(cpu_reg, opp_uV, + opp_uV + tol_uV)) { + if (opp_uV < min_uV) + min_uV = opp_uV; + if (opp_uV > max_uV) + max_uV = opp_uV; + } else { + nr -= 2; + continue; + } + + boost_opps[extra_opps].rate = rate; + boost_opps[extra_opps].u_volt = u_volt; + extra_opps++; + nr -= 2; + } +set_cpu_reg: ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); if (ret > 0) transition_latency += ret * 1000; } - ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + ret = __dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table, + extra_opps); if (ret) { pr_err("failed to init cpufreq table: %d\n", ret); + kfree(boost_opps); goto out_free_priv; } + if (extra_opps) { + nr = 0; + while (1) { + if (freq_table[nr].frequency == CPUFREQ_TABLE_END) + break; + nr++; + } + + for (i = 0; i < extra_opps; i++) { + freq_table[nr + i].flags |= CPUFREQ_BOOST_FREQ; + freq_table[nr + i].driver_data = boost_opps[i].u_volt; + freq_table[nr + i].frequency = + boost_opps[i].rate / 1000; + } + + freq_table[nr + i].frequency = CPUFREQ_TABLE_END; + } + + kfree(boost_opps); + priv->cpu_dev = cpu_dev; priv->cpu_reg = cpu_reg; policy->driver_data = priv; @@ -372,6 +455,7 @@ static struct cpufreq_driver dt_cpufreq_driver = { static int dt_cpufreq_probe(struct platform_device *pdev) { + struct cpufreq_dt_platform_data *pd; struct device *cpu_dev; struct regulator *cpu_reg; struct clk *cpu_clk; @@ -392,7 +476,11 @@ static int dt_cpufreq_probe(struct platform_device *pdev) if (!IS_ERR(cpu_reg)) regulator_put(cpu_reg); - dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev); + pd = dev_get_platdata(&pdev->dev); + dt_cpufreq_driver.driver_data = pd; + + if (pd) + dt_cpufreq_driver.boost_supported = pd->boost_supported; ret = cpufreq_register_driver(&dt_cpufreq_driver); if (ret) diff --git a/include/linux/cpufreq-dt.h b/include/linux/cpufreq-dt.h index 0414009..483ca1b 100644 --- a/include/linux/cpufreq-dt.h +++ b/include/linux/cpufreq-dt.h @@ -17,6 +17,7 @@ struct cpufreq_dt_platform_data { * clock. */ bool independent_clocks; + bool boost_supported; }; #endif /* __CPUFREQ_DT_H__ */