aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpufreq.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2007-07-20 00:38:19 -0400
committerPaul Mundt <lethal@linux-sh.org>2007-07-20 00:38:19 -0400
commitcb5ec75b8b0410bba4ae612d13a2f26e938bc49c (patch)
treedacfe59494055f6cfb862e49a033b6e03203c6d6 /arch/sh/kernel/cpufreq.c
parentfe04d7798f611bca333df27d44484bdd62eef53f (diff)
sh: cpufreq: clock framework support.
This gets the SH cpufreq working again. We follow the changes in the AVR32 implementation for wrapping in to the clock framework. CPUs that wish to use this are required to define rate rounding primitives in order to satisfy clk_round_rate(). This works well enough for the common case, though we should look at unifying this driver across all of the platforms that implement clock framework support in one capacity or another. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel/cpufreq.c')
-rw-r--r--arch/sh/kernel/cpufreq.c214
1 files changed, 80 insertions, 134 deletions
diff --git a/arch/sh/kernel/cpufreq.c b/arch/sh/kernel/cpufreq.c
index 47abf6e49dfb..675118d6aea0 100644
--- a/arch/sh/kernel/cpufreq.c
+++ b/arch/sh/kernel/cpufreq.c
@@ -3,89 +3,51 @@
3 * 3 *
4 * cpufreq driver for the SuperH processors. 4 * cpufreq driver for the SuperH processors.
5 * 5 *
6 * Copyright (C) 2002, 2003, 2004, 2005 Paul Mundt 6 * Copyright (C) 2002 - 2007 Paul Mundt
7 * Copyright (C) 2002 M. R. Brown 7 * Copyright (C) 2002 M. R. Brown
8 * 8 *
9 * This program is free software; you can redistribute it and/or modify it 9 * Clock framework bits from arch/avr32/mach-at32ap/cpufreq.c
10 * under the terms of the GNU General Public License as published by the 10 *
11 * Free Software Foundation; either version 2 of the License, or (at your 11 * Copyright (C) 2004-2007 Atmel Corporation
12 * option) any later version. 12 *
13 * This file is subject to the terms and conditions of the GNU General Public
14 * License. See the file "COPYING" in the main directory of this archive
15 * for more details.
13 */ 16 */
14#include <linux/types.h> 17#include <linux/types.h>
15#include <linux/cpufreq.h> 18#include <linux/cpufreq.h>
16#include <linux/kernel.h> 19#include <linux/kernel.h>
17#include <linux/module.h> 20#include <linux/module.h>
18#include <linux/slab.h>
19#include <linux/init.h> 21#include <linux/init.h>
20#include <linux/delay.h> 22#include <linux/err.h>
21#include <linux/cpumask.h> 23#include <linux/cpumask.h>
22#include <linux/smp.h> 24#include <linux/smp.h>
23#include <linux/sched.h> /* set_cpus_allowed() */ 25#include <linux/sched.h> /* set_cpus_allowed() */
24 26#include <linux/io.h>
27#include <linux/clk.h>
25#include <asm/processor.h> 28#include <asm/processor.h>
26#include <asm/watchdog.h> 29#include <asm/watchdog.h>
27#include <asm/freq.h> 30#include <asm/freq.h>
28#include <asm/io.h> 31#include <asm/clock.h>
29
30/*
31 * For SuperH, each policy change requires that we change the IFC, BFC, and
32 * PFC at the same time. Here we define sane values that won't trash the
33 * system.
34 *
35 * Note the max set is computed at runtime, we use the divisors that we booted
36 * with to setup our maximum operating frequencies.
37 */
38struct clock_set {
39 unsigned int ifc;
40 unsigned int bfc;
41 unsigned int pfc;
42} clock_sets[] = {
43#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH2)
44 { 0, 0, 0 }, /* not implemented yet */
45#elif defined(CONFIG_CPU_SH4)
46 { 4, 8, 8 }, /* min - IFC: 1/4, BFC: 1/8, PFC: 1/8 */
47 { 1, 2, 2 }, /* max - IFC: 1, BFC: 1/2, PFC: 1/2 */
48#endif
49};
50
51#define MIN_CLOCK_SET 0
52#define MAX_CLOCK_SET (ARRAY_SIZE(clock_sets) - 1)
53
54/*
55 * For the time being, we only support two frequencies, which in turn are
56 * aimed at the POWERSAVE and PERFORMANCE policies, which in turn are derived
57 * directly from the respective min/max clock sets. Technically we could
58 * support a wider range of frequencies, but these vary far too much for each
59 * CPU subtype (and we'd have to construct a frequency table for each subtype).
60 *
61 * Maybe something to implement in the future..
62 */
63#define SH_FREQ_MAX 0
64#define SH_FREQ_MIN 1
65 32
66static struct cpufreq_frequency_table sh_freqs[] = { 33static struct clk *cpuclk;
67 { SH_FREQ_MAX, 0 },
68 { SH_FREQ_MIN, 0 },
69 { 0, CPUFREQ_TABLE_END },
70};
71 34
72static void sh_cpufreq_update_clocks(unsigned int set) 35static unsigned int sh_cpufreq_get(unsigned int cpu)
73{ 36{
74 current_cpu_data.cpu_clock = current_cpu_data.master_clock / clock_sets[set].ifc; 37 return (clk_get_rate(cpuclk) + 500) / 1000;
75 current_cpu_data.bus_clock = current_cpu_data.master_clock / clock_sets[set].bfc;
76 current_cpu_data.module_clock = current_cpu_data.master_clock / clock_sets[set].pfc;
77 current_cpu_data.loops_per_jiffy = loops_per_jiffy;
78} 38}
79 39
80/* XXX: This needs to be split out per CPU and CPU subtype. */
81/* 40/*
82 * Here we notify other drivers of the proposed change and the final change. 41 * Here we notify other drivers of the proposed change and the final change.
83 */ 42 */
84static int sh_cpufreq_setstate(unsigned int cpu, unsigned int set) 43static int sh_cpufreq_target(struct cpufreq_policy *policy,
44 unsigned int target_freq,
45 unsigned int relation)
85{ 46{
86 unsigned short frqcr = ctrl_inw(FRQCR); 47 unsigned int cpu = policy->cpu;
87 cpumask_t cpus_allowed; 48 cpumask_t cpus_allowed;
88 struct cpufreq_freqs freqs; 49 struct cpufreq_freqs freqs;
50 long freq;
89 51
90 if (!cpu_online(cpu)) 52 if (!cpu_online(cpu))
91 return -ENODEV; 53 return -ENODEV;
@@ -95,125 +57,109 @@ static int sh_cpufreq_setstate(unsigned int cpu, unsigned int set)
95 57
96 BUG_ON(smp_processor_id() != cpu); 58 BUG_ON(smp_processor_id() != cpu);
97 59
98 freqs.cpu = cpu; 60 /* Convert target_freq from kHz to Hz */
99 freqs.old = current_cpu_data.cpu_clock / 1000; 61 freq = clk_round_rate(cpuclk, target_freq * 1000);
100 freqs.new = (current_cpu_data.master_clock / clock_sets[set].ifc) / 1000;
101 62
102 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 63 if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
103#if defined(CONFIG_CPU_SH3) 64 return -EINVAL;
104 frqcr |= (newstate & 0x4000) << 14; 65
105 frqcr |= (newstate & 0x000c) << 2; 66 pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000);
106#elif defined(CONFIG_CPU_SH4)
107 /*
108 * FRQCR.PLL2EN is 1, we need to allow the PLL to stabilize by
109 * initializing the WDT.
110 */
111 if (frqcr & (1 << 9)) {
112 __u8 csr;
113
114 /*
115 * Set the overflow period to the highest available,
116 * in this case a 1/4096 division ratio yields a 5.25ms
117 * overflow period. See asm-sh/watchdog.h for more
118 * information and a range of other divisors.
119 */
120 csr = sh_wdt_read_csr();
121 csr |= WTCSR_CKS_4096;
122 sh_wdt_write_csr(csr);
123
124 sh_wdt_write_cnt(0);
125 }
126 frqcr &= 0x0e00; /* Clear ifc, bfc, pfc */
127 frqcr |= get_ifc_value(clock_sets[set].ifc) << 6;
128 frqcr |= get_bfc_value(clock_sets[set].bfc) << 3;
129 frqcr |= get_pfc_value(clock_sets[set].pfc);
130#endif
131 ctrl_outw(frqcr, FRQCR);
132 sh_cpufreq_update_clocks(set);
133 67
68 freqs.cpu = cpu;
69 freqs.old = sh_cpufreq_get(cpu);
70 freqs.new = (freq + 500) / 1000;
71 freqs.flags = 0;
72
73 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
134 set_cpus_allowed(current, cpus_allowed); 74 set_cpus_allowed(current, cpus_allowed);
75 clk_set_rate(cpuclk, freq);
135 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 76 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
136 77
78 pr_debug("cpufreq: set frequency %lu Hz\n", freq);
79
137 return 0; 80 return 0;
138} 81}
139 82
140static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) 83static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
141{ 84{
142 unsigned int min_freq, max_freq; 85 printk(KERN_INFO "cpufreq: SuperH CPU frequency driver.\n");
143 unsigned int ifc, bfc, pfc;
144 86
145 if (!cpu_online(policy->cpu)) 87 if (!cpu_online(policy->cpu))
146 return -ENODEV; 88 return -ENODEV;
147 89
148 /* Update our maximum clock set */ 90 cpuclk = clk_get(NULL, "cpu_clk");
149 get_current_frequency_divisors(&ifc, &bfc, &pfc); 91 if (IS_ERR(cpuclk)) {
150 clock_sets[MAX_CLOCK_SET].ifc = ifc; 92 printk(KERN_ERR "cpufreq: couldn't get CPU clk\n");
151 clock_sets[MAX_CLOCK_SET].bfc = bfc; 93 return PTR_ERR(cpuclk);
152 clock_sets[MAX_CLOCK_SET].pfc = pfc; 94 }
153
154 /* Convert from Hz to kHz */
155 max_freq = current_cpu_data.cpu_clock / 1000;
156 min_freq = (current_cpu_data.master_clock / clock_sets[MIN_CLOCK_SET].ifc) / 1000;
157
158 sh_freqs[SH_FREQ_MAX].frequency = max_freq;
159 sh_freqs[SH_FREQ_MIN].frequency = min_freq;
160 95
161 /* cpuinfo and default policy values */ 96 /* cpuinfo and default policy values */
162 policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 97 policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000;
98 policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
163 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; 99 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
164 policy->cur = max_freq;
165 100
166 return cpufreq_frequency_table_cpuinfo(policy, &sh_freqs[0]); 101 policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
167} 102 policy->cur = sh_cpufreq_get(policy->cpu);
103 policy->min = policy->cpuinfo.min_freq;
104 policy->max = policy->cpuinfo.max_freq;
168 105
169static int sh_cpufreq_verify(struct cpufreq_policy *policy)
170{
171 return cpufreq_frequency_table_verify(policy, &sh_freqs[0]);
172}
173 106
174static int sh_cpufreq_target(struct cpufreq_policy *policy, 107 /*
175 unsigned int target_freq, 108 * Catch the cases where the clock framework hasn't been wired up
176 unsigned int relation) 109 * properly to support scaling.
177{ 110 */
178 unsigned int set, idx = 0; 111 if (unlikely(policy->min == policy->max)) {
112 printk(KERN_ERR "cpufreq: clock framework rate rounding "
113 "not supported on this CPU.\n");
179 114
180 if (cpufreq_frequency_table_target(policy, &sh_freqs[0], target_freq, relation, &idx)) 115 clk_put(cpuclk);
181 return -EINVAL; 116 return -EINVAL;
117 }
182 118
183 set = (idx == SH_FREQ_MIN) ? MIN_CLOCK_SET : MAX_CLOCK_SET; 119 printk(KERN_INFO "cpufreq: Frequencies - Minimum %u.%03u MHz, "
120 "Maximum %u.%03u MHz.\n",
121 policy->min / 1000, policy->min % 1000,
122 policy->max / 1000, policy->max % 1000);
184 123
185 sh_cpufreq_setstate(policy->cpu, set); 124 return 0;
125}
186 126
127static int sh_cpufreq_verify(struct cpufreq_policy *policy)
128{
129 cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
130 policy->cpuinfo.max_freq);
131 return 0;
132}
133
134static int sh_cpufreq_exit(struct cpufreq_policy *policy)
135{
136 clk_put(cpuclk);
187 return 0; 137 return 0;
188} 138}
189 139
190static struct cpufreq_driver sh_cpufreq_driver = { 140static struct cpufreq_driver sh_cpufreq_driver = {
191 .owner = THIS_MODULE, 141 .owner = THIS_MODULE,
192 .name = "SH cpufreq", 142 .name = "sh",
193 .init = sh_cpufreq_cpu_init, 143 .init = sh_cpufreq_cpu_init,
194 .verify = sh_cpufreq_verify, 144 .verify = sh_cpufreq_verify,
195 .target = sh_cpufreq_target, 145 .target = sh_cpufreq_target,
146 .get = sh_cpufreq_get,
147 .exit = sh_cpufreq_exit,
196}; 148};
197 149
198static int __init sh_cpufreq_init(void) 150static int __init sh_cpufreq_module_init(void)
199{ 151{
200 if (!current_cpu_data.cpu_clock) 152 return cpufreq_register_driver(&sh_cpufreq_driver);
201 return -EINVAL;
202 if (cpufreq_register_driver(&sh_cpufreq_driver))
203 return -EINVAL;
204
205 return 0;
206} 153}
207 154
208static void __exit sh_cpufreq_exit(void) 155static void __exit sh_cpufreq_module_exit(void)
209{ 156{
210 cpufreq_unregister_driver(&sh_cpufreq_driver); 157 cpufreq_unregister_driver(&sh_cpufreq_driver);
211} 158}
212 159
213module_init(sh_cpufreq_init); 160module_init(sh_cpufreq_module_init);
214module_exit(sh_cpufreq_exit); 161module_exit(sh_cpufreq_module_exit);
215 162
216MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); 163MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
217MODULE_DESCRIPTION("cpufreq driver for SuperH"); 164MODULE_DESCRIPTION("cpufreq driver for SuperH");
218MODULE_LICENSE("GPL"); 165MODULE_LICENSE("GPL");
219