diff options
author | David S. Miller <davem@davemloft.net> | 2008-09-20 00:17:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-09-20 00:17:43 -0400 |
commit | 69c010b24560be5ca7667e94a352183e60ed205e (patch) | |
tree | 3a5d3cfa1d80323119baed6b3b022ef14b823b47 /arch/sparc | |
parent | 2e57572a50a4de41c6cbc879a4866a312d4cd316 (diff) |
sparc32: Use PROM device probing for sun4m irq registers.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/kernel/entry.S | 40 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4m_irq.c | 163 |
2 files changed, 74 insertions, 129 deletions
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 68689facaaae..faf9ccd9ef5d 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S | |||
@@ -272,17 +272,18 @@ smp4m_ticker: | |||
272 | */ | 272 | */ |
273 | maybe_smp4m_msg: | 273 | maybe_smp4m_msg: |
274 | GET_PROCESSOR4M_ID(o3) | 274 | GET_PROCESSOR4M_ID(o3) |
275 | set sun4m_interrupts, %l5 | 275 | sethi %hi(sun4m_irq_percpu), %l5 |
276 | ld [%l5], %o5 | 276 | sll %o3, 2, %o3 |
277 | or %l5, %lo(sun4m_irq_percpu), %o5 | ||
277 | sethi %hi(0x40000000), %o2 | 278 | sethi %hi(0x40000000), %o2 |
278 | sll %o3, 12, %o3 | ||
279 | ld [%o5 + %o3], %o1 | 279 | ld [%o5 + %o3], %o1 |
280 | andcc %o1, %o2, %g0 | 280 | ld [%o1 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending |
281 | andcc %o3, %o2, %g0 | ||
281 | be,a smp4m_ticker | 282 | be,a smp4m_ticker |
282 | cmp %l7, 14 | 283 | cmp %l7, 14 |
283 | st %o2, [%o5 + 0x4] | 284 | st %o2, [%o1 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x40000000 |
284 | WRITE_PAUSE | 285 | WRITE_PAUSE |
285 | ld [%o5], %g0 | 286 | ld [%o1 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending |
286 | WRITE_PAUSE | 287 | WRITE_PAUSE |
287 | or %l0, PSR_PIL, %l4 | 288 | or %l0, PSR_PIL, %l4 |
288 | wr %l4, 0x0, %psr | 289 | wr %l4, 0x0, %psr |
@@ -300,16 +301,16 @@ linux_trap_ipi15_sun4m: | |||
300 | SAVE_ALL | 301 | SAVE_ALL |
301 | sethi %hi(0x80000000), %o2 | 302 | sethi %hi(0x80000000), %o2 |
302 | GET_PROCESSOR4M_ID(o0) | 303 | GET_PROCESSOR4M_ID(o0) |
303 | set sun4m_interrupts, %l5 | 304 | sethi %hi(sun4m_irq_percpu), %l5 |
304 | ld [%l5], %o5 | 305 | or %l5, %lo(sun4m_irq_percpu), %o5 |
305 | sll %o0, 12, %o0 | 306 | sll %o0, 2, %o0 |
306 | add %o5, %o0, %o5 | 307 | ld [%o5 + %o0], %o5 |
307 | ld [%o5], %o3 | 308 | ld [%o5 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending |
308 | andcc %o3, %o2, %g0 | 309 | andcc %o3, %o2, %g0 |
309 | be 1f ! Must be an NMI async memory error | 310 | be 1f ! Must be an NMI async memory error |
310 | st %o2, [%o5 + 4] | 311 | st %o2, [%o5 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x80000000 |
311 | WRITE_PAUSE | 312 | WRITE_PAUSE |
312 | ld [%o5], %g0 | 313 | ld [%o5 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending |
313 | WRITE_PAUSE | 314 | WRITE_PAUSE |
314 | or %l0, PSR_PIL, %l4 | 315 | or %l0, PSR_PIL, %l4 |
315 | wr %l4, 0x0, %psr | 316 | wr %l4, 0x0, %psr |
@@ -323,12 +324,11 @@ linux_trap_ipi15_sun4m: | |||
323 | 1: | 324 | 1: |
324 | /* NMI async memory error handling. */ | 325 | /* NMI async memory error handling. */ |
325 | sethi %hi(0x80000000), %l4 | 326 | sethi %hi(0x80000000), %l4 |
326 | sethi %hi(0x4000), %o3 | 327 | sethi %hi(sun4m_irq_global), %o5 |
327 | sub %o5, %o0, %o5 | 328 | ld [%o5 + %lo(sun4m_irq_global)], %l5 |
328 | add %o5, %o3, %l5 | 329 | st %l4, [%l5 + 0x0c] ! sun4m_irq_global->mask_set=0x80000000 |
329 | st %l4, [%l5 + 0xc] | ||
330 | WRITE_PAUSE | 330 | WRITE_PAUSE |
331 | ld [%l5], %g0 | 331 | ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending |
332 | WRITE_PAUSE | 332 | WRITE_PAUSE |
333 | or %l0, PSR_PIL, %l4 | 333 | or %l0, PSR_PIL, %l4 |
334 | wr %l4, 0x0, %psr | 334 | wr %l4, 0x0, %psr |
@@ -337,9 +337,9 @@ linux_trap_ipi15_sun4m: | |||
337 | WRITE_PAUSE | 337 | WRITE_PAUSE |
338 | call sun4m_nmi | 338 | call sun4m_nmi |
339 | nop | 339 | nop |
340 | st %l4, [%l5 + 0x8] | 340 | st %l4, [%l5 + 0x08] ! sun4m_irq_global->mask_clear=0x80000000 |
341 | WRITE_PAUSE | 341 | WRITE_PAUSE |
342 | ld [%l5], %g0 | 342 | ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending |
343 | WRITE_PAUSE | 343 | WRITE_PAUSE |
344 | RESTORE_ALL | 344 | RESTORE_ALL |
345 | 345 | ||
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 5b17146f0c1f..39d40e96d396 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c | |||
@@ -41,53 +41,25 @@ | |||
41 | 41 | ||
42 | #include "irq.h" | 42 | #include "irq.h" |
43 | 43 | ||
44 | /* On the sun4m, just like the timers, we have both per-cpu and master | 44 | struct sun4m_irq_percpu { |
45 | * interrupt registers. | 45 | u32 pending; |
46 | */ | 46 | u32 clear; |
47 | 47 | u32 set; | |
48 | /* These registers are used for sending/receiving irqs from/to | ||
49 | * different cpu's. | ||
50 | */ | ||
51 | struct sun4m_intreg_percpu { | ||
52 | unsigned int tbt; /* Interrupts still pending for this cpu. */ | ||
53 | |||
54 | /* These next two registers are WRITE-ONLY and are only | ||
55 | * "on bit" sensitive, "off bits" written have NO affect. | ||
56 | */ | ||
57 | unsigned int clear; /* Clear this cpus irqs here. */ | ||
58 | unsigned int set; /* Set this cpus irqs here. */ | ||
59 | unsigned char space[PAGE_SIZE - 12]; | ||
60 | }; | 48 | }; |
61 | 49 | ||
62 | /* | 50 | struct sun4m_irq_global { |
63 | * djhr | 51 | u32 pending; |
64 | * Actually the clear and set fields in this struct are misleading.. | 52 | u32 mask; |
65 | * according to the SLAVIO manual (and the same applies for the SEC) | 53 | u32 mask_clear; |
66 | * the clear field clears bits in the mask which will ENABLE that IRQ | 54 | u32 mask_set; |
67 | * the set field sets bits in the mask to DISABLE the IRQ. | 55 | u32 interrupt_target; |
68 | * | ||
69 | * Also the undirected_xx address in the SLAVIO is defined as | ||
70 | * RESERVED and write only.. | ||
71 | * | ||
72 | * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor | ||
73 | * sun4m machines, for MP the layout makes more sense. | ||
74 | */ | ||
75 | struct sun4m_intregs { | ||
76 | struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS]; | ||
77 | unsigned int tbt; /* IRQ's that are still pending. */ | ||
78 | unsigned int irqs; /* Master IRQ bits. */ | ||
79 | |||
80 | /* Again, like the above, two these registers are WRITE-ONLY. */ | ||
81 | unsigned int clear; /* Clear master IRQ's by setting bits here. */ | ||
82 | unsigned int set; /* Set master IRQ's by setting bits here. */ | ||
83 | |||
84 | /* This register is both READ and WRITE. */ | ||
85 | unsigned int undirected_target; /* Which cpu gets undirected irqs. */ | ||
86 | }; | 56 | }; |
87 | 57 | ||
88 | static unsigned long dummy; | 58 | /* Code in entry.S needs to get at these register mappings. */ |
59 | struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; | ||
60 | struct sun4m_irq_global __iomem *sun4m_irq_global; | ||
89 | 61 | ||
90 | struct sun4m_intregs *sun4m_interrupts; | 62 | static unsigned long dummy; |
91 | unsigned long *irq_rcvreg = &dummy; | 63 | unsigned long *irq_rcvreg = &dummy; |
92 | 64 | ||
93 | /* Dave Redman (djhr@tadpole.co.uk) | 65 | /* Dave Redman (djhr@tadpole.co.uk) |
@@ -182,9 +154,9 @@ static void sun4m_disable_irq(unsigned int irq_nr) | |||
182 | mask = sun4m_get_irqmask(irq_nr); | 154 | mask = sun4m_get_irqmask(irq_nr); |
183 | local_irq_save(flags); | 155 | local_irq_save(flags); |
184 | if (irq_nr > 15) | 156 | if (irq_nr > 15) |
185 | sun4m_interrupts->set = mask; | 157 | sbus_writel(mask, &sun4m_irq_global->mask_set); |
186 | else | 158 | else |
187 | sun4m_interrupts->cpu_intregs[cpu].set = mask; | 159 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); |
188 | local_irq_restore(flags); | 160 | local_irq_restore(flags); |
189 | } | 161 | } |
190 | 162 | ||
@@ -201,13 +173,13 @@ static void sun4m_enable_irq(unsigned int irq_nr) | |||
201 | mask = sun4m_get_irqmask(irq_nr); | 173 | mask = sun4m_get_irqmask(irq_nr); |
202 | local_irq_save(flags); | 174 | local_irq_save(flags); |
203 | if (irq_nr > 15) | 175 | if (irq_nr > 15) |
204 | sun4m_interrupts->clear = mask; | 176 | sbus_writel(mask, &sun4m_irq_global->mask_clear); |
205 | else | 177 | else |
206 | sun4m_interrupts->cpu_intregs[cpu].clear = mask; | 178 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); |
207 | local_irq_restore(flags); | 179 | local_irq_restore(flags); |
208 | } else { | 180 | } else { |
209 | local_irq_save(flags); | 181 | local_irq_save(flags); |
210 | sun4m_interrupts->clear = SUN4M_INT_FLOPPY; | 182 | sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear); |
211 | local_irq_restore(flags); | 183 | local_irq_restore(flags); |
212 | } | 184 | } |
213 | } | 185 | } |
@@ -236,34 +208,30 @@ static unsigned long cpu_pil_to_imask[16] = { | |||
236 | */ | 208 | */ |
237 | static void sun4m_disable_pil_irq(unsigned int pil) | 209 | static void sun4m_disable_pil_irq(unsigned int pil) |
238 | { | 210 | { |
239 | sun4m_interrupts->set = cpu_pil_to_imask[pil]; | 211 | sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set); |
240 | } | 212 | } |
241 | 213 | ||
242 | static void sun4m_enable_pil_irq(unsigned int pil) | 214 | static void sun4m_enable_pil_irq(unsigned int pil) |
243 | { | 215 | { |
244 | sun4m_interrupts->clear = cpu_pil_to_imask[pil]; | 216 | sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear); |
245 | } | 217 | } |
246 | 218 | ||
247 | #ifdef CONFIG_SMP | 219 | #ifdef CONFIG_SMP |
248 | static void sun4m_send_ipi(int cpu, int level) | 220 | static void sun4m_send_ipi(int cpu, int level) |
249 | { | 221 | { |
250 | unsigned long mask; | 222 | unsigned long mask = sun4m_get_irqmask(level); |
251 | 223 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); | |
252 | mask = sun4m_get_irqmask(level); | ||
253 | sun4m_interrupts->cpu_intregs[cpu].set = mask; | ||
254 | } | 224 | } |
255 | 225 | ||
256 | static void sun4m_clear_ipi(int cpu, int level) | 226 | static void sun4m_clear_ipi(int cpu, int level) |
257 | { | 227 | { |
258 | unsigned long mask; | 228 | unsigned long mask = sun4m_get_irqmask(level); |
259 | 229 | sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); | |
260 | mask = sun4m_get_irqmask(level); | ||
261 | sun4m_interrupts->cpu_intregs[cpu].clear = mask; | ||
262 | } | 230 | } |
263 | 231 | ||
264 | static void sun4m_set_udt(int cpu) | 232 | static void sun4m_set_udt(int cpu) |
265 | { | 233 | { |
266 | sun4m_interrupts->undirected_target = cpu; | 234 | sbus_writel(cpu, &sun4m_irq_global->interrupt_target); |
267 | } | 235 | } |
268 | #endif | 236 | #endif |
269 | 237 | ||
@@ -347,7 +315,7 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) | |||
347 | for (i = 0; i < num_cpu_timers; i++) | 315 | for (i = 0; i < num_cpu_timers; i++) |
348 | sbus_writel(0, &timers_percpu[i]->l14_limit); | 316 | sbus_writel(0, &timers_percpu[i]->l14_limit); |
349 | if (num_cpu_timers == 4) | 317 | if (num_cpu_timers == 4) |
350 | sbus_writel(SUN4M_INT_E14, &sun4m_interrupts->set); | 318 | sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set); |
351 | 319 | ||
352 | #ifdef CONFIG_SMP | 320 | #ifdef CONFIG_SMP |
353 | { | 321 | { |
@@ -372,62 +340,38 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) | |||
372 | 340 | ||
373 | void __init sun4m_init_IRQ(void) | 341 | void __init sun4m_init_IRQ(void) |
374 | { | 342 | { |
375 | int ie_node,i; | 343 | struct device_node *dp = of_find_node_by_name(NULL, "interrupt"); |
376 | struct linux_prom_registers int_regs[PROMREG_MAX]; | 344 | int len, i, mid, num_cpu_iregs; |
377 | int num_regs; | 345 | const u32 *addr; |
378 | struct resource r; | 346 | |
379 | int mid; | 347 | if (!dp) { |
380 | 348 | printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n"); | |
381 | local_irq_disable(); | 349 | return; |
382 | if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 || | ||
383 | (ie_node = prom_getchild (ie_node)) == 0 || | ||
384 | (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) { | ||
385 | prom_printf("Cannot find /obio/interrupt node\n"); | ||
386 | prom_halt(); | ||
387 | } | 350 | } |
388 | num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs, | 351 | |
389 | sizeof(int_regs)); | 352 | addr = of_get_property(dp, "address", &len); |
390 | num_regs = (num_regs/sizeof(struct linux_prom_registers)); | 353 | if (!addr) { |
391 | 354 | printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n"); | |
392 | /* Apply the obio ranges to these registers. */ | 355 | return; |
393 | prom_apply_obio_ranges(int_regs, num_regs); | ||
394 | |||
395 | int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr; | ||
396 | int_regs[4].reg_size = int_regs[num_regs-1].reg_size; | ||
397 | int_regs[4].which_io = int_regs[num_regs-1].which_io; | ||
398 | for(ie_node = 1; ie_node < 4; ie_node++) { | ||
399 | int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE; | ||
400 | int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size; | ||
401 | int_regs[ie_node].which_io = int_regs[ie_node-1].which_io; | ||
402 | } | 356 | } |
403 | 357 | ||
404 | memset((char *)&r, 0, sizeof(struct resource)); | 358 | num_cpu_iregs = (len / sizeof(u32)) - 1; |
405 | /* Map the interrupt registers for all possible cpus. */ | 359 | for (i = 0; i < num_cpu_iregs; i++) { |
406 | r.flags = int_regs[0].which_io; | 360 | sun4m_irq_percpu[i] = (void __iomem *) |
407 | r.start = int_regs[0].phys_addr; | 361 | (unsigned long) addr[i]; |
408 | sun4m_interrupts = (struct sun4m_intregs *) of_ioremap(&r, 0, | 362 | } |
409 | PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu"); | 363 | sun4m_irq_global = (void __iomem *) |
364 | (unsigned long) addr[num_cpu_iregs]; | ||
410 | 365 | ||
411 | /* Map the system interrupt control registers. */ | 366 | local_irq_disable(); |
412 | r.flags = int_regs[4].which_io; | ||
413 | r.start = int_regs[4].phys_addr; | ||
414 | of_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system"); | ||
415 | 367 | ||
416 | sun4m_interrupts->set = ~SUN4M_INT_MASKALL; | 368 | sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set); |
417 | for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) | 369 | for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) |
418 | sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff; | 370 | sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear); |
419 | 371 | ||
420 | if (!cpu_find_by_instance(1, NULL, NULL)) { | 372 | if (num_cpu_iregs == 4) { |
421 | /* system wide interrupts go to cpu 0, this should always | 373 | irq_rcvreg = (unsigned long *) &sun4m_irq_global->interrupt_target; |
422 | * be safe because it is guaranteed to be fitted or OBP doesn't | 374 | sbus_writel(0, &sun4m_irq_global->interrupt_target); |
423 | * come up | ||
424 | * | ||
425 | * Not sure, but writing here on SLAVIO systems may puke | ||
426 | * so I don't do it unless there is more than 1 cpu. | ||
427 | */ | ||
428 | irq_rcvreg = (unsigned long *) | ||
429 | &sun4m_interrupts->undirected_target; | ||
430 | sun4m_interrupts->undirected_target = 0; | ||
431 | } | 375 | } |
432 | BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); | 376 | BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); |
433 | BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); | 377 | BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); |
@@ -442,5 +386,6 @@ void __init sun4m_init_IRQ(void) | |||
442 | BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); | 386 | BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); |
443 | BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); | 387 | BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); |
444 | #endif | 388 | #endif |
389 | |||
445 | /* Cannot enable interrupts until OBP ticker is disabled. */ | 390 | /* Cannot enable interrupts until OBP ticker is disabled. */ |
446 | } | 391 | } |