summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2019-05-01 19:38:15 -0400
committerMyungJoo Ham <myungjoo.ham@samsung.com>2019-08-24 07:11:12 -0400
commitd196175ed8f45248b54bf5c2e7c05ac0e1e97d70 (patch)
treefab5249d3665fbebde83917bd2a4e1fda422a189
parent23601752911b5dac91207859e0ab77bd8c77545c (diff)
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>
-rw-r--r--drivers/devfreq/Kconfig10
-rw-r--r--drivers/devfreq/Makefile1
-rw-r--r--drivers/devfreq/tegra20-devfreq.c212
3 files changed, 223 insertions, 0 deletions
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 1091119c1d0a..f3b242987fd9 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -104,6 +104,16 @@ config ARM_TEGRA_DEVFREQ
104 It reads ACTMON counters of memory controllers and adjusts the 104 It reads ACTMON counters of memory controllers and adjusts the
105 operating frequencies and voltages with OPP support. 105 operating frequencies and voltages with OPP support.
106 106
107config 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
107config ARM_RK3399_DMC_DEVFREQ 117config ARM_RK3399_DMC_DEVFREQ
108 tristate "ARM RK3399 DMC DEVFREQ Driver" 118 tristate "ARM RK3399 DMC DEVFREQ Driver"
109 depends on ARCH_ROCKCHIP 119 depends on ARCH_ROCKCHIP
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 47e5aeeebfd1..338ae8440db6 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
11obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o 11obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
12obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o 12obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
13obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o 13obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
14obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
14 15
15# DEVFREQ Event Drivers 16# DEVFREQ Event Drivers
16obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ 17obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
diff --git a/drivers/devfreq/tegra20-devfreq.c b/drivers/devfreq/tegra20-devfreq.c
new file mode 100644
index 000000000000..ff82bac9ee4e
--- /dev/null
+++ b/drivers/devfreq/tegra20-devfreq.c
@@ -0,0 +1,212 @@
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
31struct tegra_devfreq {
32 struct devfreq *devfreq;
33 struct clk *emc_clock;
34 void __iomem *regs;
35};
36
37static 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
63restore_min_rate:
64 clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
65
66 return err;
67}
68
69static 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
94static 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
100static 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
122static 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
184remove_opps:
185 dev_pm_opp_remove_all_dynamic(&pdev->dev);
186
187 return err;
188}
189
190static 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
200static struct platform_driver tegra_devfreq_driver = {
201 .probe = tegra_devfreq_probe,
202 .remove = tegra_devfreq_remove,
203 .driver = {
204 .name = "tegra20-devfreq",
205 },
206};
207module_platform_driver(tegra_devfreq_driver);
208
209MODULE_ALIAS("platform:tegra20-devfreq");
210MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
211MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver");
212MODULE_LICENSE("GPL v2");