Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * CPU frequency scaling for DaVinci
0004  *
0005  * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
0006  *
0007  * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
0008  *
0009  *  Copyright (C) 2005 Nokia Corporation
0010  *  Written by Tony Lindgren <tony@atomide.com>
0011  *
0012  *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
0013  *
0014  * Copyright (C) 2007-2008 Texas Instruments, Inc.
0015  * Updated to support OMAP3
0016  * Rajendra Nayak <rnayak@ti.com>
0017  */
0018 #include <linux/types.h>
0019 #include <linux/cpufreq.h>
0020 #include <linux/init.h>
0021 #include <linux/err.h>
0022 #include <linux/clk.h>
0023 #include <linux/platform_data/davinci-cpufreq.h>
0024 #include <linux/platform_device.h>
0025 #include <linux/export.h>
0026 
0027 struct davinci_cpufreq {
0028     struct device *dev;
0029     struct clk *armclk;
0030     struct clk *asyncclk;
0031     unsigned long asyncrate;
0032 };
0033 static struct davinci_cpufreq cpufreq;
0034 
0035 static int davinci_target(struct cpufreq_policy *policy, unsigned int idx)
0036 {
0037     struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
0038     struct clk *armclk = cpufreq.armclk;
0039     unsigned int old_freq, new_freq;
0040     int ret = 0;
0041 
0042     old_freq = policy->cur;
0043     new_freq = pdata->freq_table[idx].frequency;
0044 
0045     /* if moving to higher frequency, up the voltage beforehand */
0046     if (pdata->set_voltage && new_freq > old_freq) {
0047         ret = pdata->set_voltage(idx);
0048         if (ret)
0049             return ret;
0050     }
0051 
0052     ret = clk_set_rate(armclk, new_freq * 1000);
0053     if (ret)
0054         return ret;
0055 
0056     if (cpufreq.asyncclk) {
0057         ret = clk_set_rate(cpufreq.asyncclk, cpufreq.asyncrate);
0058         if (ret)
0059             return ret;
0060     }
0061 
0062     /* if moving to lower freq, lower the voltage after lowering freq */
0063     if (pdata->set_voltage && new_freq < old_freq)
0064         pdata->set_voltage(idx);
0065 
0066     return 0;
0067 }
0068 
0069 static int davinci_cpu_init(struct cpufreq_policy *policy)
0070 {
0071     int result = 0;
0072     struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
0073     struct cpufreq_frequency_table *freq_table = pdata->freq_table;
0074 
0075     if (policy->cpu != 0)
0076         return -EINVAL;
0077 
0078     /* Finish platform specific initialization */
0079     if (pdata->init) {
0080         result = pdata->init();
0081         if (result)
0082             return result;
0083     }
0084 
0085     policy->clk = cpufreq.armclk;
0086 
0087     /*
0088      * Time measurement across the target() function yields ~1500-1800us
0089      * time taken with no drivers on notification list.
0090      * Setting the latency to 2000 us to accommodate addition of drivers
0091      * to pre/post change notification list.
0092      */
0093     cpufreq_generic_init(policy, freq_table, 2000 * 1000);
0094     return 0;
0095 }
0096 
0097 static struct cpufreq_driver davinci_driver = {
0098     .flags      = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
0099     .verify     = cpufreq_generic_frequency_table_verify,
0100     .target_index   = davinci_target,
0101     .get        = cpufreq_generic_get,
0102     .init       = davinci_cpu_init,
0103     .name       = "davinci",
0104     .attr       = cpufreq_generic_attr,
0105 };
0106 
0107 static int __init davinci_cpufreq_probe(struct platform_device *pdev)
0108 {
0109     struct davinci_cpufreq_config *pdata = pdev->dev.platform_data;
0110     struct clk *asyncclk;
0111 
0112     if (!pdata)
0113         return -EINVAL;
0114     if (!pdata->freq_table)
0115         return -EINVAL;
0116 
0117     cpufreq.dev = &pdev->dev;
0118 
0119     cpufreq.armclk = clk_get(NULL, "arm");
0120     if (IS_ERR(cpufreq.armclk)) {
0121         dev_err(cpufreq.dev, "Unable to get ARM clock\n");
0122         return PTR_ERR(cpufreq.armclk);
0123     }
0124 
0125     asyncclk = clk_get(cpufreq.dev, "async");
0126     if (!IS_ERR(asyncclk)) {
0127         cpufreq.asyncclk = asyncclk;
0128         cpufreq.asyncrate = clk_get_rate(asyncclk);
0129     }
0130 
0131     return cpufreq_register_driver(&davinci_driver);
0132 }
0133 
0134 static int __exit davinci_cpufreq_remove(struct platform_device *pdev)
0135 {
0136     clk_put(cpufreq.armclk);
0137 
0138     if (cpufreq.asyncclk)
0139         clk_put(cpufreq.asyncclk);
0140 
0141     return cpufreq_unregister_driver(&davinci_driver);
0142 }
0143 
0144 static struct platform_driver davinci_cpufreq_driver = {
0145     .driver = {
0146         .name    = "cpufreq-davinci",
0147     },
0148     .remove = __exit_p(davinci_cpufreq_remove),
0149 };
0150 
0151 int __init davinci_cpufreq_init(void)
0152 {
0153     return platform_driver_probe(&davinci_cpufreq_driver,
0154                             davinci_cpufreq_probe);
0155 }
0156