Commit afd24e146826cec0f46929263a0c874406a19cd8

Authored by Maxime Ripard
1 parent b2ac5d7549

irqchip: sunxi: Add irq controller driver

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
CC: Thomas Gleixner <tglx@linutronix.de>

Showing 4 changed files with 282 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt
  1 +Allwinner Sunxi Interrupt Controller
  2 +
  3 +Required properties:
  4 +
  5 +- compatible : should be "allwinner,sunxi-ic"
  6 +- reg : Specifies base physical address and size of the registers.
  7 +- interrupt-controller : Identifies the node as an interrupt controller
  8 +- #interrupt-cells : Specifies the number of cells needed to encode an
  9 + interrupt source. The value shall be 1.
  10 +
  11 +The interrupt sources are as follows:
  12 +
  13 +0: ENMI
  14 +1: UART0
  15 +2: UART1
  16 +3: UART2
  17 +4: UART3
  18 +5: IR0
  19 +6: IR1
  20 +7: I2C0
  21 +8: I2C1
  22 +9: I2C2
  23 +10: SPI0
  24 +11: SPI1
  25 +12: SPI2
  26 +13: SPDIF
  27 +14: AC97
  28 +15: TS
  29 +16: I2S
  30 +17: UART4
  31 +18: UART5
  32 +19: UART6
  33 +20: UART7
  34 +21: KEYPAD
  35 +22: TIMER0
  36 +23: TIMER1
  37 +24: TIMER2
  38 +25: TIMER3
  39 +26: CAN
  40 +27: DMA
  41 +28: PIO
  42 +29: TOUCH_PANEL
  43 +30: AUDIO_CODEC
  44 +31: LRADC
  45 +32: SDMC0
  46 +33: SDMC1
  47 +34: SDMC2
  48 +35: SDMC3
  49 +36: MEMSTICK
  50 +37: NAND
  51 +38: USB0
  52 +39: USB1
  53 +40: USB2
  54 +41: SCR
  55 +42: CSI0
  56 +43: CSI1
  57 +44: LCDCTRL0
  58 +45: LCDCTRL1
  59 +46: MP
  60 +47: DEFEBE0
  61 +48: DEFEBE1
  62 +49: PMU
  63 +50: SPI3
  64 +51: TZASC
  65 +52: PATA
  66 +53: VE
  67 +54: SS
  68 +55: EMAC
  69 +56: SATA
  70 +57: GPS
  71 +58: HDMI
  72 +59: TVE
  73 +60: ACE
  74 +61: TVD
  75 +62: PS2_0
  76 +63: PS2_1
  77 +64: USB3
  78 +65: USB4
  79 +66: PLE_PFM
  80 +67: TIMER4
  81 +68: TIMER5
  82 +69: GPU_GP
  83 +70: GPU_GPMMU
  84 +71: GPU_PP0
  85 +72: GPU_PPMMU0
  86 +73: GPU_PMU
  87 +74: GPU_RSV0
  88 +75: GPU_RSV1
  89 +76: GPU_RSV2
  90 +77: GPU_RSV3
  91 +78: GPU_RSV4
  92 +79: GPU_RSV5
  93 +80: GPU_RSV6
  94 +82: SYNC_TIMER0
  95 +83: SYNC_TIMER1
  96 +
  97 +Example:
  98 +
  99 +intc: interrupt-controller {
  100 + compatible = "allwinner,sunxi-ic";
  101 + reg = <0x01c20400 0x400>;
  102 + interrupt-controller;
  103 + #interrupt-cells = <2>;
  104 +};
drivers/irqchip/Makefile
1 1 obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
  2 +obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
drivers/irqchip/irq-sunxi.c
  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 +
  33 +static void __iomem *sunxi_irq_base;
  34 +static struct irq_domain *sunxi_irq_domain;
  35 +
  36 +void 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 +
  48 +static 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 +
  60 +static 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 +
  72 +static 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 +
  79 +static 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 +
  89 +static struct irq_domain_ops sunxi_irq_ops = {
  90 + .map = sunxi_irq_map,
  91 + .xlate = irq_domain_xlate_onecell,
  92 +};
  93 +
  94 +static 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 +
  131 +static struct of_device_id sunxi_irq_dt_ids[] __initconst = {
  132 + { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init }
  133 +};
  134 +
  135 +void __init sunxi_init_irq(void)
  136 +{
  137 + of_irq_init(sunxi_irq_dt_ids);
  138 +}
  139 +
  140 +asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs)
  141 +{
  142 + u32 irq, hwirq;
  143 +
  144 + hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2;
  145 + while (hwirq != 0) {
  146 + irq = irq_find_mapping(sunxi_irq_domain, hwirq);
  147 + handle_IRQ(irq, regs);
  148 + hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2;
  149 + }
  150 +}
include/linux/irqchip/sunxi.h
  1 +/*
  2 + * Copyright 2012 Maxime Ripard
  3 + *
  4 + * Maxime Ripard <maxime.ripard@free-electrons.com>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; either version 2 of the License, or
  9 + * (at your option) any later version.
  10 + *
  11 + * This program is distributed in the hope that it will be useful,
  12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 + * GNU General Public License for more details.
  15 + */
  16 +
  17 +#ifndef __LINUX_IRQCHIP_SUNXI_H
  18 +#define __LINUX_IRQCHIP_SUNXI_H
  19 +
  20 +#include <asm/exception.h>
  21 +
  22 +extern void sunxi_init_irq(void);
  23 +
  24 +extern asmlinkage void __exception_irq_entry sunxi_handle_irq(
  25 + struct pt_regs *regs);
  26 +
  27 +#endif