PageRenderTime 43ms CodeModel.GetById 15ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Ethereal-msm8939-beta9/arch/mips/bcm63xx/irq.c

https://bitbucket.org/MilosStamenkovic95/etherealos
C | 560 lines | 462 code | 76 blank | 22 comment | 43 complexity | 44daf8b98198b5bed017ae133050fd75 MD5 | raw file
  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 <linux/irq.h>
 15#include <asm/irq_cpu.h>
 16#include <asm/mipsregs.h>
 17#include <bcm63xx_cpu.h>
 18#include <bcm63xx_regs.h>
 19#include <bcm63xx_io.h>
 20#include <bcm63xx_irq.h>
 21
 22static void __dispatch_internal(void) __maybe_unused;
 23static void __dispatch_internal_64(void) __maybe_unused;
 24static void __internal_irq_mask_32(unsigned int irq) __maybe_unused;
 25static void __internal_irq_mask_64(unsigned int irq) __maybe_unused;
 26static void __internal_irq_unmask_32(unsigned int irq) __maybe_unused;
 27static void __internal_irq_unmask_64(unsigned int irq) __maybe_unused;
 28
 29#ifndef BCMCPU_RUNTIME_DETECT
 30#ifdef CONFIG_BCM63XX_CPU_6328
 31#define irq_stat_reg		PERF_IRQSTAT_6328_REG
 32#define irq_mask_reg		PERF_IRQMASK_6328_REG
 33#define irq_bits		64
 34#define is_ext_irq_cascaded	1
 35#define ext_irq_start		(BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE)
 36#define ext_irq_end		(BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE)
 37#define ext_irq_count		4
 38#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6328
 39#define ext_irq_cfg_reg2	0
 40#endif
 41#ifdef CONFIG_BCM63XX_CPU_6338
 42#define irq_stat_reg		PERF_IRQSTAT_6338_REG
 43#define irq_mask_reg		PERF_IRQMASK_6338_REG
 44#define irq_bits		32
 45#define is_ext_irq_cascaded	0
 46#define ext_irq_start		0
 47#define ext_irq_end		0
 48#define ext_irq_count		4
 49#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6338
 50#define ext_irq_cfg_reg2	0
 51#endif
 52#ifdef CONFIG_BCM63XX_CPU_6345
 53#define irq_stat_reg		PERF_IRQSTAT_6345_REG
 54#define irq_mask_reg		PERF_IRQMASK_6345_REG
 55#define irq_bits		32
 56#define is_ext_irq_cascaded	0
 57#define ext_irq_start		0
 58#define ext_irq_end		0
 59#define ext_irq_count		4
 60#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6345
 61#define ext_irq_cfg_reg2	0
 62#endif
 63#ifdef CONFIG_BCM63XX_CPU_6348
 64#define irq_stat_reg		PERF_IRQSTAT_6348_REG
 65#define irq_mask_reg		PERF_IRQMASK_6348_REG
 66#define irq_bits		32
 67#define is_ext_irq_cascaded	0
 68#define ext_irq_start		0
 69#define ext_irq_end		0
 70#define ext_irq_count		4
 71#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6348
 72#define ext_irq_cfg_reg2	0
 73#endif
 74#ifdef CONFIG_BCM63XX_CPU_6358
 75#define irq_stat_reg		PERF_IRQSTAT_6358_REG
 76#define irq_mask_reg		PERF_IRQMASK_6358_REG
 77#define irq_bits		32
 78#define is_ext_irq_cascaded	1
 79#define ext_irq_start		(BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE)
 80#define ext_irq_end		(BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE)
 81#define ext_irq_count		4
 82#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6358
 83#define ext_irq_cfg_reg2	0
 84#endif
 85#ifdef CONFIG_BCM63XX_CPU_6362
 86#define irq_stat_reg		PERF_IRQSTAT_6362_REG
 87#define irq_mask_reg		PERF_IRQMASK_6362_REG
 88#define irq_bits		64
 89#define is_ext_irq_cascaded	1
 90#define ext_irq_start		(BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE)
 91#define ext_irq_end		(BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE)
 92#define ext_irq_count		4
 93#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6362
 94#define ext_irq_cfg_reg2	0
 95#endif
 96#ifdef CONFIG_BCM63XX_CPU_6368
 97#define irq_stat_reg		PERF_IRQSTAT_6368_REG
 98#define irq_mask_reg		PERF_IRQMASK_6368_REG
 99#define irq_bits		64
100#define is_ext_irq_cascaded	1
101#define ext_irq_start		(BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE)
102#define ext_irq_end		(BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE)
103#define ext_irq_count		6
104#define ext_irq_cfg_reg1	PERF_EXTIRQ_CFG_REG_6368
105#define ext_irq_cfg_reg2	PERF_EXTIRQ_CFG_REG2_6368
106#endif
107
108#if irq_bits == 32
109#define dispatch_internal			__dispatch_internal
110#define internal_irq_mask			__internal_irq_mask_32
111#define internal_irq_unmask			__internal_irq_unmask_32
112#else
113#define dispatch_internal			__dispatch_internal_64
114#define internal_irq_mask			__internal_irq_mask_64
115#define internal_irq_unmask			__internal_irq_unmask_64
116#endif
117
118#define irq_stat_addr	(bcm63xx_regset_address(RSET_PERF) + irq_stat_reg)
119#define irq_mask_addr	(bcm63xx_regset_address(RSET_PERF) + irq_mask_reg)
120
121static inline void bcm63xx_init_irq(void)
122{
123}
124#else /* ! BCMCPU_RUNTIME_DETECT */
125
126static u32 irq_stat_addr, irq_mask_addr;
127static void (*dispatch_internal)(void);
128static int is_ext_irq_cascaded;
129static unsigned int ext_irq_count;
130static unsigned int ext_irq_start, ext_irq_end;
131static unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2;
132static void (*internal_irq_mask)(unsigned int irq);
133static void (*internal_irq_unmask)(unsigned int irq);
134
135static void bcm63xx_init_irq(void)
136{
137	int irq_bits;
138
139	irq_stat_addr = bcm63xx_regset_address(RSET_PERF);
140	irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
141
142	switch (bcm63xx_get_cpu_id()) {
143	case BCM6328_CPU_ID:
144		irq_stat_addr += PERF_IRQSTAT_6328_REG;
145		irq_mask_addr += PERF_IRQMASK_6328_REG;
146		irq_bits = 64;
147		ext_irq_count = 4;
148		is_ext_irq_cascaded = 1;
149		ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
150		ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
151		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
152		break;
153	case BCM6338_CPU_ID:
154		irq_stat_addr += PERF_IRQSTAT_6338_REG;
155		irq_mask_addr += PERF_IRQMASK_6338_REG;
156		irq_bits = 32;
157		ext_irq_count = 4;
158		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338;
159		break;
160	case BCM6345_CPU_ID:
161		irq_stat_addr += PERF_IRQSTAT_6345_REG;
162		irq_mask_addr += PERF_IRQMASK_6345_REG;
163		irq_bits = 32;
164		ext_irq_count = 4;
165		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345;
166		break;
167	case BCM6348_CPU_ID:
168		irq_stat_addr += PERF_IRQSTAT_6348_REG;
169		irq_mask_addr += PERF_IRQMASK_6348_REG;
170		irq_bits = 32;
171		ext_irq_count = 4;
172		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
173		break;
174	case BCM6358_CPU_ID:
175		irq_stat_addr += PERF_IRQSTAT_6358_REG;
176		irq_mask_addr += PERF_IRQMASK_6358_REG;
177		irq_bits = 32;
178		ext_irq_count = 4;
179		is_ext_irq_cascaded = 1;
180		ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
181		ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
182		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
183		break;
184	case BCM6362_CPU_ID:
185		irq_stat_addr += PERF_IRQSTAT_6362_REG;
186		irq_mask_addr += PERF_IRQMASK_6362_REG;
187		irq_bits = 64;
188		ext_irq_count = 4;
189		is_ext_irq_cascaded = 1;
190		ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE;
191		ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE;
192		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362;
193		break;
194	case BCM6368_CPU_ID:
195		irq_stat_addr += PERF_IRQSTAT_6368_REG;
196		irq_mask_addr += PERF_IRQMASK_6368_REG;
197		irq_bits = 64;
198		ext_irq_count = 6;
199		is_ext_irq_cascaded = 1;
200		ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
201		ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
202		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
203		ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
204		break;
205	default:
206		BUG();
207	}
208
209	if (irq_bits == 32) {
210		dispatch_internal = __dispatch_internal;
211		internal_irq_mask = __internal_irq_mask_32;
212		internal_irq_unmask = __internal_irq_unmask_32;
213	} else {
214		dispatch_internal = __dispatch_internal_64;
215		internal_irq_mask = __internal_irq_mask_64;
216		internal_irq_unmask = __internal_irq_unmask_64;
217	}
218}
219#endif /* ! BCMCPU_RUNTIME_DETECT */
220
221static inline u32 get_ext_irq_perf_reg(int irq)
222{
223	if (irq < 4)
224		return ext_irq_cfg_reg1;
225	return ext_irq_cfg_reg2;
226}
227
228static inline void handle_internal(int intbit)
229{
230	if (is_ext_irq_cascaded &&
231	    intbit >= ext_irq_start && intbit <= ext_irq_end)
232		do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
233	else
234		do_IRQ(intbit + IRQ_INTERNAL_BASE);
235}
236
237/*
238 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
239 * prioritize any interrupt relatively to another. the static counter
240 * will resume the loop where it ended the last time we left this
241 * function.
242 */
243static void __dispatch_internal(void)
244{
245	u32 pending;
246	static int i;
247
248	pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
249
250	if (!pending)
251		return ;
252
253	while (1) {
254		int to_call = i;
255
256		i = (i + 1) & 0x1f;
257		if (pending & (1 << to_call)) {
258			handle_internal(to_call);
259			break;
260		}
261	}
262}
263
264static void __dispatch_internal_64(void)
265{
266	u64 pending;
267	static int i;
268
269	pending = bcm_readq(irq_stat_addr) & bcm_readq(irq_mask_addr);
270
271	if (!pending)
272		return ;
273
274	while (1) {
275		int to_call = i;
276
277		i = (i + 1) & 0x3f;
278		if (pending & (1ull << to_call)) {
279			handle_internal(to_call);
280			break;
281		}
282	}
283}
284
285asmlinkage void plat_irq_dispatch(void)
286{
287	u32 cause;
288
289	do {
290		cause = read_c0_cause() & read_c0_status() & ST0_IM;
291
292		if (!cause)
293			break;
294
295		if (cause & CAUSEF_IP7)
296			do_IRQ(7);
297		if (cause & CAUSEF_IP2)
298			dispatch_internal();
299		if (!is_ext_irq_cascaded) {
300			if (cause & CAUSEF_IP3)
301				do_IRQ(IRQ_EXT_0);
302			if (cause & CAUSEF_IP4)
303				do_IRQ(IRQ_EXT_1);
304			if (cause & CAUSEF_IP5)
305				do_IRQ(IRQ_EXT_2);
306			if (cause & CAUSEF_IP6)
307				do_IRQ(IRQ_EXT_3);
308		}
309	} while (1);
310}
311
312/*
313 * internal IRQs operations: only mask/unmask on PERF irq mask
314 * register.
315 */
316static void __internal_irq_mask_32(unsigned int irq)
317{
318	u32 mask;
319
320	mask = bcm_readl(irq_mask_addr);
321	mask &= ~(1 << irq);
322	bcm_writel(mask, irq_mask_addr);
323}
324
325static void __internal_irq_mask_64(unsigned int irq)
326{
327	u64 mask;
328
329	mask = bcm_readq(irq_mask_addr);
330	mask &= ~(1ull << irq);
331	bcm_writeq(mask, irq_mask_addr);
332}
333
334static void __internal_irq_unmask_32(unsigned int irq)
335{
336	u32 mask;
337
338	mask = bcm_readl(irq_mask_addr);
339	mask |= (1 << irq);
340	bcm_writel(mask, irq_mask_addr);
341}
342
343static void __internal_irq_unmask_64(unsigned int irq)
344{
345	u64 mask;
346
347	mask = bcm_readq(irq_mask_addr);
348	mask |= (1ull << irq);
349	bcm_writeq(mask, irq_mask_addr);
350}
351
352static void bcm63xx_internal_irq_mask(struct irq_data *d)
353{
354	internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
355}
356
357static void bcm63xx_internal_irq_unmask(struct irq_data *d)
358{
359	internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
360}
361
362/*
363 * external IRQs operations: mask/unmask and clear on PERF external
364 * irq control register.
365 */
366static void bcm63xx_external_irq_mask(struct irq_data *d)
367{
368	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
369	u32 reg, regaddr;
370
371	regaddr = get_ext_irq_perf_reg(irq);
372	reg = bcm_perf_readl(regaddr);
373
374	if (BCMCPU_IS_6348())
375		reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
376	else
377		reg &= ~EXTIRQ_CFG_MASK(irq % 4);
378
379	bcm_perf_writel(reg, regaddr);
380	if (is_ext_irq_cascaded)
381		internal_irq_mask(irq + ext_irq_start);
382}
383
384static void bcm63xx_external_irq_unmask(struct irq_data *d)
385{
386	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
387	u32 reg, regaddr;
388
389	regaddr = get_ext_irq_perf_reg(irq);
390	reg = bcm_perf_readl(regaddr);
391
392	if (BCMCPU_IS_6348())
393		reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
394	else
395		reg |= EXTIRQ_CFG_MASK(irq % 4);
396
397	bcm_perf_writel(reg, regaddr);
398
399	if (is_ext_irq_cascaded)
400		internal_irq_unmask(irq + ext_irq_start);
401}
402
403static void bcm63xx_external_irq_clear(struct irq_data *d)
404{
405	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
406	u32 reg, regaddr;
407
408	regaddr = get_ext_irq_perf_reg(irq);
409	reg = bcm_perf_readl(regaddr);
410
411	if (BCMCPU_IS_6348())
412		reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
413	else
414		reg |= EXTIRQ_CFG_CLEAR(irq % 4);
415
416	bcm_perf_writel(reg, regaddr);
417}
418
419static int bcm63xx_external_irq_set_type(struct irq_data *d,
420					 unsigned int flow_type)
421{
422	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
423	u32 reg, regaddr;
424	int levelsense, sense, bothedge;
425
426	flow_type &= IRQ_TYPE_SENSE_MASK;
427
428	if (flow_type == IRQ_TYPE_NONE)
429		flow_type = IRQ_TYPE_LEVEL_LOW;
430
431	levelsense = sense = bothedge = 0;
432	switch (flow_type) {
433	case IRQ_TYPE_EDGE_BOTH:
434		bothedge = 1;
435		break;
436
437	case IRQ_TYPE_EDGE_RISING:
438		sense = 1;
439		break;
440
441	case IRQ_TYPE_EDGE_FALLING:
442		break;
443
444	case IRQ_TYPE_LEVEL_HIGH:
445		levelsense = 1;
446		sense = 1;
447		break;
448
449	case IRQ_TYPE_LEVEL_LOW:
450		levelsense = 1;
451		break;
452
453	default:
454		printk(KERN_ERR "bogus flow type combination given !\n");
455		return -EINVAL;
456	}
457
458	regaddr = get_ext_irq_perf_reg(irq);
459	reg = bcm_perf_readl(regaddr);
460	irq %= 4;
461
462	switch (bcm63xx_get_cpu_id()) {
463	case BCM6348_CPU_ID:
464		if (levelsense)
465			reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
466		else
467			reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
468		if (sense)
469			reg |= EXTIRQ_CFG_SENSE_6348(irq);
470		else
471			reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
472		if (bothedge)
473			reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
474		else
475			reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
476		break;
477
478	case BCM6328_CPU_ID:
479	case BCM6338_CPU_ID:
480	case BCM6345_CPU_ID:
481	case BCM6358_CPU_ID:
482	case BCM6362_CPU_ID:
483	case BCM6368_CPU_ID:
484		if (levelsense)
485			reg |= EXTIRQ_CFG_LEVELSENSE(irq);
486		else
487			reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
488		if (sense)
489			reg |= EXTIRQ_CFG_SENSE(irq);
490		else
491			reg &= ~EXTIRQ_CFG_SENSE(irq);
492		if (bothedge)
493			reg |= EXTIRQ_CFG_BOTHEDGE(irq);
494		else
495			reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
496		break;
497	default:
498		BUG();
499	}
500
501	bcm_perf_writel(reg, regaddr);
502
503	irqd_set_trigger_type(d, flow_type);
504	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
505		__irq_set_handler_locked(d->irq, handle_level_irq);
506	else
507		__irq_set_handler_locked(d->irq, handle_edge_irq);
508
509	return IRQ_SET_MASK_OK_NOCOPY;
510}
511
512static struct irq_chip bcm63xx_internal_irq_chip = {
513	.name		= "bcm63xx_ipic",
514	.irq_mask	= bcm63xx_internal_irq_mask,
515	.irq_unmask	= bcm63xx_internal_irq_unmask,
516};
517
518static struct irq_chip bcm63xx_external_irq_chip = {
519	.name		= "bcm63xx_epic",
520	.irq_ack	= bcm63xx_external_irq_clear,
521
522	.irq_mask	= bcm63xx_external_irq_mask,
523	.irq_unmask	= bcm63xx_external_irq_unmask,
524
525	.irq_set_type	= bcm63xx_external_irq_set_type,
526};
527
528static struct irqaction cpu_ip2_cascade_action = {
529	.handler	= no_action,
530	.name		= "cascade_ip2",
531	.flags		= IRQF_NO_THREAD,
532};
533
534static struct irqaction cpu_ext_cascade_action = {
535	.handler	= no_action,
536	.name		= "cascade_extirq",
537	.flags		= IRQF_NO_THREAD,
538};
539
540void __init arch_init_irq(void)
541{
542	int i;
543
544	bcm63xx_init_irq();
545	mips_cpu_irq_init();
546	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
547		irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
548					 handle_level_irq);
549
550	for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
551		irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
552					 handle_edge_irq);
553
554	if (!is_ext_irq_cascaded) {
555		for (i = 3; i < 3 + ext_irq_count; ++i)
556			setup_irq(MIPS_CPU_IRQ_BASE + i, &cpu_ext_cascade_action);
557	}
558
559	setup_irq(MIPS_CPU_IRQ_BASE + 2, &cpu_ip2_cascade_action);
560}