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

xtensa: add MX irqchip

MX is an interrupt distributor used in some SMP-capable xtensa
configurations.

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
26a8e96a bae07f8a

+261
+1
arch/xtensa/include/asm/irq.h
··· 50 50 unsigned long *out_hwirq, unsigned int *out_type); 51 51 int xtensa_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw); 52 52 unsigned xtensa_map_ext_irq(unsigned ext_irq); 53 + unsigned xtensa_get_ext_irq_no(unsigned irq); 53 54 54 55 #endif /* _XTENSA_IRQ_H */
+46
arch/xtensa/include/asm/mxregs.h
··· 1 + /* 2 + * Xtensa MX interrupt distributor 3 + * 4 + * This file is subject to the terms and conditions of the GNU General Public 5 + * License. See the file "COPYING" in the main directory of this archive 6 + * for more details. 7 + * 8 + * Copyright (C) 2008 - 2013 Tensilica Inc. 9 + */ 10 + 11 + #ifndef _XTENSA_MXREGS_H 12 + #define _XTENSA_MXREGS_H 13 + 14 + /* 15 + * RER/WER at, as Read/write external register 16 + * at: value 17 + * as: address 18 + * 19 + * Address Value 20 + * 00nn 0...0p..p Interrupt Routing, route IRQ n to processor p 21 + * 01pp 0...0d..d 16 bits (d) 'ored' as single IPI to processor p 22 + * 0180 0...0m..m Clear enable specified by mask (m) 23 + * 0184 0...0m..m Set enable specified by mask (m) 24 + * 0190 0...0x..x 8-bit IPI partition register 25 + * VVVVVVVVPPPPUUUUUUUUUUUUUUUUU 26 + * V (10-bit) Release/Version 27 + * P ( 4-bit) Number of cores - 1 28 + * U (18-bit) ID 29 + * 01a0 i.......i 32-bit ConfigID 30 + * 0200 0...0m..m RunStall core 'n' 31 + * 0220 c Cache coherency enabled 32 + */ 33 + 34 + #define MIROUT(irq) (0x000 + (irq)) 35 + #define MIPICAUSE(cpu) (0x100 + (cpu)) 36 + #define MIPISET(cause) (0x140 + (cause)) 37 + #define MIENG 0x180 38 + #define MIENGSET 0x184 39 + #define MIASG 0x188 /* Read Global Assert Register */ 40 + #define MIASGSET 0x18c /* Set Global Addert Regiter */ 41 + #define MIPIPART 0x190 42 + #define SYSCFGID 0x1a0 43 + #define MPSCORE 0x200 44 + #define CCON 0x220 45 + 46 + #endif /* _XTENSA_MXREGS_H */
+20
arch/xtensa/include/asm/processor.h
··· 191 191 #define set_sr(x,sr) ({unsigned int v=(unsigned int)x; WSR(v,sr);}) 192 192 #define get_sr(sr) ({unsigned int v; RSR(v,sr); v; }) 193 193 194 + #ifndef XCHAL_HAVE_EXTERN_REGS 195 + #define XCHAL_HAVE_EXTERN_REGS 0 196 + #endif 197 + 198 + #if XCHAL_HAVE_EXTERN_REGS 199 + 200 + static inline void set_er(unsigned long value, unsigned long addr) 201 + { 202 + asm volatile ("wer %0, %1" : : "a" (value), "a" (addr) : "memory"); 203 + } 204 + 205 + static inline unsigned long get_er(unsigned long addr) 206 + { 207 + register unsigned long value; 208 + asm volatile ("rer %0, %1" : "=a" (value) : "a" (addr) : "memory"); 209 + return value; 210 + } 211 + 212 + #endif /* XCHAL_HAVE_EXTERN_REGS */ 213 + 194 214 #endif /* __ASSEMBLY__ */ 195 215 #endif /* _XTENSA_PROCESSOR_H */
+8
arch/xtensa/kernel/irq.c
··· 123 123 return XCHAL_NUM_INTERRUPTS; 124 124 } 125 125 126 + unsigned xtensa_get_ext_irq_no(unsigned irq) 127 + { 128 + unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE | 129 + XCHAL_INTTYPE_MASK_EXTERN_LEVEL) & 130 + ((1u << irq) - 1); 131 + return hweight32(mask); 132 + } 133 + 126 134 void __init init_IRQ(void) 127 135 { 128 136 #ifdef CONFIG_OF
+4
drivers/irqchip/Kconfig
··· 61 61 int 62 62 default 4 63 63 depends on VERSATILE_FPGA_IRQ 64 + 65 + config XTENSA_MX 66 + bool 67 + select IRQ_DOMAIN
+1
drivers/irqchip/Makefile
··· 23 23 obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 24 24 obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 25 25 obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 26 + obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
+164
drivers/irqchip/irq-xtensa-mx.c
··· 1 + /* 2 + * Xtensa MX interrupt distributor 3 + * 4 + * Copyright (C) 2002 - 2013 Tensilica, Inc. 5 + * 6 + * This file is subject to the terms and conditions of the GNU General Public 7 + * License. See the file "COPYING" in the main directory of this archive 8 + * for more details. 9 + */ 10 + 11 + #include <linux/interrupt.h> 12 + #include <linux/irqdomain.h> 13 + #include <linux/irq.h> 14 + #include <linux/of.h> 15 + 16 + #include <asm/mxregs.h> 17 + 18 + #include "irqchip.h" 19 + 20 + #define HW_IRQ_IPI_COUNT 2 21 + #define HW_IRQ_MX_BASE 2 22 + #define HW_IRQ_EXTERN_BASE 3 23 + 24 + static DEFINE_PER_CPU(unsigned int, cached_irq_mask); 25 + 26 + static int xtensa_mx_irq_map(struct irq_domain *d, unsigned int irq, 27 + irq_hw_number_t hw) 28 + { 29 + if (hw < HW_IRQ_IPI_COUNT) { 30 + struct irq_chip *irq_chip = d->host_data; 31 + irq_set_chip_and_handler_name(irq, irq_chip, 32 + handle_percpu_irq, "ipi"); 33 + irq_set_status_flags(irq, IRQ_LEVEL); 34 + return 0; 35 + } 36 + return xtensa_irq_map(d, irq, hw); 37 + } 38 + 39 + /* 40 + * Device Tree IRQ specifier translation function which works with one or 41 + * two cell bindings. First cell value maps directly to the hwirq number. 42 + * Second cell if present specifies whether hwirq number is external (1) or 43 + * internal (0). 44 + */ 45 + static int xtensa_mx_irq_domain_xlate(struct irq_domain *d, 46 + struct device_node *ctrlr, 47 + const u32 *intspec, unsigned int intsize, 48 + unsigned long *out_hwirq, unsigned int *out_type) 49 + { 50 + return xtensa_irq_domain_xlate(intspec, intsize, 51 + intspec[0], intspec[0] + HW_IRQ_EXTERN_BASE, 52 + out_hwirq, out_type); 53 + } 54 + 55 + static const struct irq_domain_ops xtensa_mx_irq_domain_ops = { 56 + .xlate = xtensa_mx_irq_domain_xlate, 57 + .map = xtensa_mx_irq_map, 58 + }; 59 + 60 + void secondary_init_irq(void) 61 + { 62 + __this_cpu_write(cached_irq_mask, 63 + XCHAL_INTTYPE_MASK_EXTERN_EDGE | 64 + XCHAL_INTTYPE_MASK_EXTERN_LEVEL); 65 + set_sr(XCHAL_INTTYPE_MASK_EXTERN_EDGE | 66 + XCHAL_INTTYPE_MASK_EXTERN_LEVEL, intenable); 67 + } 68 + 69 + static void xtensa_mx_irq_mask(struct irq_data *d) 70 + { 71 + unsigned int mask = 1u << d->hwirq; 72 + 73 + if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE | 74 + XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) { 75 + set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) - 76 + HW_IRQ_MX_BASE), MIENG); 77 + } else { 78 + mask = __this_cpu_read(cached_irq_mask) & ~mask; 79 + __this_cpu_write(cached_irq_mask, mask); 80 + set_sr(mask, intenable); 81 + } 82 + } 83 + 84 + static void xtensa_mx_irq_unmask(struct irq_data *d) 85 + { 86 + unsigned int mask = 1u << d->hwirq; 87 + 88 + if (mask & (XCHAL_INTTYPE_MASK_EXTERN_EDGE | 89 + XCHAL_INTTYPE_MASK_EXTERN_LEVEL)) { 90 + set_er(1u << (xtensa_get_ext_irq_no(d->hwirq) - 91 + HW_IRQ_MX_BASE), MIENGSET); 92 + } else { 93 + mask |= __this_cpu_read(cached_irq_mask); 94 + __this_cpu_write(cached_irq_mask, mask); 95 + set_sr(mask, intenable); 96 + } 97 + } 98 + 99 + static void xtensa_mx_irq_enable(struct irq_data *d) 100 + { 101 + variant_irq_enable(d->hwirq); 102 + xtensa_mx_irq_unmask(d); 103 + } 104 + 105 + static void xtensa_mx_irq_disable(struct irq_data *d) 106 + { 107 + xtensa_mx_irq_mask(d); 108 + variant_irq_disable(d->hwirq); 109 + } 110 + 111 + static void xtensa_mx_irq_ack(struct irq_data *d) 112 + { 113 + set_sr(1 << d->hwirq, intclear); 114 + } 115 + 116 + static int xtensa_mx_irq_retrigger(struct irq_data *d) 117 + { 118 + set_sr(1 << d->hwirq, intset); 119 + return 1; 120 + } 121 + 122 + static int xtensa_mx_irq_set_affinity(struct irq_data *d, 123 + const struct cpumask *dest, bool force) 124 + { 125 + unsigned mask = 1u << cpumask_any(dest); 126 + 127 + set_er(mask, MIROUT(d->hwirq - HW_IRQ_MX_BASE)); 128 + return 0; 129 + 130 + } 131 + 132 + static struct irq_chip xtensa_mx_irq_chip = { 133 + .name = "xtensa-mx", 134 + .irq_enable = xtensa_mx_irq_enable, 135 + .irq_disable = xtensa_mx_irq_disable, 136 + .irq_mask = xtensa_mx_irq_mask, 137 + .irq_unmask = xtensa_mx_irq_unmask, 138 + .irq_ack = xtensa_mx_irq_ack, 139 + .irq_retrigger = xtensa_mx_irq_retrigger, 140 + .irq_set_affinity = xtensa_mx_irq_set_affinity, 141 + }; 142 + 143 + int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent) 144 + { 145 + struct irq_domain *root_domain = 146 + irq_domain_add_legacy(NULL, NR_IRQS, 0, 0, 147 + &xtensa_mx_irq_domain_ops, 148 + &xtensa_mx_irq_chip); 149 + irq_set_default_host(root_domain); 150 + secondary_init_irq(); 151 + return 0; 152 + } 153 + 154 + static int __init xtensa_mx_init(struct device_node *np, 155 + struct device_node *interrupt_parent) 156 + { 157 + struct irq_domain *root_domain = 158 + irq_domain_add_linear(np, NR_IRQS, &xtensa_mx_irq_domain_ops, 159 + &xtensa_mx_irq_chip); 160 + irq_set_default_host(root_domain); 161 + secondary_init_irq(); 162 + return 0; 163 + } 164 + IRQCHIP_DECLARE(xtensa_mx_irq_chip, "cdns,xtensa-mx", xtensa_mx_init);
+17
include/linux/irqchip/xtensa-mx.h
··· 1 + /* 2 + * Xtensa MX interrupt distributor 3 + * 4 + * Copyright (C) 2002 - 2013 Tensilica, Inc. 5 + * 6 + * This file is subject to the terms and conditions of the GNU General Public 7 + * License. See the file "COPYING" in the main directory of this archive 8 + * for more details. 9 + */ 10 + 11 + #ifndef __LINUX_IRQCHIP_XTENSA_MX_H 12 + #define __LINUX_IRQCHIP_XTENSA_MX_H 13 + 14 + struct device_node; 15 + int xtensa_mx_init_legacy(struct device_node *interrupt_parent); 16 + 17 + #endif /* __LINUX_IRQCHIP_XTENSA_MX_H */