summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-06-15 19:03:22 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:47:11 -0800
commit962a04f157cead4c55960f29933393502aeee559 (patch)
tree876268254ab45b2feaf7cf4ab7679409798b97f4 /arch
parent01e3b43a8dcfcf48f8f6fef42c4673194f8fef4b (diff)
ARM: tegra: clock: Add Tegra3 AVP activity monitor support
Added AVP clock control using Tegra3 activity monitoring device. The target AVP frequency floor is set based on average load and short term boost. Average AVP load time (time when AVP is not halted by flow controller) is determined by fixed frequency count provided by monitoring h/w featuring 1st order IIR activity filter. The boost frequency is calculated by s/w - exponentially increasing/ decreasing when sampled AVP activity has crossed upper/lower boost watermarks. The implementation is interrupt driven - periodic sampling is hidden by h/w. The tune-able debugfs parameters are: /sys/kernel/debug/tegra_actmon/avp/boost_step - boost rate increase step (% of max AVP frequency) /sys/kernel/debug/tegra_actmon/avp/boost_rate_inc - boost rate increase factor (%) /sys/kernel/debug/tegra_actmon/avp/boost_rate_dec - boost rate decrease factor (%) /sys/kernel/debug/tegra_actmon/avp/boost_threshold_up - upper activity watermark for boost increase (AVP active time in %) /sys/kernel/debug/tegra_actmon/avp/boost_threshold_dn - lower activity watermark for boost decrease (AVP active time in %) Original-Change-Id: Ia82247176531f2fb67acfc277e63b9f16916a488 Reviewed-on: http://git-master/r/37175 Reviewed-by: Niket Sirsi <nsirsi@nvidia.com> Tested-by: Niket Sirsi <nsirsi@nvidia.com> Rebase-Id: R995949fe30f188c16c3fa39e292a2ca56256f2a3
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/tegra3_actmon.c126
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c9
2 files changed, 102 insertions, 33 deletions
diff --git a/arch/arm/mach-tegra/tegra3_actmon.c b/arch/arm/mach-tegra/tegra3_actmon.c
index 7629a82e1e8e..4aebd75f30fd 100644
--- a/arch/arm/mach-tegra/tegra3_actmon.c
+++ b/arch/arm/mach-tegra/tegra3_actmon.c
@@ -66,6 +66,7 @@
#define ACTMON_DEV_INTR_AVG_UP_WMARK (0x1 << 24)
#define ACTMON_DEFAULT_AVG_WINDOW_LOG2 6
+#define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */
enum actmon_type {
ACTMON_LOAD_SAMPLER,
@@ -196,6 +197,18 @@ static inline void actmon_dev_avg_wmark_set(struct actmon_dev *dev)
actmon_writel(avg - band, offs(ACTMON_DEV_AVG_DOWN_WMARK));
}
+static unsigned long actmon_dev_avg_freq_get(struct actmon_dev *dev)
+{
+ u64 val;
+
+ if (dev->type == ACTMON_FREQ_SAMPLER)
+ return dev->avg_count / actmon_sampling_period;
+
+ val = (u64)dev->avg_count * dev->cur_freq;
+ do_div(val, actmon_clk_freq * actmon_sampling_period);
+ return (u32)val;
+}
+
/* Activity monitor sampling operations */
irqreturn_t actmon_dev_isr(int irq, void *dev_id)
{
@@ -248,36 +261,29 @@ irqreturn_t actmon_dev_isr(int irq, void *dev_id)
irqreturn_t actmon_dev_fn(int irq, void *dev_id)
{
- unsigned long flags;
- unsigned long freq = 0;
+ unsigned long flags, freq;
struct actmon_dev *dev = (struct actmon_dev *)dev_id;
spin_lock_irqsave(&dev->lock, flags);
- if (dev->state == ACTMON_ON) {
- if (dev->type == ACTMON_FREQ_SAMPLER) {
- freq = dev->avg_count / actmon_sampling_period;
- }
- else {
- u64 tmp = (u64)dev->avg_count * dev->cur_freq;
- freq = actmon_clk_freq * actmon_sampling_period;
- do_div(tmp, freq);
- freq = (u32)tmp;
- }
-
- dev->avg_actv_freq = freq;
- freq = do_percent(freq, dev->avg_sustain_coef);
- freq += dev->boost_freq;
- dev->target_freq = freq;
+ if (dev->state != ACTMON_ON) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_HANDLED;
}
+
+ freq = actmon_dev_avg_freq_get(dev);
+ dev->avg_actv_freq = freq;
+ freq = do_percent(freq, dev->avg_sustain_coef);
+ freq += dev->boost_freq;
+ dev->target_freq = freq;
+
spin_unlock_irqrestore(&dev->lock, flags);
- if (freq) {
- pr_debug("%s.%s(kHz): avg: %lu, target: %lu current: %lu\n",
+ pr_debug("%s.%s(kHz): avg: %lu, target: %lu current: %lu\n",
dev->dev_id, dev->con_id, dev->avg_actv_freq,
dev->target_freq, dev->cur_freq);
- clk_set_rate(dev->clk, freq * 1000);
- }
+ clk_set_rate(dev->clk, freq * 1000);
+
return IRQ_HANDLED;
}
@@ -309,9 +315,15 @@ static void actmon_dev_configure(struct actmon_dev *dev, unsigned long freq)
dev->target_freq = freq;
dev->avg_actv_freq = freq;
- dev->avg_count = (dev->type == ACTMON_FREQ_SAMPLER) ?
- dev->cur_freq : actmon_clk_freq;
- dev->avg_count *= actmon_sampling_period;
+ if (dev->type == ACTMON_FREQ_SAMPLER) {
+ dev->avg_count = dev->cur_freq * actmon_sampling_period;
+ dev->avg_band_freq = dev->max_freq *
+ ACTMON_DEFAULT_AVG_BAND / 1000;
+ } else {
+ dev->avg_count = actmon_clk_freq * actmon_sampling_period;
+ dev->avg_band_freq = actmon_clk_freq *
+ ACTMON_DEFAULT_AVG_BAND / 1000;
+ }
actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG));
BUG_ON(!dev->boost_up_threshold);
@@ -329,7 +341,7 @@ static void actmon_dev_configure(struct actmon_dev *dev, unsigned long freq)
val |= ((dev->down_wmark_window - 1) <<
ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT) &
ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK;
- val |= ((dev->up_wmark_window - 1) <<
+ val |= ((dev->up_wmark_window - 1) <<
ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT) &
ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK;
val |= ACTMON_DEV_CTRL_DOWN_WMARK_ENB | ACTMON_DEV_CTRL_UP_WMARK_ENB;
@@ -462,15 +474,16 @@ static int __init actmon_dev_init(struct actmon_dev *dev)
return 0;
}
-/* EMC activity monitor: frequency sampling device */
+/* EMC activity monitor: frequency sampling device:
+ * activity counter is incremented every 256 memory transactions, and
+ * each transaction takes 2 EMC clocks; count_weight = 512.
+ */
static struct actmon_dev actmon_dev_emc = {
.reg = 0x1c0,
.glb_status_irq_mask = (0x1 << 26),
.dev_id = "tegra_actmon",
.con_id = "emc",
- .avg_band_freq = 3000,
-
.boost_freq_step = 16000,
.boost_up_coef = 200,
.boost_down_coef = 50,
@@ -490,8 +503,38 @@ static struct actmon_dev actmon_dev_emc = {
},
};
+/* AVP activity monitor: load sampling device:
+ * activity counter is incremented on every actmon clock pulse while
+ * AVP is not halted by flow controller; count_weight = 1.
+ */
+static struct actmon_dev actmon_dev_avp = {
+ .reg = 0x0c0,
+ .glb_status_irq_mask = (0x1 << 30),
+ .dev_id = "tegra_actmon",
+ .con_id = "avp",
+
+ .boost_freq_step = 8000,
+ .boost_up_coef = 200,
+ .boost_down_coef = 50,
+ .boost_up_threshold = 75,
+ .boost_down_threshold = 50,
+
+ .up_wmark_window = 1,
+ .down_wmark_window = 3,
+ .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2,
+ .count_weight = 0x1,
+
+ .type = ACTMON_LOAD_SAMPLER,
+ .state = ACTMON_UNINITIALIZED,
+
+ .rate_change_nb = {
+ .notifier_call = actmon_rate_notify_cb,
+ },
+};
+
static struct actmon_dev *actmon_devices[] = {
&actmon_dev_emc,
+ &actmon_dev_avp,
};
/* Activity monitor suspend/resume */
@@ -544,6 +587,18 @@ static const struct file_operations type_fops = {
.release = single_release,
};
+static int actv_get(void *data, u64 *val)
+{
+ unsigned long flags;
+ struct actmon_dev *dev = data;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ *val = actmon_dev_avg_freq_get(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(actv_fops, actv_get, NULL, "%llu\n");
+
static int step_get(void *data, u64 *val)
{
struct actmon_dev *dev = data;
@@ -652,12 +707,21 @@ static int period_get(void *data, u64 *val)
}
static int period_set(void *data, u64 val)
{
+ int i;
+ unsigned long flags;
u8 period = (u8)val;
if (period) {
actmon_sampling_period = period;
actmon_writel(period - 1, ACTMON_GLB_PERIOD_CTRL);
- /* FIXME: update up/down wm for load sampler */
+
+ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) {
+ struct actmon_dev *dev = actmon_devices[i];
+ spin_lock_irqsave(&dev->lock, flags);
+ actmon_dev_wmark_set(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ actmon_wmb();
return 0;
}
return -EINVAL;
@@ -681,8 +745,8 @@ static int actmon_debugfs_create_dev(struct actmon_dev *dev)
if (!d)
return -ENOMEM;
- d = debugfs_create_u32(
- "avg_activity", RO_MODE, dir, (u32 *)&dev->avg_actv_freq);
+ d = debugfs_create_file(
+ "avg_activity", RO_MODE, dir, dev, &actv_fops);
if (!d)
return -ENOMEM;
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c
index ac4aaa0a9005..543d53e6249c 100644
--- a/arch/arm/mach-tegra/tegra3_clocks.c
+++ b/arch/arm/mach-tegra/tegra3_clocks.c
@@ -5,8 +5,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -1087,6 +1086,8 @@ static long tegra3_sbus_cmplx_round_rate(struct clk *c, unsigned long rate)
struct clk *new_parent;
struct clk *new_parent_after_round;
+ rate = max(rate, c->min_rate);
+
new_parent = (rate <= c->u.system.threshold) ?
c->u.system.sclk_low : c->u.system.sclk_high;
@@ -3514,6 +3515,8 @@ static struct clk tegra_clk_pclk = {
.min_rate = 40000000,
};
+static struct raw_notifier_head sbus_rate_change_nh;
+
static struct clk tegra_clk_sbus_cmplx = {
.name = "sbus",
.parent = &tegra_clk_sclk,
@@ -3529,6 +3532,7 @@ static struct clk tegra_clk_sbus_cmplx = {
.threshold = 108000000, /* exact factor of low range pll_p */
#endif
},
+ .rate_change_nh = &sbus_rate_change_nh,
};
static struct clk tegra_clk_blink = {
@@ -3814,6 +3818,7 @@ struct clk tegra_list_clks[] = {
SHARED_CLK("usb1.sclk", "tegra-ehci.0", "sclk", &tegra_clk_sbus_cmplx, NULL, 0),
SHARED_CLK("usb2.sclk", "tegra-ehci.1", "sclk", &tegra_clk_sbus_cmplx, NULL, 0),
SHARED_CLK("usb3.sclk", "tegra-ehci.2", "sclk", &tegra_clk_sbus_cmplx, NULL, 0),
+ SHARED_CLK("mon.avp", "tegra_actmon", "avp", &tegra_clk_sbus_cmplx, NULL, 0),
SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc, NULL, 0),
SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc, NULL, 0),
SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc, NULL, 0),