Merge tag 'v4.3-rc2' into topic/drm-misc
[firefly-linux-kernel-4.4.55.git] / drivers / cpufreq / tegra124-cpufreq.c
1 /*
2  * Tegra 124 cpufreq driver
3  *
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.
7  *
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.
12  */
13
14 #define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
15
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>
23 #include <linux/of.h>
24 #include <linux/platform_device.h>
25 #include <linux/pm_opp.h>
26 #include <linux/regulator/consumer.h>
27 #include <linux/types.h>
28
29 struct tegra124_cpufreq_priv {
30         struct regulator *vdd_cpu_reg;
31         struct clk *cpu_clk;
32         struct clk *pllp_clk;
33         struct clk *pllx_clk;
34         struct clk *dfll_clk;
35         struct platform_device *cpufreq_dt_pdev;
36 };
37
38 static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv *priv)
39 {
40         struct clk *orig_parent;
41         int ret;
42
43         ret = clk_set_rate(priv->dfll_clk, clk_get_rate(priv->cpu_clk));
44         if (ret)
45                 return ret;
46
47         orig_parent = clk_get_parent(priv->cpu_clk);
48         clk_set_parent(priv->cpu_clk, priv->pllp_clk);
49
50         ret = clk_prepare_enable(priv->dfll_clk);
51         if (ret)
52                 goto out;
53
54         clk_set_parent(priv->cpu_clk, priv->dfll_clk);
55
56         return 0;
57
58 out:
59         clk_set_parent(priv->cpu_clk, orig_parent);
60
61         return ret;
62 }
63
64 static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
65 {
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);
70 }
71
72 static struct cpufreq_dt_platform_data cpufreq_dt_pd = {
73         .independent_clocks = false,
74 };
75
76 static int tegra124_cpufreq_probe(struct platform_device *pdev)
77 {
78         struct tegra124_cpufreq_priv *priv;
79         struct device_node *np;
80         struct device *cpu_dev;
81         struct platform_device_info cpufreq_dt_devinfo = {};
82         int ret;
83
84         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
85         if (!priv)
86                 return -ENOMEM;
87
88         cpu_dev = get_cpu_device(0);
89         if (!cpu_dev)
90                 return -ENODEV;
91
92         np = of_cpu_device_node_get(0);
93         if (!np)
94                 return -ENODEV;
95
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);
99                 goto out_put_np;
100         }
101
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;
106         }
107
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;
112         }
113
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;
118         }
119
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;
124         }
125
126         ret = tegra124_cpu_switch_to_dfll(priv);
127         if (ret)
128                 goto out_put_pllp_clk;
129
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);
134
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;
140         }
141
142         platform_set_drvdata(pdev, priv);
143
144         return 0;
145
146 out_switch_to_pllx:
147         tegra124_cpu_switch_to_pllx(priv);
148 out_put_pllp_clk:
149         clk_put(priv->pllp_clk);
150 out_put_pllx_clk:
151         clk_put(priv->pllx_clk);
152 out_put_dfll_clk:
153         clk_put(priv->dfll_clk);
154 out_put_cpu_clk:
155         clk_put(priv->cpu_clk);
156 out_put_vdd_cpu_reg:
157         regulator_put(priv->vdd_cpu_reg);
158 out_put_np:
159         of_node_put(np);
160
161         return ret;
162 }
163
164 static int tegra124_cpufreq_remove(struct platform_device *pdev)
165 {
166         struct tegra124_cpufreq_priv *priv = platform_get_drvdata(pdev);
167
168         platform_device_unregister(priv->cpufreq_dt_pdev);
169         tegra124_cpu_switch_to_pllx(priv);
170
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);
176
177         return 0;
178 }
179
180 static struct platform_driver tegra124_cpufreq_platdrv = {
181         .driver.name    = "cpufreq-tegra124",
182         .probe          = tegra124_cpufreq_probe,
183         .remove         = tegra124_cpufreq_remove,
184 };
185
186 static int __init tegra_cpufreq_init(void)
187 {
188         int ret;
189         struct platform_device *pdev;
190
191         if (!of_machine_is_compatible("nvidia,tegra124"))
192                 return -ENODEV;
193
194         /*
195          * Platform driver+device required for handling EPROBE_DEFER with
196          * the regulator and the DFLL clock
197          */
198         ret = platform_driver_register(&tegra124_cpufreq_platdrv);
199         if (ret)
200                 return ret;
201
202         pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0);
203         if (IS_ERR(pdev)) {
204                 platform_driver_unregister(&tegra124_cpufreq_platdrv);
205                 return PTR_ERR(pdev);
206         }
207
208         return 0;
209 }
210 module_init(tegra_cpufreq_init);
211
212 MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
213 MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
214 MODULE_LICENSE("GPL v2");