lkml.org 
[lkml]   [2015]   [Apr]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/6] cpufreq-dt: add 'boost' mode frequencies support
    Date
    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 <tomasz.figa@gmail.com>
    Cc: Mike Turquette <mturquette@linaro.org>
    Cc: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
    Cc: Thomas Abraham <thomas.ab@samsung.com>
    Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
    ---
    .../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-kHz vol-uV>.
    + 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__ */
    --
    1.7.9.5


    \
     
     \ /
      Last update: 2015-04-10 20:41    [W:2.966 / U:0.192 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site