• 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/arch/mips/brcm-boards/bcm947xx/
1/*
2 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * Broadcom HND BCM47xx chips interrupt dispatcher.
17 * Derived from ../generic/irq.c
18 *
19 *	MIPS IRQ	Source
20 *      --------        ------------------
21 *             0	Software
22 *             1        Software
23 *             2        Hardware (shared)
24 *             3        Hardware
25 *             4        Hardware
26 *             5        Hardware
27 *             6        Hardware
28 *             7        Hardware (r4k timer)
29 *
30 *      MIPS IRQ        Linux IRQ
31 *      --------        -----------
32 *         0 - 1        0 - 1
33 *             2        8 and above
34 *         3 - 7        3 - 7
35 *
36 * MIPS has 8 IRQs as indicated and assigned above. SB cores
37 * that use dedicated MIPS IRQ3 to IRQ6 are 1-to-1 mapped to
38 * linux IRQ3 to IRQ6. SB cores sharing MIPS IRQ2 are mapped
39 * to linux IRQ8 and above as virtual IRQs using the following
40 * mapping:
41 *
42 *   <linux-IRQ#> = <SB-core-flag> + <base-IRQ> + 2
43 *
44 * where <base-IRQ> is specified in setup.c when calling
45 * sb_mips_init(), 2 is to offset the two software IRQs.
46 *
47 * $Id: irq.c,v 1.11 2010-01-07 06:40:35 $
48 */
49
50#include <linux/version.h>
51
52#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
53#include <linux/config.h>
54#endif
55
56#ifdef CONFIG_SMP
57/*
58 * This code is designed to work on Uniprocessor only.
59 *
60 * To support SMP we must know:
61 *   - interrupt architecture
62 *   - interrupt distribution machenism
63 */
64#error "This implementation does not support SMP"
65#endif
66
67#include <linux/init.h>
68#include <linux/irq.h>
69#include <linux/kernel_stat.h>
70
71#include <asm/mipsregs.h>
72
73#include <typedefs.h>
74#include <osl.h>
75#include <bcmutils.h>
76#include <hndsoc.h>
77#include <siutils.h>
78#include <hndcpu.h>
79#include <hndsoc.h>
80#include <mips74k_core.h>
81#include "bcm947xx.h"
82
83/* cp0 SR register IM field */
84#define SR_IM(irq)	(1 << ((irq) + STATUSB_IP0))
85
86/* cp0 CR register IP field */
87#define CR_IP(irq)	(1 << ((irq) + CAUSEB_IP0))
88
89/* other local constants */
90#define NUM_IRQS	32
91
92/* local variables and functions */
93static sbconfig_t *ccsbr = NULL;	/* Chipc core SB config regs */
94static sbconfig_t *mipssbr = NULL;	/* MIPS core SB config regs */
95static int mipsirq = -1;		/* MIPS core virtual IRQ number */
96static uint32 shints = 0;		/* Set of shared interrupts */
97static int irq2en = 0;			/* MIPS IRQ2 enable count */
98static uint *mips_corereg = NULL;
99
100/* global variables and functions */
101extern si_t *bcm947xx_sih;		/* defined in setup.c */
102
103extern asmlinkage void brcmIRQ(void);
104
105/* Control functions for MIPS IRQ0 to IRQ7 */
106static INLINE void
107enable_brcm_irq(unsigned int irq)
108{
109	set_c0_status(SR_IM(irq));
110	irq_enable_hazard();
111}
112
113static INLINE void
114disable_brcm_irq(unsigned int irq)
115{
116	clear_c0_status(SR_IM(irq));
117	irq_disable_hazard();
118}
119
120static void
121ack_brcm_irq(unsigned int irq)
122{
123	/* Done in plat_irq_dispatch()! */
124}
125
126static void
127end_brcm_irq(unsigned int irq)
128{
129	/* Done in plat_irq_dispatch()! */
130}
131
132/* Control functions for linux IRQ8 and above */
133static INLINE void
134enable_brcm_irq2(unsigned int irq)
135{
136	ASSERT(irq2en >= 0);
137	if (irq2en++)
138		return;
139	enable_brcm_irq(2);
140}
141
142static INLINE void
143disable_brcm_irq2(unsigned int irq)
144{
145	ASSERT(irq2en > 0);
146	if (--irq2en)
147		return;
148	disable_brcm_irq(2);
149}
150
151static void
152ack_brcm_irq2(unsigned int irq)
153{
154	/* Already done in plat_irq_dispatch()! */
155}
156
157static void
158end_brcm_irq2(unsigned int irq)
159{
160	/* Already done in plat_irq_dispatch()! */
161}
162
163/*
164 * Route interrupts to ISR(s).
165 *
166 * This function is entered with the IE disabled. It can be
167 * re-entered as soon as the IE is re-enabled in function
168 * handle_IRQ_envet().
169 */
170void BCMFASTPATH
171plat_irq_dispatch(struct pt_regs *regs)
172{
173	u32 pending, ipvec;
174	unsigned long flags = 0;
175	int irq;
176
177	/* Disable MIPS IRQs with pending interrupts */
178	pending = read_c0_cause() & CAUSEF_IP;
179	pending &= read_c0_status();
180	clear_c0_status(pending);
181	irq_disable_hazard();
182
183	/* Handle MIPS timer interrupt. Re-enable MIPS IRQ7
184	 * immediately after servicing the interrupt so that
185	 * we can take this kind of interrupt again later
186	 * while servicing other interrupts.
187	 */
188	if (pending & CAUSEF_IP7) {
189		do_IRQ(7);
190		pending &= ~CAUSEF_IP7;
191		set_c0_status(STATUSF_IP7);
192		irq_enable_hazard();
193	}
194
195	/* Build bitvec for pending interrupts. Start with
196	 * MIPS IRQ2 and add linux IRQs to higher bits to
197	 * make the interrupt processing uniform.
198	 */
199	ipvec = pending >> CAUSEB_IP2;
200	if (pending & CAUSEF_IP2) {
201		if (ccsbr)
202			flags = R_REG(NULL, &ccsbr->sbflagst);
203
204		/* Read intstatus */
205		if (mips_corereg)
206			flags = R_REG(NULL, &((mips74kregs_t *)mips_corereg)->intstatus);
207
208		flags &= shints;
209		ipvec |= flags << SBMIPS_VIRTIRQ_BASE;
210	}
211
212#ifdef CONFIG_HND_BMIPS3300_PROF
213	/* Handle MIPS core interrupt. Re-enable the MIPS IRQ that
214	 * MIPS core is assigned to immediately after servicing the
215	 * interrupt so that we can take this kind of interrupt again
216	 * later while servicing other interrupts.
217	 *
218	 * mipsirq < 0 indicates MIPS core IRQ # is unknown.
219	 */
220	if (mipsirq >= 0 && (ipvec & (1 << mipsirq))) {
221		/* MIPS core raised the interrupt on the shared MIPS IRQ2.
222		 * Make sure MIPS core is the only interrupt source before
223		 * re-enabling the IRQ.
224		 */
225		if (mipsirq >= SBMIPS_VIRTIRQ_BASE) {
226			if (flags == (1 << (mipsirq-SBMIPS_VIRTIRQ_BASE))) {
227				irq = mipsirq + 2;
228				do_IRQ(irq);
229				ipvec &= ~(1 << mipsirq);
230				pending &= ~CAUSEF_IP2;
231				set_c0_status(STATUSF_IP2);
232				irq_enable_hazard();
233			}
234		}
235		/* MIPS core raised the interrupt on a dedicated MIPS IRQ.
236		 * Re-enable the IRQ immediately.
237		 */
238		else {
239			irq = mipsirq + 2;
240			do_IRQ(irq);
241			ipvec &= ~(1 << mipsirq);
242			pending &= ~CR_IP(irq);
243			set_c0_status(SR_IM(irq));
244			irq_enable_hazard();
245		}
246	}
247#endif	/* CONFIG_HND_BMIPS3300_PROF */
248
249        /* Shared interrupt bits are shifted to respective bit positions in
250	 * ipvec above. IP2 (bit 0) is of no significance, hence shifting the
251	 * bit map by 1 to the right.
252	 */
253	ipvec >>= 1;
254
255	/* Handle all other interrupts. Re-enable disabled MIPS IRQs
256	 * after processing all pending interrupts.
257	 */
258	for (irq = 3; ipvec != 0; irq++) {
259		if (ipvec & 1)
260			do_IRQ(irq);
261		ipvec >>= 1;
262	}
263	set_c0_status(pending);
264	irq_enable_hazard();
265
266	/* Process any pending softirqs (tasklets, softirqs ...) */
267	local_irq_save(flags);
268	if (local_softirq_pending() && !in_interrupt())
269		__do_softirq();
270	local_irq_restore(flags);
271}
272
273/* MIPS IRQ0 to IRQ7 interrupt controller */
274static struct irq_chip brcm_irq_type = {
275	.name = "MIPS",
276	.ack = ack_brcm_irq,
277	.mask = disable_brcm_irq,
278	.mask_ack = disable_brcm_irq,
279	.unmask = enable_brcm_irq,
280	.end = end_brcm_irq
281};
282
283/* linux IRQ8 and above interrupt controller */
284static struct irq_chip brcm_irq2_type = {
285	.name = "IRQ2",
286	.ack = ack_brcm_irq2,
287	.mask = disable_brcm_irq2,
288	.mask_ack = disable_brcm_irq2,
289	.unmask = enable_brcm_irq2,
290	.end = end_brcm_irq2
291};
292
293/*
294 * We utilize chipcommon configuration register SBFlagSt to implement a
295 * smart shared IRQ handling machenism through which only ISRs registered
296 * for the SB cores that raised the interrupt are invoked. This machenism
297 * relies on the SBFlagSt register's reliable recording of the SB cores
298 * that raised the interrupt.
299 */
300void __init
301arch_init_irq(void)
302{
303	int i;
304	uint32 coreidx, mips_core_id;
305	void *regs;
306
307	if (BCM330X(current_cpu_data.processor_id))
308		mips_core_id = MIPS33_CORE_ID;
309	else if (MIPS74K(current_cpu_data.processor_id))
310		mips_core_id = MIPS74K_CORE_ID;
311	else {
312		printk(KERN_ERR "MIPS CPU type %x unknown", current_cpu_data.processor_id);
313		return;
314	}
315
316	/* Cache chipc and mips33 config registers */
317	ASSERT(bcm947xx_sih);
318	coreidx = si_coreidx(bcm947xx_sih);
319	regs = si_setcore(bcm947xx_sih, mips_core_id, 0);
320	mipsirq = si_irq(bcm947xx_sih);
321	if (bcm947xx_sih->socitype == SOCI_SB) {
322		if (regs)
323			mipssbr = (sbconfig_t *)((ulong)regs + SBCONFIGOFF);
324
325		if ((regs = si_setcore(bcm947xx_sih, CC_CORE_ID, 0)))
326			ccsbr = (sbconfig_t *)((ulong)regs + SBCONFIGOFF);
327	}
328	si_setcoreidx(bcm947xx_sih, coreidx);
329
330	if (BCM330X(current_cpu_data.processor_id)) {
331		/* Cache mips33 sbintvec register */
332		if (mipssbr)
333			shints = R_REG(NULL, &mipssbr->sbintvec);
334	} else {
335		uint32 *intmask;
336
337		/* Use intmask5 register to route the timer interrupt */
338		intmask = (uint32 *) &((mips74kregs_t *)regs)->intmask[5];
339		W_REG(NULL, intmask, 1 << 31);
340
341		intmask = (uint32 *) &((mips74kregs_t *)regs)->intmask[0];
342		shints = R_REG(NULL, intmask);
343
344		/* Save the pointer to mips core registers */
345		mips_corereg = regs;
346	}
347
348	/* Install interrupt controllers */
349	for (i = 0; i < NR_IRQS; i++) {
350		set_irq_chip(i, (i < SBMIPS_NUMIRQS ? &brcm_irq_type : &brcm_irq2_type));
351	}
352}
353