xref: /openbmc/linux/arch/xtensa/kernel/irq.c (revision b02afe1df518369dd322f48b49e50efc49250575)
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