linux/arch/ppc/syslib/mv64360_pic.c
<<
>>
Prefs
   1/*
   2 * Interrupt controller support for Marvell's MV64360.
   3 *
   4 * Author: Rabeeh Khoury <rabeeh@galileo.co.il>
   5 * Based on MV64360 PIC written by
   6 * Chris Zankel <chris@mvista.com>
   7 * Mark A. Greer <mgreer@mvista.com>
   8 *
   9 * Copyright 2004 MontaVista Software, Inc.
  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
  17/*
  18 * This file contains the specific functions to support the MV64360
  19 * interrupt controller.
  20 *
  21 * The MV64360 has two main interrupt registers (high and low) that
  22 * summarizes the interrupts generated by the units of the MV64360.
  23 * Each bit is assigned to an interrupt number, where the low register
  24 * are assigned from IRQ0 to IRQ31 and the high cause register
  25 * from IRQ32 to IRQ63
  26 * The GPP (General Purpose Pins) interrupts are assigned from IRQ64 (GPP0)
  27 * to IRQ95 (GPP31).
  28 * get_irq() returns the lowest interrupt number that is currently asserted.
  29 *
  30 * Note:
  31 *  - This driver does not initialize the GPP when used as an interrupt
  32 *    input.
  33 */
  34
  35#include <linux/stddef.h>
  36#include <linux/init.h>
  37#include <linux/sched.h>
  38#include <linux/signal.h>
  39#include <linux/stddef.h>
  40#include <linux/delay.h>
  41#include <linux/irq.h>
  42#include <linux/interrupt.h>
  43
  44#include <asm/io.h>
  45#include <asm/processor.h>
  46#include <asm/system.h>
  47#include <asm/irq.h>
  48#include <asm/mv64x60.h>
  49#include <asm/machdep.h>
  50
  51#ifdef CONFIG_IRQ_ALL_CPUS
  52#error "The mv64360 does not support distribution of IRQs on all CPUs"
  53#endif
  54/* ========================== forward declaration ========================== */
  55
  56static void mv64360_unmask_irq(unsigned int);
  57static void mv64360_mask_irq(unsigned int);
  58static irqreturn_t mv64360_cpu_error_int_handler(int, void *);
  59static irqreturn_t mv64360_sram_error_int_handler(int, void *);
  60static irqreturn_t mv64360_pci_error_int_handler(int, void *);
  61
  62/* ========================== local declarations =========================== */
  63
  64struct hw_interrupt_type mv64360_pic = {
  65        .typename = " mv64360  ",
  66        .enable   = mv64360_unmask_irq,
  67        .disable  = mv64360_mask_irq,
  68        .ack      = mv64360_mask_irq,
  69        .end      = mv64360_unmask_irq,
  70};
  71
  72#define CPU_INTR_STR    "mv64360 cpu interface error"
  73#define SRAM_INTR_STR   "mv64360 internal sram error"
  74#define PCI0_INTR_STR   "mv64360 pci 0 error"
  75#define PCI1_INTR_STR   "mv64360 pci 1 error"
  76
  77static struct mv64x60_handle bh;
  78
  79u32 mv64360_irq_base = 0;       /* MV64360 handles the next 96 IRQs from here */
  80
  81/* mv64360_init_irq()
  82 *
  83 * This function initializes the interrupt controller. It assigns
  84 * all interrupts from IRQ0 to IRQ95 to the mv64360 interrupt controller.
  85 *
  86 * Input Variable(s):
  87 *  None.
  88 *
  89 * Outpu. Variable(s):
  90 *  None.
  91 *
  92 * Returns:
  93 *  void
  94 *
  95 * Note:
  96 *  We register all GPP inputs as interrupt source, but disable them.
  97 */
  98void __init
  99mv64360_init_irq(void)
 100{
 101        int i;
 102
 103        if (ppc_md.progress)
 104                ppc_md.progress("mv64360_init_irq: enter", 0x0);
 105
 106        bh.v_base = mv64x60_get_bridge_vbase();
 107
 108        ppc_cached_irq_mask[0] = 0;
 109        ppc_cached_irq_mask[1] = 0x0f000000;    /* Enable GPP intrs */
 110        ppc_cached_irq_mask[2] = 0;
 111
 112        /* disable all interrupts and clear current interrupts */
 113        mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0);
 114        mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
 115        mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,ppc_cached_irq_mask[0]);
 116        mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,ppc_cached_irq_mask[1]);
 117
 118        /* All interrupts are level interrupts */
 119        for (i = mv64360_irq_base; i < (mv64360_irq_base + 96); i++) {
 120                irq_desc[i].status |= IRQ_LEVEL;
 121                irq_desc[i].chip = &mv64360_pic;
 122        }
 123
 124        if (ppc_md.progress)
 125                ppc_md.progress("mv64360_init_irq: exit", 0x0);
 126}
 127
 128/* mv64360_get_irq()
 129 *
 130 * This function returns the lowest interrupt number of all interrupts that
 131 * are currently asserted.
 132 *
 133 * Output Variable(s):
 134 *  None.
 135 *
 136 * Returns:
 137 *  int <interrupt number> or -2 (bogus interrupt)
 138 *
 139 */
 140int
 141mv64360_get_irq(void)
 142{
 143        int irq;
 144        int irq_gpp;
 145
 146#ifdef CONFIG_SMP
 147        /*
 148         * Second CPU gets only doorbell (message) interrupts.
 149         * The doorbell interrupt is BIT28 in the main interrupt low cause reg.
 150         */
 151        int cpu_nr = smp_processor_id();
 152        if (cpu_nr == 1) {
 153                if (!(mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO) &
 154                      (1 << MV64x60_IRQ_DOORBELL)))
 155                        return -1;
 156                return mv64360_irq_base + MV64x60_IRQ_DOORBELL;
 157        }
 158#endif
 159
 160        irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO);
 161        irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
 162
 163        if (irq == -1) {
 164                irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_HI);
 165                irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]);
 166
 167                if (irq == -1)
 168                        irq = -2; /* bogus interrupt, should never happen */
 169                else {
 170                        if ((irq >= 24) && (irq < MV64x60_IRQ_DOORBELL)) {
 171                                irq_gpp = mv64x60_read(&bh,
 172                                        MV64x60_GPP_INTR_CAUSE);
 173                                irq_gpp = __ilog2(irq_gpp &
 174                                        ppc_cached_irq_mask[2]);
 175
 176                                if (irq_gpp == -1)
 177                                        irq = -2;
 178                                else {
 179                                        irq = irq_gpp + 64;
 180                                        mv64x60_write(&bh,
 181                                                MV64x60_GPP_INTR_CAUSE,
 182                                                ~(1 << (irq - 64)));
 183                                }
 184                        }
 185                        else
 186                                irq += 32;
 187                }
 188        }
 189
 190        (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE);
 191
 192        if (irq < 0)
 193                return (irq);
 194        else
 195                return (mv64360_irq_base + irq);
 196}
 197
 198/* mv64360_unmask_irq()
 199 *
 200 * This function enables an interrupt.
 201 *
 202 * Input Variable(s):
 203 *  unsigned int        interrupt number (IRQ0...IRQ95).
 204 *
 205 * Output Variable(s):
 206 *  None.
 207 *
 208 * Returns:
 209 *  void
 210 */
 211static void
 212mv64360_unmask_irq(unsigned int irq)
 213{
 214#ifdef CONFIG_SMP
 215        /* second CPU gets only doorbell interrupts */
 216        if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) {
 217                mv64x60_set_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO,
 218                                 (1 << MV64x60_IRQ_DOORBELL));
 219                return;
 220        }
 221#endif
 222        irq -= mv64360_irq_base;
 223
 224        if (irq > 31) {
 225                if (irq > 63) /* unmask GPP irq */
 226                        mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
 227                                ppc_cached_irq_mask[2] |= (1 << (irq - 64)));
 228                else /* mask high interrupt register */
 229                        mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,
 230                                ppc_cached_irq_mask[1] |= (1 << (irq - 32)));
 231        }
 232        else /* mask low interrupt register */
 233                mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,
 234                        ppc_cached_irq_mask[0] |= (1 << irq));
 235
 236        (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
 237        return;
 238}
 239
 240/* mv64360_mask_irq()
 241 *
 242 * This function disables the requested interrupt.
 243 *
 244 * Input Variable(s):
 245 *  unsigned int        interrupt number (IRQ0...IRQ95).
 246 *
 247 * Output Variable(s):
 248 *  None.
 249 *
 250 * Returns:
 251 *  void
 252 */
 253static void
 254mv64360_mask_irq(unsigned int irq)
 255{
 256#ifdef CONFIG_SMP
 257        if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) {
 258                mv64x60_clr_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO,
 259                                 (1 << MV64x60_IRQ_DOORBELL));
 260                return;
 261        }
 262#endif
 263        irq -= mv64360_irq_base;
 264
 265        if (irq > 31) {
 266                if (irq > 63) /* mask GPP irq */
 267                        mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
 268                                ppc_cached_irq_mask[2] &= ~(1 << (irq - 64)));
 269                else /* mask high interrupt register */
 270                        mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,
 271                                ppc_cached_irq_mask[1] &= ~(1 << (irq - 32)));
 272        }
 273        else /* mask low interrupt register */
 274                mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,
 275                        ppc_cached_irq_mask[0] &= ~(1 << irq));
 276
 277        (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
 278        return;
 279}
 280
 281static irqreturn_t
 282mv64360_cpu_error_int_handler(int irq, void *dev_id)
 283{
 284        printk(KERN_ERR "mv64360_cpu_error_int_handler: %s 0x%08x\n",
 285                "Error on CPU interface - Cause regiser",
 286                mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE));
 287        printk(KERN_ERR "\tCPU error register dump:\n");
 288        printk(KERN_ERR "\tAddress low  0x%08x\n",
 289               mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO));
 290        printk(KERN_ERR "\tAddress high 0x%08x\n",
 291               mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI));
 292        printk(KERN_ERR "\tData low     0x%08x\n",
 293               mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO));
 294        printk(KERN_ERR "\tData high    0x%08x\n",
 295               mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI));
 296        printk(KERN_ERR "\tParity       0x%08x\n",
 297               mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY));
 298        mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
 299        return IRQ_HANDLED;
 300}
 301
 302static irqreturn_t
 303mv64360_sram_error_int_handler(int irq, void *dev_id)
 304{
 305        printk(KERN_ERR "mv64360_sram_error_int_handler: %s 0x%08x\n",
 306                "Error in internal SRAM - Cause register",
 307                mv64x60_read(&bh, MV64360_SRAM_ERR_CAUSE));
 308        printk(KERN_ERR "\tSRAM error register dump:\n");
 309        printk(KERN_ERR "\tAddress Low  0x%08x\n",
 310               mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_LO));
 311        printk(KERN_ERR "\tAddress High 0x%08x\n",
 312               mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_HI));
 313        printk(KERN_ERR "\tData Low     0x%08x\n",
 314               mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_LO));
 315        printk(KERN_ERR "\tData High    0x%08x\n",
 316               mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_HI));
 317        printk(KERN_ERR "\tParity       0x%08x\n",
 318                mv64x60_read(&bh, MV64360_SRAM_ERR_PARITY));
 319        mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0);
 320        return IRQ_HANDLED;
 321}
 322
 323static irqreturn_t
 324mv64360_pci_error_int_handler(int irq, void *dev_id)
 325{
 326        u32 val;
 327        unsigned int pci_bus = (unsigned int)dev_id;
 328
 329        if (pci_bus == 0) {     /* Error on PCI 0 */
 330                val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE);
 331                printk(KERN_ERR "%s: Error in PCI %d Interface\n",
 332                        "mv64360_pci_error_int_handler", pci_bus);
 333                printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
 334                printk(KERN_ERR "\tCause register 0x%08x\n", val);
 335                printk(KERN_ERR "\tAddress Low    0x%08x\n",
 336                       mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO));
 337                printk(KERN_ERR "\tAddress High   0x%08x\n",
 338                       mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI));
 339                printk(KERN_ERR "\tAttribute      0x%08x\n",
 340                       mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO));
 341                printk(KERN_ERR "\tCommand        0x%08x\n",
 342                       mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD));
 343                mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val);
 344        }
 345        if (pci_bus == 1) {     /* Error on PCI 1 */
 346                val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE);
 347                printk(KERN_ERR "%s: Error in PCI %d Interface\n",
 348                        "mv64360_pci_error_int_handler", pci_bus);
 349                printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
 350                printk(KERN_ERR "\tCause register 0x%08x\n", val);
 351                printk(KERN_ERR "\tAddress Low    0x%08x\n",
 352                       mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO));
 353                printk(KERN_ERR "\tAddress High   0x%08x\n",
 354                       mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI));
 355                printk(KERN_ERR "\tAttribute      0x%08x\n",
 356                       mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO));
 357                printk(KERN_ERR "\tCommand        0x%08x\n",
 358                       mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD));
 359                mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val);
 360        }
 361        return IRQ_HANDLED;
 362}
 363
 364/*
 365 * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of
 366 * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as
 367 * well.  IOW, don't set bit 0.
 368 */
 369#define MV64360_PCI0_ERR_MASK_VAL       0x00a50c24
 370
 371static int __init
 372mv64360_register_hdlrs(void)
 373{
 374        int     rc;
 375
 376        /* Clear old errors and register CPU interface error intr handler */
 377        mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
 378        if ((rc = request_irq(MV64x60_IRQ_CPU_ERR + mv64360_irq_base,
 379                mv64360_cpu_error_int_handler, IRQF_DISABLED, CPU_INTR_STR, NULL)))
 380                printk(KERN_WARNING "Can't register cpu error handler: %d", rc);
 381
 382        mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0);
 383        mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000ff);
 384
 385        /* Clear old errors and register internal SRAM error intr handler */
 386        mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0);
 387        if ((rc = request_irq(MV64360_IRQ_SRAM_PAR_ERR + mv64360_irq_base,
 388                mv64360_sram_error_int_handler,IRQF_DISABLED,SRAM_INTR_STR, NULL)))
 389                printk(KERN_WARNING "Can't register SRAM error handler: %d",rc);
 390
 391        /* Clear old errors and register PCI 0 error intr handler */
 392        mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, 0);
 393        if ((rc = request_irq(MV64360_IRQ_PCI0 + mv64360_irq_base,
 394                        mv64360_pci_error_int_handler,
 395                        IRQF_DISABLED, PCI0_INTR_STR, (void *)0)))
 396                printk(KERN_WARNING "Can't register pci 0 error handler: %d",
 397                        rc);
 398
 399        mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0);
 400        mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, MV64360_PCI0_ERR_MASK_VAL);
 401
 402        /* Erratum FEr PCI-#16 says to clear bit 0 of PCI SERRn Mask reg. */
 403        mv64x60_write(&bh, MV64x60_PCI0_ERR_SERR_MASK,
 404                mv64x60_read(&bh, MV64x60_PCI0_ERR_SERR_MASK) & ~0x1UL);
 405
 406        /* Clear old errors and register PCI 1 error intr handler */
 407        mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, 0);
 408        if ((rc = request_irq(MV64360_IRQ_PCI1 + mv64360_irq_base,
 409                        mv64360_pci_error_int_handler,
 410                        IRQF_DISABLED, PCI1_INTR_STR, (void *)1)))
 411                printk(KERN_WARNING "Can't register pci 1 error handler: %d",
 412                        rc);
 413
 414        mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0);
 415        mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, MV64360_PCI0_ERR_MASK_VAL);
 416
 417        /* Erratum FEr PCI-#16 says to clear bit 0 of PCI Intr Mask reg. */
 418        mv64x60_write(&bh, MV64x60_PCI1_ERR_SERR_MASK,
 419                mv64x60_read(&bh, MV64x60_PCI1_ERR_SERR_MASK) & ~0x1UL);
 420
 421        return 0;
 422}
 423
 424arch_initcall(mv64360_register_hdlrs);
 425