| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * arch/ppc/syslib/cpc700_pic.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Interrupt controller support for IBM Spruce | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: Mark Greer, Matt Porter, and Johnnie Peters | 
					
						
							|  |  |  |  *	    mgreer@mvista.com | 
					
						
							|  |  |  |  *          mporter@mvista.com | 
					
						
							|  |  |  |  *          jpeters@mvista.com | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 2001-2002 (c) MontaVista, Software, Inc.  This file is licensed under | 
					
						
							|  |  |  |  * the terms of the GNU General Public License version 2.  This program | 
					
						
							|  |  |  |  * is licensed "as is" without any warranty of any kind, whether express | 
					
						
							|  |  |  |  * or implied. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/stddef.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/signal.h>
 | 
					
						
							|  |  |  | #include <linux/irq.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <asm/system.h>
 | 
					
						
							|  |  |  | #include <asm/irq.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "cpc700.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | cpc700_unmask_irq(unsigned int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int tr_bits; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * IRQ 31 is largest IRQ supported. | 
					
						
							|  |  |  | 	 * IRQs 17-19 are reserved. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if ((irq <= 31) && ((irq < 17) || (irq > 19))) { | 
					
						
							|  |  |  | 		tr_bits = CPC700_IN_32(CPC700_UIC_UICTR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ((tr_bits & (1 << (31 - irq))) == 0) { | 
					
						
							|  |  |  | 			/* level trigger interrupt, clear bit in status
 | 
					
						
							|  |  |  | 			 * register */ | 
					
						
							|  |  |  | 			CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ | 
					
						
							|  |  |  | 		ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | cpc700_mask_irq(unsigned int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * IRQ 31 is largest IRQ supported. | 
					
						
							|  |  |  | 	 * IRQs 17-19 are reserved. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if ((irq <= 31) && ((irq < 17) || (irq > 19))) { | 
					
						
							|  |  |  | 		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ | 
					
						
							|  |  |  | 		ppc_cached_irq_mask[0] &= | 
					
						
							|  |  |  | 			~CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | cpc700_mask_and_ack_irq(unsigned int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u_int	bit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * IRQ 31 is largest IRQ supported. | 
					
						
							|  |  |  | 	 * IRQs 17-19 are reserved. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if ((irq <= 31) && ((irq < 17) || (irq > 19))) { | 
					
						
							|  |  |  | 		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ | 
					
						
							|  |  |  | 		bit = CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ppc_cached_irq_mask[0] &= ~bit; | 
					
						
							|  |  |  | 		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); | 
					
						
							|  |  |  | 		CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct hw_interrupt_type cpc700_pic = { | 
					
						
							| 
									
										
										
										
											2005-09-10 00:26:40 -07:00
										 |  |  | 	.typename = "CPC700 PIC", | 
					
						
							|  |  |  | 	.enable = cpc700_unmask_irq, | 
					
						
							|  |  |  | 	.disable = cpc700_mask_irq, | 
					
						
							|  |  |  | 	.ack = cpc700_mask_and_ack_irq, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __init static void | 
					
						
							|  |  |  | cpc700_pic_init_irq(unsigned int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int tmp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set interrupt sense */ | 
					
						
							|  |  |  | 	tmp = CPC700_IN_32(CPC700_UIC_UICTR); | 
					
						
							|  |  |  | 	if (cpc700_irq_assigns[irq][0] == 0) { | 
					
						
							|  |  |  | 		tmp &= ~CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		tmp |= CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICTR, tmp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set interrupt polarity */ | 
					
						
							|  |  |  | 	tmp = CPC700_IN_32(CPC700_UIC_UICPR); | 
					
						
							|  |  |  | 	if (cpc700_irq_assigns[irq][1]) { | 
					
						
							|  |  |  | 		tmp |= CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		tmp &= ~CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICPR, tmp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set interrupt critical */ | 
					
						
							|  |  |  | 	tmp = CPC700_IN_32(CPC700_UIC_UICCR); | 
					
						
							|  |  |  | 	tmp |= CPC700_UIC_IRQ_BIT(irq); | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICCR, tmp); | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __init void | 
					
						
							|  |  |  | cpc700_init_IRQ(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ppc_cached_irq_mask[0] = 0; | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000);    /* Disable all irq's */ | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff);    /* Clear cur intrs */ | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff);    /* Gen INT not MCP */ | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000);    /* Active low */ | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000);    /* Level Sensitive */ | 
					
						
							|  |  |  | 	CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI); | 
					
						
							|  |  |  | 						        /* IRQ 0 is highest */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < 17; i++) { | 
					
						
							|  |  |  | 		irq_desc[i].handler = &cpc700_pic; | 
					
						
							|  |  |  | 		cpc700_pic_init_irq(i); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 20; i < 32; i++) { | 
					
						
							|  |  |  | 		irq_desc[i].handler = &cpc700_pic; | 
					
						
							|  |  |  | 		cpc700_pic_init_irq(i); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Find the highest IRQ that generating an interrupt, if any. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | cpc700_get_irq(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int irq = 0; | 
					
						
							|  |  |  | 	u_int irq_status, irq_test = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	irq_status = CPC700_IN_32(CPC700_UIC_UICMSR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if (irq_status & irq_test) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		irq++; | 
					
						
							|  |  |  | 		irq_test <<= 1; | 
					
						
							|  |  |  | 	} while (irq < NR_IRQS); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (irq == NR_IRQS) | 
					
						
							|  |  |  | 	    irq = 33; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (31 - irq); | 
					
						
							|  |  |  | } |