linux/drivers/irqchip/irq-sunxi.c
<<
>>
Prefs
   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 <linux/irqchip/sunxi.h>
  24
  25#define SUNXI_IRQ_VECTOR_REG            0x00
  26#define SUNXI_IRQ_PROTECTION_REG        0x08
  27#define SUNXI_IRQ_NMI_CTRL_REG          0x0c
  28#define SUNXI_IRQ_PENDING_REG(x)        (0x10 + 0x4 * x)
  29#define SUNXI_IRQ_FIQ_PENDING_REG(x)    (0x20 + 0x4 * x)
  30#define SUNXI_IRQ_ENABLE_REG(x)         (0x40 + 0x4 * x)
  31#define SUNXI_IRQ_MASK_REG(x)           (0x50 + 0x4 * x)
  32
  33static void __iomem *sunxi_irq_base;
  34static struct irq_domain *sunxi_irq_domain;
  35
  36void sunxi_irq_ack(struct irq_data *irqd)
  37{
  38        unsigned int irq = irqd_to_hwirq(irqd);
  39        unsigned int irq_off = irq % 32;
  40        int reg = irq / 32;
  41        u32 val;
  42
  43        val = readl(sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg));
  44        writel(val | (1 << irq_off),
  45               sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg));
  46}
  47
  48static void sunxi_irq_mask(struct irq_data *irqd)
  49{
  50        unsigned int irq = irqd_to_hwirq(irqd);
  51        unsigned int irq_off = irq % 32;
  52        int reg = irq / 32;
  53        u32 val;
  54
  55        val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));
  56        writel(val & ~(1 << irq_off),
  57               sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));
  58}
  59
  60static void sunxi_irq_unmask(struct irq_data *irqd)
  61{
  62        unsigned int irq = irqd_to_hwirq(irqd);
  63        unsigned int irq_off = irq % 32;
  64        int reg = irq / 32;
  65        u32 val;
  66
  67        val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));
  68        writel(val | (1 << irq_off),
  69               sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg));
  70}
  71
  72static struct irq_chip sunxi_irq_chip = {
  73        .name           = "sunxi_irq",
  74        .irq_ack        = sunxi_irq_ack,
  75        .irq_mask       = sunxi_irq_mask,
  76        .irq_unmask     = sunxi_irq_unmask,
  77};
  78
  79static int sunxi_irq_map(struct irq_domain *d, unsigned int virq,
  80                         irq_hw_number_t hw)
  81{
  82        irq_set_chip_and_handler(virq, &sunxi_irq_chip,
  83                                 handle_level_irq);
  84        set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
  85
  86        return 0;
  87}
  88
  89static struct irq_domain_ops sunxi_irq_ops = {
  90        .map = sunxi_irq_map,
  91        .xlate = irq_domain_xlate_onecell,
  92};
  93
  94static int __init sunxi_of_init(struct device_node *node,
  95                                struct device_node *parent)
  96{
  97        sunxi_irq_base = of_iomap(node, 0);
  98        if (!sunxi_irq_base)
  99                panic("%s: unable to map IC registers\n",
 100                        node->full_name);
 101
 102        /* Disable all interrupts */
 103        writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(0));
 104        writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(1));
 105        writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(2));
 106
 107        /* Mask all the interrupts */
 108        writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(0));
 109        writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(1));
 110        writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(2));
 111
 112        /* Clear all the pending interrupts */
 113        writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(0));
 114        writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(1));
 115        writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(2));
 116
 117        /* Enable protection mode */
 118        writel(0x01, sunxi_irq_base + SUNXI_IRQ_PROTECTION_REG);
 119
 120        /* Configure the external interrupt source type */
 121        writel(0x00, sunxi_irq_base + SUNXI_IRQ_NMI_CTRL_REG);
 122
 123        sunxi_irq_domain = irq_domain_add_linear(node, 3 * 32,
 124                                                 &sunxi_irq_ops, NULL);
 125        if (!sunxi_irq_domain)
 126                panic("%s: unable to create IRQ domain\n", node->full_name);
 127
 128        return 0;
 129}
 130
 131static struct of_device_id sunxi_irq_dt_ids[] __initconst = {
 132        { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init },
 133        { }
 134};
 135
 136void __init sunxi_init_irq(void)
 137{
 138        of_irq_init(sunxi_irq_dt_ids);
 139}
 140
 141asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs)
 142{
 143        u32 irq, hwirq;
 144
 145        hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2;
 146        while (hwirq != 0) {
 147                irq = irq_find_mapping(sunxi_irq_domain, hwirq);
 148                handle_IRQ(irq, regs);
 149                hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2;
 150        }
 151}
 152
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.