linux/drivers/irqchip/irq-sun4i.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 <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
  36static void __iomem *sun4i_irq_base;
  37static struct irq_domain *sun4i_irq_domain;
  38
  39static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs);
  40
  41void 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
  53static 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
  65static 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
  77static 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
  84static 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
  94static struct irq_domain_ops sun4i_irq_ops = {
  95        .map = sun4i_irq_map,
  96        .xlate = irq_domain_xlate_onecell,
  97};
  98
  99static 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}
 137IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-ic", sun4i_of_init);
 138
 139static 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.