summaryrefslogtreecommitdiffstats
path: root/target/linux/adm5120-2.6/files/arch/mips/adm5120/irq.c
blob: 46f3bb05ee4a344bd5a2071e5eb41f05debb8c47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 *	Copyright (C) ADMtek Incorporated.
 *		Creator : daniell@admtek.com.tw
 *	Carsten Langgaard, carstenl@mips.com
 *	Copyright (C) 2000, 2001 MIPS Technologies, Inc.
 *	Copyright (C) 2001 Ralf Baechle
 *	Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org)
 */

#include <linux/autoconf.h>
#include <linux/init.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/pm.h>

#include <asm/irq.h>
#include <asm/time.h>
#include <asm/mipsregs.h>
#include <asm/gdb-stub.h>
#include <asm/irq_cpu.h>

#define MIPS_CPU_TIMER_IRQ 7

extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
extern irq_desc_t irq_desc[];
extern asmlinkage void mipsIRQ(void);

int mips_int_lock(void);
void mips_int_unlock(int);

unsigned int mips_counter_frequency;

#define ADM5120_INTC_REG(reg)	(*(volatile u32 *)(KSEG1ADDR(0x12200000+(reg))))
#define ADM5120_INTC_STATUS	ADM5120_INTC_REG(0x00)
#define ADM5120_INTC_ENABLE	ADM5120_INTC_REG(0x08)
#define ADM5120_INTC_DISABLE	ADM5120_INTC_REG(0x0c)
#define ADM5120_IRQ_MAX		9
#define ADM5120_IRQ_MASK	0x3ff

void adm5120_hw0_irqdispatch(struct pt_regs *regs)
{
	unsigned long intsrc;
	int i;

	intsrc = ADM5120_INTC_STATUS & ADM5120_IRQ_MASK;

	for (i = 0; intsrc; intsrc >>= 1, i++)
		if (intsrc & 0x1)
			do_IRQ(i);
		else
			spurious_interrupt();
}

void mips_timer_interrupt(struct pt_regs *regs)
{
        write_c0_compare(read_c0_count()+ mips_counter_frequency/HZ);
        ll_timer_interrupt(MIPS_CPU_TIMER_IRQ);
}

/* Main interrupt dispatcher */
asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
{
        unsigned int cp0_cause = read_c0_cause() & read_c0_status();

        if (cp0_cause & CAUSEF_IP7) {
                mips_timer_interrupt( regs);
        } else if (cp0_cause & CAUSEF_IP2) {
                adm5120_hw0_irqdispatch( regs);
        }
}

void enable_adm5120_irq(unsigned int irq)
{
	int s;

	/* Disable all interrupts (FIQ/IRQ) */
	s = mips_int_lock();

	if ((irq < 0) || (irq > ADM5120_IRQ_MAX)) 
		goto err_exit;

	ADM5120_INTC_ENABLE = (1<<irq);

err_exit:

	/* Restore the interrupts states */
	mips_int_unlock(s);
}


void disable_adm5120_irq(unsigned int irq)
{
	int s;

	/* Disable all interrupts (FIQ/IRQ) */
	s = mips_int_lock();

	if ((irq < 0) || (irq > ADM5120_IRQ_MAX)) 
		goto err_exit;

	ADM5120_INTC_DISABLE = (1<<irq);

err_exit:
	/* Restore the interrupts states */
	mips_int_unlock(s);
}

unsigned int startup_adm5120_irq(unsigned int irq)
{
	enable_adm5120_irq(irq);
	return 0;
}

void shutdown_adm5120_irq(unsigned int irq)
{
	disable_adm5120_irq(irq);
}

static inline void ack_adm5120_irq(unsigned int irq_nr)
{
	ADM5120_INTC_DISABLE = (1 << irq_nr);
}


static void end_adm5120_irq(unsigned int irq_nr)
{
	ADM5120_INTC_ENABLE = (1 << irq_nr);
}

static hw_irq_controller adm5120_irq_type = {
	.typename 	= "MIPS",
	.startup	= startup_adm5120_irq,
	.shutdown	= shutdown_adm5120_irq,
	.enable		= enable_adm5120_irq,
	.disable	= disable_adm5120_irq,
	.ack 		= ack_adm5120_irq,
	.end		= end_adm5120_irq,
	.set_affinity	= NULL,
};


void __init arch_init_irq(void)
{
	int i;
	
	for (i = 0; i <= ADM5120_IRQ_MAX; i++) {
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = 0;
		irq_desc[i].depth = 1;
		irq_desc[i].chip = &adm5120_irq_type;
	}
}