diff options
Diffstat (limited to 'arch/sparc/kernel/sun4m_irq.c')
-rw-r--r-- | arch/sparc/kernel/sun4m_irq.c | 495 |
1 files changed, 253 insertions, 242 deletions
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 94e02de960ea..f10317179ee6 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/ioport.h> | 22 | #include <linux/ioport.h> |
23 | #include <linux/of.h> | ||
24 | #include <linux/of_device.h> | ||
23 | 25 | ||
24 | #include <asm/ptrace.h> | 26 | #include <asm/ptrace.h> |
25 | #include <asm/processor.h> | 27 | #include <asm/processor.h> |
@@ -35,59 +37,27 @@ | |||
35 | #include <asm/smp.h> | 37 | #include <asm/smp.h> |
36 | #include <asm/irq.h> | 38 | #include <asm/irq.h> |
37 | #include <asm/io.h> | 39 | #include <asm/io.h> |
38 | #include <asm/sbus.h> | ||
39 | #include <asm/cacheflush.h> | 40 | #include <asm/cacheflush.h> |
40 | 41 | ||
41 | #include "irq.h" | 42 | #include "irq.h" |
42 | 43 | ||
43 | /* On the sun4m, just like the timers, we have both per-cpu and master | 44 | struct sun4m_irq_percpu { |
44 | * interrupt registers. | 45 | u32 pending; |
45 | */ | 46 | u32 clear; |
46 | 47 | u32 set; | |
47 | /* These registers are used for sending/receiving irqs from/to | ||
48 | * different cpu's. | ||
49 | */ | ||
50 | struct sun4m_intreg_percpu { | ||
51 | unsigned int tbt; /* Interrupts still pending for this cpu. */ | ||
52 | |||
53 | /* These next two registers are WRITE-ONLY and are only | ||
54 | * "on bit" sensitive, "off bits" written have NO affect. | ||
55 | */ | ||
56 | unsigned int clear; /* Clear this cpus irqs here. */ | ||
57 | unsigned int set; /* Set this cpus irqs here. */ | ||
58 | unsigned char space[PAGE_SIZE - 12]; | ||
59 | }; | 48 | }; |
60 | 49 | ||
61 | /* | 50 | struct sun4m_irq_global { |
62 | * djhr | 51 | u32 pending; |
63 | * Actually the clear and set fields in this struct are misleading.. | 52 | u32 mask; |
64 | * according to the SLAVIO manual (and the same applies for the SEC) | 53 | u32 mask_clear; |
65 | * the clear field clears bits in the mask which will ENABLE that IRQ | 54 | u32 mask_set; |
66 | * the set field sets bits in the mask to DISABLE the IRQ. | 55 | u32 interrupt_target; |
67 | * | ||
68 | * Also the undirected_xx address in the SLAVIO is defined as | ||
69 | * RESERVED and write only.. | ||
70 | * | ||
71 | * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor | ||
72 | * sun4m machines, for MP the layout makes more sense. | ||
73 | */ | ||
74 | struct sun4m_intregs { | ||
75 | struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS]; | ||
76 | unsigned int tbt; /* IRQ's that are still pending. */ | ||
77 | unsigned int irqs; /* Master IRQ bits. */ | ||
78 | |||
79 | /* Again, like the above, two these registers are WRITE-ONLY. */ | ||
80 | unsigned int clear; /* Clear master IRQ's by setting bits here. */ | ||
81 | unsigned int set; /* Set master IRQ's by setting bits here. */ | ||
82 | |||
83 | /* This register is both READ and WRITE. */ | ||
84 | unsigned int undirected_target; /* Which cpu gets undirected irqs. */ | ||
85 | }; | 56 | }; |
86 | 57 | ||
87 | static unsigned long dummy; | 58 | /* Code in entry.S needs to get at these register mappings. */ |
88 | 59 | struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; | |
89 | struct sun4m_intregs *sun4m_interrupts; | 60 | struct sun4m_irq_global __iomem *sun4m_irq_global; |
90 | unsigned long *irq_rcvreg = &dummy; | ||
91 | 61 | ||
92 | /* Dave Redman (djhr@tadpole.co.uk) | 62 | /* Dave Redman (djhr@tadpole.co.uk) |
93 | * The sun4m interrupt registers. | 63 | * The sun4m interrupt registers. |
@@ -101,8 +71,9 @@ unsigned long *irq_rcvreg = &dummy; | |||
101 | 71 | ||
102 | #define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */ | 72 | #define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */ |
103 | #define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */ | 73 | #define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */ |
104 | #define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */ | 74 | #define SUN4M_INT_M2S_WRITE_ERR 0x20000000 /* write buffer error */ |
105 | #define SUN4M_INT_ECC 0x10000000 /* ecc memory error */ | 75 | #define SUN4M_INT_ECC_ERR 0x10000000 /* ecc memory error */ |
76 | #define SUN4M_INT_VME_ERR 0x08000000 /* vme async error */ | ||
106 | #define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */ | 77 | #define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */ |
107 | #define SUN4M_INT_MODULE 0x00200000 /* module interrupt */ | 78 | #define SUN4M_INT_MODULE 0x00200000 /* module interrupt */ |
108 | #define SUN4M_INT_VIDEO 0x00100000 /* onboard video */ | 79 | #define SUN4M_INT_VIDEO 0x00100000 /* onboard video */ |
@@ -113,75 +84,126 @@ unsigned long *irq_rcvreg = &dummy; | |||
113 | #define SUN4M_INT_SERIAL 0x00008000 /* serial ports */ | 84 | #define SUN4M_INT_SERIAL 0x00008000 /* serial ports */ |
114 | #define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */ | 85 | #define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */ |
115 | #define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */ | 86 | #define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */ |
87 | #define SUN4M_INT_VMEBITS 0x0000007F /* vme int bits */ | ||
88 | |||
89 | #define SUN4M_INT_ERROR (SUN4M_INT_MODULE_ERR | \ | ||
90 | SUN4M_INT_M2S_WRITE_ERR | \ | ||
91 | SUN4M_INT_ECC_ERR | \ | ||
92 | SUN4M_INT_VME_ERR) | ||
116 | 93 | ||
117 | #define SUN4M_INT_SBUS(x) (1 << (x+7)) | 94 | #define SUN4M_INT_SBUS(x) (1 << (x+7)) |
118 | #define SUN4M_INT_VME(x) (1 << (x)) | 95 | #define SUN4M_INT_VME(x) (1 << (x)) |
119 | 96 | ||
120 | /* These tables only apply for interrupts greater than 15.. | 97 | /* Interrupt levels used by OBP */ |
121 | * | 98 | #define OBP_INT_LEVEL_SOFT 0x10 |
122 | * any intr value below 0x10 is considered to be a soft-int | 99 | #define OBP_INT_LEVEL_ONBOARD 0x20 |
123 | * this may be useful or it may not.. but that's how I've done it. | 100 | #define OBP_INT_LEVEL_SBUS 0x30 |
124 | * and it won't clash with what OBP is telling us about devices. | 101 | #define OBP_INT_LEVEL_VME 0x40 |
102 | |||
103 | /* Interrupt level assignment on sun4m: | ||
104 | * | ||
105 | * level source | ||
106 | * ------------------------------------------------------------ | ||
107 | * 1 softint-1 | ||
108 | * 2 softint-2, VME/SBUS level 1 | ||
109 | * 3 softint-3, VME/SBUS level 2 | ||
110 | * 4 softint-4, onboard SCSI | ||
111 | * 5 softint-5, VME/SBUS level 3 | ||
112 | * 6 softint-6, onboard ETHERNET | ||
113 | * 7 softint-7, VME/SBUS level 4 | ||
114 | * 8 softint-8, onboard VIDEO | ||
115 | * 9 softint-9, VME/SBUS level 5, Module Interrupt | ||
116 | * 10 softint-10, system counter/timer | ||
117 | * 11 softint-11, VME/SBUS level 6, Floppy | ||
118 | * 12 softint-12, Keyboard/Mouse, Serial | ||
119 | * 13 softint-13, VME/SBUS level 7, ISDN Audio | ||
120 | * 14 softint-14, per-processor counter/timer | ||
121 | * 15 softint-15, Asynchronous Errors (broadcast) | ||
125 | * | 122 | * |
126 | * take an encoded intr value and lookup if it's valid | 123 | * Each interrupt source is masked distinctly in the sun4m interrupt |
127 | * then get the mask bits that match from irq_mask | 124 | * registers. The PIL level alone is therefore ambiguous, since multiple |
125 | * interrupt sources map to a single PIL. | ||
128 | * | 126 | * |
129 | * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee. | 127 | * This ambiguity is resolved in the 'intr' property for device nodes |
128 | * in the OF device tree. Each 'intr' property entry is composed of | ||
129 | * two 32-bit words. The first word is the IRQ priority value, which | ||
130 | * is what we're intersted in. The second word is the IRQ vector, which | ||
131 | * is unused. | ||
132 | * | ||
133 | * The low 4 bits of the IRQ priority indicate the PIL, and the upper | ||
134 | * 4 bits indicate onboard vs. SBUS leveled vs. VME leveled. 0x20 | ||
135 | * means onboard, 0x30 means SBUS leveled, and 0x40 means VME leveled. | ||
136 | * | ||
137 | * For example, an 'intr' IRQ priority value of 0x24 is onboard SCSI | ||
138 | * whereas a value of 0x33 is SBUS level 2. Here are some sample | ||
139 | * 'intr' property IRQ priority values from ss4, ss5, ss10, ss20, and | ||
140 | * Tadpole S3 GX systems. | ||
141 | * | ||
142 | * esp: 0x24 onboard ESP SCSI | ||
143 | * le: 0x26 onboard Lance ETHERNET | ||
144 | * p9100: 0x32 SBUS level 1 P9100 video | ||
145 | * bpp: 0x33 SBUS level 2 BPP parallel port device | ||
146 | * DBRI: 0x39 SBUS level 5 DBRI ISDN audio | ||
147 | * SUNW,leo: 0x39 SBUS level 5 LEO video | ||
148 | * pcmcia: 0x3b SBUS level 6 PCMCIA controller | ||
149 | * uctrl: 0x3b SBUS level 6 UCTRL device | ||
150 | * modem: 0x3d SBUS level 7 MODEM | ||
151 | * zs: 0x2c onboard keyboard/mouse/serial | ||
152 | * floppy: 0x2b onboard Floppy | ||
153 | * power: 0x22 onboard power device (XXX unknown mask bit XXX) | ||
130 | */ | 154 | */ |
131 | static unsigned char irq_xlate[32] = { | ||
132 | /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */ | ||
133 | 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 5, 6, 14, 0, 7, | ||
134 | 0, 0, 8, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0 | ||
135 | }; | ||
136 | 155 | ||
137 | static unsigned long irq_mask[] = { | 156 | static unsigned long irq_mask[0x50] = { |
138 | 0, /* illegal index */ | 157 | /* SMP */ |
139 | SUN4M_INT_SCSI, /* 1 irq 4 */ | 158 | 0, SUN4M_SOFT_INT(1), |
140 | SUN4M_INT_ETHERNET, /* 2 irq 6 */ | 159 | SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), |
141 | SUN4M_INT_VIDEO, /* 3 irq 8 */ | 160 | SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5), |
142 | SUN4M_INT_REALTIME, /* 4 irq 10 */ | 161 | SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7), |
143 | SUN4M_INT_FLOPPY, /* 5 irq 11 */ | 162 | SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9), |
144 | (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), /* 6 irq 12 */ | 163 | SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11), |
145 | SUN4M_INT_MODULE_ERR, /* 7 irq 15 */ | 164 | SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13), |
146 | SUN4M_INT_SBUS(0), /* 8 irq 2 */ | 165 | SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15), |
147 | SUN4M_INT_SBUS(1), /* 9 irq 3 */ | 166 | /* soft */ |
148 | SUN4M_INT_SBUS(2), /* 10 irq 5 */ | 167 | 0, SUN4M_SOFT_INT(1), |
149 | SUN4M_INT_SBUS(3), /* 11 irq 7 */ | 168 | SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), |
150 | SUN4M_INT_SBUS(4), /* 12 irq 9 */ | 169 | SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5), |
151 | SUN4M_INT_SBUS(5), /* 13 irq 11 */ | 170 | SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7), |
152 | SUN4M_INT_SBUS(6) /* 14 irq 13 */ | 171 | SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9), |
172 | SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11), | ||
173 | SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13), | ||
174 | SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15), | ||
175 | /* onboard */ | ||
176 | 0, 0, 0, 0, | ||
177 | SUN4M_INT_SCSI, 0, SUN4M_INT_ETHERNET, 0, | ||
178 | SUN4M_INT_VIDEO, SUN4M_INT_MODULE, | ||
179 | SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY, | ||
180 | (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), | ||
181 | SUN4M_INT_AUDIO, 0, SUN4M_INT_MODULE_ERR, | ||
182 | /* sbus */ | ||
183 | 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1), | ||
184 | 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3), | ||
185 | 0, SUN4M_INT_SBUS(4), 0, SUN4M_INT_SBUS(5), | ||
186 | 0, SUN4M_INT_SBUS(6), 0, 0, | ||
187 | /* vme */ | ||
188 | 0, 0, SUN4M_INT_VME(0), SUN4M_INT_VME(1), | ||
189 | 0, SUN4M_INT_VME(2), 0, SUN4M_INT_VME(3), | ||
190 | 0, SUN4M_INT_VME(4), 0, SUN4M_INT_VME(5), | ||
191 | 0, SUN4M_INT_VME(6), 0, 0 | ||
153 | }; | 192 | }; |
154 | 193 | ||
155 | static int sun4m_pil_map[] = { 0, 2, 3, 5, 7, 9, 11, 13 }; | ||
156 | |||
157 | static unsigned int sun4m_sbint_to_irq(struct sbus_dev *sdev, | ||
158 | unsigned int sbint) | ||
159 | { | ||
160 | if (sbint >= sizeof(sun4m_pil_map)) { | ||
161 | printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); | ||
162 | BUG(); | ||
163 | } | ||
164 | return sun4m_pil_map[sbint] | 0x30; | ||
165 | } | ||
166 | |||
167 | static unsigned long sun4m_get_irqmask(unsigned int irq) | 194 | static unsigned long sun4m_get_irqmask(unsigned int irq) |
168 | { | 195 | { |
169 | unsigned long mask; | 196 | unsigned long mask; |
170 | 197 | ||
171 | if (irq > 0x20) { | 198 | if (irq < 0x50) |
172 | /* OBIO/SBUS interrupts */ | 199 | mask = irq_mask[irq]; |
173 | irq &= 0x1f; | 200 | else |
174 | mask = irq_mask[irq_xlate[irq]]; | 201 | mask = 0; |
175 | if (!mask) | 202 | |
176 | printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq); | 203 | if (!mask) |
177 | } else { | 204 | printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n", |
178 | /* Soft Interrupts will come here. | 205 | irq); |
179 | * Currently there is no way to trigger them but I'm sure | 206 | |
180 | * something could be cooked up. | ||
181 | */ | ||
182 | irq &= 0xf; | ||
183 | mask = SUN4M_SOFT_INT(irq); | ||
184 | } | ||
185 | return mask; | 207 | return mask; |
186 | } | 208 | } |
187 | 209 | ||
@@ -193,9 +215,9 @@ static void sun4m_disable_irq(unsigned int irq_nr) | |||
193 | mask = sun4m_get_irqmask(irq_nr); | 215 | mask = sun4m_get_irqmask(irq_nr); |
194 | local_irq_save(flags); | 216 | local_irq_save(flags); |
195 | if (irq_nr > 15) | 217 | if (irq_nr > 15) |
196 | sun4m_interrupts->set = mask; | 218 | sbus_writel(mask, &sun4m_irq_global->mask_set); |
197 | else | 219 | else |
198 | sun4m_interrupts->cpu_intregs[cpu].set = mask; | 220 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); |
199 | local_irq_restore(flags); | 221 | local_irq_restore(flags); |
200 | } | 222 | } |
201 | 223 | ||
@@ -212,13 +234,13 @@ static void sun4m_enable_irq(unsigned int irq_nr) | |||
212 | mask = sun4m_get_irqmask(irq_nr); | 234 | mask = sun4m_get_irqmask(irq_nr); |
213 | local_irq_save(flags); | 235 | local_irq_save(flags); |
214 | if (irq_nr > 15) | 236 | if (irq_nr > 15) |
215 | sun4m_interrupts->clear = mask; | 237 | sbus_writel(mask, &sun4m_irq_global->mask_clear); |
216 | else | 238 | else |
217 | sun4m_interrupts->cpu_intregs[cpu].clear = mask; | 239 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); |
218 | local_irq_restore(flags); | 240 | local_irq_restore(flags); |
219 | } else { | 241 | } else { |
220 | local_irq_save(flags); | 242 | local_irq_save(flags); |
221 | sun4m_interrupts->clear = SUN4M_INT_FLOPPY; | 243 | sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear); |
222 | local_irq_restore(flags); | 244 | local_irq_restore(flags); |
223 | } | 245 | } |
224 | } | 246 | } |
@@ -236,10 +258,10 @@ static unsigned long cpu_pil_to_imask[16] = { | |||
236 | /*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR, | 258 | /*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR, |
237 | /*10*/ SUN4M_INT_REALTIME, | 259 | /*10*/ SUN4M_INT_REALTIME, |
238 | /*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY, | 260 | /*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY, |
239 | /*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, | 261 | /*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, |
240 | /*13*/ SUN4M_INT_AUDIO, | 262 | /*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO, |
241 | /*14*/ SUN4M_INT_E14, | 263 | /*14*/ SUN4M_INT_E14, |
242 | /*15*/ 0x00000000 | 264 | /*15*/ SUN4M_INT_ERROR |
243 | }; | 265 | }; |
244 | 266 | ||
245 | /* We assume the caller has disabled local interrupts when these are called, | 267 | /* We assume the caller has disabled local interrupts when these are called, |
@@ -247,126 +269,141 @@ static unsigned long cpu_pil_to_imask[16] = { | |||
247 | */ | 269 | */ |
248 | static void sun4m_disable_pil_irq(unsigned int pil) | 270 | static void sun4m_disable_pil_irq(unsigned int pil) |
249 | { | 271 | { |
250 | sun4m_interrupts->set = cpu_pil_to_imask[pil]; | 272 | sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set); |
251 | } | 273 | } |
252 | 274 | ||
253 | static void sun4m_enable_pil_irq(unsigned int pil) | 275 | static void sun4m_enable_pil_irq(unsigned int pil) |
254 | { | 276 | { |
255 | sun4m_interrupts->clear = cpu_pil_to_imask[pil]; | 277 | sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear); |
256 | } | 278 | } |
257 | 279 | ||
258 | #ifdef CONFIG_SMP | 280 | #ifdef CONFIG_SMP |
259 | static void sun4m_send_ipi(int cpu, int level) | 281 | static void sun4m_send_ipi(int cpu, int level) |
260 | { | 282 | { |
261 | unsigned long mask; | 283 | unsigned long mask = sun4m_get_irqmask(level); |
262 | 284 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); | |
263 | mask = sun4m_get_irqmask(level); | ||
264 | sun4m_interrupts->cpu_intregs[cpu].set = mask; | ||
265 | } | 285 | } |
266 | 286 | ||
267 | static void sun4m_clear_ipi(int cpu, int level) | 287 | static void sun4m_clear_ipi(int cpu, int level) |
268 | { | 288 | { |
269 | unsigned long mask; | 289 | unsigned long mask = sun4m_get_irqmask(level); |
270 | 290 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); | |
271 | mask = sun4m_get_irqmask(level); | ||
272 | sun4m_interrupts->cpu_intregs[cpu].clear = mask; | ||
273 | } | 291 | } |
274 | 292 | ||
275 | static void sun4m_set_udt(int cpu) | 293 | static void sun4m_set_udt(int cpu) |
276 | { | 294 | { |
277 | sun4m_interrupts->undirected_target = cpu; | 295 | sbus_writel(cpu, &sun4m_irq_global->interrupt_target); |
278 | } | 296 | } |
279 | #endif | 297 | #endif |
280 | 298 | ||
281 | #define OBIO_INTR 0x20 | 299 | struct sun4m_timer_percpu { |
282 | #define TIMER_IRQ (OBIO_INTR | 10) | 300 | u32 l14_limit; |
283 | #define PROFILE_IRQ (OBIO_INTR | 14) | 301 | u32 l14_count; |
302 | u32 l14_limit_noclear; | ||
303 | u32 user_timer_start_stop; | ||
304 | }; | ||
305 | |||
306 | static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS]; | ||
307 | |||
308 | struct sun4m_timer_global { | ||
309 | u32 l10_limit; | ||
310 | u32 l10_count; | ||
311 | u32 l10_limit_noclear; | ||
312 | u32 reserved; | ||
313 | u32 timer_config; | ||
314 | }; | ||
315 | |||
316 | static struct sun4m_timer_global __iomem *timers_global; | ||
317 | |||
318 | #define TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10) | ||
284 | 319 | ||
285 | static struct sun4m_timer_regs *sun4m_timers; | ||
286 | unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10); | 320 | unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10); |
287 | 321 | ||
288 | static void sun4m_clear_clock_irq(void) | 322 | static void sun4m_clear_clock_irq(void) |
289 | { | 323 | { |
290 | volatile unsigned int clear_intr; | 324 | sbus_readl(&timers_global->l10_limit); |
291 | clear_intr = sun4m_timers->l10_timer_limit; | ||
292 | } | 325 | } |
293 | 326 | ||
294 | static void sun4m_clear_profile_irq(int cpu) | 327 | void sun4m_nmi(struct pt_regs *regs) |
295 | { | 328 | { |
296 | volatile unsigned int clear; | 329 | unsigned long afsr, afar, si; |
297 | 330 | ||
298 | clear = sun4m_timers->cpu_timers[cpu].l14_timer_limit; | 331 | printk(KERN_ERR "Aieee: sun4m NMI received!\n"); |
332 | /* XXX HyperSparc hack XXX */ | ||
333 | __asm__ __volatile__("mov 0x500, %%g1\n\t" | ||
334 | "lda [%%g1] 0x4, %0\n\t" | ||
335 | "mov 0x600, %%g1\n\t" | ||
336 | "lda [%%g1] 0x4, %1\n\t" : | ||
337 | "=r" (afsr), "=r" (afar)); | ||
338 | printk(KERN_ERR "afsr=%08lx afar=%08lx\n", afsr, afar); | ||
339 | si = sbus_readl(&sun4m_irq_global->pending); | ||
340 | printk(KERN_ERR "si=%08lx\n", si); | ||
341 | if (si & SUN4M_INT_MODULE_ERR) | ||
342 | printk(KERN_ERR "Module async error\n"); | ||
343 | if (si & SUN4M_INT_M2S_WRITE_ERR) | ||
344 | printk(KERN_ERR "MBus/SBus async error\n"); | ||
345 | if (si & SUN4M_INT_ECC_ERR) | ||
346 | printk(KERN_ERR "ECC memory error\n"); | ||
347 | if (si & SUN4M_INT_VME_ERR) | ||
348 | printk(KERN_ERR "VME async error\n"); | ||
349 | printk(KERN_ERR "you lose buddy boy...\n"); | ||
350 | show_regs(regs); | ||
351 | prom_halt(); | ||
352 | } | ||
353 | |||
354 | /* Exported for sun4m_smp.c */ | ||
355 | void sun4m_clear_profile_irq(int cpu) | ||
356 | { | ||
357 | sbus_readl(&timers_percpu[cpu]->l14_limit); | ||
299 | } | 358 | } |
300 | 359 | ||
301 | static void sun4m_load_profile_irq(int cpu, unsigned int limit) | 360 | static void sun4m_load_profile_irq(int cpu, unsigned int limit) |
302 | { | 361 | { |
303 | sun4m_timers->cpu_timers[cpu].l14_timer_limit = limit; | 362 | sbus_writel(limit, &timers_percpu[cpu]->l14_limit); |
304 | } | 363 | } |
305 | 364 | ||
306 | static void __init sun4m_init_timers(irq_handler_t counter_fn) | 365 | static void __init sun4m_init_timers(irq_handler_t counter_fn) |
307 | { | 366 | { |
308 | int reg_count, irq, cpu; | 367 | struct device_node *dp = of_find_node_by_name(NULL, "counter"); |
309 | struct linux_prom_registers cnt_regs[PROMREG_MAX]; | 368 | int i, err, len, num_cpu_timers; |
310 | int obio_node, cnt_node; | 369 | const u32 *addr; |
311 | struct resource r; | 370 | |
312 | 371 | if (!dp) { | |
313 | cnt_node = 0; | 372 | printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n"); |
314 | if((obio_node = | 373 | return; |
315 | prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 || | ||
316 | (obio_node = prom_getchild (obio_node)) == 0 || | ||
317 | (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) { | ||
318 | prom_printf("Cannot find /obio/counter node\n"); | ||
319 | prom_halt(); | ||
320 | } | 374 | } |
321 | reg_count = prom_getproperty(cnt_node, "reg", | 375 | |
322 | (void *) cnt_regs, sizeof(cnt_regs)); | 376 | addr = of_get_property(dp, "address", &len); |
323 | reg_count = (reg_count/sizeof(struct linux_prom_registers)); | 377 | if (!addr) { |
324 | 378 | printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n"); | |
325 | /* Apply the obio ranges to the timer registers. */ | 379 | return; |
326 | prom_apply_obio_ranges(cnt_regs, reg_count); | ||
327 | |||
328 | cnt_regs[4].phys_addr = cnt_regs[reg_count-1].phys_addr; | ||
329 | cnt_regs[4].reg_size = cnt_regs[reg_count-1].reg_size; | ||
330 | cnt_regs[4].which_io = cnt_regs[reg_count-1].which_io; | ||
331 | for(obio_node = 1; obio_node < 4; obio_node++) { | ||
332 | cnt_regs[obio_node].phys_addr = | ||
333 | cnt_regs[obio_node-1].phys_addr + PAGE_SIZE; | ||
334 | cnt_regs[obio_node].reg_size = cnt_regs[obio_node-1].reg_size; | ||
335 | cnt_regs[obio_node].which_io = cnt_regs[obio_node-1].which_io; | ||
336 | } | 380 | } |
337 | 381 | ||
338 | memset((char*)&r, 0, sizeof(struct resource)); | 382 | num_cpu_timers = (len / sizeof(u32)) - 1; |
339 | /* Map the per-cpu Counter registers. */ | 383 | for (i = 0; i < num_cpu_timers; i++) { |
340 | r.flags = cnt_regs[0].which_io; | 384 | timers_percpu[i] = (void __iomem *) |
341 | r.start = cnt_regs[0].phys_addr; | 385 | (unsigned long) addr[i]; |
342 | sun4m_timers = (struct sun4m_timer_regs *) sbus_ioremap(&r, 0, | ||
343 | PAGE_SIZE*SUN4M_NCPUS, "sun4m_cpu_cnt"); | ||
344 | /* Map the system Counter register. */ | ||
345 | /* XXX Here we expect consequent calls to yeld adjusent maps. */ | ||
346 | r.flags = cnt_regs[4].which_io; | ||
347 | r.start = cnt_regs[4].phys_addr; | ||
348 | sbus_ioremap(&r, 0, cnt_regs[4].reg_size, "sun4m_sys_cnt"); | ||
349 | |||
350 | sun4m_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10); | ||
351 | master_l10_counter = &sun4m_timers->l10_cur_count; | ||
352 | master_l10_limit = &sun4m_timers->l10_timer_limit; | ||
353 | |||
354 | irq = request_irq(TIMER_IRQ, | ||
355 | counter_fn, | ||
356 | (IRQF_DISABLED | SA_STATIC_ALLOC), | ||
357 | "timer", NULL); | ||
358 | if (irq) { | ||
359 | prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); | ||
360 | prom_halt(); | ||
361 | } | 386 | } |
362 | 387 | timers_global = (void __iomem *) | |
363 | if (!cpu_find_by_instance(1, NULL, NULL)) { | 388 | (unsigned long) addr[num_cpu_timers]; |
364 | for(cpu = 0; cpu < 4; cpu++) | 389 | |
365 | sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0; | 390 | sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); |
366 | sun4m_interrupts->set = SUN4M_INT_E14; | 391 | |
367 | } else { | 392 | master_l10_counter = &timers_global->l10_count; |
368 | sun4m_timers->cpu_timers[0].l14_timer_limit = 0; | 393 | |
394 | err = request_irq(TIMER_IRQ, counter_fn, | ||
395 | (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); | ||
396 | if (err) { | ||
397 | printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", | ||
398 | err); | ||
399 | return; | ||
369 | } | 400 | } |
401 | |||
402 | for (i = 0; i < num_cpu_timers; i++) | ||
403 | sbus_writel(0, &timers_percpu[i]->l14_limit); | ||
404 | if (num_cpu_timers == 4) | ||
405 | sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set); | ||
406 | |||
370 | #ifdef CONFIG_SMP | 407 | #ifdef CONFIG_SMP |
371 | { | 408 | { |
372 | unsigned long flags; | 409 | unsigned long flags; |
@@ -390,70 +427,43 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) | |||
390 | 427 | ||
391 | void __init sun4m_init_IRQ(void) | 428 | void __init sun4m_init_IRQ(void) |
392 | { | 429 | { |
393 | int ie_node,i; | 430 | struct device_node *dp = of_find_node_by_name(NULL, "interrupt"); |
394 | struct linux_prom_registers int_regs[PROMREG_MAX]; | 431 | int len, i, mid, num_cpu_iregs; |
395 | int num_regs; | 432 | const u32 *addr; |
396 | struct resource r; | 433 | |
397 | int mid; | 434 | if (!dp) { |
398 | 435 | printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n"); | |
399 | local_irq_disable(); | 436 | return; |
400 | if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 || | ||
401 | (ie_node = prom_getchild (ie_node)) == 0 || | ||
402 | (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) { | ||
403 | prom_printf("Cannot find /obio/interrupt node\n"); | ||
404 | prom_halt(); | ||
405 | } | 437 | } |
406 | num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs, | 438 | |
407 | sizeof(int_regs)); | 439 | addr = of_get_property(dp, "address", &len); |
408 | num_regs = (num_regs/sizeof(struct linux_prom_registers)); | 440 | if (!addr) { |
409 | 441 | printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n"); | |
410 | /* Apply the obio ranges to these registers. */ | 442 | return; |
411 | prom_apply_obio_ranges(int_regs, num_regs); | ||
412 | |||
413 | int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr; | ||
414 | int_regs[4].reg_size = int_regs[num_regs-1].reg_size; | ||
415 | int_regs[4].which_io = int_regs[num_regs-1].which_io; | ||
416 | for(ie_node = 1; ie_node < 4; ie_node++) { | ||
417 | int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE; | ||
418 | int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size; | ||
419 | int_regs[ie_node].which_io = int_regs[ie_node-1].which_io; | ||
420 | } | 443 | } |
421 | 444 | ||
422 | memset((char *)&r, 0, sizeof(struct resource)); | 445 | num_cpu_iregs = (len / sizeof(u32)) - 1; |
423 | /* Map the interrupt registers for all possible cpus. */ | 446 | for (i = 0; i < num_cpu_iregs; i++) { |
424 | r.flags = int_regs[0].which_io; | 447 | sun4m_irq_percpu[i] = (void __iomem *) |
425 | r.start = int_regs[0].phys_addr; | 448 | (unsigned long) addr[i]; |
426 | sun4m_interrupts = (struct sun4m_intregs *) sbus_ioremap(&r, 0, | 449 | } |
427 | PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu"); | 450 | sun4m_irq_global = (void __iomem *) |
451 | (unsigned long) addr[num_cpu_iregs]; | ||
428 | 452 | ||
429 | /* Map the system interrupt control registers. */ | 453 | local_irq_disable(); |
430 | r.flags = int_regs[4].which_io; | ||
431 | r.start = int_regs[4].phys_addr; | ||
432 | sbus_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system"); | ||
433 | 454 | ||
434 | sun4m_interrupts->set = ~SUN4M_INT_MASKALL; | 455 | sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set); |
435 | for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) | 456 | for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) |
436 | sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff; | 457 | sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear); |
437 | 458 | ||
438 | if (!cpu_find_by_instance(1, NULL, NULL)) { | 459 | if (num_cpu_iregs == 4) |
439 | /* system wide interrupts go to cpu 0, this should always | 460 | sbus_writel(0, &sun4m_irq_global->interrupt_target); |
440 | * be safe because it is guaranteed to be fitted or OBP doesn't | 461 | |
441 | * come up | ||
442 | * | ||
443 | * Not sure, but writing here on SLAVIO systems may puke | ||
444 | * so I don't do it unless there is more than 1 cpu. | ||
445 | */ | ||
446 | irq_rcvreg = (unsigned long *) | ||
447 | &sun4m_interrupts->undirected_target; | ||
448 | sun4m_interrupts->undirected_target = 0; | ||
449 | } | ||
450 | BTFIXUPSET_CALL(sbint_to_irq, sun4m_sbint_to_irq, BTFIXUPCALL_NORM); | ||
451 | BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); | 462 | BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); |
452 | BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); | 463 | BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); |
453 | BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM); | 464 | BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM); |
454 | BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM); | 465 | BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM); |
455 | BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); | 466 | BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); |
456 | BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM); | ||
457 | BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); | 467 | BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); |
458 | sparc_init_timers = sun4m_init_timers; | 468 | sparc_init_timers = sun4m_init_timers; |
459 | #ifdef CONFIG_SMP | 469 | #ifdef CONFIG_SMP |
@@ -461,5 +471,6 @@ void __init sun4m_init_IRQ(void) | |||
461 | BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); | 471 | BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); |
462 | BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); | 472 | BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); |
463 | #endif | 473 | #endif |
474 | |||
464 | /* Cannot enable interrupts until OBP ticker is disabled. */ | 475 | /* Cannot enable interrupts until OBP ticker is disabled. */ |
465 | } | 476 | } |