summaryrefslogtreecommitdiffstats
path: root/target/linux/atheros/files/arch/mips/atheros/ar5312/irq.c
blob: c5062bac48eb0e9daeceddc85739d6f9616d9e4a (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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2003 Atheros Communications, Inc.,  All Rights Reserved.
 * Copyright (C) 2006 FON Technology, SL.
 * Copyright (C) 2006 Imre Kaloz <kaloz@openwrt.org>
 * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
 */

/*
 * Platform devices for Atheros SoCs
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "../ar531x.h"

/*
 * Called when an interrupt is received, this function
 * determines exactly which interrupt it was, and it
 * invokes the appropriate handler.
 *
 * Implicitly, we also define interrupt priority by
 * choosing which to dispatch first.
 */
asmlinkage void ar5312_irq_dispatch(void)
{
	int pending = read_c0_status() & read_c0_cause();

	if (pending & CAUSEF_IP2)
		do_IRQ(AR5312_IRQ_WLAN0_INTRS);
	else if (pending & CAUSEF_IP3)
		do_IRQ(AR5312_IRQ_ENET0_INTRS);
	else if (pending & CAUSEF_IP4)
		do_IRQ(AR5312_IRQ_ENET1_INTRS);
	else if (pending & CAUSEF_IP5)
		do_IRQ(AR5312_IRQ_WLAN1_INTRS);
	else if (pending & CAUSEF_IP6) {
		unsigned int ar531x_misc_intrs = sysRegRead(AR531X_ISR) & sysRegRead(AR531X_IMR);

		if (ar531x_misc_intrs & AR531X_ISR_TIMER) {
			do_IRQ(AR531X_MISC_IRQ_TIMER);
			(void)sysRegRead(AR531X_TIMER);
		} else if (ar531x_misc_intrs & AR531X_ISR_AHBPROC)
			do_IRQ(AR531X_MISC_IRQ_AHB_PROC);
		else if ((ar531x_misc_intrs & AR531X_ISR_UART0))
			do_IRQ(AR531X_MISC_IRQ_UART0);
		else if (ar531x_misc_intrs & AR531X_ISR_WD)
			do_IRQ(AR531X_MISC_IRQ_WATCHDOG);
		else
			do_IRQ(AR531X_MISC_IRQ_NONE);
	} else if (pending & CAUSEF_IP7) {
		do_IRQ(AR531X_IRQ_CPU_CLOCK);
	}
	else
		do_IRQ(AR531X_IRQ_NONE);
}


/* Enable the specified AR531X_MISC_IRQ interrupt */
static void
ar5312_misc_intr_enable(unsigned int irq)
{
	unsigned int imr;

	imr = sysRegRead(AR531X_IMR);
	imr |= (1 << (irq - AR531X_MISC_IRQ_BASE - 1));
	sysRegWrite(AR531X_IMR, imr);
	sysRegRead(AR531X_IMR); /* flush write buffer */
}

/* Disable the specified AR531X_MISC_IRQ interrupt */
static void
ar5312_misc_intr_disable(unsigned int irq)
{
	unsigned int imr;

	imr = sysRegRead(AR531X_IMR);
	imr &= ~(1 << (irq - AR531X_MISC_IRQ_BASE - 1));
	sysRegWrite(AR531X_IMR, imr);
	sysRegRead(AR531X_IMR); /* flush write buffer */
}

/* Turn on the specified AR531X_MISC_IRQ interrupt */
static unsigned int
ar5312_misc_intr_startup(unsigned int irq)
{
	ar5312_misc_intr_enable(irq);
	return 0;
}

/* Turn off the specified AR531X_MISC_IRQ interrupt */
static void
ar5312_misc_intr_shutdown(unsigned int irq)
{
	ar5312_misc_intr_disable(irq);
}

static void
ar5312_misc_intr_ack(unsigned int irq)
{
	ar5312_misc_intr_disable(irq);
}

static void
ar5312_misc_intr_end(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
		ar5312_misc_intr_enable(irq);
}

static struct irq_chip ar5312_misc_intr_controller = {
	.typename	= "AR5312 misc",
	.startup	= ar5312_misc_intr_startup,
	.shutdown	= ar5312_misc_intr_shutdown,
	.enable		= ar5312_misc_intr_enable,
	.disable	= ar5312_misc_intr_disable,
	.ack		= ar5312_misc_intr_ack,
	.end		= ar5312_misc_intr_end,
};

static irqreturn_t ar5312_ahb_proc_handler(int cpl, void *dev_id)
{
	u32 proc1 = sysRegRead(AR531X_PROC1);
	u32 procAddr = sysRegRead(AR531X_PROCADDR); /* clears error state */
	u32 dma1 = sysRegRead(AR531X_DMA1);
	u32 dmaAddr = sysRegRead(AR531X_DMAADDR);   /* clears error state */

	printk("AHB interrupt: PROCADDR=0x%8.8x  PROC1=0x%8.8x  DMAADDR=0x%8.8x  DMA1=0x%8.8x\n",
			procAddr, proc1, dmaAddr, dma1);
		
	machine_restart("AHB error"); /* Catastrophic failure */
	return IRQ_HANDLED;
}


static struct irqaction ar5312_ahb_proc_interrupt  = {
	.handler	= ar5312_ahb_proc_handler,
	.flags		= IRQF_DISABLED,
	.name		= "ar5312_ahb_proc_interrupt",
};


static struct irqaction cascade  = {
	.handler	= no_action,
	.flags		= IRQF_DISABLED,
	.name		= "cascade",
};

void __init ar5312_misc_intr_init(int irq_base)
{
	int i;

	for (i = irq_base; i < irq_base + AR531X_MISC_IRQ_COUNT; i++) {
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = NULL;
		irq_desc[i].depth = 1;
		irq_desc[i].chip = &ar5312_misc_intr_controller;
	}
	setup_irq(AR531X_MISC_IRQ_AHB_PROC, &ar5312_ahb_proc_interrupt);
	setup_irq(AR5312_IRQ_MISC_INTRS, &cascade);
}