···11+/*22+ * Allwinner A1X SoCs IRQ chip driver.33+ *44+ * Copyright (C) 2012 Maxime Ripard55+ *66+ * Maxime Ripard <maxime.ripard@free-electrons.com>77+ *88+ * Based on code from99+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>1010+ * Benn Huang <benn@allwinnertech.com>1111+ *1212+ * This file is licensed under the terms of the GNU General Public1313+ * License version 2. This program is licensed "as is" without any1414+ * warranty of any kind, whether express or implied.1515+ */1616+1717+#include <linux/io.h>1818+#include <linux/irq.h>1919+#include <linux/of.h>2020+#include <linux/of_address.h>2121+#include <linux/of_irq.h>2222+2323+#include <linux/irqchip/sunxi.h>2424+2525+#define SUNXI_IRQ_VECTOR_REG 0x002626+#define SUNXI_IRQ_PROTECTION_REG 0x082727+#define SUNXI_IRQ_NMI_CTRL_REG 0x0c2828+#define SUNXI_IRQ_PENDING_REG(x) (0x10 + 0x4 * x)2929+#define SUNXI_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x)3030+#define SUNXI_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x)3131+#define SUNXI_IRQ_MASK_REG(x) (0x50 + 0x4 * x)3232+3333+static void __iomem *sunxi_irq_base;3434+static struct irq_domain *sunxi_irq_domain;3535+3636+void sunxi_irq_ack(struct irq_data *irqd)3737+{3838+ unsigned int irq = irqd_to_hwirq(irqd);3939+ unsigned int irq_off = irq % 32;4040+ int reg = irq / 32;4141+ u32 val;4242+4343+ val = readl(sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg));4444+ writel(val | (1 << irq_off),4545+ sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg));4646+}4747+4848+static void sunxi_irq_mask(struct irq_data *irqd)4949+{5050+ unsigned int irq = irqd_to_hwirq(irqd);5151+ unsigned int irq_off = irq % 32;5252+ int reg = irq / 32;5353+ u32 val;5454+5555+ val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));5656+ writel(val & ~(1 << irq_off),5757+ sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));5858+}5959+6060+static void sunxi_irq_unmask(struct irq_data *irqd)6161+{6262+ unsigned int irq = irqd_to_hwirq(irqd);6363+ unsigned int irq_off = irq % 32;6464+ int reg = irq / 32;6565+ u32 val;6666+6767+ val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));6868+ writel(val | (1 << irq_off),6969+ sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));7070+}7171+7272+static struct irq_chip sunxi_irq_chip = {7373+ .name = "sunxi_irq",7474+ .irq_ack = sunxi_irq_ack,7575+ .irq_mask = sunxi_irq_mask,7676+ .irq_unmask = sunxi_irq_unmask,7777+};7878+7979+static int sunxi_irq_map(struct irq_domain *d, unsigned int virq,8080+ irq_hw_number_t hw)8181+{8282+ irq_set_chip_and_handler(virq, &sunxi_irq_chip,8383+ handle_level_irq);8484+ set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);8585+8686+ return 0;8787+}8888+8989+static struct irq_domain_ops sunxi_irq_ops = {9090+ .map = sunxi_irq_map,9191+ .xlate = irq_domain_xlate_onecell,9292+};9393+9494+static int __init sunxi_of_init(struct device_node *node,9595+ struct device_node *parent)9696+{9797+ sunxi_irq_base = of_iomap(node, 0);9898+ if (!sunxi_irq_base)9999+ panic("%s: unable to map IC registers\n",100100+ node->full_name);101101+102102+ /* Disable all interrupts */103103+ writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(0));104104+ writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(1));105105+ writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(2));106106+107107+ /* Mask all the interrupts */108108+ writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(0));109109+ writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(1));110110+ writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(2));111111+112112+ /* Clear all the pending interrupts */113113+ writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(0));114114+ writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(1));115115+ writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(2));116116+117117+ /* Enable protection mode */118118+ writel(0x01, sunxi_irq_base + SUNXI_IRQ_PROTECTION_REG);119119+120120+ /* Configure the external interrupt source type */121121+ writel(0x00, sunxi_irq_base + SUNXI_IRQ_NMI_CTRL_REG);122122+123123+ sunxi_irq_domain = irq_domain_add_linear(node, 3 * 32,124124+ &sunxi_irq_ops, NULL);125125+ if (!sunxi_irq_domain)126126+ panic("%s: unable to create IRQ domain\n", node->full_name);127127+128128+ return 0;129129+}130130+131131+static struct of_device_id sunxi_irq_dt_ids[] __initconst = {132132+ { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init }133133+};134134+135135+void __init sunxi_init_irq(void)136136+{137137+ of_irq_init(sunxi_irq_dt_ids);138138+}139139+140140+asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs)141141+{142142+ u32 irq, hwirq;143143+144144+ hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2;145145+ while (hwirq != 0) {146146+ irq = irq_find_mapping(sunxi_irq_domain, hwirq);147147+ handle_IRQ(irq, regs);148148+ hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2;149149+ }150150+}
+27
include/linux/irqchip/sunxi.h
···11+/*22+ * Copyright 2012 Maxime Ripard33+ *44+ * Maxime Ripard <maxime.ripard@free-electrons.com>55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; either version 2 of the License, or99+ * (at your option) any later version.1010+ *1111+ * This program is distributed in the hope that it will be useful,1212+ * but WITHOUT ANY WARRANTY; without even the implied warranty of1313+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1414+ * GNU General Public License for more details.1515+ */1616+1717+#ifndef __LINUX_IRQCHIP_SUNXI_H1818+#define __LINUX_IRQCHIP_SUNXI_H1919+2020+#include <asm/exception.h>2121+2222+extern void sunxi_init_irq(void);2323+2424+extern asmlinkage void __exception_irq_entry sunxi_handle_irq(2525+ struct pt_regs *regs);2626+2727+#endif