aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/cpufreq/loongson2_clock.c
diff options
context:
space:
mode:
authorWu Zhangjin <wuzhangjin@gmail.com>2009-11-16 12:32:59 -0500
committerRalf Baechle <ralf@linux-mips.org>2009-12-16 20:57:20 -0500
commitf8ede0f700f5478851f242f291d203cde54ca6cf (patch)
tree37fba17288bcd12468c454eb6c585d72fb6f9770 /arch/mips/kernel/cpufreq/loongson2_clock.c
parent9726b43a4d7aaa5b30f559e78768aeb3d17bc224 (diff)
MIPS: Loongson 2F: Add CPU frequency scaling support
Loongson 2F supports CPU clock scaling. When put it into wait mode by setting the frequency as ZERO it will stay in this mode until an external interrupt wakes the CPU again. To enable clock scaling support, an external timer of a known stable rate is required. Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com> Cc: linux-mips@linux-mips.org Cc: cpufreq@vger.kernel.org, Cc: Dave Jones <davej@redhat.com>, Cc: Dominik Brodowski <linux@dominikbrodowski.net>, Cc: yanh@lemote.com Cc: huhb@lemote.com, Patchwork: http://patchwork.linux-mips.org/patch/660/ Patchwork: http://patchwork.linux-mips.org/patch/751/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/cpufreq/loongson2_clock.c')
-rw-r--r--arch/mips/kernel/cpufreq/loongson2_clock.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/arch/mips/kernel/cpufreq/loongson2_clock.c b/arch/mips/kernel/cpufreq/loongson2_clock.c
new file mode 100644
index 000000000000..d7ca256e33ef
--- /dev/null
+++ b/arch/mips/kernel/cpufreq/loongson2_clock.c
@@ -0,0 +1,166 @@
1/*
2 * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
3 * Author: Yanhua, yanh@lemote.com
4 *
5 * This file is subject to the terms and conditions of the GNU General Public
6 * License. See the file "COPYING" in the main directory of this archive
7 * for more details.
8 */
9
10#include <linux/cpufreq.h>
11#include <linux/platform_device.h>
12
13#include <asm/clock.h>
14
15#include <loongson.h>
16
17static LIST_HEAD(clock_list);
18static DEFINE_SPINLOCK(clock_lock);
19static DEFINE_MUTEX(clock_list_sem);
20
21/* Minimum CLK support */
22enum {
23 DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
24 DC_87PT, DC_DISABLE, DC_RESV
25};
26
27struct cpufreq_frequency_table loongson2_clockmod_table[] = {
28 {DC_RESV, CPUFREQ_ENTRY_INVALID},
29 {DC_ZERO, CPUFREQ_ENTRY_INVALID},
30 {DC_25PT, 0},
31 {DC_37PT, 0},
32 {DC_50PT, 0},
33 {DC_62PT, 0},
34 {DC_75PT, 0},
35 {DC_87PT, 0},
36 {DC_DISABLE, 0},
37 {DC_RESV, CPUFREQ_TABLE_END},
38};
39EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
40
41static struct clk cpu_clk = {
42 .name = "cpu_clk",
43 .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
44 .rate = 800000000,
45};
46
47struct clk *clk_get(struct device *dev, const char *id)
48{
49 return &cpu_clk;
50}
51EXPORT_SYMBOL(clk_get);
52
53static void propagate_rate(struct clk *clk)
54{
55 struct clk *clkp;
56
57 list_for_each_entry(clkp, &clock_list, node) {
58 if (likely(clkp->parent != clk))
59 continue;
60 if (likely(clkp->ops && clkp->ops->recalc))
61 clkp->ops->recalc(clkp);
62 if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
63 propagate_rate(clkp);
64 }
65}
66
67int clk_enable(struct clk *clk)
68{
69 return 0;
70}
71EXPORT_SYMBOL(clk_enable);
72
73void clk_disable(struct clk *clk)
74{
75}
76EXPORT_SYMBOL(clk_disable);
77
78unsigned long clk_get_rate(struct clk *clk)
79{
80 return (unsigned long)clk->rate;
81}
82EXPORT_SYMBOL(clk_get_rate);
83
84void clk_put(struct clk *clk)
85{
86}
87EXPORT_SYMBOL(clk_put);
88
89int clk_set_rate(struct clk *clk, unsigned long rate)
90{
91 return clk_set_rate_ex(clk, rate, 0);
92}
93EXPORT_SYMBOL_GPL(clk_set_rate);
94
95int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
96{
97 int ret = 0;
98 int regval;
99 int i;
100
101 if (likely(clk->ops && clk->ops->set_rate)) {
102 unsigned long flags;
103
104 spin_lock_irqsave(&clock_lock, flags);
105 ret = clk->ops->set_rate(clk, rate, algo_id);
106 spin_unlock_irqrestore(&clock_lock, flags);
107 }
108
109 if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
110 propagate_rate(clk);
111
112 for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
113 i++) {
114 if (loongson2_clockmod_table[i].frequency ==
115 CPUFREQ_ENTRY_INVALID)
116 continue;
117 if (rate == loongson2_clockmod_table[i].frequency)
118 break;
119 }
120 if (rate != loongson2_clockmod_table[i].frequency)
121 return -ENOTSUPP;
122
123 clk->rate = rate;
124
125 regval = LOONGSON_CHIPCFG0;
126 regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1);
127 LOONGSON_CHIPCFG0 = regval;
128
129 return ret;
130}
131EXPORT_SYMBOL_GPL(clk_set_rate_ex);
132
133long clk_round_rate(struct clk *clk, unsigned long rate)
134{
135 if (likely(clk->ops && clk->ops->round_rate)) {
136 unsigned long flags, rounded;
137
138 spin_lock_irqsave(&clock_lock, flags);
139 rounded = clk->ops->round_rate(clk, rate);
140 spin_unlock_irqrestore(&clock_lock, flags);
141
142 return rounded;
143 }
144
145 return rate;
146}
147EXPORT_SYMBOL_GPL(clk_round_rate);
148
149/*
150 * This is the simple version of Loongson-2 wait, Maybe we need do this in
151 * interrupt disabled content
152 */
153
154DEFINE_SPINLOCK(loongson2_wait_lock);
155void loongson2_cpu_wait(void)
156{
157 u32 cpu_freq;
158 unsigned long flags;
159
160 spin_lock_irqsave(&loongson2_wait_lock, flags);
161 cpu_freq = LOONGSON_CHIPCFG0;
162 LOONGSON_CHIPCFG0 &= ~0x7; /* Put CPU into wait mode */
163 LOONGSON_CHIPCFG0 = cpu_freq; /* Restore CPU state */
164 spin_unlock_irqrestore(&loongson2_wait_lock, flags);
165}
166EXPORT_SYMBOL_GPL(loongson2_cpu_wait);