aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r--arch/sh/kernel/Makefile4
-rw-r--r--arch/sh/kernel/cpu/Makefile9
-rw-r--r--arch/sh/kernel/cpu/bus.c36
-rw-r--r--arch/sh/kernel/cpu/clock.c287
-rw-r--r--arch/sh/kernel/cpu/irq/Makefile7
-rw-r--r--arch/sh/kernel/cpu/irq/imask.c (renamed from arch/sh/kernel/cpu/irq_imask.c)16
-rw-r--r--arch/sh/kernel/cpu/irq/intc2.c284
-rw-r--r--arch/sh/kernel/cpu/irq/ipr.c (renamed from arch/sh/kernel/cpu/irq_ipr.c)217
-rw-r--r--arch/sh/kernel/cpu/irq/pint.c169
-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
-rw-r--r--arch/sh/kernel/cpu/sh4/irq_intc2.c222
-rw-r--r--arch/sh/kernel/io.c41
-rw-r--r--arch/sh/kernel/io_generic.c192
-rw-r--r--arch/sh/kernel/irq.c64
-rw-r--r--arch/sh/kernel/machine_kexec.c112
-rw-r--r--arch/sh/kernel/process.c10
-rw-r--r--arch/sh/kernel/relocate_kernel.S102
-rw-r--r--arch/sh/kernel/time.c518
-rw-r--r--arch/sh/kernel/timers/Makefile8
-rw-r--r--arch/sh/kernel/timers/timer-tmu.c229
-rw-r--r--arch/sh/kernel/timers/timer.c50
31 files changed, 2404 insertions, 1077 deletions
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile
index 8b819698df14..7a86eeb22655 100644
--- a/arch/sh/kernel/Makefile
+++ b/arch/sh/kernel/Makefile
@@ -17,6 +17,4 @@ obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o
17obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o 17obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
18obj-$(CONFIG_MODULES) += module.o 18obj-$(CONFIG_MODULES) += module.o
19obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 19obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
20 20obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
21USE_STANDARD_AS_RULE := true
22
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index cd43714df61a..5bfc33bec5d0 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -2,15 +2,12 @@
2# Makefile for the Linux/SuperH CPU-specifc backends. 2# Makefile for the Linux/SuperH CPU-specifc backends.
3# 3#
4 4
5obj-y := irq_ipr.o irq_imask.o init.o bus.o 5obj-y += irq/ init.o bus.o clock.o
6 6
7obj-$(CONFIG_CPU_SH2) += sh2/ 7obj-$(CONFIG_CPU_SH2) += sh2/
8obj-$(CONFIG_CPU_SH3) += sh3/ 8obj-$(CONFIG_CPU_SH3) += sh3/
9obj-$(CONFIG_CPU_SH4) += sh4/ 9obj-$(CONFIG_CPU_SH4) += sh4/
10 10
11obj-$(CONFIG_SH_RTC) += rtc.o 11obj-$(CONFIG_SH_RTC) += rtc.o
12obj-$(CONFIG_UBC_WAKEUP) += ubc.o 12obj-$(CONFIG_UBC_WAKEUP) += ubc.o
13obj-$(CONFIG_SH_ADC) += adc.o 13obj-$(CONFIG_SH_ADC) += adc.o
14
15USE_STANDARD_AS_RULE := true
16
diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c
index d4fee2a79373..fc6c4bd40c65 100644
--- a/arch/sh/kernel/cpu/bus.c
+++ b/arch/sh/kernel/cpu/bus.c
@@ -53,21 +53,6 @@ static int sh_bus_resume(struct device *dev)
53 return 0; 53 return 0;
54} 54}
55 55
56static struct device sh_bus_devices[SH_NR_BUSES] = {
57 {
58 .bus_id = SH_BUS_NAME_VIRT,
59 },
60};
61
62struct bus_type sh_bus_types[SH_NR_BUSES] = {
63 {
64 .name = SH_BUS_NAME_VIRT,
65 .match = sh_bus_match,
66 .suspend = sh_bus_suspend,
67 .resume = sh_bus_resume,
68 },
69};
70
71static int sh_device_probe(struct device *dev) 56static int sh_device_probe(struct device *dev)
72{ 57{
73 struct sh_dev *shdev = to_sh_dev(dev); 58 struct sh_dev *shdev = to_sh_dev(dev);
@@ -90,6 +75,23 @@ static int sh_device_remove(struct device *dev)
90 return 0; 75 return 0;
91} 76}
92 77
78static struct device sh_bus_devices[SH_NR_BUSES] = {
79 {
80 .bus_id = SH_BUS_NAME_VIRT,
81 },
82};
83
84struct bus_type sh_bus_types[SH_NR_BUSES] = {
85 {
86 .name = SH_BUS_NAME_VIRT,
87 .match = sh_bus_match,
88 .probe = sh_bus_probe,
89 .remove = sh_bus_remove,
90 .suspend = sh_bus_suspend,
91 .resume = sh_bus_resume,
92 },
93};
94
93int sh_device_register(struct sh_dev *dev) 95int sh_device_register(struct sh_dev *dev)
94{ 96{
95 if (!dev) 97 if (!dev)
@@ -107,6 +109,8 @@ int sh_device_register(struct sh_dev *dev)
107 /* This is needed for USB OHCI to work */ 109 /* This is needed for USB OHCI to work */
108 if (dev->dma_mask) 110 if (dev->dma_mask)
109 dev->dev.dma_mask = dev->dma_mask; 111 dev->dev.dma_mask = dev->dma_mask;
112 if (dev->coherent_dma_mask)
113 dev->dev.coherent_dma_mask = dev->coherent_dma_mask;
110 114
111 snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u", 115 snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u",
112 dev->name, dev->dev_id); 116 dev->name, dev->dev_id);
@@ -133,8 +137,6 @@ int sh_driver_register(struct sh_driver *drv)
133 return -EINVAL; 137 return -EINVAL;
134 } 138 }
135 139
136 drv->drv.probe = sh_device_probe;
137 drv->drv.remove = sh_device_remove;
138 drv->drv.bus = &sh_bus_types[drv->bus_id]; 140 drv->drv.bus = &sh_bus_types[drv->bus_id];
139 141
140 return driver_register(&drv->drv); 142 return driver_register(&drv->drv);
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/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile
new file mode 100644
index 000000000000..e3cccea15e1d
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for the Linux/SuperH CPU-specifc IRQ handlers.
3#
4obj-y += ipr.o imask.o
5
6obj-$(CONFIG_CPU_HAS_PINT_IRQ) += pint.o
7obj-$(CONFIG_CPU_HAS_INTC2_IRQ) += intc2.o
diff --git a/arch/sh/kernel/cpu/irq_imask.c b/arch/sh/kernel/cpu/irq/imask.c
index a963d00a971e..baed9a550d39 100644
--- a/arch/sh/kernel/cpu/irq_imask.c
+++ b/arch/sh/kernel/cpu/irq/imask.c
@@ -1,16 +1,12 @@
1/* $Id: irq_imask.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ 1/*
2 * 2 * arch/sh/kernel/cpu/irq/imask.c
3 * linux/arch/sh/kernel/irq_imask.c
4 * 3 *
5 * Copyright (C) 1999, 2000 Niibe Yutaka 4 * Copyright (C) 1999, 2000 Niibe Yutaka
6 * 5 *
7 * Simple interrupt handling using IMASK of SR register. 6 * Simple interrupt handling using IMASK of SR register.
8 * 7 *
9 */ 8 */
10
11/* NOTE: Will not work on level 15 */ 9/* NOTE: Will not work on level 15 */
12
13
14#include <linux/ptrace.h> 10#include <linux/ptrace.h>
15#include <linux/errno.h> 11#include <linux/errno.h>
16#include <linux/kernel_stat.h> 12#include <linux/kernel_stat.h>
@@ -19,13 +15,11 @@
19#include <linux/interrupt.h> 15#include <linux/interrupt.h>
20#include <linux/init.h> 16#include <linux/init.h>
21#include <linux/bitops.h> 17#include <linux/bitops.h>
22
23#include <asm/system.h>
24#include <asm/irq.h>
25
26#include <linux/spinlock.h> 18#include <linux/spinlock.h>
27#include <linux/cache.h> 19#include <linux/cache.h>
28#include <linux/irq.h> 20#include <linux/irq.h>
21#include <asm/system.h>
22#include <asm/irq.h>
29 23
30/* Bitmap of IRQ masked */ 24/* Bitmap of IRQ masked */
31static unsigned long imask_mask = 0x7fff; 25static unsigned long imask_mask = 0x7fff;
@@ -40,7 +34,7 @@ static void end_imask_irq(unsigned int irq);
40#define IMASK_PRIORITY 15 34#define IMASK_PRIORITY 15
41 35
42static unsigned int startup_imask_irq(unsigned int irq) 36static unsigned int startup_imask_irq(unsigned int irq)
43{ 37{
44 /* Nothing to do */ 38 /* Nothing to do */
45 return 0; /* never anything pending */ 39 return 0; /* never anything pending */
46} 40}
diff --git a/arch/sh/kernel/cpu/irq/intc2.c b/arch/sh/kernel/cpu/irq/intc2.c
new file mode 100644
index 000000000000..06e8afab32e4
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq/intc2.c
@@ -0,0 +1,284 @@
1/*
2 * Interrupt handling for INTC2-based IRQ.
3 *
4 * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
5 * Copyright (C) 2005, 2006 Paul Mundt (lethal@linux-sh.org)
6 *
7 * May be copied or modified under the terms of the GNU General Public
8 * License. See linux/COPYING for more information.
9 *
10 * These are the "new Hitachi style" interrupts, as present on the
11 * Hitachi 7751, the STM ST40 STB1, SH7760, and SH7780.
12 */
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/irq.h>
17#include <asm/system.h>
18#include <asm/io.h>
19#include <asm/machvec.h>
20
21struct intc2_data {
22 unsigned char msk_offset;
23 unsigned char msk_shift;
24
25 int (*clear_irq) (int);
26};
27
28static struct intc2_data intc2_data[NR_INTC2_IRQS];
29
30static void enable_intc2_irq(unsigned int irq);
31static void disable_intc2_irq(unsigned int irq);
32
33/* shutdown is same as "disable" */
34#define shutdown_intc2_irq disable_intc2_irq
35
36static void mask_and_ack_intc2(unsigned int);
37static void end_intc2_irq(unsigned int irq);
38
39static unsigned int startup_intc2_irq(unsigned int irq)
40{
41 enable_intc2_irq(irq);
42 return 0; /* never anything pending */
43}
44
45static struct hw_interrupt_type intc2_irq_type = {
46 .typename = "INTC2-IRQ",
47 .startup = startup_intc2_irq,
48 .shutdown = shutdown_intc2_irq,
49 .enable = enable_intc2_irq,
50 .disable = disable_intc2_irq,
51 .ack = mask_and_ack_intc2,
52 .end = end_intc2_irq
53};
54
55static void disable_intc2_irq(unsigned int irq)
56{
57 int irq_offset = irq - INTC2_FIRST_IRQ;
58 int msk_shift, msk_offset;
59
60 /* Sanity check */
61 if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS))
62 return;
63
64 msk_shift = intc2_data[irq_offset].msk_shift;
65 msk_offset = intc2_data[irq_offset].msk_offset;
66
67 ctrl_outl(1 << msk_shift,
68 INTC2_BASE + INTC2_INTMSK_OFFSET + msk_offset);
69}
70
71static void enable_intc2_irq(unsigned int irq)
72{
73 int irq_offset = irq - INTC2_FIRST_IRQ;
74 int msk_shift, msk_offset;
75
76 /* Sanity check */
77 if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS))
78 return;
79
80 msk_shift = intc2_data[irq_offset].msk_shift;
81 msk_offset = intc2_data[irq_offset].msk_offset;
82
83 ctrl_outl(1 << msk_shift,
84 INTC2_BASE + INTC2_INTMSKCLR_OFFSET + msk_offset);
85}
86
87static void mask_and_ack_intc2(unsigned int irq)
88{
89 disable_intc2_irq(irq);
90}
91
92static void end_intc2_irq(unsigned int irq)
93{
94 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
95 enable_intc2_irq(irq);
96
97 if (unlikely(intc2_data[irq - INTC2_FIRST_IRQ].clear_irq))
98 intc2_data[irq - INTC2_FIRST_IRQ].clear_irq(irq);
99}
100
101/*
102 * Setup an INTC2 style interrupt.
103 * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
104 * allowing the use of the numbers straight out of the datasheet.
105 * For example:
106 * PIO1 which is INTPRI00[19,16] and INTMSK00[13]
107 * would be: ^ ^ ^ ^
108 * | | | |
109 * make_intc2_irq(84, 0, 16, 0, 13);
110 */
111void make_intc2_irq(unsigned int irq,
112 unsigned int ipr_offset, unsigned int ipr_shift,
113 unsigned int msk_offset, unsigned int msk_shift,
114 unsigned int priority)
115{
116 int irq_offset = irq - INTC2_FIRST_IRQ;
117 unsigned int flags;
118 unsigned long ipr;
119
120 if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS))
121 return;
122
123 disable_irq_nosync(irq);
124
125 /* Fill the data we need */
126 intc2_data[irq_offset].msk_offset = msk_offset;
127 intc2_data[irq_offset].msk_shift = msk_shift;
128 intc2_data[irq_offset].clear_irq = NULL;
129
130 /* Set the priority level */
131 local_irq_save(flags);
132
133 ipr = ctrl_inl(INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset);
134 ipr &= ~(0xf << ipr_shift);
135 ipr |= priority << ipr_shift;
136 ctrl_outl(ipr, INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset);
137
138 local_irq_restore(flags);
139
140 irq_desc[irq].handler = &intc2_irq_type;
141
142 disable_intc2_irq(irq);
143}
144
145static struct intc2_init {
146 unsigned short irq;
147 unsigned char ipr_offset, ipr_shift;
148 unsigned char msk_offset, msk_shift;
149 unsigned char priority;
150} intc2_init_data[] __initdata = {
151#if defined(CONFIG_CPU_SUBTYPE_ST40)
152 {64, 0, 0, 0, 0, 13}, /* PCI serr */
153 {65, 0, 4, 0, 1, 13}, /* PCI err */
154 {66, 0, 4, 0, 2, 13}, /* PCI ad */
155 {67, 0, 4, 0, 3, 13}, /* PCI pwd down */
156 {72, 0, 8, 0, 5, 13}, /* DMAC INT0 */
157 {73, 0, 8, 0, 6, 13}, /* DMAC INT1 */
158 {74, 0, 8, 0, 7, 13}, /* DMAC INT2 */
159 {75, 0, 8, 0, 8, 13}, /* DMAC INT3 */
160 {76, 0, 8, 0, 9, 13}, /* DMAC INT4 */
161 {78, 0, 8, 0, 11, 13}, /* DMAC ERR */
162 {80, 0, 12, 0, 12, 13}, /* PIO0 */
163 {84, 0, 16, 0, 13, 13}, /* PIO1 */
164 {88, 0, 20, 0, 14, 13}, /* PIO2 */
165 {112, 4, 0, 4, 0, 13}, /* Mailbox */
166 #ifdef CONFIG_CPU_SUBTYPE_ST40GX1
167 {116, 4, 4, 4, 4, 13}, /* SSC0 */
168 {120, 4, 8, 4, 8, 13}, /* IR Blaster */
169 {124, 4, 12, 4, 12, 13}, /* USB host */
170 {128, 4, 16, 4, 16, 13}, /* Video processor BLITTER */
171 {132, 4, 20, 4, 20, 13}, /* UART0 */
172 {134, 4, 20, 4, 22, 13}, /* UART2 */
173 {136, 4, 24, 4, 24, 13}, /* IO_PIO0 */
174 {140, 4, 28, 4, 28, 13}, /* EMPI */
175 {144, 8, 0, 8, 0, 13}, /* MAFE */
176 {148, 8, 4, 8, 4, 13}, /* PWM */
177 {152, 8, 8, 8, 8, 13}, /* SSC1 */
178 {156, 8, 12, 8, 12, 13}, /* IO_PIO1 */
179 {160, 8, 16, 8, 16, 13}, /* USB target */
180 {164, 8, 20, 8, 20, 13}, /* UART1 */
181 {168, 8, 24, 8, 24, 13}, /* Teletext */
182 {172, 8, 28, 8, 28, 13}, /* VideoSync VTG */
183 {173, 8, 28, 8, 29, 13}, /* VideoSync DVP0 */
184 {174, 8, 28, 8, 30, 13}, /* VideoSync DVP1 */
185#endif
186#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
187/*
188 * SH7760 INTC2-Style interrupts, vectors IRQ48-111 INTEVT 0x800-0xFE0
189 */
190 /* INTPRIO0 | INTMSK0 */
191 {48, 0, 28, 0, 31, 3}, /* IRQ 4 */
192 {49, 0, 24, 0, 30, 3}, /* IRQ 3 */
193 {50, 0, 20, 0, 29, 3}, /* IRQ 2 */
194 {51, 0, 16, 0, 28, 3}, /* IRQ 1 */
195 /* 52-55 (INTEVT 0x880-0x8E0) unused/reserved */
196 /* INTPRIO4 | INTMSK0 */
197 {56, 4, 28, 0, 25, 3}, /* HCAN2_CHAN0 */
198 {57, 4, 24, 0, 24, 3}, /* HCAN2_CHAN1 */
199 {58, 4, 20, 0, 23, 3}, /* I2S_CHAN0 */
200 {59, 4, 16, 0, 22, 3}, /* I2S_CHAN1 */
201 {60, 4, 12, 0, 21, 3}, /* AC97_CHAN0 */
202 {61, 4, 8, 0, 20, 3}, /* AC97_CHAN1 */
203 {62, 4, 4, 0, 19, 3}, /* I2C_CHAN0 */
204 {63, 4, 0, 0, 18, 3}, /* I2C_CHAN1 */
205 /* INTPRIO8 | INTMSK0 */
206 {52, 8, 16, 0, 11, 3}, /* SCIF0_ERI_IRQ */
207 {53, 8, 16, 0, 10, 3}, /* SCIF0_RXI_IRQ */
208 {54, 8, 16, 0, 9, 3}, /* SCIF0_BRI_IRQ */
209 {55, 8, 16, 0, 8, 3}, /* SCIF0_TXI_IRQ */
210 {64, 8, 28, 0, 17, 3}, /* USBHI_IRQ */
211 {65, 8, 24, 0, 16, 3}, /* LCDC */
212 /* 66, 67 unused */
213 {68, 8, 20, 0, 14, 13}, /* DMABRGI0_IRQ */
214 {69, 8, 20, 0, 13, 13}, /* DMABRGI1_IRQ */
215 {70, 8, 20, 0, 12, 13}, /* DMABRGI2_IRQ */
216 /* 71 unused */
217 {72, 8, 12, 0, 7, 3}, /* SCIF1_ERI_IRQ */
218 {73, 8, 12, 0, 6, 3}, /* SCIF1_RXI_IRQ */
219 {74, 8, 12, 0, 5, 3}, /* SCIF1_BRI_IRQ */
220 {75, 8, 12, 0, 4, 3}, /* SCIF1_TXI_IRQ */
221 {76, 8, 8, 0, 3, 3}, /* SCIF2_ERI_IRQ */
222 {77, 8, 8, 0, 2, 3}, /* SCIF2_RXI_IRQ */
223 {78, 8, 8, 0, 1, 3}, /* SCIF2_BRI_IRQ */
224 {79, 8, 8, 0, 0, 3}, /* SCIF2_TXI_IRQ */
225 /* | INTMSK4 */
226 {80, 8, 4, 4, 23, 3}, /* SIM_ERI */
227 {81, 8, 4, 4, 22, 3}, /* SIM_RXI */
228 {82, 8, 4, 4, 21, 3}, /* SIM_TXI */
229 {83, 8, 4, 4, 20, 3}, /* SIM_TEI */
230 {84, 8, 0, 4, 19, 3}, /* HSPII */
231 /* INTPRIOC | INTMSK4 */
232 /* 85-87 unused/reserved */
233 {88, 12, 20, 4, 18, 3}, /* MMCI0 */
234 {89, 12, 20, 4, 17, 3}, /* MMCI1 */
235 {90, 12, 20, 4, 16, 3}, /* MMCI2 */
236 {91, 12, 20, 4, 15, 3}, /* MMCI3 */
237 {92, 12, 12, 4, 6, 3}, /* MFI (unsure, bug? in my 7760 manual*/
238 /* 93-107 reserved/undocumented */
239 {108,12, 4, 4, 1, 3}, /* ADC */
240 {109,12, 0, 4, 0, 3}, /* CMTI */
241 /* 110-111 reserved/unused */
242#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
243 { TIMER_IRQ, 0, 24, 0, INTC_TMU0_MSK, 2},
244#ifdef CONFIG_SH_RTC
245 { RTC_IRQ, 4, 0, 0, INTC_RTC_MSK, TIMER_PRIORITY },
246#endif
247 { SCIF0_ERI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
248 { SCIF0_RXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
249 { SCIF0_BRI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
250 { SCIF0_TXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
251
252 { SCIF1_ERI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
253 { SCIF1_RXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
254 { SCIF1_BRI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
255 { SCIF1_TXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
256
257 { PCIC0_IRQ, 0x10, 8, 0, INTC_PCIC0_MSK, PCIC0_PRIORITY },
258 { PCIC1_IRQ, 0x10, 0, 0, INTC_PCIC1_MSK, PCIC1_PRIORITY },
259 { PCIC2_IRQ, 0x14, 24, 0, INTC_PCIC2_MSK, PCIC2_PRIORITY },
260 { PCIC3_IRQ, 0x14, 16, 0, INTC_PCIC3_MSK, PCIC3_PRIORITY },
261 { PCIC4_IRQ, 0x14, 8, 0, INTC_PCIC4_MSK, PCIC4_PRIORITY },
262#endif
263};
264
265void __init init_IRQ_intc2(void)
266{
267 int i;
268
269 for (i = 0; i < ARRAY_SIZE(intc2_init_data); i++) {
270 struct intc2_init *p = intc2_init_data + i;
271 make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
272 p-> msk_offset, p->msk_shift, p->priority);
273 }
274}
275
276/* Adds a termination callback to the interrupt */
277void intc2_add_clear_irq(int irq, int (*fn)(int))
278{
279 if (unlikely(irq < INTC2_FIRST_IRQ))
280 return;
281
282 intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
283}
284
diff --git a/arch/sh/kernel/cpu/irq_ipr.c b/arch/sh/kernel/cpu/irq/ipr.c
index 71f92096132b..fdbd718ae5c6 100644
--- a/arch/sh/kernel/cpu/irq_ipr.c
+++ b/arch/sh/kernel/cpu/irq/ipr.c
@@ -1,6 +1,5 @@
1/* $Id: irq_ipr.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ 1/*
2 * 2 * arch/sh/kernel/cpu/irq/ipr.c
3 * linux/arch/sh/kernel/irq_ipr.c
4 * 3 *
5 * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi 4 * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
6 * Copyright (C) 2000 Kazumoto Kojima 5 * Copyright (C) 2000 Kazumoto Kojima
@@ -109,7 +108,8 @@ static void end_ipr_irq(unsigned int irq)
109 enable_ipr_irq(irq); 108 enable_ipr_irq(irq);
110} 109}
111 110
112void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority) 111void make_ipr_irq(unsigned int irq, unsigned int addr, int pos,
112 int priority, int maskpos)
113{ 113{
114 disable_irq_nosync(irq); 114 disable_irq_nosync(irq);
115 ipr_data[irq].addr = addr; 115 ipr_data[irq].addr = addr;
@@ -120,126 +120,47 @@ void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority)
120 disable_ipr_irq(irq); 120 disable_ipr_irq(irq);
121} 121}
122 122
123#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
124 defined(CONFIG_CPU_SUBTYPE_SH7707) || \
125 defined(CONFIG_CPU_SUBTYPE_SH7709)
126static unsigned char pint_map[256];
127static unsigned long portcr_mask = 0;
128
129static void enable_pint_irq(unsigned int irq);
130static void disable_pint_irq(unsigned int irq);
131
132/* shutdown is same as "disable" */
133#define shutdown_pint_irq disable_pint_irq
134
135static void mask_and_ack_pint(unsigned int);
136static void end_pint_irq(unsigned int irq);
137
138static unsigned int startup_pint_irq(unsigned int irq)
139{
140 enable_pint_irq(irq);
141 return 0; /* never anything pending */
142}
143
144static struct hw_interrupt_type pint_irq_type = {
145 .typename = "PINT-IRQ",
146 .startup = startup_pint_irq,
147 .shutdown = shutdown_pint_irq,
148 .enable = enable_pint_irq,
149 .disable = disable_pint_irq,
150 .ack = mask_and_ack_pint,
151 .end = end_pint_irq
152};
153
154static void disable_pint_irq(unsigned int irq)
155{
156 unsigned long val, flags;
157
158 local_irq_save(flags);
159 val = ctrl_inw(INTC_INTER);
160 val &= ~(1 << (irq - PINT_IRQ_BASE));
161 ctrl_outw(val, INTC_INTER); /* disable PINTn */
162 portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
163 local_irq_restore(flags);
164}
165
166static void enable_pint_irq(unsigned int irq)
167{
168 unsigned long val, flags;
169
170 local_irq_save(flags);
171 val = ctrl_inw(INTC_INTER);
172 val |= 1 << (irq - PINT_IRQ_BASE);
173 ctrl_outw(val, INTC_INTER); /* enable PINTn */
174 portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
175 local_irq_restore(flags);
176}
177
178static void mask_and_ack_pint(unsigned int irq)
179{
180 disable_pint_irq(irq);
181}
182
183static void end_pint_irq(unsigned int irq)
184{
185 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
186 enable_pint_irq(irq);
187}
188
189void make_pint_irq(unsigned int irq)
190{
191 disable_irq_nosync(irq);
192 irq_desc[irq].handler = &pint_irq_type;
193 disable_pint_irq(irq);
194}
195#endif
196
197void __init init_IRQ(void) 123void __init init_IRQ(void)
198{ 124{
199#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ 125#ifndef CONFIG_CPU_SUBTYPE_SH7780
200 defined(CONFIG_CPU_SUBTYPE_SH7707) || \ 126 make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY, 0);
201 defined(CONFIG_CPU_SUBTYPE_SH7709) 127 make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY, 0);
202 int i;
203#endif
204
205 make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY);
206 make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY);
207#if defined(CONFIG_SH_RTC) 128#if defined(CONFIG_SH_RTC)
208 make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); 129 make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY, 0);
209#endif 130#endif
210 131
211#ifdef SCI_ERI_IRQ 132#ifdef SCI_ERI_IRQ
212 make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); 133 make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0);
213 make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); 134 make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0);
214 make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); 135 make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0);
215#endif 136#endif
216 137
217#ifdef SCIF1_ERI_IRQ 138#ifdef SCIF1_ERI_IRQ
218 make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 139 make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
219 make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 140 make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
220 make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 141 make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
221 make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 142 make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
222#endif 143#endif
223 144
224#if defined(CONFIG_CPU_SUBTYPE_SH7300) 145#if defined(CONFIG_CPU_SUBTYPE_SH7300)
225 make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY); 146 make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY, 0);
226 make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); 147 make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0);
227 make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); 148 make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0);
228 make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY); 149 make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY, 0);
229#endif 150#endif
230 151
231#ifdef SCIF_ERI_IRQ 152#ifdef SCIF_ERI_IRQ
232 make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 153 make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
233 make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 154 make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
234 make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 155 make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
235 make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 156 make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
236#endif 157#endif
237 158
238#ifdef IRDA_ERI_IRQ 159#ifdef IRDA_ERI_IRQ
239 make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 160 make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
240 make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 161 make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
241 make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 162 make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
242 make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 163 make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
243#endif 164#endif
244 165
245#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ 166#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
@@ -254,86 +175,32 @@ void __init init_IRQ(void)
254 * You should set corresponding bits of PFC to "00" 175 * You should set corresponding bits of PFC to "00"
255 * to enable these interrupts. 176 * to enable these interrupts.
256 */ 177 */
257 make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY); 178 make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY, 0);
258 make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY); 179 make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY, 0);
259 make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY); 180 make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY, 0);
260 make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY); 181 make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY, 0);
261 make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY); 182 make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY, 0);
262 make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY); 183 make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY, 0);
263#if !defined(CONFIG_CPU_SUBTYPE_SH7300) 184#endif
264 make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); 185#endif
265 make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY);
266 enable_ipr_irq(PINT0_IRQ);
267 enable_ipr_irq(PINT8_IRQ);
268 186
269 for(i = 0; i < 16; i++) 187#ifdef CONFIG_CPU_HAS_PINT_IRQ
270 make_pint_irq(PINT_IRQ_BASE + i); 188 init_IRQ_pint();
271 for(i = 0; i < 256; i++) 189#endif
272 {
273 if(i & 1) pint_map[i] = 0;
274 else if(i & 2) pint_map[i] = 1;
275 else if(i & 4) pint_map[i] = 2;
276 else if(i & 8) pint_map[i] = 3;
277 else if(i & 0x10) pint_map[i] = 4;
278 else if(i & 0x20) pint_map[i] = 5;
279 else if(i & 0x40) pint_map[i] = 6;
280 else if(i & 0x80) pint_map[i] = 7;
281 }
282#endif /* !CONFIG_CPU_SUBTYPE_SH7300 */
283#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 || CONFIG_CPU_SUBTYPE_SH7300*/
284 190
285#ifdef CONFIG_CPU_SUBTYPE_ST40 191#ifdef CONFIG_CPU_HAS_INTC2_IRQ
286 init_IRQ_intc2(); 192 init_IRQ_intc2();
287#endif 193#endif
288
289 /* Perform the machine specific initialisation */ 194 /* Perform the machine specific initialisation */
290 if (sh_mv.mv_init_irq != NULL) { 195 if (sh_mv.mv_init_irq != NULL)
291 sh_mv.mv_init_irq(); 196 sh_mv.mv_init_irq();
292 }
293} 197}
294#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ 198
295 defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) 199#if !defined(CONFIG_CPU_HAS_PINT_IRQ)
296int ipr_irq_demux(int irq) 200int ipr_irq_demux(int irq)
297{ 201{
298#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
299 unsigned long creg, dreg, d, sav;
300
301 if(irq == PINT0_IRQ)
302 {
303#if defined(CONFIG_CPU_SUBTYPE_SH7707)
304 creg = PORT_PACR;
305 dreg = PORT_PADR;
306#else
307 creg = PORT_PCCR;
308 dreg = PORT_PCDR;
309#endif
310 sav = ctrl_inw(creg);
311 ctrl_outw(sav | portcr_mask, creg);
312 d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff;
313 ctrl_outw(sav, creg);
314 if(d == 0) return irq;
315 return PINT_IRQ_BASE + pint_map[d];
316 }
317 else if(irq == PINT8_IRQ)
318 {
319#if defined(CONFIG_CPU_SUBTYPE_SH7707)
320 creg = PORT_PBCR;
321 dreg = PORT_PBDR;
322#else
323 creg = PORT_PFCR;
324 dreg = PORT_PFDR;
325#endif
326 sav = ctrl_inw(creg);
327 ctrl_outw(sav | (portcr_mask >> 16), creg);
328 d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff;
329 ctrl_outw(sav, creg);
330 if(d == 0) return irq;
331 return PINT_IRQ_BASE + 8 + pint_map[d];
332 }
333#endif
334 return irq; 202 return irq;
335} 203}
336#endif 204#endif
337 205
338EXPORT_SYMBOL(make_ipr_irq); 206EXPORT_SYMBOL(make_ipr_irq);
339
diff --git a/arch/sh/kernel/cpu/irq/pint.c b/arch/sh/kernel/cpu/irq/pint.c
new file mode 100644
index 000000000000..95d6024fe1ae
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq/pint.c
@@ -0,0 +1,169 @@
1/*
2 * arch/sh/kernel/cpu/irq/pint.c - Interrupt handling for PINT-based IRQs.
3 *
4 * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
5 * Copyright (C) 2000 Kazumoto Kojima
6 * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
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
13#include <linux/config.h>
14#include <linux/init.h>
15#include <linux/irq.h>
16#include <linux/module.h>
17
18#include <asm/system.h>
19#include <asm/io.h>
20#include <asm/machvec.h>
21
22static unsigned char pint_map[256];
23static unsigned long portcr_mask;
24
25static void enable_pint_irq(unsigned int irq);
26static void disable_pint_irq(unsigned int irq);
27
28/* shutdown is same as "disable" */
29#define shutdown_pint_irq disable_pint_irq
30
31static void mask_and_ack_pint(unsigned int);
32static void end_pint_irq(unsigned int irq);
33
34static unsigned int startup_pint_irq(unsigned int irq)
35{
36 enable_pint_irq(irq);
37 return 0; /* never anything pending */
38}
39
40static struct hw_interrupt_type pint_irq_type = {
41 .typename = "PINT-IRQ",
42 .startup = startup_pint_irq,
43 .shutdown = shutdown_pint_irq,
44 .enable = enable_pint_irq,
45 .disable = disable_pint_irq,
46 .ack = mask_and_ack_pint,
47 .end = end_pint_irq
48};
49
50static void disable_pint_irq(unsigned int irq)
51{
52 unsigned long val, flags;
53
54 local_irq_save(flags);
55 val = ctrl_inw(INTC_INTER);
56 val &= ~(1 << (irq - PINT_IRQ_BASE));
57 ctrl_outw(val, INTC_INTER); /* disable PINTn */
58 portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
59 local_irq_restore(flags);
60}
61
62static void enable_pint_irq(unsigned int irq)
63{
64 unsigned long val, flags;
65
66 local_irq_save(flags);
67 val = ctrl_inw(INTC_INTER);
68 val |= 1 << (irq - PINT_IRQ_BASE);
69 ctrl_outw(val, INTC_INTER); /* enable PINTn */
70 portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
71 local_irq_restore(flags);
72}
73
74static void mask_and_ack_pint(unsigned int irq)
75{
76 disable_pint_irq(irq);
77}
78
79static void end_pint_irq(unsigned int irq)
80{
81 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
82 enable_pint_irq(irq);
83}
84
85void make_pint_irq(unsigned int irq)
86{
87 disable_irq_nosync(irq);
88 irq_desc[irq].handler = &pint_irq_type;
89 disable_pint_irq(irq);
90}
91
92void __init init_IRQ_pint(void)
93{
94 int i;
95
96 make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY);
97 make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY);
98
99 enable_irq(PINT0_IRQ);
100 enable_irq(PINT8_IRQ);
101
102 for(i = 0; i < 16; i++)
103 make_pint_irq(PINT_IRQ_BASE + i);
104
105 for(i = 0; i < 256; i++) {
106 if (i & 1)
107 pint_map[i] = 0;
108 else if (i & 2)
109 pint_map[i] = 1;
110 else if (i & 4)
111 pint_map[i] = 2;
112 else if (i & 8)
113 pint_map[i] = 3;
114 else if (i & 0x10)
115 pint_map[i] = 4;
116 else if (i & 0x20)
117 pint_map[i] = 5;
118 else if (i & 0x40)
119 pint_map[i] = 6;
120 else if (i & 0x80)
121 pint_map[i] = 7;
122 }
123}
124
125int ipr_irq_demux(int irq)
126{
127 unsigned long creg, dreg, d, sav;
128
129 if (irq == PINT0_IRQ) {
130#if defined(CONFIG_CPU_SUBTYPE_SH7707)
131 creg = PORT_PACR;
132 dreg = PORT_PADR;
133#else
134 creg = PORT_PCCR;
135 dreg = PORT_PCDR;
136#endif
137 sav = ctrl_inw(creg);
138 ctrl_outw(sav | portcr_mask, creg);
139 d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) &
140 ctrl_inw(INTC_INTER) & 0xff;
141 ctrl_outw(sav, creg);
142
143 if (d == 0)
144 return irq;
145
146 return PINT_IRQ_BASE + pint_map[d];
147 } else if (irq == PINT8_IRQ) {
148#if defined(CONFIG_CPU_SUBTYPE_SH7707)
149 creg = PORT_PBCR;
150 dreg = PORT_PBDR;
151#else
152 creg = PORT_PFCR;
153 dreg = PORT_PFDR;
154#endif
155 sav = ctrl_inw(creg);
156 ctrl_outw(sav | (portcr_mask >> 16), creg);
157 d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) &
158 (ctrl_inw(INTC_INTER) >> 8) & 0xff;
159 ctrl_outw(sav, creg);
160
161 if (d == 0)
162 return irq;
163
164 return PINT_IRQ_BASE + 8 + pint_map[d];
165 }
166
167 return irq;
168}
169
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
diff --git a/arch/sh/kernel/cpu/sh4/irq_intc2.c b/arch/sh/kernel/cpu/sh4/irq_intc2.c
deleted file mode 100644
index f6b16ba01932..000000000000
--- a/arch/sh/kernel/cpu/sh4/irq_intc2.c
+++ /dev/null
@@ -1,222 +0,0 @@
1/*
2 * linux/arch/sh/kernel/irq_intc2.c
3 *
4 * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
5 *
6 * May be copied or modified under the terms of the GNU General Public
7 * License. See linux/COPYING for more information.
8 *
9 * Interrupt handling for INTC2-based IRQ.
10 *
11 * These are the "new Hitachi style" interrupts, as present on the
12 * Hitachi 7751 and the STM ST40 STB1.
13 */
14
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/irq.h>
18
19#include <asm/system.h>
20#include <asm/io.h>
21#include <asm/machvec.h>
22
23
24struct intc2_data {
25 unsigned char msk_offset;
26 unsigned char msk_shift;
27#ifdef CONFIG_CPU_SUBTYPE_ST40
28 int (*clear_irq) (int);
29#endif
30};
31
32
33static struct intc2_data intc2_data[NR_INTC2_IRQS];
34
35static void enable_intc2_irq(unsigned int irq);
36static void disable_intc2_irq(unsigned int irq);
37
38/* shutdown is same as "disable" */
39#define shutdown_intc2_irq disable_intc2_irq
40
41static void mask_and_ack_intc2(unsigned int);
42static void end_intc2_irq(unsigned int irq);
43
44static unsigned int startup_intc2_irq(unsigned int irq)
45{
46 enable_intc2_irq(irq);
47 return 0; /* never anything pending */
48}
49
50static struct hw_interrupt_type intc2_irq_type = {
51 .typename = "INTC2-IRQ",
52 .startup = startup_intc2_irq,
53 .shutdown = shutdown_intc2_irq,
54 .enable = enable_intc2_irq,
55 .disable = disable_intc2_irq,
56 .ack = mask_and_ack_intc2,
57 .end = end_intc2_irq
58};
59
60static void disable_intc2_irq(unsigned int irq)
61{
62 int irq_offset = irq - INTC2_FIRST_IRQ;
63 int msk_shift, msk_offset;
64
65 // Sanity check
66 if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
67 return;
68
69 msk_shift = intc2_data[irq_offset].msk_shift;
70 msk_offset = intc2_data[irq_offset].msk_offset;
71
72 ctrl_outl(1<<msk_shift,
73 INTC2_BASE+INTC2_INTMSK_OFFSET+msk_offset);
74}
75
76static void enable_intc2_irq(unsigned int irq)
77{
78 int irq_offset = irq - INTC2_FIRST_IRQ;
79 int msk_shift, msk_offset;
80
81 /* Sanity check */
82 if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
83 return;
84
85 msk_shift = intc2_data[irq_offset].msk_shift;
86 msk_offset = intc2_data[irq_offset].msk_offset;
87
88 ctrl_outl(1<<msk_shift,
89 INTC2_BASE+INTC2_INTMSKCLR_OFFSET+msk_offset);
90}
91
92static void mask_and_ack_intc2(unsigned int irq)
93{
94 disable_intc2_irq(irq);
95}
96
97static void end_intc2_irq(unsigned int irq)
98{
99 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
100 enable_intc2_irq(irq);
101
102#ifdef CONFIG_CPU_SUBTYPE_ST40
103 if (intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)
104 intc2_data[irq - INTC2_FIRST_IRQ].clear_irq (irq);
105#endif
106}
107
108/*
109 * Setup an INTC2 style interrupt.
110 * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
111 * allowing the use of the numbers straight out of the datasheet.
112 * For example:
113 * PIO1 which is INTPRI00[19,16] and INTMSK00[13]
114 * would be: ^ ^ ^ ^
115 * | | | |
116 * make_intc2_irq(84, 0, 16, 0, 13);
117 */
118void make_intc2_irq(unsigned int irq,
119 unsigned int ipr_offset, unsigned int ipr_shift,
120 unsigned int msk_offset, unsigned int msk_shift,
121 unsigned int priority)
122{
123 int irq_offset = irq - INTC2_FIRST_IRQ;
124 unsigned int flags;
125 unsigned long ipr;
126
127 if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
128 return;
129
130 disable_irq_nosync(irq);
131
132 /* Fill the data we need */
133 intc2_data[irq_offset].msk_offset = msk_offset;
134 intc2_data[irq_offset].msk_shift = msk_shift;
135#ifdef CONFIG_CPU_SUBTYPE_ST40
136 intc2_data[irq_offset].clear_irq = NULL;
137#endif
138
139 /* Set the priority level */
140 local_irq_save(flags);
141
142 ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
143 ipr&=~(0xf<<ipr_shift);
144 ipr|=(priority)<<ipr_shift;
145 ctrl_outl(ipr, INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
146
147 local_irq_restore(flags);
148
149 irq_desc[irq].handler=&intc2_irq_type;
150
151 disable_intc2_irq(irq);
152}
153
154#ifdef CONFIG_CPU_SUBTYPE_ST40
155
156struct intc2_init {
157 unsigned short irq;
158 unsigned char ipr_offset, ipr_shift;
159 unsigned char msk_offset, msk_shift;
160};
161
162static struct intc2_init intc2_init_data[] __initdata = {
163 {64, 0, 0, 0, 0}, /* PCI serr */
164 {65, 0, 4, 0, 1}, /* PCI err */
165 {66, 0, 4, 0, 2}, /* PCI ad */
166 {67, 0, 4, 0, 3}, /* PCI pwd down */
167 {72, 0, 8, 0, 5}, /* DMAC INT0 */
168 {73, 0, 8, 0, 6}, /* DMAC INT1 */
169 {74, 0, 8, 0, 7}, /* DMAC INT2 */
170 {75, 0, 8, 0, 8}, /* DMAC INT3 */
171 {76, 0, 8, 0, 9}, /* DMAC INT4 */
172 {78, 0, 8, 0, 11}, /* DMAC ERR */
173 {80, 0, 12, 0, 12}, /* PIO0 */
174 {84, 0, 16, 0, 13}, /* PIO1 */
175 {88, 0, 20, 0, 14}, /* PIO2 */
176 {112, 4, 0, 4, 0}, /* Mailbox */
177#ifdef CONFIG_CPU_SUBTYPE_ST40GX1
178 {116, 4, 4, 4, 4}, /* SSC0 */
179 {120, 4, 8, 4, 8}, /* IR Blaster */
180 {124, 4, 12, 4, 12}, /* USB host */
181 {128, 4, 16, 4, 16}, /* Video processor BLITTER */
182 {132, 4, 20, 4, 20}, /* UART0 */
183 {134, 4, 20, 4, 22}, /* UART2 */
184 {136, 4, 24, 4, 24}, /* IO_PIO0 */
185 {140, 4, 28, 4, 28}, /* EMPI */
186 {144, 8, 0, 8, 0}, /* MAFE */
187 {148, 8, 4, 8, 4}, /* PWM */
188 {152, 8, 8, 8, 8}, /* SSC1 */
189 {156, 8, 12, 8, 12}, /* IO_PIO1 */
190 {160, 8, 16, 8, 16}, /* USB target */
191 {164, 8, 20, 8, 20}, /* UART1 */
192 {168, 8, 24, 8, 24}, /* Teletext */
193 {172, 8, 28, 8, 28}, /* VideoSync VTG */
194 {173, 8, 28, 8, 29}, /* VideoSync DVP0 */
195 {174, 8, 28, 8, 30}, /* VideoSync DVP1 */
196#endif
197};
198
199void __init init_IRQ_intc2(void)
200{
201 struct intc2_init *p;
202
203 printk(KERN_ALERT "init_IRQ_intc2\n");
204
205 for (p = intc2_init_data;
206 p<intc2_init_data+ARRAY_SIZE(intc2_init_data);
207 p++) {
208 make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
209 p-> msk_offset, p->msk_shift, 13);
210 }
211}
212
213/* Adds a termination callback to the interrupt */
214void intc2_add_clear_irq(int irq, int (*fn)(int))
215{
216 if (irq < INTC2_FIRST_IRQ)
217 return;
218
219 intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
220}
221
222#endif /* CONFIG_CPU_SUBTYPE_ST40 */
diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c
index d9932f25993b..71c9fde2fd90 100644
--- a/arch/sh/kernel/io.c
+++ b/arch/sh/kernel/io.c
@@ -2,58 +2,73 @@
2 * linux/arch/sh/kernel/io.c 2 * linux/arch/sh/kernel/io.c
3 * 3 *
4 * Copyright (C) 2000 Stuart Menefy 4 * Copyright (C) 2000 Stuart Menefy
5 * Copyright (C) 2005 Paul Mundt
5 * 6 *
6 * Provide real functions which expand to whatever the header file defined. 7 * Provide real functions which expand to whatever the header file defined.
7 * Also definitions of machine independent IO functions. 8 * Also definitions of machine independent IO functions.
9 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file "COPYING" in the main directory of this archive
12 * for more details.
8 */ 13 */
9
10#include <asm/io.h>
11#include <linux/module.h> 14#include <linux/module.h>
15#include <asm/machvec.h>
16#include <asm/io.h>
12 17
13/* 18/*
14 * Copy data from IO memory space to "real" memory space. 19 * Copy data from IO memory space to "real" memory space.
15 * This needs to be optimized. 20 * This needs to be optimized.
16 */ 21 */
17void memcpy_fromio(void * to, unsigned long from, unsigned long count) 22void memcpy_fromio(void *to, volatile void __iomem *from, unsigned long count)
18{ 23{
19 char *p = to; 24 char *p = to;
20 while (count) { 25 while (count) {
21 count--; 26 count--;
22 *p = readb(from); 27 *p = readb((void __iomem *)from);
23 p++; 28 p++;
24 from++; 29 from++;
25 } 30 }
26} 31}
27 32EXPORT_SYMBOL(memcpy_fromio);
33
28/* 34/*
29 * Copy data from "real" memory space to IO memory space. 35 * Copy data from "real" memory space to IO memory space.
30 * This needs to be optimized. 36 * This needs to be optimized.
31 */ 37 */
32void memcpy_toio(unsigned long to, const void * from, unsigned long count) 38void memcpy_toio(volatile void __iomem *to, const void *from, unsigned long count)
33{ 39{
34 const char *p = from; 40 const char *p = from;
35 while (count) { 41 while (count) {
36 count--; 42 count--;
37 writeb(*p, to); 43 writeb(*p, (void __iomem *)to);
38 p++; 44 p++;
39 to++; 45 to++;
40 } 46 }
41} 47}
42 48EXPORT_SYMBOL(memcpy_toio);
49
43/* 50/*
44 * "memset" on IO memory space. 51 * "memset" on IO memory space.
45 * This needs to be optimized. 52 * This needs to be optimized.
46 */ 53 */
47void memset_io(unsigned long dst, int c, unsigned long count) 54void memset_io(volatile void __iomem *dst, int c, unsigned long count)
48{ 55{
49 while (count) { 56 while (count) {
50 count--; 57 count--;
51 writeb(c, dst); 58 writeb(c, (void __iomem *)dst);
52 dst++; 59 dst++;
53 } 60 }
54} 61}
55
56EXPORT_SYMBOL(memcpy_fromio);
57EXPORT_SYMBOL(memcpy_toio);
58EXPORT_SYMBOL(memset_io); 62EXPORT_SYMBOL(memset_io);
59 63
64void __iomem *ioport_map(unsigned long port, unsigned int nr)
65{
66 return sh_mv.mv_ioport_map(port, nr);
67}
68EXPORT_SYMBOL(ioport_map);
69
70void ioport_unmap(void __iomem *addr)
71{
72 sh_mv.mv_ioport_unmap(addr);
73}
74EXPORT_SYMBOL(ioport_unmap);
diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c
index a911b0149d1f..28ec7487de8c 100644
--- a/arch/sh/kernel/io_generic.c
+++ b/arch/sh/kernel/io_generic.c
@@ -3,6 +3,7 @@
3 * linux/arch/sh/kernel/io_generic.c 3 * linux/arch/sh/kernel/io_generic.c
4 * 4 *
5 * Copyright (C) 2000 Niibe Yutaka 5 * Copyright (C) 2000 Niibe Yutaka
6 * Copyright (C) 2005 Paul Mundt
6 * 7 *
7 * Generic I/O routine. These can be used where a machine specific version 8 * Generic I/O routine. These can be used where a machine specific version
8 * is not required. 9 * is not required.
@@ -10,21 +11,20 @@
10 * This file is subject to the terms and conditions of the GNU General Public 11 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file "COPYING" in the main directory of this archive 12 * License. See the file "COPYING" in the main directory of this archive
12 * for more details. 13 * for more details.
13 *
14 */ 14 */
15 15#include <linux/module.h>
16#include <asm/io.h> 16#include <asm/io.h>
17#include <asm/machvec.h> 17#include <asm/machvec.h>
18#include <linux/module.h>
19 18
20#if defined(CONFIG_CPU_SH3) 19#ifdef CONFIG_CPU_SH3
20/* SH3 has a PCMCIA bug that needs a dummy read from area 6 for a
21 * workaround. */
21/* I'm not sure SH7709 has this kind of bug */ 22/* I'm not sure SH7709 has this kind of bug */
22#define SH3_PCMCIA_BUG_WORKAROUND 1 23#define dummy_read() ctrl_inb(0xba000000)
23#define DUMMY_READ_AREA6 0xba000000 24#else
25#define dummy_read()
24#endif 26#endif
25 27
26#define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x))
27
28unsigned long generic_io_base; 28unsigned long generic_io_base;
29 29
30static inline void delay(void) 30static inline void delay(void)
@@ -32,40 +32,40 @@ static inline void delay(void)
32 ctrl_inw(0xa0000000); 32 ctrl_inw(0xa0000000);
33} 33}
34 34
35unsigned char generic_inb(unsigned long port) 35u8 generic_inb(unsigned long port)
36{ 36{
37 return *(volatile unsigned char*)PORT2ADDR(port); 37 return ctrl_inb((unsigned long __force)ioport_map(port, 1));
38} 38}
39 39
40unsigned short generic_inw(unsigned long port) 40u16 generic_inw(unsigned long port)
41{ 41{
42 return *(volatile unsigned short*)PORT2ADDR(port); 42 return ctrl_inw((unsigned long __force)ioport_map(port, 2));
43} 43}
44 44
45unsigned int generic_inl(unsigned long port) 45u32 generic_inl(unsigned long port)
46{ 46{
47 return *(volatile unsigned long*)PORT2ADDR(port); 47 return ctrl_inl((unsigned long __force)ioport_map(port, 4));
48} 48}
49 49
50unsigned char generic_inb_p(unsigned long port) 50u8 generic_inb_p(unsigned long port)
51{ 51{
52 unsigned long v = *(volatile unsigned char*)PORT2ADDR(port); 52 unsigned long v = generic_inb(port);
53 53
54 delay(); 54 delay();
55 return v; 55 return v;
56} 56}
57 57
58unsigned short generic_inw_p(unsigned long port) 58u16 generic_inw_p(unsigned long port)
59{ 59{
60 unsigned long v = *(volatile unsigned short*)PORT2ADDR(port); 60 unsigned long v = generic_inw(port);
61 61
62 delay(); 62 delay();
63 return v; 63 return v;
64} 64}
65 65
66unsigned int generic_inl_p(unsigned long port) 66u32 generic_inl_p(unsigned long port)
67{ 67{
68 unsigned long v = *(volatile unsigned long*)PORT2ADDR(port); 68 unsigned long v = generic_inl(port);
69 69
70 delay(); 70 delay();
71 return v; 71 return v;
@@ -77,75 +77,70 @@ unsigned int generic_inl_p(unsigned long port)
77 * convert the port address to real address once. 77 * convert the port address to real address once.
78 */ 78 */
79 79
80void generic_insb(unsigned long port, void *buffer, unsigned long count) 80void generic_insb(unsigned long port, void *dst, unsigned long count)
81{ 81{
82 volatile unsigned char *port_addr; 82 volatile u8 *port_addr;
83 unsigned char *buf=buffer; 83 u8 *buf = dst;
84
85 port_addr = (volatile unsigned char *)PORT2ADDR(port);
86 84
87 while(count--) 85 port_addr = (volatile u8 *)ioport_map(port, 1);
88 *buf++ = *port_addr; 86 while (count--)
87 *buf++ = *port_addr;
89} 88}
90 89
91void generic_insw(unsigned long port, void *buffer, unsigned long count) 90void generic_insw(unsigned long port, void *dst, unsigned long count)
92{ 91{
93 volatile unsigned short *port_addr; 92 volatile u16 *port_addr;
94 unsigned short *buf=buffer; 93 u16 *buf = dst;
95 94
96 port_addr = (volatile unsigned short *)PORT2ADDR(port); 95 port_addr = (volatile u16 *)ioport_map(port, 2);
96 while (count--)
97 *buf++ = *port_addr;
97 98
98 while(count--) 99 dummy_read();
99 *buf++ = *port_addr;
100#ifdef SH3_PCMCIA_BUG_WORKAROUND
101 ctrl_inb (DUMMY_READ_AREA6);
102#endif
103} 100}
104 101
105void generic_insl(unsigned long port, void *buffer, unsigned long count) 102void generic_insl(unsigned long port, void *dst, unsigned long count)
106{ 103{
107 volatile unsigned long *port_addr; 104 volatile u32 *port_addr;
108 unsigned long *buf=buffer; 105 u32 *buf = dst;
109 106
110 port_addr = (volatile unsigned long *)PORT2ADDR(port); 107 port_addr = (volatile u32 *)ioport_map(port, 4);
108 while (count--)
109 *buf++ = *port_addr;
111 110
112 while(count--) 111 dummy_read();
113 *buf++ = *port_addr;
114#ifdef SH3_PCMCIA_BUG_WORKAROUND
115 ctrl_inb (DUMMY_READ_AREA6);
116#endif
117} 112}
118 113
119void generic_outb(unsigned char b, unsigned long port) 114void generic_outb(u8 b, unsigned long port)
120{ 115{
121 *(volatile unsigned char*)PORT2ADDR(port) = b; 116 ctrl_outb(b, (unsigned long __force)ioport_map(port, 1));
122} 117}
123 118
124void generic_outw(unsigned short b, unsigned long port) 119void generic_outw(u16 b, unsigned long port)
125{ 120{
126 *(volatile unsigned short*)PORT2ADDR(port) = b; 121 ctrl_outw(b, (unsigned long __force)ioport_map(port, 2));
127} 122}
128 123
129void generic_outl(unsigned int b, unsigned long port) 124void generic_outl(u32 b, unsigned long port)
130{ 125{
131 *(volatile unsigned long*)PORT2ADDR(port) = b; 126 ctrl_outl(b, (unsigned long __force)ioport_map(port, 4));
132} 127}
133 128
134void generic_outb_p(unsigned char b, unsigned long port) 129void generic_outb_p(u8 b, unsigned long port)
135{ 130{
136 *(volatile unsigned char*)PORT2ADDR(port) = b; 131 generic_outb(b, port);
137 delay(); 132 delay();
138} 133}
139 134
140void generic_outw_p(unsigned short b, unsigned long port) 135void generic_outw_p(u16 b, unsigned long port)
141{ 136{
142 *(volatile unsigned short*)PORT2ADDR(port) = b; 137 generic_outw(b, port);
143 delay(); 138 delay();
144} 139}
145 140
146void generic_outl_p(unsigned int b, unsigned long port) 141void generic_outl_p(u32 b, unsigned long port)
147{ 142{
148 *(volatile unsigned long*)PORT2ADDR(port) = b; 143 generic_outl(b, port);
149 delay(); 144 delay();
150} 145}
151 146
@@ -154,90 +149,77 @@ void generic_outl_p(unsigned int b, unsigned long port)
154 * address. However as the port address doesn't change we only need to 149 * address. However as the port address doesn't change we only need to
155 * convert the port address to real address once. 150 * convert the port address to real address once.
156 */ 151 */
157 152void generic_outsb(unsigned long port, const void *src, unsigned long count)
158void generic_outsb(unsigned long port, const void *buffer, unsigned long count)
159{ 153{
160 volatile unsigned char *port_addr; 154 volatile u8 *port_addr;
161 const unsigned char *buf=buffer; 155 const u8 *buf = src;
162 156
163 port_addr = (volatile unsigned char *)PORT2ADDR(port); 157 port_addr = (volatile u8 __force *)ioport_map(port, 1);
164 158
165 while(count--) 159 while (count--)
166 *port_addr = *buf++; 160 *port_addr = *buf++;
167} 161}
168 162
169void generic_outsw(unsigned long port, const void *buffer, unsigned long count) 163void generic_outsw(unsigned long port, const void *src, unsigned long count)
170{ 164{
171 volatile unsigned short *port_addr; 165 volatile u16 *port_addr;
172 const unsigned short *buf=buffer; 166 const u16 *buf = src;
173 167
174 port_addr = (volatile unsigned short *)PORT2ADDR(port); 168 port_addr = (volatile u16 __force *)ioport_map(port, 2);
175 169
176 while(count--) 170 while (count--)
177 *port_addr = *buf++; 171 *port_addr = *buf++;
178 172
179#ifdef SH3_PCMCIA_BUG_WORKAROUND 173 dummy_read();
180 ctrl_inb (DUMMY_READ_AREA6);
181#endif
182} 174}
183 175
184void generic_outsl(unsigned long port, const void *buffer, unsigned long count) 176void generic_outsl(unsigned long port, const void *src, unsigned long count)
185{ 177{
186 volatile unsigned long *port_addr; 178 volatile u32 *port_addr;
187 const unsigned long *buf=buffer; 179 const u32 *buf = src;
188 180
189 port_addr = (volatile unsigned long *)PORT2ADDR(port); 181 port_addr = (volatile u32 __force *)ioport_map(port, 4);
182 while (count--)
183 *port_addr = *buf++;
190 184
191 while(count--) 185 dummy_read();
192 *port_addr = *buf++;
193
194#ifdef SH3_PCMCIA_BUG_WORKAROUND
195 ctrl_inb (DUMMY_READ_AREA6);
196#endif
197}
198
199unsigned char generic_readb(unsigned long addr)
200{
201 return *(volatile unsigned char*)addr;
202} 186}
203 187
204unsigned short generic_readw(unsigned long addr) 188u8 generic_readb(void __iomem *addr)
205{ 189{
206 return *(volatile unsigned short*)addr; 190 return ctrl_inb((unsigned long __force)addr);
207} 191}
208 192
209unsigned int generic_readl(unsigned long addr) 193u16 generic_readw(void __iomem *addr)
210{ 194{
211 return *(volatile unsigned long*)addr; 195 return ctrl_inw((unsigned long __force)addr);
212} 196}
213 197
214void generic_writeb(unsigned char b, unsigned long addr) 198u32 generic_readl(void __iomem *addr)
215{ 199{
216 *(volatile unsigned char*)addr = b; 200 return ctrl_inl((unsigned long __force)addr);
217} 201}
218 202
219void generic_writew(unsigned short b, unsigned long addr) 203void generic_writeb(u8 b, void __iomem *addr)
220{ 204{
221 *(volatile unsigned short*)addr = b; 205 ctrl_outb(b, (unsigned long __force)addr);
222} 206}
223 207
224void generic_writel(unsigned int b, unsigned long addr) 208void generic_writew(u16 b, void __iomem *addr)
225{ 209{
226 *(volatile unsigned long*)addr = b; 210 ctrl_outw(b, (unsigned long __force)addr);
227} 211}
228 212
229void * generic_ioremap(unsigned long offset, unsigned long size) 213void generic_writel(u32 b, void __iomem *addr)
230{ 214{
231 return (void *) P2SEGADDR(offset); 215 ctrl_outl(b, (unsigned long __force)addr);
232} 216}
233EXPORT_SYMBOL(generic_ioremap);
234 217
235void generic_iounmap(void *addr) 218void __iomem *generic_ioport_map(unsigned long addr, unsigned int size)
236{ 219{
220 return (void __iomem *)(addr + generic_io_base);
237} 221}
238EXPORT_SYMBOL(generic_iounmap);
239 222
240unsigned long generic_isa_port2addr(unsigned long offset) 223void generic_ioport_unmap(void __iomem *addr)
241{ 224{
242 return offset + generic_io_base;
243} 225}
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
index 54c171225b78..6883c00728cb 100644
--- a/arch/sh/kernel/irq.c
+++ b/arch/sh/kernel/irq.c
@@ -8,38 +8,13 @@
8 * SuperH version: Copyright (C) 1999 Niibe Yutaka 8 * SuperH version: Copyright (C) 1999 Niibe Yutaka
9 */ 9 */
10 10
11/* 11#include <linux/irq.h>
12 * IRQs are in fact implemented a bit like signal handlers for the kernel.
13 * Naturally it's not a 1:1 relation, but there are similarities.
14 */
15
16#include <linux/config.h>
17#include <linux/module.h>
18#include <linux/ptrace.h>
19#include <linux/errno.h>
20#include <linux/kernel_stat.h>
21#include <linux/signal.h>
22#include <linux/sched.h>
23#include <linux/ioport.h>
24#include <linux/interrupt.h> 12#include <linux/interrupt.h>
25#include <linux/timex.h> 13#include <linux/kernel_stat.h>
26#include <linux/mm.h>
27#include <linux/slab.h>
28#include <linux/random.h>
29#include <linux/smp.h>
30#include <linux/smp_lock.h>
31#include <linux/init.h>
32#include <linux/seq_file.h> 14#include <linux/seq_file.h>
33#include <linux/kallsyms.h>
34#include <linux/bitops.h>
35
36#include <asm/system.h>
37#include <asm/io.h>
38#include <asm/pgalloc.h>
39#include <asm/delay.h>
40#include <asm/irq.h> 15#include <asm/irq.h>
41#include <linux/irq.h> 16#include <asm/processor.h>
42 17#include <asm/cpu/mmu_context.h>
43 18
44/* 19/*
45 * 'what should we do if we get a hw irq event on an illegal vector'. 20 * 'what should we do if we get a hw irq event on an illegal vector'.
@@ -66,7 +41,7 @@ int show_interrupts(struct seq_file *p, void *v)
66 seq_putc(p, '\n'); 41 seq_putc(p, '\n');
67 } 42 }
68 43
69 if (i < ACTUAL_NR_IRQS) { 44 if (i < NR_IRQS) {
70 spin_lock_irqsave(&irq_desc[i].lock, flags); 45 spin_lock_irqsave(&irq_desc[i].lock, flags);
71 action = irq_desc[i].action; 46 action = irq_desc[i].action;
72 if (!action) 47 if (!action)
@@ -86,19 +61,32 @@ unlock:
86} 61}
87#endif 62#endif
88 63
64
89asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, 65asmlinkage int do_IRQ(unsigned long r4, unsigned long r5,
90 unsigned long r6, unsigned long r7, 66 unsigned long r6, unsigned long r7,
91 struct pt_regs regs) 67 struct pt_regs regs)
92{ 68{
93 int irq; 69 int irq = r4;
94 70
95 irq_enter(); 71 irq_enter();
96 asm volatile("stc r2_bank, %0\n\t" 72
97 "shlr2 %0\n\t" 73#ifdef CONFIG_CPU_HAS_INTEVT
98 "shlr2 %0\n\t" 74 __asm__ __volatile__ (
99 "shlr %0\n\t" 75#ifdef CONFIG_CPU_HAS_SR_RB
100 "add #-16, %0\n\t" 76 "stc r2_bank, %0\n\t"
101 :"=z" (irq)); 77#else
78 "mov.l @%1, %0\n\t"
79#endif
80 "shlr2 %0\n\t"
81 "shlr2 %0\n\t"
82 "shlr %0\n\t"
83 "add #-16, %0\n\t"
84 : "=z" (irq), "=r" (r4)
85 : "1" (INTEVT)
86 : "memory"
87 );
88#endif
89
102 irq = irq_demux(irq); 90 irq = irq_demux(irq);
103 __do_IRQ(irq, &regs); 91 __do_IRQ(irq, &regs);
104 irq_exit(); 92 irq_exit();
diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c
new file mode 100644
index 000000000000..43546525f28f
--- /dev/null
+++ b/arch/sh/kernel/machine_kexec.c
@@ -0,0 +1,112 @@
1/*
2 * machine_kexec.c - handle transition of Linux booting another kernel
3 * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com>
4 *
5 * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
6 * LANDISK/sh4 supported by kogiidena
7 *
8 * This source code is licensed under the GNU General Public License,
9 * Version 2. See the file COPYING for more details.
10 */
11
12#include <linux/mm.h>
13#include <linux/kexec.h>
14#include <linux/delay.h>
15#include <linux/reboot.h>
16#include <asm/pgtable.h>
17#include <asm/pgalloc.h>
18#include <asm/mmu_context.h>
19#include <asm/io.h>
20#include <asm/cacheflush.h>
21
22typedef NORET_TYPE void (*relocate_new_kernel_t)(
23 unsigned long indirection_page,
24 unsigned long reboot_code_buffer,
25 unsigned long start_address,
26 unsigned long vbr_reg) ATTRIB_NORET;
27
28const extern unsigned char relocate_new_kernel[];
29const extern unsigned int relocate_new_kernel_size;
30extern void *gdb_vbr_vector;
31
32/*
33 * Provide a dummy crash_notes definition while crash dump arrives to ppc.
34 * This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
35 */
36void *crash_notes = NULL;
37
38void machine_shutdown(void)
39{
40}
41
42void machine_crash_shutdown(struct pt_regs *regs)
43{
44}
45
46/*
47 * Do what every setup is needed on image and the
48 * reboot code buffer to allow us to avoid allocations
49 * later.
50 */
51int machine_kexec_prepare(struct kimage *image)
52{
53 return 0;
54}
55
56void machine_kexec_cleanup(struct kimage *image)
57{
58}
59
60static void kexec_info(struct kimage *image)
61{
62 int i;
63 printk("kexec information\n");
64 for (i = 0; i < image->nr_segments; i++) {
65 printk(" segment[%d]: 0x%08x - 0x%08x (0x%08x)\n",
66 i,
67 (unsigned int)image->segment[i].mem,
68 (unsigned int)image->segment[i].mem + image->segment[i].memsz,
69 (unsigned int)image->segment[i].memsz);
70 }
71 printk(" start : 0x%08x\n\n", (unsigned int)image->start);
72}
73
74
75/*
76 * Do not allocate memory (or fail in any way) in machine_kexec().
77 * We are past the point of no return, committed to rebooting now.
78 */
79NORET_TYPE void machine_kexec(struct kimage *image)
80{
81
82 unsigned long page_list;
83 unsigned long reboot_code_buffer;
84 unsigned long vbr_reg;
85 relocate_new_kernel_t rnk;
86
87#if defined(CONFIG_SH_STANDARD_BIOS)
88 vbr_reg = ((unsigned long )gdb_vbr_vector) - 0x100;
89#else
90 vbr_reg = 0x80000000; // dummy
91#endif
92 /* Interrupts aren't acceptable while we reboot */
93 local_irq_disable();
94
95 page_list = image->head;
96
97 /* we need both effective and real address here */
98 reboot_code_buffer =
99 (unsigned long)page_address(image->control_code_page);
100
101 /* copy our kernel relocation code to the control code page */
102 memcpy((void *)reboot_code_buffer, relocate_new_kernel,
103 relocate_new_kernel_size);
104
105 kexec_info(image);
106 flush_cache_all();
107
108 /* now call it */
109 rnk = (relocate_new_kernel_t) reboot_code_buffer;
110 (*rnk)(page_list, reboot_code_buffer, image->start, vbr_reg);
111}
112
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c
index aac15e42d03b..a4dc2b532e10 100644
--- a/arch/sh/kernel/process.c
+++ b/arch/sh/kernel/process.c
@@ -71,6 +71,16 @@ void cpu_idle(void)
71 71
72void machine_restart(char * __unused) 72void machine_restart(char * __unused)
73{ 73{
74
75#ifdef CONFIG_KEXEC
76 struct kimage *image;
77 image = xchg(&kexec_image, 0);
78 if (image) {
79 machine_shutdown();
80 machine_kexec(image);
81 }
82#endif
83
74 /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */ 84 /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
75 asm volatile("ldc %0, sr\n\t" 85 asm volatile("ldc %0, sr\n\t"
76 "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001)); 86 "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001));
diff --git a/arch/sh/kernel/relocate_kernel.S b/arch/sh/kernel/relocate_kernel.S
new file mode 100644
index 000000000000..b0695cffec6e
--- /dev/null
+++ b/arch/sh/kernel/relocate_kernel.S
@@ -0,0 +1,102 @@
1/*
2 * relocate_kernel.S - put the kernel image in place to boot
3 * 2005.9.17 kogiidena@eggplant.ddo.jp
4 *
5 * LANDISK/sh4 is supported. Maybe, SH archtecture works well.
6 *
7 * This source code is licensed under the GNU General Public License,
8 * Version 2. See the file COPYING for more details.
9 */
10
11#include <linux/config.h>
12#include <linux/linkage.h>
13
14#define PAGE_SIZE 4096 /* must be same value as in <asm/page.h> */
15
16
17 .globl relocate_new_kernel
18relocate_new_kernel:
19 /* r4 = indirection_page */
20 /* r5 = reboot_code_buffer */
21 /* r6 = start_address */
22 /* r7 = vbr_reg */
23
24 mov.l 10f,r8 /* 4096 */
25 mov.l 11f,r9 /* 0xa0000000 */
26
27 /* stack setting */
28 add r8,r5
29 mov r5,r15
30
31 bra 1f
32 mov r4,r0 /* cmd = indirection_page */
330:
34 mov.l @r4+,r0 /* cmd = *ind++ */
35
361: /* addr = (cmd | 0xa0000000) & 0xfffffff0 */
37 mov r0,r2
38 or r9,r2
39 mov #-16,r1
40 and r1,r2
41
42 /* if(cmd & IND_DESTINATION) dst = addr */
43 tst #1,r0
44 bt 2f
45 bra 0b
46 mov r2,r5
47
482: /* else if(cmd & IND_INDIRECTION) ind = addr */
49 tst #2,r0
50 bt 3f
51 bra 0b
52 mov r2,r4
53
543: /* else if(cmd & IND_DONE) goto 6 */
55 tst #4,r0
56 bt 4f
57 bra 6f
58 nop
59
604: /* else if(cmd & IND_SOURCE) memcpy(dst,addr,PAGE_SIZE) */
61 tst #8,r0
62 bt 0b
63
64 mov r8,r3
65 shlr2 r3
66 shlr2 r3
675:
68 dt r3
69 mov.l @r2+,r1 /* 16n+0 */
70 mov.l r1,@r5
71 add #4,r5
72 mov.l @r2+,r1 /* 16n+4 */
73 mov.l r1,@r5
74 add #4,r5
75 mov.l @r2+,r1 /* 16n+8 */
76 mov.l r1,@r5
77 add #4,r5
78 mov.l @r2+,r1 /* 16n+12 */
79 mov.l r1,@r5
80 add #4,r5
81 bf 5b
82
83 bra 0b
84 nop
856:
86#ifdef CONFIG_SH_STANDARD_BIOS
87 ldc r7, vbr
88#endif
89 jmp @r6
90 nop
91
92 .align 2
9310:
94 .long PAGE_SIZE
9511:
96 .long 0xa0000000
97
98relocate_new_kernel_end:
99
100 .globl relocate_new_kernel_size
101relocate_new_kernel_size:
102 .long relocate_new_kernel_end - relocate_new_kernel
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c
index 671b876416bf..314a275c04e0 100644
--- a/arch/sh/kernel/time.c
+++ b/arch/sh/kernel/time.c
@@ -3,7 +3,7 @@
3 * 3 *
4 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka 4 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
5 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> 5 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
6 * Copyright (C) 2002, 2003, 2004 Paul Mundt 6 * Copyright (C) 2002, 2003, 2004, 2005 Paul Mundt
7 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> 7 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
8 * 8 *
9 * Some code taken from i386 version. 9 * Some code taken from i386 version.
@@ -11,50 +11,21 @@
11 */ 11 */
12 12
13#include <linux/config.h> 13#include <linux/config.h>
14#include <linux/errno.h>
15#include <linux/module.h>
16#include <linux/sched.h>
17#include <linux/kernel.h> 14#include <linux/kernel.h>
18#include <linux/param.h> 15#include <linux/module.h>
19#include <linux/string.h>
20#include <linux/mm.h>
21#include <linux/interrupt.h>
22#include <linux/time.h>
23#include <linux/delay.h>
24#include <linux/init.h> 16#include <linux/init.h>
25#include <linux/smp.h>
26#include <linux/profile.h> 17#include <linux/profile.h>
27 18#include <asm/clock.h>
28#include <asm/processor.h>
29#include <asm/uaccess.h>
30#include <asm/io.h>
31#include <asm/irq.h>
32#include <asm/delay.h>
33#include <asm/machvec.h>
34#include <asm/rtc.h> 19#include <asm/rtc.h>
35#include <asm/freq.h> 20#include <asm/timer.h>
36#include <asm/cpu/timer.h>
37#ifdef CONFIG_SH_KGDB
38#include <asm/kgdb.h> 21#include <asm/kgdb.h>
39#endif
40
41#include <linux/timex.h>
42#include <linux/irq.h>
43
44#define TMU_TOCR_INIT 0x00
45#define TMU0_TCR_INIT 0x0020
46#define TMU_TSTR_INIT 1
47
48#define TMU0_TCR_CALIB 0x0000
49
50#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
51#define CLOCKGEN_MEMCLKCR 0xbb040038
52#define MEMCLKCR_RATIO_MASK 0x7
53#endif /* CONFIG_CPU_SUBTYPE_ST40STB1 */
54 22
55extern unsigned long wall_jiffies; 23extern unsigned long wall_jiffies;
56#define TICK_SIZE (tick_nsec / 1000) 24struct sys_timer *sys_timer;
57DEFINE_SPINLOCK(tmu0_lock); 25
26/* Move this somewhere more sensible.. */
27DEFINE_SPINLOCK(rtc_lock);
28EXPORT_SYMBOL(rtc_lock);
58 29
59/* XXX: Can we initialize this in a routine somewhere? Dreamcast doesn't want 30/* XXX: Can we initialize this in a routine somewhere? Dreamcast doesn't want
60 * these routines anywhere... */ 31 * these routines anywhere... */
@@ -66,98 +37,14 @@ void (*rtc_get_time)(struct timespec *);
66int (*rtc_set_time)(const time_t); 37int (*rtc_set_time)(const time_t);
67#endif 38#endif
68 39
69#if defined(CONFIG_CPU_SUBTYPE_SH7300)
70static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 };
71#endif
72#if defined(CONFIG_CPU_SH3)
73static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
74static int stc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 };
75#define bfc_divisors stc_multipliers
76#define bfc_values stc_values
77static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
78static int ifc_values[] = { 0, 1, 4, 2, 0, 0, 0, 0 };
79static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
80static int pfc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 };
81#elif defined(CONFIG_CPU_SH4)
82#if defined(CONFIG_CPU_SUBTYPE_SH73180)
83static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
84static int ifc_values[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
85#define bfc_divisors ifc_divisors /* Same */
86#define bfc_values ifc_values
87#define pfc_divisors ifc_divisors /* Same */
88#define pfc_values ifc_values
89#else
90static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 };
91static int ifc_values[] = { 0, 1, 2, 3, 0, 4, 0, 5 };
92#define bfc_divisors ifc_divisors /* Same */
93#define bfc_values ifc_values
94static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 };
95static int pfc_values[] = { 0, 0, 1, 2, 0, 3, 0, 4 };
96#endif
97#else
98#error "Unknown ifc/bfc/pfc/stc values for this processor"
99#endif
100
101/* 40/*
102 * Scheduler clock - returns current time in nanosec units. 41 * Scheduler clock - returns current time in nanosec units.
103 */ 42 */
104unsigned long long sched_clock(void) 43unsigned long long __attribute__ ((weak)) sched_clock(void)
105{ 44{
106 return (unsigned long long)jiffies * (1000000000 / HZ); 45 return (unsigned long long)jiffies * (1000000000 / HZ);
107} 46}
108 47
109static unsigned long do_gettimeoffset(void)
110{
111 int count;
112 unsigned long flags;
113
114 static int count_p = 0x7fffffff; /* for the first call after boot */
115 static unsigned long jiffies_p = 0;
116
117 /*
118 * cache volatile jiffies temporarily; we have IRQs turned off.
119 */
120 unsigned long jiffies_t;
121
122 spin_lock_irqsave(&tmu0_lock, flags);
123 /* timer count may underflow right here */
124 count = ctrl_inl(TMU0_TCNT); /* read the latched count */
125
126 jiffies_t = jiffies;
127
128 /*
129 * avoiding timer inconsistencies (they are rare, but they happen)...
130 * there is one kind of problem that must be avoided here:
131 * 1. the timer counter underflows
132 */
133
134 if( jiffies_t == jiffies_p ) {
135 if( count > count_p ) {
136 /* the nutcase */
137
138 if(ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */
139 /*
140 * We cannot detect lost timer interrupts ...
141 * well, that's why we call them lost, don't we? :)
142 * [hmm, on the Pentium and Alpha we can ... sort of]
143 */
144 count -= LATCH;
145 } else {
146 printk("do_slow_gettimeoffset(): hardware timer problem?\n");
147 }
148 }
149 } else
150 jiffies_p = jiffies_t;
151
152 count_p = count;
153 spin_unlock_irqrestore(&tmu0_lock, flags);
154
155 count = ((LATCH-1) - count) * TICK_SIZE;
156 count = (count + LATCH/2) / LATCH;
157
158 return count;
159}
160
161void do_gettimeofday(struct timeval *tv) 48void do_gettimeofday(struct timeval *tv)
162{ 49{
163 unsigned long seq; 50 unsigned long seq;
@@ -166,7 +53,7 @@ void do_gettimeofday(struct timeval *tv)
166 53
167 do { 54 do {
168 seq = read_seqbegin(&xtime_lock); 55 seq = read_seqbegin(&xtime_lock);
169 usec = do_gettimeoffset(); 56 usec = get_timer_offset();
170 57
171 lost = jiffies - wall_jiffies; 58 lost = jiffies - wall_jiffies;
172 if (lost) 59 if (lost)
@@ -202,7 +89,7 @@ int do_settimeofday(struct timespec *tv)
202 * wall time. Discover what correction gettimeofday() would have 89 * wall time. Discover what correction gettimeofday() would have
203 * made, and then undo it! 90 * made, and then undo it!
204 */ 91 */
205 nsec -= 1000 * (do_gettimeoffset() + 92 nsec -= 1000 * (get_timer_offset() +
206 (jiffies - wall_jiffies) * (1000000 / HZ)); 93 (jiffies - wall_jiffies) * (1000000 / HZ));
207 94
208 wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); 95 wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
@@ -224,10 +111,10 @@ EXPORT_SYMBOL(do_settimeofday);
224static long last_rtc_update; 111static long last_rtc_update;
225 112
226/* 113/*
227 * timer_interrupt() needs to keep up the real-time clock, 114 * handle_timer_tick() needs to keep up the real-time clock,
228 * as well as call the "do_timer()" routine every clocktick 115 * as well as call the "do_timer()" routine every clocktick
229 */ 116 */
230static inline void do_timer_interrupt(int irq, struct pt_regs *regs) 117void handle_timer_tick(struct pt_regs *regs)
231{ 118{
232 do_timer(regs); 119 do_timer(regs);
233#ifndef CONFIG_SMP 120#ifndef CONFIG_SMP
@@ -252,337 +139,35 @@ static inline void do_timer_interrupt(int irq, struct pt_regs *regs)
252 if (rtc_set_time(xtime.tv_sec) == 0) 139 if (rtc_set_time(xtime.tv_sec) == 0)
253 last_rtc_update = xtime.tv_sec; 140 last_rtc_update = xtime.tv_sec;
254 else 141 else
255 last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ 142 /* do it again in 60s */
143 last_rtc_update = xtime.tv_sec - 600;
256 } 144 }
257} 145}
258 146
259/* 147static struct sysdev_class timer_sysclass = {
260 * This is the same as the above, except we _also_ save the current 148 set_kset_name("timer"),
261 * Time Stamp Counter value at the time of the timer interrupt, so that
262 * we later on can estimate the time of day more exactly.
263 */
264static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
265{
266 unsigned long timer_status;
267
268 /* Clear UNF bit */
269 timer_status = ctrl_inw(TMU0_TCR);
270 timer_status &= ~0x100;
271 ctrl_outw(timer_status, TMU0_TCR);
272
273 /*
274 * Here we are in the timer irq handler. We just have irqs locally
275 * disabled but we don't know if the timer_bh is running on the other
276 * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
277 * the irq version of write_lock because as just said we have irq
278 * locally disabled. -arca
279 */
280 write_seqlock(&xtime_lock);
281 do_timer_interrupt(irq, regs);
282 write_sequnlock(&xtime_lock);
283
284 return IRQ_HANDLED;
285}
286
287/*
288 * Hah! We'll see if this works (switching from usecs to nsecs).
289 */
290static unsigned int __init get_timer_frequency(void)
291{
292 u32 freq;
293 struct timespec ts1, ts2;
294 unsigned long diff_nsec;
295 unsigned long factor;
296
297 /* Setup the timer: We don't want to generate interrupts, just
298 * have it count down at its natural rate.
299 */
300 ctrl_outb(0, TMU_TSTR);
301#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
302 ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
303#endif
304 ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR);
305 ctrl_outl(0xffffffff, TMU0_TCOR);
306 ctrl_outl(0xffffffff, TMU0_TCNT);
307
308 rtc_get_time(&ts2);
309
310 do {
311 rtc_get_time(&ts1);
312 } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
313
314 /* actually start the timer */
315 ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
316
317 do {
318 rtc_get_time(&ts2);
319 } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
320
321 freq = 0xffffffff - ctrl_inl(TMU0_TCNT);
322 if (ts2.tv_nsec < ts1.tv_nsec) {
323 ts2.tv_nsec += 1000000000;
324 ts2.tv_sec--;
325 }
326
327 diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
328
329 /* this should work well if the RTC has a precision of n Hz, where
330 * n is an integer. I don't think we have to worry about the other
331 * cases. */
332 factor = (1000000000 + diff_nsec/2) / diff_nsec;
333
334 if (factor * diff_nsec > 1100000000 ||
335 factor * diff_nsec < 900000000)
336 panic("weird RTC (diff_nsec %ld)", diff_nsec);
337
338 return freq * factor;
339}
340
341void (*board_time_init)(void);
342void (*board_timer_setup)(struct irqaction *irq);
343
344static unsigned int sh_pclk_freq __initdata = CONFIG_SH_PCLK_FREQ;
345
346static int __init sh_pclk_setup(char *str)
347{
348 unsigned int freq;
349
350 if (get_option(&str, &freq))
351 sh_pclk_freq = freq;
352
353 return 1;
354}
355__setup("sh_pclk=", sh_pclk_setup);
356
357static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};
358
359void get_current_frequency_divisors(unsigned int *ifc, unsigned int *bfc, unsigned int *pfc)
360{
361 unsigned int frqcr = ctrl_inw(FRQCR);
362
363#if defined(CONFIG_CPU_SH3)
364#if defined(CONFIG_CPU_SUBTYPE_SH7300)
365 *ifc = md_table[((frqcr & 0x0070) >> 4)];
366 *bfc = md_table[((frqcr & 0x0700) >> 8)];
367 *pfc = md_table[frqcr & 0x0007];
368#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
369 *bfc = stc_multipliers[(frqcr & 0x0300) >> 8];
370 *ifc = ifc_divisors[(frqcr & 0x0030) >> 4];
371 *pfc = pfc_divisors[frqcr & 0x0003];
372#else
373 unsigned int tmp;
374
375 tmp = (frqcr & 0x8000) >> 13;
376 tmp |= (frqcr & 0x0030) >> 4;
377 *bfc = stc_multipliers[tmp];
378 tmp = (frqcr & 0x4000) >> 12;
379 tmp |= (frqcr & 0x000c) >> 2;
380 *ifc = ifc_divisors[tmp];
381 tmp = (frqcr & 0x2000) >> 11;
382 tmp |= frqcr & 0x0003;
383 *pfc = pfc_divisors[tmp];
384#endif
385#elif defined(CONFIG_CPU_SH4)
386#if defined(CONFIG_CPU_SUBTYPE_SH73180)
387 *ifc = ifc_divisors[(frqcr>> 20) & 0x0007];
388 *bfc = bfc_divisors[(frqcr>> 12) & 0x0007];
389 *pfc = pfc_divisors[frqcr & 0x0007];
390#else
391 *ifc = ifc_divisors[(frqcr >> 6) & 0x0007];
392 *bfc = bfc_divisors[(frqcr >> 3) & 0x0007];
393 *pfc = pfc_divisors[frqcr & 0x0007];
394#endif
395#endif
396}
397
398/*
399 * This bit of ugliness builds up accessor routines to get at both
400 * the divisors and the physical values.
401 */
402#define _FREQ_TABLE(x) \
403 unsigned int get_##x##_divisor(unsigned int value) \
404 { return x##_divisors[value]; } \
405 \
406 unsigned int get_##x##_value(unsigned int divisor) \
407 { return x##_values[(divisor - 1)]; }
408
409_FREQ_TABLE(ifc);
410_FREQ_TABLE(bfc);
411_FREQ_TABLE(pfc);
412
413#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
414
415/*
416 * The ST40 divisors are totally different so we set the cpu data
417 * clocks using a different algorithm
418 *
419 * I've just plugged this from the 2.4 code
420 * - Alex Bennee <kernel-hacker@bennee.com>
421 */
422#define CCN_PVR_CHIP_SHIFT 24
423#define CCN_PVR_CHIP_MASK 0xff
424#define CCN_PVR_CHIP_ST40STB1 0x4
425
426
427struct frqcr_data {
428 unsigned short frqcr;
429
430 struct {
431 unsigned char multiplier;
432 unsigned char divisor;
433 } factor[3];
434};
435
436static struct frqcr_data st40_frqcr_table[] = {
437 { 0x000, {{1,1}, {1,1}, {1,2}}},
438 { 0x002, {{1,1}, {1,1}, {1,4}}},
439 { 0x004, {{1,1}, {1,1}, {1,8}}},
440 { 0x008, {{1,1}, {1,2}, {1,2}}},
441 { 0x00A, {{1,1}, {1,2}, {1,4}}},
442 { 0x00C, {{1,1}, {1,2}, {1,8}}},
443 { 0x011, {{1,1}, {2,3}, {1,6}}},
444 { 0x013, {{1,1}, {2,3}, {1,3}}},
445 { 0x01A, {{1,1}, {1,2}, {1,4}}},
446 { 0x01C, {{1,1}, {1,2}, {1,8}}},
447 { 0x023, {{1,1}, {2,3}, {1,3}}},
448 { 0x02C, {{1,1}, {1,2}, {1,8}}},
449 { 0x048, {{1,2}, {1,2}, {1,4}}},
450 { 0x04A, {{1,2}, {1,2}, {1,6}}},
451 { 0x04C, {{1,2}, {1,2}, {1,8}}},
452 { 0x05A, {{1,2}, {1,3}, {1,6}}},
453 { 0x05C, {{1,2}, {1,3}, {1,6}}},
454 { 0x063, {{1,2}, {1,4}, {1,4}}},
455 { 0x06C, {{1,2}, {1,4}, {1,8}}},
456 { 0x091, {{1,3}, {1,3}, {1,6}}},
457 { 0x093, {{1,3}, {1,3}, {1,6}}},
458 { 0x0A3, {{1,3}, {1,6}, {1,6}}},
459 { 0x0DA, {{1,4}, {1,4}, {1,8}}},
460 { 0x0DC, {{1,4}, {1,4}, {1,8}}},
461 { 0x0EC, {{1,4}, {1,8}, {1,8}}},
462 { 0x123, {{1,4}, {1,4}, {1,8}}},
463 { 0x16C, {{1,4}, {1,8}, {1,8}}},
464}; 149};
465 150
466struct memclk_data { 151static int __init timer_init_sysfs(void)
467 unsigned char multiplier;
468 unsigned char divisor;
469};
470
471static struct memclk_data st40_memclk_table[8] = {
472 {1,1}, // 000
473 {1,2}, // 001
474 {1,3}, // 010
475 {2,3}, // 011
476 {1,4}, // 100
477 {1,6}, // 101
478 {1,8}, // 110
479 {1,8} // 111
480};
481
482static void st40_specific_time_init(unsigned int module_clock, unsigned short frqcr)
483{ 152{
484 unsigned int cpu_clock, master_clock, bus_clock, memory_clock; 153 int ret = sysdev_class_register(&timer_sysclass);
485 struct frqcr_data *d; 154 if (ret != 0)
486 int a; 155 return ret;
487 unsigned long memclkcr;
488 struct memclk_data *e;
489 156
490 for (a = 0; a < ARRAY_SIZE(st40_frqcr_table); a++) { 157 sys_timer->dev.cls = &timer_sysclass;
491 d = &st40_frqcr_table[a]; 158 return sysdev_register(&sys_timer->dev);
492 159}
493 if (d->frqcr == (frqcr & 0x1ff))
494 break;
495 }
496 160
497 if (a == ARRAY_SIZE(st40_frqcr_table)) { 161device_initcall(timer_init_sysfs);
498 d = st40_frqcr_table;
499 162
500 printk("ERROR: Unrecognised FRQCR value (0x%x), " 163void (*board_time_init)(void);
501 "using default multipliers\n", frqcr);
502 }
503
504 memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR);
505 e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK];
506
507 printk(KERN_INFO "Clock multipliers: CPU: %d/%d Bus: %d/%d "
508 "Mem: %d/%d Periph: %d/%d\n",
509 d->factor[0].multiplier, d->factor[0].divisor,
510 d->factor[1].multiplier, d->factor[1].divisor,
511 e->multiplier, e->divisor,
512 d->factor[2].multiplier, d->factor[2].divisor);
513
514 master_clock = module_clock * d->factor[2].divisor
515 / d->factor[2].multiplier;
516 bus_clock = master_clock * d->factor[1].multiplier
517 / d->factor[1].divisor;
518 memory_clock = master_clock * e->multiplier
519 / e->divisor;
520 cpu_clock = master_clock * d->factor[0].multiplier
521 / d->factor[0].divisor;
522
523 current_cpu_data.cpu_clock = cpu_clock;
524 current_cpu_data.master_clock = master_clock;
525 current_cpu_data.bus_clock = bus_clock;
526 current_cpu_data.memory_clock = memory_clock;
527 current_cpu_data.module_clock = module_clock;
528}
529#endif
530 164
531void __init time_init(void) 165void __init time_init(void)
532{ 166{
533 unsigned int timer_freq = 0;
534 unsigned int ifc, pfc, bfc;
535 unsigned long interval;
536#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
537 unsigned long pvr;
538 unsigned short frqcr;
539#endif
540
541 if (board_time_init) 167 if (board_time_init)
542 board_time_init(); 168 board_time_init();
543 169
544 /* 170 clk_init();
545 * If we don't have an RTC (such as with the SH7300), don't attempt to
546 * probe the timer frequency. Rely on an either hardcoded peripheral
547 * clock value, or on the sh_pclk command line option. Note that we
548 * still need to have CONFIG_SH_PCLK_FREQ set in order for things like
549 * CLOCK_TICK_RATE to be sane.
550 */
551 current_cpu_data.module_clock = sh_pclk_freq;
552
553#ifdef CONFIG_SH_PCLK_CALC
554 /* XXX: Switch this over to a more generic test. */
555 {
556 unsigned int freq;
557
558 /*
559 * If we've specified a peripheral clock frequency, and we have
560 * an RTC, compare it against the autodetected value. Complain
561 * if there's a mismatch.
562 */
563 timer_freq = get_timer_frequency();
564 freq = timer_freq * 4;
565
566 if (sh_pclk_freq && (sh_pclk_freq/100*99 > freq || sh_pclk_freq/100*101 < freq)) {
567 printk(KERN_NOTICE "Calculated peripheral clock value "
568 "%d differs from sh_pclk value %d, fixing..\n",
569 freq, sh_pclk_freq);
570 current_cpu_data.module_clock = freq;
571 }
572 }
573#endif
574
575#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
576 /* XXX: Update ST40 code to use board_time_init() */
577 pvr = ctrl_inl(CCN_PVR);
578 frqcr = ctrl_inw(FRQCR);
579 printk("time.c ST40 Probe: PVR %08lx, FRQCR %04hx\n", pvr, frqcr);
580
581 if (((pvr >> CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1)
582 st40_specific_time_init(current_cpu_data.module_clock, frqcr);
583 else
584#endif
585 get_current_frequency_divisors(&ifc, &bfc, &pfc);
586 171
587 if (rtc_get_time) { 172 if (rtc_get_time) {
588 rtc_get_time(&xtime); 173 rtc_get_time(&xtime);
@@ -594,51 +179,12 @@ void __init time_init(void)
594 set_normalized_timespec(&wall_to_monotonic, 179 set_normalized_timespec(&wall_to_monotonic,
595 -xtime.tv_sec, -xtime.tv_nsec); 180 -xtime.tv_sec, -xtime.tv_nsec);
596 181
597 if (board_timer_setup) {
598 board_timer_setup(&irq0);
599 } else {
600 setup_irq(TIMER_IRQ, &irq0);
601 }
602
603 /* 182 /*
604 * for ST40 chips the current_cpu_data should already be set 183 * Find the timer to use as the system timer, it will be
605 * so not having valid pfc/bfc/ifc shouldn't be a problem 184 * initialized for us.
606 */ 185 */
607 if (!current_cpu_data.master_clock) 186 sys_timer = get_sys_timer();
608 current_cpu_data.master_clock = current_cpu_data.module_clock * pfc; 187 printk(KERN_INFO "Using %s for system timer\n", sys_timer->name);
609 if (!current_cpu_data.bus_clock)
610 current_cpu_data.bus_clock = current_cpu_data.master_clock / bfc;
611 if (!current_cpu_data.cpu_clock)
612 current_cpu_data.cpu_clock = current_cpu_data.master_clock / ifc;
613
614 printk("CPU clock: %d.%02dMHz\n",
615 (current_cpu_data.cpu_clock / 1000000),
616 (current_cpu_data.cpu_clock % 1000000)/10000);
617 printk("Bus clock: %d.%02dMHz\n",
618 (current_cpu_data.bus_clock / 1000000),
619 (current_cpu_data.bus_clock % 1000000)/10000);
620#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
621 printk("Memory clock: %d.%02dMHz\n",
622 (current_cpu_data.memory_clock / 1000000),
623 (current_cpu_data.memory_clock % 1000000)/10000);
624#endif
625 printk("Module clock: %d.%02dMHz\n",
626 (current_cpu_data.module_clock / 1000000),
627 (current_cpu_data.module_clock % 1000000)/10000);
628
629 interval = (current_cpu_data.module_clock/4 + HZ/2) / HZ;
630
631 printk("Interval = %ld\n", interval);
632
633 /* Start TMU0 */
634 ctrl_outb(0, TMU_TSTR);
635#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
636 ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
637#endif
638 ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
639 ctrl_outl(interval, TMU0_TCOR);
640 ctrl_outl(interval, TMU0_TCNT);
641 ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
642 188
643#if defined(CONFIG_SH_KGDB) 189#if defined(CONFIG_SH_KGDB)
644 /* 190 /*
diff --git a/arch/sh/kernel/timers/Makefile b/arch/sh/kernel/timers/Makefile
new file mode 100644
index 000000000000..151a6a304cec
--- /dev/null
+++ b/arch/sh/kernel/timers/Makefile
@@ -0,0 +1,8 @@
1#
2# Makefile for the various Linux/SuperH timers
3#
4
5obj-y := timer.o
6
7obj-$(CONFIG_SH_TMU) += timer-tmu.o
8
diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c
new file mode 100644
index 000000000000..96a64cb13106
--- /dev/null
+++ b/arch/sh/kernel/timers/timer-tmu.c
@@ -0,0 +1,229 @@
1/*
2 * arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support
3 *
4 * Copyright (C) 2005 Paul Mundt
5 *
6 * TMU handling code hacked out of arch/sh/kernel/time.c
7 *
8 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
9 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
10 * Copyright (C) 2002, 2003, 2004 Paul Mundt
11 * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
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.
16 */
17#include <linux/init.h>
18#include <linux/kernel.h>
19#include <linux/interrupt.h>
20#include <linux/spinlock.h>
21#include <linux/seqlock.h>
22#include <asm/timer.h>
23#include <asm/rtc.h>
24#include <asm/io.h>
25#include <asm/irq.h>
26#include <asm/clock.h>
27
28#define TMU_TOCR_INIT 0x00
29#define TMU0_TCR_INIT 0x0020
30#define TMU_TSTR_INIT 1
31
32#define TMU0_TCR_CALIB 0x0000
33
34static DEFINE_SPINLOCK(tmu0_lock);
35
36static unsigned long tmu_timer_get_offset(void)
37{
38 int count;
39 unsigned long flags;
40
41 static int count_p = 0x7fffffff; /* for the first call after boot */
42 static unsigned long jiffies_p = 0;
43
44 /*
45 * cache volatile jiffies temporarily; we have IRQs turned off.
46 */
47 unsigned long jiffies_t;
48
49 spin_lock_irqsave(&tmu0_lock, flags);
50 /* timer count may underflow right here */
51 count = ctrl_inl(TMU0_TCNT); /* read the latched count */
52
53 jiffies_t = jiffies;
54
55 /*
56 * avoiding timer inconsistencies (they are rare, but they happen)...
57 * there is one kind of problem that must be avoided here:
58 * 1. the timer counter underflows
59 */
60
61 if (jiffies_t == jiffies_p) {
62 if (count > count_p) {
63 /* the nutcase */
64 if (ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */
65 count -= LATCH;
66 } else {
67 printk("%s (): hardware timer problem?\n",
68 __FUNCTION__);
69 }
70 }
71 } else
72 jiffies_p = jiffies_t;
73
74 count_p = count;
75 spin_unlock_irqrestore(&tmu0_lock, flags);
76
77 count = ((LATCH-1) - count) * TICK_SIZE;
78 count = (count + LATCH/2) / LATCH;
79
80 return count;
81}
82
83static irqreturn_t tmu_timer_interrupt(int irq, void *dev_id,
84 struct pt_regs *regs)
85{
86 unsigned long timer_status;
87
88 /* Clear UNF bit */
89 timer_status = ctrl_inw(TMU0_TCR);
90 timer_status &= ~0x100;
91 ctrl_outw(timer_status, TMU0_TCR);
92
93 /*
94 * Here we are in the timer irq handler. We just have irqs locally
95 * disabled but we don't know if the timer_bh is running on the other
96 * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
97 * the irq version of write_lock because as just said we have irq
98 * locally disabled. -arca
99 */
100 write_seqlock(&xtime_lock);
101 handle_timer_tick(regs);
102 write_sequnlock(&xtime_lock);
103
104 return IRQ_HANDLED;
105}
106
107static struct irqaction tmu_irq = {
108 .name = "timer",
109 .handler = tmu_timer_interrupt,
110 .flags = SA_INTERRUPT,
111 .mask = CPU_MASK_NONE,
112};
113
114/*
115 * Hah! We'll see if this works (switching from usecs to nsecs).
116 */
117static unsigned long tmu_timer_get_frequency(void)
118{
119 u32 freq;
120 struct timespec ts1, ts2;
121 unsigned long diff_nsec;
122 unsigned long factor;
123
124 /* Setup the timer: We don't want to generate interrupts, just
125 * have it count down at its natural rate.
126 */
127 ctrl_outb(0, TMU_TSTR);
128#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760)
129 ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
130#endif
131 ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR);
132 ctrl_outl(0xffffffff, TMU0_TCOR);
133 ctrl_outl(0xffffffff, TMU0_TCNT);
134
135 rtc_get_time(&ts2);
136
137 do {
138 rtc_get_time(&ts1);
139 } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
140
141 /* actually start the timer */
142 ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
143
144 do {
145 rtc_get_time(&ts2);
146 } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
147
148 freq = 0xffffffff - ctrl_inl(TMU0_TCNT);
149 if (ts2.tv_nsec < ts1.tv_nsec) {
150 ts2.tv_nsec += 1000000000;
151 ts2.tv_sec--;
152 }
153
154 diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
155
156 /* this should work well if the RTC has a precision of n Hz, where
157 * n is an integer. I don't think we have to worry about the other
158 * cases. */
159 factor = (1000000000 + diff_nsec/2) / diff_nsec;
160
161 if (factor * diff_nsec > 1100000000 ||
162 factor * diff_nsec < 900000000)
163 panic("weird RTC (diff_nsec %ld)", diff_nsec);
164
165 return freq * factor;
166}
167
168static void tmu_clk_init(struct clk *clk)
169{
170 u8 divisor = TMU0_TCR_INIT & 0x7;
171 ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
172 clk->rate = clk->parent->rate / (4 << (divisor << 1));
173}
174
175static void tmu_clk_recalc(struct clk *clk)
176{
177 u8 divisor = ctrl_inw(TMU0_TCR) & 0x7;
178 clk->rate = clk->parent->rate / (4 << (divisor << 1));
179}
180
181static struct clk_ops tmu_clk_ops = {
182 .init = tmu_clk_init,
183 .recalc = tmu_clk_recalc,
184};
185
186static struct clk tmu0_clk = {
187 .name = "tmu0_clk",
188 .ops = &tmu_clk_ops,
189};
190
191static int tmu_timer_init(void)
192{
193 unsigned long interval;
194
195 setup_irq(TIMER_IRQ, &tmu_irq);
196
197 tmu0_clk.parent = clk_get("module_clk");
198
199 /* Start TMU0 */
200 ctrl_outb(0, TMU_TSTR);
201#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760)
202 ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
203#endif
204
205 clk_register(&tmu0_clk);
206 clk_enable(&tmu0_clk);
207
208 interval = (clk_get_rate(&tmu0_clk) + HZ / 2) / HZ;
209 printk(KERN_INFO "Interval = %ld\n", interval);
210
211 ctrl_outl(interval, TMU0_TCOR);
212 ctrl_outl(interval, TMU0_TCNT);
213
214 ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
215
216 return 0;
217}
218
219struct sys_timer_ops tmu_timer_ops = {
220 .init = tmu_timer_init,
221 .get_frequency = tmu_timer_get_frequency,
222 .get_offset = tmu_timer_get_offset,
223};
224
225struct sys_timer tmu_timer = {
226 .name = "tmu",
227 .ops = &tmu_timer_ops,
228};
229
diff --git a/arch/sh/kernel/timers/timer.c b/arch/sh/kernel/timers/timer.c
new file mode 100644
index 000000000000..dc1f631053a8
--- /dev/null
+++ b/arch/sh/kernel/timers/timer.c
@@ -0,0 +1,50 @@
1/*
2 * arch/sh/kernel/timers/timer.c - Common timer code
3 *
4 * Copyright (C) 2005 Paul Mundt
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file "COPYING" in the main directory of this archive
8 * for more details.
9 */
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/timer.h>
13#include <linux/string.h>
14#include <asm/timer.h>
15
16static struct sys_timer *sys_timers[] __initdata = {
17#ifdef CONFIG_SH_TMU
18 &tmu_timer,
19#endif
20 NULL,
21};
22
23static char timer_override[10] __initdata;
24static int __init timer_setup(char *str)
25{
26 if (str)
27 strlcpy(timer_override, str, sizeof(timer_override));
28 return 1;
29}
30__setup("timer=", timer_setup);
31
32struct sys_timer *get_sys_timer(void)
33{
34 int i;
35
36 for (i = 0; i < ARRAY_SIZE(sys_timers); i++) {
37 struct sys_timer *t = sys_timers[i];
38
39 if (unlikely(!t))
40 break;
41 if (unlikely(timer_override[0]))
42 if ((strcmp(timer_override, t->name) != 0))
43 continue;
44 if (likely(t->ops->init() == 0))
45 return t;
46 }
47
48 return NULL;
49}
50