1 /* 2 * Allwinner A1X SoCs IRQ chip driver. 3 * 4 * Copyright (C) 2012 Maxime Ripard 5 * 6 * Maxime Ripard <maxime.ripard@free-electrons.com> 7 * 8 * Based on code from 9 * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 10 * Benn Huang <benn@allwinnertech.com> 11 * 12 * This file is licensed under the terms of the GNU General Public 13 * License version 2. This program is licensed "as is" without any 14 * warranty of any kind, whether express or implied. 15 */ 16 17 #include <linux/io.h> 18 #include <linux/irq.h> 19 #include <linux/of.h> 20 #include <linux/of_address.h> 21 #include <linux/of_irq.h> 22 23 #include <asm/exception.h> 24 #include <asm/mach/irq.h> 25 26 #include "irqchip.h" 27 28 #define SUN4I_IRQ_VECTOR_REG 0x00 29 #define SUN4I_IRQ_PROTECTION_REG 0x08 30 #define SUN4I_IRQ_NMI_CTRL_REG 0x0c 31 #define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) 32 #define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) 33 #define SUN4I_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) 34 #define SUN4I_IRQ_MASK_REG(x) (0x50 + 0x4 * x) 35 36 static void __iomem *sun4i_irq_base; 37 static struct irq_domain *sun4i_irq_domain; 38 39 static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); 40 41 void sun4i_irq_ack(struct irq_data *irqd) 42 { 43 unsigned int irq = irqd_to_hwirq(irqd); 44 unsigned int irq_off = irq % 32; 45 int reg = irq / 32; 46 u32 val; 47 48 val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); 49 writel(val | (1 << irq_off), 50 sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); 51 } 52 53 static void sun4i_irq_mask(struct irq_data *irqd) 54 { 55 unsigned int irq = irqd_to_hwirq(irqd); 56 unsigned int irq_off = irq % 32; 57 int reg = irq / 32; 58 u32 val; 59 60 val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 61 writel(val & ~(1 << irq_off), 62 sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 63 } 64 65 static void sun4i_irq_unmask(struct irq_data *irqd) 66 { 67 unsigned int irq = irqd_to_hwirq(irqd); 68 unsigned int irq_off = irq % 32; 69 int reg = irq / 32; 70 u32 val; 71 72 val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 73 writel(val | (1 << irq_off), 74 sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); 75 } 76 77 static struct irq_chip sun4i_irq_chip = { 78 .name = "sun4i_irq", 79 .irq_ack = sun4i_irq_ack, 80 .irq_mask = sun4i_irq_mask, 81 .irq_unmask = sun4i_irq_unmask, 82 }; 83 84 static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, 85 irq_hw_number_t hw) 86 { 87 irq_set_chip_and_handler(virq, &sun4i_irq_chip, 88 handle_level_irq); 89 set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); 90 91 return 0; 92 } 93 94 static struct irq_domain_ops sun4i_irq_ops = { 95 .map = sun4i_irq_map, 96 .xlate = irq_domain_xlate_onecell, 97 }; 98 99 static int __init sun4i_of_init(struct device_node *node, 100 struct device_node *parent) 101 { 102 sun4i_irq_base = of_iomap(node, 0); 103 if (!sun4i_irq_base) 104 panic("%s: unable to map IC registers\n", 105 node->full_name); 106 107 /* Disable all interrupts */ 108 writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0)); 109 writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); 110 writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); 111 112 /* Mask all the interrupts */ 113 writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); 114 writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); 115 writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); 116 117 /* Clear all the pending interrupts */ 118 writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); 119 writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1)); 120 writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2)); 121 122 /* Enable protection mode */ 123 writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG); 124 125 /* Configure the external interrupt source type */ 126 writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG); 127 128 sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32, 129 &sun4i_irq_ops, NULL); 130 if (!sun4i_irq_domain) 131 panic("%s: unable to create IRQ domain\n", node->full_name); 132 133 set_handle_irq(sun4i_handle_irq); 134 135 return 0; 136 } 137 IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-ic", sun4i_of_init); 138 139 static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) 140 { 141 u32 irq, hwirq; 142 143 hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; 144 while (hwirq != 0) { 145 irq = irq_find_mapping(sun4i_irq_domain, hwirq); 146 handle_IRQ(irq, regs); 147 hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; 148 } 149 } 150