Logo Search packages:      
Sourcecode: linux version File versions  Download package

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/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(void)
{
      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(void)
{
      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(void)
{
      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(void)
{
      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;
}

Generated by  Doxygen 1.6.0   Back to index