Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / devfreq: Introduce driver for NVIDIA Tegra20

Add devfreq driver for NVIDIA Tegra20 SoC's. The driver periodically
reads out Memory Controller counters and adjusts memory frequency based
on the memory clients activity.

Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
[Removed MAINTAINERS updates by MyungJoo so that it can be sent elsewhere.]
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>

authored by

Dmitry Osipenko and committed by
MyungJoo Ham
d196175e 23601752

+223
+10
drivers/devfreq/Kconfig
··· 104 104 It reads ACTMON counters of memory controllers and adjusts the 105 105 operating frequencies and voltages with OPP support. 106 106 107 + config ARM_TEGRA20_DEVFREQ 108 + tristate "NVIDIA Tegra20 DEVFREQ Driver" 109 + depends on (TEGRA_MC && TEGRA20_EMC) || COMPILE_TEST 110 + select DEVFREQ_GOV_SIMPLE_ONDEMAND 111 + select PM_OPP 112 + help 113 + This adds the DEVFREQ driver for the Tegra20 family of SoCs. 114 + It reads Memory Controller counters and adjusts the operating 115 + frequencies and voltages with OPP support. 116 + 107 117 config ARM_RK3399_DMC_DEVFREQ 108 118 tristate "ARM RK3399 DMC DEVFREQ Driver" 109 119 depends on ARCH_ROCKCHIP
+1
drivers/devfreq/Makefile
··· 11 11 obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o 12 12 obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o 13 13 obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o 14 + obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o 14 15 15 16 # DEVFREQ Event Drivers 16 17 obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
+212
drivers/devfreq/tegra20-devfreq.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * NVIDIA Tegra20 devfreq driver 4 + * 5 + * Copyright (C) 2019 GRATE-DRIVER project 6 + */ 7 + 8 + #include <linux/clk.h> 9 + #include <linux/devfreq.h> 10 + #include <linux/io.h> 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/of_device.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/pm_opp.h> 16 + #include <linux/slab.h> 17 + 18 + #include <soc/tegra/mc.h> 19 + 20 + #include "governor.h" 21 + 22 + #define MC_STAT_CONTROL 0x90 23 + #define MC_STAT_EMC_CLOCK_LIMIT 0xa0 24 + #define MC_STAT_EMC_CLOCKS 0xa4 25 + #define MC_STAT_EMC_CONTROL 0xa8 26 + #define MC_STAT_EMC_COUNT 0xb8 27 + 28 + #define EMC_GATHER_CLEAR (1 << 8) 29 + #define EMC_GATHER_ENABLE (3 << 8) 30 + 31 + struct tegra_devfreq { 32 + struct devfreq *devfreq; 33 + struct clk *emc_clock; 34 + void __iomem *regs; 35 + }; 36 + 37 + static int tegra_devfreq_target(struct device *dev, unsigned long *freq, 38 + u32 flags) 39 + { 40 + struct tegra_devfreq *tegra = dev_get_drvdata(dev); 41 + struct devfreq *devfreq = tegra->devfreq; 42 + struct dev_pm_opp *opp; 43 + unsigned long rate; 44 + int err; 45 + 46 + opp = devfreq_recommended_opp(dev, freq, flags); 47 + if (IS_ERR(opp)) 48 + return PTR_ERR(opp); 49 + 50 + rate = dev_pm_opp_get_freq(opp); 51 + dev_pm_opp_put(opp); 52 + 53 + err = clk_set_min_rate(tegra->emc_clock, rate); 54 + if (err) 55 + return err; 56 + 57 + err = clk_set_rate(tegra->emc_clock, 0); 58 + if (err) 59 + goto restore_min_rate; 60 + 61 + return 0; 62 + 63 + restore_min_rate: 64 + clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq); 65 + 66 + return err; 67 + } 68 + 69 + static int tegra_devfreq_get_dev_status(struct device *dev, 70 + struct devfreq_dev_status *stat) 71 + { 72 + struct tegra_devfreq *tegra = dev_get_drvdata(dev); 73 + 74 + /* 75 + * EMC_COUNT returns number of memory events, that number is lower 76 + * than the number of clocks. Conversion ratio of 1/8 results in a 77 + * bit higher bandwidth than actually needed, it is good enough for 78 + * the time being because drivers don't support requesting minimum 79 + * needed memory bandwidth yet. 80 + * 81 + * TODO: adjust the ratio value once relevant drivers will support 82 + * memory bandwidth management. 83 + */ 84 + stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT); 85 + stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8; 86 + stat->current_frequency = clk_get_rate(tegra->emc_clock); 87 + 88 + writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL); 89 + writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL); 90 + 91 + return 0; 92 + } 93 + 94 + static struct devfreq_dev_profile tegra_devfreq_profile = { 95 + .polling_ms = 500, 96 + .target = tegra_devfreq_target, 97 + .get_dev_status = tegra_devfreq_get_dev_status, 98 + }; 99 + 100 + static struct tegra_mc *tegra_get_memory_controller(void) 101 + { 102 + struct platform_device *pdev; 103 + struct device_node *np; 104 + struct tegra_mc *mc; 105 + 106 + np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart"); 107 + if (!np) 108 + return ERR_PTR(-ENOENT); 109 + 110 + pdev = of_find_device_by_node(np); 111 + of_node_put(np); 112 + if (!pdev) 113 + return ERR_PTR(-ENODEV); 114 + 115 + mc = platform_get_drvdata(pdev); 116 + if (!mc) 117 + return ERR_PTR(-EPROBE_DEFER); 118 + 119 + return mc; 120 + } 121 + 122 + static int tegra_devfreq_probe(struct platform_device *pdev) 123 + { 124 + struct tegra_devfreq *tegra; 125 + struct tegra_mc *mc; 126 + unsigned long max_rate; 127 + unsigned long rate; 128 + int err; 129 + 130 + mc = tegra_get_memory_controller(); 131 + if (IS_ERR(mc)) { 132 + err = PTR_ERR(mc); 133 + dev_err(&pdev->dev, "failed to get memory controller: %d\n", 134 + err); 135 + return err; 136 + } 137 + 138 + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 139 + if (!tegra) 140 + return -ENOMEM; 141 + 142 + /* EMC is a system-critical clock that is always enabled */ 143 + tegra->emc_clock = devm_clk_get(&pdev->dev, "emc"); 144 + if (IS_ERR(tegra->emc_clock)) { 145 + err = PTR_ERR(tegra->emc_clock); 146 + dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); 147 + return err; 148 + } 149 + 150 + tegra->regs = mc->regs; 151 + 152 + max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX); 153 + 154 + for (rate = 0; rate <= max_rate; rate++) { 155 + rate = clk_round_rate(tegra->emc_clock, rate); 156 + 157 + err = dev_pm_opp_add(&pdev->dev, rate, 0); 158 + if (err) { 159 + dev_err(&pdev->dev, "failed to add opp: %d\n", err); 160 + goto remove_opps; 161 + } 162 + } 163 + 164 + /* 165 + * Reset statistic gathers state, select global bandwidth for the 166 + * statistics collection mode and set clocks counter saturation 167 + * limit to maximum. 168 + */ 169 + writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL); 170 + writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL); 171 + writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT); 172 + 173 + platform_set_drvdata(pdev, tegra); 174 + 175 + tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile, 176 + DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); 177 + if (IS_ERR(tegra->devfreq)) { 178 + err = PTR_ERR(tegra->devfreq); 179 + goto remove_opps; 180 + } 181 + 182 + return 0; 183 + 184 + remove_opps: 185 + dev_pm_opp_remove_all_dynamic(&pdev->dev); 186 + 187 + return err; 188 + } 189 + 190 + static int tegra_devfreq_remove(struct platform_device *pdev) 191 + { 192 + struct tegra_devfreq *tegra = platform_get_drvdata(pdev); 193 + 194 + devfreq_remove_device(tegra->devfreq); 195 + dev_pm_opp_remove_all_dynamic(&pdev->dev); 196 + 197 + return 0; 198 + } 199 + 200 + static struct platform_driver tegra_devfreq_driver = { 201 + .probe = tegra_devfreq_probe, 202 + .remove = tegra_devfreq_remove, 203 + .driver = { 204 + .name = "tegra20-devfreq", 205 + }, 206 + }; 207 + module_platform_driver(tegra_devfreq_driver); 208 + 209 + MODULE_ALIAS("platform:tegra20-devfreq"); 210 + MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); 211 + MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver"); 212 + MODULE_LICENSE("GPL v2");