hc
2024-05-10 23fa18eaa71266feff7ba8d83022d9e1cc83c65a
kernel/drivers/cpufreq/tegra20-cpufreq.c
....@@ -1,222 +1,102 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * Copyright (C) 2010 Google, Inc.
34 *
45 * Author:
56 * Colin Cross <ccross@google.com>
67 * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
7
- *
8
- * This software is licensed under the terms of the GNU General Public
9
- * License version 2, as published by the Free Software Foundation, and
10
- * may be copied, distributed, and modified under those terms.
11
- *
12
- * This program is distributed in the hope that it will be useful,
13
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- * GNU General Public License for more details.
16
- *
178 */
189
19
-#include <linux/clk.h>
20
-#include <linux/cpufreq.h>
10
+#include <linux/bits.h>
11
+#include <linux/cpu.h>
2112 #include <linux/err.h>
2213 #include <linux/init.h>
2314 #include <linux/module.h>
15
+#include <linux/of_device.h>
2416 #include <linux/platform_device.h>
17
+#include <linux/pm_opp.h>
2518 #include <linux/types.h>
2619
27
-static struct cpufreq_frequency_table freq_table[] = {
28
- { .frequency = 216000 },
29
- { .frequency = 312000 },
30
- { .frequency = 456000 },
31
- { .frequency = 608000 },
32
- { .frequency = 760000 },
33
- { .frequency = 816000 },
34
- { .frequency = 912000 },
35
- { .frequency = 1000000 },
36
- { .frequency = CPUFREQ_TABLE_END },
37
-};
20
+#include <soc/tegra/common.h>
21
+#include <soc/tegra/fuse.h>
3822
39
-struct tegra20_cpufreq {
40
- struct device *dev;
41
- struct cpufreq_driver driver;
42
- struct clk *cpu_clk;
43
- struct clk *pll_x_clk;
44
- struct clk *pll_p_clk;
45
- bool pll_x_prepared;
46
-};
47
-
48
-static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy,
49
- unsigned int index)
23
+static bool cpu0_node_has_opp_v2_prop(void)
5024 {
51
- struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
52
- unsigned int ifreq = clk_get_rate(cpufreq->pll_p_clk) / 1000;
25
+ struct device_node *np = of_cpu_device_node_get(0);
26
+ bool ret = false;
5327
54
- /*
55
- * Don't switch to intermediate freq if:
56
- * - we are already at it, i.e. policy->cur == ifreq
57
- * - index corresponds to ifreq
58
- */
59
- if (freq_table[index].frequency == ifreq || policy->cur == ifreq)
60
- return 0;
28
+ if (of_get_property(np, "operating-points-v2", NULL))
29
+ ret = true;
6130
62
- return ifreq;
63
-}
64
-
65
-static int tegra_target_intermediate(struct cpufreq_policy *policy,
66
- unsigned int index)
67
-{
68
- struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
69
- int ret;
70
-
71
- /*
72
- * Take an extra reference to the main pll so it doesn't turn
73
- * off when we move the cpu off of it as enabling it again while we
74
- * switch to it from tegra_target() would take additional time.
75
- *
76
- * When target-freq is equal to intermediate freq we don't need to
77
- * switch to an intermediate freq and so this routine isn't called.
78
- * Also, we wouldn't be using pll_x anymore and must not take extra
79
- * reference to it, as it can be disabled now to save some power.
80
- */
81
- clk_prepare_enable(cpufreq->pll_x_clk);
82
-
83
- ret = clk_set_parent(cpufreq->cpu_clk, cpufreq->pll_p_clk);
84
- if (ret)
85
- clk_disable_unprepare(cpufreq->pll_x_clk);
86
- else
87
- cpufreq->pll_x_prepared = true;
88
-
31
+ of_node_put(np);
8932 return ret;
90
-}
91
-
92
-static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
93
-{
94
- struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
95
- unsigned long rate = freq_table[index].frequency;
96
- unsigned int ifreq = clk_get_rate(cpufreq->pll_p_clk) / 1000;
97
- int ret;
98
-
99
- /*
100
- * target freq == pll_p, don't need to take extra reference to pll_x_clk
101
- * as it isn't used anymore.
102
- */
103
- if (rate == ifreq)
104
- return clk_set_parent(cpufreq->cpu_clk, cpufreq->pll_p_clk);
105
-
106
- ret = clk_set_rate(cpufreq->pll_x_clk, rate * 1000);
107
- /* Restore to earlier frequency on error, i.e. pll_x */
108
- if (ret)
109
- dev_err(cpufreq->dev, "Failed to change pll_x to %lu\n", rate);
110
-
111
- ret = clk_set_parent(cpufreq->cpu_clk, cpufreq->pll_x_clk);
112
- /* This shouldn't fail while changing or restoring */
113
- WARN_ON(ret);
114
-
115
- /*
116
- * Drop count to pll_x clock only if we switched to intermediate freq
117
- * earlier while transitioning to a target frequency.
118
- */
119
- if (cpufreq->pll_x_prepared) {
120
- clk_disable_unprepare(cpufreq->pll_x_clk);
121
- cpufreq->pll_x_prepared = false;
122
- }
123
-
124
- return ret;
125
-}
126
-
127
-static int tegra_cpu_init(struct cpufreq_policy *policy)
128
-{
129
- struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
130
- int ret;
131
-
132
- clk_prepare_enable(cpufreq->cpu_clk);
133
-
134
- /* FIXME: what's the actual transition time? */
135
- ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
136
- if (ret) {
137
- clk_disable_unprepare(cpufreq->cpu_clk);
138
- return ret;
139
- }
140
-
141
- policy->clk = cpufreq->cpu_clk;
142
- policy->suspend_freq = freq_table[0].frequency;
143
- return 0;
144
-}
145
-
146
-static int tegra_cpu_exit(struct cpufreq_policy *policy)
147
-{
148
- struct tegra20_cpufreq *cpufreq = cpufreq_get_driver_data();
149
-
150
- clk_disable_unprepare(cpufreq->cpu_clk);
151
- return 0;
15233 }
15334
15435 static int tegra20_cpufreq_probe(struct platform_device *pdev)
15536 {
156
- struct tegra20_cpufreq *cpufreq;
37
+ struct platform_device *cpufreq_dt;
38
+ struct opp_table *opp_table;
39
+ struct device *cpu_dev;
40
+ u32 versions[2];
15741 int err;
15842
159
- cpufreq = devm_kzalloc(&pdev->dev, sizeof(*cpufreq), GFP_KERNEL);
160
- if (!cpufreq)
161
- return -ENOMEM;
162
-
163
- cpufreq->cpu_clk = clk_get_sys(NULL, "cclk");
164
- if (IS_ERR(cpufreq->cpu_clk))
165
- return PTR_ERR(cpufreq->cpu_clk);
166
-
167
- cpufreq->pll_x_clk = clk_get_sys(NULL, "pll_x");
168
- if (IS_ERR(cpufreq->pll_x_clk)) {
169
- err = PTR_ERR(cpufreq->pll_x_clk);
170
- goto put_cpu;
43
+ if (!cpu0_node_has_opp_v2_prop()) {
44
+ dev_err(&pdev->dev, "operating points not found\n");
45
+ dev_err(&pdev->dev, "please update your device tree\n");
46
+ return -ENODEV;
17147 }
17248
173
- cpufreq->pll_p_clk = clk_get_sys(NULL, "pll_p");
174
- if (IS_ERR(cpufreq->pll_p_clk)) {
175
- err = PTR_ERR(cpufreq->pll_p_clk);
176
- goto put_pll_x;
49
+ if (of_machine_is_compatible("nvidia,tegra20")) {
50
+ versions[0] = BIT(tegra_sku_info.cpu_process_id);
51
+ versions[1] = BIT(tegra_sku_info.soc_speedo_id);
52
+ } else {
53
+ versions[0] = BIT(tegra_sku_info.cpu_process_id);
54
+ versions[1] = BIT(tegra_sku_info.cpu_speedo_id);
17755 }
17856
179
- cpufreq->dev = &pdev->dev;
180
- cpufreq->driver.get = cpufreq_generic_get;
181
- cpufreq->driver.attr = cpufreq_generic_attr;
182
- cpufreq->driver.init = tegra_cpu_init;
183
- cpufreq->driver.exit = tegra_cpu_exit;
184
- cpufreq->driver.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK;
185
- cpufreq->driver.verify = cpufreq_generic_frequency_table_verify;
186
- cpufreq->driver.suspend = cpufreq_generic_suspend;
187
- cpufreq->driver.driver_data = cpufreq;
188
- cpufreq->driver.target_index = tegra_target;
189
- cpufreq->driver.get_intermediate = tegra_get_intermediate;
190
- cpufreq->driver.target_intermediate = tegra_target_intermediate;
191
- snprintf(cpufreq->driver.name, CPUFREQ_NAME_LEN, "tegra");
57
+ dev_info(&pdev->dev, "hardware version 0x%x 0x%x\n",
58
+ versions[0], versions[1]);
19259
193
- err = cpufreq_register_driver(&cpufreq->driver);
194
- if (err)
195
- goto put_pll_p;
60
+ cpu_dev = get_cpu_device(0);
61
+ if (WARN_ON(!cpu_dev))
62
+ return -ENODEV;
19663
197
- platform_set_drvdata(pdev, cpufreq);
64
+ opp_table = dev_pm_opp_set_supported_hw(cpu_dev, versions, 2);
65
+ err = PTR_ERR_OR_ZERO(opp_table);
66
+ if (err) {
67
+ dev_err(&pdev->dev, "failed to set supported hw: %d\n", err);
68
+ return err;
69
+ }
70
+
71
+ cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
72
+ err = PTR_ERR_OR_ZERO(cpufreq_dt);
73
+ if (err) {
74
+ dev_err(&pdev->dev,
75
+ "failed to create cpufreq-dt device: %d\n", err);
76
+ goto err_put_supported_hw;
77
+ }
78
+
79
+ platform_set_drvdata(pdev, cpufreq_dt);
19880
19981 return 0;
20082
201
-put_pll_p:
202
- clk_put(cpufreq->pll_p_clk);
203
-put_pll_x:
204
- clk_put(cpufreq->pll_x_clk);
205
-put_cpu:
206
- clk_put(cpufreq->cpu_clk);
83
+err_put_supported_hw:
84
+ dev_pm_opp_put_supported_hw(opp_table);
20785
20886 return err;
20987 }
21088
21189 static int tegra20_cpufreq_remove(struct platform_device *pdev)
21290 {
213
- struct tegra20_cpufreq *cpufreq = platform_get_drvdata(pdev);
91
+ struct platform_device *cpufreq_dt;
92
+ struct opp_table *opp_table;
21493
215
- cpufreq_unregister_driver(&cpufreq->driver);
94
+ cpufreq_dt = platform_get_drvdata(pdev);
95
+ platform_device_unregister(cpufreq_dt);
21696
217
- clk_put(cpufreq->pll_p_clk);
218
- clk_put(cpufreq->pll_x_clk);
219
- clk_put(cpufreq->cpu_clk);
97
+ opp_table = dev_pm_opp_get_opp_table(get_cpu_device(0));
98
+ dev_pm_opp_put_supported_hw(opp_table);
99
+ dev_pm_opp_put_opp_table(opp_table);
220100
221101 return 0;
222102 }