1/*
2 * Copyright 2001 MontaVista Software Inc.
3 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
4 *
5 * Copyright (C) 2001 Ralf Baechle
6 *
7 * This file define the irq handler for MIPS CPU interrupts.
8 *
9 * This program is free software; you can redistribute  it and/or modify it
10 * under  the terms of  the GNU General  Public License as published by the
11 * Free Software Foundation;  either version 2 of the  License, or (at your
12 * option) any later version.
13 */
14
15/*
16 * Almost all MIPS CPUs define 8 interrupt sources.  They are typically
17 * level triggered (i.e., cannot be cleared from CPU; must be cleared from
18 * device).  The first two are software interrupts which we don't really
19 * use or support.  The last one is usually cpu timer interrupt if a counter
20 * register is present.
21 *
22 * Don't even think about using this on SMP.  You have been warned.
23 *
24 * This file exports one global function:
25 *	mips_cpu_irq_init(u32 irq_base);
26 */
27#include <linux/interrupt.h>
28#include <linux/types.h>
29#include <linux/kernel.h>
30
31#include <asm/mipsregs.h>
32#include <asm/system.h>
33
34static int mips_cpu_irq_base;
35
36static inline void unmask_mips_irq(unsigned int irq)
37{
38	clear_c0_cause(0x100 << (irq - mips_cpu_irq_base));
39	set_c0_status(0x100 << (irq - mips_cpu_irq_base));
40}
41
42static inline void mask_mips_irq(unsigned int irq)
43{
44	clear_c0_status(0x100 << (irq - mips_cpu_irq_base));
45}
46
47static inline void mips_cpu_irq_enable(unsigned int irq)
48{
49	unsigned long flags;
50
51	local_irq_save(flags);
52	unmask_mips_irq(irq);
53	local_irq_restore(flags);
54}
55
56static void mips_cpu_irq_disable(unsigned int irq)
57{
58	unsigned long flags;
59
60	local_irq_save(flags);
61	mask_mips_irq(irq);
62	local_irq_restore(flags);
63}
64
65static unsigned int mips_cpu_irq_startup(unsigned int irq)
66{
67	mips_cpu_irq_enable(irq);
68
69	return 0;
70}
71
72#define	mips_cpu_irq_shutdown	mips_cpu_irq_disable
73
74/*
75 * While we ack the interrupt interrupts are disabled and thus we don't need
76 * to deal with concurrency issues.  Same for mips_cpu_irq_end.
77 */
78static void mips_cpu_irq_ack(unsigned int irq)
79{
80	/* Only necessary for soft interrupts */
81	clear_c0_cause(1 << (irq - mips_cpu_irq_base + 8));
82
83	mask_mips_irq(irq);
84}
85
86static void mips_cpu_irq_end(unsigned int irq)
87{
88	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
89		unmask_mips_irq(irq);
90}
91
92static hw_irq_controller mips_cpu_irq_controller = {
93	"CPU_irq",
94	mips_cpu_irq_startup,
95	mips_cpu_irq_shutdown,
96	mips_cpu_irq_enable,
97	mips_cpu_irq_disable,
98	mips_cpu_irq_ack,
99	mips_cpu_irq_end,
100	NULL			/* no affinity stuff for UP */
101};
102
103
104void mips_cpu_irq_init(u32 irq_base)
105{
106	u32 i;
107
108	for (i = irq_base; i < irq_base + 8; i++) {
109		irq_desc[i].status = IRQ_DISABLED;
110		irq_desc[i].action = NULL;
111		irq_desc[i].depth = 1;
112		irq_desc[i].handler = &mips_cpu_irq_controller;
113	}
114
115	mips_cpu_irq_base = irq_base;
116}
117