Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip: sunxi: Add irq controller driver

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

+282
+104
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 + };
+1
drivers/irqchip/Makefile
··· 1 1 obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o 2 + obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
+150
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 + }
+27
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