mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-22 14:41:27 +00:00
PM / devfreq: tegra30: Support variable polling interval
The ACTMON governor is interrupt-driven and currently hardware's polling interval is fixed to 16ms in the driver. Devfreq supports variable polling interval by the generic governors, let's re-use the generic interface for changing of the polling interval. Now the polling interval can be changed dynamically via /sys/class/devfreq/devfreq0/polling_interval. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
This commit is contained in:
parent
5c0f6c7959
commit
f61ee20106
1 changed files with 66 additions and 12 deletions
|
@ -171,6 +171,8 @@ struct tegra_devfreq {
|
||||||
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
|
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
|
||||||
|
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
|
|
||||||
|
bool started;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tegra_actmon_emc_ratio {
|
struct tegra_actmon_emc_ratio {
|
||||||
|
@ -209,18 +211,26 @@ static void device_writel(struct tegra_devfreq_device *dev, u32 val,
|
||||||
writel_relaxed(val, dev->regs + offset);
|
writel_relaxed(val, dev->regs + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long do_percent(unsigned long val, unsigned int pct)
|
static unsigned long do_percent(unsigned long long val, unsigned int pct)
|
||||||
{
|
{
|
||||||
return val * pct / 100;
|
val = val * pct;
|
||||||
|
do_div(val, 100);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* High freq + high boosting percent + large polling interval are
|
||||||
|
* resulting in integer overflow when watermarks are calculated.
|
||||||
|
*/
|
||||||
|
return min_t(u64, val, U32_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
|
static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
|
||||||
struct tegra_devfreq_device *dev)
|
struct tegra_devfreq_device *dev)
|
||||||
{
|
{
|
||||||
u32 avg = dev->avg_count;
|
|
||||||
u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
|
u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
|
||||||
u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD;
|
u32 band = avg_band_freq * tegra->devfreq->profile->polling_ms;
|
||||||
|
u32 avg;
|
||||||
|
|
||||||
|
avg = min(dev->avg_count, U32_MAX - band);
|
||||||
device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
|
device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
|
||||||
|
|
||||||
avg = max(dev->avg_count, band);
|
avg = max(dev->avg_count, band);
|
||||||
|
@ -230,7 +240,7 @@ static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
|
||||||
static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
|
static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
|
||||||
struct tegra_devfreq_device *dev)
|
struct tegra_devfreq_device *dev)
|
||||||
{
|
{
|
||||||
u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
|
u32 val = tegra->cur_freq * tegra->devfreq->profile->polling_ms;
|
||||||
|
|
||||||
device_writel(dev, do_percent(val, dev->config->boost_up_threshold),
|
device_writel(dev, do_percent(val, dev->config->boost_up_threshold),
|
||||||
ACTMON_DEV_UPPER_WMARK);
|
ACTMON_DEV_UPPER_WMARK);
|
||||||
|
@ -309,7 +319,7 @@ static unsigned long actmon_device_target_freq(struct tegra_devfreq *tegra,
|
||||||
unsigned int avg_sustain_coef;
|
unsigned int avg_sustain_coef;
|
||||||
unsigned long target_freq;
|
unsigned long target_freq;
|
||||||
|
|
||||||
target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD;
|
target_freq = dev->avg_count / tegra->devfreq->profile->polling_ms;
|
||||||
avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold;
|
avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold;
|
||||||
target_freq = do_percent(target_freq, avg_sustain_coef);
|
target_freq = do_percent(target_freq, avg_sustain_coef);
|
||||||
|
|
||||||
|
@ -469,7 +479,7 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
|
||||||
|
|
||||||
dev->target_freq = tegra->cur_freq;
|
dev->target_freq = tegra->cur_freq;
|
||||||
|
|
||||||
dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
|
dev->avg_count = tegra->cur_freq * tegra->devfreq->profile->polling_ms;
|
||||||
device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
|
device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
|
||||||
|
|
||||||
tegra_devfreq_update_avg_wmark(tegra, dev);
|
tegra_devfreq_update_avg_wmark(tegra, dev);
|
||||||
|
@ -505,12 +515,15 @@ static void tegra_actmon_stop_devices(struct tegra_devfreq *tegra)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tegra_actmon_start(struct tegra_devfreq *tegra)
|
static int tegra_actmon_resume(struct tegra_devfreq *tegra)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
|
if (!tegra->devfreq->profile->polling_ms || !tegra->started)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
actmon_writel(tegra, tegra->devfreq->profile->polling_ms - 1,
|
||||||
ACTMON_GLB_PERIOD_CTRL);
|
ACTMON_GLB_PERIOD_CTRL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -558,8 +571,26 @@ err_stop:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tegra_actmon_stop(struct tegra_devfreq *tegra)
|
static int tegra_actmon_start(struct tegra_devfreq *tegra)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!tegra->started) {
|
||||||
|
tegra->started = true;
|
||||||
|
|
||||||
|
ret = tegra_actmon_resume(tegra);
|
||||||
|
if (ret)
|
||||||
|
tegra->started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_actmon_pause(struct tegra_devfreq *tegra)
|
||||||
|
{
|
||||||
|
if (!tegra->devfreq->profile->polling_ms || !tegra->started)
|
||||||
|
return;
|
||||||
|
|
||||||
disable_irq(tegra->irq);
|
disable_irq(tegra->irq);
|
||||||
|
|
||||||
cpufreq_unregister_notifier(&tegra->cpu_rate_change_nb,
|
cpufreq_unregister_notifier(&tegra->cpu_rate_change_nb,
|
||||||
|
@ -572,6 +603,12 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
|
||||||
clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
|
clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tegra_actmon_stop(struct tegra_devfreq *tegra)
|
||||||
|
{
|
||||||
|
tegra_actmon_pause(tegra);
|
||||||
|
tegra->started = false;
|
||||||
|
}
|
||||||
|
|
||||||
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
|
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
|
||||||
u32 flags)
|
u32 flags)
|
||||||
{
|
{
|
||||||
|
@ -629,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
|
||||||
stat->busy_time *= 100 / BUS_SATURATION_RATIO;
|
stat->busy_time *= 100 / BUS_SATURATION_RATIO;
|
||||||
|
|
||||||
/* Number of cycles in a sampling period */
|
/* Number of cycles in a sampling period */
|
||||||
stat->total_time = ACTMON_SAMPLING_PERIOD * cur_freq;
|
stat->total_time = tegra->devfreq->profile->polling_ms * cur_freq;
|
||||||
|
|
||||||
stat->busy_time = min(stat->busy_time, stat->total_time);
|
stat->busy_time = min(stat->busy_time, stat->total_time);
|
||||||
|
|
||||||
|
@ -637,7 +674,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct devfreq_dev_profile tegra_devfreq_profile = {
|
static struct devfreq_dev_profile tegra_devfreq_profile = {
|
||||||
.polling_ms = 0,
|
.polling_ms = ACTMON_SAMPLING_PERIOD,
|
||||||
.target = tegra_devfreq_target,
|
.target = tegra_devfreq_target,
|
||||||
.get_dev_status = tegra_devfreq_get_dev_status,
|
.get_dev_status = tegra_devfreq_get_dev_status,
|
||||||
};
|
};
|
||||||
|
@ -677,6 +714,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
|
||||||
unsigned int event, void *data)
|
unsigned int event, void *data)
|
||||||
{
|
{
|
||||||
struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent);
|
struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent);
|
||||||
|
unsigned int *new_delay = data;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -696,6 +734,21 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
|
||||||
devfreq_monitor_stop(devfreq);
|
devfreq_monitor_stop(devfreq);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DEVFREQ_GOV_INTERVAL:
|
||||||
|
/*
|
||||||
|
* ACTMON hardware supports up to 256 milliseconds for the
|
||||||
|
* sampling period.
|
||||||
|
*/
|
||||||
|
if (*new_delay > 256) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tegra_actmon_pause(tegra);
|
||||||
|
devfreq_interval_update(devfreq, new_delay);
|
||||||
|
ret = tegra_actmon_resume(tegra);
|
||||||
|
break;
|
||||||
|
|
||||||
case DEVFREQ_GOV_SUSPEND:
|
case DEVFREQ_GOV_SUSPEND:
|
||||||
tegra_actmon_stop(tegra);
|
tegra_actmon_stop(tegra);
|
||||||
devfreq_monitor_suspend(devfreq);
|
devfreq_monitor_suspend(devfreq);
|
||||||
|
@ -715,6 +768,7 @@ static struct devfreq_governor tegra_devfreq_governor = {
|
||||||
.get_target_freq = tegra_governor_get_target,
|
.get_target_freq = tegra_governor_get_target,
|
||||||
.event_handler = tegra_governor_event_handler,
|
.event_handler = tegra_governor_event_handler,
|
||||||
.immutable = true,
|
.immutable = true,
|
||||||
|
.interrupt_driven = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tegra_devfreq_probe(struct platform_device *pdev)
|
static int tegra_devfreq_probe(struct platform_device *pdev)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue