0001
0002
0003
0004
0005
0006 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0007
0008 #include <linux/clk.h>
0009 #include <linux/cpufreq.h>
0010 #include <linux/err.h>
0011 #include <linux/init.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/of_device.h>
0015 #include <linux/of.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/pm_opp.h>
0018 #include <linux/types.h>
0019
0020 struct tegra124_cpufreq_priv {
0021 struct clk *cpu_clk;
0022 struct clk *pllp_clk;
0023 struct clk *pllx_clk;
0024 struct clk *dfll_clk;
0025 struct platform_device *cpufreq_dt_pdev;
0026 };
0027
0028 static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv *priv)
0029 {
0030 struct clk *orig_parent;
0031 int ret;
0032
0033 ret = clk_set_rate(priv->dfll_clk, clk_get_rate(priv->cpu_clk));
0034 if (ret)
0035 return ret;
0036
0037 orig_parent = clk_get_parent(priv->cpu_clk);
0038 clk_set_parent(priv->cpu_clk, priv->pllp_clk);
0039
0040 ret = clk_prepare_enable(priv->dfll_clk);
0041 if (ret)
0042 goto out;
0043
0044 clk_set_parent(priv->cpu_clk, priv->dfll_clk);
0045
0046 return 0;
0047
0048 out:
0049 clk_set_parent(priv->cpu_clk, orig_parent);
0050
0051 return ret;
0052 }
0053
0054 static int tegra124_cpufreq_probe(struct platform_device *pdev)
0055 {
0056 struct tegra124_cpufreq_priv *priv;
0057 struct device_node *np;
0058 struct device *cpu_dev;
0059 struct platform_device_info cpufreq_dt_devinfo = {};
0060 int ret;
0061
0062 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0063 if (!priv)
0064 return -ENOMEM;
0065
0066 cpu_dev = get_cpu_device(0);
0067 if (!cpu_dev)
0068 return -ENODEV;
0069
0070 np = of_cpu_device_node_get(0);
0071 if (!np)
0072 return -ENODEV;
0073
0074 priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
0075 if (IS_ERR(priv->cpu_clk)) {
0076 ret = PTR_ERR(priv->cpu_clk);
0077 goto out_put_np;
0078 }
0079
0080 priv->dfll_clk = of_clk_get_by_name(np, "dfll");
0081 if (IS_ERR(priv->dfll_clk)) {
0082 ret = PTR_ERR(priv->dfll_clk);
0083 goto out_put_cpu_clk;
0084 }
0085
0086 priv->pllx_clk = of_clk_get_by_name(np, "pll_x");
0087 if (IS_ERR(priv->pllx_clk)) {
0088 ret = PTR_ERR(priv->pllx_clk);
0089 goto out_put_dfll_clk;
0090 }
0091
0092 priv->pllp_clk = of_clk_get_by_name(np, "pll_p");
0093 if (IS_ERR(priv->pllp_clk)) {
0094 ret = PTR_ERR(priv->pllp_clk);
0095 goto out_put_pllx_clk;
0096 }
0097
0098 ret = tegra124_cpu_switch_to_dfll(priv);
0099 if (ret)
0100 goto out_put_pllp_clk;
0101
0102 cpufreq_dt_devinfo.name = "cpufreq-dt";
0103 cpufreq_dt_devinfo.parent = &pdev->dev;
0104
0105 priv->cpufreq_dt_pdev =
0106 platform_device_register_full(&cpufreq_dt_devinfo);
0107 if (IS_ERR(priv->cpufreq_dt_pdev)) {
0108 ret = PTR_ERR(priv->cpufreq_dt_pdev);
0109 goto out_put_pllp_clk;
0110 }
0111
0112 platform_set_drvdata(pdev, priv);
0113
0114 of_node_put(np);
0115
0116 return 0;
0117
0118 out_put_pllp_clk:
0119 clk_put(priv->pllp_clk);
0120 out_put_pllx_clk:
0121 clk_put(priv->pllx_clk);
0122 out_put_dfll_clk:
0123 clk_put(priv->dfll_clk);
0124 out_put_cpu_clk:
0125 clk_put(priv->cpu_clk);
0126 out_put_np:
0127 of_node_put(np);
0128
0129 return ret;
0130 }
0131
0132 static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
0133 {
0134 struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
0135 int err;
0136
0137
0138
0139
0140
0141
0142 err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
0143 if (err < 0) {
0144 dev_err(dev, "failed to reparent to PLLP: %d\n", err);
0145 return err;
0146 }
0147
0148 clk_disable_unprepare(priv->dfll_clk);
0149
0150 return 0;
0151 }
0152
0153 static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
0154 {
0155 struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
0156 int err;
0157
0158
0159
0160
0161
0162 err = clk_prepare_enable(priv->dfll_clk);
0163 if (err < 0) {
0164 dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
0165 goto disable_cpufreq;
0166 }
0167
0168 err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
0169 if (err < 0) {
0170 dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
0171 goto disable_dfll;
0172 }
0173
0174 return 0;
0175
0176 disable_dfll:
0177 clk_disable_unprepare(priv->dfll_clk);
0178 disable_cpufreq:
0179 disable_cpufreq();
0180
0181 return err;
0182 }
0183
0184 static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
0185 SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
0186 tegra124_cpufreq_resume)
0187 };
0188
0189 static struct platform_driver tegra124_cpufreq_platdrv = {
0190 .driver.name = "cpufreq-tegra124",
0191 .driver.pm = &tegra124_cpufreq_pm_ops,
0192 .probe = tegra124_cpufreq_probe,
0193 };
0194
0195 static int __init tegra_cpufreq_init(void)
0196 {
0197 int ret;
0198 struct platform_device *pdev;
0199
0200 if (!(of_machine_is_compatible("nvidia,tegra124") ||
0201 of_machine_is_compatible("nvidia,tegra210")))
0202 return -ENODEV;
0203
0204
0205
0206
0207
0208 ret = platform_driver_register(&tegra124_cpufreq_platdrv);
0209 if (ret)
0210 return ret;
0211
0212 pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0);
0213 if (IS_ERR(pdev)) {
0214 platform_driver_unregister(&tegra124_cpufreq_platdrv);
0215 return PTR_ERR(pdev);
0216 }
0217
0218 return 0;
0219 }
0220 module_init(tegra_cpufreq_init);
0221
0222 MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
0223 MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
0224 MODULE_LICENSE("GPL v2");