1 /* 2 * CPU frequency scaling for DaVinci 3 * 4 * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ 5 * 6 * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows: 7 * 8 * Copyright (C) 2005 Nokia Corporation 9 * Written by Tony Lindgren <tony@atomide.com> 10 * 11 * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King 12 * 13 * Copyright (C) 2007-2008 Texas Instruments, Inc. 14 * Updated to support OMAP3 15 * Rajendra Nayak <rnayak@ti.com> 16 * 17 * This program is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License version 2 as 19 * published by the Free Software Foundation. 20 */ 21 #include <linux/types.h> 22 #include <linux/cpufreq.h> 23 #include <linux/init.h> 24 #include <linux/err.h> 25 #include <linux/clk.h> 26 #include <linux/platform_device.h> 27 #include <linux/export.h> 28 29 #include <mach/hardware.h> 30 #include <mach/cpufreq.h> 31 #include <mach/common.h> 32 33 struct davinci_cpufreq { 34 struct device *dev; 35 struct clk *armclk; 36 struct clk *asyncclk; 37 unsigned long asyncrate; 38 }; 39 static struct davinci_cpufreq cpufreq; 40 41 static int davinci_verify_speed(struct cpufreq_policy *policy) 42 { 43 struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data; 44 struct cpufreq_frequency_table *freq_table = pdata->freq_table; 45 struct clk *armclk = cpufreq.armclk; 46 47 if (freq_table) 48 return cpufreq_frequency_table_verify(policy, freq_table); 49 50 if (policy->cpu) 51 return -EINVAL; 52 53 cpufreq_verify_within_cpu_limits(policy); 54 policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000; 55 policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000; 56 cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, 57 policy->cpuinfo.max_freq); 58 return 0; 59 } 60 61 static unsigned int davinci_getspeed(unsigned int cpu) 62 { 63 if (cpu) 64 return 0; 65 66 return clk_get_rate(cpufreq.armclk) / 1000; 67 } 68 69 static int davinci_target(struct cpufreq_policy *policy, unsigned int idx) 70 { 71 struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data; 72 struct clk *armclk = cpufreq.armclk; 73 unsigned int old_freq, new_freq; 74 int ret = 0; 75 76 old_freq = davinci_getspeed(0); 77 new_freq = pdata->freq_table[idx].frequency; 78 79 /* if moving to higher frequency, up the voltage beforehand */ 80 if (pdata->set_voltage && new_freq > old_freq) { 81 ret = pdata->set_voltage(idx); 82 if (ret) 83 return ret; 84 } 85 86 ret = clk_set_rate(armclk, idx); 87 if (ret) 88 return ret; 89 90 if (cpufreq.asyncclk) { 91 ret = clk_set_rate(cpufreq.asyncclk, cpufreq.asyncrate); 92 if (ret) 93 return ret; 94 } 95 96 /* if moving to lower freq, lower the voltage after lowering freq */ 97 if (pdata->set_voltage && new_freq < old_freq) 98 pdata->set_voltage(idx); 99 100 return 0; 101 } 102 103 static int davinci_cpu_init(struct cpufreq_policy *policy) 104 { 105 int result = 0; 106 struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data; 107 struct cpufreq_frequency_table *freq_table = pdata->freq_table; 108 109 if (policy->cpu != 0) 110 return -EINVAL; 111 112 /* Finish platform specific initialization */ 113 if (pdata->init) { 114 result = pdata->init(); 115 if (result) 116 return result; 117 } 118 119 /* 120 * Time measurement across the target() function yields ~1500-1800us 121 * time taken with no drivers on notification list. 122 * Setting the latency to 2000 us to accommodate addition of drivers 123 * to pre/post change notification list. 124 */ 125 return cpufreq_generic_init(policy, freq_table, 2000 * 1000); 126 } 127 128 static struct cpufreq_driver davinci_driver = { 129 .flags = CPUFREQ_STICKY, 130 .verify = davinci_verify_speed, 131 .target_index = davinci_target, 132 .get = davinci_getspeed, 133 .init = davinci_cpu_init, 134 .exit = cpufreq_generic_exit, 135 .name = "davinci", 136 .attr = cpufreq_generic_attr, 137 }; 138 139 static int __init davinci_cpufreq_probe(struct platform_device *pdev) 140 { 141 struct davinci_cpufreq_config *pdata = pdev->dev.platform_data; 142 struct clk *asyncclk; 143 144 if (!pdata) 145 return -EINVAL; 146 if (!pdata->freq_table) 147 return -EINVAL; 148 149 cpufreq.dev = &pdev->dev; 150 151 cpufreq.armclk = clk_get(NULL, "arm"); 152 if (IS_ERR(cpufreq.armclk)) { 153 dev_err(cpufreq.dev, "Unable to get ARM clock\n"); 154 return PTR_ERR(cpufreq.armclk); 155 } 156 157 asyncclk = clk_get(cpufreq.dev, "async"); 158 if (!IS_ERR(asyncclk)) { 159 cpufreq.asyncclk = asyncclk; 160 cpufreq.asyncrate = clk_get_rate(asyncclk); 161 } 162 163 return cpufreq_register_driver(&davinci_driver); 164 } 165 166 static int __exit davinci_cpufreq_remove(struct platform_device *pdev) 167 { 168 clk_put(cpufreq.armclk); 169 170 if (cpufreq.asyncclk) 171 clk_put(cpufreq.asyncclk); 172 173 return cpufreq_unregister_driver(&davinci_driver); 174 } 175 176 static struct platform_driver davinci_cpufreq_driver = { 177 .driver = { 178 .name = "cpufreq-davinci", 179 .owner = THIS_MODULE, 180 }, 181 .remove = __exit_p(davinci_cpufreq_remove), 182 }; 183 184 int __init davinci_cpufreq_init(void) 185 { 186 return platform_driver_probe(&davinci_cpufreq_driver, 187 davinci_cpufreq_probe); 188 } 189 190