2 * Tegra 124 cpufreq driver
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16 #include <linux/clk.h>
17 #include <linux/err.h>
18 #include <linux/init.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/of_device.h>
23 #include <linux/platform_device.h>
24 #include <linux/pm_opp.h>
25 #include <linux/regulator/consumer.h>
26 #include <linux/types.h>
28 struct tegra124_cpufreq_priv
{
29 struct regulator
*vdd_cpu_reg
;
34 struct platform_device
*cpufreq_dt_pdev
;
37 static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv
*priv
)
39 struct clk
*orig_parent
;
42 ret
= clk_set_rate(priv
->dfll_clk
, clk_get_rate(priv
->cpu_clk
));
46 orig_parent
= clk_get_parent(priv
->cpu_clk
);
47 clk_set_parent(priv
->cpu_clk
, priv
->pllp_clk
);
49 ret
= clk_prepare_enable(priv
->dfll_clk
);
53 clk_set_parent(priv
->cpu_clk
, priv
->dfll_clk
);
58 clk_set_parent(priv
->cpu_clk
, orig_parent
);
63 static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv
*priv
)
65 clk_set_parent(priv
->cpu_clk
, priv
->pllp_clk
);
66 clk_disable_unprepare(priv
->dfll_clk
);
67 regulator_sync_voltage(priv
->vdd_cpu_reg
);
68 clk_set_parent(priv
->cpu_clk
, priv
->pllx_clk
);
71 static int tegra124_cpufreq_probe(struct platform_device
*pdev
)
73 struct tegra124_cpufreq_priv
*priv
;
74 struct device_node
*np
;
75 struct device
*cpu_dev
;
76 struct platform_device_info cpufreq_dt_devinfo
= {};
79 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
83 cpu_dev
= get_cpu_device(0);
87 np
= of_cpu_device_node_get(0);
91 priv
->vdd_cpu_reg
= regulator_get(cpu_dev
, "vdd-cpu");
92 if (IS_ERR(priv
->vdd_cpu_reg
)) {
93 ret
= PTR_ERR(priv
->vdd_cpu_reg
);
97 priv
->cpu_clk
= of_clk_get_by_name(np
, "cpu_g");
98 if (IS_ERR(priv
->cpu_clk
)) {
99 ret
= PTR_ERR(priv
->cpu_clk
);
100 goto out_put_vdd_cpu_reg
;
103 priv
->dfll_clk
= of_clk_get_by_name(np
, "dfll");
104 if (IS_ERR(priv
->dfll_clk
)) {
105 ret
= PTR_ERR(priv
->dfll_clk
);
106 goto out_put_cpu_clk
;
109 priv
->pllx_clk
= of_clk_get_by_name(np
, "pll_x");
110 if (IS_ERR(priv
->pllx_clk
)) {
111 ret
= PTR_ERR(priv
->pllx_clk
);
112 goto out_put_dfll_clk
;
115 priv
->pllp_clk
= of_clk_get_by_name(np
, "pll_p");
116 if (IS_ERR(priv
->pllp_clk
)) {
117 ret
= PTR_ERR(priv
->pllp_clk
);
118 goto out_put_pllx_clk
;
121 ret
= tegra124_cpu_switch_to_dfll(priv
);
123 goto out_put_pllp_clk
;
125 cpufreq_dt_devinfo
.name
= "cpufreq-dt";
126 cpufreq_dt_devinfo
.parent
= &pdev
->dev
;
128 priv
->cpufreq_dt_pdev
=
129 platform_device_register_full(&cpufreq_dt_devinfo
);
130 if (IS_ERR(priv
->cpufreq_dt_pdev
)) {
131 ret
= PTR_ERR(priv
->cpufreq_dt_pdev
);
132 goto out_switch_to_pllx
;
135 platform_set_drvdata(pdev
, priv
);
140 tegra124_cpu_switch_to_pllx(priv
);
142 clk_put(priv
->pllp_clk
);
144 clk_put(priv
->pllx_clk
);
146 clk_put(priv
->dfll_clk
);
148 clk_put(priv
->cpu_clk
);
150 regulator_put(priv
->vdd_cpu_reg
);
157 static int tegra124_cpufreq_remove(struct platform_device
*pdev
)
159 struct tegra124_cpufreq_priv
*priv
= platform_get_drvdata(pdev
);
161 platform_device_unregister(priv
->cpufreq_dt_pdev
);
162 tegra124_cpu_switch_to_pllx(priv
);
164 clk_put(priv
->pllp_clk
);
165 clk_put(priv
->pllx_clk
);
166 clk_put(priv
->dfll_clk
);
167 clk_put(priv
->cpu_clk
);
168 regulator_put(priv
->vdd_cpu_reg
);
173 static struct platform_driver tegra124_cpufreq_platdrv
= {
174 .driver
.name
= "cpufreq-tegra124",
175 .probe
= tegra124_cpufreq_probe
,
176 .remove
= tegra124_cpufreq_remove
,
179 static int __init
tegra_cpufreq_init(void)
182 struct platform_device
*pdev
;
184 if (!of_machine_is_compatible("nvidia,tegra124"))
188 * Platform driver+device required for handling EPROBE_DEFER with
189 * the regulator and the DFLL clock
191 ret
= platform_driver_register(&tegra124_cpufreq_platdrv
);
195 pdev
= platform_device_register_simple("cpufreq-tegra124", -1, NULL
, 0);
197 platform_driver_unregister(&tegra124_cpufreq_platdrv
);
198 return PTR_ERR(pdev
);
203 module_init(tegra_cpufreq_init
);
205 MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
206 MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
207 MODULE_LICENSE("GPL v2");