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/cpufreq-dt.h>
18 #include <linux/err.h>
19 #include <linux/init.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 #include <linux/pm_opp.h>
26 #include <linux/regulator/consumer.h>
27 #include <linux/types.h>
29 struct tegra124_cpufreq_priv
{
30 struct regulator
*vdd_cpu_reg
;
35 struct platform_device
*cpufreq_dt_pdev
;
38 static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv
*priv
)
40 struct clk
*orig_parent
;
43 ret
= clk_set_rate(priv
->dfll_clk
, clk_get_rate(priv
->cpu_clk
));
47 orig_parent
= clk_get_parent(priv
->cpu_clk
);
48 clk_set_parent(priv
->cpu_clk
, priv
->pllp_clk
);
50 ret
= clk_prepare_enable(priv
->dfll_clk
);
54 clk_set_parent(priv
->cpu_clk
, priv
->dfll_clk
);
59 clk_set_parent(priv
->cpu_clk
, orig_parent
);
64 static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv
*priv
)
66 clk_set_parent(priv
->cpu_clk
, priv
->pllp_clk
);
67 clk_disable_unprepare(priv
->dfll_clk
);
68 regulator_sync_voltage(priv
->vdd_cpu_reg
);
69 clk_set_parent(priv
->cpu_clk
, priv
->pllx_clk
);
72 static struct cpufreq_dt_platform_data cpufreq_dt_pd
= {
73 .independent_clocks
= false,
76 static int tegra124_cpufreq_probe(struct platform_device
*pdev
)
78 struct tegra124_cpufreq_priv
*priv
;
79 struct device_node
*np
;
80 struct device
*cpu_dev
;
81 struct platform_device_info cpufreq_dt_devinfo
= {};
84 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
88 cpu_dev
= get_cpu_device(0);
92 np
= of_cpu_device_node_get(0);
96 priv
->vdd_cpu_reg
= regulator_get(cpu_dev
, "vdd-cpu");
97 if (IS_ERR(priv
->vdd_cpu_reg
)) {
98 ret
= PTR_ERR(priv
->vdd_cpu_reg
);
102 priv
->cpu_clk
= of_clk_get_by_name(np
, "cpu_g");
103 if (IS_ERR(priv
->cpu_clk
)) {
104 ret
= PTR_ERR(priv
->cpu_clk
);
105 goto out_put_vdd_cpu_reg
;
108 priv
->dfll_clk
= of_clk_get_by_name(np
, "dfll");
109 if (IS_ERR(priv
->dfll_clk
)) {
110 ret
= PTR_ERR(priv
->dfll_clk
);
111 goto out_put_cpu_clk
;
114 priv
->pllx_clk
= of_clk_get_by_name(np
, "pll_x");
115 if (IS_ERR(priv
->pllx_clk
)) {
116 ret
= PTR_ERR(priv
->pllx_clk
);
117 goto out_put_dfll_clk
;
120 priv
->pllp_clk
= of_clk_get_by_name(np
, "pll_p");
121 if (IS_ERR(priv
->pllp_clk
)) {
122 ret
= PTR_ERR(priv
->pllp_clk
);
123 goto out_put_pllx_clk
;
126 ret
= tegra124_cpu_switch_to_dfll(priv
);
128 goto out_put_pllp_clk
;
130 cpufreq_dt_devinfo
.name
= "cpufreq-dt";
131 cpufreq_dt_devinfo
.parent
= &pdev
->dev
;
132 cpufreq_dt_devinfo
.data
= &cpufreq_dt_pd
;
133 cpufreq_dt_devinfo
.size_data
= sizeof(cpufreq_dt_pd
);
135 priv
->cpufreq_dt_pdev
=
136 platform_device_register_full(&cpufreq_dt_devinfo
);
137 if (IS_ERR(priv
->cpufreq_dt_pdev
)) {
138 ret
= PTR_ERR(priv
->cpufreq_dt_pdev
);
139 goto out_switch_to_pllx
;
142 platform_set_drvdata(pdev
, priv
);
147 tegra124_cpu_switch_to_pllx(priv
);
149 clk_put(priv
->pllp_clk
);
151 clk_put(priv
->pllx_clk
);
153 clk_put(priv
->dfll_clk
);
155 clk_put(priv
->cpu_clk
);
157 regulator_put(priv
->vdd_cpu_reg
);
164 static int tegra124_cpufreq_remove(struct platform_device
*pdev
)
166 struct tegra124_cpufreq_priv
*priv
= platform_get_drvdata(pdev
);
168 platform_device_unregister(priv
->cpufreq_dt_pdev
);
169 tegra124_cpu_switch_to_pllx(priv
);
171 clk_put(priv
->pllp_clk
);
172 clk_put(priv
->pllx_clk
);
173 clk_put(priv
->dfll_clk
);
174 clk_put(priv
->cpu_clk
);
175 regulator_put(priv
->vdd_cpu_reg
);
180 static struct platform_driver tegra124_cpufreq_platdrv
= {
181 .driver
.name
= "cpufreq-tegra124",
182 .probe
= tegra124_cpufreq_probe
,
183 .remove
= tegra124_cpufreq_remove
,
186 static int __init
tegra_cpufreq_init(void)
189 struct platform_device
*pdev
;
191 if (!of_machine_is_compatible("nvidia,tegra124"))
195 * Platform driver+device required for handling EPROBE_DEFER with
196 * the regulator and the DFLL clock
198 ret
= platform_driver_register(&tegra124_cpufreq_platdrv
);
202 pdev
= platform_device_register_simple("cpufreq-tegra124", -1, NULL
, 0);
204 platform_driver_unregister(&tegra124_cpufreq_platdrv
);
205 return PTR_ERR(pdev
);
210 module_init(tegra_cpufreq_init
);
212 MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
213 MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
214 MODULE_LICENSE("GPL v2");