linux/arch/ppc/syslib/ppc4xx_pic.c
<<
>>
Prefs
   1/*
   2 * Interrupt controller driver for PowerPC 4xx-based processors.
   3 *
   4 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
   5 * Copyright (c) 2004, 2005 Zultys Technologies
   6 *
   7 * Based on original code by
   8 *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
   9 *    Armin Custer <akuster@mvista.com>
  10 *
  11 * This program is free software; you can redistribute  it and/or modify it
  12 * under  the terms of  the GNU General  Public License as published by the
  13 * Free Software Foundation;  either version 2 of the  License, or (at your
  14 * option) any later version.
  15*/
  16#include <linux/init.h>
  17#include <linux/sched.h>
  18#include <linux/signal.h>
  19#include <linux/stddef.h>
  20
  21#include <asm/processor.h>
  22#include <asm/system.h>
  23#include <asm/irq.h>
  24#include <asm/ppc4xx_pic.h>
  25#include <asm/machdep.h>
  26
  27/* See comment in include/arch-ppc/ppc4xx_pic.h
  28 * for more info about these two variables
  29 */
  30extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
  31    __attribute__ ((weak));
  32extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
  33
  34#define IRQ_MASK_UIC0(irq)              (1 << (31 - (irq)))
  35#define IRQ_MASK_UICx(irq)              (1 << (31 - ((irq) & 0x1f)))
  36#define IRQ_MASK_UIC1(irq)              IRQ_MASK_UICx(irq)
  37#define IRQ_MASK_UIC2(irq)              IRQ_MASK_UICx(irq)
  38#define IRQ_MASK_UIC3(irq)              IRQ_MASK_UICx(irq)
  39
  40#define UIC_HANDLERS(n)                                                 \
  41static void ppc4xx_uic##n##_enable(unsigned int irq)                    \
  42{                                                                       \
  43        u32 mask = IRQ_MASK_UIC##n(irq);                                \
  44        if (irq_desc[irq].status & IRQ_LEVEL)                           \
  45                mtdcr(DCRN_UIC_SR(UIC##n), mask);                       \
  46        ppc_cached_irq_mask[n] |= mask;                                 \
  47        mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
  48}                                                                       \
  49                                                                        \
  50static void ppc4xx_uic##n##_disable(unsigned int irq)                   \
  51{                                                                       \
  52        ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq);                \
  53        mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
  54        ACK_UIC##n##_PARENT                                             \
  55}                                                                       \
  56                                                                        \
  57static void ppc4xx_uic##n##_ack(unsigned int irq)                       \
  58{                                                                       \
  59        u32 mask = IRQ_MASK_UIC##n(irq);                                \
  60        ppc_cached_irq_mask[n] &= ~mask;                                \
  61        mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);             \
  62        mtdcr(DCRN_UIC_SR(UIC##n), mask);                               \
  63        ACK_UIC##n##_PARENT                                             \
  64}                                                                       \
  65                                                                        \
  66static void ppc4xx_uic##n##_end(unsigned int irq)                       \
  67{                                                                       \
  68        unsigned int status = irq_desc[irq].status;                     \
  69        u32 mask = IRQ_MASK_UIC##n(irq);                                \
  70        if (status & IRQ_LEVEL) {                                       \
  71                mtdcr(DCRN_UIC_SR(UIC##n), mask);                       \
  72                ACK_UIC##n##_PARENT                                     \
  73        }                                                               \
  74        if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {              \
  75                ppc_cached_irq_mask[n] |= mask;                         \
  76                mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]);     \
  77        }                                                               \
  78}
  79
  80#define DECLARE_UIC(n)                                                  \
  81{                                                                       \
  82        .typename       = "UIC"#n,                                      \
  83        .enable         = ppc4xx_uic##n##_enable,                       \
  84        .disable        = ppc4xx_uic##n##_disable,                      \
  85        .ack            = ppc4xx_uic##n##_ack,                          \
  86        .end            = ppc4xx_uic##n##_end,                          \
  87}                                                                       \
  88
  89#if NR_UICS == 4
  90#define ACK_UIC0_PARENT
  91#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
  92#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC2NC);
  93#define ACK_UIC3_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC3NC);
  94UIC_HANDLERS(0);
  95UIC_HANDLERS(1);
  96UIC_HANDLERS(2);
  97UIC_HANDLERS(3);
  98
  99static int ppc4xx_pic_get_irq(void)
 100{
 101        u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
 102        if (uic0 & UIC0_UIC1NC)
 103                return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
 104        else if (uic0 & UIC0_UIC2NC)
 105                return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
 106        else if (uic0 & UIC0_UIC3NC)
 107                return 128 - ffs(mfdcr(DCRN_UIC_MSR(UIC3)));
 108        else
 109                return uic0 ? 32 - ffs(uic0) : -1;
 110}
 111
 112static void __init ppc4xx_pic_impl_init(void)
 113{
 114        /* Enable cascade interrupts in UIC0 */
 115        ppc_cached_irq_mask[0] |= UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC;
 116        mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC | UIC0_UIC2NC | UIC0_UIC3NC);
 117        mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
 118}
 119
 120#elif NR_UICS == 3
 121#define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
 122#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
 123#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
 124UIC_HANDLERS(0);
 125UIC_HANDLERS(1);
 126UIC_HANDLERS(2);
 127
 128static int ppc4xx_pic_get_irq(void)
 129{
 130        u32 uicb = mfdcr(DCRN_UIC_MSR(UICB));
 131        if (uicb & UICB_UIC0NC)
 132                return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0)));
 133        else if (uicb & UICB_UIC1NC)
 134                return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
 135        else if (uicb & UICB_UIC2NC)
 136                return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
 137        else
 138                return -1;
 139}
 140
 141static void __init ppc4xx_pic_impl_init(void)
 142{
 143#if defined(CONFIG_440GX)
 144        /* Disable 440GP compatibility mode if it was enabled in firmware */
 145        SDR_WRITE(DCRN_SDR_MFR, SDR_READ(DCRN_SDR_MFR) & ~DCRN_SDR_MFR_PCM);
 146#endif
 147        /* Configure Base UIC */
 148        mtdcr(DCRN_UIC_CR(UICB), 0);
 149        mtdcr(DCRN_UIC_TR(UICB), 0);
 150        mtdcr(DCRN_UIC_PR(UICB), 0xffffffff);
 151        mtdcr(DCRN_UIC_SR(UICB), 0xffffffff);
 152        mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC);
 153}
 154
 155#elif NR_UICS == 2
 156#define ACK_UIC0_PARENT
 157#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
 158UIC_HANDLERS(0);
 159UIC_HANDLERS(1);
 160
 161static int ppc4xx_pic_get_irq(void)
 162{
 163        u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
 164        if (uic0 & UIC0_UIC1NC)
 165                return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
 166        else
 167                return uic0 ? 32 - ffs(uic0) : -1;
 168}
 169
 170static void __init ppc4xx_pic_impl_init(void)
 171{
 172        /* Enable cascade interrupt in UIC0 */
 173        ppc_cached_irq_mask[0] |= UIC0_UIC1NC;
 174        mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
 175        mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
 176}
 177
 178#elif NR_UICS == 1
 179#define ACK_UIC0_PARENT
 180UIC_HANDLERS(0);
 181
 182static int ppc4xx_pic_get_irq(void)
 183{
 184        u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
 185        return uic0 ? 32 - ffs(uic0) : -1;
 186}
 187
 188static inline void ppc4xx_pic_impl_init(void)
 189{
 190}
 191#endif
 192
 193static struct ppc4xx_uic_impl {
 194        struct hw_interrupt_type decl;
 195        int base;                       /* Base DCR number */
 196} __uic[] = {
 197        { .decl = DECLARE_UIC(0), .base = UIC0 },
 198#if NR_UICS > 1
 199        { .decl = DECLARE_UIC(1), .base = UIC1 },
 200#if NR_UICS > 2
 201        { .decl = DECLARE_UIC(2), .base = UIC2 },
 202#if NR_UICS > 3
 203        { .decl = DECLARE_UIC(3), .base = UIC3 },
 204#endif
 205#endif
 206#endif
 207};
 208
 209static inline int is_level_sensitive(int irq)
 210{
 211        u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
 212        return (tr & IRQ_MASK_UICx(irq)) == 0;
 213}
 214
 215void __init ppc4xx_pic_init(void)
 216{
 217        int i;
 218        unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
 219
 220        for (i = 0; i < NR_UICS; ++i) {
 221                int base = __uic[i].base;
 222
 223                /* Disable everything by default */
 224                ppc_cached_irq_mask[i] = 0;
 225                mtdcr(DCRN_UIC_ER(base), 0);
 226
 227                /* We don't use critical interrupts */
 228                mtdcr(DCRN_UIC_CR(base), 0);
 229
 230                /* Configure polarity and triggering */
 231                if (ppc4xx_core_uic_cfg) {
 232                        struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i;
 233                        u32 mask = p->ext_irq_mask;
 234                        u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask;
 235                        u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask;
 236
 237                        /* "Fixed" interrupts (on-chip devices) */
 238                        pr |= p->polarity & ~mask;
 239                        tr |= p->triggering & ~mask;
 240
 241                        /* Merge external IRQs settings if board port
 242                         * provided them
 243                         */
 244                        if (eirqs && mask) {
 245                                pr &= ~mask;
 246                                tr &= ~mask;
 247                                while (mask) {
 248                                        /* Extract current external IRQ mask */
 249                                        u32 eirq_mask = 1 << __ilog2(mask);
 250
 251                                        if (!(*eirqs & IRQ_SENSE_LEVEL))
 252                                                tr |= eirq_mask;
 253
 254                                        if (*eirqs & IRQ_POLARITY_POSITIVE)
 255                                                pr |= eirq_mask;
 256
 257                                        mask &= ~eirq_mask;
 258                                        ++eirqs;
 259                                }
 260                        }
 261                        mtdcr(DCRN_UIC_PR(base), pr);
 262                        mtdcr(DCRN_UIC_TR(base), tr);
 263                }
 264
 265                /* ACK any pending interrupts to prevent false
 266                 * triggering after first enable
 267                 */
 268                mtdcr(DCRN_UIC_SR(base), 0xffffffff);
 269        }
 270
 271        /* Perform optional implementation specific setup
 272         * (e.g. enable cascade interrupts for multi-UIC configurations)
 273         */
 274        ppc4xx_pic_impl_init();
 275
 276        /* Attach low-level handlers */
 277        for (i = 0; i < (NR_UICS << 5); ++i) {
 278                irq_desc[i].chip = &__uic[i >> 5].decl;
 279                if (is_level_sensitive(i))
 280                        irq_desc[i].status |= IRQ_LEVEL;
 281        }
 282
 283        ppc_md.get_irq = ppc4xx_pic_get_irq;
 284}
 285