Merge branch 'am5-sdio-fixes' into fixes
[linux-2.6-microblaze.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/err.h>
18 #include <linux/init.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/of_device.h>
22 #include <linux/of.h>
23 #include <linux/platform_device.h>
24 #include <linux/pm_opp.h>
25 #include <linux/types.h>
26
27 struct tegra124_cpufreq_priv {
28         struct clk *cpu_clk;
29         struct clk *pllp_clk;
30         struct clk *pllx_clk;
31         struct clk *dfll_clk;
32         struct platform_device *cpufreq_dt_pdev;
33 };
34
35 static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv *priv)
36 {
37         struct clk *orig_parent;
38         int ret;
39
40         ret = clk_set_rate(priv->dfll_clk, clk_get_rate(priv->cpu_clk));
41         if (ret)
42                 return ret;
43
44         orig_parent = clk_get_parent(priv->cpu_clk);
45         clk_set_parent(priv->cpu_clk, priv->pllp_clk);
46
47         ret = clk_prepare_enable(priv->dfll_clk);
48         if (ret)
49                 goto out;
50
51         clk_set_parent(priv->cpu_clk, priv->dfll_clk);
52
53         return 0;
54
55 out:
56         clk_set_parent(priv->cpu_clk, orig_parent);
57
58         return ret;
59 }
60
61 static int tegra124_cpufreq_probe(struct platform_device *pdev)
62 {
63         struct tegra124_cpufreq_priv *priv;
64         struct device_node *np;
65         struct device *cpu_dev;
66         struct platform_device_info cpufreq_dt_devinfo = {};
67         int ret;
68
69         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
70         if (!priv)
71                 return -ENOMEM;
72
73         cpu_dev = get_cpu_device(0);
74         if (!cpu_dev)
75                 return -ENODEV;
76
77         np = of_cpu_device_node_get(0);
78         if (!np)
79                 return -ENODEV;
80
81         priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
82         if (IS_ERR(priv->cpu_clk)) {
83                 ret = PTR_ERR(priv->cpu_clk);
84                 goto out_put_np;
85         }
86
87         priv->dfll_clk = of_clk_get_by_name(np, "dfll");
88         if (IS_ERR(priv->dfll_clk)) {
89                 ret = PTR_ERR(priv->dfll_clk);
90                 goto out_put_cpu_clk;
91         }
92
93         priv->pllx_clk = of_clk_get_by_name(np, "pll_x");
94         if (IS_ERR(priv->pllx_clk)) {
95                 ret = PTR_ERR(priv->pllx_clk);
96                 goto out_put_dfll_clk;
97         }
98
99         priv->pllp_clk = of_clk_get_by_name(np, "pll_p");
100         if (IS_ERR(priv->pllp_clk)) {
101                 ret = PTR_ERR(priv->pllp_clk);
102                 goto out_put_pllx_clk;
103         }
104
105         ret = tegra124_cpu_switch_to_dfll(priv);
106         if (ret)
107                 goto out_put_pllp_clk;
108
109         cpufreq_dt_devinfo.name = "cpufreq-dt";
110         cpufreq_dt_devinfo.parent = &pdev->dev;
111
112         priv->cpufreq_dt_pdev =
113                 platform_device_register_full(&cpufreq_dt_devinfo);
114         if (IS_ERR(priv->cpufreq_dt_pdev)) {
115                 ret = PTR_ERR(priv->cpufreq_dt_pdev);
116                 goto out_put_pllp_clk;
117         }
118
119         platform_set_drvdata(pdev, priv);
120
121         of_node_put(np);
122
123         return 0;
124
125 out_put_pllp_clk:
126         clk_put(priv->pllp_clk);
127 out_put_pllx_clk:
128         clk_put(priv->pllx_clk);
129 out_put_dfll_clk:
130         clk_put(priv->dfll_clk);
131 out_put_cpu_clk:
132         clk_put(priv->cpu_clk);
133 out_put_np:
134         of_node_put(np);
135
136         return ret;
137 }
138
139 static struct platform_driver tegra124_cpufreq_platdrv = {
140         .driver.name    = "cpufreq-tegra124",
141         .probe          = tegra124_cpufreq_probe,
142 };
143
144 static int __init tegra_cpufreq_init(void)
145 {
146         int ret;
147         struct platform_device *pdev;
148
149         if (!(of_machine_is_compatible("nvidia,tegra124") ||
150                 of_machine_is_compatible("nvidia,tegra210")))
151                 return -ENODEV;
152
153         /*
154          * Platform driver+device required for handling EPROBE_DEFER with
155          * the regulator and the DFLL clock
156          */
157         ret = platform_driver_register(&tegra124_cpufreq_platdrv);
158         if (ret)
159                 return ret;
160
161         pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0);
162         if (IS_ERR(pdev)) {
163                 platform_driver_unregister(&tegra124_cpufreq_platdrv);
164                 return PTR_ERR(pdev);
165         }
166
167         return 0;
168 }
169 module_init(tegra_cpufreq_init);
170
171 MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
172 MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
173 MODULE_LICENSE("GPL v2");