linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 00/18] More improvements for Tegra30 devfreq driver
@ 2019-11-03 20:41 Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 01/18] PM / devfreq: tegra30: Change irq type to unsigned int Dmitry Osipenko
                   ` (17 more replies)
  0 siblings, 18 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

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/0fb50eb1-a173-1756-6889-2526a10ac707@gmail.com/T/

Changelog:

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 (18):
  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: 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         |   9 +-
 drivers/devfreq/governor.h        |   3 +
 drivers/devfreq/tegra30-devfreq.c | 394 +++++++++++++++++++++---------
 4 files changed, 303 insertions(+), 112 deletions(-)

-- 
2.23.0


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v8 01/18] PM / devfreq: tegra30: Change irq type to unsigned int
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 02/18] PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped Dmitry Osipenko
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

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.

Suggested-by: Thierry Reding <thierry.reding@gmail.com>
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index a6ba75f4106d..a27300f40b0b 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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);
 
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 02/18] PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 01/18] PM / devfreq: tegra30: Change irq type to unsigned int Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 03/18] PM / devfreq: tegra30: Handle possible round-rate error Dmitry Osipenko
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

There is no real need to keep interrupt always-enabled, will be nicer
to keep it disabled while governor is inactive.

Suggested-by: Thierry Reding <thierry.reding@gmail.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 47 ++++++++++++++++---------------
 1 file changed, 24 insertions(+), 23 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index a27300f40b0b..a0a5f3f7b789 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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);
 
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 03/18] PM / devfreq: tegra30: Handle possible round-rate error
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 01/18] PM / devfreq: tegra30: Change irq type to unsigned int Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 02/18] PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 04/18] PM / devfreq: tegra30: Drop write-barrier Dmitry Osipenko
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

The EMC clock rate rounding technically could fail, hence let's handle
the error cases properly.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index a0a5f3f7b789..66dfa98d8c6b 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 04/18] PM / devfreq: tegra30: Drop write-barrier
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (2 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 03/18] PM / devfreq: tegra30: Handle possible round-rate error Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 05/18] PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out Dmitry Osipenko
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

There 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 66dfa98d8c6b..b50bd1615010 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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,
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 05/18] PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (3 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 04/18] PM / devfreq: tegra30: Drop write-barrier Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 06/18] PM / devfreq: tegra30: Use kHz units uniformly in the code Dmitry Osipenko
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

There 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index b50bd1615010..7d7b7eecc19c 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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 },
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 06/18] PM / devfreq: tegra30: Use kHz units uniformly in the code
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (4 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 05/18] PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 07/18] PM / devfreq: tegra30: Use CPUFreq notifier Dmitry Osipenko
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

Part 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.

Tested-by: Peter Geis <pgwipeout@gmail.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 7d7b7eecc19c..9ccde64be0a0 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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)) {
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 07/18] PM / devfreq: tegra30: Use CPUFreq notifier
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (5 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 06/18] PM / devfreq: tegra30: Use kHz units uniformly in the code Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 08/18] PM / devfreq: tegra30: Move clk-notifier's registration to governor's start Dmitry Osipenko
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

The 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 180 +++++++++++++++++++++++++-----
 1 file changed, 155 insertions(+), 25 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 9ccde64be0a0..2d720e7e2236 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 08/18] PM / devfreq: tegra30: Move clk-notifier's registration to governor's start
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (6 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 07/18] PM / devfreq: tegra30: Use CPUFreq notifier Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 09/18] PM / devfreq: tegra30: Reset boosting on startup Dmitry Osipenko
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

There 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 39 ++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 2d720e7e2236..6960d8ba0577 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 09/18] PM / devfreq: tegra30: Reset boosting on startup
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (7 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 08/18] PM / devfreq: tegra30: Move clk-notifier's registration to governor's start Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 10/18] PM / devfreq: tegra30: Don't enable consecutive-down interrupt " Dmitry Osipenko
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

Governor 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 6960d8ba0577..9cb2d6468175 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 10/18] PM / devfreq: tegra30: Don't enable consecutive-down interrupt on startup
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (8 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 09/18] PM / devfreq: tegra30: Reset boosting on startup Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 11/18] PM / devfreq: tegra30: Constify structs Dmitry Osipenko
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

The 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 9cb2d6468175..bc46af155b99 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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;
 
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 11/18] PM / devfreq: tegra30: Constify structs
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (9 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 10/18] PM / devfreq: tegra30: Don't enable consecutive-down interrupt " Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 12/18] PM / devfreq: tegra30: Include appropriate header Dmitry Osipenko
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

Constify unmodifiable structs, for consistency.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index bc46af155b99..9bd4dd982927 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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) {
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 12/18] PM / devfreq: tegra30: Include appropriate header
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (10 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 11/18] PM / devfreq: tegra30: Constify structs Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 13/18] PM / devfreq: tegra30: Don't enable already enabled consecutive interrupts Dmitry Osipenko
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

It's not very correct to include mod_devicetable.h for the OF device
drivers and of_device.h should be included instead.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 9bd4dd982927..7c8126e74750 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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>
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 13/18] PM / devfreq: tegra30: Don't enable already enabled consecutive interrupts
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (11 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 12/18] PM / devfreq: tegra30: Include appropriate header Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 14/18] PM / devfreq: tegra30: Disable consecutive interrupts when appropriate Dmitry Osipenko
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

Consecutive 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 7c8126e74750..4a5d513904a2 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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) {
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 14/18] PM / devfreq: tegra30: Disable consecutive interrupts when appropriate
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (12 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 13/18] PM / devfreq: tegra30: Don't enable already enabled consecutive interrupts Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors Dmitry Osipenko
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

Consecutive 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 4a5d513904a2..852bde6249c7 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (13 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 14/18] PM / devfreq: tegra30: Disable consecutive interrupts when appropriate Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-04  2:48   ` Chanwoo Choi
  2019-11-03 20:41 ` [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval Dmitry Osipenko
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

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 <digetx@gmail.com>
---
 drivers/devfreq/devfreq.c  | 9 +++++++--
 drivers/devfreq/governor.h | 3 +++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index b905963cea7d..a711a76d386e 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -410,7 +410,8 @@ static void devfreq_monitor(struct work_struct *work)
 void devfreq_monitor_start(struct devfreq *devfreq)
 {
 	INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
-	if (devfreq->profile->polling_ms)
+	if (devfreq->profile->polling_ms &&
+	    !devfreq->governor->interrupt_driven)
 		queue_delayed_work(devfreq_wq, &devfreq->work,
 			msecs_to_jiffies(devfreq->profile->polling_ms));
 }
@@ -474,7 +475,8 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
 		goto out;
 
 	if (!delayed_work_pending(&devfreq->work) &&
-			devfreq->profile->polling_ms)
+			devfreq->profile->polling_ms &&
+			!devfreq->governor->interrupt_driven)
 		queue_delayed_work(devfreq_wq, &devfreq->work,
 			msecs_to_jiffies(devfreq->profile->polling_ms));
 
@@ -509,6 +511,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);
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
index bbe5ff9fcecf..dc7533ccc3db 100644
--- a/drivers/devfreq/governor.h
+++ b/drivers/devfreq/governor.h
@@ -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);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (14 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-04  2:53   ` Chanwoo Choi
  2019-11-03 20:41 ` [PATCH v8 17/18] PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 18/18] PM / devfreq: tegra20/30: Add Dmitry as a maintainer Dmitry Osipenko
  17 siblings, 1 reply; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

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>
---
 drivers/devfreq/tegra30-devfreq.c | 83 +++++++++++++++++++++++--------
 1 file changed, 61 insertions(+), 22 deletions(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 852bde6249c7..9c645e83ef8b 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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 = 5 * KHZ,
 	},
 };
 
@@ -208,9 +209,16 @@ 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,
@@ -218,8 +226,9 @@ static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
 {
 	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;
 
+	avg = min(dev->avg_count, U32_MAX - band);
 	device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
 
 	avg = max(dev->avg_count, band);
@@ -229,7 +238,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);
@@ -308,10 +317,9 @@ 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);
-	target_freq += dev->boost_freq;
 
 	return target_freq;
 }
@@ -322,15 +330,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 +407,16 @@ static unsigned long
 tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra,
 				  unsigned int cpu_freq)
 {
+	struct tegra_devfreq_device *dev = &tegra->devices[MCCPU];
 	unsigned long static_cpu_emc_freq, dev_freq;
 
+	dev_freq = actmon_device_target_freq(tegra, 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 < 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;
@@ -465,7 +477,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);
@@ -506,7 +518,10 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)
 	unsigned int i;
 	int err;
 
-	actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
+	if (!tegra->devfreq->profile->polling_ms || tegra->cur_freq)
+		return 0;
+
+	actmon_writel(tegra, tegra->devfreq->profile->polling_ms - 1,
 		      ACTMON_GLB_PERIOD_CTRL);
 
 	/*
@@ -551,11 +566,16 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)
 
 	clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
 
+	tegra->cur_freq = 0;
+
 	return err;
 }
 
 static void tegra_actmon_stop(struct tegra_devfreq *tegra)
 {
+	if (!tegra->devfreq->profile->polling_ms || !tegra->cur_freq)
+		return;
+
 	disable_irq(tegra->irq);
 
 	cpufreq_unregister_notifier(&tegra->cpu_rate_change_nb,
@@ -566,6 +586,8 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
 	tegra_actmon_stop_devices(tegra);
 
 	clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
+
+	tegra->cur_freq = 0;
 }
 
 static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
@@ -625,7 +647,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);
 
@@ -633,7 +655,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,
 };
@@ -673,6 +695,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;
 
 	/*
@@ -692,6 +715,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_stop(tegra);
+		devfreq_interval_update(devfreq, new_delay);
+		ret = tegra_actmon_start(tegra);
+		break;
+
 	case DEVFREQ_GOV_SUSPEND:
 		tegra_actmon_stop(tegra);
 		devfreq_monitor_suspend(devfreq);
@@ -711,6 +749,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)
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 17/18] PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (15 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  2019-11-03 20:41 ` [PATCH v8 18/18] PM / devfreq: tegra20/30: Add Dmitry as a maintainer Dmitry Osipenko
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

MCCPU 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.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/devfreq/tegra30-devfreq.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 9c645e83ef8b..732cacb54c3b 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -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 = 5 * KHZ,
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v8 18/18] PM / devfreq: tegra20/30: Add Dmitry as a maintainer
  2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
                   ` (16 preceding siblings ...)
  2019-11-03 20:41 ` [PATCH v8 17/18] PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient Dmitry Osipenko
@ 2019-11-03 20:41 ` Dmitry Osipenko
  17 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-03 20:41 UTC (permalink / raw)
  To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

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 <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9f69d01da3a6..4b9679988514 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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 <digetx@gmail.com>
+L:	linux-pm@vger.kernel.org
+L:	linux-tegra@vger.kernel.org
+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:	linux-mm@kvack.org
 W:	http://www.linux-mm.org
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors
  2019-11-03 20:41 ` [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors Dmitry Osipenko
@ 2019-11-04  2:48   ` Chanwoo Choi
  2019-11-04 14:15     ` Dmitry Osipenko
  0 siblings, 1 reply; 23+ messages in thread
From: Chanwoo Choi @ 2019-11-04  2:48 UTC (permalink / raw)
  To: Dmitry Osipenko, Thierry Reding, MyungJoo Ham, Kyungmin Park,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

Hi Dmitry,

Sorry for the additional comment of this patch.
I think that you better to change the 'interrupt_driven' checking
style as following in order to keep the consistency of governor
flag checking style.

diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 27de8ddeaaa8..fe409fc1bcc4 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -410,9 +410,11 @@ 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 &&
-           !devfreq->governor->interrupt_driven)
                queue_delayed_work(devfreq_wq, &devfreq->work,
                        msecs_to_jiffies(devfreq->profile->polling_ms));
 }
@@ -475,12 +477,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 &&
-                       !devfreq->governor->interrupt_driven)
                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;


If you edit it as following, feel free to add my reviewed-by tag:
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>


On 19. 11. 4. 오전 5:41, Dmitry Osipenko wrote:
> 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 <digetx@gmail.com>
> ---
>  drivers/devfreq/devfreq.c  | 9 +++++++--
>  drivers/devfreq/governor.h | 3 +++
>  2 files changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index b905963cea7d..a711a76d386e 100644
> --- a/drivers/devfreq/devfreq.c
> +++ b/drivers/devfreq/devfreq.c
> @@ -410,7 +410,8 @@ static void devfreq_monitor(struct work_struct *work)
>  void devfreq_monitor_start(struct devfreq *devfreq)
>  {
>  	INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
> -	if (devfreq->profile->polling_ms)
> +	if (devfreq->profile->polling_ms &&
> +	    !devfreq->governor->interrupt_driven)
>  		queue_delayed_work(devfreq_wq, &devfreq->work,
>  			msecs_to_jiffies(devfreq->profile->polling_ms));
>  }
> @@ -474,7 +475,8 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
>  		goto out;
>  
>  	if (!delayed_work_pending(&devfreq->work) &&
> -			devfreq->profile->polling_ms)
> +			devfreq->profile->polling_ms &&
> +			!devfreq->governor->interrupt_driven)
>  		queue_delayed_work(devfreq_wq, &devfreq->work,
>  			msecs_to_jiffies(devfreq->profile->polling_ms));
>  
> @@ -509,6 +511,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);
> diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
> index bbe5ff9fcecf..dc7533ccc3db 100644
> --- a/drivers/devfreq/governor.h
> +++ b/drivers/devfreq/governor.h
> @@ -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);
> 


-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval
  2019-11-03 20:41 ` [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval Dmitry Osipenko
@ 2019-11-04  2:53   ` Chanwoo Choi
  2019-11-04 14:36     ` Dmitry Osipenko
  0 siblings, 1 reply; 23+ messages in thread
From: Chanwoo Choi @ 2019-11-04  2:53 UTC (permalink / raw)
  To: Dmitry Osipenko, Thierry Reding, MyungJoo Ham, Kyungmin Park,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

On 19. 11. 4. 오전 5:41, Dmitry Osipenko wrote:
> 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>
> ---
>  drivers/devfreq/tegra30-devfreq.c | 83 +++++++++++++++++++++++--------
>  1 file changed, 61 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
> index 852bde6249c7..9c645e83ef8b 100644
> --- a/drivers/devfreq/tegra30-devfreq.c
> +++ b/drivers/devfreq/tegra30-devfreq.c
> @@ -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 = 5 * KHZ,

nitpick.
Actually, this change is not related to this patch.
Even if it is trivial, it is not proper.

>  	},
>  };
>  
> @@ -208,9 +209,16 @@ 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,
> @@ -218,8 +226,9 @@ static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
>  {
>  	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;
>  
> +	avg = min(dev->avg_count, U32_MAX - band);
>  	device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
>  
>  	avg = max(dev->avg_count, band);
> @@ -229,7 +238,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);
> @@ -308,10 +317,9 @@ 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);
> -	target_freq += dev->boost_freq;
>  
>  	return target_freq;
>  }
> @@ -322,15 +330,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) {

This changes are related to polling interval change at the run time?
This patch touched the 'avg_dependency_threshold'. It is related to the 
dynamic change of polling interval?

>  		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;
> +	}

ditto. This changes are related to polling interval change at the run time?

>  }
>  
>  static irqreturn_t actmon_thread_isr(int irq, void *data)
> @@ -396,15 +407,16 @@ static unsigned long
>  tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra,
>  				  unsigned int cpu_freq)
>  {
> +	struct tegra_devfreq_device *dev = &tegra->devices[MCCPU];
>  	unsigned long static_cpu_emc_freq, dev_freq;
>  
> +	dev_freq = actmon_device_target_freq(tegra, 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 < dev->config->avg_dependency_threshold)

ditto.

>  		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;
> @@ -465,7 +477,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);
> @@ -506,7 +518,10 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)
>  	unsigned int i;
>  	int err;
>  
> -	actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
> +	if (!tegra->devfreq->profile->polling_ms || tegra->cur_freq)
> +		return 0;
> +
> +	actmon_writel(tegra, tegra->devfreq->profile->polling_ms - 1,
>  		      ACTMON_GLB_PERIOD_CTRL);
>  
>  	/*
> @@ -551,11 +566,16 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)
>  
>  	clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
>  
> +	tegra->cur_freq = 0;
> +
>  	return err;
>  }
>  
>  static void tegra_actmon_stop(struct tegra_devfreq *tegra)
>  {
> +	if (!tegra->devfreq->profile->polling_ms || !tegra->cur_freq)
> +		return;
> +
>  	disable_irq(tegra->irq);
>  
>  	cpufreq_unregister_notifier(&tegra->cpu_rate_change_nb,
> @@ -566,6 +586,8 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
>  	tegra_actmon_stop_devices(tegra);
>  
>  	clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
> +
> +	tegra->cur_freq = 0;
>  }
>  
>  static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
> @@ -625,7 +647,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);
>  
> @@ -633,7 +655,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,
>  };
> @@ -673,6 +695,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;
>  
>  	/*
> @@ -692,6 +715,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_stop(tegra);
> +		devfreq_interval_update(devfreq, new_delay);
> +		ret = tegra_actmon_start(tegra);
> +		break;
> +
>  	case DEVFREQ_GOV_SUSPEND:
>  		tegra_actmon_stop(tegra);
>  		devfreq_monitor_suspend(devfreq);
> @@ -711,6 +749,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)
> 


-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors
  2019-11-04  2:48   ` Chanwoo Choi
@ 2019-11-04 14:15     ` Dmitry Osipenko
  0 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-04 14:15 UTC (permalink / raw)
  To: Chanwoo Choi, Thierry Reding, MyungJoo Ham, Kyungmin Park,
	Jonathan Hunter, Tomeu Vizoso, Peter Geis,
	Michał Mirosław
  Cc: linux-pm, linux-tegra, linux-kernel

04.11.2019 05:48, Chanwoo Choi пишет:
> Hi Dmitry,
> 
> Sorry for the additional comment of this patch.
> I think that you better to change the 'interrupt_driven' checking
> style as following in order to keep the consistency of governor
> flag checking style.

Hello Chanwoo,

Okay, I'll update this patch.

> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index 27de8ddeaaa8..fe409fc1bcc4 100644
> --- a/drivers/devfreq/devfreq.c
> +++ b/drivers/devfreq/devfreq.c
> @@ -410,9 +410,11 @@ 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 &&
> -           !devfreq->governor->interrupt_driven)
>                 queue_delayed_work(devfreq_wq, &devfreq->work,
>                         msecs_to_jiffies(devfreq->profile->polling_ms));
>  }
> @@ -475,12 +477,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 &&
> -                       !devfreq->governor->interrupt_driven)
>                 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;
> 
> 
> If you edit it as following, feel free to add my reviewed-by tag:
> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
> 
> 
> On 19. 11. 4. 오전 5:41, Dmitry Osipenko wrote:
>> 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 <digetx@gmail.com>
>> ---
>>  drivers/devfreq/devfreq.c  | 9 +++++++--
>>  drivers/devfreq/governor.h | 3 +++
>>  2 files changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>> index b905963cea7d..a711a76d386e 100644
>> --- a/drivers/devfreq/devfreq.c
>> +++ b/drivers/devfreq/devfreq.c
>> @@ -410,7 +410,8 @@ static void devfreq_monitor(struct work_struct *work)
>>  void devfreq_monitor_start(struct devfreq *devfreq)
>>  {
>>  	INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
>> -	if (devfreq->profile->polling_ms)
>> +	if (devfreq->profile->polling_ms &&
>> +	    !devfreq->governor->interrupt_driven)
>>  		queue_delayed_work(devfreq_wq, &devfreq->work,
>>  			msecs_to_jiffies(devfreq->profile->polling_ms));
>>  }
>> @@ -474,7 +475,8 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
>>  		goto out;
>>  
>>  	if (!delayed_work_pending(&devfreq->work) &&
>> -			devfreq->profile->polling_ms)
>> +			devfreq->profile->polling_ms &&
>> +			!devfreq->governor->interrupt_driven)
>>  		queue_delayed_work(devfreq_wq, &devfreq->work,
>>  			msecs_to_jiffies(devfreq->profile->polling_ms));
>>  
>> @@ -509,6 +511,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);
>> diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
>> index bbe5ff9fcecf..dc7533ccc3db 100644
>> --- a/drivers/devfreq/governor.h
>> +++ b/drivers/devfreq/governor.h
>> @@ -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);
>>
> 
> 


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval
  2019-11-04  2:53   ` Chanwoo Choi
@ 2019-11-04 14:36     ` Dmitry Osipenko
  0 siblings, 0 replies; 23+ messages in thread
From: Dmitry Osipenko @ 2019-11-04 14:36 UTC (permalink / raw)
  To: Chanwoo Choi
  Cc: Thierry Reding, MyungJoo Ham, Kyungmin Park, Jonathan Hunter,
	Tomeu Vizoso, Peter Geis, Michał Mirosław, linux-pm,
	linux-tegra, linux-kernel

04.11.2019 05:53, Chanwoo Choi пишет:
> On 19. 11. 4. 오전 5:41, Dmitry Osipenko wrote:
>> 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>
>> ---
>>  drivers/devfreq/tegra30-devfreq.c | 83 +++++++++++++++++++++++--------
>>  1 file changed, 61 insertions(+), 22 deletions(-)
>>
>> diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
>> index 852bde6249c7..9c645e83ef8b 100644
>> --- a/drivers/devfreq/tegra30-devfreq.c
>> +++ b/drivers/devfreq/tegra30-devfreq.c
>> @@ -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 = 5 * KHZ,
> 
> nitpick.
> Actually, this change is not related to this patch.
> Even if it is trivial, it is not proper.

As I wrote in the cover letter, this change is directly related to this
patch. It was missed in v7 by accident. Please see more comments below.

>>  	},
>>  };
>>  
>> @@ -208,9 +209,16 @@ 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,
>> @@ -218,8 +226,9 @@ static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
>>  {
>>  	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;
>>  
>> +	avg = min(dev->avg_count, U32_MAX - band);
>>  	device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
>>  
>>  	avg = max(dev->avg_count, band);
>> @@ -229,7 +238,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);
>> @@ -308,10 +317,9 @@ 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);
>> -	target_freq += dev->boost_freq;
>>  
>>  	return target_freq;
>>  }
>> @@ -322,15 +330,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) {
> 
> This changes are related to polling interval change at the run time?
> This patch touched the 'avg_dependency_threshold'. It is related to the 
> dynamic change of polling interval?
Before this patch the avg_dependency_threshold was defined in "cycle"
units and the the predefined value was fixed for the polling interval of
12ms.

After this patch the avg_dependency_threshold is redefined to use "kHz"
units which are independent of the polling interval.

So yes, this change is related to the dynamic changing of the polling
interval at run time.

>>  		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;
>> +	}
> 
> ditto. This changes are related to polling interval change at the run time?

It is possible to derive these changes into a separate patch. This
should help a tad with the following of the code changes. I'll do it in v9

>>  }
>>  
>>  static irqreturn_t actmon_thread_isr(int irq, void *data)
>> @@ -396,15 +407,16 @@ static unsigned long
>>  tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra,
>>  				  unsigned int cpu_freq)
>>  {
>> +	struct tegra_devfreq_device *dev = &tegra->devices[MCCPU];
>>  	unsigned long static_cpu_emc_freq, dev_freq;
>>  
>> +	dev_freq = actmon_device_target_freq(tegra, 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 < dev->config->avg_dependency_threshold)
> 
> ditto.
> 
>>  		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;
>> @@ -465,7 +477,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);
>> @@ -506,7 +518,10 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)
>>  	unsigned int i;
>>  	int err;
>>  
>> -	actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
>> +	if (!tegra->devfreq->profile->polling_ms || tegra->cur_freq)
>> +		return 0;
>> +
>> +	actmon_writel(tegra, tegra->devfreq->profile->polling_ms - 1,
>>  		      ACTMON_GLB_PERIOD_CTRL);
>>  
>>  	/*
>> @@ -551,11 +566,16 @@ static int tegra_actmon_start(struct tegra_devfreq *tegra)
>>  
>>  	clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
>>  
>> +	tegra->cur_freq = 0;
>> +
>>  	return err;
>>  }
>>  
>>  static void tegra_actmon_stop(struct tegra_devfreq *tegra)
>>  {
>> +	if (!tegra->devfreq->profile->polling_ms || !tegra->cur_freq)
>> +		return;
>> +
>>  	disable_irq(tegra->irq);
>>  
>>  	cpufreq_unregister_notifier(&tegra->cpu_rate_change_nb,
>> @@ -566,6 +586,8 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
>>  	tegra_actmon_stop_devices(tegra);
>>  
>>  	clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
>> +
>> +	tegra->cur_freq = 0;
>>  }
>>  
>>  static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
>> @@ -625,7 +647,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);
>>  
>> @@ -633,7 +655,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,
>>  };
>> @@ -673,6 +695,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;
>>  
>>  	/*
>> @@ -692,6 +715,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_stop(tegra);
>> +		devfreq_interval_update(devfreq, new_delay);
>> +		ret = tegra_actmon_start(tegra);
>> +		break;
>> +
>>  	case DEVFREQ_GOV_SUSPEND:
>>  		tegra_actmon_stop(tegra);
>>  		devfreq_monitor_suspend(devfreq);
>> @@ -711,6 +749,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)
>>
> 
> 


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2019-11-04 14:36 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-03 20:41 [PATCH v8 00/18] More improvements for Tegra30 devfreq driver Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 01/18] PM / devfreq: tegra30: Change irq type to unsigned int Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 02/18] PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 03/18] PM / devfreq: tegra30: Handle possible round-rate error Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 04/18] PM / devfreq: tegra30: Drop write-barrier Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 05/18] PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 06/18] PM / devfreq: tegra30: Use kHz units uniformly in the code Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 07/18] PM / devfreq: tegra30: Use CPUFreq notifier Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 08/18] PM / devfreq: tegra30: Move clk-notifier's registration to governor's start Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 09/18] PM / devfreq: tegra30: Reset boosting on startup Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 10/18] PM / devfreq: tegra30: Don't enable consecutive-down interrupt " Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 11/18] PM / devfreq: tegra30: Constify structs Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 12/18] PM / devfreq: tegra30: Include appropriate header Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 13/18] PM / devfreq: tegra30: Don't enable already enabled consecutive interrupts Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 14/18] PM / devfreq: tegra30: Disable consecutive interrupts when appropriate Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 15/18] PM / devfreq: Add new interrupt_driven flag for governors Dmitry Osipenko
2019-11-04  2:48   ` Chanwoo Choi
2019-11-04 14:15     ` Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 16/18] PM / devfreq: tegra30: Support variable polling interval Dmitry Osipenko
2019-11-04  2:53   ` Chanwoo Choi
2019-11-04 14:36     ` Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 17/18] PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient Dmitry Osipenko
2019-11-03 20:41 ` [PATCH v8 18/18] PM / devfreq: tegra20/30: Add Dmitry as a maintainer Dmitry Osipenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).