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

xtensa: move built-in PIC to drivers/irqchip

Extract xtensa built-in interrupt controller implementation from
xtensa/kernel/irq.c and move it to other irqchips, providing way to
instantiate it from the device tree.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>

authored by

Max Filippov and committed by
Chris Zankel
cbd1de2e c8f3a7dc

+165 -108
+1 -1
arch/xtensa/boot/dts/xtfpga.dtsi
··· 26 26 }; 27 27 28 28 pic: pic { 29 - compatible = "xtensa,pic"; 29 + compatible = "cdns,xtensa-pic"; 30 30 /* one cell: internal irq number, 31 31 * two cells: second cell == 0: internal irq number 32 32 * second cell == 1: external irq number
+7
arch/xtensa/include/asm/irq.h
··· 43 43 } 44 44 45 45 struct irqaction; 46 + struct irq_domain; 47 + 48 + int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, 49 + unsigned long int_irq, unsigned long ext_irq, 50 + unsigned long *out_hwirq, unsigned int *out_type); 51 + int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw); 52 + unsigned xtensa_map_ext_irq(unsigned ext_irq); 46 53 47 54 #endif /* _XTENSA_IRQ_H */
+30 -107
arch/xtensa/kernel/irq.c
··· 18 18 #include <linux/interrupt.h> 19 19 #include <linux/irq.h> 20 20 #include <linux/kernel_stat.h> 21 + #include <linux/irqchip.h> 22 + #include <linux/irqchip/xtensa-pic.h> 21 23 #include <linux/irqdomain.h> 22 24 #include <linux/of.h> 23 25 24 26 #include <asm/uaccess.h> 25 27 #include <asm/platform.h> 26 28 27 - static unsigned int cached_irq_mask; 28 - 29 29 atomic_t irq_err_count; 30 - 31 - static struct irq_domain *root_domain; 32 - 33 - /* 34 - * do_IRQ handles all normal device IRQ's (the special 35 - * SMP cross-CPU interrupts have their own specific 36 - * handlers). 37 - */ 38 30 39 31 asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) 40 32 { 41 33 struct pt_regs *old_regs = set_irq_regs(regs); 42 - int irq = irq_find_mapping(root_domain, hwirq); 34 + int irq = irq_find_mapping(NULL, hwirq); 43 35 44 36 if (hwirq >= NR_IRQS) { 45 37 printk(KERN_EMERG "%s: cannot handle IRQ %d\n", ··· 66 74 return 0; 67 75 } 68 76 69 - static void xtensa_irq_mask(struct irq_data *d) 77 + int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, 78 + unsigned long int_irq, unsigned long ext_irq, 79 + unsigned long *out_hwirq, unsigned int *out_type) 70 80 { 71 - cached_irq_mask &= ~(1 << d->hwirq); 72 - set_sr (cached_irq_mask, intenable); 81 + if (WARN_ON(intsize < 1 || intsize > 2)) 82 + return -EINVAL; 83 + if (intsize == 2 && intspec[1] == 1) { 84 + int_irq = xtensa_map_ext_irq(ext_irq); 85 + if (int_irq < XCHAL_NUM_INTERRUPTS) 86 + *out_hwirq = int_irq; 87 + else 88 + return -EINVAL; 89 + } else { 90 + *out_hwirq = int_irq; 91 + } 92 + *out_type = IRQ_TYPE_NONE; 93 + return 0; 73 94 } 74 95 75 - static void xtensa_irq_unmask(struct irq_data *d) 76 - { 77 - cached_irq_mask |= 1 << d->hwirq; 78 - set_sr (cached_irq_mask, intenable); 79 - } 80 - 81 - static void xtensa_irq_enable(struct irq_data *d) 82 - { 83 - variant_irq_enable(d->hwirq); 84 - xtensa_irq_unmask(d); 85 - } 86 - 87 - static void xtensa_irq_disable(struct irq_data *d) 88 - { 89 - xtensa_irq_mask(d); 90 - variant_irq_disable(d->hwirq); 91 - } 92 - 93 - static void xtensa_irq_ack(struct irq_data *d) 94 - { 95 - set_sr(1 << d->hwirq, intclear); 96 - } 97 - 98 - static int xtensa_irq_retrigger(struct irq_data *d) 99 - { 100 - set_sr(1 << d->hwirq, intset); 101 - return 1; 102 - } 103 - 104 - static struct irq_chip xtensa_irq_chip = { 105 - .name = "xtensa", 106 - .irq_enable = xtensa_irq_enable, 107 - .irq_disable = xtensa_irq_disable, 108 - .irq_mask = xtensa_irq_mask, 109 - .irq_unmask = xtensa_irq_unmask, 110 - .irq_ack = xtensa_irq_ack, 111 - .irq_retrigger = xtensa_irq_retrigger, 112 - }; 113 - 114 - static int xtensa_irq_map(struct irq_domain *d, unsigned int irq, 96 + int xtensa_irq_map(struct irq_domain *d, unsigned int irq, 115 97 irq_hw_number_t hw) 116 98 { 99 + struct irq_chip *irq_chip = d->host_data; 117 100 u32 mask = 1 << hw; 118 101 119 102 if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) { 120 - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, 103 + irq_set_chip_and_handler_name(irq, irq_chip, 121 104 handle_simple_irq, "level"); 122 105 irq_set_status_flags(irq, IRQ_LEVEL); 123 106 } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) { 124 - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, 107 + irq_set_chip_and_handler_name(irq, irq_chip, 125 108 handle_edge_irq, "edge"); 126 109 irq_clear_status_flags(irq, IRQ_LEVEL); 127 110 } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) { 128 - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, 111 + irq_set_chip_and_handler_name(irq, irq_chip, 129 112 handle_level_irq, "level"); 130 113 irq_set_status_flags(irq, IRQ_LEVEL); 131 114 } else if (mask & XCHAL_INTTYPE_MASK_TIMER) { 132 - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, 133 - handle_edge_irq, "edge"); 115 + irq_set_chip_and_handler_name(irq, irq_chip, 116 + handle_percpu_irq, "timer"); 134 117 irq_clear_status_flags(irq, IRQ_LEVEL); 135 118 } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */ 136 119 /* XCHAL_INTTYPE_MASK_NMI */ 137 - 138 - irq_set_chip_and_handler_name(irq, &xtensa_irq_chip, 120 + irq_set_chip_and_handler_name(irq, irq_chip, 139 121 handle_level_irq, "level"); 140 122 irq_set_status_flags(irq, IRQ_LEVEL); 141 123 } 142 124 return 0; 143 125 } 144 126 145 - static unsigned map_ext_irq(unsigned ext_irq) 127 + unsigned xtensa_map_ext_irq(unsigned ext_irq) 146 128 { 147 129 unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE | 148 130 XCHAL_INTTYPE_MASK_EXTERN_LEVEL; ··· 129 163 return XCHAL_NUM_INTERRUPTS; 130 164 } 131 165 132 - /* 133 - * Device Tree IRQ specifier translation function which works with one or 134 - * two cell bindings. First cell value maps directly to the hwirq number. 135 - * Second cell if present specifies whether hwirq number is external (1) or 136 - * internal (0). 137 - */ 138 - int xtensa_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, 139 - const u32 *intspec, unsigned int intsize, 140 - unsigned long *out_hwirq, unsigned int *out_type) 141 - { 142 - if (WARN_ON(intsize < 1 || intsize > 2)) 143 - return -EINVAL; 144 - if (intsize == 2 && intspec[1] == 1) { 145 - unsigned int_irq = map_ext_irq(intspec[0]); 146 - if (int_irq < XCHAL_NUM_INTERRUPTS) 147 - *out_hwirq = int_irq; 148 - else 149 - return -EINVAL; 150 - } else { 151 - *out_hwirq = intspec[0]; 152 - } 153 - *out_type = IRQ_TYPE_NONE; 154 - return 0; 155 - } 156 - 157 - static const struct irq_domain_ops xtensa_irq_domain_ops = { 158 - .xlate = xtensa_irq_domain_xlate, 159 - .map = xtensa_irq_map, 160 - }; 161 - 162 166 void __init init_IRQ(void) 163 167 { 164 - struct device_node *intc = NULL; 165 - 166 - cached_irq_mask = 0; 167 - set_sr(~0, intclear); 168 - 169 168 #ifdef CONFIG_OF 170 - /* The interrupt controller device node is mandatory */ 171 - intc = of_find_compatible_node(NULL, NULL, "xtensa,pic"); 172 - BUG_ON(!intc); 173 - 174 - root_domain = irq_domain_add_linear(intc, NR_IRQS, 175 - &xtensa_irq_domain_ops, NULL); 169 + irqchip_init(); 176 170 #else 177 - root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, 178 - &xtensa_irq_domain_ops, NULL); 171 + xtensa_pic_init_legacy(NULL); 179 172 #endif 180 - irq_set_default_host(root_domain); 181 - 182 173 variant_init_irq(); 183 174 }
+1
drivers/irqchip/Makefile
··· 22 22 obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 23 23 obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 24 24 obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 25 + obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
+108
drivers/irqchip/irq-xtensa-pic.c
··· 1 + /* 2 + * Xtensa built-in interrupt controller 3 + * 4 + * Copyright (C) 2002 - 2013 Tensilica, Inc. 5 + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar 6 + * 7 + * This file is subject to the terms and conditions of the GNU General Public 8 + * License. See the file "COPYING" in the main directory of this archive 9 + * for more details. 10 + * 11 + * Chris Zankel <chris@zankel.net> 12 + * Kevin Chea 13 + */ 14 + 15 + #include <linux/interrupt.h> 16 + #include <linux/irqdomain.h> 17 + #include <linux/irq.h> 18 + #include <linux/of.h> 19 + 20 + #include "irqchip.h" 21 + 22 + unsigned int cached_irq_mask; 23 + 24 + /* 25 + * Device Tree IRQ specifier translation function which works with one or 26 + * two cell bindings. First cell value maps directly to the hwirq number. 27 + * Second cell if present specifies whether hwirq number is external (1) or 28 + * internal (0). 29 + */ 30 + static int xtensa_pic_irq_domain_xlate(struct irq_domain *d, 31 + struct device_node *ctrlr, 32 + const u32 *intspec, unsigned int intsize, 33 + unsigned long *out_hwirq, unsigned int *out_type) 34 + { 35 + return xtensa_irq_domain_xlate(intspec, intsize, 36 + intspec[0], intspec[0], 37 + out_hwirq, out_type); 38 + } 39 + 40 + static const struct irq_domain_ops xtensa_irq_domain_ops = { 41 + .xlate = xtensa_pic_irq_domain_xlate, 42 + .map = xtensa_irq_map, 43 + }; 44 + 45 + static void xtensa_irq_mask(struct irq_data *d) 46 + { 47 + cached_irq_mask &= ~(1 << d->hwirq); 48 + set_sr(cached_irq_mask, intenable); 49 + } 50 + 51 + static void xtensa_irq_unmask(struct irq_data *d) 52 + { 53 + cached_irq_mask |= 1 << d->hwirq; 54 + set_sr(cached_irq_mask, intenable); 55 + } 56 + 57 + static void xtensa_irq_enable(struct irq_data *d) 58 + { 59 + variant_irq_enable(d->hwirq); 60 + xtensa_irq_unmask(d); 61 + } 62 + 63 + static void xtensa_irq_disable(struct irq_data *d) 64 + { 65 + xtensa_irq_mask(d); 66 + variant_irq_disable(d->hwirq); 67 + } 68 + 69 + static void xtensa_irq_ack(struct irq_data *d) 70 + { 71 + set_sr(1 << d->hwirq, intclear); 72 + } 73 + 74 + static int xtensa_irq_retrigger(struct irq_data *d) 75 + { 76 + set_sr(1 << d->hwirq, intset); 77 + return 1; 78 + } 79 + 80 + static struct irq_chip xtensa_irq_chip = { 81 + .name = "xtensa", 82 + .irq_enable = xtensa_irq_enable, 83 + .irq_disable = xtensa_irq_disable, 84 + .irq_mask = xtensa_irq_mask, 85 + .irq_unmask = xtensa_irq_unmask, 86 + .irq_ack = xtensa_irq_ack, 87 + .irq_retrigger = xtensa_irq_retrigger, 88 + }; 89 + 90 + int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent) 91 + { 92 + struct irq_domain *root_domain = 93 + irq_domain_add_legacy(NULL, NR_IRQS, 0, 0, 94 + &xtensa_irq_domain_ops, &xtensa_irq_chip); 95 + irq_set_default_host(root_domain); 96 + return 0; 97 + } 98 + 99 + static int __init xtensa_pic_init(struct device_node *np, 100 + struct device_node *interrupt_parent) 101 + { 102 + struct irq_domain *root_domain = 103 + irq_domain_add_linear(np, NR_IRQS, &xtensa_irq_domain_ops, 104 + &xtensa_irq_chip); 105 + irq_set_default_host(root_domain); 106 + return 0; 107 + } 108 + IRQCHIP_DECLARE(xtensa_irq_chip, "cdns,xtensa-pic", xtensa_pic_init);
+18
include/linux/irqchip/xtensa-pic.h
··· 1 + /* 2 + * Xtensa built-in interrupt controller 3 + * 4 + * Copyright (C) 2002 - 2013 Tensilica, Inc. 5 + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar 6 + * 7 + * This file is subject to the terms and conditions of the GNU General Public 8 + * License. See the file "COPYING" in the main directory of this archive 9 + * for more details. 10 + */ 11 + 12 + #ifndef __LINUX_IRQCHIP_XTENSA_PIC_H 13 + #define __LINUX_IRQCHIP_XTENSA_PIC_H 14 + 15 + struct device_node; 16 + int xtensa_pic_init_legacy(struct device_node *interrupt_parent); 17 + 18 + #endif /* __LINUX_IRQCHIP_XTENSA_PIC_H */