1d7fbc6caSMaxime Ripard /* 2d7fbc6caSMaxime Ripard * Allwinner A1X SoCs IRQ chip driver. 3d7fbc6caSMaxime Ripard * 4d7fbc6caSMaxime Ripard * Copyright (C) 2012 Maxime Ripard 5d7fbc6caSMaxime Ripard * 6d7fbc6caSMaxime Ripard * Maxime Ripard <maxime.ripard@free-electrons.com> 7d7fbc6caSMaxime Ripard * 8d7fbc6caSMaxime Ripard * Based on code from 9d7fbc6caSMaxime Ripard * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 10d7fbc6caSMaxime Ripard * Benn Huang <benn@allwinnertech.com> 11d7fbc6caSMaxime Ripard * 12d7fbc6caSMaxime Ripard * This file is licensed under the terms of the GNU General Public 13d7fbc6caSMaxime Ripard * License version 2. This program is licensed "as is" without any 14d7fbc6caSMaxime Ripard * warranty of any kind, whether express or implied. 15d7fbc6caSMaxime Ripard */ 16d7fbc6caSMaxime Ripard 17d7fbc6caSMaxime Ripard #include <linux/io.h> 18d7fbc6caSMaxime Ripard #include <linux/irq.h> 19d7fbc6caSMaxime Ripard #include <linux/of.h> 20d7fbc6caSMaxime Ripard #include <linux/of_address.h> 21d7fbc6caSMaxime Ripard #include <linux/of_irq.h> 22d7fbc6caSMaxime Ripard 23d7fbc6caSMaxime Ripard #include <asm/exception.h> 24d7fbc6caSMaxime Ripard #include <asm/mach/irq.h> 25d7fbc6caSMaxime Ripard 26d7fbc6caSMaxime Ripard #include "irqchip.h" 27d7fbc6caSMaxime Ripard 28d7fbc6caSMaxime Ripard #define SUN4I_IRQ_VECTOR_REG 0x00 29d7fbc6caSMaxime Ripard #define SUN4I_IRQ_PROTECTION_REG 0x08 30d7fbc6caSMaxime Ripard #define SUN4I_IRQ_NMI_CTRL_REG 0x0c 31d7fbc6caSMaxime Ripard #define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) 32d7fbc6caSMaxime Ripard #define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) 33d7fbc6caSMaxime Ripard #define SUN4I_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) 34d7fbc6caSMaxime Ripard #define SUN4I_IRQ_MASK_REG(x) (0x50 + 0x4 * x) 35d7fbc6caSMaxime Ripard 36d7fbc6caSMaxime Ripard static void __iomem *sun4i_irq_base; 37d7fbc6caSMaxime Ripard static struct irq_domain *sun4i_irq_domain; 38d7fbc6caSMaxime Ripard 398783dd3aSStephen Boyd static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); 40d7fbc6caSMaxime Ripard 41baaecfa7SAxel Lin static void sun4i_irq_ack(struct irq_data *irqd) 42d7fbc6caSMaxime Ripard { 43d7fbc6caSMaxime Ripard unsigned int irq = irqd_to_hwirq(irqd); 44d7fbc6caSMaxime Ripard unsigned int irq_off = irq % 32; 45d7fbc6caSMaxime Ripard int reg = irq / 32; 46d7fbc6caSMaxime Ripard u32 val; 47d7fbc6caSMaxime Ripard 48d7fbc6caSMaxime Ripard val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); 49d7fbc6caSMaxime Ripard writel(val | (1 << irq_off), 50d7fbc6caSMaxime Ripard sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); 51d7fbc6caSMaxime Ripard } 52d7fbc6caSMaxime Ripard 53d7fbc6caSMaxime Ripard static void sun4i_irq_mask(struct irq_data *irqd) 54d7fbc6caSMaxime Ripard { 55d7fbc6caSMaxime Ripard unsigned int irq = irqd_to_hwirq(irqd); 56d7fbc6caSMaxime Ripard unsigned int irq_off = irq % 32; 57d7fbc6caSMaxime Ripard int reg = irq / 32; 58d7fbc6caSMaxime Ripard u32 val; 59d7fbc6caSMaxime Ripard 60d7fbc6caSMaxime Ripard val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 61d7fbc6caSMaxime Ripard writel(val & ~(1 << irq_off), 62d7fbc6caSMaxime Ripard sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 63d7fbc6caSMaxime Ripard } 64d7fbc6caSMaxime Ripard 65d7fbc6caSMaxime Ripard static void sun4i_irq_unmask(struct irq_data *irqd) 66d7fbc6caSMaxime Ripard { 67d7fbc6caSMaxime Ripard unsigned int irq = irqd_to_hwirq(irqd); 68d7fbc6caSMaxime Ripard unsigned int irq_off = irq % 32; 69d7fbc6caSMaxime Ripard int reg = irq / 32; 70d7fbc6caSMaxime Ripard u32 val; 71d7fbc6caSMaxime Ripard 72d7fbc6caSMaxime Ripard val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 73d7fbc6caSMaxime Ripard writel(val | (1 << irq_off), 74d7fbc6caSMaxime Ripard sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 75d7fbc6caSMaxime Ripard } 76d7fbc6caSMaxime Ripard 77d7fbc6caSMaxime Ripard static struct irq_chip sun4i_irq_chip = { 78d7fbc6caSMaxime Ripard .name = "sun4i_irq", 79d7fbc6caSMaxime Ripard .irq_mask = sun4i_irq_mask, 80d7fbc6caSMaxime Ripard .irq_unmask = sun4i_irq_unmask, 81d7fbc6caSMaxime Ripard }; 82d7fbc6caSMaxime Ripard 83*e9df9e22SHans de Goede /* IRQ 0 / the ENMI needs a late eoi call */ 84*e9df9e22SHans de Goede static struct irq_chip sun4i_irq_chip_enmi = { 85*e9df9e22SHans de Goede .name = "sun4i_irq", 86*e9df9e22SHans de Goede .irq_eoi = sun4i_irq_ack, 87*e9df9e22SHans de Goede .irq_mask = sun4i_irq_mask, 88*e9df9e22SHans de Goede .irq_unmask = sun4i_irq_unmask, 89*e9df9e22SHans de Goede .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED, 90*e9df9e22SHans de Goede }; 91*e9df9e22SHans de Goede 92d7fbc6caSMaxime Ripard static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, 93d7fbc6caSMaxime Ripard irq_hw_number_t hw) 94d7fbc6caSMaxime Ripard { 95*e9df9e22SHans de Goede if (hw == 0) 96*e9df9e22SHans de Goede irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi, 97*e9df9e22SHans de Goede handle_fasteoi_irq); 98*e9df9e22SHans de Goede else 99d7fbc6caSMaxime Ripard irq_set_chip_and_handler(virq, &sun4i_irq_chip, 100d7fbc6caSMaxime Ripard handle_level_irq); 101*e9df9e22SHans de Goede 102d7fbc6caSMaxime Ripard set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); 103d7fbc6caSMaxime Ripard 104d7fbc6caSMaxime Ripard return 0; 105d7fbc6caSMaxime Ripard } 106d7fbc6caSMaxime Ripard 107d7fbc6caSMaxime Ripard static struct irq_domain_ops sun4i_irq_ops = { 108d7fbc6caSMaxime Ripard .map = sun4i_irq_map, 109d7fbc6caSMaxime Ripard .xlate = irq_domain_xlate_onecell, 110d7fbc6caSMaxime Ripard }; 111d7fbc6caSMaxime Ripard 112d7fbc6caSMaxime Ripard static int __init sun4i_of_init(struct device_node *node, 113d7fbc6caSMaxime Ripard struct device_node *parent) 114d7fbc6caSMaxime Ripard { 115d7fbc6caSMaxime Ripard sun4i_irq_base = of_iomap(node, 0); 116d7fbc6caSMaxime Ripard if (!sun4i_irq_base) 117d7fbc6caSMaxime Ripard panic("%s: unable to map IC registers\n", 118d7fbc6caSMaxime Ripard node->full_name); 119d7fbc6caSMaxime Ripard 120d7fbc6caSMaxime Ripard /* Disable all interrupts */ 121d7fbc6caSMaxime Ripard writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0)); 122d7fbc6caSMaxime Ripard writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); 123d7fbc6caSMaxime Ripard writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); 124d7fbc6caSMaxime Ripard 125649ff46eSHans de Goede /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */ 126d7fbc6caSMaxime Ripard writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); 127d7fbc6caSMaxime Ripard writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); 128d7fbc6caSMaxime Ripard writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); 129d7fbc6caSMaxime Ripard 130d7fbc6caSMaxime Ripard /* Clear all the pending interrupts */ 131d7fbc6caSMaxime Ripard writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); 132d7fbc6caSMaxime Ripard writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1)); 133d7fbc6caSMaxime Ripard writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2)); 134d7fbc6caSMaxime Ripard 135d7fbc6caSMaxime Ripard /* Enable protection mode */ 136d7fbc6caSMaxime Ripard writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG); 137d7fbc6caSMaxime Ripard 138d7fbc6caSMaxime Ripard /* Configure the external interrupt source type */ 139d7fbc6caSMaxime Ripard writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG); 140d7fbc6caSMaxime Ripard 141d7fbc6caSMaxime Ripard sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32, 142d7fbc6caSMaxime Ripard &sun4i_irq_ops, NULL); 143d7fbc6caSMaxime Ripard if (!sun4i_irq_domain) 144d7fbc6caSMaxime Ripard panic("%s: unable to create IRQ domain\n", node->full_name); 145d7fbc6caSMaxime Ripard 146d7fbc6caSMaxime Ripard set_handle_irq(sun4i_handle_irq); 147d7fbc6caSMaxime Ripard 148d7fbc6caSMaxime Ripard return 0; 149d7fbc6caSMaxime Ripard } 150a7e8b4b5SMaxime Ripard IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init); 151d7fbc6caSMaxime Ripard 1528783dd3aSStephen Boyd static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) 153d7fbc6caSMaxime Ripard { 154d7fbc6caSMaxime Ripard u32 irq, hwirq; 155d7fbc6caSMaxime Ripard 15656af0416SHans de Goede /* 15756af0416SHans de Goede * hwirq == 0 can mean one of 3 things: 15856af0416SHans de Goede * 1) no more irqs pending 15956af0416SHans de Goede * 2) irq 0 pending 16056af0416SHans de Goede * 3) spurious irq 16156af0416SHans de Goede * So if we immediately get a reading of 0, check the irq-pending reg 16256af0416SHans de Goede * to differentiate between 2 and 3. We only do this once to avoid 16356af0416SHans de Goede * the extra check in the common case of 1 hapening after having 16456af0416SHans de Goede * read the vector-reg once. 16556af0416SHans de Goede */ 166d7fbc6caSMaxime Ripard hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; 16756af0416SHans de Goede if (hwirq == 0 && 16856af0416SHans de Goede !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0))) 16956af0416SHans de Goede return; 17056af0416SHans de Goede 17156af0416SHans de Goede do { 172d7fbc6caSMaxime Ripard irq = irq_find_mapping(sun4i_irq_domain, hwirq); 173d7fbc6caSMaxime Ripard handle_IRQ(irq, regs); 174d7fbc6caSMaxime Ripard hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; 17556af0416SHans de Goede } while (hwirq != 0); 176d7fbc6caSMaxime Ripard } 177