| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * arch/ppc/syslib/ppc4xx_pic.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Interrupt controller driver for PowerPC 4xx-based processors. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | 
					
						
							|  |  |  |  * Copyright (c) 2004, 2005 Zultys Technologies | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on original code by | 
					
						
							|  |  |  |  *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> | 
					
						
							|  |  |  |  *    Armin Custer <akuster@mvista.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute  it and/or modify it | 
					
						
							|  |  |  |  * under  the terms of  the GNU General  Public License as published by the | 
					
						
							|  |  |  |  * Free Software Foundation;  either version 2 of the  License, or (at your | 
					
						
							|  |  |  |  * option) any later version. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | #include <linux/config.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/signal.h>
 | 
					
						
							|  |  |  | #include <linux/stddef.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/processor.h>
 | 
					
						
							|  |  |  | #include <asm/system.h>
 | 
					
						
							|  |  |  | #include <asm/irq.h>
 | 
					
						
							|  |  |  | #include <asm/ppc4xx_pic.h>
 | 
					
						
							| 
									
										
										
										
											2005-10-11 22:08:12 +10:00
										 |  |  | #include <asm/machdep.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* See comment in include/arch-ppc/ppc4xx_pic.h
 | 
					
						
							|  |  |  |  * for more info about these two variables | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS] | 
					
						
							|  |  |  |     __attribute__ ((weak)); | 
					
						
							|  |  |  | extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define IRQ_MASK_UIC0(irq)		(1 << (31 - (irq)))
 | 
					
						
							|  |  |  | #define IRQ_MASK_UICx(irq)		(1 << (31 - ((irq) & 0x1f)))
 | 
					
						
							|  |  |  | #define IRQ_MASK_UIC1(irq)		IRQ_MASK_UICx(irq)
 | 
					
						
							|  |  |  | #define IRQ_MASK_UIC2(irq)		IRQ_MASK_UICx(irq)
 | 
					
						
							| 
									
										
										
										
											2005-11-07 00:58:13 -08:00
										 |  |  | #define IRQ_MASK_UIC3(irq)		IRQ_MASK_UICx(irq)
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define UIC_HANDLERS(n)							\
 | 
					
						
							|  |  |  | static void ppc4xx_uic##n##_enable(unsigned int irq)			\ | 
					
						
							|  |  |  | {									\ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:24:15 -07:00
										 |  |  | 	u32 mask = IRQ_MASK_UIC##n(irq);				\ | 
					
						
							|  |  |  | 	if (irq_desc[irq].status & IRQ_LEVEL)				\ | 
					
						
							|  |  |  | 		mtdcr(DCRN_UIC_SR(UIC##n), mask);			\ | 
					
						
							|  |  |  | 	ppc_cached_irq_mask[n] |= mask;					\ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);		\ | 
					
						
							|  |  |  | }									\ | 
					
						
							|  |  |  | 									\ | 
					
						
							|  |  |  | static void ppc4xx_uic##n##_disable(unsigned int irq)			\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq);		\ | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);		\ | 
					
						
							|  |  |  | 	ACK_UIC##n##_PARENT						\ | 
					
						
							|  |  |  | }									\ | 
					
						
							|  |  |  | 									\ | 
					
						
							|  |  |  | static void ppc4xx_uic##n##_ack(unsigned int irq)			\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	u32 mask = IRQ_MASK_UIC##n(irq);				\ | 
					
						
							|  |  |  | 	ppc_cached_irq_mask[n] &= ~mask;				\ | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);		\ | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_SR(UIC##n), mask);				\ | 
					
						
							|  |  |  | 	ACK_UIC##n##_PARENT						\ | 
					
						
							|  |  |  | }									\ | 
					
						
							|  |  |  | 									\ | 
					
						
							|  |  |  | static void ppc4xx_uic##n##_end(unsigned int irq)			\ | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	unsigned int status = irq_desc[irq].status;			\ | 
					
						
							|  |  |  | 	u32 mask = IRQ_MASK_UIC##n(irq);				\ | 
					
						
							|  |  |  | 	if (status & IRQ_LEVEL) {					\ | 
					
						
							|  |  |  | 		mtdcr(DCRN_UIC_SR(UIC##n), mask);			\ | 
					
						
							|  |  |  | 		ACK_UIC##n##_PARENT					\ | 
					
						
							|  |  |  | 	}								\ | 
					
						
							|  |  |  | 	if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {		\ | 
					
						
							|  |  |  | 		ppc_cached_irq_mask[n] |= mask;				\ | 
					
						
							|  |  |  | 		mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);	\ | 
					
						
							|  |  |  | 	}								\ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DECLARE_UIC(n)							\
 | 
					
						
							|  |  |  | {									\ | 
					
						
							|  |  |  | 	.typename 	= "UIC"#n,					\ | 
					
						
							|  |  |  | 	.enable 	= ppc4xx_uic##n##_enable,			\ | 
					
						
							|  |  |  | 	.disable 	= ppc4xx_uic##n##_disable,			\ | 
					
						
							|  |  |  | 	.ack 		= ppc4xx_uic##n##_ack,				\ | 
					
						
							|  |  |  | 	.end 		= ppc4xx_uic##n##_end,				\ | 
					
						
							|  |  |  | }									\ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-11-07 00:58:13 -08:00
										 |  |  | #if NR_UICS == 4
 | 
					
						
							|  |  |  | #define ACK_UIC0_PARENT
 | 
					
						
							|  |  |  | #define ACK_UIC1_PARENT	mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
 | 
					
						
							|  |  |  | #define ACK_UIC2_PARENT	mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC2NC);
 | 
					
						
							|  |  |  | #define ACK_UIC3_PARENT	mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC3NC);
 | 
					
						
							|  |  |  | UIC_HANDLERS(0); | 
					
						
							|  |  |  | UIC_HANDLERS(1); | 
					
						
							|  |  |  | UIC_HANDLERS(2); | 
					
						
							|  |  |  | UIC_HANDLERS(3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); | 
					
						
							|  |  |  | 	if (uic0 & UIC0_UIC1NC) | 
					
						
							|  |  |  | 		return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); | 
					
						
							|  |  |  | 	else if (uic0 & UIC0_UIC2NC) | 
					
						
							|  |  |  | 		return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2))); | 
					
						
							|  |  |  | 	else if (uic0 & UIC0_UIC3NC) | 
					
						
							|  |  |  | 		return 128 - ffs(mfdcr(DCRN_UIC_MSR(UIC3))); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return uic0 ? 32 - ffs(uic0) : -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init ppc4xx_pic_impl_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Enable cascade interrupts in UIC0 */ | 
					
						
							|  |  |  | 	ppc_cached_irq_mask[0] |= UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC; | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC); | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #elif NR_UICS == 3
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #define ACK_UIC0_PARENT	mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
 | 
					
						
							|  |  |  | #define ACK_UIC1_PARENT	mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
 | 
					
						
							|  |  |  | #define ACK_UIC2_PARENT	mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
 | 
					
						
							|  |  |  | UIC_HANDLERS(0); | 
					
						
							|  |  |  | UIC_HANDLERS(1); | 
					
						
							|  |  |  | UIC_HANDLERS(2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 uicb = mfdcr(DCRN_UIC_MSR(UICB)); | 
					
						
							|  |  |  | 	if (uicb & UICB_UIC0NC) | 
					
						
							|  |  |  | 		return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0))); | 
					
						
							|  |  |  | 	else if (uicb & UICB_UIC1NC) | 
					
						
							|  |  |  | 		return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); | 
					
						
							|  |  |  | 	else if (uicb & UICB_UIC2NC) | 
					
						
							|  |  |  | 		return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2))); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init ppc4xx_pic_impl_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-07-05 18:54:45 -07:00
										 |  |  | #if defined(CONFIG_440GX)
 | 
					
						
							|  |  |  | 	/* Disable 440GP compatibility mode if it was enabled in firmware */ | 
					
						
							|  |  |  | 	SDR_WRITE(DCRN_SDR_MFR, SDR_READ(DCRN_SDR_MFR) & ~DCRN_SDR_MFR_PCM); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	/* Configure Base UIC */ | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_CR(UICB), 0); | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_TR(UICB), 0); | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_PR(UICB), 0xffffffff); | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_SR(UICB), 0xffffffff); | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #elif NR_UICS == 2
 | 
					
						
							|  |  |  | #define ACK_UIC0_PARENT
 | 
					
						
							|  |  |  | #define ACK_UIC1_PARENT	mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
 | 
					
						
							|  |  |  | UIC_HANDLERS(0); | 
					
						
							|  |  |  | UIC_HANDLERS(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); | 
					
						
							|  |  |  | 	if (uic0 & UIC0_UIC1NC) | 
					
						
							|  |  |  | 		return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return uic0 ? 32 - ffs(uic0) : -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init ppc4xx_pic_impl_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Enable cascade interrupt in UIC0 */ | 
					
						
							|  |  |  | 	ppc_cached_irq_mask[0] |= UIC0_UIC1NC; | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC); | 
					
						
							|  |  |  | 	mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #elif NR_UICS == 1
 | 
					
						
							|  |  |  | #define ACK_UIC0_PARENT
 | 
					
						
							|  |  |  | UIC_HANDLERS(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); | 
					
						
							|  |  |  | 	return uic0 ? 32 - ffs(uic0) : -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void ppc4xx_pic_impl_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ppc4xx_uic_impl { | 
					
						
							|  |  |  | 	struct hw_interrupt_type decl; | 
					
						
							|  |  |  | 	int base;			/* Base DCR number */ | 
					
						
							|  |  |  | } __uic[] = { | 
					
						
							|  |  |  | 	{ .decl = DECLARE_UIC(0), .base = UIC0 }, | 
					
						
							|  |  |  | #if NR_UICS > 1
 | 
					
						
							|  |  |  | 	{ .decl = DECLARE_UIC(1), .base = UIC1 }, | 
					
						
							|  |  |  | #if NR_UICS > 2
 | 
					
						
							|  |  |  | 	{ .decl = DECLARE_UIC(2), .base = UIC2 }, | 
					
						
							| 
									
										
										
										
											2005-11-07 00:58:13 -08:00
										 |  |  | #if NR_UICS > 3
 | 
					
						
							|  |  |  | 	{ .decl = DECLARE_UIC(3), .base = UIC3 }, | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int is_level_sensitive(int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base)); | 
					
						
							|  |  |  | 	return (tr & IRQ_MASK_UICx(irq)) == 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init ppc4xx_pic_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < NR_UICS; ++i) { | 
					
						
							|  |  |  | 		int base = __uic[i].base; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Disable everything by default */ | 
					
						
							|  |  |  | 		ppc_cached_irq_mask[i] = 0; | 
					
						
							|  |  |  | 		mtdcr(DCRN_UIC_ER(base), 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* We don't use critical interrupts */ | 
					
						
							|  |  |  | 		mtdcr(DCRN_UIC_CR(base), 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Configure polarity and triggering */ | 
					
						
							|  |  |  | 		if (ppc4xx_core_uic_cfg) { | 
					
						
							|  |  |  | 			struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i; | 
					
						
							|  |  |  | 			u32 mask = p->ext_irq_mask; | 
					
						
							|  |  |  | 			u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask; | 
					
						
							|  |  |  | 			u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* "Fixed" interrupts (on-chip devices) */ | 
					
						
							|  |  |  | 			pr |= p->polarity & ~mask; | 
					
						
							|  |  |  | 			tr |= p->triggering & ~mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Merge external IRQs settings if board port
 | 
					
						
							|  |  |  | 			 * provided them | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (eirqs && mask) { | 
					
						
							|  |  |  | 				pr &= ~mask; | 
					
						
							|  |  |  | 				tr &= ~mask; | 
					
						
							|  |  |  | 				while (mask) { | 
					
						
							|  |  |  | 					/* Extract current external IRQ mask */ | 
					
						
							|  |  |  | 					u32 eirq_mask = 1 << __ilog2(mask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (!(*eirqs & IRQ_SENSE_LEVEL)) | 
					
						
							|  |  |  | 						tr |= eirq_mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (*eirqs & IRQ_POLARITY_POSITIVE) | 
					
						
							|  |  |  | 						pr |= eirq_mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					mask &= ~eirq_mask; | 
					
						
							|  |  |  | 					++eirqs; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mtdcr(DCRN_UIC_PR(base), pr); | 
					
						
							|  |  |  | 			mtdcr(DCRN_UIC_TR(base), tr); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* ACK any pending interrupts to prevent false
 | 
					
						
							|  |  |  | 		 * triggering after first enable | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		mtdcr(DCRN_UIC_SR(base), 0xffffffff); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Perform optional implementation specific setup
 | 
					
						
							|  |  |  | 	 * (e.g. enable cascade interrupts for multi-UIC configurations) | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ppc4xx_pic_impl_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Attach low-level handlers */ | 
					
						
							|  |  |  | 	for (i = 0; i < (NR_UICS << 5); ++i) { | 
					
						
							|  |  |  | 		irq_desc[i].handler = &__uic[i >> 5].decl; | 
					
						
							|  |  |  | 		if (is_level_sensitive(i)) | 
					
						
							|  |  |  | 			irq_desc[i].status |= IRQ_LEVEL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ppc_md.get_irq = ppc4xx_pic_get_irq; | 
					
						
							|  |  |  | } |