150397Sobrien// SPDX-License-Identifier: GPL-2.0-only 250397Sobrien/* 350397Sobrien * drivers/irq/irq-nvic.c 450397Sobrien * 550397Sobrien * Copyright (C) 2008 ARM Limited, All Rights Reserved. 650397Sobrien * Copyright (C) 2013 Pengutronix 750397Sobrien * 850397Sobrien * Support for the Nested Vectored Interrupt Controller found on the 950397Sobrien * ARMv7-M CPUs (Cortex-M3/M4) 1050397Sobrien */ 1150397Sobrien#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1250397Sobrien 1350397Sobrien#include <linux/init.h> 1450397Sobrien#include <linux/kernel.h> 1550397Sobrien#include <linux/slab.h> 1650397Sobrien#include <linux/err.h> 1750397Sobrien#include <linux/io.h> 1850397Sobrien#include <linux/of.h> 1950397Sobrien#include <linux/of_address.h> 2050397Sobrien#include <linux/irq.h> 2150397Sobrien#include <linux/irqchip.h> 2250397Sobrien#include <linux/irqdomain.h> 2350397Sobrien 2450397Sobrien#include <asm/v7m.h> 2550397Sobrien#include <asm/exception.h> 2650397Sobrien 2750397Sobrien#define NVIC_ISER 0x000 2850397Sobrien#define NVIC_ICER 0x080 2950397Sobrien#define NVIC_IPR 0x400 3050397Sobrien 3150397Sobrien#define NVIC_MAX_BANKS 16 3250397Sobrien/* 3350397Sobrien * Each bank handles 32 irqs. Only the 16th (= last) bank handles only 3450397Sobrien * 16 irqs. 3550397Sobrien */ 3650397Sobrien#define NVIC_MAX_IRQ ((NVIC_MAX_BANKS - 1) * 32 + 16) 3750397Sobrien 3850397Sobrienstatic struct irq_domain *nvic_irq_domain; 3950397Sobrien 4050397Sobrienstatic void __irq_entry nvic_handle_irq(struct pt_regs *regs) 4150397Sobrien{ 4250397Sobrien unsigned long icsr = readl_relaxed(BASEADDR_V7M_SCB + V7M_SCB_ICSR); 4350397Sobrien irq_hw_number_t hwirq = (icsr & V7M_SCB_ICSR_VECTACTIVE) - 16; 4450397Sobrien 4550397Sobrien generic_handle_domain_irq(nvic_irq_domain, hwirq); 4650397Sobrien} 4750397Sobrien 4850397Sobrienstatic int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 4950397Sobrien unsigned int nr_irqs, void *arg) 5050397Sobrien{ 5150397Sobrien int i, ret; 5250397Sobrien irq_hw_number_t hwirq; 5350397Sobrien unsigned int type = IRQ_TYPE_NONE; 5450397Sobrien struct irq_fwspec *fwspec = arg; 5550397Sobrien 5650397Sobrien ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); 5750397Sobrien if (ret) 5850397Sobrien return ret; 5950397Sobrien 6050397Sobrien for (i = 0; i < nr_irqs; i++) 6150397Sobrien irq_map_generic_chip(domain, virq + i, hwirq + i); 6250397Sobrien 6350397Sobrien return 0; 6450397Sobrien} 6550397Sobrien 6650397Sobrienstatic const struct irq_domain_ops nvic_irq_domain_ops = { 6750397Sobrien .translate = irq_domain_translate_onecell, 6850397Sobrien .alloc = nvic_irq_domain_alloc, 6950397Sobrien .free = irq_domain_free_irqs_top, 7050397Sobrien}; 7150397Sobrien 7250397Sobrienstatic int __init nvic_of_init(struct device_node *node, 7350397Sobrien struct device_node *parent) 7450397Sobrien{ 7550397Sobrien unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 7650397Sobrien unsigned int irqs, i, ret, numbanks; 7750397Sobrien void __iomem *nvic_base; 7850397Sobrien 7950397Sobrien numbanks = (readl_relaxed(V7M_SCS_ICTR) & 8050397Sobrien V7M_SCS_ICTR_INTLINESNUM_MASK) + 1; 8150397Sobrien 8250397Sobrien nvic_base = of_iomap(node, 0); 8350397Sobrien if (!nvic_base) { 8450397Sobrien pr_warn("unable to map nvic registers\n"); 8550397Sobrien return -ENOMEM; 8650397Sobrien } 8750397Sobrien 8850397Sobrien irqs = numbanks * 32; 8950397Sobrien if (irqs > NVIC_MAX_IRQ) 9050397Sobrien irqs = NVIC_MAX_IRQ; 9150397Sobrien 9250397Sobrien nvic_irq_domain = 9350397Sobrien irq_domain_add_linear(node, irqs, &nvic_irq_domain_ops, NULL); 9450397Sobrien 9550397Sobrien if (!nvic_irq_domain) { 9650397Sobrien pr_warn("Failed to allocate irq domain\n"); 9750397Sobrien iounmap(nvic_base); 9850397Sobrien return -ENOMEM; 9950397Sobrien } 10050397Sobrien 10150397Sobrien ret = irq_alloc_domain_generic_chips(nvic_irq_domain, 32, 1, 10250397Sobrien "nvic_irq", handle_fasteoi_irq, 10350397Sobrien clr, 0, IRQ_GC_INIT_MASK_CACHE); 10450397Sobrien if (ret) { 10550397Sobrien pr_warn("Failed to allocate irq chips\n"); 10650397Sobrien irq_domain_remove(nvic_irq_domain); 10750397Sobrien iounmap(nvic_base); 10850397Sobrien return ret; 10950397Sobrien } 11050397Sobrien 11150397Sobrien for (i = 0; i < numbanks; ++i) { 11250397Sobrien struct irq_chip_generic *gc; 11350397Sobrien 11450397Sobrien gc = irq_get_domain_generic_chip(nvic_irq_domain, 32 * i); 11550397Sobrien gc->reg_base = nvic_base + 4 * i; 11650397Sobrien gc->chip_types[0].regs.enable = NVIC_ISER; 11750397Sobrien gc->chip_types[0].regs.disable = NVIC_ICER; 11850397Sobrien gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; 11950397Sobrien gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; 12050397Sobrien /* This is a no-op as end of interrupt is signaled by the 12150397Sobrien * exception return sequence. 12250397Sobrien */ 12350397Sobrien gc->chip_types[0].chip.irq_eoi = irq_gc_noop; 12450397Sobrien 12550397Sobrien /* disable interrupts */ 12650397Sobrien writel_relaxed(~0, gc->reg_base + NVIC_ICER); 12750397Sobrien } 12850397Sobrien 12950397Sobrien /* Set priority on all interrupts */ 13050397Sobrien for (i = 0; i < irqs; i += 4) 13150397Sobrien writel_relaxed(0, nvic_base + NVIC_IPR + i); 13250397Sobrien 13350397Sobrien set_handle_irq(nvic_handle_irq); 13450397Sobrien return 0; 13550397Sobrien} 13650397SobrienIRQCHIP_DECLARE(armv7m_nvic, "arm,armv7m-nvic", nvic_of_init); 13750397Sobrien