ARM: tegra: clock: Add Tegra3 AVP activity monitor support
Alex Frid [Thu, 16 Jun 2011 02:03:22 +0000 (19:03 -0700)]
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

arch/arm/mach-tegra/tegra3_actmon.c
arch/arm/mach-tegra/tegra3_clocks.c

index 7629a82..4aebd75 100644 (file)
@@ -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;
 
index ac4aaa0..543d53e 100644 (file)
@@ -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),