aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel/cpu')
-rw-r--r--arch/sh/kernel/cpu/clock.c287
-rw-r--r--arch/sh/kernel/cpu/sh3/Makefile7
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh3.c89
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7300.c78
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7705.c84
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7709.c96
-rw-r--r--arch/sh/kernel/cpu/sh4/Makefile11
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh4-202.c179
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh4.c80
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh73180.c81
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh7770.c73
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh7780.c126
12 files changed, 1190 insertions, 1 deletions
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
new file mode 100644
index 000000000000..989e7fdd524d
--- /dev/null
+++ b/arch/sh/kernel/cpu/clock.c
@@ -0,0 +1,287 @@
1/*
2 * arch/sh/kernel/cpu/clock.c - SuperH clock framework
3 *
4 * Copyright (C) 2005 Paul Mundt
5 *
6 * This clock framework is derived from the OMAP version by:
7 *
8 * Copyright (C) 2004 Nokia Corporation
9 * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
10 *
11 * This file is subject to the terms and conditions of the GNU General Public
12 * License. See the file "COPYING" in the main directory of this archive
13 * for more details.
14 */
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/list.h>
19#include <linux/kref.h>
20#include <linux/seq_file.h>
21#include <linux/err.h>
22#include <asm/clock.h>
23#include <asm/timer.h>
24
25static LIST_HEAD(clock_list);
26static DEFINE_SPINLOCK(clock_lock);
27static DECLARE_MUTEX(clock_list_sem);
28
29/*
30 * Each subtype is expected to define the init routines for these clocks,
31 * as each subtype (or processor family) will have these clocks at the
32 * very least. These are all provided through the CPG, which even some of
33 * the more quirky parts (such as ST40, SH4-202, etc.) still have.
34 *
35 * The processor-specific code is expected to register any additional
36 * clock sources that are of interest.
37 */
38static struct clk master_clk = {
39 .name = "master_clk",
40 .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
41#ifdef CONFIG_SH_PCLK_FREQ_BOOL
42 .rate = CONFIG_SH_PCLK_FREQ,
43#endif
44};
45
46static struct clk module_clk = {
47 .name = "module_clk",
48 .parent = &master_clk,
49 .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
50};
51
52static struct clk bus_clk = {
53 .name = "bus_clk",
54 .parent = &master_clk,
55 .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
56};
57
58static struct clk cpu_clk = {
59 .name = "cpu_clk",
60 .parent = &master_clk,
61 .flags = CLK_ALWAYS_ENABLED,
62};
63
64/*
65 * The ordering of these clocks matters, do not change it.
66 */
67static struct clk *onchip_clocks[] = {
68 &master_clk,
69 &module_clk,
70 &bus_clk,
71 &cpu_clk,
72};
73
74static void propagate_rate(struct clk *clk)
75{
76 struct clk *clkp;
77
78 list_for_each_entry(clkp, &clock_list, node) {
79 if (likely(clkp->parent != clk))
80 continue;
81 if (likely(clkp->ops && clkp->ops->recalc))
82 clkp->ops->recalc(clkp);
83 }
84}
85
86int __clk_enable(struct clk *clk)
87{
88 /*
89 * See if this is the first time we're enabling the clock, some
90 * clocks that are always enabled still require "special"
91 * initialization. This is especially true if the clock mode
92 * changes and the clock needs to hunt for the proper set of
93 * divisors to use before it can effectively recalc.
94 */
95 if (unlikely(atomic_read(&clk->kref.refcount) == 1))
96 if (clk->ops && clk->ops->init)
97 clk->ops->init(clk);
98
99 if (clk->flags & CLK_ALWAYS_ENABLED)
100 return 0;
101
102 if (likely(clk->ops && clk->ops->enable))
103 clk->ops->enable(clk);
104
105 kref_get(&clk->kref);
106 return 0;
107}
108
109int clk_enable(struct clk *clk)
110{
111 unsigned long flags;
112 int ret;
113
114 spin_lock_irqsave(&clock_lock, flags);
115 ret = __clk_enable(clk);
116 spin_unlock_irqrestore(&clock_lock, flags);
117
118 return ret;
119}
120
121static void clk_kref_release(struct kref *kref)
122{
123 /* Nothing to do */
124}
125
126void __clk_disable(struct clk *clk)
127{
128 if (clk->flags & CLK_ALWAYS_ENABLED)
129 return;
130
131 kref_put(&clk->kref, clk_kref_release);
132}
133
134void clk_disable(struct clk *clk)
135{
136 unsigned long flags;
137
138 spin_lock_irqsave(&clock_lock, flags);
139 __clk_disable(clk);
140 spin_unlock_irqrestore(&clock_lock, flags);
141}
142
143int clk_register(struct clk *clk)
144{
145 down(&clock_list_sem);
146
147 list_add(&clk->node, &clock_list);
148 kref_init(&clk->kref);
149
150 up(&clock_list_sem);
151
152 return 0;
153}
154
155void clk_unregister(struct clk *clk)
156{
157 down(&clock_list_sem);
158 list_del(&clk->node);
159 up(&clock_list_sem);
160}
161
162inline unsigned long clk_get_rate(struct clk *clk)
163{
164 return clk->rate;
165}
166
167int clk_set_rate(struct clk *clk, unsigned long rate)
168{
169 int ret = -EOPNOTSUPP;
170
171 if (likely(clk->ops && clk->ops->set_rate)) {
172 unsigned long flags;
173
174 spin_lock_irqsave(&clock_lock, flags);
175 ret = clk->ops->set_rate(clk, rate);
176 spin_unlock_irqrestore(&clock_lock, flags);
177 }
178
179 if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
180 propagate_rate(clk);
181
182 return ret;
183}
184
185void clk_recalc_rate(struct clk *clk)
186{
187 if (likely(clk->ops && clk->ops->recalc)) {
188 unsigned long flags;
189
190 spin_lock_irqsave(&clock_lock, flags);
191 clk->ops->recalc(clk);
192 spin_unlock_irqrestore(&clock_lock, flags);
193 }
194
195 if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
196 propagate_rate(clk);
197}
198
199struct clk *clk_get(const char *id)
200{
201 struct clk *p, *clk = ERR_PTR(-ENOENT);
202
203 down(&clock_list_sem);
204 list_for_each_entry(p, &clock_list, node) {
205 if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
206 clk = p;
207 break;
208 }
209 }
210 up(&clock_list_sem);
211
212 return clk;
213}
214
215void clk_put(struct clk *clk)
216{
217 if (clk && !IS_ERR(clk))
218 module_put(clk->owner);
219}
220
221void __init __attribute__ ((weak))
222arch_init_clk_ops(struct clk_ops **ops, int type)
223{
224}
225
226int __init clk_init(void)
227{
228 int i, ret = 0;
229
230 if (unlikely(!master_clk.rate))
231 /*
232 * NOTE: This will break if the default divisor has been
233 * changed.
234 *
235 * No one should be changing the default on us however,
236 * expect that a sane value for CONFIG_SH_PCLK_FREQ will
237 * be defined in the event of a different divisor.
238 */
239 master_clk.rate = get_timer_frequency() * 4;
240
241 for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) {
242 struct clk *clk = onchip_clocks[i];
243
244 arch_init_clk_ops(&clk->ops, i);
245 ret |= clk_register(clk);
246 clk_enable(clk);
247 }
248
249 /* Kick the child clocks.. */
250 propagate_rate(&master_clk);
251 propagate_rate(&bus_clk);
252
253 return ret;
254}
255
256int show_clocks(struct seq_file *m)
257{
258 struct clk *clk;
259
260 list_for_each_entry_reverse(clk, &clock_list, node) {
261 unsigned long rate = clk_get_rate(clk);
262
263 /*
264 * Don't bother listing dummy clocks with no ancestry
265 * that only support enable and disable ops.
266 */
267 if (unlikely(!rate && !clk->parent))
268 continue;
269
270 seq_printf(m, "%-12s\t: %ld.%02ldMHz\n", clk->name,
271 rate / 1000000, (rate % 1000000) / 10000);
272 }
273
274 return 0;
275}
276
277EXPORT_SYMBOL_GPL(clk_register);
278EXPORT_SYMBOL_GPL(clk_unregister);
279EXPORT_SYMBOL_GPL(clk_get);
280EXPORT_SYMBOL_GPL(clk_put);
281EXPORT_SYMBOL_GPL(clk_enable);
282EXPORT_SYMBOL_GPL(clk_disable);
283EXPORT_SYMBOL_GPL(__clk_enable);
284EXPORT_SYMBOL_GPL(__clk_disable);
285EXPORT_SYMBOL_GPL(clk_get_rate);
286EXPORT_SYMBOL_GPL(clk_set_rate);
287EXPORT_SYMBOL_GPL(clk_recalc_rate);
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile
index a64532e4dc63..b54dbb9a0c86 100644
--- a/arch/sh/kernel/cpu/sh3/Makefile
+++ b/arch/sh/kernel/cpu/sh3/Makefile
@@ -4,3 +4,10 @@
4 4
5obj-y := ex.o probe.o 5obj-y := ex.o probe.o
6 6
7clock-$(CONFIG_CPU_SH3) := clock-sh3.o
8clock-$(CONFIG_CPU_SUBTYPE_SH7300) := clock-sh7300.o
9clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o
10clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o
11
12obj-y += $(clock-y)
13
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh3.c b/arch/sh/kernel/cpu/sh3/clock-sh3.c
new file mode 100644
index 000000000000..c3c945958baf
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh3.c
@@ -0,0 +1,89 @@
1/*
2 * arch/sh/kernel/cpu/sh3/clock-sh3.c
3 *
4 * Generic SH-3 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * FRQCR parsing hacked out of arch/sh/kernel/time.c
9 *
10 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
11 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
12 * Copyright (C) 2002, 2003, 2004 Paul Mundt
13 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
14 *
15 * This file is subject to the terms and conditions of the GNU General Public
16 * License. See the file "COPYING" in the main directory of this archive
17 * for more details.
18 */
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <asm/clock.h>
22#include <asm/freq.h>
23#include <asm/io.h>
24
25static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
26static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
27static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
28
29static void master_clk_init(struct clk *clk)
30{
31 int frqcr = ctrl_inw(FRQCR);
32 int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
33
34 clk->rate *= pfc_divisors[idx];
35}
36
37static struct clk_ops sh3_master_clk_ops = {
38 .init = master_clk_init,
39};
40
41static void module_clk_recalc(struct clk *clk)
42{
43 int frqcr = ctrl_inw(FRQCR);
44 int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
45
46 clk->rate = clk->parent->rate / pfc_divisors[idx];
47}
48
49static struct clk_ops sh3_module_clk_ops = {
50 .recalc = module_clk_recalc,
51};
52
53static void bus_clk_recalc(struct clk *clk)
54{
55 int frqcr = ctrl_inw(FRQCR);
56 int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4);
57
58 clk->rate = clk->parent->rate / stc_multipliers[idx];
59}
60
61static struct clk_ops sh3_bus_clk_ops = {
62 .recalc = bus_clk_recalc,
63};
64
65static void cpu_clk_recalc(struct clk *clk)
66{
67 int frqcr = ctrl_inw(FRQCR);
68 int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2);
69
70 clk->rate = clk->parent->rate / ifc_divisors[idx];
71}
72
73static struct clk_ops sh3_cpu_clk_ops = {
74 .recalc = cpu_clk_recalc,
75};
76
77static struct clk_ops *sh3_clk_ops[] = {
78 &sh3_master_clk_ops,
79 &sh3_module_clk_ops,
80 &sh3_bus_clk_ops,
81 &sh3_cpu_clk_ops,
82};
83
84void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
85{
86 if (idx < ARRAY_SIZE(sh3_clk_ops))
87 *ops = sh3_clk_ops[idx];
88}
89
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7300.c b/arch/sh/kernel/cpu/sh3/clock-sh7300.c
new file mode 100644
index 000000000000..e804174b9625
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh7300.c
@@ -0,0 +1,78 @@
1/*
2 * arch/sh/kernel/cpu/sh3/clock-sh7300.c
3 *
4 * SH7300 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * FRQCR parsing hacked out of arch/sh/kernel/time.c
9 *
10 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
11 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
12 * Copyright (C) 2002, 2003, 2004 Paul Mundt
13 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
14 *
15 * This file is subject to the terms and conditions of the GNU General Public
16 * License. See the file "COPYING" in the main directory of this archive
17 * for more details.
18 */
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <asm/clock.h>
22#include <asm/freq.h>
23#include <asm/io.h>
24
25static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 };
26
27static void master_clk_init(struct clk *clk)
28{
29 clk->rate *= md_table[ctrl_inw(FRQCR) & 0x0007];
30}
31
32static struct clk_ops sh7300_master_clk_ops = {
33 .init = master_clk_init,
34};
35
36static void module_clk_recalc(struct clk *clk)
37{
38 int idx = (ctrl_inw(FRQCR) & 0x0007);
39 clk->rate = clk->parent->rate / md_table[idx];
40}
41
42static struct clk_ops sh7300_module_clk_ops = {
43 .recalc = module_clk_recalc,
44};
45
46static void bus_clk_recalc(struct clk *clk)
47{
48 int idx = (ctrl_inw(FRQCR) & 0x0700) >> 8;
49 clk->rate = clk->parent->rate / md_table[idx];
50}
51
52static struct clk_ops sh7300_bus_clk_ops = {
53 .recalc = bus_clk_recalc,
54};
55
56static void cpu_clk_recalc(struct clk *clk)
57{
58 int idx = (ctrl_inw(FRQCR) & 0x0070) >> 4;
59 clk->rate = clk->parent->rate / md_table[idx];
60}
61
62static struct clk_ops sh7300_cpu_clk_ops = {
63 .recalc = cpu_clk_recalc,
64};
65
66static struct clk_ops *sh7300_clk_ops[] = {
67 &sh7300_master_clk_ops,
68 &sh7300_module_clk_ops,
69 &sh7300_bus_clk_ops,
70 &sh7300_cpu_clk_ops,
71};
72
73void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
74{
75 if (idx < ARRAY_SIZE(sh7300_clk_ops))
76 *ops = sh7300_clk_ops[idx];
77}
78
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7705.c b/arch/sh/kernel/cpu/sh3/clock-sh7705.c
new file mode 100644
index 000000000000..dfdbf3277fd7
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh7705.c
@@ -0,0 +1,84 @@
1/*
2 * arch/sh/kernel/cpu/sh3/clock-sh7705.c
3 *
4 * SH7705 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * FRQCR parsing hacked out of arch/sh/kernel/time.c
9 *
10 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
11 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
12 * Copyright (C) 2002, 2003, 2004 Paul Mundt
13 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
14 *
15 * This file is subject to the terms and conditions of the GNU General Public
16 * License. See the file "COPYING" in the main directory of this archive
17 * for more details.
18 */
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <asm/clock.h>
22#include <asm/freq.h>
23#include <asm/io.h>
24
25/*
26 * SH7705 uses the same divisors as the generic SH-3 case, it's just the
27 * FRQCR layout that is a bit different..
28 */
29static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
30static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
31static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
32
33static void master_clk_init(struct clk *clk)
34{
35 clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0003];
36}
37
38static struct clk_ops sh7705_master_clk_ops = {
39 .init = master_clk_init,
40};
41
42static void module_clk_recalc(struct clk *clk)
43{
44 int idx = ctrl_inw(FRQCR) & 0x0003;
45 clk->rate = clk->parent->rate / pfc_divisors[idx];
46}
47
48static struct clk_ops sh7705_module_clk_ops = {
49 .recalc = module_clk_recalc,
50};
51
52static void bus_clk_recalc(struct clk *clk)
53{
54 int idx = (ctrl_inw(FRQCR) & 0x0300) >> 8;
55 clk->rate = clk->parent->rate / stc_multipliers[idx];
56}
57
58static struct clk_ops sh7705_bus_clk_ops = {
59 .recalc = bus_clk_recalc,
60};
61
62static void cpu_clk_recalc(struct clk *clk)
63{
64 int idx = (ctrl_inw(FRQCR) & 0x0030) >> 4;
65 clk->rate = clk->parent->rate / ifc_divisors[idx];
66}
67
68static struct clk_ops sh7705_cpu_clk_ops = {
69 .recalc = cpu_clk_recalc,
70};
71
72static struct clk_ops *sh7705_clk_ops[] = {
73 &sh7705_master_clk_ops,
74 &sh7705_module_clk_ops,
75 &sh7705_bus_clk_ops,
76 &sh7705_cpu_clk_ops,
77};
78
79void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
80{
81 if (idx < ARRAY_SIZE(sh7705_clk_ops))
82 *ops = sh7705_clk_ops[idx];
83}
84
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7709.c b/arch/sh/kernel/cpu/sh3/clock-sh7709.c
new file mode 100644
index 000000000000..10461a745e5f
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh7709.c
@@ -0,0 +1,96 @@
1/*
2 * arch/sh/kernel/cpu/sh3/clock-sh7709.c
3 *
4 * SH7709 support for the clock framework
5 *
6 * Copyright (C) 2005 Andriy Skulysh
7 *
8 * Based on arch/sh/kernel/cpu/sh3/clock-sh7705.c
9 * Copyright (C) 2005 Paul Mundt
10 *
11 * This file is subject to the terms and conditions of the GNU General Public
12 * License. See the file "COPYING" in the main directory of this archive
13 * for more details.
14 */
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <asm/clock.h>
18#include <asm/freq.h>
19#include <asm/io.h>
20
21static int stc_multipliers[] = { 1, 2, 4, 8, 3, 6, 1, 1 };
22static int ifc_divisors[] = { 1, 2, 4, 1, 3, 1, 1, 1 };
23static int pfc_divisors[] = { 1, 2, 4, 1, 3, 6, 1, 1 };
24
25static void set_bus_parent(struct clk *clk)
26{
27 struct clk *bus_clk = clk_get("bus_clk");
28 clk->parent = bus_clk;
29 clk_put(bus_clk);
30}
31
32static void master_clk_init(struct clk *clk)
33{
34 int frqcr = ctrl_inw(FRQCR);
35 int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
36
37 clk->rate *= pfc_divisors[idx];
38}
39
40static struct clk_ops sh7709_master_clk_ops = {
41 .init = master_clk_init,
42};
43
44static void module_clk_recalc(struct clk *clk)
45{
46 int frqcr = ctrl_inw(FRQCR);
47 int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
48
49 clk->rate = clk->parent->rate / pfc_divisors[idx];
50}
51
52static struct clk_ops sh7709_module_clk_ops = {
53#ifdef CLOCK_MODE_0_1_2_7
54 .init = set_bus_parent,
55#endif
56 .recalc = module_clk_recalc,
57};
58
59static void bus_clk_recalc(struct clk *clk)
60{
61 int frqcr = ctrl_inw(FRQCR);
62 int idx = (frqcr & 0x0080) ?
63 ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4) : 1;
64
65 clk->rate = clk->parent->rate * stc_multipliers[idx];
66}
67
68static struct clk_ops sh7709_bus_clk_ops = {
69 .recalc = bus_clk_recalc,
70};
71
72static void cpu_clk_recalc(struct clk *clk)
73{
74 int frqcr = ctrl_inw(FRQCR);
75 int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2);
76
77 clk->rate = clk->parent->rate / ifc_divisors[idx];
78}
79
80static struct clk_ops sh7709_cpu_clk_ops = {
81 .init = set_bus_parent,
82 .recalc = cpu_clk_recalc,
83};
84
85static struct clk_ops *sh7709_clk_ops[] = {
86 &sh7709_master_clk_ops,
87 &sh7709_module_clk_ops,
88 &sh7709_bus_clk_ops,
89 &sh7709_cpu_clk_ops,
90};
91
92void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
93{
94 if (idx < ARRAY_SIZE(sh7709_clk_ops))
95 *ops = sh7709_clk_ops[idx];
96}
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile
index ead1071eac73..3d5cafc71ae3 100644
--- a/arch/sh/kernel/cpu/sh4/Makefile
+++ b/arch/sh/kernel/cpu/sh4/Makefile
@@ -5,6 +5,15 @@
5obj-y := ex.o probe.o 5obj-y := ex.o probe.o
6 6
7obj-$(CONFIG_SH_FPU) += fpu.o 7obj-$(CONFIG_SH_FPU) += fpu.o
8obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o
9obj-$(CONFIG_SH_STORE_QUEUES) += sq.o 8obj-$(CONFIG_SH_STORE_QUEUES) += sq.o
10 9
10# Primary on-chip clocks (common)
11clock-$(CONFIG_CPU_SH4) := clock-sh4.o
12clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o
13clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o
14clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o
15
16# Additional clocks by subtype
17clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o
18
19obj-y += $(clock-y)
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
new file mode 100644
index 000000000000..bfdf5fe8d948
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
@@ -0,0 +1,179 @@
1/*
2 * arch/sh/kernel/cpu/sh4/clock-sh4-202.c
3 *
4 * Additional SH4-202 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/err.h>
15#include <asm/clock.h>
16#include <asm/freq.h>
17#include <asm/io.h>
18
19#define CPG2_FRQCR3 0xfe0a0018
20
21static int frqcr3_divisors[] = { 1, 2, 3, 4, 6, 8, 16 };
22static int frqcr3_values[] = { 0, 1, 2, 3, 4, 5, 6 };
23
24static void emi_clk_recalc(struct clk *clk)
25{
26 int idx = ctrl_inl(CPG2_FRQCR3) & 0x0007;
27 clk->rate = clk->parent->rate / frqcr3_divisors[idx];
28}
29
30static inline int frqcr3_lookup(struct clk *clk, unsigned long rate)
31{
32 int divisor = clk->parent->rate / rate;
33 int i;
34
35 for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++)
36 if (frqcr3_divisors[i] == divisor)
37 return frqcr3_values[i];
38
39 /* Safe fallback */
40 return 5;
41}
42
43static struct clk_ops sh4202_emi_clk_ops = {
44 .recalc = emi_clk_recalc,
45};
46
47static struct clk sh4202_emi_clk = {
48 .name = "emi_clk",
49 .flags = CLK_ALWAYS_ENABLED,
50 .ops = &sh4202_emi_clk_ops,
51};
52
53static void femi_clk_recalc(struct clk *clk)
54{
55 int idx = (ctrl_inl(CPG2_FRQCR3) >> 3) & 0x0007;
56 clk->rate = clk->parent->rate / frqcr3_divisors[idx];
57}
58
59static struct clk_ops sh4202_femi_clk_ops = {
60 .recalc = femi_clk_recalc,
61};
62
63static struct clk sh4202_femi_clk = {
64 .name = "femi_clk",
65 .flags = CLK_ALWAYS_ENABLED,
66 .ops = &sh4202_femi_clk_ops,
67};
68
69static void shoc_clk_init(struct clk *clk)
70{
71 int i;
72
73 /*
74 * For some reason, the shoc_clk seems to be set to some really
75 * insane value at boot (values outside of the allowable frequency
76 * range for instance). We deal with this by scaling it back down
77 * to something sensible just in case.
78 *
79 * Start scaling from the high end down until we find something
80 * that passes rate verification..
81 */
82 for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) {
83 int divisor = frqcr3_divisors[i];
84
85 if (clk->ops->set_rate(clk, clk->parent->rate / divisor) == 0)
86 break;
87 }
88
89 WARN_ON(i == ARRAY_SIZE(frqcr3_divisors)); /* Undefined clock */
90}
91
92static void shoc_clk_recalc(struct clk *clk)
93{
94 int idx = (ctrl_inl(CPG2_FRQCR3) >> 6) & 0x0007;
95 clk->rate = clk->parent->rate / frqcr3_divisors[idx];
96}
97
98static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate)
99{
100 struct clk *bclk = clk_get("bus_clk");
101 unsigned long bclk_rate = clk_get_rate(bclk);
102
103 clk_put(bclk);
104
105 if (rate > bclk_rate)
106 return 1;
107 if (rate > 66000000)
108 return 1;
109
110 return 0;
111}
112
113static int shoc_clk_set_rate(struct clk *clk, unsigned long rate)
114{
115 unsigned long frqcr3;
116 unsigned int tmp;
117
118 /* Make sure we have something sensible to switch to */
119 if (shoc_clk_verify_rate(clk, rate) != 0)
120 return -EINVAL;
121
122 tmp = frqcr3_lookup(clk, rate);
123
124 frqcr3 = ctrl_inl(CPG2_FRQCR3);
125 frqcr3 &= ~(0x0007 << 6);
126 frqcr3 |= tmp << 6;
127 ctrl_outl(frqcr3, CPG2_FRQCR3);
128
129 clk->rate = clk->parent->rate / frqcr3_divisors[tmp];
130
131 return 0;
132}
133
134static struct clk_ops sh4202_shoc_clk_ops = {
135 .init = shoc_clk_init,
136 .recalc = shoc_clk_recalc,
137 .set_rate = shoc_clk_set_rate,
138};
139
140static struct clk sh4202_shoc_clk = {
141 .name = "shoc_clk",
142 .flags = CLK_ALWAYS_ENABLED,
143 .ops = &sh4202_shoc_clk_ops,
144};
145
146static struct clk *sh4202_onchip_clocks[] = {
147 &sh4202_emi_clk,
148 &sh4202_femi_clk,
149 &sh4202_shoc_clk,
150};
151
152static int __init sh4202_clk_init(void)
153{
154 struct clk *clk = clk_get("master_clk");
155 int i;
156
157 for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) {
158 struct clk *clkp = sh4202_onchip_clocks[i];
159
160 clkp->parent = clk;
161 clk_register(clkp);
162 clk_enable(clkp);
163 }
164
165 /*
166 * Now that we have the rest of the clocks registered, we need to
167 * force the parent clock to propagate so that these clocks will
168 * automatically figure out their rate. We cheat by handing the
169 * parent clock its current rate and forcing child propagation.
170 */
171 clk_set_rate(clk, clk_get_rate(clk));
172
173 clk_put(clk);
174
175 return 0;
176}
177
178arch_initcall(sh4202_clk_init);
179
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4.c b/arch/sh/kernel/cpu/sh4/clock-sh4.c
new file mode 100644
index 000000000000..dca9f87a12d6
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh4.c
@@ -0,0 +1,80 @@
1/*
2 * arch/sh/kernel/cpu/sh4/clock-sh4.c
3 *
4 * Generic SH-4 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * FRQCR parsing hacked out of arch/sh/kernel/time.c
9 *
10 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
11 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
12 * Copyright (C) 2002, 2003, 2004 Paul Mundt
13 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
14 *
15 * This file is subject to the terms and conditions of the GNU General Public
16 * License. See the file "COPYING" in the main directory of this archive
17 * for more details.
18 */
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <asm/clock.h>
22#include <asm/freq.h>
23#include <asm/io.h>
24
25static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 };
26#define bfc_divisors ifc_divisors /* Same */
27static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 };
28
29static void master_clk_init(struct clk *clk)
30{
31 clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0007];
32}
33
34static struct clk_ops sh4_master_clk_ops = {
35 .init = master_clk_init,
36};
37
38static void module_clk_recalc(struct clk *clk)
39{
40 int idx = (ctrl_inw(FRQCR) & 0x0007);
41 clk->rate = clk->parent->rate / pfc_divisors[idx];
42}
43
44static struct clk_ops sh4_module_clk_ops = {
45 .recalc = module_clk_recalc,
46};
47
48static void bus_clk_recalc(struct clk *clk)
49{
50 int idx = (ctrl_inw(FRQCR) >> 3) & 0x0007;
51 clk->rate = clk->parent->rate / bfc_divisors[idx];
52}
53
54static struct clk_ops sh4_bus_clk_ops = {
55 .recalc = bus_clk_recalc,
56};
57
58static void cpu_clk_recalc(struct clk *clk)
59{
60 int idx = (ctrl_inw(FRQCR) >> 6) & 0x0007;
61 clk->rate = clk->parent->rate / ifc_divisors[idx];
62}
63
64static struct clk_ops sh4_cpu_clk_ops = {
65 .recalc = cpu_clk_recalc,
66};
67
68static struct clk_ops *sh4_clk_ops[] = {
69 &sh4_master_clk_ops,
70 &sh4_module_clk_ops,
71 &sh4_bus_clk_ops,
72 &sh4_cpu_clk_ops,
73};
74
75void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
76{
77 if (idx < ARRAY_SIZE(sh4_clk_ops))
78 *ops = sh4_clk_ops[idx];
79}
80
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh73180.c b/arch/sh/kernel/cpu/sh4/clock-sh73180.c
new file mode 100644
index 000000000000..2fa5cb2ae68d
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh73180.c
@@ -0,0 +1,81 @@
1/*
2 * arch/sh/kernel/cpu/sh4/clock-sh73180.c
3 *
4 * SH73180 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * FRQCR parsing hacked out of arch/sh/kernel/time.c
9 *
10 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
11 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
12 * Copyright (C) 2002, 2003, 2004 Paul Mundt
13 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
14 *
15 * This file is subject to the terms and conditions of the GNU General Public
16 * License. See the file "COPYING" in the main directory of this archive
17 * for more details.
18 */
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <asm/clock.h>
22#include <asm/freq.h>
23#include <asm/io.h>
24
25/*
26 * SH73180 uses a common set of divisors, so this is quite simple..
27 */
28static int divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
29
30static void master_clk_init(struct clk *clk)
31{
32 clk->rate *= divisors[ctrl_inl(FRQCR) & 0x0007];
33}
34
35static struct clk_ops sh73180_master_clk_ops = {
36 .init = master_clk_init,
37};
38
39static void module_clk_recalc(struct clk *clk)
40{
41 int idx = (ctrl_inl(FRQCR) & 0x0007);
42 clk->rate = clk->parent->rate / divisors[idx];
43}
44
45static struct clk_ops sh73180_module_clk_ops = {
46 .recalc = module_clk_recalc,
47};
48
49static void bus_clk_recalc(struct clk *clk)
50{
51 int idx = (ctrl_inl(FRQCR) >> 12) & 0x0007;
52 clk->rate = clk->parent->rate / divisors[idx];
53}
54
55static struct clk_ops sh73180_bus_clk_ops = {
56 .recalc = bus_clk_recalc,
57};
58
59static void cpu_clk_recalc(struct clk *clk)
60{
61 int idx = (ctrl_inl(FRQCR) >> 20) & 0x0007;
62 clk->rate = clk->parent->rate / divisors[idx];
63}
64
65static struct clk_ops sh73180_cpu_clk_ops = {
66 .recalc = cpu_clk_recalc,
67};
68
69static struct clk_ops *sh73180_clk_ops[] = {
70 &sh73180_master_clk_ops,
71 &sh73180_module_clk_ops,
72 &sh73180_bus_clk_ops,
73 &sh73180_cpu_clk_ops,
74};
75
76void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
77{
78 if (idx < ARRAY_SIZE(sh73180_clk_ops))
79 *ops = sh73180_clk_ops[idx];
80}
81
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7770.c b/arch/sh/kernel/cpu/sh4/clock-sh7770.c
new file mode 100644
index 000000000000..c8694bac6477
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh7770.c
@@ -0,0 +1,73 @@
1/*
2 * arch/sh/kernel/cpu/sh4/clock-sh7770.c
3 *
4 * SH7770 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <asm/clock.h>
15#include <asm/freq.h>
16#include <asm/io.h>
17
18static int ifc_divisors[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
19static int bfc_divisors[] = { 1, 1, 1, 1, 1, 8,12, 1 };
20static int pfc_divisors[] = { 1, 8, 1,10,12,16, 1, 1 };
21
22static void master_clk_init(struct clk *clk)
23{
24 clk->rate *= pfc_divisors[(ctrl_inl(FRQCR) >> 28) & 0x000f];
25}
26
27static struct clk_ops sh7770_master_clk_ops = {
28 .init = master_clk_init,
29};
30
31static void module_clk_recalc(struct clk *clk)
32{
33 int idx = ((ctrl_inl(FRQCR) >> 28) & 0x000f);
34 clk->rate = clk->parent->rate / pfc_divisors[idx];
35}
36
37static struct clk_ops sh7770_module_clk_ops = {
38 .recalc = module_clk_recalc,
39};
40
41static void bus_clk_recalc(struct clk *clk)
42{
43 int idx = (ctrl_inl(FRQCR) & 0x000f);
44 clk->rate = clk->parent->rate / bfc_divisors[idx];
45}
46
47static struct clk_ops sh7770_bus_clk_ops = {
48 .recalc = bus_clk_recalc,
49};
50
51static void cpu_clk_recalc(struct clk *clk)
52{
53 int idx = ((ctrl_inl(FRQCR) >> 24) & 0x000f);
54 clk->rate = clk->parent->rate / ifc_divisors[idx];
55}
56
57static struct clk_ops sh7770_cpu_clk_ops = {
58 .recalc = cpu_clk_recalc,
59};
60
61static struct clk_ops *sh7770_clk_ops[] = {
62 &sh7770_master_clk_ops,
63 &sh7770_module_clk_ops,
64 &sh7770_bus_clk_ops,
65 &sh7770_cpu_clk_ops,
66};
67
68void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
69{
70 if (idx < ARRAY_SIZE(sh7770_clk_ops))
71 *ops = sh7770_clk_ops[idx];
72}
73
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7780.c b/arch/sh/kernel/cpu/sh4/clock-sh7780.c
new file mode 100644
index 000000000000..93ad367342c9
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh7780.c
@@ -0,0 +1,126 @@
1/*
2 * arch/sh/kernel/cpu/sh4/clock-sh7780.c
3 *
4 * SH7780 support for the clock framework
5 *
6 * Copyright (C) 2005 Paul Mundt
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <asm/clock.h>
15#include <asm/freq.h>
16#include <asm/io.h>
17
18static int ifc_divisors[] = { 2, 4 };
19static int bfc_divisors[] = { 1, 1, 1, 8, 12, 16, 24, 1 };
20static int pfc_divisors[] = { 1, 24, 24, 1 };
21static int cfc_divisors[] = { 1, 1, 4, 1, 6, 1, 1, 1 };
22
23static void master_clk_init(struct clk *clk)
24{
25 clk->rate *= pfc_divisors[ctrl_inl(FRQCR) & 0x0003];
26}
27
28static struct clk_ops sh7780_master_clk_ops = {
29 .init = master_clk_init,
30};
31
32static void module_clk_recalc(struct clk *clk)
33{
34 int idx = (ctrl_inl(FRQCR) & 0x0003);
35 clk->rate = clk->parent->rate / pfc_divisors[idx];
36}
37
38static struct clk_ops sh7780_module_clk_ops = {
39 .recalc = module_clk_recalc,
40};
41
42static void bus_clk_recalc(struct clk *clk)
43{
44 int idx = ((ctrl_inl(FRQCR) >> 16) & 0x0007);
45 clk->rate = clk->parent->rate / bfc_divisors[idx];
46}
47
48static struct clk_ops sh7780_bus_clk_ops = {
49 .recalc = bus_clk_recalc,
50};
51
52static void cpu_clk_recalc(struct clk *clk)
53{
54 int idx = ((ctrl_inl(FRQCR) >> 24) & 0x0001);
55 clk->rate = clk->parent->rate / ifc_divisors[idx];
56}
57
58static struct clk_ops sh7780_cpu_clk_ops = {
59 .recalc = cpu_clk_recalc,
60};
61
62static struct clk_ops *sh7780_clk_ops[] = {
63 &sh7780_master_clk_ops,
64 &sh7780_module_clk_ops,
65 &sh7780_bus_clk_ops,
66 &sh7780_cpu_clk_ops,
67};
68
69void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
70{
71 if (idx < ARRAY_SIZE(sh7780_clk_ops))
72 *ops = sh7780_clk_ops[idx];
73}
74
75static void shyway_clk_recalc(struct clk *clk)
76{
77 int idx = ((ctrl_inl(FRQCR) >> 20) & 0x0007);
78 clk->rate = clk->parent->rate / cfc_divisors[idx];
79}
80
81static struct clk_ops sh7780_shyway_clk_ops = {
82 .recalc = shyway_clk_recalc,
83};
84
85static struct clk sh7780_shyway_clk = {
86 .name = "shyway_clk",
87 .flags = CLK_ALWAYS_ENABLED,
88 .ops = &sh7780_shyway_clk_ops,
89};
90
91/*
92 * Additional SH7780-specific on-chip clocks that aren't already part of the
93 * clock framework
94 */
95static struct clk *sh7780_onchip_clocks[] = {
96 &sh7780_shyway_clk,
97};
98
99static int __init sh7780_clk_init(void)
100{
101 struct clk *clk = clk_get("master_clk");
102 int i;
103
104 for (i = 0; i < ARRAY_SIZE(sh7780_onchip_clocks); i++) {
105 struct clk *clkp = sh7780_onchip_clocks[i];
106
107 clkp->parent = clk;
108 clk_register(clkp);
109 clk_enable(clkp);
110 }
111
112 /*
113 * Now that we have the rest of the clocks registered, we need to
114 * force the parent clock to propagate so that these clocks will
115 * automatically figure out their rate. We cheat by handing the
116 * parent clock its current rate and forcing child propagation.
117 */
118 clk_set_rate(clk, clk_get_rate(clk));
119
120 clk_put(clk);
121
122 return 0;
123}
124
125arch_initcall(sh7780_clk_init);
126