123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /*
- * 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>
- /* 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 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 == 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 },
- #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;
- }
|