Skip to content

Commit

Permalink
PM / devfreq: tegra30: Support interconnect and OPPs from device-tree
Browse files Browse the repository at this point in the history
This patch moves ACTMON driver away from generating OPP table by itself,
transitioning it to use the table which comes from device-tree. This
change breaks compatibility with older device-trees and brings support
for the interconnect framework to the driver. This is a mandatory change
which needs to be done in order to implement interconnect-based memory
DVFS, i.e. device-trees need to be updated. Now ACTMON issues a memory
bandwidth requests using dev_pm_opp_set_bw() instead of driving EMC clock
rate directly.

Tested-by: Peter Geis <pgwipeout@gmail.com>
Tested-by: Nicolas Chauvet <kwizart@gmail.com>
Acked-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
  • Loading branch information
Dmitry Osipenko authored and Chanwoo Choi committed Dec 7, 2020
1 parent afd589c commit 16e8b2a
Showing 1 changed file with 37 additions and 42 deletions.
79 changes: 37 additions & 42 deletions drivers/devfreq/tegra30-devfreq.c
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@
#include <linux/reset.h>
#include <linux/workqueue.h>

#include <soc/tegra/fuse.h>

#include "governor.h"

#define ACTMON_GLB_STATUS 0x0
@@ -155,6 +157,7 @@ struct tegra_devfreq_device {

struct tegra_devfreq {
struct devfreq *devfreq;
struct opp_table *opp_table;

struct reset_control *reset;
struct clk *clock;
@@ -612,34 +615,19 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
unsigned long rate;
int err;
int ret;

opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp)) {
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
return PTR_ERR(opp);
}
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);

err = clk_set_min_rate(tegra->emc_clock, rate * KHZ);
if (err)
return err;

err = clk_set_rate(tegra->emc_clock, 0);
if (err)
goto restore_min_rate;

return 0;

restore_min_rate:
clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
ret = dev_pm_opp_set_bw(dev, opp);
dev_pm_opp_put(opp);

return err;
return ret;
}

static int tegra_devfreq_get_dev_status(struct device *dev,
@@ -655,7 +643,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
stat->private_data = tegra;

/* The below are to be used by the other governors */
stat->current_frequency = cur_freq;
stat->current_frequency = cur_freq * KHZ;

actmon_dev = &tegra->devices[MCALL];

@@ -705,7 +693,12 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
target_freq = max(target_freq, dev->target_freq);
}

*freq = target_freq;
/*
* tegra-devfreq driver operates with KHz units, while OPP table
* entries use Hz units. Hence we need to convert the units for the
* devfreq core.
*/
*freq = target_freq * KHZ;

return 0;
}
@@ -774,6 +767,7 @@ static struct devfreq_governor tegra_devfreq_governor = {

static int tegra_devfreq_probe(struct platform_device *pdev)
{
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
struct tegra_devfreq_device *dev;
struct tegra_devfreq *tegra;
struct devfreq *devfreq;
@@ -822,11 +816,25 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err;
}

tegra->opp_table = dev_pm_opp_set_supported_hw(&pdev->dev,
&hw_version, 1);
err = PTR_ERR_OR_ZERO(tegra->opp_table);
if (err) {
dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
return err;
}

err = dev_pm_opp_of_add_table(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
goto put_hw;
}

err = clk_prepare_enable(tegra->clock);
if (err) {
dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n");
return err;
goto remove_table;
}

err = reset_control_reset(tegra->reset);
@@ -850,23 +858,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
dev->regs = tegra->regs + dev->config->offset;
}

for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);

if (rate < 0) {
dev_err(&pdev->dev,
"Failed to round clock rate: %ld\n", rate);
err = rate;
goto remove_opps;
}

err = dev_pm_opp_add(&pdev->dev, rate / KHZ, 0);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
goto remove_opps;
}
}

platform_set_drvdata(pdev, tegra);

tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb;
@@ -882,7 +873,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}

tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra_devfreq_profile.initial_freq /= KHZ;

devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL);
@@ -902,6 +892,10 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
reset_control_reset(tegra->reset);
disable_clk:
clk_disable_unprepare(tegra->clock);
remove_table:
dev_pm_opp_of_remove_table(&pdev->dev);
put_hw:
dev_pm_opp_put_supported_hw(tegra->opp_table);

return err;
}
@@ -913,11 +907,12 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor);

dev_pm_opp_remove_all_dynamic(&pdev->dev);

reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock);

dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_supported_hw(tegra->opp_table);

return 0;
}

0 comments on commit 16e8b2a

Please sign in to comment.