/* * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include "ape_actmon.h" #include "dev.h" #define ACTMON_DEV_CTRL 0x00 #define ACTMON_DEV_CTRL_ENB (0x1 << 31) #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 21 #define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK (0x7 << 21) #define ACTMON_DEV_CTRL_UP_WMARK_ENB (0x1 << 19) #define ACTMON_DEV_CTRL_DOWN_WMARK_ENB (0x1 << 18) #define ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB (0x1 << 17) #define ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB (0x1 << 16) #define ACTMON_DEV_CTRL_AT_END_ENB (0x1 << 15) #define ACTMON_DEV_CTRL_PERIODIC_ENB (0x1 << 13) #define ACTMON_DEV_CTRL_K_VAL_SHIFT 10 #define ACTMON_DEV_CTRL_K_VAL_MASK (0x7 << 10) #define ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT (0) #define ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK (0xff << 0) #define ACTMON_DEV_UP_WMARK 0x04 #define ACTMON_DEV_DOWN_WMARK 0x08 #define ACTMON_DEV_AVG_UP_WMARK 0x0c #define ACTMON_DEV_AVG_DOWN_WMARK 0x10 #define ACTMON_DEV_INIT_AVG 0x14 #define ACTMON_DEV_COUNT 0x18 #define ACTMON_DEV_AVG_COUNT 0x1c #define ACTMON_DEV_INTR_STATUS 0x20 #define ACTMON_DEV_INTR_UP_WMARK (0x1 << 31) #define ACTMON_DEV_INTR_DOWN_WMARK (0x1 << 30) #define ACTMON_DEV_INTR_AVG_DOWN_WMARK (0x1 << 29) #define ACTMON_DEV_INTR_AVG_UP_WMARK (0x1 << 28) #define ACTMON_DEV_COUNT_WEGHT 0x24 #define ACTMON_DEV_SAMPLE_CTRL 0x28 #define ACTMON_DEV_SAMPLE_CTRL_TICK_65536 (0x1 << 2) #define ACTMON_DEV_SAMPLE_CTRL_TICK_256 (0x0 << 1) #define AMISC_ACTMON_0 0x54 #define AMISC_ACTMON_CNT_TARGET_ENABLE (0x1 << 31) #define ACTMON_DEFAULT_AVG_WINDOW_LOG2 7 /* 1/10 of % i.e 60 % of max freq */ #define ACTMON_DEFAULT_AVG_BAND 6 #define ACTMON_MAX_REG_OFFSET 0x2c /* TBD: These would come via dts file */ #define ACTMON_REG_OFFSET 0x800 /* milli second divider as SAMPLE_TICK*/ #define SAMPLE_MS_DIVIDER 65536 /* Sample period in ms */ #define ACTMON_DEFAULT_SAMPLING_PERIOD 20 #define AVG_COUNT_THRESHOLD 100000 static struct actmon ape_actmon; static struct actmon *apemon; /* APE activity monitor: Samples ADSP activity */ static struct actmon_dev actmon_dev_adsp = { .reg = 0x000, .clk_name = "adsp_cpu", /* ADSP suspend activity floor */ .suspend_freq = 51200, /* min step by which we want to boost in case of sudden boost request */ .boost_freq_step = 51200, /* % of boost freq for boosting up */ .boost_up_coef = 200, /* * % of boost freq for boosting down. Should be boosted down by * exponential down */ .boost_down_coef = 80, /* * % of device freq collected in a sample period set as boost up * threshold. boost interrupt is generated when actmon_count * (absolute actmon count in a sample period) * crosses this threshold consecutively by up_wmark_window. */ .boost_up_threshold = 95, /* * % of device freq collected in a sample period set as boost down * threshold. boost interrupt is generated when actmon_count(raw_count) * crosses this threshold consecutively by down_wmark_window. */ .boost_down_threshold = 80, /* * No of times raw counts hits the up_threshold to generate an * interrupt */ .up_wmark_window = 4, /* * No of times raw counts hits the down_threshold to generate an * interrupt. */ .down_wmark_window = 8, /* * No of samples = 2^ avg_window_log2 for calculating exponential moving * average. */ .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2, /* * "weight" is used to scale the count to match the device freq * When 256 adsp active cpu clock are generated, actmon count * is increamented by 1. Making weight as 256 ensures that 1 adsp active * clk increaments actmon_count by 1. * This makes actmon_count exactly reflect active adsp cpu clk * cycles. */ .count_weight = 0x100, /* * FREQ_SAMPLER: samples number of device(adsp) active cycles * weighted by count_weight to reflect * actmon_count within a * sample period. * LOAD_SAMPLER: samples actmon active cycles weighted by * count_weight to reflect actmon_count within a sample period. */ .type = ACTMON_FREQ_SAMPLER, .state = ACTMON_UNINITIALIZED, }; static struct actmon_dev *actmon_devices[] = { &actmon_dev_adsp, }; static inline u32 actmon_readl(u32 offset) { return __raw_readl(apemon->base + offset); } static inline void actmon_writel(u32 val, u32 offset) { __raw_writel(val, apemon->base + offset); } static inline void actmon_wmb(void) { wmb(); } #define offs(x) (dev->reg + x) static inline unsigned long do_percent(unsigned long val, unsigned int pct) { return val * pct / 100; } static void actmon_update_sample_period(unsigned long period) { u32 sample_period_in_clks; u32 val = 0; apemon->sampling_period = period; /* * sample_period_in_clks <1..255> = (actmon_clk_freq<1..40800> * * actmon_sample_period <10ms..40ms>) / SAMPLE_MS_DIVIDER(65536) */ sample_period_in_clks = (apemon->freq * apemon->sampling_period) / SAMPLE_MS_DIVIDER; val = actmon_readl(ACTMON_DEV_CTRL); val &= ~ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; val |= (sample_period_in_clks << ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT) & ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; actmon_writel(val, ACTMON_DEV_CTRL); } 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 : apemon->freq; val = freq * apemon->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 : apemon->freq; val = freq * apemon->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 : apemon->freq; val = freq * apemon->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) { /* * band: delta from current count to be set for avg upper * and lower thresholds */ u32 band = dev->avg_band_freq * apemon->sampling_period; u32 avg = dev->avg_count; actmon_writel(avg + band, offs(ACTMON_DEV_AVG_UP_WMARK)); avg = max(avg, band); 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 / apemon->sampling_period; val = (u64) dev->avg_count * dev->cur_freq; do_div(val , apemon->freq * apemon->sampling_period); return (u32)val; } /* Activity monitor sampling operations */ static irqreturn_t ape_actmon_dev_isr(int irq, void *dev_id) { u32 val, devval; unsigned long flags; struct actmon_dev *dev = (struct actmon_dev *)dev_id; spin_lock_irqsave(&dev->lock, flags); val = actmon_readl(offs(ACTMON_DEV_INTR_STATUS)); actmon_writel(val, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */ devval = actmon_readl(offs(ACTMON_DEV_CTRL)); if (val & ACTMON_DEV_INTR_AVG_UP_WMARK) { devval |= (ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB); dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); actmon_dev_avg_wmark_set(dev); } else if (val & ACTMON_DEV_INTR_AVG_DOWN_WMARK) { devval |= (ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB); dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); actmon_dev_avg_wmark_set(dev); } if (val & ACTMON_DEV_INTR_UP_WMARK) { devval |= (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; devval &= ~ACTMON_DEV_CTRL_UP_WMARK_ENB; } } else if (val & ACTMON_DEV_INTR_DOWN_WMARK) { devval |= (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 == 0) { devval &= ~ACTMON_DEV_CTRL_DOWN_WMARK_ENB; } } actmon_writel(devval, offs(ACTMON_DEV_CTRL)); actmon_wmb(); spin_unlock_irqrestore(&dev->lock, flags); return IRQ_WAKE_THREAD; } static irqreturn_t ape_actmon_dev_fn(int irq, void *dev_id) { unsigned long flags, freq; struct actmon_dev *dev = (struct actmon_dev *)dev_id; spin_lock_irqsave(&dev->lock, flags); 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; /* in kHz */ freq = do_percent(freq, dev->avg_sustain_coef); freq += dev->boost_freq; dev->target_freq = freq; spin_unlock_irqrestore(&dev->lock, flags); dev_dbg(dev->device, "%s(kHz): avg: %lu, boost: %lu, target: %lu, current: %lu\n", dev->clk_name, dev->avg_actv_freq, dev->boost_freq, dev->target_freq, dev->cur_freq); #if defined(CONFIG_TEGRA_ADSP_DFS) adsp_cpu_set_rate(freq); #endif return IRQ_HANDLED; } /* Activity monitor configuration and control */ static void actmon_dev_configure(struct actmon_dev *dev, unsigned long freq) { u32 val; dev->boost_freq = 0; dev->cur_freq = freq; dev->target_freq = freq; dev->avg_actv_freq = freq; if (dev->type == ACTMON_FREQ_SAMPLER) { /* * max actmon count = (count_weight * adsp_freq (khz) * sample_period (ms)) / (PULSE_N_CLK+1) * As Count_weight is set as 256(0x100) and * (PULSE_N_CLK+1) = 256. both would be * compensated while coming up max_actmon_count. * in other word * max actmon count = ((count_weight * adsp_freq * * sample_period_reg * SAMPLE_TICK) * / (ape_freq * (PULSE_N_CLK+1))) * where - * sample_period_reg : <1..255> sample period in no of * actmon clocks per sample * SAMPLE_TICK : Arbtrary value for ms - 65536, us - 256 * (PULSE_N_CLK + 1) : 256 - No of adsp "active" clocks to * increament raw_count/ actmon_count * by one. */ dev->avg_count = dev->cur_freq * apemon->sampling_period; dev->avg_band_freq = dev->max_freq * ACTMON_DEFAULT_AVG_BAND / 1000; } else { dev->avg_count = apemon->freq * apemon->sampling_period; dev->avg_band_freq = apemon->freq * ACTMON_DEFAULT_AVG_BAND / 1000; } 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)); val = actmon_readl(ACTMON_DEV_CTRL); 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 int actmon_dev_probe(struct actmon_dev *dev) { struct nvadsp_drv_data *drv_data = dev_get_drvdata(dev->device); int ret; dev->irq = drv_data->agic_irqs[ACTMON_VIRQ]; ret = request_threaded_irq(dev->irq, ape_actmon_dev_isr, ape_actmon_dev_fn, IRQ_TYPE_LEVEL_HIGH, dev->clk_name, dev); if (ret) { dev_err(dev->device, "Failed irq %d request for %s\n", dev->irq, dev->clk_name); goto end; } disable_irq(dev->irq); end: return ret; } static int actmon_dev_init(struct actmon_dev *dev) { int ret = -EINVAL; unsigned long freq; spin_lock_init(&dev->lock); dev->clk = clk_get_sys(NULL, dev->clk_name); if (IS_ERR_OR_NULL(dev->clk)) { dev_err(dev->device, "Failed to find %s clock\n", dev->clk_name); goto end; } ret = clk_prepare_enable(dev->clk); if (ret) { dev_err(dev->device, "unable to enable %s clock\n", dev->clk_name); goto err_enable; } dev->max_freq = freq = clk_get_rate(dev->clk) / 1000; actmon_dev_configure(dev, freq); dev->state = ACTMON_OFF; actmon_dev_enable(dev); enable_irq(dev->irq); return 0; err_enable: clk_put(dev->clk); end: return ret; } #ifdef CONFIG_DEBUG_FS #define RW_MODE (S_IWUSR | S_IRUSR) #define RO_MODE S_IRUSR 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 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; *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 count_weight_get(void *data, u64 *val) { struct actmon_dev *dev = data; *val = dev->count_weight; return 0; } static int count_weight_set(void *data, u64 val) { unsigned long flags; struct actmon_dev *dev = data; spin_lock_irqsave(&dev->lock, flags); dev->count_weight = (u32) val; actmon_writel(dev->count_weight, offs(ACTMON_DEV_COUNT_WEGHT)); spin_unlock_irqrestore(&dev->lock, flags); return 0; } DEFINE_SIMPLE_ATTRIBUTE(cnt_wt_fops, count_weight_get, count_weight_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; 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"); /* Get period in msec */ static int period_get(void *data, u64 *val) { *val = apemon->sampling_period; return 0; } /* Set period in msec */ static int period_set(void *data, u64 val) { int i; unsigned long flags; u8 period = (u8)val; if (period) { actmon_update_sample_period(period); 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; } 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->clk_name, 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_file( "avg_activity", RO_MODE, dir, dev, &actv_fops); 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; d = debugfs_create_file( "cnt_wt", RW_MODE, dir, dev, &cnt_wt_fops); if (!d) return -ENOMEM; return 0; } static int actmon_debugfs_init(struct nvadsp_drv_data *drv) { int i; int ret = -ENOMEM; struct dentry *d; if (!drv->adsp_debugfs_root) return ret; d = debugfs_create_dir("adsp_actmon", drv->adsp_debugfs_root); 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 /* freq in KHz */ void actmon_rate_change(unsigned long freq, bool override) { struct actmon_dev *dev = &actmon_dev_adsp; unsigned long flags; if (override) { actmon_dev_disable(dev); spin_lock_irqsave(&dev->lock, flags); dev->cur_freq = freq; dev->avg_count = freq * apemon->sampling_period; actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG)); actmon_dev_avg_wmark_set(dev); actmon_dev_wmark_set(dev); actmon_wmb(); spin_unlock_irqrestore(&dev->lock, flags); actmon_dev_enable(dev); } else { spin_lock_irqsave(&dev->lock, flags); dev->cur_freq = freq; if (dev->state == ACTMON_ON) { actmon_dev_wmark_set(dev); actmon_wmb(); } spin_unlock_irqrestore(&dev->lock, flags); } /* change ape rate as half of adsp rate */ clk_set_rate(apemon->clk, freq * 500); }; int ape_actmon_probe(struct platform_device *pdev) { int ret = 0; int i; for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { actmon_devices[i]->device = &pdev->dev; ret = actmon_dev_probe(actmon_devices[i]); dev_dbg(&pdev->dev, "%s actmon: %s probe (%d)\n", actmon_devices[i]->clk_name, ret ? "Failed" : "Completed", ret); } return ret; } static int ape_actmon_rc_cb( struct notifier_block *nb, unsigned long rate, void *v) { struct actmon_dev *dev = &actmon_dev_adsp; unsigned long flags; u32 init_cnt; if (dev->state != ACTMON_ON) { dev_dbg(dev->device, "adsp actmon is not ON\n"); goto exit_out; } actmon_dev_disable(dev); spin_lock_irqsave(&dev->lock, flags); init_cnt = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); /* update sample period to maintain number of clock */ apemon->freq = rate / 1000; /* in KHz */ actmon_update_sample_period(ACTMON_DEFAULT_SAMPLING_PERIOD); actmon_writel(init_cnt, offs(ACTMON_DEV_INIT_AVG)); spin_unlock_irqrestore(&dev->lock, flags); actmon_dev_enable(dev); exit_out: return NOTIFY_OK; } int ape_actmon_init(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); static void __iomem *amisc_base; u32 sample_period_in_clks; struct clk *p; u32 val = 0; int i, ret; if (drv->actmon_initialized) return 0; apemon = &ape_actmon; apemon->base = drv->base_regs[AMISC] + ACTMON_REG_OFFSET; amisc_base = drv->base_regs[AMISC]; apemon->clk = clk_get_sys(NULL, "adsp.ape"); if (!apemon->clk) { dev_err(&pdev->dev, "Failed to find actmon clock\n"); ret = -EINVAL; goto err_out; } ret = clk_prepare_enable(apemon->clk); if (ret) { dev_err(&pdev->dev, "Failed to enable actmon clock\n"); ret = -EINVAL; goto err_out; } apemon->clk_rc_nb.notifier_call = ape_actmon_rc_cb; /* * "adsp.ape" clk is shared bus user clock and "ape" is bus clock * but rate change notification should come from bus clock itself. */ p = clk_get_parent(apemon->clk); if (!p) { dev_err(&pdev->dev, "Failed to find actmon parent clock\n"); ret = -EINVAL; goto clk_err_out; } ret = tegra_register_clk_rate_notifier(p, &apemon->clk_rc_nb); if (ret) { dev_err(&pdev->dev, "Registration fail: %s rate change notifier for %s\n", p->name, apemon->clk->name); goto clk_err_out; } apemon->freq = clk_get_rate(apemon->clk) / 1000; /* in KHz */ apemon->sampling_period = ACTMON_DEFAULT_SAMPLING_PERIOD; /* * sample period as no of actmon clocks * Actmon is derived from APE clk. * suppose APE clk is 204MHz = 204000 KHz and want to calculate * clocks in 10ms sample * in 1ms = 204000 cycles * 10ms = 204000 * 10 APE cycles * SAMPLE_MS_DIVIDER is an arbitrary number */ sample_period_in_clks = (apemon->freq * apemon->sampling_period) / SAMPLE_MS_DIVIDER; /* set ms mode */ actmon_writel(ACTMON_DEV_SAMPLE_CTRL_TICK_65536, ACTMON_DEV_SAMPLE_CTRL); val = actmon_readl(ACTMON_DEV_CTRL); val &= ~ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; val |= (sample_period_in_clks << ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT) & ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; actmon_writel(val, ACTMON_DEV_CTRL); /* Enable AMISC_ACTMON */ val = __raw_readl(amisc_base + AMISC_ACTMON_0); val |= AMISC_ACTMON_CNT_TARGET_ENABLE; __raw_writel(val, amisc_base + AMISC_ACTMON_0); actmon_writel(0xffffffff, ACTMON_DEV_INTR_STATUS); /* clr all */ for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { ret = actmon_dev_init(actmon_devices[i]); dev_dbg(&pdev->dev, "%s actmon device: %s initialization (%d)\n", actmon_devices[i]->clk_name, ret ? "Failed" : "Completed", ret); } #ifdef CONFIG_DEBUG_FS actmon_debugfs_init(drv); #endif drv->actmon_initialized = true; dev_dbg(&pdev->dev, "adsp actmon initialized ....\n"); return 0; clk_err_out: if (apemon->clk) clk_disable_unprepare(apemon->clk); err_out: if (apemon->clk) clk_put(apemon->clk); return ret; } int ape_actmon_exit(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); struct actmon_dev *dev; status_t ret = 0; int i; /* return if actmon is not initialized */ if (!drv->actmon_initialized) return -ENODEV; for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { dev = actmon_devices[i]; actmon_dev_disable(dev); disable_irq(dev->irq); clk_disable_unprepare(dev->clk); clk_put(dev->clk); } tegra_unregister_clk_rate_notifier(clk_get_parent(apemon->clk), &apemon->clk_rc_nb); clk_disable_unprepare(apemon->clk); clk_put(apemon->clk); drv->actmon_initialized = false; dev_dbg(&pdev->dev, "adsp actmon has exited ....\n"); return ret; }