linux/arch/mips/kernel/irq_cpu.c
<<
>>
Prefs
   1/*
   2 * Copyright 2001 MontaVista Software Inc.
   3 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
   4 *
   5 * Copyright (C) 2001 Ralf Baechle
   6 * Copyright (C) 2005  MIPS Technologies, Inc.  All rights reserved.
   7 *      Author: Maciej W. Rozycki <macro@mips.com>
   8 *
   9 * This file define the irq handler for MIPS CPU interrupts.
  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 * Almost all MIPS CPUs define 8 interrupt sources.  They are typically
  19 * level triggered (i.e., cannot be cleared from CPU; must be cleared from
  20 * device).  The first two are software interrupts which we don't really
  21 * use or support.  The last one is usually the CPU timer interrupt if
  22 * counter register is present or, for CPUs with an external FPU, by
  23 * convention it's the FPU exception interrupt.
  24 *
  25 * Don't even think about using this on SMP.  You have been warned.
  26 *
  27 * This file exports one global function:
  28 *      void mips_cpu_irq_init(void);
  29 */
  30#include <linux/init.h>
  31#include <linux/interrupt.h>
  32#include <linux/kernel.h>
  33#include <linux/irq.h>
  34#include <linux/irqdomain.h>
  35
  36#include <asm/irq_cpu.h>
  37#include <asm/mipsregs.h>
  38#include <asm/mipsmtregs.h>
  39
  40static inline void unmask_mips_irq(struct irq_data *d)
  41{
  42        set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  43        irq_enable_hazard();
  44}
  45
  46static inline void mask_mips_irq(struct irq_data *d)
  47{
  48        clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  49        irq_disable_hazard();
  50}
  51
  52static struct irq_chip mips_cpu_irq_controller = {
  53        .name           = "MIPS",
  54        .irq_ack        = mask_mips_irq,
  55        .irq_mask       = mask_mips_irq,
  56        .irq_mask_ack   = mask_mips_irq,
  57        .irq_unmask     = unmask_mips_irq,
  58        .irq_eoi        = unmask_mips_irq,
  59};
  60
  61/*
  62 * Basically the same as above but taking care of all the MT stuff
  63 */
  64
  65static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d)
  66{
  67        unsigned int vpflags = dvpe();
  68
  69        clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  70        evpe(vpflags);
  71        unmask_mips_irq(d);
  72        return 0;
  73}
  74
  75/*
  76 * While we ack the interrupt interrupts are disabled and thus we don't need
  77 * to deal with concurrency issues.  Same for mips_cpu_irq_end.
  78 */
  79static void mips_mt_cpu_irq_ack(struct irq_data *d)
  80{
  81        unsigned int vpflags = dvpe();
  82        clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  83        evpe(vpflags);
  84        mask_mips_irq(d);
  85}
  86
  87static struct irq_chip mips_mt_cpu_irq_controller = {
  88        .name           = "MIPS",
  89        .irq_startup    = mips_mt_cpu_irq_startup,
  90        .irq_ack        = mips_mt_cpu_irq_ack,
  91        .irq_mask       = mask_mips_irq,
  92        .irq_mask_ack   = mips_mt_cpu_irq_ack,
  93        .irq_unmask     = unmask_mips_irq,
  94        .irq_eoi        = unmask_mips_irq,
  95};
  96
  97void __init mips_cpu_irq_init(void)
  98{
  99        int irq_base = MIPS_CPU_IRQ_BASE;
 100        int i;
 101
 102        /* Mask interrupts. */
 103        clear_c0_status(ST0_IM);
 104        clear_c0_cause(CAUSEF_IP);
 105
 106        /* Software interrupts are used for MT/CMT IPI */
 107        for (i = irq_base; i < irq_base + 2; i++)
 108                irq_set_chip_and_handler(i, cpu_has_mipsmt ?
 109                                         &mips_mt_cpu_irq_controller :
 110                                         &mips_cpu_irq_controller,
 111                                         handle_percpu_irq);
 112
 113        for (i = irq_base + 2; i < irq_base + 8; i++)
 114                irq_set_chip_and_handler(i, &mips_cpu_irq_controller,
 115                                         handle_percpu_irq);
 116}
 117
 118#ifdef CONFIG_IRQ_DOMAIN
 119static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq,
 120                             irq_hw_number_t hw)
 121{
 122        static struct irq_chip *chip;
 123
 124        if (hw < 2 && cpu_has_mipsmt) {
 125                /* Software interrupts are used for MT/CMT IPI */
 126                chip = &mips_mt_cpu_irq_controller;
 127        } else {
 128                chip = &mips_cpu_irq_controller;
 129        }
 130
 131        irq_set_chip_and_handler(irq, chip, handle_percpu_irq);
 132
 133        return 0;
 134}
 135
 136static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
 137        .map = mips_cpu_intc_map,
 138        .xlate = irq_domain_xlate_onecell,
 139};
 140
 141int __init mips_cpu_intc_init(struct device_node *of_node,
 142                              struct device_node *parent)
 143{
 144        struct irq_domain *domain;
 145
 146        /* Mask interrupts. */
 147        clear_c0_status(ST0_IM);
 148        clear_c0_cause(CAUSEF_IP);
 149
 150        domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0,
 151                                       &mips_cpu_intc_irq_domain_ops, NULL);
 152        if (!domain)
 153                panic("Failed to add irqdomain for MIPS CPU\n");
 154
 155        return 0;
 156}
 157#endif /* CONFIG_IRQ_DOMAIN */
 158