linux/arch/mips/netlogic/common/irq.c
<<
>>
Prefs
   1/*
   2 * Copyright 2003-2011 NetLogic Microsystems, Inc. (NetLogic). All rights
   3 * reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the NetLogic
   9 * license below:
  10 *
  11 * Redistribution and use in source and binary forms, with or without
  12 * modification, are permitted provided that the following conditions
  13 * are met:
  14 *
  15 * 1. Redistributions of source code must retain the above copyright
  16 *    notice, this list of conditions and the following disclaimer.
  17 * 2. Redistributions in binary form must reproduce the above copyright
  18 *    notice, this list of conditions and the following disclaimer in
  19 *    the documentation and/or other materials provided with the
  20 *    distribution.
  21 *
  22 * THIS SOFTWARE IS PROVIDED BY NETLOGIC ``AS IS'' AND ANY EXPRESS OR
  23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25 * ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE LIABLE
  26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  29 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  31 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  32 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33 */
  34
  35#include <linux/kernel.h>
  36#include <linux/init.h>
  37#include <linux/linkage.h>
  38#include <linux/interrupt.h>
  39#include <linux/mm.h>
  40#include <linux/slab.h>
  41#include <linux/irq.h>
  42
  43#include <linux/irqdomain.h>
  44#include <linux/of_address.h>
  45#include <linux/of_irq.h>
  46
  47#include <asm/errno.h>
  48#include <asm/signal.h>
  49#include <asm/ptrace.h>
  50#include <asm/mipsregs.h>
  51#include <asm/thread_info.h>
  52
  53#include <asm/netlogic/mips-extns.h>
  54#include <asm/netlogic/interrupt.h>
  55#include <asm/netlogic/haldefs.h>
  56#include <asm/netlogic/common.h>
  57
  58#if defined(CONFIG_CPU_XLP)
  59#include <asm/netlogic/xlp-hal/iomap.h>
  60#include <asm/netlogic/xlp-hal/xlp.h>
  61#include <asm/netlogic/xlp-hal/pic.h>
  62#elif defined(CONFIG_CPU_XLR)
  63#include <asm/netlogic/xlr/iomap.h>
  64#include <asm/netlogic/xlr/pic.h>
  65#include <asm/netlogic/xlr/fmn.h>
  66#else
  67#error "Unknown CPU"
  68#endif
  69
  70#ifdef CONFIG_SMP
  71#define SMP_IRQ_MASK    ((1ULL << IRQ_IPI_SMP_FUNCTION) | \
  72                                 (1ULL << IRQ_IPI_SMP_RESCHEDULE))
  73#else
  74#define SMP_IRQ_MASK    0
  75#endif
  76#define PERCPU_IRQ_MASK (SMP_IRQ_MASK | (1ull << IRQ_TIMER) | \
  77                                (1ull << IRQ_FMN))
  78
  79struct nlm_pic_irq {
  80        void    (*extra_ack)(struct irq_data *);
  81        struct  nlm_soc_info *node;
  82        int     picirq;
  83        int     irt;
  84        int     flags;
  85};
  86
  87static void xlp_pic_enable(struct irq_data *d)
  88{
  89        unsigned long flags;
  90        struct nlm_pic_irq *pd = irq_data_get_irq_chip_data(d);
  91
  92        BUG_ON(!pd);
  93        spin_lock_irqsave(&pd->node->piclock, flags);
  94        nlm_pic_enable_irt(pd->node->picbase, pd->irt);
  95        spin_unlock_irqrestore(&pd->node->piclock, flags);
  96}
  97
  98static void xlp_pic_disable(struct irq_data *d)
  99{
 100        struct nlm_pic_irq *pd = irq_data_get_irq_chip_data(d);
 101        unsigned long flags;
 102
 103        BUG_ON(!pd);
 104        spin_lock_irqsave(&pd->node->piclock, flags);
 105        nlm_pic_disable_irt(pd->node->picbase, pd->irt);
 106        spin_unlock_irqrestore(&pd->node->piclock, flags);
 107}
 108
 109static void xlp_pic_mask_ack(struct irq_data *d)
 110{
 111        struct nlm_pic_irq *pd = irq_data_get_irq_chip_data(d);
 112
 113        clear_c0_eimr(pd->picirq);
 114        ack_c0_eirr(pd->picirq);
 115}
 116
 117static void xlp_pic_unmask(struct irq_data *d)
 118{
 119        struct nlm_pic_irq *pd = irq_data_get_irq_chip_data(d);
 120
 121        BUG_ON(!pd);
 122
 123        if (pd->extra_ack)
 124                pd->extra_ack(d);
 125
 126        /* re-enable the intr on this cpu */
 127        set_c0_eimr(pd->picirq);
 128
 129        /* Ack is a single write, no need to lock */
 130        nlm_pic_ack(pd->node->picbase, pd->irt);
 131}
 132
 133static struct irq_chip xlp_pic = {
 134        .name           = "XLP-PIC",
 135        .irq_enable     = xlp_pic_enable,
 136        .irq_disable    = xlp_pic_disable,
 137        .irq_mask_ack   = xlp_pic_mask_ack,
 138        .irq_unmask     = xlp_pic_unmask,
 139};
 140
 141static void cpuintr_disable(struct irq_data *d)
 142{
 143        clear_c0_eimr(d->irq);
 144}
 145
 146static void cpuintr_enable(struct irq_data *d)
 147{
 148        set_c0_eimr(d->irq);
 149}
 150
 151static void cpuintr_ack(struct irq_data *d)
 152{
 153        ack_c0_eirr(d->irq);
 154}
 155
 156/*
 157 * Chip definition for CPU originated interrupts(timer, msg) and
 158 * IPIs
 159 */
 160struct irq_chip nlm_cpu_intr = {
 161        .name           = "XLP-CPU-INTR",
 162        .irq_enable     = cpuintr_enable,
 163        .irq_disable    = cpuintr_disable,
 164        .irq_mask       = cpuintr_disable,
 165        .irq_ack        = cpuintr_ack,
 166        .irq_eoi        = cpuintr_enable,
 167};
 168
 169static void __init nlm_init_percpu_irqs(void)
 170{
 171        int i;
 172
 173        for (i = 0; i < PIC_IRT_FIRST_IRQ; i++)
 174                irq_set_chip_and_handler(i, &nlm_cpu_intr, handle_percpu_irq);
 175#ifdef CONFIG_SMP
 176        irq_set_chip_and_handler(IRQ_IPI_SMP_FUNCTION, &nlm_cpu_intr,
 177                         nlm_smp_function_ipi_handler);
 178        irq_set_chip_and_handler(IRQ_IPI_SMP_RESCHEDULE, &nlm_cpu_intr,
 179                         nlm_smp_resched_ipi_handler);
 180#endif
 181}
 182
 183
 184void nlm_setup_pic_irq(int node, int picirq, int irq, int irt)
 185{
 186        struct nlm_pic_irq *pic_data;
 187        int xirq;
 188
 189        xirq = nlm_irq_to_xirq(node, irq);
 190        pic_data = kzalloc(sizeof(*pic_data), GFP_KERNEL);
 191        BUG_ON(pic_data == NULL);
 192        pic_data->irt = irt;
 193        pic_data->picirq = picirq;
 194        pic_data->node = nlm_get_node(node);
 195        irq_set_chip_and_handler(xirq, &xlp_pic, handle_level_irq);
 196        irq_set_chip_data(xirq, pic_data);
 197}
 198
 199void nlm_set_pic_extra_ack(int node, int irq, void (*xack)(struct irq_data *))
 200{
 201        struct nlm_pic_irq *pic_data;
 202        int xirq;
 203
 204        xirq = nlm_irq_to_xirq(node, irq);
 205        pic_data = irq_get_chip_data(xirq);
 206        if (WARN_ON(!pic_data))
 207                return;
 208        pic_data->extra_ack = xack;
 209}
 210
 211static void nlm_init_node_irqs(int node)
 212{
 213        struct nlm_soc_info *nodep;
 214        int i, irt;
 215
 216        pr_info("Init IRQ for node %d\n", node);
 217        nodep = nlm_get_node(node);
 218        nodep->irqmask = PERCPU_IRQ_MASK;
 219        for (i = PIC_IRT_FIRST_IRQ; i <= PIC_IRT_LAST_IRQ; i++) {
 220                irt = nlm_irq_to_irt(i);
 221                if (irt == -1)          /* unused irq */
 222                        continue;
 223                nodep->irqmask |= 1ull << i;
 224                if (irt == -2)          /* not a direct PIC irq */
 225                        continue;
 226
 227                nlm_pic_init_irt(nodep->picbase, irt, i,
 228                                node * nlm_threads_per_node(), 0);
 229                nlm_setup_pic_irq(node, i, i, irt);
 230        }
 231}
 232
 233void nlm_smp_irq_init(int hwtid)
 234{
 235        int cpu, node;
 236
 237        cpu = hwtid % nlm_threads_per_node();
 238        node = hwtid / nlm_threads_per_node();
 239
 240        if (cpu == 0 && node != 0)
 241                nlm_init_node_irqs(node);
 242        write_c0_eimr(nlm_get_node(node)->irqmask);
 243}
 244
 245asmlinkage void plat_irq_dispatch(void)
 246{
 247        uint64_t eirr;
 248        int i, node;
 249
 250        node = nlm_nodeid();
 251        eirr = read_c0_eirr_and_eimr();
 252        if (eirr == 0)
 253                return;
 254
 255        i = __ffs64(eirr);
 256        /* per-CPU IRQs don't need translation */
 257        if (i < PIC_IRQ_BASE) {
 258                do_IRQ(i);
 259                return;
 260        }
 261
 262#if defined(CONFIG_PCI_MSI) && defined(CONFIG_CPU_XLP)
 263        /* PCI interrupts need a second level dispatch for MSI bits */
 264        if (i >= PIC_PCIE_LINK_MSI_IRQ(0) && i <= PIC_PCIE_LINK_MSI_IRQ(3)) {
 265                nlm_dispatch_msi(node, i);
 266                return;
 267        }
 268        if (i >= PIC_PCIE_MSIX_IRQ(0) && i <= PIC_PCIE_MSIX_IRQ(3)) {
 269                nlm_dispatch_msix(node, i);
 270                return;
 271        }
 272
 273#endif
 274        /* top level irq handling */
 275        do_IRQ(nlm_irq_to_xirq(node, i));
 276}
 277
 278#ifdef CONFIG_CPU_XLP
 279static int __init xlp_of_pic_init(struct device_node *node,
 280                                        struct device_node *parent)
 281{
 282        const int n_picirqs = PIC_IRT_LAST_IRQ - PIC_IRQ_BASE + 1;
 283        struct irq_domain *xlp_pic_domain;
 284        struct resource res;
 285        int socid, ret, bus;
 286
 287        /* we need a hack to get the PIC's SoC chip id */
 288        ret = of_address_to_resource(node, 0, &res);
 289        if (ret < 0) {
 290                pr_err("PIC %pOFn: reg property not found!\n", node);
 291                return -EINVAL;
 292        }
 293
 294        if (cpu_is_xlp9xx()) {
 295                bus = (res.start >> 20) & 0xf;
 296                for (socid = 0; socid < NLM_NR_NODES; socid++) {
 297                        if (!nlm_node_present(socid))
 298                                continue;
 299                        if (nlm_get_node(socid)->socbus == bus)
 300                                break;
 301                }
 302                if (socid == NLM_NR_NODES) {
 303                        pr_err("PIC %pOFn: Node mapping for bus %d not found!\n",
 304                                        node, bus);
 305                        return -EINVAL;
 306                }
 307        } else {
 308                socid = (res.start >> 18) & 0x3;
 309                if (!nlm_node_present(socid)) {
 310                        pr_err("PIC %pOFn: node %d does not exist!\n",
 311                                                        node, socid);
 312                        return -EINVAL;
 313                }
 314        }
 315
 316        if (!nlm_node_present(socid)) {
 317                pr_err("PIC %pOFn: node %d does not exist!\n", node, socid);
 318                return -EINVAL;
 319        }
 320
 321        xlp_pic_domain = irq_domain_add_legacy(node, n_picirqs,
 322                nlm_irq_to_xirq(socid, PIC_IRQ_BASE), PIC_IRQ_BASE,
 323                &irq_domain_simple_ops, NULL);
 324        if (xlp_pic_domain == NULL) {
 325                pr_err("PIC %pOFn: Creating legacy domain failed!\n", node);
 326                return -EINVAL;
 327        }
 328        pr_info("Node %d: IRQ domain created for PIC@%pR\n", socid, &res);
 329        return 0;
 330}
 331
 332static struct of_device_id __initdata xlp_pic_irq_ids[] = {
 333        { .compatible = "netlogic,xlp-pic", .data = xlp_of_pic_init },
 334        {},
 335};
 336#endif
 337
 338void __init arch_init_irq(void)
 339{
 340        /* Initialize the irq descriptors */
 341        nlm_init_percpu_irqs();
 342        nlm_init_node_irqs(0);
 343        write_c0_eimr(nlm_current_node()->irqmask);
 344#if defined(CONFIG_CPU_XLR)
 345        nlm_setup_fmn_irq();
 346#endif
 347#ifdef CONFIG_CPU_XLP
 348        of_irq_init(xlp_pic_irq_ids);
 349#endif
 350}
 351