1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25a0015d6SChris Zankel /*
35a0015d6SChris Zankel * linux/arch/xtensa/kernel/irq.c
45a0015d6SChris Zankel *
55a0015d6SChris Zankel * Xtensa built-in interrupt controller and some generic functions copied
65a0015d6SChris Zankel * from i386.
75a0015d6SChris Zankel *
8f615136cSMax Filippov * Copyright (C) 2002 - 2013 Tensilica, Inc.
95a0015d6SChris Zankel * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar
105a0015d6SChris Zankel *
115a0015d6SChris Zankel *
125a0015d6SChris Zankel * Chris Zankel <chris@zankel.net>
135a0015d6SChris Zankel * Kevin Chea
145a0015d6SChris Zankel *
155a0015d6SChris Zankel */
165a0015d6SChris Zankel
175a0015d6SChris Zankel #include <linux/module.h>
185a0015d6SChris Zankel #include <linux/seq_file.h>
195a0015d6SChris Zankel #include <linux/interrupt.h>
205a0015d6SChris Zankel #include <linux/irq.h>
215a0015d6SChris Zankel #include <linux/kernel_stat.h>
22cbd1de2eSMax Filippov #include <linux/irqchip.h>
23f615136cSMax Filippov #include <linux/irqchip/xtensa-mx.h>
24cbd1de2eSMax Filippov #include <linux/irqchip/xtensa-pic.h>
252206d5ddSMax Filippov #include <linux/irqdomain.h>
26da844a81SMax Filippov #include <linux/of.h>
275a0015d6SChris Zankel
28f615136cSMax Filippov #include <asm/mxregs.h>
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
305a0015d6SChris Zankel #include <asm/platform.h>
31*4052a37aSRandy Dunlap #include <asm/traps.h>
325a0015d6SChris Zankel
3338fef73cSMax Filippov DECLARE_PER_CPU(unsigned long, nmi_count);
345a0015d6SChris Zankel
do_IRQ(int hwirq,struct pt_regs * regs)352206d5ddSMax Filippov asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
365a0015d6SChris Zankel {
375a0015d6SChris Zankel #ifdef CONFIG_DEBUG_STACKOVERFLOW
385a0015d6SChris Zankel /* Debugging check for stack overflow: is there less than 1KB free? */
395a0015d6SChris Zankel {
4092652cf9SKees Cook unsigned long sp = current_stack_pointer;
415a0015d6SChris Zankel
425a0015d6SChris Zankel sp &= THREAD_SIZE - 1;
435a0015d6SChris Zankel
445a0015d6SChris Zankel if (unlikely(sp < (sizeof(thread_info) + 1024)))
455a0015d6SChris Zankel printk("Stack overflow in do_IRQ: %ld\n",
465a0015d6SChris Zankel sp - sizeof(struct thread_info));
475a0015d6SChris Zankel }
485a0015d6SChris Zankel #endif
49d3c149b7SMarc Zyngier generic_handle_domain_irq(NULL, hwirq);
505a0015d6SChris Zankel }
515a0015d6SChris Zankel
arch_show_interrupts(struct seq_file * p,int prec)5247a5d9dcSThomas Gleixner int arch_show_interrupts(struct seq_file *p, int prec)
535a0015d6SChris Zankel {
5438fef73cSMax Filippov unsigned cpu __maybe_unused;
55f615136cSMax Filippov #ifdef CONFIG_SMP
56f615136cSMax Filippov show_ipi_list(p, prec);
57f615136cSMax Filippov #endif
5838fef73cSMax Filippov #if XTENSA_FAKE_NMI
5938fef73cSMax Filippov seq_printf(p, "%*s:", prec, "NMI");
6038fef73cSMax Filippov for_each_online_cpu(cpu)
6138fef73cSMax Filippov seq_printf(p, " %10lu", per_cpu(nmi_count, cpu));
6238fef73cSMax Filippov seq_puts(p, " Non-maskable interrupts\n");
6338fef73cSMax Filippov #endif
645a0015d6SChris Zankel return 0;
655a0015d6SChris Zankel }
665a0015d6SChris Zankel
xtensa_irq_domain_xlate(const u32 * intspec,unsigned int intsize,unsigned long int_irq,unsigned long ext_irq,unsigned long * out_hwirq,unsigned int * out_type)67cbd1de2eSMax Filippov int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize,
68cbd1de2eSMax Filippov unsigned long int_irq, unsigned long ext_irq,
69cbd1de2eSMax Filippov unsigned long *out_hwirq, unsigned int *out_type)
705a0015d6SChris Zankel {
71cbd1de2eSMax Filippov if (WARN_ON(intsize < 1 || intsize > 2))
72cbd1de2eSMax Filippov return -EINVAL;
73cbd1de2eSMax Filippov if (intsize == 2 && intspec[1] == 1) {
74cbd1de2eSMax Filippov int_irq = xtensa_map_ext_irq(ext_irq);
75cbd1de2eSMax Filippov if (int_irq < XCHAL_NUM_INTERRUPTS)
76cbd1de2eSMax Filippov *out_hwirq = int_irq;
77cbd1de2eSMax Filippov else
78cbd1de2eSMax Filippov return -EINVAL;
79cbd1de2eSMax Filippov } else {
80cbd1de2eSMax Filippov *out_hwirq = int_irq;
81cbd1de2eSMax Filippov }
82cbd1de2eSMax Filippov *out_type = IRQ_TYPE_NONE;
83cbd1de2eSMax Filippov return 0;
845a0015d6SChris Zankel }
855a0015d6SChris Zankel
xtensa_irq_map(struct irq_domain * d,unsigned int irq,irq_hw_number_t hw)86cbd1de2eSMax Filippov int xtensa_irq_map(struct irq_domain *d, unsigned int irq,
872206d5ddSMax Filippov irq_hw_number_t hw)
885a0015d6SChris Zankel {
89cbd1de2eSMax Filippov struct irq_chip *irq_chip = d->host_data;
902206d5ddSMax Filippov u32 mask = 1 << hw;
915a0015d6SChris Zankel
922206d5ddSMax Filippov if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) {
93cbd1de2eSMax Filippov irq_set_chip_and_handler_name(irq, irq_chip,
942206d5ddSMax Filippov handle_simple_irq, "level");
952206d5ddSMax Filippov irq_set_status_flags(irq, IRQ_LEVEL);
962206d5ddSMax Filippov } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) {
97cbd1de2eSMax Filippov irq_set_chip_and_handler_name(irq, irq_chip,
982206d5ddSMax Filippov handle_edge_irq, "edge");
992206d5ddSMax Filippov irq_clear_status_flags(irq, IRQ_LEVEL);
1002206d5ddSMax Filippov } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) {
101cbd1de2eSMax Filippov irq_set_chip_and_handler_name(irq, irq_chip,
1022206d5ddSMax Filippov handle_level_irq, "level");
1032206d5ddSMax Filippov irq_set_status_flags(irq, IRQ_LEVEL);
1042206d5ddSMax Filippov } else if (mask & XCHAL_INTTYPE_MASK_TIMER) {
105cbd1de2eSMax Filippov irq_set_chip_and_handler_name(irq, irq_chip,
106cbd1de2eSMax Filippov handle_percpu_irq, "timer");
1072206d5ddSMax Filippov irq_clear_status_flags(irq, IRQ_LEVEL);
108ae0b7139SMax Filippov #ifdef XCHAL_INTTYPE_MASK_PROFILING
109ae0b7139SMax Filippov } else if (mask & XCHAL_INTTYPE_MASK_PROFILING) {
110ae0b7139SMax Filippov irq_set_chip_and_handler_name(irq, irq_chip,
111ae0b7139SMax Filippov handle_percpu_irq, "profiling");
112ae0b7139SMax Filippov irq_set_status_flags(irq, IRQ_LEVEL);
113ae0b7139SMax Filippov #endif
1142206d5ddSMax Filippov } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */
115fd43fe19SChris Zankel /* XCHAL_INTTYPE_MASK_NMI */
116cbd1de2eSMax Filippov irq_set_chip_and_handler_name(irq, irq_chip,
1172206d5ddSMax Filippov handle_level_irq, "level");
1182206d5ddSMax Filippov irq_set_status_flags(irq, IRQ_LEVEL);
1192206d5ddSMax Filippov }
1202206d5ddSMax Filippov return 0;
121fd43fe19SChris Zankel }
1225a0015d6SChris Zankel
xtensa_map_ext_irq(unsigned ext_irq)123cbd1de2eSMax Filippov unsigned xtensa_map_ext_irq(unsigned ext_irq)
1242206d5ddSMax Filippov {
1252206d5ddSMax Filippov unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE |
1262206d5ddSMax Filippov XCHAL_INTTYPE_MASK_EXTERN_LEVEL;
1272206d5ddSMax Filippov unsigned i;
1282206d5ddSMax Filippov
1292206d5ddSMax Filippov for (i = 0; mask; ++i, mask >>= 1) {
1302206d5ddSMax Filippov if ((mask & 1) && ext_irq-- == 0)
1312206d5ddSMax Filippov return i;
1322206d5ddSMax Filippov }
1332206d5ddSMax Filippov return XCHAL_NUM_INTERRUPTS;
1342206d5ddSMax Filippov }
1352206d5ddSMax Filippov
xtensa_get_ext_irq_no(unsigned irq)13626a8e96aSMax Filippov unsigned xtensa_get_ext_irq_no(unsigned irq)
13726a8e96aSMax Filippov {
13826a8e96aSMax Filippov unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE |
13926a8e96aSMax Filippov XCHAL_INTTYPE_MASK_EXTERN_LEVEL) &
14026a8e96aSMax Filippov ((1u << irq) - 1);
14126a8e96aSMax Filippov return hweight32(mask);
14226a8e96aSMax Filippov }
14326a8e96aSMax Filippov
init_IRQ(void)1442206d5ddSMax Filippov void __init init_IRQ(void)
1452206d5ddSMax Filippov {
1466489f8d0SMax Filippov #ifdef CONFIG_USE_OF
147cbd1de2eSMax Filippov irqchip_init();
148da844a81SMax Filippov #else
149f615136cSMax Filippov #ifdef CONFIG_HAVE_SMP
150f615136cSMax Filippov xtensa_mx_init_legacy(NULL);
151f615136cSMax Filippov #else
152cbd1de2eSMax Filippov xtensa_pic_init_legacy(NULL);
153da844a81SMax Filippov #endif
154f615136cSMax Filippov #endif
155f615136cSMax Filippov
156f615136cSMax Filippov #ifdef CONFIG_SMP
157f615136cSMax Filippov ipi_init();
158f615136cSMax Filippov #endif
1595a0015d6SChris Zankel }
16049b424feSMax Filippov
16149b424feSMax Filippov #ifdef CONFIG_HOTPLUG_CPU
16249b424feSMax Filippov /*
16349b424feSMax Filippov * The CPU has been marked offline. Migrate IRQs off this CPU. If
16449b424feSMax Filippov * the affinity settings do not allow other CPUs, force them onto any
16549b424feSMax Filippov * available CPU.
16649b424feSMax Filippov */
migrate_irqs(void)16749b424feSMax Filippov void migrate_irqs(void)
16849b424feSMax Filippov {
16949b424feSMax Filippov unsigned int i, cpu = smp_processor_id();
17049b424feSMax Filippov
171b58d971dSThomas Gleixner for_each_active_irq(i) {
172b58d971dSThomas Gleixner struct irq_data *data = irq_get_irq_data(i);
1734d0b8298SSamuel Holland const struct cpumask *mask;
17449b424feSMax Filippov unsigned int newcpu;
17549b424feSMax Filippov
17649b424feSMax Filippov if (irqd_is_per_cpu(data))
17749b424feSMax Filippov continue;
17849b424feSMax Filippov
1791559f3b8SJiang Liu mask = irq_data_get_affinity_mask(data);
1801559f3b8SJiang Liu if (!cpumask_test_cpu(cpu, mask))
18149b424feSMax Filippov continue;
18249b424feSMax Filippov
1831559f3b8SJiang Liu newcpu = cpumask_any_and(mask, cpu_online_mask);
18449b424feSMax Filippov
18549b424feSMax Filippov if (newcpu >= nr_cpu_ids) {
18649b424feSMax Filippov pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n",
18749b424feSMax Filippov i, cpu);
18849b424feSMax Filippov
1894d0b8298SSamuel Holland irq_set_affinity(i, cpu_all_mask);
1904d0b8298SSamuel Holland } else {
1911559f3b8SJiang Liu irq_set_affinity(i, mask);
19249b424feSMax Filippov }
19349b424feSMax Filippov }
1944d0b8298SSamuel Holland }
19549b424feSMax Filippov #endif /* CONFIG_HOTPLUG_CPU */
196