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> 1941a83e06SJoel Porquet #include <linux/irqchip.h> 20d7fbc6caSMaxime Ripard #include <linux/of.h> 21d7fbc6caSMaxime Ripard #include <linux/of_address.h> 22d7fbc6caSMaxime Ripard #include <linux/of_irq.h> 23d7fbc6caSMaxime Ripard 24d7fbc6caSMaxime Ripard #include <asm/exception.h> 25d7fbc6caSMaxime Ripard 26d7fbc6caSMaxime Ripard #define SUN4I_IRQ_VECTOR_REG 0x00 27d7fbc6caSMaxime Ripard #define SUN4I_IRQ_PROTECTION_REG 0x08 28d7fbc6caSMaxime Ripard #define SUN4I_IRQ_NMI_CTRL_REG 0x0c 29d7fbc6caSMaxime Ripard #define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) 30d7fbc6caSMaxime Ripard #define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) 31d4fc2ea0SMesih Kilinc #define SUN4I_IRQ_ENABLE_REG(data, x) ((data)->enable_reg_offset + 0x4 * x) 32d4fc2ea0SMesih Kilinc #define SUN4I_IRQ_MASK_REG(data, x) ((data)->mask_reg_offset + 0x4 * x) 33d4fc2ea0SMesih Kilinc #define SUN4I_IRQ_ENABLE_REG_OFFSET 0x40 34d4fc2ea0SMesih Kilinc #define SUN4I_IRQ_MASK_REG_OFFSET 0x50 35b0c4b9f3SMesih Kilinc #define SUNIV_IRQ_ENABLE_REG_OFFSET 0x20 36b0c4b9f3SMesih Kilinc #define SUNIV_IRQ_MASK_REG_OFFSET 0x30 37d7fbc6caSMaxime Ripard 38177304cfSMesih Kilinc struct sun4i_irq_chip_data { 39177304cfSMesih Kilinc void __iomem *irq_base; 40177304cfSMesih Kilinc struct irq_domain *irq_domain; 41d4fc2ea0SMesih Kilinc u32 enable_reg_offset; 42d4fc2ea0SMesih Kilinc u32 mask_reg_offset; 43177304cfSMesih Kilinc }; 44177304cfSMesih Kilinc 45177304cfSMesih Kilinc static struct sun4i_irq_chip_data *irq_ic_data; 46d7fbc6caSMaxime Ripard 478783dd3aSStephen Boyd static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); 48d7fbc6caSMaxime Ripard 49baaecfa7SAxel Lin static void sun4i_irq_ack(struct irq_data *irqd) 50d7fbc6caSMaxime Ripard { 51d7fbc6caSMaxime Ripard unsigned int irq = irqd_to_hwirq(irqd); 52d7fbc6caSMaxime Ripard 53915b78ceSHans de Goede if (irq != 0) 54915b78ceSHans de Goede return; /* Only IRQ 0 / the ENMI needs to be acked */ 55915b78ceSHans de Goede 56177304cfSMesih Kilinc writel(BIT(0), irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)); 57d7fbc6caSMaxime Ripard } 58d7fbc6caSMaxime Ripard 59d7fbc6caSMaxime Ripard static void sun4i_irq_mask(struct irq_data *irqd) 60d7fbc6caSMaxime Ripard { 61d7fbc6caSMaxime Ripard unsigned int irq = irqd_to_hwirq(irqd); 62d7fbc6caSMaxime Ripard unsigned int irq_off = irq % 32; 63d7fbc6caSMaxime Ripard int reg = irq / 32; 64d7fbc6caSMaxime Ripard u32 val; 65d7fbc6caSMaxime Ripard 66d4fc2ea0SMesih Kilinc val = readl(irq_ic_data->irq_base + 67d4fc2ea0SMesih Kilinc SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); 68d7fbc6caSMaxime Ripard writel(val & ~(1 << irq_off), 69d4fc2ea0SMesih Kilinc irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); 70d7fbc6caSMaxime Ripard } 71d7fbc6caSMaxime Ripard 72d7fbc6caSMaxime Ripard static void sun4i_irq_unmask(struct irq_data *irqd) 73d7fbc6caSMaxime Ripard { 74d7fbc6caSMaxime Ripard unsigned int irq = irqd_to_hwirq(irqd); 75d7fbc6caSMaxime Ripard unsigned int irq_off = irq % 32; 76d7fbc6caSMaxime Ripard int reg = irq / 32; 77d7fbc6caSMaxime Ripard u32 val; 78d7fbc6caSMaxime Ripard 79d4fc2ea0SMesih Kilinc val = readl(irq_ic_data->irq_base + 80d4fc2ea0SMesih Kilinc SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); 81d7fbc6caSMaxime Ripard writel(val | (1 << irq_off), 82d4fc2ea0SMesih Kilinc irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, reg)); 83d7fbc6caSMaxime Ripard } 84d7fbc6caSMaxime Ripard 85d7fbc6caSMaxime Ripard static struct irq_chip sun4i_irq_chip = { 86d7fbc6caSMaxime Ripard .name = "sun4i_irq", 87e9df9e22SHans de Goede .irq_eoi = sun4i_irq_ack, 88e9df9e22SHans de Goede .irq_mask = sun4i_irq_mask, 89e9df9e22SHans de Goede .irq_unmask = sun4i_irq_unmask, 90e9df9e22SHans de Goede .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED, 91e9df9e22SHans de Goede }; 92e9df9e22SHans de Goede 93d7fbc6caSMaxime Ripard static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, 94d7fbc6caSMaxime Ripard irq_hw_number_t hw) 95d7fbc6caSMaxime Ripard { 96915b78ceSHans de Goede irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq); 97d17cab44SRob Herring irq_set_probe(virq); 98d7fbc6caSMaxime Ripard 99d7fbc6caSMaxime Ripard return 0; 100d7fbc6caSMaxime Ripard } 101d7fbc6caSMaxime Ripard 10296009736SKrzysztof Kozlowski static const struct irq_domain_ops sun4i_irq_ops = { 103d7fbc6caSMaxime Ripard .map = sun4i_irq_map, 104d7fbc6caSMaxime Ripard .xlate = irq_domain_xlate_onecell, 105d7fbc6caSMaxime Ripard }; 106d7fbc6caSMaxime Ripard 107d7fbc6caSMaxime Ripard static int __init sun4i_of_init(struct device_node *node, 108d7fbc6caSMaxime Ripard struct device_node *parent) 109d7fbc6caSMaxime Ripard { 110177304cfSMesih Kilinc irq_ic_data->irq_base = of_iomap(node, 0); 111177304cfSMesih Kilinc if (!irq_ic_data->irq_base) 112e81f54c6SRob Herring panic("%pOF: unable to map IC registers\n", 113e81f54c6SRob Herring node); 114d7fbc6caSMaxime Ripard 115d7fbc6caSMaxime Ripard /* Disable all interrupts */ 116d4fc2ea0SMesih Kilinc writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 0)); 117d4fc2ea0SMesih Kilinc writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 1)); 118d4fc2ea0SMesih Kilinc writel(0, irq_ic_data->irq_base + SUN4I_IRQ_ENABLE_REG(irq_ic_data, 2)); 119d7fbc6caSMaxime Ripard 120649ff46eSHans de Goede /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */ 121d4fc2ea0SMesih Kilinc writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 0)); 122d4fc2ea0SMesih Kilinc writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 1)); 123d4fc2ea0SMesih Kilinc writel(0, irq_ic_data->irq_base + SUN4I_IRQ_MASK_REG(irq_ic_data, 2)); 124d7fbc6caSMaxime Ripard 125d7fbc6caSMaxime Ripard /* Clear all the pending interrupts */ 126177304cfSMesih Kilinc writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)); 127177304cfSMesih Kilinc writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(1)); 128177304cfSMesih Kilinc writel(0xffffffff, irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(2)); 129d7fbc6caSMaxime Ripard 130d7fbc6caSMaxime Ripard /* Enable protection mode */ 131177304cfSMesih Kilinc writel(0x01, irq_ic_data->irq_base + SUN4I_IRQ_PROTECTION_REG); 132d7fbc6caSMaxime Ripard 133d7fbc6caSMaxime Ripard /* Configure the external interrupt source type */ 134177304cfSMesih Kilinc writel(0x00, irq_ic_data->irq_base + SUN4I_IRQ_NMI_CTRL_REG); 135d7fbc6caSMaxime Ripard 136177304cfSMesih Kilinc irq_ic_data->irq_domain = irq_domain_add_linear(node, 3 * 32, 137d7fbc6caSMaxime Ripard &sun4i_irq_ops, NULL); 138177304cfSMesih Kilinc if (!irq_ic_data->irq_domain) 139e81f54c6SRob Herring panic("%pOF: unable to create IRQ domain\n", node); 140d7fbc6caSMaxime Ripard 141d7fbc6caSMaxime Ripard set_handle_irq(sun4i_handle_irq); 142d7fbc6caSMaxime Ripard 143d7fbc6caSMaxime Ripard return 0; 144d7fbc6caSMaxime Ripard } 145b0c4b9f3SMesih Kilinc 146b0c4b9f3SMesih Kilinc static int __init sun4i_ic_of_init(struct device_node *node, 147b0c4b9f3SMesih Kilinc struct device_node *parent) 148b0c4b9f3SMesih Kilinc { 149b0c4b9f3SMesih Kilinc irq_ic_data = kzalloc(sizeof(struct sun4i_irq_chip_data), GFP_KERNEL); 15075768e39SZhen Lei if (!irq_ic_data) 151b0c4b9f3SMesih Kilinc return -ENOMEM; 152b0c4b9f3SMesih Kilinc 153b0c4b9f3SMesih Kilinc irq_ic_data->enable_reg_offset = SUN4I_IRQ_ENABLE_REG_OFFSET; 154b0c4b9f3SMesih Kilinc irq_ic_data->mask_reg_offset = SUN4I_IRQ_MASK_REG_OFFSET; 155b0c4b9f3SMesih Kilinc 156b0c4b9f3SMesih Kilinc return sun4i_of_init(node, parent); 157b0c4b9f3SMesih Kilinc } 158b0c4b9f3SMesih Kilinc 159b0c4b9f3SMesih Kilinc IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_ic_of_init); 160b0c4b9f3SMesih Kilinc 161b0c4b9f3SMesih Kilinc static int __init suniv_ic_of_init(struct device_node *node, 162b0c4b9f3SMesih Kilinc struct device_node *parent) 163b0c4b9f3SMesih Kilinc { 164b0c4b9f3SMesih Kilinc irq_ic_data = kzalloc(sizeof(struct sun4i_irq_chip_data), GFP_KERNEL); 16575768e39SZhen Lei if (!irq_ic_data) 166b0c4b9f3SMesih Kilinc return -ENOMEM; 167b0c4b9f3SMesih Kilinc 168b0c4b9f3SMesih Kilinc irq_ic_data->enable_reg_offset = SUNIV_IRQ_ENABLE_REG_OFFSET; 169b0c4b9f3SMesih Kilinc irq_ic_data->mask_reg_offset = SUNIV_IRQ_MASK_REG_OFFSET; 170b0c4b9f3SMesih Kilinc 171b0c4b9f3SMesih Kilinc return sun4i_of_init(node, parent); 172b0c4b9f3SMesih Kilinc } 173b0c4b9f3SMesih Kilinc 174b0c4b9f3SMesih Kilinc IRQCHIP_DECLARE(allwinner_sunvi_ic, "allwinner,suniv-f1c100s-ic", 175b0c4b9f3SMesih Kilinc suniv_ic_of_init); 176d7fbc6caSMaxime Ripard 1778783dd3aSStephen Boyd static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) 178d7fbc6caSMaxime Ripard { 17921d06d91SMarc Zyngier u32 hwirq; 180d7fbc6caSMaxime Ripard 18156af0416SHans de Goede /* 18256af0416SHans de Goede * hwirq == 0 can mean one of 3 things: 18356af0416SHans de Goede * 1) no more irqs pending 18456af0416SHans de Goede * 2) irq 0 pending 18556af0416SHans de Goede * 3) spurious irq 18656af0416SHans de Goede * So if we immediately get a reading of 0, check the irq-pending reg 18756af0416SHans de Goede * to differentiate between 2 and 3. We only do this once to avoid 188a359f757SIngo Molnar * the extra check in the common case of 1 happening after having 18956af0416SHans de Goede * read the vector-reg once. 19056af0416SHans de Goede */ 191177304cfSMesih Kilinc hwirq = readl(irq_ic_data->irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; 19256af0416SHans de Goede if (hwirq == 0 && 193177304cfSMesih Kilinc !(readl(irq_ic_data->irq_base + SUN4I_IRQ_PENDING_REG(0)) & 194177304cfSMesih Kilinc BIT(0))) 19556af0416SHans de Goede return; 19656af0416SHans de Goede 19756af0416SHans de Goede do { 1980953fb26SMark Rutland generic_handle_domain_irq(irq_ic_data->irq_domain, hwirq); 199177304cfSMesih Kilinc hwirq = readl(irq_ic_data->irq_base + 200177304cfSMesih Kilinc SUN4I_IRQ_VECTOR_REG) >> 2; 20156af0416SHans de Goede } while (hwirq != 0); 202d7fbc6caSMaxime Ripard } 203