Commit 16e8b2a7 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Chanwoo Choi
Browse files

PM / devfreq: tegra30: Support interconnect and OPPs from device-tree



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: default avatarPeter Geis <pgwipeout@gmail.com>
Tested-by: default avatarNicolas Chauvet <kwizart@gmail.com>
Acked-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
parent afd589c7
Loading
Loading
Loading
Loading
+37 −42
Original line number Diff line number Diff line
@@ -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 @@ remove_opps:
	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;
}