More improvements for Tegra30 devfreq driver
Subsystems (3)
Dmitry Osipenko
maintainerv9 cover More improvements for Tegra30 devfreq driver
Hello,
This series addresses some additional review comments that were made by Thierry Reding to [1], makes several important changes to the driver, reducing interrupts activity, and adds new features. In the end I'm proposing myself as a maintainer for the Tegra devfreq drivers.
[1] https://lore.kernel.org/lkml/[email protected]/T/
Changelog:
v9: Addressed review comments that were made by Chanwoo Choi to v8 by:
1. Updating the "Add new interrupt_driven flag for governors" patch in accordance to the comments.
2. Factoring out the change of the dependency threshold units into a separate patch:
PM / devfreq: tegra30: Use kHz units for dependency threshold
In this patch I also corrected the new threshold value because it was miscalculated in v8 using MCALL coefficient instead of MCCPU.
Patch "Support variable polling interval" now again handles properly governor's enable-state while polling interval is changed, it won't start polling if governor is stopped. The problem was introduced in v8 when switched away from using devfreq->stop_polling because using tegra->cur_freq is not enough and the whole "stop_polling" logic needs to be duplicated in the driver.
v8: Addressed review comments that were made by Chanwoo Choi to v7 by:
1. Taking into account suggestions that were made for the "Add new interrupt_driven flag for governors".
2. Patch "Support variable polling interval" now doesn't use devfreq->stop_polling and uses tegra->cur_freq instead. There is now a comment for the 256ms max polling interval limitation as well.
The patch "Increase sampling period to 16ms" is dropped from the series for now since we found out that it doesn't bring much benefit.
The cur_freq value is now refreshed after clk-notifier registration in the "Move clk-notifier's registration to governor's start" patch, for consistency. This also becomes useful for the "Support variable polling interval" patch since it now uses the cur_freq for tracking of the governor's enable-state.
The patch "Support variable polling interval" now has a proper scaling of the dependency threshold value, which I missed to backport into v7 after dropping some of the v6 patches. The do_percent() function now doesn't suffer from integer overflow bug when freq, boosting and polling interval values are too large.
v7: Addressed review comments that were made by Chanwoo Choi to v6 by:
1. Dropping patch "Ensure that target freq won't overflow".
2. Making "Use kHz units uniformly in the code" patch more easier for review and improving its commit's message.
3. The "Support variable polling interval" patch is reworked, now it doesn't touch things that should stay internal to devfreq core, like devfreq->stop_polling and etc. In a result a new generic interrupt_driven flag is available for devfreq governors (added in a new patch "Add new interrupt_driven flag for governors").
Patch "Set up watermarks properly" and related to it patches are dropped because I found a case where it doesn't work well. These patches need some more thought and not ready for now, I'll continue working on them. I extracted useful changes from the dropped patches and then tuned up MCCPU boost-down coefficient to keep ACTMON silent when system is idling, in a result there are these new patches:
1. Don't enable already enabled consecutive interrupts
2. Disable consecutive interrupts when appropriate
3. Tune up MCCPU boost-down coefficient
v6: Addressed review comment that was made by Chanwoo Choi to v5 by
squashing "Define ACTMON_DEV_CTRL_STOP" patch into the "Use CPUFreq notifier" patch.
v5: Addressed review comments that were made by Chanwoo Choi to v4 by
squashing few patches, dropping some questionable patches, rewording comments to the code, restructuring the code and etc.
These patches are now dropped from the series:
PM / devfreq: tegra30: Use tracepoints for debugging PM / devfreq: tegra30: Inline all one-line functions
The interrupt-optimization patches are squashed into a single patch:
PM / devfreq: tegra30: Reduce unnecessary interrupts activity
because it's better to keep the optimizations as a separate change and this also helps to reduce code churning, since the code changes depend on a previous patch in order to stay cleaner.
Fixed a lockup bug that I spotted recently, which is caused by a clk-notifier->cpufreq_get()->clk_set_rate() sequence. Now a non-blocking variant of CPU's frequency retrieving is used, i.e. cpufreq_quick_get().
Further optimized the CPUFreq notifier by postponing the delayed updating in accordance to the polling interval, this actually uncovered the above lockup bug.
Implemented new minor driver feature in the new patch:
PM / devfreq: tegra30: Support variable polling interval
v4: Added two new patches to the series:
PM / devfreq: tegra30: Synchronize average count on target's update PM / devfreq: tegra30: Increase sampling period to 16ms
The first patch addresses problem where governor could get stuck due to outdated "average count" value which is snapshoted by ISR and there are cases where manual update of the value is required.
The second patch is just a minor optimization.
v3: Added support for tracepoints, replacing the debug messages.
Fixed few more bugs with the help of tracepoints.
New patches in this version:
PM / devfreq: tegra30: Use tracepoints for debugging
PM / devfreq: tegra30: Optimize CPUFreq notifier
PM / devfreq: tegra30: Optimize upper consecutive watermark selection PM / devfreq: tegra30: Optimize upper average watermark selection PM / devfreq: tegra30: Include appropriate header
Some of older patches of this series also got some extra minor polish.
v2: Added more patches that are cleaning driver's code further and
squashing another kHz conversion bug.
The patch "Rework frequency management logic" of the v1 series is now converted to "Set up watermarks properly" because I found some problems in the original patch and then realized that there is no need to change the logic much. So the logic mostly preserved and only got improvements.
The series is based on the today's linux-next (25 Jun) and takes into account minor changes that MyungJoo Ham made to the already queued patches from the first batch [1].
Dmitry Osipenko (19):
PM / devfreq: tegra30: Change irq type to unsigned int
PM / devfreq: tegra30: Keep interrupt disabled while governor is
stopped
PM / devfreq: tegra30: Handle possible round-rate error
PM / devfreq: tegra30: Drop write-barrier
PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out PM / devfreq: tegra30: Use kHz units uniformly in the code PM / devfreq: tegra30: Use CPUFreq notifier
PM / devfreq: tegra30: Move clk-notifier's registration to governor's
start
PM / devfreq: tegra30: Reset boosting on startup
PM / devfreq: tegra30: Don't enable consecutive-down interrupt on
startup
PM / devfreq: tegra30: Constify structs
PM / devfreq: tegra30: Include appropriate header
PM / devfreq: tegra30: Don't enable already enabled consecutive
interrupts
PM / devfreq: tegra30: Disable consecutive interrupts when appropriate PM / devfreq: tegra30: Use kHz units for dependency threshold PM / devfreq: Add new interrupt_driven flag for governors PM / devfreq: tegra30: Support variable polling interval PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient PM / devfreq: tegra20/30: Add Dmitry as a maintainer
MAINTAINERS | 9 + drivers/devfreq/devfreq.c | 17 ++ drivers/devfreq/governor.h | 3 + drivers/devfreq/tegra30-devfreq.c | 417 ++++++++++++++++++++++-------- 4 files changed, 334 insertions(+), 112 deletions(-)
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 1/19 PM / devfreq: tegra30: Change irq type to unsigned int
IRQ numbers are always positive, hence the corresponding variable should be unsigned to keep types consistent. This is a minor change that cleans up code a tad more.
--- drivers/devfreq/tegra30-devfreq.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/devfreq/tegra30-devfreq.c +4 -4
@@ -160,7 +160,7 @@ struct tegra_devfreq {struct tegra_devfreq_device devices[ARRAY_SIZE (actmon_device_configs)]; -int irq; +unsigned int irq; };struct tegra_actmon_emc_ratio {
@@ -618,12 +618,12 @@ static int tegra_devfreq_probe(struct platform_device *pdev)return PTR_ERR (tegra->emc_clock); } - tegra->irq =platform_get_irq (pdev,0 ); -if (tegra->irq <0 ) { - err = tegra->irq; + err =platform_get_irq (pdev,0 ); +if (err <0 ) {dev_err (&pdev->dev,"Failed to get IRQ: %d\n" , err);return err; } + tegra->irq = err;reset_control_assert (tegra->reset);
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 2/19 PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped
In reply to Dmitry OsipenkoThere is no real need to keep interrupt always-enabled, will be nicer to keep it disabled while governor is inactive.
--- drivers/devfreq/tegra30-devfreq.c | 47 ++++++++++++++++--------------- 1 file changed, 24 insertions(+), 23 deletions(-)
drivers/devfreq/tegra30-devfreq.c +24 -23
@@ -11,6 +11,7 @@#include <linux/devfreq.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h>
@@ -416,8 +417,6 @@ static void tegra_actmon_start(struct tegra_devfreq *tegra) {unsigned int i; -disable_irq (tegra->irq); -actmon_writel (tegra,ACTMON_SAMPLING_PERIOD -1 ,ACTMON_GLB_PERIOD_CTRL );
@@ -442,8 +441,6 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra) }actmon_write_barrier (tegra); - -enable_irq (tegra->irq); } static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
@@ -552,6 +549,12 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, {struct tegra_devfreq *tegra =dev_get_drvdata (devfreq->dev.parent); +/* +* Couple devfreq-device with the governor early because it is +* needed at the moment of governor's start (used by ISR). +*/ + tegra->devfreq = devfreq; +switch (event) {case DEVFREQ_GOV_START :devfreq_monitor_start (devfreq);
@@ -586,10 +589,11 @@ static struct devfreq_governor tegra_devfreq_governor = {static int tegra_devfreq_probe (struct platform_device *pdev) { -struct tegra_devfreq *tegra;struct tegra_devfreq_device *dev; -unsigned int i; +struct tegra_devfreq *tegra; +struct devfreq *devfreq;unsigned long rate; +unsigned int i;int err; tegra =devm_kzalloc (&pdev->dev,sizeof (*tegra),GFP_KERNEL );
@@ -625,6 +629,16 @@ static int tegra_devfreq_probe(struct platform_device *pdev) } tegra->irq = err; +irq_set_status_flags (tegra->irq,IRQ_NOAUTOEN ); + + err =devm_request_threaded_irq (&pdev->dev, tegra->irq,NULL , + actmon_thread_isr,IRQF_ONESHOT , +"tegra-devfreq" , tegra); +if (err) { +dev_err (&pdev->dev,"Interrupt request failed: %d\n" , err); +return err; + } +reset_control_assert (tegra->reset); err =clk_prepare_enable (tegra->clock);
@@ -672,28 +686,15 @@ static int tegra_devfreq_probe(struct platform_device *pdev) } tegra_devfreq_profile.initial_freq =clk_get_rate (tegra->emc_clock); - tegra->devfreq =devfreq_add_device (&pdev->dev, - &tegra_devfreq_profile, -"tegra_actmon" , -NULL ); -if (IS_ERR (tegra->devfreq)) { - err =PTR_ERR (tegra->devfreq); + devfreq =devfreq_add_device (&pdev->dev, &tegra_devfreq_profile, +"tegra_actmon" ,NULL ); +if (IS_ERR (devfreq)) { + err =PTR_ERR (devfreq); goto remove_governor; } - err =devm_request_threaded_irq (&pdev->dev, tegra->irq,NULL , - actmon_thread_isr,IRQF_ONESHOT , -"tegra-devfreq" , tegra); -if (err) { -dev_err (&pdev->dev,"Interrupt request failed: %d\n" , err); - goto remove_devfreq; - } -return 0 ; -remove_devfreq: -devfreq_remove_device (tegra->devfreq); - remove_governor:devfreq_remove_governor (&tegra_devfreq_governor);
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 3/19 PM / devfreq: tegra30: Handle possible round-rate error
In reply to Dmitry OsipenkoThe EMC clock rate rounding technically could fail, hence let's handle the error cases properly.
--- drivers/devfreq/tegra30-devfreq.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
drivers/devfreq/tegra30-devfreq.c +15 -2
@@ -592,8 +592,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)struct tegra_devfreq_device *dev;struct tegra_devfreq *tegra;struct devfreq *devfreq; -unsigned long rate;unsigned int i; +long rate;int err; tegra =devm_kzalloc (&pdev->dev,sizeof (*tegra),GFP_KERNEL );
@@ -650,8 +650,14 @@ static int tegra_devfreq_probe(struct platform_device *pdev)reset_control_deassert (tegra->reset); - tegra->max_freq =clk_round_rate (tegra->emc_clock,ULONG_MAX ) /KHZ ; + rate =clk_round_rate (tegra->emc_clock,ULONG_MAX ); +if (rate <0 ) { +dev_err (&pdev->dev,"Failed to round clock rate: %ld\n" , rate); +return rate; + } + tegra->cur_freq =clk_get_rate (tegra->emc_clock) /KHZ ; + tegra->max_freq = rate /KHZ ;for (i =0 ; i <ARRAY_SIZE (actmon_device_configs); i++) { dev = tegra->devices + i;
@@ -662,6 +668,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev)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,0 );if (err) {dev_err (&pdev->dev,"Failed to add OPP: %d\n" , err);
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 4/19 PM / devfreq: tegra30: Drop write-barrier
In reply to Dmitry OsipenkoThere is no need in a write-barrier now, given that interrupt masking is handled by CPU's GIC now. Hence we know exactly that interrupt won't fire after stopping the devfreq's governor. In other cases we don't care about potential buffering of the writes to hardware and thus there is no need to stall CPU.
--- drivers/devfreq/tegra30-devfreq.c | 14 -------------- 1 file changed, 14 deletions(-)
drivers/devfreq/tegra30-devfreq.c +0 -14
@@ -230,12 +230,6 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, ACTMON_DEV_LOWER_WMARK); } -static void actmon_write_barrier (struct tegra_devfreq *tegra) -{ -/* ensure the update has reached the ACTMON */ -readl (tegra->regs +ACTMON_GLB_STATUS ); -} -static void actmon_isr_device (struct tegra_devfreq *tegra,struct tegra_devfreq_device *dev) {
@@ -287,8 +281,6 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,device_writel (dev, dev_ctrl,ACTMON_DEV_CTRL );device_writel (dev,ACTMON_INTR_STATUS_CLEAR ,ACTMON_DEV_INTR_STATUS ); - -actmon_write_barrier (tegra); } static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
@@ -376,8 +368,6 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,tegra_devfreq_update_wmark (tegra, dev); } -actmon_write_barrier (tegra); -return NOTIFY_OK ; }
@@ -423,8 +413,6 @@ static void tegra_actmon_start(struct tegra_devfreq *tegra)for (i =0 ; i <ARRAY_SIZE (tegra->devices); i++)tegra_actmon_configure_device (tegra, &tegra->devices[i]); -actmon_write_barrier (tegra); -enable_irq (tegra->irq); }
@@ -439,8 +427,6 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)device_writel (&tegra->devices[i],ACTMON_INTR_STATUS_CLEAR ,ACTMON_DEV_INTR_STATUS ); } - -actmon_write_barrier (tegra); } static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 5/19 PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out
In reply to Dmitry OsipenkoThere is another kHz-conversion bug in the code, resulting in integer overflow. Although, this time the resulting value is 4294966296 and it's close to ULONG_MAX, which is okay in this case.
--- drivers/devfreq/tegra30-devfreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/devfreq/tegra30-devfreq.c +3 -1
@@ -69,6 +69,8 @@#define KHZ 1000 +#define KHZ_MAX (ULONG_MAX / KHZ) +/* Assume that the bus is saturated if the utilization is 25% */ #define BUS_SATURATION_RATIO 25
@@ -170,7 +172,7 @@ struct tegra_actmon_emc_ratio { };static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { - {1400000 ,ULONG_MAX }, + {1400000 ,KHZ_MAX }, {1200000 ,750000 }, {1100000 ,600000 }, {1000000 ,500000 },
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 6/19 PM / devfreq: tegra30: Use kHz units uniformly in the code
In reply to Dmitry OsipenkoPart of the code uses Hz units and the other kHz, let's switch to kHz everywhere for consistency. A small benefit from this change (besides code's cleanup) is that now powertop utility correctly displays devfreq's stats, for some reason it expects them to be in kHz.
--- drivers/devfreq/tegra30-devfreq.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/devfreq/tegra30-devfreq.c +6 -5
@@ -448,7 +448,7 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq, rate =dev_pm_opp_get_freq (opp);dev_pm_opp_put (opp); - err =clk_set_min_rate (tegra->emc_clock, rate); + err =clk_set_min_rate (tegra->emc_clock, rate *KHZ );if (err)return err;
@@ -477,7 +477,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 *KHZ ; + stat->current_frequency = cur_freq; actmon_dev = &tegra->devices[MCALL ];
@@ -527,7 +527,7 @@ static int tegra_governor_get_target(struct devfreq *devfreq, target_freq =max (target_freq, dev->target_freq); } - *freq = target_freq *KHZ ; + *freq = target_freq;return 0 ; }
@@ -663,7 +663,7 @@ static int tegra_devfreq_probe(struct platform_device *pdev) goto remove_opps; } - err =dev_pm_opp_add (&pdev->dev, rate,0 ); + 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;
@@ -686,7 +686,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev) goto unreg_notifier; } - tegra_devfreq_profile.initial_freq =clk_get_rate (tegra->emc_clock); + tegra_devfreq_profile.initial_freq = tegra->cur_freq; + devfreq =devfreq_add_device (&pdev->dev, &tegra_devfreq_profile,"tegra_actmon" ,NULL );if (IS_ERR (devfreq)) {
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 7/19 PM / devfreq: tegra30: Use CPUFreq notifier
In reply to Dmitry OsipenkoThe CPU's client need to take into account that CPUFreq may change while memory activity not, staying high. Thus an appropriate frequency notifier should be used in addition to the clk-notifier.
--- drivers/devfreq/tegra30-devfreq.c | 180 +++++++++++++++++++++++++----- 1 file changed, 155 insertions(+), 25 deletions(-)
drivers/devfreq/tegra30-devfreq.c +155 -25
@@ -17,6 +17,7 @@#include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/reset.h> +#include <linux/workqueue.h> #include "governor.h"
@@ -34,6 +35,8 @@#define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN BIT(30)#define ACTMON_DEV_CTRL_ENB BIT(31) +#define ACTMON_DEV_CTRL_STOP 0x00000000 +#define ACTMON_DEV_UPPER_WMARK 0x4#define ACTMON_DEV_LOWER_WMARK 0x8#define ACTMON_DEV_INIT_AVG 0xc
@@ -159,7 +162,10 @@ struct tegra_devfreq {struct clk *emc_clock;unsigned long max_freq;unsigned long cur_freq; -struct notifier_block rate_change_nb; +struct notifier_block clk_rate_change_nb; + +struct delayed_work cpufreq_update_work; +struct notifier_block cpu_rate_change_nb;struct tegra_devfreq_device devices[ARRAY_SIZE (actmon_device_configs)];
@@ -303,22 +309,32 @@ static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,return 0 ; } +static unsigned long actmon_device_target_freq (struct tegra_devfreq *tegra, +struct tegra_devfreq_device *dev) +{ +unsigned int avg_sustain_coef; +unsigned long target_freq; + + target_freq = dev->avg_count /ACTMON_SAMPLING_PERIOD ; + avg_sustain_coef =100 *100 / dev->config->boost_up_threshold; + target_freq =do_percent (target_freq, avg_sustain_coef); + target_freq += dev->boost_freq; + +return target_freq; +} +static void actmon_update_target (struct tegra_devfreq *tegra,struct tegra_devfreq_device *dev) {unsigned long cpu_freq =0 ;unsigned long static_cpu_emc_freq =0 ; -unsigned int avg_sustain_coef;if (dev->config->avg_dependency_threshold) { - cpu_freq =cpufreq_get (0 ); + cpu_freq =cpufreq_quick_get (0 ); static_cpu_emc_freq =actmon_cpu_to_emc_rate (tegra, cpu_freq); } - dev->target_freq = dev->avg_count /ACTMON_SAMPLING_PERIOD ; - avg_sustain_coef =100 *100 / dev->config->boost_up_threshold; - dev->target_freq =do_percent (dev->target_freq, avg_sustain_coef); - dev->target_freq += dev->boost_freq; + dev->target_freq =actmon_device_target_freq (tegra, dev);if (dev->avg_count >= dev->config->avg_dependency_threshold) dev->target_freq =max (dev->target_freq, static_cpu_emc_freq);
@@ -349,8 +365,8 @@ static irqreturn_t actmon_thread_isr(int irq, void *data)return handled ?IRQ_HANDLED :IRQ_NONE ; } -static int tegra_actmon_rate_notify_cb (struct notifier_block *nb, -unsigned long action,void *ptr) +static int tegra_actmon_clk_notify_cb (struct notifier_block *nb, +unsigned long action,void *ptr) {struct clk_notifier_data *data = ptr;struct tegra_devfreq *tegra;
@@ -360,7 +376,7 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,if (action !=POST_RATE_CHANGE )return NOTIFY_OK ; - tegra = container_of(nb, struct tegra_devfreq, rate_change_nb); + tegra = container_of(nb, struct tegra_devfreq, clk_rate_change_nb); tegra->cur_freq = data->new_rate /KHZ ;
@@ -373,6 +389,79 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,return NOTIFY_OK ; } +static void tegra_actmon_delayed_update (struct work_struct *work) +{ + struct tegra_devfreq *tegra = container_of(work, struct tegra_devfreq, + cpufreq_update_work.work); + +mutex_lock (&tegra->devfreq->lock); +update_devfreq (tegra->devfreq); +mutex_unlock (&tegra->devfreq->lock); +} + +static unsigned long +tegra_actmon_cpufreq_contribution (struct tegra_devfreq *tegra, +unsigned int cpu_freq) +{ +unsigned long static_cpu_emc_freq, dev_freq; + +/* check whether CPU's freq is taken into account at all */ +if (tegra->devices[MCCPU ].avg_count < + tegra->devices[MCCPU ].config->avg_dependency_threshold) +return 0 ; + + static_cpu_emc_freq =actmon_cpu_to_emc_rate (tegra, cpu_freq); + dev_freq =actmon_device_target_freq (tegra, &tegra->devices[MCCPU ]); + +if (dev_freq >= static_cpu_emc_freq) +return 0 ; + +return static_cpu_emc_freq; +} + +static int tegra_actmon_cpu_notify_cb (struct notifier_block *nb, +unsigned long action,void *ptr) +{ +struct cpufreq_freqs *freqs = ptr; +struct tegra_devfreq *tegra; +unsigned long old, new, delay; + +if (action !=CPUFREQ_POSTCHANGE ) +return NOTIFY_OK ; + + tegra = container_of(nb, struct tegra_devfreq, cpu_rate_change_nb); + +/* +* Quickly check whether CPU frequency should be taken into account +* at all, without blocking CPUFreq's core. +*/ +if (mutex_trylock (&tegra->devfreq->lock)) { + old =tegra_actmon_cpufreq_contribution (tegra, freqs->old); + new =tegra_actmon_cpufreq_contribution (tegra, freqs->new); +mutex_unlock (&tegra->devfreq->lock); + +/* +* If CPU's frequency shouldn't be taken into account at +* the moment, then there is no need to update the devfreq's +* state because ISR will re-check CPU's frequency on the +* next interrupt. +*/ +if (old == new) +return NOTIFY_OK ; + } + +/* +* CPUFreq driver should support CPUFREQ_ASYNC_NOTIFICATION in order +* to allow asynchronous notifications. This means we can't block +* here for too long, otherwise CPUFreq's core will complain with a +* warning splat. +*/ + delay =msecs_to_jiffies (ACTMON_SAMPLING_PERIOD ); +schedule_delayed_work (&tegra->cpufreq_update_work, delay); + +return NOTIFY_OK ; +} +static void tegra_actmon_configure_device (struct tegra_devfreq *tegra,struct tegra_devfreq_device *dev) {
@@ -405,9 +494,22 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,device_writel (dev, val,ACTMON_DEV_CTRL ); } -static void tegra_actmon_start (struct tegra_devfreq *tegra) +static void tegra_actmon_stop_devices (struct tegra_devfreq *tegra) +{ +struct tegra_devfreq_device *dev = tegra->devices; +unsigned int i; + +for (i =0 ; i <ARRAY_SIZE (tegra->devices); i++, dev++) { +device_writel (dev,ACTMON_DEV_CTRL_STOP ,ACTMON_DEV_CTRL ); +device_writel (dev,ACTMON_INTR_STATUS_CLEAR , +ACTMON_DEV_INTR_STATUS ); + } +} + +static int tegra_actmon_start (struct tegra_devfreq *tegra) {unsigned int i; +int err;actmon_writel (tegra,ACTMON_SAMPLING_PERIOD -1 ,ACTMON_GLB_PERIOD_CTRL );
@@ -415,20 +517,41 @@ static void tegra_actmon_start(struct tegra_devfreq *tegra)for (i =0 ; i <ARRAY_SIZE (tegra->devices); i++)tegra_actmon_configure_device (tegra, &tegra->devices[i]); +/* +* We are estimating CPU's memory bandwidth requirement based on +* amount of memory accesses and system's load, judging by CPU's +* frequency. We also don't want to receive events about CPU's +* frequency transaction when governor is stopped, hence notifier +* is registered dynamically. +*/ + err =cpufreq_register_notifier (&tegra->cpu_rate_change_nb, +CPUFREQ_TRANSITION_NOTIFIER ); +if (err) { +dev_err (tegra->devfreq->dev.parent, +"Failed to register rate change notifier: %d\n" , err); + goto err_stop; + } +enable_irq (tegra->irq); + +return 0 ; + +err_stop: +tegra_actmon_stop_devices (tegra); + +return err; }static void tegra_actmon_stop (struct tegra_devfreq *tegra) { -unsigned int i; -disable_irq (tegra->irq); -for (i =0 ; i <ARRAY_SIZE (tegra->devices); i++) { -device_writel (&tegra->devices[i],0x00000000 ,ACTMON_DEV_CTRL ); -device_writel (&tegra->devices[i],ACTMON_INTR_STATUS_CLEAR , -ACTMON_DEV_INTR_STATUS ); - } +cpufreq_unregister_notifier (&tegra->cpu_rate_change_nb, +CPUFREQ_TRANSITION_NOTIFIER ); + +cancel_delayed_work_sync (&tegra->cpufreq_update_work); + +tegra_actmon_stop_devices (tegra); } static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
@@ -536,6 +659,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, unsigned int event, void *data) { struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent); +int ret =0 ;/* * Couple devfreq-device with the governor early because it is
@@ -546,7 +670,7 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,switch (event) {case DEVFREQ_GOV_START :devfreq_monitor_start (devfreq); -tegra_actmon_start (tegra); + ret =tegra_actmon_start (tegra);break ;case DEVFREQ_GOV_STOP :
@@ -561,11 +685,11 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,case DEVFREQ_GOV_RESUME :devfreq_monitor_resume (devfreq); -tegra_actmon_start (tegra); + ret =tegra_actmon_start (tegra);break ; } -return 0 ; +return ret; }static struct devfreq_governor tegra_devfreq_governor = {
@@ -672,8 +796,14 @@ static int tegra_devfreq_probe(struct platform_device *pdev)platform_set_drvdata (pdev, tegra); - tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; - err =clk_notifier_register (tegra->emc_clock, &tegra->rate_change_nb); + tegra->cpu_rate_change_nb.notifier_call = tegra_actmon_cpu_notify_cb; + +INIT_DELAYED_WORK (&tegra->cpufreq_update_work, + tegra_actmon_delayed_update); + + tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb; + err =clk_notifier_register (tegra->emc_clock, + &tegra->clk_rate_change_nb);if (err) {dev_err (&pdev->dev,"Failed to register rate change notifier\n" );
@@ -701,7 +831,7 @@ static int tegra_devfreq_probe(struct platform_device *pdev)devfreq_remove_governor (&tegra_devfreq_governor); unreg_notifier: -clk_notifier_unregister (tegra->emc_clock, &tegra->rate_change_nb); +clk_notifier_unregister (tegra->emc_clock, &tegra->clk_rate_change_nb); remove_opps:dev_pm_opp_remove_all_dynamic (&pdev->dev);
@@ -719,7 +849,7 @@ static int tegra_devfreq_remove(struct platform_device *pdev)devfreq_remove_device (tegra->devfreq);devfreq_remove_governor (&tegra_devfreq_governor); -clk_notifier_unregister (tegra->emc_clock, &tegra->rate_change_nb); +clk_notifier_unregister (tegra->emc_clock, &tegra->clk_rate_change_nb);dev_pm_opp_remove_all_dynamic (&pdev->dev);reset_control_reset (tegra->reset);
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 8/19 PM / devfreq: tegra30: Move clk-notifier's registration to governor's start
In reply to Dmitry OsipenkoThere is no point in receiving of the notifications while governor is stopped, let's keep them disabled like we do for the CPU freq-change notifications. This also fixes a potential use-after-free bug if notification happens after device's removal.
--- drivers/devfreq/tegra30-devfreq.c | 39 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-)
drivers/devfreq/tegra30-devfreq.c +23 -16
@@ -514,6 +514,21 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)actmon_writel (tegra,ACTMON_SAMPLING_PERIOD -1 ,ACTMON_GLB_PERIOD_CTRL ); +/* +* CLK notifications are needed in order to reconfigure the upper +* consecutive watermark in accordance to the actual clock rate +* to avoid unnecessary upper interrupts. +*/ + err =clk_notifier_register (tegra->emc_clock, + &tegra->clk_rate_change_nb); +if (err) { +dev_err (tegra->devfreq->dev.parent, +"Failed to register rate change notifier\n" ); +return err; + } + + tegra->cur_freq =clk_get_rate (tegra->emc_clock) /KHZ ; +for (i =0 ; i <ARRAY_SIZE (tegra->devices); i++)tegra_actmon_configure_device (tegra, &tegra->devices[i]);
@@ -539,6 +554,8 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra) err_stop:tegra_actmon_stop_devices (tegra); +clk_notifier_unregister (tegra->emc_clock, &tegra->clk_rate_change_nb); +return err; }
@@ -552,6 +569,8 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)cancel_delayed_work_sync (&tegra->cpufreq_update_work);tegra_actmon_stop_devices (tegra); + +clk_notifier_unregister (tegra->emc_clock, &tegra->clk_rate_change_nb); } static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
@@ -768,7 +787,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)return rate; } - tegra->cur_freq =clk_get_rate (tegra->emc_clock) /KHZ ; tegra->max_freq = rate /KHZ ;for (i =0 ; i <ARRAY_SIZE (actmon_device_configs); i++) {
@@ -796,27 +814,20 @@ static int tegra_devfreq_probe(struct platform_device *pdev)platform_set_drvdata (pdev, tegra); + tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb; tegra->cpu_rate_change_nb.notifier_call = tegra_actmon_cpu_notify_cb;INIT_DELAYED_WORK (&tegra->cpufreq_update_work, tegra_actmon_delayed_update); - tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb; - err =clk_notifier_register (tegra->emc_clock, - &tegra->clk_rate_change_nb); -if (err) { -dev_err (&pdev->dev, -"Failed to register rate change notifier\n" ); - goto remove_opps; - } - err =devfreq_add_governor (&tegra_devfreq_governor);if (err) {dev_err (&pdev->dev,"Failed to add governor: %d\n" , err); - goto unreg_notifier; + goto remove_opps; } - tegra_devfreq_profile.initial_freq = tegra->cur_freq; + 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 );
@@ -830,9 +841,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev) remove_governor:devfreq_remove_governor (&tegra_devfreq_governor); -unreg_notifier: -clk_notifier_unregister (tegra->emc_clock, &tegra->clk_rate_change_nb); - remove_opps:dev_pm_opp_remove_all_dynamic (&pdev->dev);
@@ -849,7 +857,6 @@ static int tegra_devfreq_remove(struct platform_device *pdev)devfreq_remove_device (tegra->devfreq);devfreq_remove_governor (&tegra_devfreq_governor); -clk_notifier_unregister (tegra->emc_clock, &tegra->clk_rate_change_nb);dev_pm_opp_remove_all_dynamic (&pdev->dev);reset_control_reset (tegra->reset);
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 9/19 PM / devfreq: tegra30: Reset boosting on startup
In reply to Dmitry OsipenkoGovernor could be stopped while boosting is active. We have assumption that everything is reset on governor's restart, including the boosting value, which was missed.
--- drivers/devfreq/tegra30-devfreq.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/devfreq/tegra30-devfreq.c +3 -0
@@ -467,6 +467,9 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, {u32 val =0 ; +/* reset boosting on governor's restart */ + dev->boost_freq =0 ; + dev->target_freq = tegra->cur_freq; dev->avg_count = tegra->cur_freq *ACTMON_SAMPLING_PERIOD ;
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 10/19 PM / devfreq: tegra30: Don't enable consecutive-down interrupt on startup
In reply to Dmitry OsipenkoThe consecutive-down event tells that we should perform frequency de-boosting, but boosting is in a reset state on start and hence the event won't do anything useful for us and it will be just a dummy interrupt request.
--- drivers/devfreq/tegra30-devfreq.c | 1 - 1 file changed, 1 deletion(-)
drivers/devfreq/tegra30-devfreq.c +0 -1
@@ -490,7 +490,6 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; val |=ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN ; val |=ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN ; - val |=ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN ; val |=ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN ; val |=ACTMON_DEV_CTRL_ENB ;
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 11/19 PM / devfreq: tegra30: Constify structs
In reply to Dmitry OsipenkoConstify unmodifiable structs, for consistency.
--- drivers/devfreq/tegra30-devfreq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/devfreq/tegra30-devfreq.c +3 -3
@@ -108,7 +108,7 @@ enum tegra_actmon_device { MCCPU, }; -static struct tegra_devfreq_device_config actmon_device_configs[] = { +static const struct tegra_devfreq_device_config actmon_device_configs[] = { {/* MCALL: All memory accesses (including from the CPUs) */ .offset =0x1c0 ,
@@ -177,7 +177,7 @@ struct tegra_actmon_emc_ratio {unsigned long emc_freq; }; -static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { +static const struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { {1400000 ,KHZ_MAX }, {1200000 ,750000 }, {1100000 ,600000 },
@@ -295,7 +295,7 @@ static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra, unsigned long cpu_freq) {unsigned int i; -struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios; +const struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios;for (i =0 ; i <ARRAY_SIZE (actmon_emc_ratios); i++, ratio++) {if (cpu_freq >= ratio->cpu_freq) {
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 12/19 PM / devfreq: tegra30: Include appropriate header
In reply to Dmitry OsipenkoIt's not very correct to include mod_devicetable.h for the OF device drivers and of_device.h should be included instead.
--- drivers/devfreq/tegra30-devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/devfreq/tegra30-devfreq.c +1 -1
@@ -13,7 +13,7 @@#include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/reset.h>
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 13/19 PM / devfreq: tegra30: Don't enable already enabled consecutive interrupts
In reply to Dmitry OsipenkoConsecutive up/down interrupt-bit is set in the interrupt status register only if that interrupt was previously enabled. Thus enabling the already enabled interrupt doesn't do much for us.
--- drivers/devfreq/tegra30-devfreq.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/devfreq/tegra30-devfreq.c +0 -4
@@ -261,8 +261,6 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,if (dev->boost_freq >= tegra->max_freq) dev->boost_freq = tegra->max_freq; -else - dev_ctrl |=ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN ; }else if (intr_status &ACTMON_DEV_INTR_CONSECUTIVE_LOWER ) {/* * new_boost = old_boost * down_coef
@@ -275,8 +273,6 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >>1 )) dev->boost_freq =0 ; -else - dev_ctrl |=ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN ; }if (dev->config->avg_dependency_threshold) {
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 14/19 PM / devfreq: tegra30: Disable consecutive interrupts when appropriate
In reply to Dmitry OsipenkoConsecutive interrupts should be disabled when boosting is completed.
Currently the disabling of "lower" interrupt happens only for MCCPU monitor that uses dependency threshold, but even in a case of MCCPU the interrupt isn't getting disabled if CPU's activity is above the threshold. This results in a lot of dummy interrupt requests. The boosting feature is used by both MCCPU and MCALL, boosting should be stopped once it reaches 0 for both of the monitors and regardless of the activity level.
The boosting stops to grow once the maximum limit is hit and thus the "upper" interrupt needs to be disabled when the limit is reached.
--- drivers/devfreq/tegra30-devfreq.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
drivers/devfreq/tegra30-devfreq.c +6 -9
@@ -259,8 +259,10 @@ static void actmon_isr_device(struct tegra_devfreq *tegra, dev_ctrl |=ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN ; -if (dev->boost_freq >= tegra->max_freq) +if (dev->boost_freq >= tegra->max_freq) { + dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN ; dev->boost_freq = tegra->max_freq; + } }else if (intr_status &ACTMON_DEV_INTR_CONSECUTIVE_LOWER ) {/* * new_boost = old_boost * down_coef
@@ -271,15 +273,10 @@ static void actmon_isr_device(struct tegra_devfreq *tegra, dev_ctrl |=ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN ; -if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >>1 )) - dev->boost_freq =0 ; - } - -if (dev->config->avg_dependency_threshold) { -if (dev->avg_count >= dev->config->avg_dependency_threshold) - dev_ctrl |=ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN ; -else if (dev->boost_freq ==0 ) +if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >>1 )) { dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN ; + dev->boost_freq =0 ; + } }device_writel (dev, dev_ctrl,ACTMON_DEV_CTRL );
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 15/19 PM / devfreq: tegra30: Use kHz units for dependency threshold
In reply to Dmitry OsipenkoThe dependency threshold designates a memory activity level below which CPU's frequency isn't accounted. Currently the threshold is given in "memory cycle" units and that value depends on the polling interval which is fixed to 12ms in the driver. Later on we'd want to add support for a variable polling interval and thus the threshold value either needs to be scaled in accordance to the polling interval or it needs to be represented in a units that do not depend on the polling interval.
It is nicer to have threshold value being defined independently of the polling interval, thus this patch converts the dependency threshold units from "cycle" to "kHz". Having this change as a separate-preparatory patch will make easier to follow further patches.
--- drivers/devfreq/tegra30-devfreq.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-)
drivers/devfreq/tegra30-devfreq.c +17 -13
@@ -96,9 +96,10 @@ struct tegra_devfreq_device_config {unsigned int boost_down_threshold;/* -* Threshold of activity (cycles) below which the CPU frequency isn't -* to be taken into account. This is to avoid increasing the EMC -* frequency when the CPU is very busy but not accessing the bus often. +* Threshold of activity (cycles translated to kHz) below which the +* CPU frequency isn't to be taken into account. This is to avoid +* increasing the EMC frequency when the CPU is very busy but not +* accessing the bus often. */ u32 avg_dependency_threshold; };
@@ -126,7 +127,7 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = { .boost_down_coeff = 90, .boost_up_threshold = 27, .boost_down_threshold = 10, - .avg_dependency_threshold = 50000, + .avg_dependency_threshold = 16000, /* 16MHz in kHz units */ }, };
@@ -311,7 +312,6 @@ static unsigned long actmon_device_target_freq(struct tegra_devfreq *tegra, target_freq = dev->avg_count /ACTMON_SAMPLING_PERIOD ; avg_sustain_coef =100 *100 / dev->config->boost_up_threshold; target_freq =do_percent (target_freq, avg_sustain_coef); - target_freq += dev->boost_freq;return target_freq; }
@@ -322,15 +322,18 @@ static void actmon_update_target(struct tegra_devfreq *tegra,unsigned long cpu_freq =0 ;unsigned long static_cpu_emc_freq =0 ; -if (dev->config->avg_dependency_threshold) { + dev->target_freq =actmon_device_target_freq (tegra, dev); + +if (dev->config->avg_dependency_threshold && + dev->config->avg_dependency_threshold <= dev->target_freq) { cpu_freq =cpufreq_quick_get (0 ); static_cpu_emc_freq =actmon_cpu_to_emc_rate (tegra, cpu_freq); - } - dev->target_freq =actmon_device_target_freq (tegra, dev); - -if (dev->avg_count >= dev->config->avg_dependency_threshold) + dev->target_freq += dev->boost_freq; dev->target_freq =max (dev->target_freq, static_cpu_emc_freq); + }else { + dev->target_freq += dev->boost_freq; + } }static irqreturn_t actmon_thread_isr (int irq,void *data)
@@ -396,15 +399,16 @@ static unsigned long tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra, unsigned int cpu_freq) { +struct tegra_devfreq_device *actmon_dev = &tegra->devices[MCCPU ];unsigned long static_cpu_emc_freq, dev_freq; + dev_freq =actmon_device_target_freq (tegra, actmon_dev); +/* check whether CPU's freq is taken into account at all */ -if (tegra->devices[MCCPU ].avg_count < - tegra->devices[MCCPU ].config->avg_dependency_threshold) +if (dev_freq < actmon_dev->config->avg_dependency_threshold)return 0 ; static_cpu_emc_freq =actmon_cpu_to_emc_rate (tegra, cpu_freq); - dev_freq =actmon_device_target_freq (tegra, &tegra->devices[MCCPU ]);if (dev_freq >= static_cpu_emc_freq)return 0 ;
Signature · 1 line
2.23.0
Re: [PATCH v9 15/19] PM / devfreq: tegra30: Use kHz units for dependency threshold
Quoted 88 lines · Dmitry Osipenko
The dependency threshold designates a memory activity level below which CPU's frequency isn't accounted. Currently the threshold is given in "memory cycle" units and that value depends on the polling interval which is fixed to 12ms in the driver. Later on we'd want to add support for a variable polling interval and thus the threshold value either needs to be scaled in accordance to the polling interval or it needs to be represented in a units that do not depend on the polling interval.
It is nicer to have threshold value being defined independently of the polling interval, thus this patch converts the dependency threshold units from "cycle" to "kHz". Having this change as a separate-preparatory patch will make easier to follow further patches.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/devfreq/tegra30-devfreq.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-)drivers/devfreq/tegra30-devfreq.c +16
-10@@ -96,9 +96,10 @@ struct tegra_devfreq_device_config {unsigned int boost_down_threshold;/* -* Threshold of activity (cycles) below which the CPU frequency isn't -* to be taken into account. This is to avoid increasing the EMC -* frequency when the CPU is very busy but not accessing the bus often. +* Threshold of activity (cycles translated to kHz) below which the +* CPU frequency isn't to be taken into account. This is to avoid +* increasing the EMC frequency when the CPU is very busy but not +* accessing the bus often. */ u32 avg_dependency_threshold; };@@ -126,7 +127,7 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = { .boost_down_coeff = 90, .boost_up_threshold = 27, .boost_down_threshold = 10, - .avg_dependency_threshold = 50000, + .avg_dependency_threshold = 16000, /* 16MHz in kHz units */ }, };@@ -311,7 +312,6 @@ static unsigned long actmon_device_target_freq(struct tegra_devfreq *tegra, target_freq = dev->avg_count /ACTMON_SAMPLING_PERIOD ; avg_sustain_coef =100 *100 / dev->config->boost_up_threshold; target_freq =do_percent (target_freq, avg_sustain_coef); - target_freq += dev->boost_freq;return target_freq; }@@ -322,15 +322,18 @@ static void actmon_update_target(struct tegra_devfreq *tegra,unsigned long cpu_freq =0 ;unsigned long static_cpu_emc_freq =0 ; -if (dev->config->avg_dependency_threshold) { + dev->target_freq =actmon_device_target_freq (tegra, dev); + +if (dev->config->avg_dependency_threshold && + dev->config->avg_dependency_threshold <= dev->target_freq) { cpu_freq =cpufreq_quick_get (0 ); static_cpu_emc_freq =actmon_cpu_to_emc_rate (tegra, cpu_freq); - } - dev->target_freq =actmon_device_target_freq (tegra, dev); - -if (dev->avg_count >= dev->config->avg_dependency_threshold) + dev->target_freq += dev->boost_freq; dev->target_freq =max (dev->target_freq, static_cpu_emc_freq); + }else { + dev->target_freq += dev->boost_freq; + } }static irqreturn_t actmon_thread_isr (int irq,void *data)@@ -396,15 +399,16 @@ static unsigned long tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra, unsigned int cpu_freq) { +struct tegra_devfreq_device *actmon_dev = &tegra->devices[MCCPU ];unsigned long static_cpu_emc_freq, dev_freq; + dev_freq =actmon_device_target_freq (tegra, actmon_dev); +/* check whether CPU's freq is taken into account at all */
drivers/devfreq/tegra30-devfreq.c static unsigned long · line 414
Quoted 10 lines
drivers/devfreq/tegra30-devfreq.c +1
-3-if (tegra->devices[MCCPU ].avg_count < - tegra->devices[MCCPU ].config->avg_dependency_threshold) +if (dev_freq < actmon_dev->config->avg_dependency_threshold)return 0 ; static_cpu_emc_freq =actmon_cpu_to_emc_rate (tegra, cpu_freq); - dev_freq =actmon_device_target_freq (tegra, &tegra->devices[MCCPU ]);if (dev_freq >= static_cpu_emc_freq)return 0 ;
Signature · 3 lines
Best Regards, Chanwoo Choi Samsung Electronics
Dmitry Osipenko
maintainerPATCH v9 16/19 PM / devfreq: Add new interrupt_driven flag for governors
In reply to Dmitry OsipenkoCurrently interrupt-driven governors (like NVIDIA Tegra30 ACTMON governor) are used to set polling_ms=0 in order to avoid periodic polling of device status by devfreq core. This means that polling interval can't be changed by userspace for such governors.
The new governor flag allows interrupt-driven governors to convey that devfreq core shouldn't perform polling of device status and thus generic devfreq polling interval could be supported by these governors now.
--- drivers/devfreq/devfreq.c | 17 +++++++++++++++++ drivers/devfreq/governor.h | 3 +++ 2 files changed, 20 insertions(+)
drivers/devfreq/devfreq.c +17 -0
@@ -409,6 +409,9 @@ static void devfreq_monitor(struct work_struct *work)*/ void devfreq_monitor_start (struct devfreq *devfreq) { +if (devfreq->governor->interrupt_driven) +return ; +INIT_DEFERRABLE_WORK (&devfreq->work, devfreq_monitor);if (devfreq->profile->polling_ms) queue_delayed_work(devfreq_wq, &devfreq->work,
@@ -426,6 +429,9 @@ EXPORT_SYMBOL(devfreq_monitor_start);*/ void devfreq_monitor_stop (struct devfreq *devfreq) { +if (devfreq->governor->interrupt_driven) +return ; +cancel_delayed_work_sync (&devfreq->work); }EXPORT_SYMBOL (devfreq_monitor_stop);
@@ -453,6 +459,10 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)devfreq_update_status (devfreq, devfreq->previous_freq); devfreq->stop_polling = true;mutex_unlock (&devfreq->lock); + +if (devfreq->governor->interrupt_driven) +return ; +cancel_delayed_work_sync (&devfreq->work); }EXPORT_SYMBOL (devfreq_monitor_suspend);
@@ -473,11 +483,15 @@ void devfreq_monitor_resume(struct devfreq *devfreq)if (!devfreq->stop_polling) goto out; +if (devfreq->governor->interrupt_driven) + goto out_update; +if (!delayed_work_pending (&devfreq->work) && devfreq->profile->polling_ms)queue_delayed_work (devfreq_wq, &devfreq->work,msecs_to_jiffies (devfreq->profile->polling_ms)); +out_update: devfreq->last_stat_updated = jiffies; devfreq->stop_polling = false;
@@ -509,6 +523,9 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay)if (devfreq->stop_polling) goto out; +if (devfreq->governor->interrupt_driven) + goto out; +/* if new delay is zero, stop polling */ if (!new_delay) {mutex_unlock (&devfreq->lock);
drivers/devfreq/governor.h +3 -0
@@ -31,6 +31,8 @@* @name: Governor's name * @immutable: Immutable flag for governor. If the value is 1, * this govenror is never changeable to other governor. +* @interrupt_driven: Devfreq core won't schedule polling work for this +* governor if value is set to 1. * @get_target_freq: Returns desired operating frequency for the device. * Basically, get_target_freq will run * devfreq_dev_profile.get_dev_status() to get the
@@ -49,6 +51,7 @@ struct devfreq_governor {const char name[DEVFREQ_NAME_LEN ];const unsigned int immutable; +const unsigned int interrupt_driven;int (*get_target_freq)(struct devfreq *this,unsigned long *freq);int (*event_handler)(struct devfreq *devfreq,unsigned int event,void *data);
Signature · 1 line
2.23.0
Re: [PATCH v9 16/19] PM / devfreq: Add new interrupt_driven flag for governors
Hi Dmitry,
Quoted 91 lines · Dmitry Osipenko
Currently interrupt-driven governors (like NVIDIA Tegra30 ACTMON governor) are used to set polling_ms=0 in order to avoid periodic polling of device status by devfreq core. This means that polling interval can't be changed by userspace for such governors.
The new governor flag allows interrupt-driven governors to convey that devfreq core shouldn't perform polling of device status and thus generic devfreq polling interval could be supported by these governors now.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/devfreq/devfreq.c | 17 +++++++++++++++++ drivers/devfreq/governor.h | 3 +++ 2 files changed, 20 insertions(+)drivers/devfreq/devfreq.c +17
-0@@ -409,6 +409,9 @@ static void devfreq_monitor(struct work_struct *work)*/ void devfreq_monitor_start (struct devfreq *devfreq) { +if (devfreq->governor->interrupt_driven) +return ; +INIT_DEFERRABLE_WORK (&devfreq->work, devfreq_monitor);if (devfreq->profile->polling_ms) queue_delayed_work(devfreq_wq, &devfreq->work,@@ -426,6 +429,9 @@ EXPORT_SYMBOL(devfreq_monitor_start);*/ void devfreq_monitor_stop (struct devfreq *devfreq) { +if (devfreq->governor->interrupt_driven) +return ; +cancel_delayed_work_sync (&devfreq->work); }EXPORT_SYMBOL (devfreq_monitor_stop);@@ -453,6 +459,10 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)devfreq_update_status (devfreq, devfreq->previous_freq); devfreq->stop_polling = true;mutex_unlock (&devfreq->lock); + +if (devfreq->governor->interrupt_driven) +return ; +cancel_delayed_work_sync (&devfreq->work); }EXPORT_SYMBOL (devfreq_monitor_suspend);@@ -473,11 +483,15 @@ void devfreq_monitor_resume(struct devfreq *devfreq)if (!devfreq->stop_polling) goto out; +if (devfreq->governor->interrupt_driven) + goto out_update; +if (!delayed_work_pending (&devfreq->work) && devfreq->profile->polling_ms)queue_delayed_work (devfreq_wq, &devfreq->work,msecs_to_jiffies (devfreq->profile->polling_ms)); +out_update: devfreq->last_stat_updated = jiffies; devfreq->stop_polling = false;@@ -509,6 +523,9 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay)if (devfreq->stop_polling) goto out; +if (devfreq->governor->interrupt_driven) + goto out; +/* if new delay is zero, stop polling */ if (!new_delay) {mutex_unlock (&devfreq->lock);drivers/devfreq/governor.h +2
-0@@ -31,6 +31,8 @@* @name: Governor's name * @immutable: Immutable flag for governor. If the value is 1, * this govenror is never changeable to other governor. +* @interrupt_driven: Devfreq core won't schedule polling work for this +* governor if value is set to 1. * @get_target_freq: Returns desired operating frequency for the device. * Basically, get_target_freq will run * devfreq_dev_profile.get_dev_status() to get the
drivers/devfreq/governor.h struct devfreq_governor { · line 57
Quoted 7 lines
drivers/devfreq/governor.h +1
-0@@ -49,6 +51,7 @@ struct devfreq_governor {const char name[DEVFREQ_NAME_LEN ];const unsigned int immutable; +const unsigned int interrupt_driven;int (*get_target_freq)(struct devfreq *this,unsigned long *freq);int (*event_handler)(struct devfreq *devfreq,unsigned int event,void *data);
Signature · 3 lines
Best Regards, Chanwoo Choi Samsung Electronics
Dmitry Osipenko
maintainerPATCH v9 17/19 PM / devfreq: tegra30: Support variable polling interval
In reply to Dmitry OsipenkoThe 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.
--- drivers/devfreq/tegra30-devfreq.c | 78 ++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 12 deletions(-)
drivers/devfreq/tegra30-devfreq.c +66 -12
@@ -171,6 +171,8 @@ struct tegra_devfreq {struct tegra_devfreq_device devices[ARRAY_SIZE (actmon_device_configs)];unsigned int irq; + +bool started; };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); } -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,struct tegra_devfreq_device *dev) { -u32 avg = dev->avg_count;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 ); 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,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),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 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; 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->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 );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;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 );/*
@@ -558,8 +571,26 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)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); 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); } +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,u32 flags) {
@@ -629,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, stat->busy_time *=100 /BUS_SATURATION_RATIO ;/* 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);
@@ -637,7 +674,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, }static struct devfreq_dev_profile tegra_devfreq_profile = { - .polling_ms =0 , + .polling_ms =ACTMON_SAMPLING_PERIOD , .target = tegra_devfreq_target, .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) { struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent); +unsigned int *new_delay = data;int ret =0 ;/*
@@ -696,6 +734,21 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,devfreq_monitor_stop (devfreq);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 :tegra_actmon_stop (tegra);devfreq_monitor_suspend (devfreq);
@@ -715,6 +768,7 @@ static struct devfreq_governor tegra_devfreq_governor = { .get_target_freq = tegra_governor_get_target, .event_handler = tegra_governor_event_handler, .immutable = true, + .interrupt_driven = true, };static int tegra_devfreq_probe (struct platform_device *pdev)
Signature · 1 line
2.23.0
Re: [PATCH v9 17/19] PM / devfreq: tegra30: Support variable polling interval
Hi Dmitry,
Quoted 191 lines · Dmitry Osipenko
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 <[email protected]>--- drivers/devfreq/tegra30-devfreq.c | 78 ++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 12 deletions(-)drivers/devfreq/tegra30-devfreq.c +65
-12@@ -171,6 +171,8 @@ struct tegra_devfreq {struct tegra_devfreq_device devices[ARRAY_SIZE (actmon_device_configs)];unsigned int irq; + +bool started; };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); } -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,struct tegra_devfreq_device *dev) { -u32 avg = dev->avg_count;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 ); 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,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),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 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; 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->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 );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;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 );/* @@ -558,8 +571,26 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)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); 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); } +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,u32 flags) {@@ -629,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, stat->busy_time *=100 /BUS_SATURATION_RATIO ;/* 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);@@ -637,7 +674,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, }static struct devfreq_dev_profile tegra_devfreq_profile = { - .polling_ms =0 , + .polling_ms =ACTMON_SAMPLING_PERIOD , .target = tegra_devfreq_target, .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) { struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent); +unsigned int *new_delay = data;int ret =0 ;/* @@ -696,6 +734,21 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,devfreq_monitor_stop (devfreq);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 :tegra_actmon_stop (tegra);devfreq_monitor_suspend (devfreq);
drivers/devfreq/tegra30-devfreq.c static struct devfreq_governor tegra_devfreq_governor = { · line 774
Quoted 7 lines
drivers/devfreq/tegra30-devfreq.c +1
-0@@ -715,6 +768,7 @@ static struct devfreq_governor tegra_devfreq_governor = { .get_target_freq = tegra_governor_get_target, .event_handler = tegra_governor_event_handler, .immutable = true, + .interrupt_driven = true, };static int tegra_devfreq_probe (struct platform_device *pdev)
Signature · 3 lines
Best Regards, Chanwoo Choi Samsung Electronics
Dmitry Osipenko
maintainerPATCH v9 18/19 PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient
In reply to Dmitry OsipenkoMCCPU boosts up very aggressively by 800% and boosts down very mildly by 10%. This doesn't work well when system is idling because the very slow de-boosting results in lots of consecutive-down interrupts, in result memory stays clocked high and CPU doesn't enter deepest idling state instead of keeping memory at lowest freq and having CPU cluster turned off. A more faster de-boosting fixes the case of idling system and doesn't affect the case of an active system.
--- drivers/devfreq/tegra30-devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/devfreq/tegra30-devfreq.c +1 -1
@@ -124,7 +124,7 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = { .offset = 0x200, .irq_mask = 1 << 25, .boost_up_coeff = 800, - .boost_down_coeff = 90, + .boost_down_coeff = 40, .boost_up_threshold = 27, .boost_down_threshold = 10, .avg_dependency_threshold = 16000, /* 16MHz in kHz units */
Signature · 1 line
2.23.0
Dmitry Osipenko
maintainerPATCH v9 19/19 PM / devfreq: tegra20/30: Add Dmitry as a maintainer
In reply to Dmitry OsipenkoI was contributing to the NVIDIA Tegra20+ devfreq drivers recently and want to help keep them working and evolving in the future.
--- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+)
MAINTAINERS +9 -0
@@ -10632,6 +10632,15 @@ F: include/linux/memblock.h F: mm/memblock.c F: Documentation/core-api/boot-time-mm.rst +MEMORY FREQUENCY SCALING DRIVERS FOR NVIDIA TEGRA +M: Dmitry Osipenko <[email protected]> +L: [email protected] +L: [email protected] +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git +S: Maintained +F: drivers/devfreq/tegra20-devfreq.c +F: drivers/devfreq/tegra30-devfreq.c + MEMORY MANAGEMENT L: [email protected] W: http://www.linux-mm.org
Signature · 1 line
2.23.0
Chanwoo Choi
maintainerRe: [PATCH v9 19/19] PM / devfreq: tegra20/30: Add Dmitry as a maintainer
Quoted 20 lines · Dmitry Osipenko
I was contributing to the NVIDIA Tegra20+ devfreq drivers recently and want to help keep them working and evolving in the future.
Acked-by Chanwoo Choi <[email protected]>Signed-off-by Dmitry Osipenko <[email protected]>--- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+)MAINTAINERS +2
-0@@ -10632,6 +10632,15 @@ F: include/linux/memblock.h F: mm/memblock.c F: Documentation/core-api/boot-time-mm.rst +MEMORY FREQUENCY SCALING DRIVERS FOR NVIDIA TEGRA +M: Dmitry Osipenko <[email protected]>
MAINTAINERS F: include/linux/memblock.h · line 10646
Quoted 10 lines
MAINTAINERS +7
-0+L: [email protected] +L: [email protected] +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git +S: Maintained +F: drivers/devfreq/tegra20-devfreq.c +F: drivers/devfreq/tegra30-devfreq.c + MEMORY MANAGEMENT L: [email protected] W: https://protect2.fireeye.com/url?k=9d0ba644-c09508de-9d0a2d0b-0cc47a336fae-300ed90f1ba3077c&u=http://www.linux-mm.org/
Looks good to me.
But, the merge conflict might be occurred. After getting the review from Myungjoo, you better to send this patch separately based on the latest MAINTAINERS file.
Thanks for your all efforts.
Signature · 3 lines
Best Regards, Chanwoo Choi Samsung Electronics
Dmitry Osipenko
maintainerRe: [PATCH v9 19/19] PM / devfreq: tegra20/30: Add Dmitry as a maintainer
05.11.2019 07:07, Chanwoo Choi пишет:
Quoted 34 lines · Dmitry Osipenko
Quoted 20 lines
I was contributing to the NVIDIA Tegra20+ devfreq drivers recently and want to help keep them working and evolving in the future.
Acked-by Chanwoo Choi <[email protected]>Signed-off-by Dmitry Osipenko <[email protected]>--- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+)MAINTAINERS +2
-0@@ -10632,6 +10632,15 @@ F: include/linux/memblock.h F: mm/memblock.c F: Documentation/core-api/boot-time-mm.rst +MEMORY FREQUENCY SCALING DRIVERS FOR NVIDIA TEGRA +M: Dmitry Osipenko <[email protected]>MAINTAINERS F: include/linux/memblock.h · line 10646
Quoted 10 lines
MAINTAINERS +7
-0+L: [email protected] +L: [email protected] +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git +S: Maintained +F: drivers/devfreq/tegra20-devfreq.c +F: drivers/devfreq/tegra30-devfreq.c + MEMORY MANAGEMENT L: [email protected] W: https://protect2.fireeye.com/url?k=9d0ba644-c09508de-9d0a2d0b-0cc47a336fae-300ed90f1ba3077c&u=http://www.linux-mm.org/Looks good to me.
Quoted 6 lines
But, the merge conflict might be occurred. After getting the review from Myungjoo, you better to send this patch separately based on the latest
MAINTAINERSfile.Thanks for your all efforts.
Okay, I'll wait for the rest of patches to land and then re-send this patch separately. Thank you very much for the reviews.
Re: [PATCH v9 19/19] PM / devfreq: tegra20/30: Add Dmitry as a maintainer
Hi Dmitry,
Quoted 19 lines · Dmitry Osipenko
05.11.2019 07:07, Chanwoo Choi пишет:
Quoted 18 lines · Dmitry Osipenko
Quoted 17 lines
I was contributing to the NVIDIA Tegra20+ devfreq drivers recently and want to help keep them working and evolving in the future.
Acked-by Chanwoo Choi <[email protected]>Signed-off-by Dmitry Osipenko <[email protected]>--- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+)MAINTAINERS +0
-0@@ -10632,6 +10632,15 @@ F: include/linux/memblock.h F: mm/memblock.c F: Documentation/core-api/boot-time-mm.rst
Quoted 5 lines
Quoted 5 lines
MAINTAINERS F: include/linux/memblock.h · line 10639
Quoted 5 lines
MAINTAINERS +5
-0+MEMORY FREQUENCY SCALING DRIVERS FOR NVIDIA TEGRA +M: Dmitry Osipenko <[email protected]> +L: [email protected] +L: [email protected] +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git
From now, I'll help and review the devfreq patches as maintainer and the devfreq git information is changed[1]. You should change the git info as following:
[1] https://lkml.org/lkml/2019/11/5/39
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
Also, I add my acked-by tag.
Quoted 22 lines
Quoted 11 lines
MAINTAINERS F: include/linux/memblock.h
Quoted 8 lines
MAINTAINERS +4
-0+S: Maintained +F: drivers/devfreq/tegra20-devfreq.c +F: drivers/devfreq/tegra30-devfreq.c + MEMORY MANAGEMENT L: [email protected] W: https://protect2.fireeye.com/url?k=9d0ba644-c09508de-9d0a2d0b-0cc47a336fae-300ed90f1ba3077c&u=http://www.linux-mm.org/Looks good to me.
Quoted 6 lines
But, the merge conflict might be occurred. After getting the review from Myungjoo, you better to send this patch separately based on the latest
MAINTAINERSfile.Thanks for your all efforts.
Okay, I'll wait for the rest of patches to land and then re-send this patch separately. Thank you very much for the reviews.
Signature · 3 lines
Best Regards, Chanwoo Choi Samsung Electronics
Dmitry Osipenko
maintainerRe: [PATCH v9 19/19] PM / devfreq: tegra20/30: Add Dmitry as a maintainer
06.11.2019 04:24, Chanwoo Choi пишет:
Quoted 33 lines
Hi Dmitry,
Quoted 19 lines · Dmitry Osipenko
05.11.2019 07:07, Chanwoo Choi пишет:
Quoted 18 lines · Dmitry Osipenko
Quoted 17 lines
I was contributing to the NVIDIA Tegra20+ devfreq drivers recently and want to help keep them working and evolving in the future.
Acked-by Chanwoo Choi <[email protected]>Signed-off-by Dmitry Osipenko <[email protected]>--- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+)MAINTAINERS +0
-0@@ -10632,6 +10632,15 @@ F: include/linux/memblock.h F: mm/memblock.c F: Documentation/core-api/boot-time-mm.rstQuoted 5 lines
Quoted 5 lines
MAINTAINERS F: include/linux/memblock.h · line 10639
Quoted 5 lines
MAINTAINERS +5
-0+MEMORY FREQUENCY SCALING DRIVERS FOR NVIDIA TEGRA +M: Dmitry Osipenko <[email protected]> +L: [email protected] +L: [email protected] +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.gitFrom now, I'll help and review the devfreq patches as maintainer and the devfreq git information is changed[1]. You should change the git info as following:
[1] https://lkml.org/lkml/2019/11/5/39
Quoted 4 lines
+T: git
git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.gitAlso, I add my acked-by tag.
Acked-by Chanwoo Choi <[email protected]>
Thanks, I'll update this patch.