Commit fa6a599e authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-devfreq'

* pm-devfreq: (26 commits)
  PM / devfreq: tegra30: Tune up MCCPU boost-down coefficient
  PM / devfreq: tegra30: Support variable polling interval
  PM / devfreq: Add new interrupt_driven flag for governors
  PM / devfreq: tegra30: Use kHz units for dependency threshold
  PM / devfreq: tegra30: Disable consecutive interrupts when appropriate
  PM / devfreq: tegra30: Don't enable already enabled consecutive interrupts
  PM / devfreq: tegra30: Include appropriate header
  PM / devfreq: tegra30: Constify structs
  PM / devfreq: tegra30: Don't enable consecutive-down interrupt on startup
  PM / devfreq: tegra30: Reset boosting on startup
  PM / devfreq: tegra30: Move clk-notifier's registration to governor's start
  PM / devfreq: tegra30: Use CPUFreq notifier
  PM / devfreq: tegra30: Use kHz units uniformly in the code
  PM / devfreq: tegra30: Fix integer overflow on CPU's freq max out
  PM / devfreq: tegra30: Drop write-barrier
  PM / devfreq: tegra30: Handle possible round-rate error
  PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped
  PM / devfreq: tegra30: Change irq type to unsigned int
  PM / devfreq: exynos-ppmu: remove useless assignment
  PM / devfreq: Lock devfreq in trans_stat_show
  ...
parents c48b90f8 2b328421
...@@ -10,14 +10,23 @@ The Exynos PPMU driver uses the devfreq-event class to provide event data ...@@ -10,14 +10,23 @@ The Exynos PPMU driver uses the devfreq-event class to provide event data
to various devfreq devices. The devfreq devices would use the event data when to various devfreq devices. The devfreq devices would use the event data when
derterming the current state of each IP. derterming the current state of each IP.
Required properties: Required properties for PPMU device:
- compatible: Should be "samsung,exynos-ppmu" or "samsung,exynos-ppmu-v2. - compatible: Should be "samsung,exynos-ppmu" or "samsung,exynos-ppmu-v2.
- reg: physical base address of each PPMU and length of memory mapped region. - reg: physical base address of each PPMU and length of memory mapped region.
Optional properties: Optional properties for PPMU device:
- clock-names : the name of clock used by the PPMU, "ppmu" - clock-names : the name of clock used by the PPMU, "ppmu"
- clocks : phandles for clock specified in "clock-names" property - clocks : phandles for clock specified in "clock-names" property
Required properties for 'events' child node of PPMU device:
- event-name : the unique event name among PPMU device
Optional properties for 'events' child node of PPMU device:
- event-data-type : Define the type of data which shell be counted
by the counter. You can check include/dt-bindings/pmu/exynos_ppmu.h for
all possible type, i.e. count read requests, count write data in bytes,
etc. This field is optional and when it is missing, the driver code
will use default data type.
Example1 : PPMUv1 nodes in exynos3250.dtsi are listed below. Example1 : PPMUv1 nodes in exynos3250.dtsi are listed below.
ppmu_dmc0: ppmu_dmc0@106a0000 { ppmu_dmc0: ppmu_dmc0@106a0000 {
...@@ -145,3 +154,16 @@ Example3 : PPMUv2 nodes in exynos5433.dtsi are listed below. ...@@ -145,3 +154,16 @@ Example3 : PPMUv2 nodes in exynos5433.dtsi are listed below.
reg = <0x104d0000 0x2000>; reg = <0x104d0000 0x2000>;
status = "disabled"; status = "disabled";
}; };
Example4 : 'event-data-type' in exynos4412-ppmu-common.dtsi are listed below.
&ppmu_dmc0 {
status = "okay";
events {
ppmu_dmc0_3: ppmu-event3-dmc0 {
event-name = "ppmu-event3-dmc0";
event-data-type = <(PPMU_RO_DATA_CNT |
PPMU_WO_DATA_CNT)>;
};
};
};
...@@ -50,8 +50,6 @@ Required properties only for passive bus device: ...@@ -50,8 +50,6 @@ Required properties only for passive bus device:
Optional properties only for parent bus device: Optional properties only for parent bus device:
- exynos,saturation-ratio: the percentage value which is used to calibrate - exynos,saturation-ratio: the percentage value which is used to calibrate
the performance count against total cycle count. the performance count against total cycle count.
- exynos,voltage-tolerance: the percentage value for bus voltage tolerance
which is used to calculate the max voltage.
Detailed correlation between sub-blocks and power line according to Exynos SoC: Detailed correlation between sub-blocks and power line according to Exynos SoC:
- In case of Exynos3250, there are two power line as following: - In case of Exynos3250, there are two power line as following:
......
...@@ -3532,7 +3532,7 @@ BUS FREQUENCY DRIVER FOR SAMSUNG EXYNOS ...@@ -3532,7 +3532,7 @@ BUS FREQUENCY DRIVER FOR SAMSUNG EXYNOS
M: Chanwoo Choi <cw00.choi@samsung.com> M: Chanwoo Choi <cw00.choi@samsung.com>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
L: linux-samsung-soc@vger.kernel.org L: linux-samsung-soc@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Maintained S: Maintained
F: drivers/devfreq/exynos-bus.c F: drivers/devfreq/exynos-bus.c
F: Documentation/devicetree/bindings/devfreq/exynos-bus.txt F: Documentation/devicetree/bindings/devfreq/exynos-bus.txt
...@@ -4760,9 +4760,9 @@ F: include/linux/devcoredump.h ...@@ -4760,9 +4760,9 @@ F: include/linux/devcoredump.h
DEVICE FREQUENCY (DEVFREQ) DEVICE FREQUENCY (DEVFREQ)
M: MyungJoo Ham <myungjoo.ham@samsung.com> M: MyungJoo Ham <myungjoo.ham@samsung.com>
M: Kyungmin Park <kyungmin.park@samsung.com> M: Kyungmin Park <kyungmin.park@samsung.com>
R: Chanwoo Choi <cw00.choi@samsung.com> M: Chanwoo Choi <cw00.choi@samsung.com>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Maintained S: Maintained
F: drivers/devfreq/ F: drivers/devfreq/
F: include/linux/devfreq.h F: include/linux/devfreq.h
...@@ -4772,10 +4772,11 @@ F: include/trace/events/devfreq.h ...@@ -4772,10 +4772,11 @@ F: include/trace/events/devfreq.h
DEVICE FREQUENCY EVENT (DEVFREQ-EVENT) DEVICE FREQUENCY EVENT (DEVFREQ-EVENT)
M: Chanwoo Choi <cw00.choi@samsung.com> M: Chanwoo Choi <cw00.choi@samsung.com>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Supported S: Supported
F: drivers/devfreq/event/ F: drivers/devfreq/event/
F: drivers/devfreq/devfreq-event.c F: drivers/devfreq/devfreq-event.c
F: include/dt-bindings/pmu/exynos_ppmu.h
F: include/linux/devfreq-event.h F: include/linux/devfreq-event.h
F: Documentation/devicetree/bindings/devfreq/event/ F: Documentation/devicetree/bindings/devfreq/event/
......
...@@ -160,6 +160,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) ...@@ -160,6 +160,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
int lev, prev_lev, ret = 0; int lev, prev_lev, ret = 0;
unsigned long cur_time; unsigned long cur_time;
lockdep_assert_held(&devfreq->lock);
cur_time = jiffies; cur_time = jiffies;
/* Immediately exit if previous_freq is not initialized yet. */ /* Immediately exit if previous_freq is not initialized yet. */
...@@ -409,6 +410,9 @@ static void devfreq_monitor(struct work_struct *work) ...@@ -409,6 +410,9 @@ static void devfreq_monitor(struct work_struct *work)
*/ */
void devfreq_monitor_start(struct devfreq *devfreq) void devfreq_monitor_start(struct devfreq *devfreq)
{ {
if (devfreq->governor->interrupt_driven)
return;
INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor); INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
if (devfreq->profile->polling_ms) if (devfreq->profile->polling_ms)
queue_delayed_work(devfreq_wq, &devfreq->work, queue_delayed_work(devfreq_wq, &devfreq->work,
...@@ -426,6 +430,9 @@ EXPORT_SYMBOL(devfreq_monitor_start); ...@@ -426,6 +430,9 @@ EXPORT_SYMBOL(devfreq_monitor_start);
*/ */
void devfreq_monitor_stop(struct devfreq *devfreq) void devfreq_monitor_stop(struct devfreq *devfreq)
{ {
if (devfreq->governor->interrupt_driven)
return;
cancel_delayed_work_sync(&devfreq->work); cancel_delayed_work_sync(&devfreq->work);
} }
EXPORT_SYMBOL(devfreq_monitor_stop); EXPORT_SYMBOL(devfreq_monitor_stop);
...@@ -453,6 +460,10 @@ void devfreq_monitor_suspend(struct devfreq *devfreq) ...@@ -453,6 +460,10 @@ void devfreq_monitor_suspend(struct devfreq *devfreq)
devfreq_update_status(devfreq, devfreq->previous_freq); devfreq_update_status(devfreq, devfreq->previous_freq);
devfreq->stop_polling = true; devfreq->stop_polling = true;
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
if (devfreq->governor->interrupt_driven)
return;
cancel_delayed_work_sync(&devfreq->work); cancel_delayed_work_sync(&devfreq->work);
} }
EXPORT_SYMBOL(devfreq_monitor_suspend); EXPORT_SYMBOL(devfreq_monitor_suspend);
...@@ -473,11 +484,15 @@ void devfreq_monitor_resume(struct devfreq *devfreq) ...@@ -473,11 +484,15 @@ void devfreq_monitor_resume(struct devfreq *devfreq)
if (!devfreq->stop_polling) if (!devfreq->stop_polling)
goto out; goto out;
if (devfreq->governor->interrupt_driven)
goto out_update;
if (!delayed_work_pending(&devfreq->work) && if (!delayed_work_pending(&devfreq->work) &&
devfreq->profile->polling_ms) devfreq->profile->polling_ms)
queue_delayed_work(devfreq_wq, &devfreq->work, queue_delayed_work(devfreq_wq, &devfreq->work,
msecs_to_jiffies(devfreq->profile->polling_ms)); msecs_to_jiffies(devfreq->profile->polling_ms));
out_update:
devfreq->last_stat_updated = jiffies; devfreq->last_stat_updated = jiffies;
devfreq->stop_polling = false; devfreq->stop_polling = false;
...@@ -509,6 +524,9 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay) ...@@ -509,6 +524,9 @@ void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay)
if (devfreq->stop_polling) if (devfreq->stop_polling)
goto out; goto out;
if (devfreq->governor->interrupt_driven)
goto out;
/* if new delay is zero, stop polling */ /* if new delay is zero, stop polling */
if (!new_delay) { if (!new_delay) {
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
...@@ -625,7 +643,7 @@ struct devfreq *devfreq_add_device(struct device *dev, ...@@ -625,7 +643,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq = find_device_devfreq(dev); devfreq = find_device_devfreq(dev);
mutex_unlock(&devfreq_list_lock); mutex_unlock(&devfreq_list_lock);
if (!IS_ERR(devfreq)) { if (!IS_ERR(devfreq)) {
dev_err(dev, "%s: Unable to create devfreq for the device.\n", dev_err(dev, "%s: devfreq device already exists!\n",
__func__); __func__);
err = -EINVAL; err = -EINVAL;
goto err_out; goto err_out;
...@@ -1195,7 +1213,7 @@ static ssize_t available_governors_show(struct device *d, ...@@ -1195,7 +1213,7 @@ static ssize_t available_governors_show(struct device *d,
* The devfreq with immutable governor (e.g., passive) shows * The devfreq with immutable governor (e.g., passive) shows
* only own governor. * only own governor.
*/ */
if (df->governor->immutable) { if (df->governor && df->governor->immutable) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
"%s ", df->governor_name); "%s ", df->governor_name);
/* /*
...@@ -1397,12 +1415,17 @@ static ssize_t trans_stat_show(struct device *dev, ...@@ -1397,12 +1415,17 @@ static ssize_t trans_stat_show(struct device *dev,
int i, j; int i, j;
unsigned int max_state = devfreq->profile->max_state; unsigned int max_state = devfreq->profile->max_state;
if (!devfreq->stop_polling &&
devfreq_update_status(devfreq, devfreq->previous_freq))
return 0;
if (max_state == 0) if (max_state == 0)
return sprintf(buf, "Not Supported.\n"); return sprintf(buf, "Not Supported.\n");
mutex_lock(&devfreq->lock);
if (!devfreq->stop_polling &&
devfreq_update_status(devfreq, devfreq->previous_freq)) {
mutex_unlock(&devfreq->lock);
return 0;
}
mutex_unlock(&devfreq->lock);
len = sprintf(buf, " From : To\n"); len = sprintf(buf, " From : To\n");
len += sprintf(buf + len, " :"); len += sprintf(buf + len, " :");
for (i = 0; i < max_state; i++) for (i = 0; i < max_state; i++)
......
...@@ -673,7 +673,6 @@ static int exynos_ppmu_probe(struct platform_device *pdev) ...@@ -673,7 +673,6 @@ static int exynos_ppmu_probe(struct platform_device *pdev)
for (i = 0; i < info->num_events; i++) { for (i = 0; i < info->num_events; i++) {
edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
if (IS_ERR(edev[i])) { if (IS_ERR(edev[i])) {
ret = PTR_ERR(edev[i]);
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to add devfreq-event device\n"); "failed to add devfreq-event device\n");
return PTR_ERR(edev[i]); return PTR_ERR(edev[i]);
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
* @name: Governor's name * @name: Governor's name
* @immutable: Immutable flag for governor. If the value is 1, * @immutable: Immutable flag for governor. If the value is 1,
* this govenror is never changeable to other governor. * 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. * @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run * Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the * devfreq_dev_profile.get_dev_status() to get the
...@@ -49,6 +51,7 @@ struct devfreq_governor { ...@@ -49,6 +51,7 @@ struct devfreq_governor {
const char name[DEVFREQ_NAME_LEN]; const char name[DEVFREQ_NAME_LEN];
const unsigned int immutable; const unsigned int immutable;
const unsigned int interrupt_driven;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq); int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq, int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data); unsigned int event, void *data);
......
...@@ -11,11 +11,13 @@ ...@@ -11,11 +11,13 @@
#include <linux/devfreq.h> #include <linux/devfreq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/workqueue.h>
#include "governor.h" #include "governor.h"
...@@ -33,6 +35,8 @@ ...@@ -33,6 +35,8 @@
#define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN BIT(30) #define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN BIT(30)
#define ACTMON_DEV_CTRL_ENB BIT(31) #define ACTMON_DEV_CTRL_ENB BIT(31)
#define ACTMON_DEV_CTRL_STOP 0x00000000
#define ACTMON_DEV_UPPER_WMARK 0x4 #define ACTMON_DEV_UPPER_WMARK 0x4
#define ACTMON_DEV_LOWER_WMARK 0x8 #define ACTMON_DEV_LOWER_WMARK 0x8
#define ACTMON_DEV_INIT_AVG 0xc #define ACTMON_DEV_INIT_AVG 0xc
...@@ -68,6 +72,8 @@ ...@@ -68,6 +72,8 @@
#define KHZ 1000 #define KHZ 1000
#define KHZ_MAX (ULONG_MAX / KHZ)
/* Assume that the bus is saturated if the utilization is 25% */ /* Assume that the bus is saturated if the utilization is 25% */
#define BUS_SATURATION_RATIO 25 #define BUS_SATURATION_RATIO 25
...@@ -90,9 +96,10 @@ struct tegra_devfreq_device_config { ...@@ -90,9 +96,10 @@ struct tegra_devfreq_device_config {
unsigned int boost_down_threshold; unsigned int boost_down_threshold;
/* /*
* Threshold of activity (cycles) below which the CPU frequency isn't * Threshold of activity (cycles translated to kHz) below which the
* to be taken into account. This is to avoid increasing the EMC * CPU frequency isn't to be taken into account. This is to avoid
* frequency when the CPU is very busy but not accessing the bus often. * increasing the EMC frequency when the CPU is very busy but not
* accessing the bus often.
*/ */
u32 avg_dependency_threshold; u32 avg_dependency_threshold;
}; };
...@@ -102,7 +109,7 @@ enum tegra_actmon_device { ...@@ -102,7 +109,7 @@ enum tegra_actmon_device {
MCCPU, 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) */ /* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0, .offset = 0x1c0,
...@@ -117,10 +124,10 @@ static struct tegra_devfreq_device_config actmon_device_configs[] = { ...@@ -117,10 +124,10 @@ static struct tegra_devfreq_device_config actmon_device_configs[] = {
.offset = 0x200, .offset = 0x200,
.irq_mask = 1 << 25, .irq_mask = 1 << 25,
.boost_up_coeff = 800, .boost_up_coeff = 800,
.boost_down_coeff = 90, .boost_down_coeff = 40,
.boost_up_threshold = 27, .boost_up_threshold = 27,
.boost_down_threshold = 10, .boost_down_threshold = 10,
.avg_dependency_threshold = 50000, .avg_dependency_threshold = 16000, /* 16MHz in kHz units */
}, },
}; };
...@@ -156,11 +163,16 @@ struct tegra_devfreq { ...@@ -156,11 +163,16 @@ struct tegra_devfreq {
struct clk *emc_clock; struct clk *emc_clock;
unsigned long max_freq; unsigned long max_freq;
unsigned long cur_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)]; struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
int irq; unsigned int irq;
bool started;
}; };
struct tegra_actmon_emc_ratio { struct tegra_actmon_emc_ratio {
...@@ -168,8 +180,8 @@ struct tegra_actmon_emc_ratio { ...@@ -168,8 +180,8 @@ struct tegra_actmon_emc_ratio {
unsigned long emc_freq; unsigned long emc_freq;
}; };
static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { static const struct tegra_actmon_emc_ratio actmon_emc_ratios[] = {
{ 1400000, ULONG_MAX }, { 1400000, KHZ_MAX },
{ 1200000, 750000 }, { 1200000, 750000 },
{ 1100000, 600000 }, { 1100000, 600000 },
{ 1000000, 500000 }, { 1000000, 500000 },
...@@ -199,18 +211,26 @@ static void device_writel(struct tegra_devfreq_device *dev, u32 val, ...@@ -199,18 +211,26 @@ static void device_writel(struct tegra_devfreq_device *dev, u32 val,
writel_relaxed(val, dev->regs + offset); writel_relaxed(val, dev->regs + offset);
} }
static unsigned long do_percent(unsigned long val, unsigned int pct) static unsigned long do_percent(unsigned long long val, unsigned int pct)
{ {
return val * pct / 100; val = val * pct;
do_div(val, 100);
/*
* High freq + high boosting percent + large polling interval are
* resulting in integer overflow when watermarks are calculated.
*/
return min_t(u64, val, U32_MAX);
} }
static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra, static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev) struct tegra_devfreq_device *dev)
{ {
u32 avg = dev->avg_count;
u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ; u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD; u32 band = avg_band_freq * tegra->devfreq->profile->polling_ms;
u32 avg;
avg = min(dev->avg_count, U32_MAX - band);
device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK); device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
avg = max(dev->avg_count, band); avg = max(dev->avg_count, band);
...@@ -220,7 +240,7 @@ static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra, ...@@ -220,7 +240,7 @@ static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev) struct tegra_devfreq_device *dev)
{ {
u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; u32 val = tegra->cur_freq * tegra->devfreq->profile->polling_ms;
device_writel(dev, do_percent(val, dev->config->boost_up_threshold), device_writel(dev, do_percent(val, dev->config->boost_up_threshold),
ACTMON_DEV_UPPER_WMARK); ACTMON_DEV_UPPER_WMARK);
...@@ -229,12 +249,6 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, ...@@ -229,12 +249,6 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
ACTMON_DEV_LOWER_WMARK); 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, static void actmon_isr_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev) struct tegra_devfreq_device *dev)
{ {
...@@ -256,10 +270,10 @@ static void actmon_isr_device(struct tegra_devfreq *tegra, ...@@ -256,10 +270,10 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; 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; 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) { } else if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) {
/* /*
* new_boost = old_boost * down_coef * new_boost = old_boost * down_coef
...@@ -270,31 +284,22 @@ static void actmon_isr_device(struct tegra_devfreq *tegra, ...@@ -270,31 +284,22 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) {
dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
dev->boost_freq = 0; dev->boost_freq = 0;
else
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
} }
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)
dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
} }
device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL); device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS); 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, static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
unsigned long cpu_freq) unsigned long cpu_freq)
{ {
unsigned int i; 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++) { for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++) {
if (cpu_freq >= ratio->cpu_freq) { if (cpu_freq >= ratio->cpu_freq) {
...@@ -308,25 +313,37 @@ static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra, ...@@ -308,25 +313,37 @@ static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
return 0; 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 / tegra->devfreq->profile->polling_ms;
avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold;
target_freq = do_percent(target_freq, avg_sustain_coef);
return target_freq;
}
static void actmon_update_target(struct tegra_devfreq *tegra, static void actmon_update_target(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev) struct tegra_devfreq_device *dev)
{ {
unsigned long cpu_freq = 0; unsigned long cpu_freq = 0;
unsigned long static_cpu_emc_freq = 0; unsigned long static_cpu_emc_freq = 0;
unsigned int avg_sustain_coef;
if (dev->config->avg_dependency_threshold) { dev->target_freq = actmon_device_target_freq(tegra, dev);
cpu_freq = cpufreq_get(0);
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); 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 += dev->boost_freq;
if (dev->avg_count >= dev->config->avg_dependency_threshold)
dev->target_freq = max(dev->target_freq, static_cpu_emc_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) static irqreturn_t actmon_thread_isr(int irq, void *data)
...@@ -354,7 +371,7 @@ static irqreturn_t actmon_thread_isr(int irq, void *data) ...@@ -354,7 +371,7 @@ static irqreturn_t actmon_thread_isr(int irq, void *data)
return handled ? IRQ_HANDLED : IRQ_NONE; return handled ? IRQ_HANDLED : IRQ_NONE;
} }
static int tegra_actmon_rate_notify_cb(struct notifier_block *nb, static int tegra_actmon_clk_notify_cb(struct notifier_block *nb,
unsigned long action, void *ptr) unsigned long action, void *ptr)
{ {
struct clk_notifier_data *data = ptr; struct clk_notifier_data *data = ptr;
...@@ -365,7 +382,7 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb, ...@@ -365,7 +382,7 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,
if (action != POST_RATE_CHANGE) if (action != POST_RATE_CHANGE)
return NOTIFY_OK; 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; tegra->cur_freq = data->new_rate / KHZ;
...@@ -375,7 +392,79 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb, ...@@ -375,7 +392,79 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,
tegra_devfreq_update_wmark(tegra, dev); tegra_devfreq_update_wmark(tegra, dev);
} }
actmon_write_barrier(tegra); 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)
{
struct tegra_devfreq_device *actmon_dev = &tegra->devices[MCCPU];
unsigned long static_cpu_emc_freq, dev_freq;
dev_freq = actmon_device_target_freq(tegra, actmon_dev);
/* check whether CPU's freq is taken into account at all */
if (dev_freq < actmon_dev->config->avg_dependency_threshold)
return 0;
static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);
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; return NOTIFY_OK;
} }
...@@ -385,9 +474,12 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, ...@@ -385,9 +474,12 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
{ {
u32 val = 0; u32 val = 0;
/* reset boosting on governor's restart */
dev->boost_freq = 0;
dev->target_freq = tegra->cur_freq; dev->target_freq = tegra->cur_freq;
dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; dev->avg_count = tegra->cur_freq * tegra->devfreq->profile->polling_ms;
device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG); device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
tegra_devfreq_update_avg_wmark(tegra, dev); tegra_devfreq_update_avg_wmark(tegra, dev);
...@@ -405,45 +497,116 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, ...@@ -405,45 +497,116 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
<< ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT;
val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN; val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN;
val |= ACTMON_DEV_CTRL_AVG_BELOW_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_CONSECUTIVE_ABOVE_WMARK_EN;
val |= ACTMON_DEV_CTRL_ENB; val |= ACTMON_DEV_CTRL_ENB;
device_writel(dev, val, ACTMON_DEV_CTRL); 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; unsigned int i;
disable_irq(tegra->irq); 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_resume(struct tegra_devfreq *tegra)
{
unsigned int i;
int err;
if (!tegra->devfreq->profile->polling_ms || !tegra->started)
return 0;
actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1, actmon_writel(tegra, tegra->devfreq->profile->polling_ms - 1,
ACTMON_GLB_PERIOD_CTRL); 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++) for (i = 0; i < ARRAY_SIZE(tegra->devices); i++)
tegra_actmon_configure_device(tegra, &tegra->devices[i]); tegra_actmon_configure_device(tegra, &tegra->devices[i]);
actmon_write_barrier(tegra); /*
* 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); enable_irq(tegra->irq);
return 0;
err_stop:
tegra_actmon_stop_devices(tegra);
clk_notifier_unregister(tegra->emc_clock, &tegra->clk_rate_change_nb);
return err;
} }
static void tegra_actmon_stop(struct tegra_devfreq *tegra) static int tegra_actmon_start(struct tegra_devfreq *tegra)
{ {
unsigned int i; int ret = 0;
disable_irq(tegra->irq); if (!tegra->started) {
tegra->started = true;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { ret = tegra_actmon_resume(tegra);
device_writel(&tegra->devices[i], 0x00000000, ACTMON_DEV_CTRL); if (ret)
device_writel(&tegra->devices[i], ACTMON_INTR_STATUS_CLEAR, tegra->started = false;
ACTMON_DEV_INTR_STATUS);
} }
actmon_write_barrier(tegra); return ret;
}
static void tegra_actmon_pause(struct tegra_devfreq *tegra)
{
if (!tegra->devfreq->profile->polling_ms || !tegra->started)
return;
enable_irq(tegra->irq); disable_irq(tegra->irq);
cpufreq_unregister_notifier(&tegra->cpu_rate_change_nb,
CPUFREQ_TRANSITION_NOTIFIER);
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 void tegra_actmon_stop(struct tegra_devfreq *tegra)
{
tegra_actmon_pause(tegra);
tegra->started = false;
} }
static int tegra_devfreq_target(struct device *dev, unsigned long *freq, static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
...@@ -463,7 +626,7 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq, ...@@ -463,7 +626,7 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
rate = dev_pm_opp_get_freq(opp); rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(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) if (err)
return err; return err;
...@@ -492,7 +655,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, ...@@ -492,7 +655,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
stat->private_data = tegra; stat->private_data = tegra;
/* The below are to be used by the other governors */ /* 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]; actmon_dev = &tegra->devices[MCALL];
...@@ -503,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, ...@@ -503,7 +666,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
stat->busy_time *= 100 / BUS_SATURATION_RATIO; stat->busy_time *= 100 / BUS_SATURATION_RATIO;
/* Number of cycles in a sampling period */ /* Number of cycles in a sampling period */
stat->total_time = ACTMON_SAMPLING_PERIOD * cur_freq; stat->total_time = tegra->devfreq->profile->polling_ms * cur_freq;
stat->busy_time = min(stat->busy_time, stat->total_time); stat->busy_time = min(stat->busy_time, stat->total_time);
...@@ -511,7 +674,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, ...@@ -511,7 +674,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
} }
static struct devfreq_dev_profile tegra_devfreq_profile = { static struct devfreq_dev_profile tegra_devfreq_profile = {
.polling_ms = 0, .polling_ms = ACTMON_SAMPLING_PERIOD,
.target = tegra_devfreq_target, .target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status, .get_dev_status = tegra_devfreq_get_dev_status,
}; };
...@@ -542,7 +705,7 @@ static int tegra_governor_get_target(struct devfreq *devfreq, ...@@ -542,7 +705,7 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
target_freq = max(target_freq, dev->target_freq); target_freq = max(target_freq, dev->target_freq);
} }
*freq = target_freq * KHZ; *freq = target_freq;
return 0; return 0;
} }
...@@ -551,11 +714,19 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, ...@@ -551,11 +714,19 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
unsigned int event, void *data) unsigned int event, void *data)
{ {
struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent); struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent);
unsigned int *new_delay = data;
int ret = 0;
/*
* 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) { switch (event) {
case DEVFREQ_GOV_START: case DEVFREQ_GOV_START:
devfreq_monitor_start(devfreq); devfreq_monitor_start(devfreq);
tegra_actmon_start(tegra); ret = tegra_actmon_start(tegra);
break; break;
case DEVFREQ_GOV_STOP: case DEVFREQ_GOV_STOP:
...@@ -563,6 +734,21 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, ...@@ -563,6 +734,21 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
devfreq_monitor_stop(devfreq); devfreq_monitor_stop(devfreq);
break; break;
case DEVFREQ_GOV_INTERVAL:
/*
* ACTMON hardware supports up to 256 milliseconds for the
* sampling period.
*/
if (*new_delay > 256) {
ret = -EINVAL;
break;
}
tegra_actmon_pause(tegra);
devfreq_interval_update(devfreq, new_delay);
ret = tegra_actmon_resume(tegra);
break;
case DEVFREQ_GOV_SUSPEND: case DEVFREQ_GOV_SUSPEND:
tegra_actmon_stop(tegra); tegra_actmon_stop(tegra);
devfreq_monitor_suspend(devfreq); devfreq_monitor_suspend(devfreq);
...@@ -570,11 +756,11 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, ...@@ -570,11 +756,11 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
case DEVFREQ_GOV_RESUME: case DEVFREQ_GOV_RESUME:
devfreq_monitor_resume(devfreq); devfreq_monitor_resume(devfreq);
tegra_actmon_start(tegra); ret = tegra_actmon_start(tegra);
break; break;
} }
return 0; return ret;
} }
static struct devfreq_governor tegra_devfreq_governor = { static struct devfreq_governor tegra_devfreq_governor = {
...@@ -582,14 +768,16 @@ static struct devfreq_governor tegra_devfreq_governor = { ...@@ -582,14 +768,16 @@ static struct devfreq_governor tegra_devfreq_governor = {
.get_target_freq = tegra_governor_get_target, .get_target_freq = tegra_governor_get_target,
.event_handler = tegra_governor_event_handler, .event_handler = tegra_governor_event_handler,
.immutable = true, .immutable = true,
.interrupt_driven = true,
}; };
static int tegra_devfreq_probe(struct platform_device *pdev) static int tegra_devfreq_probe(struct platform_device *pdev)
{ {
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev; struct tegra_devfreq_device *dev;
struct tegra_devfreq *tegra;
struct devfreq *devfreq;
unsigned int i; unsigned int i;
unsigned long rate; long rate;
int err; int err;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
...@@ -618,12 +806,22 @@ static int tegra_devfreq_probe(struct platform_device *pdev) ...@@ -618,12 +806,22 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return PTR_ERR(tegra->emc_clock); return PTR_ERR(tegra->emc_clock);
} }
tegra->irq = platform_get_irq(pdev, 0); err = platform_get_irq(pdev, 0);
if (tegra->irq < 0) { if (err < 0) {
err = tegra->irq;
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err); dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err);
return err; return err;
} }
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); reset_control_assert(tegra->reset);
...@@ -636,8 +834,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev) ...@@ -636,8 +834,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
reset_control_deassert(tegra->reset); 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);
tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ; if (rate < 0) {
dev_err(&pdev->dev, "Failed to round clock rate: %ld\n", rate);
return rate;
}
tegra->max_freq = rate / KHZ;
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) { for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
dev = tegra->devices + i; dev = tegra->devices + i;
...@@ -648,7 +851,14 @@ static int tegra_devfreq_probe(struct platform_device *pdev) ...@@ -648,7 +851,14 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) { for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate); rate = clk_round_rate(tegra->emc_clock, rate);
err = dev_pm_opp_add(&pdev->dev, rate, 0); if (rate < 0) {
dev_err(&pdev->dev,
"Failed to round clock rate: %ld\n", rate);
err = rate;
goto remove_opps;
}
err = dev_pm_opp_add(&pdev->dev, rate / KHZ, 0);
if (err) { if (err) {
dev_err(&pdev->dev, "Failed to add OPP: %d\n", err); dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
goto remove_opps; goto remove_opps;
...@@ -657,49 +867,33 @@ static int tegra_devfreq_probe(struct platform_device *pdev) ...@@ -657,49 +867,33 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, tegra); platform_set_drvdata(pdev, tegra);
tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_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;
if (err) {
dev_err(&pdev->dev, INIT_DELAYED_WORK(&tegra->cpufreq_update_work,
"Failed to register rate change notifier\n"); tegra_actmon_delayed_update);
goto remove_opps;
}
err = devfreq_add_governor(&tegra_devfreq_governor); err = devfreq_add_governor(&tegra_devfreq_governor);
if (err) { if (err) {
dev_err(&pdev->dev, "Failed to add governor: %d\n", err); dev_err(&pdev->dev, "Failed to add governor: %d\n", err);
goto unreg_notifier; goto remove_opps;
} }
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra->devfreq = devfreq_add_device(&pdev->dev, tegra_devfreq_profile.initial_freq /= KHZ;
&tegra_devfreq_profile,
"tegra_actmon",
NULL);
if (IS_ERR(tegra->devfreq)) {
err = PTR_ERR(tegra->devfreq);
goto remove_governor;
}
err = devm_request_threaded_irq(&pdev->dev, tegra->irq, NULL, devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
actmon_thread_isr, IRQF_ONESHOT, "tegra_actmon", NULL);
"tegra-devfreq", tegra); if (IS_ERR(devfreq)) {
if (err) { err = PTR_ERR(devfreq);
dev_err(&pdev->dev, "Interrupt request failed: %d\n", err); goto remove_governor;
goto remove_devfreq;
} }
return 0; return 0;
remove_devfreq:
devfreq_remove_device(tegra->devfreq);
remove_governor: remove_governor:
devfreq_remove_governor(&tegra_devfreq_governor); devfreq_remove_governor(&tegra_devfreq_governor);
unreg_notifier:
clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb);
remove_opps: remove_opps:
dev_pm_opp_remove_all_dynamic(&pdev->dev); dev_pm_opp_remove_all_dynamic(&pdev->dev);
...@@ -716,7 +910,6 @@ static int tegra_devfreq_remove(struct platform_device *pdev) ...@@ -716,7 +910,6 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
devfreq_remove_device(tegra->devfreq); devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor); devfreq_remove_governor(&tegra_devfreq_governor);
clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb);
dev_pm_opp_remove_all_dynamic(&pdev->dev); dev_pm_opp_remove_all_dynamic(&pdev->dev);
reset_control_reset(tegra->reset); reset_control_reset(tegra->reset);
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Samsung Exynos PPMU event types for counting in regs
*
* Copyright (c) 2019, Samsung Electronics
* Author: Lukasz Luba <l.luba@partner.samsung.com>
*/
#ifndef __DT_BINDINGS_PMU_EXYNOS_PPMU_H
#define __DT_BINDINGS_PMU_EXYNOS_PPMU_H
#define PPMU_RO_BUSY_CYCLE_CNT 0x0
#define PPMU_WO_BUSY_CYCLE_CNT 0x1
#define PPMU_RW_BUSY_CYCLE_CNT 0x2
#define PPMU_RO_REQUEST_CNT 0x3
#define PPMU_WO_REQUEST_CNT 0x4
#define PPMU_RO_DATA_CNT 0x5
#define PPMU_WO_DATA_CNT 0x6
#define PPMU_RO_LATENCY 0x12
#define PPMU_WO_LATENCY 0x16
#define PPMU_V2_RO_DATA_CNT 0x4
#define PPMU_V2_WO_DATA_CNT 0x5
#define PPMU_V2_EVT3_RW_DATA_CNT 0x22
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment