123601752SDmitry Osipenko // SPDX-License-Identifier: GPL-2.0-only 223601752SDmitry Osipenko /* 323601752SDmitry Osipenko * A devfreq driver for NVIDIA Tegra SoCs 423601752SDmitry Osipenko * 523601752SDmitry Osipenko * Copyright (c) 2014 NVIDIA CORPORATION. All rights reserved. 623601752SDmitry Osipenko * Copyright (C) 2014 Google, Inc 723601752SDmitry Osipenko */ 823601752SDmitry Osipenko 923601752SDmitry Osipenko #include <linux/clk.h> 1023601752SDmitry Osipenko #include <linux/cpufreq.h> 1123601752SDmitry Osipenko #include <linux/devfreq.h> 1223601752SDmitry Osipenko #include <linux/interrupt.h> 1323601752SDmitry Osipenko #include <linux/io.h> 14d49eeb1eSDmitry Osipenko #include <linux/irq.h> 1523601752SDmitry Osipenko #include <linux/module.h> 1623601752SDmitry Osipenko #include <linux/mod_devicetable.h> 1723601752SDmitry Osipenko #include <linux/platform_device.h> 1823601752SDmitry Osipenko #include <linux/pm_opp.h> 1923601752SDmitry Osipenko #include <linux/reset.h> 2023601752SDmitry Osipenko 2123601752SDmitry Osipenko #include "governor.h" 2223601752SDmitry Osipenko 2323601752SDmitry Osipenko #define ACTMON_GLB_STATUS 0x0 2423601752SDmitry Osipenko #define ACTMON_GLB_PERIOD_CTRL 0x4 2523601752SDmitry Osipenko 2623601752SDmitry Osipenko #define ACTMON_DEV_CTRL 0x0 2723601752SDmitry Osipenko #define ACTMON_DEV_CTRL_K_VAL_SHIFT 10 2823601752SDmitry Osipenko #define ACTMON_DEV_CTRL_ENB_PERIODIC BIT(18) 2923601752SDmitry Osipenko #define ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN BIT(20) 3023601752SDmitry Osipenko #define ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN BIT(21) 3123601752SDmitry Osipenko #define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT 23 3223601752SDmitry Osipenko #define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT 26 3323601752SDmitry Osipenko #define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN BIT(29) 3423601752SDmitry Osipenko #define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN BIT(30) 3523601752SDmitry Osipenko #define ACTMON_DEV_CTRL_ENB BIT(31) 3623601752SDmitry Osipenko 3723601752SDmitry Osipenko #define ACTMON_DEV_UPPER_WMARK 0x4 3823601752SDmitry Osipenko #define ACTMON_DEV_LOWER_WMARK 0x8 3923601752SDmitry Osipenko #define ACTMON_DEV_INIT_AVG 0xc 4023601752SDmitry Osipenko #define ACTMON_DEV_AVG_UPPER_WMARK 0x10 4123601752SDmitry Osipenko #define ACTMON_DEV_AVG_LOWER_WMARK 0x14 4223601752SDmitry Osipenko #define ACTMON_DEV_COUNT_WEIGHT 0x18 4323601752SDmitry Osipenko #define ACTMON_DEV_AVG_COUNT 0x20 4423601752SDmitry Osipenko #define ACTMON_DEV_INTR_STATUS 0x24 4523601752SDmitry Osipenko 4623601752SDmitry Osipenko #define ACTMON_INTR_STATUS_CLEAR 0xffffffff 4723601752SDmitry Osipenko 4823601752SDmitry Osipenko #define ACTMON_DEV_INTR_CONSECUTIVE_UPPER BIT(31) 4923601752SDmitry Osipenko #define ACTMON_DEV_INTR_CONSECUTIVE_LOWER BIT(30) 5023601752SDmitry Osipenko 5123601752SDmitry Osipenko #define ACTMON_ABOVE_WMARK_WINDOW 1 5223601752SDmitry Osipenko #define ACTMON_BELOW_WMARK_WINDOW 3 5323601752SDmitry Osipenko #define ACTMON_BOOST_FREQ_STEP 16000 5423601752SDmitry Osipenko 5523601752SDmitry Osipenko /* 5623601752SDmitry Osipenko * Activity counter is incremented every 256 memory transactions, and each 5723601752SDmitry Osipenko * transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is 5823601752SDmitry Osipenko * 4 * 256 = 1024. 5923601752SDmitry Osipenko */ 6023601752SDmitry Osipenko #define ACTMON_COUNT_WEIGHT 0x400 6123601752SDmitry Osipenko 6223601752SDmitry Osipenko /* 6323601752SDmitry Osipenko * ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which 6423601752SDmitry Osipenko * translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128 6523601752SDmitry Osipenko */ 6623601752SDmitry Osipenko #define ACTMON_AVERAGE_WINDOW_LOG2 6 6723601752SDmitry Osipenko #define ACTMON_SAMPLING_PERIOD 12 /* ms */ 6823601752SDmitry Osipenko #define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */ 6923601752SDmitry Osipenko 7023601752SDmitry Osipenko #define KHZ 1000 7123601752SDmitry Osipenko 7223601752SDmitry Osipenko /* Assume that the bus is saturated if the utilization is 25% */ 7323601752SDmitry Osipenko #define BUS_SATURATION_RATIO 25 7423601752SDmitry Osipenko 7523601752SDmitry Osipenko /** 7623601752SDmitry Osipenko * struct tegra_devfreq_device_config - configuration specific to an ACTMON 7723601752SDmitry Osipenko * device 7823601752SDmitry Osipenko * 7923601752SDmitry Osipenko * Coefficients and thresholds are percentages unless otherwise noted 8023601752SDmitry Osipenko */ 8123601752SDmitry Osipenko struct tegra_devfreq_device_config { 8223601752SDmitry Osipenko u32 offset; 8323601752SDmitry Osipenko u32 irq_mask; 8423601752SDmitry Osipenko 8523601752SDmitry Osipenko /* Factors applied to boost_freq every consecutive watermark breach */ 8623601752SDmitry Osipenko unsigned int boost_up_coeff; 8723601752SDmitry Osipenko unsigned int boost_down_coeff; 8823601752SDmitry Osipenko 8923601752SDmitry Osipenko /* Define the watermark bounds when applied to the current avg */ 9023601752SDmitry Osipenko unsigned int boost_up_threshold; 9123601752SDmitry Osipenko unsigned int boost_down_threshold; 9223601752SDmitry Osipenko 9323601752SDmitry Osipenko /* 9423601752SDmitry Osipenko * Threshold of activity (cycles) below which the CPU frequency isn't 9523601752SDmitry Osipenko * to be taken into account. This is to avoid increasing the EMC 9623601752SDmitry Osipenko * frequency when the CPU is very busy but not accessing the bus often. 9723601752SDmitry Osipenko */ 9823601752SDmitry Osipenko u32 avg_dependency_threshold; 9923601752SDmitry Osipenko }; 10023601752SDmitry Osipenko 10123601752SDmitry Osipenko enum tegra_actmon_device { 10223601752SDmitry Osipenko MCALL = 0, 10323601752SDmitry Osipenko MCCPU, 10423601752SDmitry Osipenko }; 10523601752SDmitry Osipenko 10623601752SDmitry Osipenko static struct tegra_devfreq_device_config actmon_device_configs[] = { 10723601752SDmitry Osipenko { 10823601752SDmitry Osipenko /* MCALL: All memory accesses (including from the CPUs) */ 10923601752SDmitry Osipenko .offset = 0x1c0, 11023601752SDmitry Osipenko .irq_mask = 1 << 26, 11123601752SDmitry Osipenko .boost_up_coeff = 200, 11223601752SDmitry Osipenko .boost_down_coeff = 50, 11323601752SDmitry Osipenko .boost_up_threshold = 60, 11423601752SDmitry Osipenko .boost_down_threshold = 40, 11523601752SDmitry Osipenko }, 11623601752SDmitry Osipenko { 11723601752SDmitry Osipenko /* MCCPU: memory accesses from the CPUs */ 11823601752SDmitry Osipenko .offset = 0x200, 11923601752SDmitry Osipenko .irq_mask = 1 << 25, 12023601752SDmitry Osipenko .boost_up_coeff = 800, 12123601752SDmitry Osipenko .boost_down_coeff = 90, 12223601752SDmitry Osipenko .boost_up_threshold = 27, 12323601752SDmitry Osipenko .boost_down_threshold = 10, 12423601752SDmitry Osipenko .avg_dependency_threshold = 50000, 12523601752SDmitry Osipenko }, 12623601752SDmitry Osipenko }; 12723601752SDmitry Osipenko 12823601752SDmitry Osipenko /** 12923601752SDmitry Osipenko * struct tegra_devfreq_device - state specific to an ACTMON device 13023601752SDmitry Osipenko * 13123601752SDmitry Osipenko * Frequencies are in kHz. 13223601752SDmitry Osipenko */ 13323601752SDmitry Osipenko struct tegra_devfreq_device { 13423601752SDmitry Osipenko const struct tegra_devfreq_device_config *config; 13523601752SDmitry Osipenko void __iomem *regs; 13623601752SDmitry Osipenko 13723601752SDmitry Osipenko /* Average event count sampled in the last interrupt */ 13823601752SDmitry Osipenko u32 avg_count; 13923601752SDmitry Osipenko 14023601752SDmitry Osipenko /* 14123601752SDmitry Osipenko * Extra frequency to increase the target by due to consecutive 14223601752SDmitry Osipenko * watermark breaches. 14323601752SDmitry Osipenko */ 14423601752SDmitry Osipenko unsigned long boost_freq; 14523601752SDmitry Osipenko 14623601752SDmitry Osipenko /* Optimal frequency calculated from the stats for this device */ 14723601752SDmitry Osipenko unsigned long target_freq; 14823601752SDmitry Osipenko }; 14923601752SDmitry Osipenko 15023601752SDmitry Osipenko struct tegra_devfreq { 15123601752SDmitry Osipenko struct devfreq *devfreq; 15223601752SDmitry Osipenko 15323601752SDmitry Osipenko struct reset_control *reset; 15423601752SDmitry Osipenko struct clk *clock; 15523601752SDmitry Osipenko void __iomem *regs; 15623601752SDmitry Osipenko 15723601752SDmitry Osipenko struct clk *emc_clock; 15823601752SDmitry Osipenko unsigned long max_freq; 15923601752SDmitry Osipenko unsigned long cur_freq; 16023601752SDmitry Osipenko struct notifier_block rate_change_nb; 16123601752SDmitry Osipenko 16223601752SDmitry Osipenko struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)]; 16323601752SDmitry Osipenko 164dccdea01SDmitry Osipenko unsigned int irq; 16523601752SDmitry Osipenko }; 16623601752SDmitry Osipenko 16723601752SDmitry Osipenko struct tegra_actmon_emc_ratio { 16823601752SDmitry Osipenko unsigned long cpu_freq; 16923601752SDmitry Osipenko unsigned long emc_freq; 17023601752SDmitry Osipenko }; 17123601752SDmitry Osipenko 17223601752SDmitry Osipenko static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { 17323601752SDmitry Osipenko { 1400000, ULONG_MAX }, 17423601752SDmitry Osipenko { 1200000, 750000 }, 17523601752SDmitry Osipenko { 1100000, 600000 }, 17623601752SDmitry Osipenko { 1000000, 500000 }, 17723601752SDmitry Osipenko { 800000, 375000 }, 17823601752SDmitry Osipenko { 500000, 200000 }, 17923601752SDmitry Osipenko { 250000, 100000 }, 18023601752SDmitry Osipenko }; 18123601752SDmitry Osipenko 18223601752SDmitry Osipenko static u32 actmon_readl(struct tegra_devfreq *tegra, u32 offset) 18323601752SDmitry Osipenko { 18423601752SDmitry Osipenko return readl_relaxed(tegra->regs + offset); 18523601752SDmitry Osipenko } 18623601752SDmitry Osipenko 18723601752SDmitry Osipenko static void actmon_writel(struct tegra_devfreq *tegra, u32 val, u32 offset) 18823601752SDmitry Osipenko { 18923601752SDmitry Osipenko writel_relaxed(val, tegra->regs + offset); 19023601752SDmitry Osipenko } 19123601752SDmitry Osipenko 19223601752SDmitry Osipenko static u32 device_readl(struct tegra_devfreq_device *dev, u32 offset) 19323601752SDmitry Osipenko { 19423601752SDmitry Osipenko return readl_relaxed(dev->regs + offset); 19523601752SDmitry Osipenko } 19623601752SDmitry Osipenko 19723601752SDmitry Osipenko static void device_writel(struct tegra_devfreq_device *dev, u32 val, 19823601752SDmitry Osipenko u32 offset) 19923601752SDmitry Osipenko { 20023601752SDmitry Osipenko writel_relaxed(val, dev->regs + offset); 20123601752SDmitry Osipenko } 20223601752SDmitry Osipenko 20323601752SDmitry Osipenko static unsigned long do_percent(unsigned long val, unsigned int pct) 20423601752SDmitry Osipenko { 20523601752SDmitry Osipenko return val * pct / 100; 20623601752SDmitry Osipenko } 20723601752SDmitry Osipenko 20823601752SDmitry Osipenko static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra, 20923601752SDmitry Osipenko struct tegra_devfreq_device *dev) 21023601752SDmitry Osipenko { 21123601752SDmitry Osipenko u32 avg = dev->avg_count; 21223601752SDmitry Osipenko u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ; 21323601752SDmitry Osipenko u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD; 21423601752SDmitry Osipenko 21523601752SDmitry Osipenko device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK); 21623601752SDmitry Osipenko 21723601752SDmitry Osipenko avg = max(dev->avg_count, band); 21823601752SDmitry Osipenko device_writel(dev, avg - band, ACTMON_DEV_AVG_LOWER_WMARK); 21923601752SDmitry Osipenko } 22023601752SDmitry Osipenko 22123601752SDmitry Osipenko static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, 22223601752SDmitry Osipenko struct tegra_devfreq_device *dev) 22323601752SDmitry Osipenko { 22423601752SDmitry Osipenko u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; 22523601752SDmitry Osipenko 22623601752SDmitry Osipenko device_writel(dev, do_percent(val, dev->config->boost_up_threshold), 22723601752SDmitry Osipenko ACTMON_DEV_UPPER_WMARK); 22823601752SDmitry Osipenko 22923601752SDmitry Osipenko device_writel(dev, do_percent(val, dev->config->boost_down_threshold), 23023601752SDmitry Osipenko ACTMON_DEV_LOWER_WMARK); 23123601752SDmitry Osipenko } 23223601752SDmitry Osipenko 23323601752SDmitry Osipenko static void actmon_write_barrier(struct tegra_devfreq *tegra) 23423601752SDmitry Osipenko { 23523601752SDmitry Osipenko /* ensure the update has reached the ACTMON */ 23623601752SDmitry Osipenko readl(tegra->regs + ACTMON_GLB_STATUS); 23723601752SDmitry Osipenko } 23823601752SDmitry Osipenko 23923601752SDmitry Osipenko static void actmon_isr_device(struct tegra_devfreq *tegra, 24023601752SDmitry Osipenko struct tegra_devfreq_device *dev) 24123601752SDmitry Osipenko { 24223601752SDmitry Osipenko u32 intr_status, dev_ctrl; 24323601752SDmitry Osipenko 24423601752SDmitry Osipenko dev->avg_count = device_readl(dev, ACTMON_DEV_AVG_COUNT); 24523601752SDmitry Osipenko tegra_devfreq_update_avg_wmark(tegra, dev); 24623601752SDmitry Osipenko 24723601752SDmitry Osipenko intr_status = device_readl(dev, ACTMON_DEV_INTR_STATUS); 24823601752SDmitry Osipenko dev_ctrl = device_readl(dev, ACTMON_DEV_CTRL); 24923601752SDmitry Osipenko 25023601752SDmitry Osipenko if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) { 25123601752SDmitry Osipenko /* 25223601752SDmitry Osipenko * new_boost = min(old_boost * up_coef + step, max_freq) 25323601752SDmitry Osipenko */ 25423601752SDmitry Osipenko dev->boost_freq = do_percent(dev->boost_freq, 25523601752SDmitry Osipenko dev->config->boost_up_coeff); 25623601752SDmitry Osipenko dev->boost_freq += ACTMON_BOOST_FREQ_STEP; 25723601752SDmitry Osipenko 25823601752SDmitry Osipenko dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; 25923601752SDmitry Osipenko 26023601752SDmitry Osipenko if (dev->boost_freq >= tegra->max_freq) 26123601752SDmitry Osipenko dev->boost_freq = tegra->max_freq; 26223601752SDmitry Osipenko else 26323601752SDmitry Osipenko dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; 26423601752SDmitry Osipenko } else if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) { 26523601752SDmitry Osipenko /* 26623601752SDmitry Osipenko * new_boost = old_boost * down_coef 26723601752SDmitry Osipenko * or 0 if (old_boost * down_coef < step / 2) 26823601752SDmitry Osipenko */ 26923601752SDmitry Osipenko dev->boost_freq = do_percent(dev->boost_freq, 27023601752SDmitry Osipenko dev->config->boost_down_coeff); 27123601752SDmitry Osipenko 27223601752SDmitry Osipenko dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; 27323601752SDmitry Osipenko 27423601752SDmitry Osipenko if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) 27523601752SDmitry Osipenko dev->boost_freq = 0; 27623601752SDmitry Osipenko else 27723601752SDmitry Osipenko dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; 27823601752SDmitry Osipenko } 27923601752SDmitry Osipenko 28023601752SDmitry Osipenko if (dev->config->avg_dependency_threshold) { 28123601752SDmitry Osipenko if (dev->avg_count >= dev->config->avg_dependency_threshold) 28223601752SDmitry Osipenko dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; 28323601752SDmitry Osipenko else if (dev->boost_freq == 0) 28423601752SDmitry Osipenko dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; 28523601752SDmitry Osipenko } 28623601752SDmitry Osipenko 28723601752SDmitry Osipenko device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL); 28823601752SDmitry Osipenko 28923601752SDmitry Osipenko device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS); 29023601752SDmitry Osipenko 29123601752SDmitry Osipenko actmon_write_barrier(tegra); 29223601752SDmitry Osipenko } 29323601752SDmitry Osipenko 29423601752SDmitry Osipenko static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra, 29523601752SDmitry Osipenko unsigned long cpu_freq) 29623601752SDmitry Osipenko { 29723601752SDmitry Osipenko unsigned int i; 29823601752SDmitry Osipenko struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios; 29923601752SDmitry Osipenko 30023601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++) { 30123601752SDmitry Osipenko if (cpu_freq >= ratio->cpu_freq) { 30223601752SDmitry Osipenko if (ratio->emc_freq >= tegra->max_freq) 30323601752SDmitry Osipenko return tegra->max_freq; 30423601752SDmitry Osipenko else 30523601752SDmitry Osipenko return ratio->emc_freq; 30623601752SDmitry Osipenko } 30723601752SDmitry Osipenko } 30823601752SDmitry Osipenko 30923601752SDmitry Osipenko return 0; 31023601752SDmitry Osipenko } 31123601752SDmitry Osipenko 31223601752SDmitry Osipenko static void actmon_update_target(struct tegra_devfreq *tegra, 31323601752SDmitry Osipenko struct tegra_devfreq_device *dev) 31423601752SDmitry Osipenko { 31523601752SDmitry Osipenko unsigned long cpu_freq = 0; 31623601752SDmitry Osipenko unsigned long static_cpu_emc_freq = 0; 31723601752SDmitry Osipenko unsigned int avg_sustain_coef; 31823601752SDmitry Osipenko 31923601752SDmitry Osipenko if (dev->config->avg_dependency_threshold) { 32023601752SDmitry Osipenko cpu_freq = cpufreq_get(0); 32123601752SDmitry Osipenko static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq); 32223601752SDmitry Osipenko } 32323601752SDmitry Osipenko 32423601752SDmitry Osipenko dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD; 32523601752SDmitry Osipenko avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold; 32623601752SDmitry Osipenko dev->target_freq = do_percent(dev->target_freq, avg_sustain_coef); 32723601752SDmitry Osipenko dev->target_freq += dev->boost_freq; 32823601752SDmitry Osipenko 32923601752SDmitry Osipenko if (dev->avg_count >= dev->config->avg_dependency_threshold) 33023601752SDmitry Osipenko dev->target_freq = max(dev->target_freq, static_cpu_emc_freq); 33123601752SDmitry Osipenko } 33223601752SDmitry Osipenko 33323601752SDmitry Osipenko static irqreturn_t actmon_thread_isr(int irq, void *data) 33423601752SDmitry Osipenko { 33523601752SDmitry Osipenko struct tegra_devfreq *tegra = data; 33623601752SDmitry Osipenko bool handled = false; 33723601752SDmitry Osipenko unsigned int i; 33823601752SDmitry Osipenko u32 val; 33923601752SDmitry Osipenko 34023601752SDmitry Osipenko mutex_lock(&tegra->devfreq->lock); 34123601752SDmitry Osipenko 34223601752SDmitry Osipenko val = actmon_readl(tegra, ACTMON_GLB_STATUS); 34323601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { 34423601752SDmitry Osipenko if (val & tegra->devices[i].config->irq_mask) { 34523601752SDmitry Osipenko actmon_isr_device(tegra, tegra->devices + i); 34623601752SDmitry Osipenko handled = true; 34723601752SDmitry Osipenko } 34823601752SDmitry Osipenko } 34923601752SDmitry Osipenko 35023601752SDmitry Osipenko if (handled) 35123601752SDmitry Osipenko update_devfreq(tegra->devfreq); 35223601752SDmitry Osipenko 35323601752SDmitry Osipenko mutex_unlock(&tegra->devfreq->lock); 35423601752SDmitry Osipenko 35523601752SDmitry Osipenko return handled ? IRQ_HANDLED : IRQ_NONE; 35623601752SDmitry Osipenko } 35723601752SDmitry Osipenko 35823601752SDmitry Osipenko static int tegra_actmon_rate_notify_cb(struct notifier_block *nb, 35923601752SDmitry Osipenko unsigned long action, void *ptr) 36023601752SDmitry Osipenko { 36123601752SDmitry Osipenko struct clk_notifier_data *data = ptr; 36223601752SDmitry Osipenko struct tegra_devfreq *tegra; 36323601752SDmitry Osipenko struct tegra_devfreq_device *dev; 36423601752SDmitry Osipenko unsigned int i; 36523601752SDmitry Osipenko 36623601752SDmitry Osipenko if (action != POST_RATE_CHANGE) 36723601752SDmitry Osipenko return NOTIFY_OK; 36823601752SDmitry Osipenko 36923601752SDmitry Osipenko tegra = container_of(nb, struct tegra_devfreq, rate_change_nb); 37023601752SDmitry Osipenko 37123601752SDmitry Osipenko tegra->cur_freq = data->new_rate / KHZ; 37223601752SDmitry Osipenko 37323601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { 37423601752SDmitry Osipenko dev = &tegra->devices[i]; 37523601752SDmitry Osipenko 37623601752SDmitry Osipenko tegra_devfreq_update_wmark(tegra, dev); 37723601752SDmitry Osipenko } 37823601752SDmitry Osipenko 37923601752SDmitry Osipenko actmon_write_barrier(tegra); 38023601752SDmitry Osipenko 38123601752SDmitry Osipenko return NOTIFY_OK; 38223601752SDmitry Osipenko } 38323601752SDmitry Osipenko 38423601752SDmitry Osipenko static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, 38523601752SDmitry Osipenko struct tegra_devfreq_device *dev) 38623601752SDmitry Osipenko { 38723601752SDmitry Osipenko u32 val = 0; 38823601752SDmitry Osipenko 38923601752SDmitry Osipenko dev->target_freq = tegra->cur_freq; 39023601752SDmitry Osipenko 39123601752SDmitry Osipenko dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; 39223601752SDmitry Osipenko device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG); 39323601752SDmitry Osipenko 39423601752SDmitry Osipenko tegra_devfreq_update_avg_wmark(tegra, dev); 39523601752SDmitry Osipenko tegra_devfreq_update_wmark(tegra, dev); 39623601752SDmitry Osipenko 39723601752SDmitry Osipenko device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT); 39823601752SDmitry Osipenko device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS); 39923601752SDmitry Osipenko 40023601752SDmitry Osipenko val |= ACTMON_DEV_CTRL_ENB_PERIODIC; 40123601752SDmitry Osipenko val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1) 40223601752SDmitry Osipenko << ACTMON_DEV_CTRL_K_VAL_SHIFT; 40323601752SDmitry Osipenko val |= (ACTMON_BELOW_WMARK_WINDOW - 1) 40423601752SDmitry Osipenko << ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT; 40523601752SDmitry Osipenko val |= (ACTMON_ABOVE_WMARK_WINDOW - 1) 40623601752SDmitry Osipenko << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; 40723601752SDmitry Osipenko val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN; 40823601752SDmitry Osipenko val |= ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN; 40923601752SDmitry Osipenko val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; 41023601752SDmitry Osipenko val |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; 41123601752SDmitry Osipenko val |= ACTMON_DEV_CTRL_ENB; 41223601752SDmitry Osipenko 41323601752SDmitry Osipenko device_writel(dev, val, ACTMON_DEV_CTRL); 41423601752SDmitry Osipenko } 41523601752SDmitry Osipenko 41623601752SDmitry Osipenko static void tegra_actmon_start(struct tegra_devfreq *tegra) 41723601752SDmitry Osipenko { 41823601752SDmitry Osipenko unsigned int i; 41923601752SDmitry Osipenko 42023601752SDmitry Osipenko actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1, 42123601752SDmitry Osipenko ACTMON_GLB_PERIOD_CTRL); 42223601752SDmitry Osipenko 42323601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) 42423601752SDmitry Osipenko tegra_actmon_configure_device(tegra, &tegra->devices[i]); 42523601752SDmitry Osipenko 42623601752SDmitry Osipenko actmon_write_barrier(tegra); 42723601752SDmitry Osipenko 42823601752SDmitry Osipenko enable_irq(tegra->irq); 42923601752SDmitry Osipenko } 43023601752SDmitry Osipenko 43123601752SDmitry Osipenko static void tegra_actmon_stop(struct tegra_devfreq *tegra) 43223601752SDmitry Osipenko { 43323601752SDmitry Osipenko unsigned int i; 43423601752SDmitry Osipenko 43523601752SDmitry Osipenko disable_irq(tegra->irq); 43623601752SDmitry Osipenko 43723601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { 43823601752SDmitry Osipenko device_writel(&tegra->devices[i], 0x00000000, ACTMON_DEV_CTRL); 43923601752SDmitry Osipenko device_writel(&tegra->devices[i], ACTMON_INTR_STATUS_CLEAR, 44023601752SDmitry Osipenko ACTMON_DEV_INTR_STATUS); 44123601752SDmitry Osipenko } 44223601752SDmitry Osipenko 44323601752SDmitry Osipenko actmon_write_barrier(tegra); 44423601752SDmitry Osipenko } 44523601752SDmitry Osipenko 44623601752SDmitry Osipenko static int tegra_devfreq_target(struct device *dev, unsigned long *freq, 44723601752SDmitry Osipenko u32 flags) 44823601752SDmitry Osipenko { 44923601752SDmitry Osipenko struct tegra_devfreq *tegra = dev_get_drvdata(dev); 45023601752SDmitry Osipenko struct devfreq *devfreq = tegra->devfreq; 45123601752SDmitry Osipenko struct dev_pm_opp *opp; 45223601752SDmitry Osipenko unsigned long rate; 45323601752SDmitry Osipenko int err; 45423601752SDmitry Osipenko 45523601752SDmitry Osipenko opp = devfreq_recommended_opp(dev, freq, flags); 45623601752SDmitry Osipenko if (IS_ERR(opp)) { 45723601752SDmitry Osipenko dev_err(dev, "Failed to find opp for %lu Hz\n", *freq); 45823601752SDmitry Osipenko return PTR_ERR(opp); 45923601752SDmitry Osipenko } 46023601752SDmitry Osipenko rate = dev_pm_opp_get_freq(opp); 46123601752SDmitry Osipenko dev_pm_opp_put(opp); 46223601752SDmitry Osipenko 46323601752SDmitry Osipenko err = clk_set_min_rate(tegra->emc_clock, rate); 46423601752SDmitry Osipenko if (err) 46523601752SDmitry Osipenko return err; 46623601752SDmitry Osipenko 46723601752SDmitry Osipenko err = clk_set_rate(tegra->emc_clock, 0); 46823601752SDmitry Osipenko if (err) 46923601752SDmitry Osipenko goto restore_min_rate; 47023601752SDmitry Osipenko 47123601752SDmitry Osipenko return 0; 47223601752SDmitry Osipenko 47323601752SDmitry Osipenko restore_min_rate: 47423601752SDmitry Osipenko clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq); 47523601752SDmitry Osipenko 47623601752SDmitry Osipenko return err; 47723601752SDmitry Osipenko } 47823601752SDmitry Osipenko 47923601752SDmitry Osipenko static int tegra_devfreq_get_dev_status(struct device *dev, 48023601752SDmitry Osipenko struct devfreq_dev_status *stat) 48123601752SDmitry Osipenko { 48223601752SDmitry Osipenko struct tegra_devfreq *tegra = dev_get_drvdata(dev); 48323601752SDmitry Osipenko struct tegra_devfreq_device *actmon_dev; 48423601752SDmitry Osipenko unsigned long cur_freq; 48523601752SDmitry Osipenko 48623601752SDmitry Osipenko cur_freq = READ_ONCE(tegra->cur_freq); 48723601752SDmitry Osipenko 48823601752SDmitry Osipenko /* To be used by the tegra governor */ 48923601752SDmitry Osipenko stat->private_data = tegra; 49023601752SDmitry Osipenko 49123601752SDmitry Osipenko /* The below are to be used by the other governors */ 49223601752SDmitry Osipenko stat->current_frequency = cur_freq * KHZ; 49323601752SDmitry Osipenko 49423601752SDmitry Osipenko actmon_dev = &tegra->devices[MCALL]; 49523601752SDmitry Osipenko 49623601752SDmitry Osipenko /* Number of cycles spent on memory access */ 49723601752SDmitry Osipenko stat->busy_time = device_readl(actmon_dev, ACTMON_DEV_AVG_COUNT); 49823601752SDmitry Osipenko 49923601752SDmitry Osipenko /* The bus can be considered to be saturated way before 100% */ 50023601752SDmitry Osipenko stat->busy_time *= 100 / BUS_SATURATION_RATIO; 50123601752SDmitry Osipenko 50223601752SDmitry Osipenko /* Number of cycles in a sampling period */ 50323601752SDmitry Osipenko stat->total_time = ACTMON_SAMPLING_PERIOD * cur_freq; 50423601752SDmitry Osipenko 50523601752SDmitry Osipenko stat->busy_time = min(stat->busy_time, stat->total_time); 50623601752SDmitry Osipenko 50723601752SDmitry Osipenko return 0; 50823601752SDmitry Osipenko } 50923601752SDmitry Osipenko 51023601752SDmitry Osipenko static struct devfreq_dev_profile tegra_devfreq_profile = { 51123601752SDmitry Osipenko .polling_ms = 0, 51223601752SDmitry Osipenko .target = tegra_devfreq_target, 51323601752SDmitry Osipenko .get_dev_status = tegra_devfreq_get_dev_status, 51423601752SDmitry Osipenko }; 51523601752SDmitry Osipenko 51623601752SDmitry Osipenko static int tegra_governor_get_target(struct devfreq *devfreq, 51723601752SDmitry Osipenko unsigned long *freq) 51823601752SDmitry Osipenko { 51923601752SDmitry Osipenko struct devfreq_dev_status *stat; 52023601752SDmitry Osipenko struct tegra_devfreq *tegra; 52123601752SDmitry Osipenko struct tegra_devfreq_device *dev; 52223601752SDmitry Osipenko unsigned long target_freq = 0; 52323601752SDmitry Osipenko unsigned int i; 52423601752SDmitry Osipenko int err; 52523601752SDmitry Osipenko 52623601752SDmitry Osipenko err = devfreq_update_stats(devfreq); 52723601752SDmitry Osipenko if (err) 52823601752SDmitry Osipenko return err; 52923601752SDmitry Osipenko 53023601752SDmitry Osipenko stat = &devfreq->last_status; 53123601752SDmitry Osipenko 53223601752SDmitry Osipenko tegra = stat->private_data; 53323601752SDmitry Osipenko 53423601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { 53523601752SDmitry Osipenko dev = &tegra->devices[i]; 53623601752SDmitry Osipenko 53723601752SDmitry Osipenko actmon_update_target(tegra, dev); 53823601752SDmitry Osipenko 53923601752SDmitry Osipenko target_freq = max(target_freq, dev->target_freq); 54023601752SDmitry Osipenko } 54123601752SDmitry Osipenko 54223601752SDmitry Osipenko *freq = target_freq * KHZ; 54323601752SDmitry Osipenko 54423601752SDmitry Osipenko return 0; 54523601752SDmitry Osipenko } 54623601752SDmitry Osipenko 54723601752SDmitry Osipenko static int tegra_governor_event_handler(struct devfreq *devfreq, 54823601752SDmitry Osipenko unsigned int event, void *data) 54923601752SDmitry Osipenko { 55023601752SDmitry Osipenko struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent); 55123601752SDmitry Osipenko 552d49eeb1eSDmitry Osipenko /* 553d49eeb1eSDmitry Osipenko * Couple devfreq-device with the governor early because it is 554d49eeb1eSDmitry Osipenko * needed at the moment of governor's start (used by ISR). 555d49eeb1eSDmitry Osipenko */ 556d49eeb1eSDmitry Osipenko tegra->devfreq = devfreq; 557d49eeb1eSDmitry Osipenko 55823601752SDmitry Osipenko switch (event) { 55923601752SDmitry Osipenko case DEVFREQ_GOV_START: 56023601752SDmitry Osipenko devfreq_monitor_start(devfreq); 56123601752SDmitry Osipenko tegra_actmon_start(tegra); 56223601752SDmitry Osipenko break; 56323601752SDmitry Osipenko 56423601752SDmitry Osipenko case DEVFREQ_GOV_STOP: 56523601752SDmitry Osipenko tegra_actmon_stop(tegra); 56623601752SDmitry Osipenko devfreq_monitor_stop(devfreq); 56723601752SDmitry Osipenko break; 56823601752SDmitry Osipenko 56923601752SDmitry Osipenko case DEVFREQ_GOV_SUSPEND: 57023601752SDmitry Osipenko tegra_actmon_stop(tegra); 57123601752SDmitry Osipenko devfreq_monitor_suspend(devfreq); 57223601752SDmitry Osipenko break; 57323601752SDmitry Osipenko 57423601752SDmitry Osipenko case DEVFREQ_GOV_RESUME: 57523601752SDmitry Osipenko devfreq_monitor_resume(devfreq); 57623601752SDmitry Osipenko tegra_actmon_start(tegra); 57723601752SDmitry Osipenko break; 57823601752SDmitry Osipenko } 57923601752SDmitry Osipenko 58023601752SDmitry Osipenko return 0; 58123601752SDmitry Osipenko } 58223601752SDmitry Osipenko 58323601752SDmitry Osipenko static struct devfreq_governor tegra_devfreq_governor = { 58423601752SDmitry Osipenko .name = "tegra_actmon", 58523601752SDmitry Osipenko .get_target_freq = tegra_governor_get_target, 58623601752SDmitry Osipenko .event_handler = tegra_governor_event_handler, 58723601752SDmitry Osipenko .immutable = true, 58823601752SDmitry Osipenko }; 58923601752SDmitry Osipenko 59023601752SDmitry Osipenko static int tegra_devfreq_probe(struct platform_device *pdev) 59123601752SDmitry Osipenko { 59223601752SDmitry Osipenko struct tegra_devfreq_device *dev; 593d49eeb1eSDmitry Osipenko struct tegra_devfreq *tegra; 594d49eeb1eSDmitry Osipenko struct devfreq *devfreq; 595d49eeb1eSDmitry Osipenko unsigned int i; 596*7296443bSDmitry Osipenko long rate; 59723601752SDmitry Osipenko int err; 59823601752SDmitry Osipenko 59923601752SDmitry Osipenko tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 60023601752SDmitry Osipenko if (!tegra) 60123601752SDmitry Osipenko return -ENOMEM; 60223601752SDmitry Osipenko 60323601752SDmitry Osipenko tegra->regs = devm_platform_ioremap_resource(pdev, 0); 60423601752SDmitry Osipenko if (IS_ERR(tegra->regs)) 60523601752SDmitry Osipenko return PTR_ERR(tegra->regs); 60623601752SDmitry Osipenko 60723601752SDmitry Osipenko tegra->reset = devm_reset_control_get(&pdev->dev, "actmon"); 60823601752SDmitry Osipenko if (IS_ERR(tegra->reset)) { 60923601752SDmitry Osipenko dev_err(&pdev->dev, "Failed to get reset\n"); 61023601752SDmitry Osipenko return PTR_ERR(tegra->reset); 61123601752SDmitry Osipenko } 61223601752SDmitry Osipenko 61323601752SDmitry Osipenko tegra->clock = devm_clk_get(&pdev->dev, "actmon"); 61423601752SDmitry Osipenko if (IS_ERR(tegra->clock)) { 61523601752SDmitry Osipenko dev_err(&pdev->dev, "Failed to get actmon clock\n"); 61623601752SDmitry Osipenko return PTR_ERR(tegra->clock); 61723601752SDmitry Osipenko } 61823601752SDmitry Osipenko 61923601752SDmitry Osipenko tegra->emc_clock = devm_clk_get(&pdev->dev, "emc"); 62023601752SDmitry Osipenko if (IS_ERR(tegra->emc_clock)) { 62123601752SDmitry Osipenko dev_err(&pdev->dev, "Failed to get emc clock\n"); 62223601752SDmitry Osipenko return PTR_ERR(tegra->emc_clock); 62323601752SDmitry Osipenko } 62423601752SDmitry Osipenko 625dccdea01SDmitry Osipenko err = platform_get_irq(pdev, 0); 626dccdea01SDmitry Osipenko if (err < 0) { 62723601752SDmitry Osipenko dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err); 62823601752SDmitry Osipenko return err; 62923601752SDmitry Osipenko } 630dccdea01SDmitry Osipenko tegra->irq = err; 63123601752SDmitry Osipenko 632d49eeb1eSDmitry Osipenko irq_set_status_flags(tegra->irq, IRQ_NOAUTOEN); 633d49eeb1eSDmitry Osipenko 634d49eeb1eSDmitry Osipenko err = devm_request_threaded_irq(&pdev->dev, tegra->irq, NULL, 635d49eeb1eSDmitry Osipenko actmon_thread_isr, IRQF_ONESHOT, 636d49eeb1eSDmitry Osipenko "tegra-devfreq", tegra); 637d49eeb1eSDmitry Osipenko if (err) { 638d49eeb1eSDmitry Osipenko dev_err(&pdev->dev, "Interrupt request failed: %d\n", err); 639d49eeb1eSDmitry Osipenko return err; 640d49eeb1eSDmitry Osipenko } 641d49eeb1eSDmitry Osipenko 64223601752SDmitry Osipenko reset_control_assert(tegra->reset); 64323601752SDmitry Osipenko 64423601752SDmitry Osipenko err = clk_prepare_enable(tegra->clock); 64523601752SDmitry Osipenko if (err) { 64623601752SDmitry Osipenko dev_err(&pdev->dev, 64723601752SDmitry Osipenko "Failed to prepare and enable ACTMON clock\n"); 64823601752SDmitry Osipenko return err; 64923601752SDmitry Osipenko } 65023601752SDmitry Osipenko 65123601752SDmitry Osipenko reset_control_deassert(tegra->reset); 65223601752SDmitry Osipenko 653*7296443bSDmitry Osipenko rate = clk_round_rate(tegra->emc_clock, ULONG_MAX); 654*7296443bSDmitry Osipenko if (rate < 0) { 655*7296443bSDmitry Osipenko dev_err(&pdev->dev, "Failed to round clock rate: %ld\n", rate); 656*7296443bSDmitry Osipenko return rate; 657*7296443bSDmitry Osipenko } 658*7296443bSDmitry Osipenko 65923601752SDmitry Osipenko tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ; 660*7296443bSDmitry Osipenko tegra->max_freq = rate / KHZ; 66123601752SDmitry Osipenko 66223601752SDmitry Osipenko for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) { 66323601752SDmitry Osipenko dev = tegra->devices + i; 66423601752SDmitry Osipenko dev->config = actmon_device_configs + i; 66523601752SDmitry Osipenko dev->regs = tegra->regs + dev->config->offset; 66623601752SDmitry Osipenko } 66723601752SDmitry Osipenko 66823601752SDmitry Osipenko for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) { 66923601752SDmitry Osipenko rate = clk_round_rate(tegra->emc_clock, rate); 67023601752SDmitry Osipenko 671*7296443bSDmitry Osipenko if (rate < 0) { 672*7296443bSDmitry Osipenko dev_err(&pdev->dev, 673*7296443bSDmitry Osipenko "Failed to round clock rate: %ld\n", rate); 674*7296443bSDmitry Osipenko err = rate; 675*7296443bSDmitry Osipenko goto remove_opps; 676*7296443bSDmitry Osipenko } 677*7296443bSDmitry Osipenko 67823601752SDmitry Osipenko err = dev_pm_opp_add(&pdev->dev, rate, 0); 67923601752SDmitry Osipenko if (err) { 68023601752SDmitry Osipenko dev_err(&pdev->dev, "Failed to add OPP: %d\n", err); 68123601752SDmitry Osipenko goto remove_opps; 68223601752SDmitry Osipenko } 68323601752SDmitry Osipenko } 68423601752SDmitry Osipenko 68523601752SDmitry Osipenko platform_set_drvdata(pdev, tegra); 68623601752SDmitry Osipenko 68723601752SDmitry Osipenko tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; 68823601752SDmitry Osipenko err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb); 68923601752SDmitry Osipenko if (err) { 69023601752SDmitry Osipenko dev_err(&pdev->dev, 69123601752SDmitry Osipenko "Failed to register rate change notifier\n"); 69223601752SDmitry Osipenko goto remove_opps; 69323601752SDmitry Osipenko } 69423601752SDmitry Osipenko 69523601752SDmitry Osipenko err = devfreq_add_governor(&tegra_devfreq_governor); 69623601752SDmitry Osipenko if (err) { 69723601752SDmitry Osipenko dev_err(&pdev->dev, "Failed to add governor: %d\n", err); 69823601752SDmitry Osipenko goto unreg_notifier; 69923601752SDmitry Osipenko } 70023601752SDmitry Osipenko 70123601752SDmitry Osipenko tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); 702d49eeb1eSDmitry Osipenko devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile, 703d49eeb1eSDmitry Osipenko "tegra_actmon", NULL); 704d49eeb1eSDmitry Osipenko if (IS_ERR(devfreq)) { 705d49eeb1eSDmitry Osipenko err = PTR_ERR(devfreq); 70623601752SDmitry Osipenko goto remove_governor; 70723601752SDmitry Osipenko } 70823601752SDmitry Osipenko 70923601752SDmitry Osipenko return 0; 71023601752SDmitry Osipenko 71123601752SDmitry Osipenko remove_governor: 71223601752SDmitry Osipenko devfreq_remove_governor(&tegra_devfreq_governor); 71323601752SDmitry Osipenko 71423601752SDmitry Osipenko unreg_notifier: 71523601752SDmitry Osipenko clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb); 71623601752SDmitry Osipenko 71723601752SDmitry Osipenko remove_opps: 71823601752SDmitry Osipenko dev_pm_opp_remove_all_dynamic(&pdev->dev); 71923601752SDmitry Osipenko 72023601752SDmitry Osipenko reset_control_reset(tegra->reset); 72123601752SDmitry Osipenko clk_disable_unprepare(tegra->clock); 72223601752SDmitry Osipenko 72323601752SDmitry Osipenko return err; 72423601752SDmitry Osipenko } 72523601752SDmitry Osipenko 72623601752SDmitry Osipenko static int tegra_devfreq_remove(struct platform_device *pdev) 72723601752SDmitry Osipenko { 72823601752SDmitry Osipenko struct tegra_devfreq *tegra = platform_get_drvdata(pdev); 72923601752SDmitry Osipenko 73023601752SDmitry Osipenko devfreq_remove_device(tegra->devfreq); 73123601752SDmitry Osipenko devfreq_remove_governor(&tegra_devfreq_governor); 73223601752SDmitry Osipenko 73323601752SDmitry Osipenko clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb); 73423601752SDmitry Osipenko dev_pm_opp_remove_all_dynamic(&pdev->dev); 73523601752SDmitry Osipenko 73623601752SDmitry Osipenko reset_control_reset(tegra->reset); 73723601752SDmitry Osipenko clk_disable_unprepare(tegra->clock); 73823601752SDmitry Osipenko 73923601752SDmitry Osipenko return 0; 74023601752SDmitry Osipenko } 74123601752SDmitry Osipenko 74223601752SDmitry Osipenko static const struct of_device_id tegra_devfreq_of_match[] = { 74323601752SDmitry Osipenko { .compatible = "nvidia,tegra30-actmon" }, 74423601752SDmitry Osipenko { .compatible = "nvidia,tegra124-actmon" }, 74523601752SDmitry Osipenko { }, 74623601752SDmitry Osipenko }; 74723601752SDmitry Osipenko 74823601752SDmitry Osipenko MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match); 74923601752SDmitry Osipenko 75023601752SDmitry Osipenko static struct platform_driver tegra_devfreq_driver = { 75123601752SDmitry Osipenko .probe = tegra_devfreq_probe, 75223601752SDmitry Osipenko .remove = tegra_devfreq_remove, 75323601752SDmitry Osipenko .driver = { 75423601752SDmitry Osipenko .name = "tegra-devfreq", 75523601752SDmitry Osipenko .of_match_table = tegra_devfreq_of_match, 75623601752SDmitry Osipenko }, 75723601752SDmitry Osipenko }; 75823601752SDmitry Osipenko module_platform_driver(tegra_devfreq_driver); 75923601752SDmitry Osipenko 76023601752SDmitry Osipenko MODULE_LICENSE("GPL v2"); 76123601752SDmitry Osipenko MODULE_DESCRIPTION("Tegra devfreq driver"); 76223601752SDmitry Osipenko MODULE_AUTHOR("Tomeu Vizoso <tomeu.vizoso@collabora.com>"); 763