diff options
-rw-r--r-- | arch/arm/mach-tegra/tegra3_actmon.c | 126 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 9 |
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), |