• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/mips/bcm63xx/
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8 */
9
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/module.h>
14#include <asm/irq_cpu.h>
15#include <asm/mipsregs.h>
16#include <bcm63xx_cpu.h>
17#include <bcm63xx_regs.h>
18#include <bcm63xx_io.h>
19#include <bcm63xx_irq.h>
20
21/*
22 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
23 * prioritize any interrupt relatively to another. the static counter
24 * will resume the loop where it ended the last time we left this
25 * function.
26 */
27static void bcm63xx_irq_dispatch_internal(void)
28{
29	u32 pending;
30	static int i;
31
32	pending = bcm_perf_readl(PERF_IRQMASK_REG) &
33		bcm_perf_readl(PERF_IRQSTAT_REG);
34
35	if (!pending)
36		return ;
37
38	while (1) {
39		int to_call = i;
40
41		i = (i + 1) & 0x1f;
42		if (pending & (1 << to_call)) {
43			do_IRQ(to_call + IRQ_INTERNAL_BASE);
44			break;
45		}
46	}
47}
48
49asmlinkage void plat_irq_dispatch(void)
50{
51	u32 cause;
52
53	do {
54		cause = read_c0_cause() & read_c0_status() & ST0_IM;
55
56		if (!cause)
57			break;
58
59		if (cause & CAUSEF_IP7)
60			do_IRQ(7);
61		if (cause & CAUSEF_IP2)
62			bcm63xx_irq_dispatch_internal();
63		if (cause & CAUSEF_IP3)
64			do_IRQ(IRQ_EXT_0);
65		if (cause & CAUSEF_IP4)
66			do_IRQ(IRQ_EXT_1);
67		if (cause & CAUSEF_IP5)
68			do_IRQ(IRQ_EXT_2);
69		if (cause & CAUSEF_IP6)
70			do_IRQ(IRQ_EXT_3);
71	} while (1);
72}
73
74/*
75 * internal IRQs operations: only mask/unmask on PERF irq mask
76 * register.
77 */
78static inline void bcm63xx_internal_irq_mask(unsigned int irq)
79{
80	u32 mask;
81
82	irq -= IRQ_INTERNAL_BASE;
83	mask = bcm_perf_readl(PERF_IRQMASK_REG);
84	mask &= ~(1 << irq);
85	bcm_perf_writel(mask, PERF_IRQMASK_REG);
86}
87
88static void bcm63xx_internal_irq_unmask(unsigned int irq)
89{
90	u32 mask;
91
92	irq -= IRQ_INTERNAL_BASE;
93	mask = bcm_perf_readl(PERF_IRQMASK_REG);
94	mask |= (1 << irq);
95	bcm_perf_writel(mask, PERF_IRQMASK_REG);
96}
97
98static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
99{
100	bcm63xx_internal_irq_unmask(irq);
101	return 0;
102}
103
104/*
105 * external IRQs operations: mask/unmask and clear on PERF external
106 * irq control register.
107 */
108static void bcm63xx_external_irq_mask(unsigned int irq)
109{
110	u32 reg;
111
112	irq -= IRQ_EXT_BASE;
113	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
114	reg &= ~EXTIRQ_CFG_MASK(irq);
115	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
116}
117
118static void bcm63xx_external_irq_unmask(unsigned int irq)
119{
120	u32 reg;
121
122	irq -= IRQ_EXT_BASE;
123	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
124	reg |= EXTIRQ_CFG_MASK(irq);
125	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
126}
127
128static void bcm63xx_external_irq_clear(unsigned int irq)
129{
130	u32 reg;
131
132	irq -= IRQ_EXT_BASE;
133	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
134	reg |= EXTIRQ_CFG_CLEAR(irq);
135	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
136}
137
138static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
139{
140	set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
141	irq_enable_hazard();
142	bcm63xx_external_irq_unmask(irq);
143	return 0;
144}
145
146static void bcm63xx_external_irq_shutdown(unsigned int irq)
147{
148	bcm63xx_external_irq_mask(irq);
149	clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
150	irq_disable_hazard();
151}
152
153static int bcm63xx_external_irq_set_type(unsigned int irq,
154					 unsigned int flow_type)
155{
156	u32 reg;
157	struct irq_desc *desc = irq_desc + irq;
158
159	irq -= IRQ_EXT_BASE;
160
161	flow_type &= IRQ_TYPE_SENSE_MASK;
162
163	if (flow_type == IRQ_TYPE_NONE)
164		flow_type = IRQ_TYPE_LEVEL_LOW;
165
166	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
167	switch (flow_type) {
168	case IRQ_TYPE_EDGE_BOTH:
169		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
170		reg |= EXTIRQ_CFG_BOTHEDGE(irq);
171		break;
172
173	case IRQ_TYPE_EDGE_RISING:
174		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
175		reg |= EXTIRQ_CFG_SENSE(irq);
176		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
177		break;
178
179	case IRQ_TYPE_EDGE_FALLING:
180		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
181		reg &= ~EXTIRQ_CFG_SENSE(irq);
182		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
183		break;
184
185	case IRQ_TYPE_LEVEL_HIGH:
186		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
187		reg |= EXTIRQ_CFG_SENSE(irq);
188		break;
189
190	case IRQ_TYPE_LEVEL_LOW:
191		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
192		reg &= ~EXTIRQ_CFG_SENSE(irq);
193		break;
194
195	default:
196		printk(KERN_ERR "bogus flow type combination given !\n");
197		return -EINVAL;
198	}
199	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
200
201	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))  {
202		desc->status |= IRQ_LEVEL;
203		desc->handle_irq = handle_level_irq;
204	} else {
205		desc->handle_irq = handle_edge_irq;
206	}
207
208	return 0;
209}
210
211static struct irq_chip bcm63xx_internal_irq_chip = {
212	.name		= "bcm63xx_ipic",
213	.startup	= bcm63xx_internal_irq_startup,
214	.shutdown	= bcm63xx_internal_irq_mask,
215
216	.mask		= bcm63xx_internal_irq_mask,
217	.mask_ack	= bcm63xx_internal_irq_mask,
218	.unmask		= bcm63xx_internal_irq_unmask,
219};
220
221static struct irq_chip bcm63xx_external_irq_chip = {
222	.name		= "bcm63xx_epic",
223	.startup	= bcm63xx_external_irq_startup,
224	.shutdown	= bcm63xx_external_irq_shutdown,
225
226	.ack		= bcm63xx_external_irq_clear,
227
228	.mask		= bcm63xx_external_irq_mask,
229	.unmask		= bcm63xx_external_irq_unmask,
230
231	.set_type	= bcm63xx_external_irq_set_type,
232};
233
234static struct irqaction cpu_ip2_cascade_action = {
235	.handler	= no_action,
236	.name		= "cascade_ip2",
237};
238
239void __init arch_init_irq(void)
240{
241	int i;
242
243	mips_cpu_irq_init();
244	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
245		set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
246					 handle_level_irq);
247
248	for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
249		set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
250					 handle_edge_irq);
251
252	setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
253}
254