blob: ec66d4aab09849f0fba18a3024c407b6cbda8263 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* sun4m_irq.c
2 * arch/sparc/kernel/sun4m_irq.c:
3 *
4 * djhr: Hacked out of irq.c into a CPU dependent version.
5 *
6 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
7 * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
8 * Copyright (C) 1995 Pete A. Zaitcev (zaitcev@yahoo.com)
9 * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
10 */
11
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/errno.h>
13#include <linux/linkage.h>
14#include <linux/kernel_stat.h>
15#include <linux/signal.h>
16#include <linux/sched.h>
17#include <linux/ptrace.h>
18#include <linux/smp.h>
19#include <linux/interrupt.h>
20#include <linux/slab.h>
21#include <linux/init.h>
22#include <linux/ioport.h>
David S. Miller454eeb22008-08-27 04:05:35 -070023#include <linux/of.h>
24#include <linux/of_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26#include <asm/ptrace.h>
27#include <asm/processor.h>
28#include <asm/system.h>
29#include <asm/psr.h>
30#include <asm/vaddrs.h>
31#include <asm/timer.h>
32#include <asm/openprom.h>
33#include <asm/oplib.h>
34#include <asm/traps.h>
35#include <asm/pgalloc.h>
36#include <asm/pgtable.h>
37#include <asm/smp.h>
38#include <asm/irq.h>
39#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <asm/cacheflush.h>
41
Al Viro32231a62007-07-21 19:18:57 -070042#include "irq.h"
43
David S. Miller69c010b2008-09-19 21:17:43 -070044struct sun4m_irq_percpu {
45 u32 pending;
46 u32 clear;
47 u32 set;
Al Viro32231a62007-07-21 19:18:57 -070048};
49
David S. Miller69c010b2008-09-19 21:17:43 -070050struct sun4m_irq_global {
51 u32 pending;
52 u32 mask;
53 u32 mask_clear;
54 u32 mask_set;
55 u32 interrupt_target;
Al Viro32231a62007-07-21 19:18:57 -070056};
57
David S. Miller69c010b2008-09-19 21:17:43 -070058/* Code in entry.S needs to get at these register mappings. */
59struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
60struct sun4m_irq_global __iomem *sun4m_irq_global;
61
Al Viro32231a62007-07-21 19:18:57 -070062/* Dave Redman (djhr@tadpole.co.uk)
63 * The sun4m interrupt registers.
64 */
65#define SUN4M_INT_ENABLE 0x80000000
66#define SUN4M_INT_E14 0x00000080
67#define SUN4M_INT_E10 0x00080000
68
69#define SUN4M_HARD_INT(x) (0x000000001 << (x))
70#define SUN4M_SOFT_INT(x) (0x000010000 << (x))
71
72#define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */
73#define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */
74#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */
75#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */
76#define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */
77#define SUN4M_INT_MODULE 0x00200000 /* module interrupt */
78#define SUN4M_INT_VIDEO 0x00100000 /* onboard video */
79#define SUN4M_INT_REALTIME 0x00080000 /* system timer */
80#define SUN4M_INT_SCSI 0x00040000 /* onboard scsi */
81#define SUN4M_INT_AUDIO 0x00020000 /* audio/isdn */
82#define SUN4M_INT_ETHERNET 0x00010000 /* onboard ethernet */
83#define SUN4M_INT_SERIAL 0x00008000 /* serial ports */
84#define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */
85#define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */
86
87#define SUN4M_INT_SBUS(x) (1 << (x+7))
88#define SUN4M_INT_VME(x) (1 << (x))
89
David S. Miller778b1c62008-09-19 15:33:21 -070090/* Interrupt level assignment on sun4m:
91 *
92 * level source
93 * ------------------------------------------------------------
94 * 1 softint-1
95 * 2 softint-2, VME/SBUS level 1
96 * 3 softint-3, VME/SBUS level 2
97 * 4 softint-4, onboard SCSI
98 * 5 softint-5, VME/SBUS level 3
99 * 6 softint-6, onboard ETHERNET
100 * 7 softint-7, VME/SBUS level 4
101 * 8 softint-8, onboard VIDEO
102 * 9 softint-9, VME/SBUS level 5, Module Interrupt
103 * 10 softint-10, system counter/timer
104 * 11 softint-11, VME/SBUS level 6, Floppy
105 * 12 softint-12, Keyboard/Mouse, Serial
106 * 13 softint-13, VME/SBUS level 7, ISDN Audio
107 * 14 softint-14, per-processor counter/timer
108 * 15 softint-15, Asynchronous Errors (broadcast)
109 *
110 * Each interrupt source is masked distinctly in the sun4m interrupt
111 * registers. The PIL level alone is therefore ambiguous, since multiple
112 * interrupt sources map to a single PIL.
113 *
114 * This ambiguity is resolved in the 'intr' property for device nodes
115 * in the OF device tree. Each 'intr' property entry is composed of
116 * two 32-bit words. The first word is the IRQ priority value, which
117 * is what we're intersted in. The second word is the IRQ vector, which
118 * is unused.
119 *
120 * The low 4 bits of the IRQ priority indicate the PIL, and the upper
121 * 4 bits indicate onboard vs. SBUS leveled vs. VME leveled. 0x20
122 * means onboard, 0x30 means SBUS leveled, and 0x40 means VME leveled.
123 *
124 * For example, an 'intr' IRQ priority value of 0x24 is onboard SCSI
125 * whereas a value of 0x33 is SBUS level 2. Here are some sample
126 * 'intr' property IRQ priority values from ss4, ss5, ss10, ss20, and
127 * Tadpole S3 GX systems.
128 *
129 * esp: 0x24 onboard ESP SCSI
130 * le: 0x26 onboard Lance ETHERNET
131 * p9100: 0x32 SBUS level 1 P9100 video
132 * bpp: 0x33 SBUS level 2 BPP parallel port device
133 * DBRI: 0x39 SBUS level 5 DBRI ISDN audio
134 * SUNW,leo: 0x39 SBUS level 5 LEO video
135 * pcmcia: 0x3b SBUS level 6 PCMCIA controller
136 * uctrl: 0x3b SBUS level 6 UCTRL device
137 * modem: 0x3d SBUS level 7 MODEM
138 * zs: 0x2c onboard keyboard/mouse/serial
139 * floppy: 0x2b onboard Floppy
140 * power: 0x22 onboard power device (XXX unknown mask bit XXX)
141 */
142
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143/* These tables only apply for interrupts greater than 15..
144 *
145 * any intr value below 0x10 is considered to be a soft-int
146 * this may be useful or it may not.. but that's how I've done it.
147 * and it won't clash with what OBP is telling us about devices.
148 *
149 * take an encoded intr value and lookup if it's valid
150 * then get the mask bits that match from irq_mask
151 *
152 * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee.
153 */
154static unsigned char irq_xlate[32] = {
155 /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */
156 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 5, 6, 14, 0, 7,
157 0, 0, 8, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0
158};
159
160static unsigned long irq_mask[] = {
161 0, /* illegal index */
162 SUN4M_INT_SCSI, /* 1 irq 4 */
163 SUN4M_INT_ETHERNET, /* 2 irq 6 */
164 SUN4M_INT_VIDEO, /* 3 irq 8 */
165 SUN4M_INT_REALTIME, /* 4 irq 10 */
166 SUN4M_INT_FLOPPY, /* 5 irq 11 */
167 (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), /* 6 irq 12 */
168 SUN4M_INT_MODULE_ERR, /* 7 irq 15 */
169 SUN4M_INT_SBUS(0), /* 8 irq 2 */
170 SUN4M_INT_SBUS(1), /* 9 irq 3 */
171 SUN4M_INT_SBUS(2), /* 10 irq 5 */
172 SUN4M_INT_SBUS(3), /* 11 irq 7 */
173 SUN4M_INT_SBUS(4), /* 12 irq 9 */
174 SUN4M_INT_SBUS(5), /* 13 irq 11 */
175 SUN4M_INT_SBUS(6) /* 14 irq 13 */
176};
177
Adrian Bunkc61c65c2008-06-05 11:40:58 -0700178static unsigned long sun4m_get_irqmask(unsigned int irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179{
180 unsigned long mask;
181
182 if (irq > 0x20) {
183 /* OBIO/SBUS interrupts */
184 irq &= 0x1f;
185 mask = irq_mask[irq_xlate[irq]];
186 if (!mask)
187 printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
188 } else {
189 /* Soft Interrupts will come here.
190 * Currently there is no way to trigger them but I'm sure
191 * something could be cooked up.
192 */
193 irq &= 0xf;
194 mask = SUN4M_SOFT_INT(irq);
195 }
196 return mask;
197}
198
199static void sun4m_disable_irq(unsigned int irq_nr)
200{
201 unsigned long mask, flags;
202 int cpu = smp_processor_id();
203
204 mask = sun4m_get_irqmask(irq_nr);
205 local_irq_save(flags);
206 if (irq_nr > 15)
David S. Miller69c010b2008-09-19 21:17:43 -0700207 sbus_writel(mask, &sun4m_irq_global->mask_set);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 else
David S. Miller69c010b2008-09-19 21:17:43 -0700209 sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 local_irq_restore(flags);
211}
212
213static void sun4m_enable_irq(unsigned int irq_nr)
214{
215 unsigned long mask, flags;
216 int cpu = smp_processor_id();
217
218 /* Dreadful floppy hack. When we use 0x2b instead of
219 * 0x0b the system blows (it starts to whistle!).
220 * So we continue to use 0x0b. Fixme ASAP. --P3
221 */
222 if (irq_nr != 0x0b) {
223 mask = sun4m_get_irqmask(irq_nr);
224 local_irq_save(flags);
225 if (irq_nr > 15)
David S. Miller69c010b2008-09-19 21:17:43 -0700226 sbus_writel(mask, &sun4m_irq_global->mask_clear);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 else
David S. Miller69c010b2008-09-19 21:17:43 -0700228 sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 local_irq_restore(flags);
230 } else {
231 local_irq_save(flags);
David S. Miller69c010b2008-09-19 21:17:43 -0700232 sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 local_irq_restore(flags);
234 }
235}
236
237static unsigned long cpu_pil_to_imask[16] = {
238/*0*/ 0x00000000,
239/*1*/ 0x00000000,
240/*2*/ SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0),
241/*3*/ SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1),
242/*4*/ SUN4M_INT_SCSI,
243/*5*/ SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
244/*6*/ SUN4M_INT_ETHERNET,
245/*7*/ SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
246/*8*/ SUN4M_INT_VIDEO,
247/*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
248/*10*/ SUN4M_INT_REALTIME,
249/*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
250/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
251/*13*/ SUN4M_INT_AUDIO,
252/*14*/ SUN4M_INT_E14,
253/*15*/ 0x00000000
254};
255
256/* We assume the caller has disabled local interrupts when these are called,
257 * or else very bizarre behavior will result.
258 */
259static void sun4m_disable_pil_irq(unsigned int pil)
260{
David S. Miller69c010b2008-09-19 21:17:43 -0700261 sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262}
263
264static void sun4m_enable_pil_irq(unsigned int pil)
265{
David S. Miller69c010b2008-09-19 21:17:43 -0700266 sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267}
268
269#ifdef CONFIG_SMP
270static void sun4m_send_ipi(int cpu, int level)
271{
David S. Miller69c010b2008-09-19 21:17:43 -0700272 unsigned long mask = sun4m_get_irqmask(level);
273 sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
276static void sun4m_clear_ipi(int cpu, int level)
277{
David S. Miller69c010b2008-09-19 21:17:43 -0700278 unsigned long mask = sun4m_get_irqmask(level);
279 sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
282static void sun4m_set_udt(int cpu)
283{
David S. Miller69c010b2008-09-19 21:17:43 -0700284 sbus_writel(cpu, &sun4m_irq_global->interrupt_target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285}
286#endif
287
David S. Miller9b2e43a2008-09-11 23:08:30 -0700288struct sun4m_timer_percpu {
289 u32 l14_limit;
290 u32 l14_count;
291 u32 l14_limit_noclear;
292 u32 user_timer_start_stop;
293};
294
295static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS];
296
297struct sun4m_timer_global {
298 u32 l10_limit;
299 u32 l10_count;
300 u32 l10_limit_noclear;
301 u32 reserved;
302 u32 timer_config;
303};
304
305static struct sun4m_timer_global __iomem *timers_global;
306
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307#define OBIO_INTR 0x20
308#define TIMER_IRQ (OBIO_INTR | 10)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
311
312static void sun4m_clear_clock_irq(void)
313{
David S. Miller9b2e43a2008-09-11 23:08:30 -0700314 sbus_readl(&timers_global->l10_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315}
316
David S. Miller1de937a2008-09-13 22:07:56 -0700317/* Exported for sun4m_smp.c */
318void sun4m_clear_profile_irq(int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
David S. Miller9b2e43a2008-09-11 23:08:30 -0700320 sbus_readl(&timers_percpu[cpu]->l14_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321}
322
323static void sun4m_load_profile_irq(int cpu, unsigned int limit)
324{
David S. Miller9b2e43a2008-09-11 23:08:30 -0700325 sbus_writel(limit, &timers_percpu[cpu]->l14_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326}
327
David Howells40220c12006-10-09 12:19:47 +0100328static void __init sun4m_init_timers(irq_handler_t counter_fn)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
David S. Miller9b2e43a2008-09-11 23:08:30 -0700330 struct device_node *dp = of_find_node_by_name(NULL, "counter");
331 int i, err, len, num_cpu_timers;
332 const u32 *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
David S. Miller9b2e43a2008-09-11 23:08:30 -0700334 if (!dp) {
335 printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n");
336 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 }
338
David S. Miller9b2e43a2008-09-11 23:08:30 -0700339 addr = of_get_property(dp, "address", &len);
340 if (!addr) {
341 printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n");
342 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 }
David S. Miller9b2e43a2008-09-11 23:08:30 -0700344
345 num_cpu_timers = (len / sizeof(u32)) - 1;
346 for (i = 0; i < num_cpu_timers; i++) {
347 timers_percpu[i] = (void __iomem *)
348 (unsigned long) addr[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 }
David S. Miller9b2e43a2008-09-11 23:08:30 -0700350 timers_global = (void __iomem *)
351 (unsigned long) addr[num_cpu_timers];
352
353 sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit);
354
355 master_l10_counter = &timers_global->l10_count;
David S. Miller9b2e43a2008-09-11 23:08:30 -0700356
357 err = request_irq(TIMER_IRQ, counter_fn,
358 (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL);
359 if (err) {
360 printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
361 err);
362 return;
363 }
364
365 for (i = 0; i < num_cpu_timers; i++)
366 sbus_writel(0, &timers_percpu[i]->l14_limit);
367 if (num_cpu_timers == 4)
David S. Miller69c010b2008-09-19 21:17:43 -0700368 sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set);
David S. Miller9b2e43a2008-09-11 23:08:30 -0700369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370#ifdef CONFIG_SMP
371 {
372 unsigned long flags;
373 extern unsigned long lvl14_save[4];
374 struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
375
376 /* For SMP we use the level 14 ticker, however the bootup code
Simon Arlottd1a78c32007-05-11 13:51:23 -0700377 * has copied the firmware's level 14 vector into the boot cpu's
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 * trap table, we must fix this now or we get squashed.
379 */
380 local_irq_save(flags);
381 trap_table->inst_one = lvl14_save[0];
382 trap_table->inst_two = lvl14_save[1];
383 trap_table->inst_three = lvl14_save[2];
384 trap_table->inst_four = lvl14_save[3];
385 local_flush_cache_all();
386 local_irq_restore(flags);
387 }
388#endif
389}
390
391void __init sun4m_init_IRQ(void)
392{
David S. Miller69c010b2008-09-19 21:17:43 -0700393 struct device_node *dp = of_find_node_by_name(NULL, "interrupt");
394 int len, i, mid, num_cpu_iregs;
395 const u32 *addr;
396
397 if (!dp) {
398 printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n");
399 return;
400 }
401
402 addr = of_get_property(dp, "address", &len);
403 if (!addr) {
404 printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n");
405 return;
406 }
407
408 num_cpu_iregs = (len / sizeof(u32)) - 1;
409 for (i = 0; i < num_cpu_iregs; i++) {
410 sun4m_irq_percpu[i] = (void __iomem *)
411 (unsigned long) addr[i];
412 }
413 sun4m_irq_global = (void __iomem *)
414 (unsigned long) addr[num_cpu_iregs];
415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 local_irq_disable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
David S. Miller69c010b2008-09-19 21:17:43 -0700418 sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
David S. Miller69c010b2008-09-19 21:17:43 -0700420 sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
David S. Millere7913de2008-09-13 22:48:41 -0700422 if (num_cpu_iregs == 4)
David S. Miller69c010b2008-09-19 21:17:43 -0700423 sbus_writel(0, &sun4m_irq_global->interrupt_target);
David S. Millere7913de2008-09-13 22:48:41 -0700424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
426 BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
427 BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
428 BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
429 BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 sparc_init_timers = sun4m_init_timers;
432#ifdef CONFIG_SMP
433 BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
434 BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
435 BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
436#endif
David S. Miller69c010b2008-09-19 21:17:43 -0700437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 /* Cannot enable interrupts until OBP ticker is disabled. */
439}