Signed-off-by: Jörn Engel <joern@wohnheim.fh-wedel.de> Signed-off-by: Adrian Bunk <bunk@stusta.de>
		
			
				
	
	
		
			285 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			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/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>
 | |
| #include <asm/machdep.h>
 | |
| 
 | |
| /* 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)
 | |
| #define IRQ_MASK_UIC3(irq)		IRQ_MASK_UICx(irq)
 | |
| 
 | |
| #define UIC_HANDLERS(n)							\
 | |
| static void ppc4xx_uic##n##_enable(unsigned int irq)			\
 | |
| {									\
 | |
| 	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;					\
 | |
| 	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,				\
 | |
| }									\
 | |
| 
 | |
| #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
 | |
| #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)
 | |
| {
 | |
| #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
 | |
| 	/* 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 },
 | |
| #if NR_UICS > 3
 | |
| 	{ .decl = DECLARE_UIC(3), .base = UIC3 },
 | |
| #endif
 | |
| #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].chip = &__uic[i >> 5].decl;
 | |
| 		if (is_level_sensitive(i))
 | |
| 			irq_desc[i].status |= IRQ_LEVEL;
 | |
| 	}
 | |
| 
 | |
| 	ppc_md.get_irq = ppc4xx_pic_get_irq;
 | |
| }
 |