Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Tegra 124 cpufreq driver
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      * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
0139      * use during suspend and resume. So, switch the CPU clock source
0140      * to PLLP and disable DFLL.
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      * Warmboot code powers up the CPU with PLLP clock source.
0160      * Enable DFLL clock and switch CPU clock source back to DFLL.
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      * Platform driver+device required for handling EPROBE_DEFER with
0206      * the regulator and the DFLL clock
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");