blob: 4dbe7efd86e5ca0be3595fa89eb8dea6c4ed0f35 [file] [log] [blame]
Shawn Guo95ceafd2012-09-06 07:09:11 +00001/*
2 * Copyright (C) 2012 Freescale Semiconductor, Inc.
3 *
4 * The OPP code in function cpu0_set_target() is reused from
5 * drivers/cpufreq/omap-cpufreq.c
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
14#include <linux/clk.h>
Sudeep KarkadaNageshae1825b22013-09-10 18:59:46 +010015#include <linux/cpu.h>
Shawn Guo95ceafd2012-09-06 07:09:11 +000016#include <linux/cpufreq.h>
17#include <linux/err.h>
18#include <linux/module.h>
19#include <linux/of.h>
Nishanth Menone4db1c72013-09-19 16:03:52 -050020#include <linux/pm_opp.h>
Shawn Guo5553f9e2013-01-30 14:27:49 +000021#include <linux/platform_device.h>
Shawn Guo95ceafd2012-09-06 07:09:11 +000022#include <linux/regulator/consumer.h>
23#include <linux/slab.h>
24
25static unsigned int transition_latency;
26static unsigned int voltage_tolerance; /* in percentage */
27
28static struct device *cpu_dev;
29static struct clk *cpu_clk;
30static struct regulator *cpu_reg;
31static struct cpufreq_frequency_table *freq_table;
32
Shawn Guo95ceafd2012-09-06 07:09:11 +000033static unsigned int cpu0_get_speed(unsigned int cpu)
34{
35 return clk_get_rate(cpu_clk) / 1000;
36}
37
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +053038static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index)
Shawn Guo95ceafd2012-09-06 07:09:11 +000039{
40 struct cpufreq_freqs freqs;
Nishanth Menon47d43ba2013-09-19 16:03:51 -050041 struct dev_pm_opp *opp;
jhbird.choi@samsung.com5df60552013-03-18 08:09:42 +000042 unsigned long volt = 0, volt_old = 0, tol = 0;
Guennadi Liakhovetski0ca68432013-02-25 18:22:37 +010043 long freq_Hz, freq_exact;
Shawn Guo95ceafd2012-09-06 07:09:11 +000044 int ret;
45
Shawn Guo95ceafd2012-09-06 07:09:11 +000046 freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
47 if (freq_Hz < 0)
48 freq_Hz = freq_table[index].frequency * 1000;
Guennadi Liakhovetski0ca68432013-02-25 18:22:37 +010049 freq_exact = freq_Hz;
Shawn Guo95ceafd2012-09-06 07:09:11 +000050 freqs.new = freq_Hz / 1000;
51 freqs.old = clk_get_rate(cpu_clk) / 1000;
52
Viresh Kumarb43a7ff2013-03-24 11:56:43 +053053 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
Shawn Guo95ceafd2012-09-06 07:09:11 +000054
Mark Brown4a511de2013-08-13 14:58:24 +020055 if (!IS_ERR(cpu_reg)) {
Nishanth Menon78e8eb82013-01-18 19:52:33 +000056 rcu_read_lock();
Nishanth Menon5d4879c2013-09-19 16:03:50 -050057 opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
Shawn Guo95ceafd2012-09-06 07:09:11 +000058 if (IS_ERR(opp)) {
Nishanth Menon78e8eb82013-01-18 19:52:33 +000059 rcu_read_unlock();
Shawn Guo95ceafd2012-09-06 07:09:11 +000060 pr_err("failed to find OPP for %ld\n", freq_Hz);
Viresh Kumarfd143b42013-04-01 12:57:44 +000061 freqs.new = freqs.old;
62 ret = PTR_ERR(opp);
63 goto post_notify;
Shawn Guo95ceafd2012-09-06 07:09:11 +000064 }
Nishanth Menon5d4879c2013-09-19 16:03:50 -050065 volt = dev_pm_opp_get_voltage(opp);
Nishanth Menon78e8eb82013-01-18 19:52:33 +000066 rcu_read_unlock();
Shawn Guo95ceafd2012-09-06 07:09:11 +000067 tol = volt * voltage_tolerance / 100;
68 volt_old = regulator_get_voltage(cpu_reg);
69 }
70
71 pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
72 freqs.old / 1000, volt_old ? volt_old / 1000 : -1,
73 freqs.new / 1000, volt ? volt / 1000 : -1);
74
75 /* scaling up? scale voltage before frequency */
Mark Brown4a511de2013-08-13 14:58:24 +020076 if (!IS_ERR(cpu_reg) && freqs.new > freqs.old) {
Shawn Guo95ceafd2012-09-06 07:09:11 +000077 ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
78 if (ret) {
79 pr_err("failed to scale voltage up: %d\n", ret);
80 freqs.new = freqs.old;
Viresh Kumarfd143b42013-04-01 12:57:44 +000081 goto post_notify;
Shawn Guo95ceafd2012-09-06 07:09:11 +000082 }
83 }
84
Guennadi Liakhovetski0ca68432013-02-25 18:22:37 +010085 ret = clk_set_rate(cpu_clk, freq_exact);
Shawn Guo95ceafd2012-09-06 07:09:11 +000086 if (ret) {
87 pr_err("failed to set clock rate: %d\n", ret);
Mark Brown4a511de2013-08-13 14:58:24 +020088 if (!IS_ERR(cpu_reg))
Shawn Guo95ceafd2012-09-06 07:09:11 +000089 regulator_set_voltage_tol(cpu_reg, volt_old, tol);
Viresh Kumarfd143b42013-04-01 12:57:44 +000090 freqs.new = freqs.old;
91 goto post_notify;
Shawn Guo95ceafd2012-09-06 07:09:11 +000092 }
93
94 /* scaling down? scale voltage after frequency */
Mark Brown4a511de2013-08-13 14:58:24 +020095 if (!IS_ERR(cpu_reg) && freqs.new < freqs.old) {
Shawn Guo95ceafd2012-09-06 07:09:11 +000096 ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
97 if (ret) {
98 pr_err("failed to scale voltage down: %d\n", ret);
99 clk_set_rate(cpu_clk, freqs.old * 1000);
100 freqs.new = freqs.old;
Shawn Guo95ceafd2012-09-06 07:09:11 +0000101 }
102 }
103
Viresh Kumarfd143b42013-04-01 12:57:44 +0000104post_notify:
Viresh Kumarb43a7ff2013-03-24 11:56:43 +0530105 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000106
Viresh Kumarfd143b42013-04-01 12:57:44 +0000107 return ret;
Shawn Guo95ceafd2012-09-06 07:09:11 +0000108}
109
110static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
111{
Viresh Kumar78b3d102013-10-03 20:29:09 +0530112 return cpufreq_generic_init(policy, freq_table, transition_latency);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000113}
114
Shawn Guo95ceafd2012-09-06 07:09:11 +0000115static struct cpufreq_driver cpu0_cpufreq_driver = {
116 .flags = CPUFREQ_STICKY,
Viresh Kumarf793d792013-10-03 20:28:00 +0530117 .verify = cpufreq_generic_frequency_table_verify,
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +0530118 .target_index = cpu0_set_target,
Shawn Guo95ceafd2012-09-06 07:09:11 +0000119 .get = cpu0_get_speed,
120 .init = cpu0_cpufreq_init,
Viresh Kumarf793d792013-10-03 20:28:00 +0530121 .exit = cpufreq_generic_exit,
Shawn Guo95ceafd2012-09-06 07:09:11 +0000122 .name = "generic_cpu0",
Viresh Kumarf793d792013-10-03 20:28:00 +0530123 .attr = cpufreq_generic_attr,
Shawn Guo95ceafd2012-09-06 07:09:11 +0000124};
125
Shawn Guo5553f9e2013-01-30 14:27:49 +0000126static int cpu0_cpufreq_probe(struct platform_device *pdev)
Shawn Guo95ceafd2012-09-06 07:09:11 +0000127{
Sudeep KarkadaNageshaf837a9b2013-06-17 15:04:19 +0100128 struct device_node *np;
Shawn Guo95ceafd2012-09-06 07:09:11 +0000129 int ret;
130
Sudeep KarkadaNageshae1825b22013-09-10 18:59:46 +0100131 cpu_dev = get_cpu_device(0);
132 if (!cpu_dev) {
133 pr_err("failed to get cpu0 device\n");
134 return -ENODEV;
135 }
Paolo Pisatif5c3ef22013-03-28 09:24:29 +0000136
Sudeep KarkadaNageshaf837a9b2013-06-17 15:04:19 +0100137 np = of_node_get(cpu_dev->of_node);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000138 if (!np) {
139 pr_err("failed to find cpu0 node\n");
Sudeep KarkadaNageshaf837a9b2013-06-17 15:04:19 +0100140 return -ENOENT;
Shawn Guo95ceafd2012-09-06 07:09:11 +0000141 }
142
Mark Brown7d748972013-08-09 19:07:12 +0100143 cpu_reg = devm_regulator_get_optional(cpu_dev, "cpu0");
Nishanth Menonfc31d6f2013-05-01 13:38:12 +0000144 if (IS_ERR(cpu_reg)) {
145 /*
146 * If cpu0 regulator supply node is present, but regulator is
147 * not yet registered, we should try defering probe.
148 */
149 if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) {
150 dev_err(cpu_dev, "cpu0 regulator not ready, retry\n");
151 ret = -EPROBE_DEFER;
152 goto out_put_node;
153 }
154 pr_warn("failed to get cpu0 regulator: %ld\n",
155 PTR_ERR(cpu_reg));
Nishanth Menonfc31d6f2013-05-01 13:38:12 +0000156 }
157
Shawn Guo5553f9e2013-01-30 14:27:49 +0000158 cpu_clk = devm_clk_get(cpu_dev, NULL);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000159 if (IS_ERR(cpu_clk)) {
160 ret = PTR_ERR(cpu_clk);
161 pr_err("failed to get cpu0 clock: %d\n", ret);
162 goto out_put_node;
163 }
164
Shawn Guo95ceafd2012-09-06 07:09:11 +0000165 ret = of_init_opp_table(cpu_dev);
166 if (ret) {
167 pr_err("failed to init OPP table: %d\n", ret);
168 goto out_put_node;
169 }
170
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500171 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000172 if (ret) {
173 pr_err("failed to init cpufreq table: %d\n", ret);
174 goto out_put_node;
175 }
176
177 of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
178
179 if (of_property_read_u32(np, "clock-latency", &transition_latency))
180 transition_latency = CPUFREQ_ETERNAL;
181
Philipp Zabel43c638e2013-09-26 11:19:37 +0200182 if (!IS_ERR(cpu_reg)) {
Nishanth Menon47d43ba2013-09-19 16:03:51 -0500183 struct dev_pm_opp *opp;
Shawn Guo95ceafd2012-09-06 07:09:11 +0000184 unsigned long min_uV, max_uV;
185 int i;
186
187 /*
188 * OPP is maintained in order of increasing frequency, and
189 * freq_table initialised from OPP is therefore sorted in the
190 * same order.
191 */
192 for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
193 ;
Nishanth Menon78e8eb82013-01-18 19:52:33 +0000194 rcu_read_lock();
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500195 opp = dev_pm_opp_find_freq_exact(cpu_dev,
Shawn Guo95ceafd2012-09-06 07:09:11 +0000196 freq_table[0].frequency * 1000, true);
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500197 min_uV = dev_pm_opp_get_voltage(opp);
198 opp = dev_pm_opp_find_freq_exact(cpu_dev,
Shawn Guo95ceafd2012-09-06 07:09:11 +0000199 freq_table[i-1].frequency * 1000, true);
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500200 max_uV = dev_pm_opp_get_voltage(opp);
Nishanth Menon78e8eb82013-01-18 19:52:33 +0000201 rcu_read_unlock();
Shawn Guo95ceafd2012-09-06 07:09:11 +0000202 ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
203 if (ret > 0)
204 transition_latency += ret * 1000;
205 }
206
207 ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
208 if (ret) {
209 pr_err("failed register driver: %d\n", ret);
210 goto out_free_table;
211 }
212
213 of_node_put(np);
214 return 0;
215
216out_free_table:
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500217 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000218out_put_node:
219 of_node_put(np);
220 return ret;
221}
Shawn Guo5553f9e2013-01-30 14:27:49 +0000222
223static int cpu0_cpufreq_remove(struct platform_device *pdev)
224{
225 cpufreq_unregister_driver(&cpu0_cpufreq_driver);
Nishanth Menon5d4879c2013-09-19 16:03:50 -0500226 dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
Shawn Guo5553f9e2013-01-30 14:27:49 +0000227
228 return 0;
229}
230
231static struct platform_driver cpu0_cpufreq_platdrv = {
232 .driver = {
233 .name = "cpufreq-cpu0",
234 .owner = THIS_MODULE,
235 },
236 .probe = cpu0_cpufreq_probe,
237 .remove = cpu0_cpufreq_remove,
238};
239module_platform_driver(cpu0_cpufreq_platdrv);
Shawn Guo95ceafd2012-09-06 07:09:11 +0000240
241MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
242MODULE_DESCRIPTION("Generic CPU0 cpufreq driver");
243MODULE_LICENSE("GPL");