diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_actmon.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra3_actmon.c | 782 |
1 files changed, 782 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra3_actmon.c b/arch/arm/mach-tegra/tegra3_actmon.c new file mode 100644 index 000000000000..7629a82e1e8e --- /dev/null +++ b/arch/arm/mach-tegra/tegra3_actmon.c @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2011, NVIDIA Corporation. + * + * 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; 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 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/suspend.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include <mach/iomap.h> +#include <mach/irqs.h> +#include <mach/clk.h> + +#include "clock.h" + +#define ACTMON_GLB_STATUS 0x00 +#define ACTMON_GLB_PERIOD_CTRL 0x04 + +#define ACTMON_DEV_CTRL 0x00 +#define ACTMON_DEV_CTRL_ENB (0x1 << 31) +#define ACTMON_DEV_CTRL_UP_WMARK_ENB (0x1 << 30) +#define ACTMON_DEV_CTRL_DOWN_WMARK_ENB (0x1 << 29) +#define ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT 26 +#define ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK (0x7 << 26) +#define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT 23 +#define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK (0x7 << 23) +#define ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB (0x1 << 21) +#define ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB (0x1 << 20) +#define ACTMON_DEV_CTRL_PERIODIC_ENB (0x1 << 18) +#define ACTMON_DEV_CTRL_K_VAL_SHIFT 10 +#define ACTMON_DEV_CTRL_K_VAL_MASK (0x7 << 10) + +#define ACTMON_DEV_UP_WMARK 0x04 +#define ACTMON_DEV_DOWN_WMARK 0x08 +#define ACTMON_DEV_INIT_AVG 0x0c +#define ACTMON_DEV_AVG_UP_WMARK 0x10 +#define ACTMON_DEV_AVG_DOWN_WMARK 0x14 + +#define ACTMON_DEV_COUNT_WEGHT 0x18 +#define ACTMON_DEV_COUNT 0x1c +#define ACTMON_DEV_AVG_COUNT 0x20 + +#define ACTMON_DEV_INTR_STATUS 0x24 +#define ACTMON_DEV_INTR_UP_WMARK (0x1 << 31) +#define ACTMON_DEV_INTR_DOWN_WMARK (0x1 << 30) +#define ACTMON_DEV_INTR_AVG_DOWN_WMARK (0x1 << 25) +#define ACTMON_DEV_INTR_AVG_UP_WMARK (0x1 << 24) + +#define ACTMON_DEFAULT_AVG_WINDOW_LOG2 6 + +enum actmon_type { + ACTMON_LOAD_SAMPLER, + ACTMON_FREQ_SAMPLER, +}; + +enum actmon_state { + ACTMON_UNINITIALIZED = -1, + ACTMON_OFF = 0, + ACTMON_ON = 1, + ACTMON_SUSPENDED = 2, +}; + +#define ACTMON_DEFAULT_SAMPLING_PERIOD 10 +static u8 actmon_sampling_period; + +static unsigned long actmon_clk_freq; + + +/* Units: + * - frequency in kHz + * - coefficients, and thresholds in % + * - sampling period in ms + * - window in sample periods (value = setting + 1) + */ +struct actmon_dev { + u32 reg; + u32 glb_status_irq_mask; + const char *dev_id; + const char *con_id; + struct clk *clk; + + unsigned long max_freq; + unsigned long target_freq; + unsigned long cur_freq; + + unsigned long avg_actv_freq; + unsigned long avg_band_freq; + unsigned int avg_sustain_coef; + u32 avg_count; + + unsigned long boost_freq; + unsigned long boost_freq_step; + unsigned int boost_up_coef; + unsigned int boost_down_coef; + unsigned int boost_up_threshold; + unsigned int boost_down_threshold; + + u8 up_wmark_window; + u8 down_wmark_window; + u8 avg_window_log2; + u32 count_weight; + + enum actmon_type type; + enum actmon_state state; + enum actmon_state saved_state; + + spinlock_t lock; + + struct notifier_block rate_change_nb; +}; + +static void __iomem *actmon_base = IO_ADDRESS(TEGRA_ACTMON_BASE); + +static inline u32 actmon_readl(u32 offset) +{ + return __raw_readl((u32)actmon_base + offset); +} +static inline void actmon_writel(u32 val, u32 offset) +{ + __raw_writel(val, (u32)actmon_base + offset); +} +static inline void actmon_wmb(void) +{ + wmb(); + actmon_readl(ACTMON_GLB_STATUS); +} + +#define offs(x) (dev->reg + x) + +static inline unsigned long do_percent(unsigned long val, unsigned int pct) +{ + return val * pct / 100; +} + +static inline void actmon_dev_up_wmark_set(struct actmon_dev *dev) +{ + u32 val; + unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? + dev->cur_freq : actmon_clk_freq; + + val = freq * actmon_sampling_period; + actmon_writel(do_percent(val, dev->boost_up_threshold), + offs(ACTMON_DEV_UP_WMARK)); +} + +static inline void actmon_dev_down_wmark_set(struct actmon_dev *dev) +{ + u32 val; + unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? + dev->cur_freq : actmon_clk_freq; + + val = freq * actmon_sampling_period; + actmon_writel(do_percent(val, dev->boost_down_threshold), + offs(ACTMON_DEV_DOWN_WMARK)); +} + +static inline void actmon_dev_wmark_set(struct actmon_dev *dev) +{ + u32 val; + unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? + dev->cur_freq : actmon_clk_freq; + + val = freq * actmon_sampling_period; + actmon_writel(do_percent(val, dev->boost_up_threshold), + offs(ACTMON_DEV_UP_WMARK)); + actmon_writel(do_percent(val, dev->boost_down_threshold), + offs(ACTMON_DEV_DOWN_WMARK)); +} + +static inline void actmon_dev_avg_wmark_set(struct actmon_dev *dev) +{ + u32 avg = dev->avg_count; + u32 band = dev->avg_band_freq * actmon_sampling_period; + + actmon_writel(avg + band, offs(ACTMON_DEV_AVG_UP_WMARK)); + avg = max(avg, band); + actmon_writel(avg - band, offs(ACTMON_DEV_AVG_DOWN_WMARK)); +} + +/* Activity monitor sampling operations */ +irqreturn_t actmon_dev_isr(int irq, void *dev_id) +{ + u32 val; + unsigned long flags; + struct actmon_dev *dev = (struct actmon_dev *)dev_id; + + val = actmon_readl(ACTMON_GLB_STATUS) & dev->glb_status_irq_mask; + if (!val) + return IRQ_NONE; + + spin_lock_irqsave(&dev->lock, flags); + + dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); + actmon_dev_avg_wmark_set(dev); + + val = actmon_readl(offs(ACTMON_DEV_INTR_STATUS)); + if (val & ACTMON_DEV_INTR_UP_WMARK) { + val = actmon_readl(offs(ACTMON_DEV_CTRL)) | + ACTMON_DEV_CTRL_UP_WMARK_ENB | + ACTMON_DEV_CTRL_DOWN_WMARK_ENB; + + dev->boost_freq = dev->boost_freq_step + + do_percent(dev->boost_freq, dev->boost_up_coef); + if (dev->boost_freq >= dev->max_freq) { + dev->boost_freq = dev->max_freq; + val &= ~ACTMON_DEV_CTRL_UP_WMARK_ENB; + } + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + } else if (val & ACTMON_DEV_INTR_DOWN_WMARK) { + val = actmon_readl(offs(ACTMON_DEV_CTRL)) | + ACTMON_DEV_CTRL_UP_WMARK_ENB | + ACTMON_DEV_CTRL_DOWN_WMARK_ENB; + + dev->boost_freq = + do_percent(dev->boost_freq, dev->boost_down_coef); + if (dev->boost_freq < (dev->boost_freq_step >> 1)) { + dev->boost_freq = 0; + val &= ~ACTMON_DEV_CTRL_DOWN_WMARK_ENB; + } + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + } + + actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */ + actmon_wmb(); + + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_WAKE_THREAD; +} + +irqreturn_t actmon_dev_fn(int irq, void *dev_id) +{ + unsigned long flags; + unsigned long freq = 0; + 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; + } + spin_unlock_irqrestore(&dev->lock, flags); + + if (freq) { + 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); + } + return IRQ_HANDLED; +} + +static int actmon_rate_notify_cb( + struct notifier_block *nb, unsigned long rate, void *v) +{ + unsigned long flags; + struct actmon_dev *dev = container_of( + nb, struct actmon_dev, rate_change_nb); + + spin_lock_irqsave(&dev->lock, flags); + + dev->cur_freq = rate / 1000; + if (dev->type == ACTMON_FREQ_SAMPLER) { + actmon_dev_wmark_set(dev); + actmon_wmb(); + } + + spin_unlock_irqrestore(&dev->lock, flags); + return NOTIFY_OK; +}; + +/* Activity monitor configuration and control */ +static void actmon_dev_configure(struct actmon_dev *dev, unsigned long freq) +{ + u32 val; + + dev->cur_freq = 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; + actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG)); + + BUG_ON(!dev->boost_up_threshold); + dev->avg_sustain_coef = 100 * 100 / dev->boost_up_threshold; + actmon_dev_avg_wmark_set(dev); + actmon_dev_wmark_set(dev); + + actmon_writel(dev->count_weight, offs(ACTMON_DEV_COUNT_WEGHT)); + actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */ + + val = ACTMON_DEV_CTRL_PERIODIC_ENB | ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | + ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB; + val |= ((dev->avg_window_log2 - 1) << ACTMON_DEV_CTRL_K_VAL_SHIFT) & + ACTMON_DEV_CTRL_K_VAL_MASK; + 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) << + 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; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_wmb(); +} + +static void actmon_dev_enable(struct actmon_dev *dev) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->state == ACTMON_OFF) { + dev->state = ACTMON_ON; + + val = actmon_readl(offs(ACTMON_DEV_CTRL)); + val |= ACTMON_DEV_CTRL_ENB; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_wmb(); + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void actmon_dev_disable(struct actmon_dev *dev) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->state == ACTMON_ON) { + dev->state = ACTMON_OFF; + + val = actmon_readl(offs(ACTMON_DEV_CTRL)); + val &= ~ACTMON_DEV_CTRL_ENB; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); + actmon_wmb(); + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void actmon_dev_suspend(struct actmon_dev *dev) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if ((dev->state == ACTMON_ON) || (dev->state == ACTMON_OFF)){ + dev->saved_state = dev->state; + dev->state = ACTMON_SUSPENDED; + + val = actmon_readl(offs(ACTMON_DEV_CTRL)); + val &= ~ACTMON_DEV_CTRL_ENB; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); + actmon_wmb(); + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void actmon_dev_resume(struct actmon_dev *dev) +{ + u32 val; + unsigned long flags, freq; + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->state == ACTMON_SUSPENDED) { + freq = clk_get_rate(dev->clk) / 1000; + actmon_dev_configure(dev, freq); + dev->state = dev->saved_state; + if (dev->state == ACTMON_ON) { + val = actmon_readl(offs(ACTMON_DEV_CTRL)); + val |= ACTMON_DEV_CTRL_ENB; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_wmb(); + } + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static int __init actmon_dev_init(struct actmon_dev *dev) +{ + int ret; + struct clk *p; + unsigned long freq; + + spin_lock_init(&dev->lock); + + dev->clk = clk_get_sys(dev->dev_id, dev->con_id); + if (IS_ERR(dev->clk)) { + pr_err("Failed to find %s.%s clock\n", + dev->dev_id, dev->con_id); + return -ENODEV; + } + dev->max_freq = clk_get_max_rate(dev->clk) / 1000; + freq = clk_get_rate(dev->clk) / 1000; + actmon_dev_configure(dev, freq); + + /* actmon device controls shared bus user clock, but rate + change notification should come from bus clock itself */ + p = clk_get_parent(dev->clk); + BUG_ON(!p); + + if (dev->rate_change_nb.notifier_call) { + ret = tegra_register_clk_rate_notifier(p, &dev->rate_change_nb); + if (ret) { + pr_err("Failed to register %s rate change notifier" + " for %s\n", p->name, dev->dev_id); + return ret; + } + } + + ret = request_threaded_irq(INT_ACTMON, actmon_dev_isr, actmon_dev_fn, + IRQF_SHARED, dev->dev_id, dev); + if (ret) { + pr_err("Failed irq %d request for %s.%s\n", + INT_ACTMON, dev->dev_id, dev->con_id); + tegra_unregister_clk_rate_notifier(p, &dev->rate_change_nb); + return ret; + } + + dev->state = ACTMON_OFF; + actmon_dev_enable(dev); + clk_enable(dev->clk); + return 0; +} + +/* EMC activity monitor: frequency sampling device */ +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, + .boost_up_threshold = 60, + .boost_down_threshold = 40, + + .up_wmark_window = 1, + .down_wmark_window = 3, + .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2, + .count_weight = 0x200, + + .type = ACTMON_FREQ_SAMPLER, + .state = ACTMON_UNINITIALIZED, + + .rate_change_nb = { + .notifier_call = actmon_rate_notify_cb, + }, +}; + +static struct actmon_dev *actmon_devices[] = { + &actmon_dev_emc, +}; + +/* Activity monitor suspend/resume */ +static int actmon_pm_notify(struct notifier_block *nb, + unsigned long event, void *data) +{ + int i; + + switch (event) { + case PM_SUSPEND_PREPARE: + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) + actmon_dev_suspend(actmon_devices[i]); + break; + case PM_POST_SUSPEND: + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) + actmon_dev_resume(actmon_devices[i]); + break; + } + + return NOTIFY_OK; +}; + +static struct notifier_block actmon_pm_nb = { + .notifier_call = actmon_pm_notify, +}; + +#ifdef CONFIG_DEBUG_FS + +#define RW_MODE (S_IWUSR | S_IRUGO) +#define RO_MODE S_IRUGO + +static struct dentry *clk_debugfs_root; + +static int type_show(struct seq_file *s, void *data) +{ + struct actmon_dev *dev = s->private; + + seq_printf(s, "%s\n", (dev->type == ACTMON_LOAD_SAMPLER) ? + "Load Activity Monitor" : "Frequency Activity Monitor"); + return 0; +} +static int type_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_show, inode->i_private); +} +static const struct file_operations type_fops = { + .open = type_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int step_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->boost_freq_step * 100 / dev->max_freq; + return 0; +} +static int step_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + + if (val > 100) + val = 100; + + spin_lock_irqsave(&dev->lock, flags); + dev->boost_freq_step = do_percent(dev->max_freq, (unsigned int)val); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(step_fops, step_get, step_set, "%llu\n"); + +static int up_threshold_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->boost_up_threshold; + return 0; +} +static int up_threshold_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + unsigned int up_threshold = (unsigned int)val; + + if (up_threshold > 100) + up_threshold = 100; + + spin_lock_irqsave(&dev->lock, flags); + + if (up_threshold <= dev->boost_down_threshold) + up_threshold = dev->boost_down_threshold; + if (up_threshold) + dev->avg_sustain_coef = 100 * 100 / up_threshold; + dev->boost_up_threshold = up_threshold; + + actmon_dev_up_wmark_set(dev); + actmon_wmb(); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(up_threshold_fops, up_threshold_get, + up_threshold_set, "%llu\n"); + +static int down_threshold_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->boost_down_threshold; + return 0; +} +static int down_threshold_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + unsigned int down_threshold = (unsigned int)val; + + if (down_threshold < 0) + down_threshold = 0; + + spin_lock_irqsave(&dev->lock, flags); + + if (down_threshold >= dev->boost_up_threshold) + down_threshold = dev->boost_up_threshold; + dev->boost_down_threshold = down_threshold; + + actmon_dev_down_wmark_set(dev); + actmon_wmb(); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(down_threshold_fops, down_threshold_get, + down_threshold_set, "%llu\n"); + +static int state_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->state; + return 0; +} +static int state_set(void *data, u64 val) +{ + struct actmon_dev *dev = data; + + if (val) + actmon_dev_enable(dev); + else + actmon_dev_disable(dev); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(state_fops, state_get, state_set, "%llu\n"); + +static int period_get(void *data, u64 *val) +{ + *val = actmon_sampling_period; + return 0; +} +static int period_set(void *data, u64 val) +{ + 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 */ + return 0; + } + return -EINVAL; +} +DEFINE_SIMPLE_ATTRIBUTE(period_fops, period_get, period_set, "%llu\n"); + + +static int actmon_debugfs_create_dev(struct actmon_dev *dev) +{ + struct dentry *dir, *d; + + if (dev->state == ACTMON_UNINITIALIZED) + return 0; + + dir = debugfs_create_dir(dev->con_id, clk_debugfs_root); + if (!dir) + return -ENOMEM; + + d = debugfs_create_file( + "actv_type", RO_MODE, dir, dev, &type_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_u32( + "avg_activity", RO_MODE, dir, (u32 *)&dev->avg_actv_freq); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "boost_step", RW_MODE, dir, dev, &step_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_u32( + "boost_rate_dec", RW_MODE, dir, (u32 *)&dev->boost_down_coef); + if (!d) + return -ENOMEM; + + d = debugfs_create_u32( + "boost_rate_inc", RW_MODE, dir, (u32 *)&dev->boost_up_coef); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "boost_threshold_dn", RW_MODE, dir, dev, &down_threshold_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "boost_threshold_up", RW_MODE, dir, dev, &up_threshold_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "state", RW_MODE, dir, dev, &state_fops); + if (!d) + return -ENOMEM; + + return 0; +} + +static int __init actmon_debugfs_init(void) +{ + int i; + int ret = -ENOMEM; + struct dentry *d; + + d = debugfs_create_dir("tegra_actmon", NULL); + if (!d) + return ret; + clk_debugfs_root = d; + + d = debugfs_create_file("period", RW_MODE, d, NULL, &period_fops); + if (!d) + goto err_out; + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + ret = actmon_debugfs_create_dev(actmon_devices[i]); + if (ret) + goto err_out; + } + return 0; + +err_out: + debugfs_remove_recursive(clk_debugfs_root); + return ret; +} + +#endif + +static int __init tegra_actmon_init(void) +{ + int i, ret; + struct clk *c = tegra_get_clock_by_name("actmon"); + + if (!c) { + pr_err("%s: Failed to find actmon clock\n", __func__); + return 0; + } + actmon_clk_freq = clk_get_rate(c) / 1000; + ret = clk_enable(c); + if (ret) { + pr_err("%s: Failed to enable actmon clock\n", __func__); + return 0; + } + actmon_sampling_period = ACTMON_DEFAULT_SAMPLING_PERIOD; + actmon_writel(actmon_sampling_period - 1, ACTMON_GLB_PERIOD_CTRL); + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + ret = actmon_dev_init(actmon_devices[i]); + pr_info("%s.%s: %s initialization (%d)\n", + actmon_devices[i]->dev_id, actmon_devices[i]->con_id, + ret ? "Failed" : "Completed", ret); + } + register_pm_notifier(&actmon_pm_nb); + +#ifdef CONFIG_DEBUG_FS + actmon_debugfs_init(); +#endif + return 0; +} +late_initcall(tegra_actmon_init); |