linux/drivers/irqchip/irq-mips-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/irqchip.h>
  35#include <linux/irqdomain.h>
  36
  37#include <asm/irq_cpu.h>
  38#include <asm/mipsregs.h>
  39#include <asm/mipsmtregs.h>
  40#include <asm/setup.h>
  41
  42static inline void unmask_mips_irq(struct irq_data *d)
  43{
  44        set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  45        irq_enable_hazard();
  46}
  47
  48static inline void mask_mips_irq(struct irq_data *d)
  49{
  50        clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  51        irq_disable_hazard();
  52}
  53
  54static struct irq_chip mips_cpu_irq_controller = {
  55        .name           = "MIPS",
  56        .irq_ack        = mask_mips_irq,
  57        .irq_mask       = mask_mips_irq,
  58        .irq_mask_ack   = mask_mips_irq,
  59        .irq_unmask     = unmask_mips_irq,
  60        .irq_eoi        = unmask_mips_irq,
  61        .irq_disable    = mask_mips_irq,
  62        .irq_enable     = unmask_mips_irq,
  63};
  64
  65/*
  66 * Basically the same as above but taking care of all the MT stuff
  67 */
  68
  69static unsigned int mips_mt_cpu_irq_startup(struct irq_data *d)
  70{
  71        unsigned int vpflags = dvpe();
  72
  73        clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  74        evpe(vpflags);
  75        unmask_mips_irq(d);
  76        return 0;
  77}
  78
  79/*
  80 * While we ack the interrupt interrupts are disabled and thus we don't need
  81 * to deal with concurrency issues.  Same for mips_cpu_irq_end.
  82 */
  83static void mips_mt_cpu_irq_ack(struct irq_data *d)
  84{
  85        unsigned int vpflags = dvpe();
  86        clear_c0_cause(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
  87        evpe(vpflags);
  88        mask_mips_irq(d);
  89}
  90
  91static struct irq_chip mips_mt_cpu_irq_controller = {
  92        .name           = "MIPS",
  93        .irq_startup    = mips_mt_cpu_irq_startup,
  94        .irq_ack        = mips_mt_cpu_irq_ack,
  95        .irq_mask       = mask_mips_irq,
  96        .irq_mask_ack   = mips_mt_cpu_irq_ack,
  97        .irq_unmask     = unmask_mips_irq,
  98        .irq_eoi        = unmask_mips_irq,
  99        .irq_disable    = mask_mips_irq,
 100        .irq_enable     = unmask_mips_irq,
 101};
 102
 103asmlinkage void __weak plat_irq_dispatch(void)
 104{
 105        unsigned long pending = read_c0_cause() & read_c0_status() & ST0_IM;
 106        int irq;
 107
 108        if (!pending) {
 109                spurious_interrupt();
 110                return;
 111        }
 112
 113        pending >>= CAUSEB_IP;
 114        while (pending) {
 115                irq = fls(pending) - 1;
 116                do_IRQ(MIPS_CPU_IRQ_BASE + irq);
 117                pending &= ~BIT(irq);
 118        }
 119}
 120
 121static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq,
 122                             irq_hw_number_t hw)
 123{
 124        static struct irq_chip *chip;
 125
 126        if (hw < 2 && cpu_has_mipsmt) {
 127                /* Software interrupts are used for MT/CMT IPI */
 128                chip = &mips_mt_cpu_irq_controller;
 129        } else {
 130                chip = &mips_cpu_irq_controller;
 131        }
 132
 133        if (cpu_has_vint)
 134                set_vi_handler(hw, plat_irq_dispatch);
 135
 136        irq_set_chip_and_handler(irq, chip, handle_percpu_irq);
 137
 138        return 0;
 139}
 140
 141static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
 142        .map = mips_cpu_intc_map,
 143        .xlate = irq_domain_xlate_onecell,
 144};
 145
 146static void __init __mips_cpu_irq_init(struct device_node *of_node)
 147{
 148        struct irq_domain *domain;
 149
 150        /* Mask interrupts. */
 151        clear_c0_status(ST0_IM);
 152        clear_c0_cause(CAUSEF_IP);
 153
 154        domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0,
 155                                       &mips_cpu_intc_irq_domain_ops, NULL);
 156        if (!domain)
 157                panic("Failed to add irqdomain for MIPS CPU");
 158}
 159
 160void __init mips_cpu_irq_init(void)
 161{
 162        __mips_cpu_irq_init(NULL);
 163}
 164
 165int __init mips_cpu_irq_of_init(struct device_node *of_node,
 166                                struct device_node *parent)
 167{
 168        __mips_cpu_irq_init(of_node);
 169        return 0;
 170}
 171IRQCHIP_DECLARE(cpu_intc, "mti,cpu-interrupt-controller", mips_cpu_irq_of_init);
 172