Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * drivers/irq/irq-nvic.c
0004  *
0005  * Copyright (C) 2008 ARM Limited, All Rights Reserved.
0006  * Copyright (C) 2013 Pengutronix
0007  *
0008  * Support for the Nested Vectored Interrupt Controller found on the
0009  * ARMv7-M CPUs (Cortex-M3/M4)
0010  */
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/init.h>
0014 #include <linux/kernel.h>
0015 #include <linux/slab.h>
0016 #include <linux/err.h>
0017 #include <linux/io.h>
0018 #include <linux/of.h>
0019 #include <linux/of_address.h>
0020 #include <linux/irq.h>
0021 #include <linux/irqchip.h>
0022 #include <linux/irqdomain.h>
0023 
0024 #include <asm/v7m.h>
0025 #include <asm/exception.h>
0026 
0027 #define NVIC_ISER       0x000
0028 #define NVIC_ICER       0x080
0029 #define NVIC_IPR        0x400
0030 
0031 #define NVIC_MAX_BANKS      16
0032 /*
0033  * Each bank handles 32 irqs. Only the 16th (= last) bank handles only
0034  * 16 irqs.
0035  */
0036 #define NVIC_MAX_IRQ        ((NVIC_MAX_BANKS - 1) * 32 + 16)
0037 
0038 static struct irq_domain *nvic_irq_domain;
0039 
0040 static void __irq_entry nvic_handle_irq(struct pt_regs *regs)
0041 {
0042     unsigned long icsr = readl_relaxed(BASEADDR_V7M_SCB + V7M_SCB_ICSR);
0043     irq_hw_number_t hwirq = (icsr & V7M_SCB_ICSR_VECTACTIVE) - 16;
0044 
0045     generic_handle_domain_irq(nvic_irq_domain, hwirq);
0046 }
0047 
0048 static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
0049                 unsigned int nr_irqs, void *arg)
0050 {
0051     int i, ret;
0052     irq_hw_number_t hwirq;
0053     unsigned int type = IRQ_TYPE_NONE;
0054     struct irq_fwspec *fwspec = arg;
0055 
0056     ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
0057     if (ret)
0058         return ret;
0059 
0060     for (i = 0; i < nr_irqs; i++)
0061         irq_map_generic_chip(domain, virq + i, hwirq + i);
0062 
0063     return 0;
0064 }
0065 
0066 static const struct irq_domain_ops nvic_irq_domain_ops = {
0067     .translate = irq_domain_translate_onecell,
0068     .alloc = nvic_irq_domain_alloc,
0069     .free = irq_domain_free_irqs_top,
0070 };
0071 
0072 static int __init nvic_of_init(struct device_node *node,
0073                    struct device_node *parent)
0074 {
0075     unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
0076     unsigned int irqs, i, ret, numbanks;
0077     void __iomem *nvic_base;
0078 
0079     numbanks = (readl_relaxed(V7M_SCS_ICTR) &
0080             V7M_SCS_ICTR_INTLINESNUM_MASK) + 1;
0081 
0082     nvic_base = of_iomap(node, 0);
0083     if (!nvic_base) {
0084         pr_warn("unable to map nvic registers\n");
0085         return -ENOMEM;
0086     }
0087 
0088     irqs = numbanks * 32;
0089     if (irqs > NVIC_MAX_IRQ)
0090         irqs = NVIC_MAX_IRQ;
0091 
0092     nvic_irq_domain =
0093         irq_domain_add_linear(node, irqs, &nvic_irq_domain_ops, NULL);
0094 
0095     if (!nvic_irq_domain) {
0096         pr_warn("Failed to allocate irq domain\n");
0097         iounmap(nvic_base);
0098         return -ENOMEM;
0099     }
0100 
0101     ret = irq_alloc_domain_generic_chips(nvic_irq_domain, 32, 1,
0102                          "nvic_irq", handle_fasteoi_irq,
0103                          clr, 0, IRQ_GC_INIT_MASK_CACHE);
0104     if (ret) {
0105         pr_warn("Failed to allocate irq chips\n");
0106         irq_domain_remove(nvic_irq_domain);
0107         iounmap(nvic_base);
0108         return ret;
0109     }
0110 
0111     for (i = 0; i < numbanks; ++i) {
0112         struct irq_chip_generic *gc;
0113 
0114         gc = irq_get_domain_generic_chip(nvic_irq_domain, 32 * i);
0115         gc->reg_base = nvic_base + 4 * i;
0116         gc->chip_types[0].regs.enable = NVIC_ISER;
0117         gc->chip_types[0].regs.disable = NVIC_ICER;
0118         gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
0119         gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
0120         /* This is a no-op as end of interrupt is signaled by the
0121          * exception return sequence.
0122          */
0123         gc->chip_types[0].chip.irq_eoi = irq_gc_noop;
0124 
0125         /* disable interrupts */
0126         writel_relaxed(~0, gc->reg_base + NVIC_ICER);
0127     }
0128 
0129     /* Set priority on all interrupts */
0130     for (i = 0; i < irqs; i += 4)
0131         writel_relaxed(0, nvic_base + NVIC_IPR + i);
0132 
0133     set_handle_irq(nvic_handle_irq);
0134     return 0;
0135 }
0136 IRQCHIP_DECLARE(armv7m_nvic, "arm,armv7m-nvic", nvic_of_init);