summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 e446870d0e73..a46074d43f53 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
@@ -1046,6 +1045,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;
@@ -3473,6 +3474,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,
@@ -3488,6 +3491,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 = {
@@ -3773,6 +3777,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),