162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Code to handle x86 style IRQs plus some generic interrupt stuff.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 1992 Linus Torvalds
962306a36Sopenharmony_ci * Copyright (C) 1994 - 2000 Ralf Baechle
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/ioport.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/irqchip.h>
1662306a36Sopenharmony_ci#include <linux/irqdomain.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/of_irq.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/syscore_ops.h>
2162306a36Sopenharmony_ci#include <linux/irq.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/i8259.h>
2462306a36Sopenharmony_ci#include <asm/io.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * This is the 'legacy' 8259A Programmable Interrupt Controller,
2862306a36Sopenharmony_ci * present in the majority of PC/AT boxes.
2962306a36Sopenharmony_ci * plus some generic x86 specific things if generic specifics makes
3062306a36Sopenharmony_ci * any sense at all.
3162306a36Sopenharmony_ci * this file should become arch/i386/kernel/irq.c when the old irq.c
3262306a36Sopenharmony_ci * moves to arch independent land
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int i8259A_auto_eoi = -1;
3662306a36Sopenharmony_ciDEFINE_RAW_SPINLOCK(i8259A_lock);
3762306a36Sopenharmony_cistatic void disable_8259A_irq(struct irq_data *d);
3862306a36Sopenharmony_cistatic void enable_8259A_irq(struct irq_data *d);
3962306a36Sopenharmony_cistatic void mask_and_ack_8259A(struct irq_data *d);
4062306a36Sopenharmony_cistatic void init_8259A(int auto_eoi);
4162306a36Sopenharmony_cistatic int (*i8259_poll)(void) = i8259_irq;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic struct irq_chip i8259A_chip = {
4462306a36Sopenharmony_ci	.name			= "XT-PIC",
4562306a36Sopenharmony_ci	.irq_mask		= disable_8259A_irq,
4662306a36Sopenharmony_ci	.irq_disable		= disable_8259A_irq,
4762306a36Sopenharmony_ci	.irq_unmask		= enable_8259A_irq,
4862306a36Sopenharmony_ci	.irq_mask_ack		= mask_and_ack_8259A,
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * 8259A PIC functions to handle ISA devices:
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_civoid i8259_set_poll(int (*poll)(void))
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	i8259_poll = poll;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * This contains the irq mask for both 8259A irq controllers,
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_cistatic unsigned int cached_irq_mask = 0xffff;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define cached_master_mask	(cached_irq_mask)
6662306a36Sopenharmony_ci#define cached_slave_mask	(cached_irq_mask >> 8)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void disable_8259A_irq(struct irq_data *d)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	unsigned int mask, irq = d->irq - I8259A_IRQ_BASE;
7162306a36Sopenharmony_ci	unsigned long flags;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mask = 1 << irq;
7462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&i8259A_lock, flags);
7562306a36Sopenharmony_ci	cached_irq_mask |= mask;
7662306a36Sopenharmony_ci	if (irq & 8)
7762306a36Sopenharmony_ci		outb(cached_slave_mask, PIC_SLAVE_IMR);
7862306a36Sopenharmony_ci	else
7962306a36Sopenharmony_ci		outb(cached_master_mask, PIC_MASTER_IMR);
8062306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&i8259A_lock, flags);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void enable_8259A_irq(struct irq_data *d)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	unsigned int mask, irq = d->irq - I8259A_IRQ_BASE;
8662306a36Sopenharmony_ci	unsigned long flags;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	mask = ~(1 << irq);
8962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&i8259A_lock, flags);
9062306a36Sopenharmony_ci	cached_irq_mask &= mask;
9162306a36Sopenharmony_ci	if (irq & 8)
9262306a36Sopenharmony_ci		outb(cached_slave_mask, PIC_SLAVE_IMR);
9362306a36Sopenharmony_ci	else
9462306a36Sopenharmony_ci		outb(cached_master_mask, PIC_MASTER_IMR);
9562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&i8259A_lock, flags);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_civoid make_8259A_irq(unsigned int irq)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	disable_irq_nosync(irq);
10162306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, &i8259A_chip, handle_level_irq);
10262306a36Sopenharmony_ci	enable_irq(irq);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci * This function assumes to be called rarely. Switching between
10762306a36Sopenharmony_ci * 8259A registers is slow.
10862306a36Sopenharmony_ci * This has to be protected by the irq controller spinlock
10962306a36Sopenharmony_ci * before being called.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic inline int i8259A_irq_real(unsigned int irq)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int value;
11462306a36Sopenharmony_ci	int irqmask = 1 << irq;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (irq < 8) {
11762306a36Sopenharmony_ci		outb(0x0B, PIC_MASTER_CMD);	/* ISR register */
11862306a36Sopenharmony_ci		value = inb(PIC_MASTER_CMD) & irqmask;
11962306a36Sopenharmony_ci		outb(0x0A, PIC_MASTER_CMD);	/* back to the IRR register */
12062306a36Sopenharmony_ci		return value;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	outb(0x0B, PIC_SLAVE_CMD);	/* ISR register */
12362306a36Sopenharmony_ci	value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
12462306a36Sopenharmony_ci	outb(0x0A, PIC_SLAVE_CMD);	/* back to the IRR register */
12562306a36Sopenharmony_ci	return value;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/*
12962306a36Sopenharmony_ci * Careful! The 8259A is a fragile beast, it pretty
13062306a36Sopenharmony_ci * much _has_ to be done exactly like this (mask it
13162306a36Sopenharmony_ci * first, _then_ send the EOI, and the order of EOI
13262306a36Sopenharmony_ci * to the two 8259s is important!
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistatic void mask_and_ack_8259A(struct irq_data *d)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	unsigned int irqmask, irq = d->irq - I8259A_IRQ_BASE;
13762306a36Sopenharmony_ci	unsigned long flags;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	irqmask = 1 << irq;
14062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&i8259A_lock, flags);
14162306a36Sopenharmony_ci	/*
14262306a36Sopenharmony_ci	 * Lightweight spurious IRQ detection. We do not want
14362306a36Sopenharmony_ci	 * to overdo spurious IRQ handling - it's usually a sign
14462306a36Sopenharmony_ci	 * of hardware problems, so we only do the checks we can
14562306a36Sopenharmony_ci	 * do without slowing down good hardware unnecessarily.
14662306a36Sopenharmony_ci	 *
14762306a36Sopenharmony_ci	 * Note that IRQ7 and IRQ15 (the two spurious IRQs
14862306a36Sopenharmony_ci	 * usually resulting from the 8259A-1|2 PICs) occur
14962306a36Sopenharmony_ci	 * even if the IRQ is masked in the 8259A. Thus we
15062306a36Sopenharmony_ci	 * can check spurious 8259A IRQs without doing the
15162306a36Sopenharmony_ci	 * quite slow i8259A_irq_real() call for every IRQ.
15262306a36Sopenharmony_ci	 * This does not cover 100% of spurious interrupts,
15362306a36Sopenharmony_ci	 * but should be enough to warn the user that there
15462306a36Sopenharmony_ci	 * is something bad going on ...
15562306a36Sopenharmony_ci	 */
15662306a36Sopenharmony_ci	if (cached_irq_mask & irqmask)
15762306a36Sopenharmony_ci		goto spurious_8259A_irq;
15862306a36Sopenharmony_ci	cached_irq_mask |= irqmask;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cihandle_real_irq:
16162306a36Sopenharmony_ci	if (irq & 8) {
16262306a36Sopenharmony_ci		inb(PIC_SLAVE_IMR);	/* DUMMY - (do we need this?) */
16362306a36Sopenharmony_ci		outb(cached_slave_mask, PIC_SLAVE_IMR);
16462306a36Sopenharmony_ci		outb(0x60+(irq&7), PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
16562306a36Sopenharmony_ci		outb(0x60+PIC_CASCADE_IR, PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
16662306a36Sopenharmony_ci	} else {
16762306a36Sopenharmony_ci		inb(PIC_MASTER_IMR);	/* DUMMY - (do we need this?) */
16862306a36Sopenharmony_ci		outb(cached_master_mask, PIC_MASTER_IMR);
16962306a36Sopenharmony_ci		outb(0x60+irq, PIC_MASTER_CMD); /* 'Specific EOI to master */
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&i8259A_lock, flags);
17262306a36Sopenharmony_ci	return;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cispurious_8259A_irq:
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * this is the slow path - should happen rarely.
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	if (i8259A_irq_real(irq))
17962306a36Sopenharmony_ci		/*
18062306a36Sopenharmony_ci		 * oops, the IRQ _is_ in service according to the
18162306a36Sopenharmony_ci		 * 8259A - not spurious, go handle it.
18262306a36Sopenharmony_ci		 */
18362306a36Sopenharmony_ci		goto handle_real_irq;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	{
18662306a36Sopenharmony_ci		static int spurious_irq_mask;
18762306a36Sopenharmony_ci		/*
18862306a36Sopenharmony_ci		 * At this point we can be sure the IRQ is spurious,
18962306a36Sopenharmony_ci		 * lets ACK and report it. [once per IRQ]
19062306a36Sopenharmony_ci		 */
19162306a36Sopenharmony_ci		if (!(spurious_irq_mask & irqmask)) {
19262306a36Sopenharmony_ci			printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
19362306a36Sopenharmony_ci			spurious_irq_mask |= irqmask;
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci		atomic_inc(&irq_err_count);
19662306a36Sopenharmony_ci		/*
19762306a36Sopenharmony_ci		 * Theoretically we do not have to handle this IRQ,
19862306a36Sopenharmony_ci		 * but in Linux this does not cause problems and is
19962306a36Sopenharmony_ci		 * simpler for us.
20062306a36Sopenharmony_ci		 */
20162306a36Sopenharmony_ci		goto handle_real_irq;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic void i8259A_resume(void)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	if (i8259A_auto_eoi >= 0)
20862306a36Sopenharmony_ci		init_8259A(i8259A_auto_eoi);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void i8259A_shutdown(void)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	/* Put the i8259A into a quiescent state that
21462306a36Sopenharmony_ci	 * the kernel initialization code can get it
21562306a36Sopenharmony_ci	 * out of.
21662306a36Sopenharmony_ci	 */
21762306a36Sopenharmony_ci	if (i8259A_auto_eoi >= 0) {
21862306a36Sopenharmony_ci		outb(0xff, PIC_MASTER_IMR);	/* mask all of 8259A-1 */
21962306a36Sopenharmony_ci		outb(0xff, PIC_SLAVE_IMR);	/* mask all of 8259A-2 */
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic struct syscore_ops i8259_syscore_ops = {
22462306a36Sopenharmony_ci	.resume = i8259A_resume,
22562306a36Sopenharmony_ci	.shutdown = i8259A_shutdown,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void init_8259A(int auto_eoi)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	unsigned long flags;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	i8259A_auto_eoi = auto_eoi;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&i8259A_lock, flags);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	outb(0xff, PIC_MASTER_IMR);	/* mask all of 8259A-1 */
23762306a36Sopenharmony_ci	outb(0xff, PIC_SLAVE_IMR);	/* mask all of 8259A-2 */
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/*
24062306a36Sopenharmony_ci	 * outb_p - this has to work on a wide range of PC hardware.
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	outb_p(0x11, PIC_MASTER_CMD);	/* ICW1: select 8259A-1 init */
24362306a36Sopenharmony_ci	outb_p(I8259A_IRQ_BASE + 0, PIC_MASTER_IMR);	/* ICW2: 8259A-1 IR0 mapped to I8259A_IRQ_BASE + 0x00 */
24462306a36Sopenharmony_ci	outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);	/* 8259A-1 (the master) has a slave on IR2 */
24562306a36Sopenharmony_ci	if (auto_eoi)	/* master does Auto EOI */
24662306a36Sopenharmony_ci		outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
24762306a36Sopenharmony_ci	else		/* master expects normal EOI */
24862306a36Sopenharmony_ci		outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	outb_p(0x11, PIC_SLAVE_CMD);	/* ICW1: select 8259A-2 init */
25162306a36Sopenharmony_ci	outb_p(I8259A_IRQ_BASE + 8, PIC_SLAVE_IMR);	/* ICW2: 8259A-2 IR0 mapped to I8259A_IRQ_BASE + 0x08 */
25262306a36Sopenharmony_ci	outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR);	/* 8259A-2 is a slave on master's IR2 */
25362306a36Sopenharmony_ci	outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
25462306a36Sopenharmony_ci	if (auto_eoi)
25562306a36Sopenharmony_ci		/*
25662306a36Sopenharmony_ci		 * In AEOI mode we just have to mask the interrupt
25762306a36Sopenharmony_ci		 * when acking.
25862306a36Sopenharmony_ci		 */
25962306a36Sopenharmony_ci		i8259A_chip.irq_mask_ack = disable_8259A_irq;
26062306a36Sopenharmony_ci	else
26162306a36Sopenharmony_ci		i8259A_chip.irq_mask_ack = mask_and_ack_8259A;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	udelay(100);		/* wait for 8259A to initialize */
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
26662306a36Sopenharmony_ci	outb(cached_slave_mask, PIC_SLAVE_IMR);	  /* restore slave IRQ mask */
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&i8259A_lock, flags);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic struct resource pic1_io_resource = {
27262306a36Sopenharmony_ci	.name = "pic1",
27362306a36Sopenharmony_ci	.start = PIC_MASTER_CMD,
27462306a36Sopenharmony_ci	.end = PIC_MASTER_IMR,
27562306a36Sopenharmony_ci	.flags = IORESOURCE_IO | IORESOURCE_BUSY
27662306a36Sopenharmony_ci};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct resource pic2_io_resource = {
27962306a36Sopenharmony_ci	.name = "pic2",
28062306a36Sopenharmony_ci	.start = PIC_SLAVE_CMD,
28162306a36Sopenharmony_ci	.end = PIC_SLAVE_IMR,
28262306a36Sopenharmony_ci	.flags = IORESOURCE_IO | IORESOURCE_BUSY
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int i8259A_irq_domain_map(struct irq_domain *d, unsigned int virq,
28662306a36Sopenharmony_ci				 irq_hw_number_t hw)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	irq_set_chip_and_handler(virq, &i8259A_chip, handle_level_irq);
28962306a36Sopenharmony_ci	irq_set_probe(virq);
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic const struct irq_domain_ops i8259A_ops = {
29462306a36Sopenharmony_ci	.map = i8259A_irq_domain_map,
29562306a36Sopenharmony_ci	.xlate = irq_domain_xlate_onecell,
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/*
29962306a36Sopenharmony_ci * On systems with i8259-style interrupt controllers we assume for
30062306a36Sopenharmony_ci * driver compatibility reasons interrupts 0 - 15 to be the i8259
30162306a36Sopenharmony_ci * interrupts even if the hardware uses a different interrupt numbering.
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_cistruct irq_domain * __init __init_i8259_irqs(struct device_node *node)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	/*
30662306a36Sopenharmony_ci	 * PIC_CASCADE_IR is cascade interrupt to second interrupt controller
30762306a36Sopenharmony_ci	 */
30862306a36Sopenharmony_ci	int irq = I8259A_IRQ_BASE + PIC_CASCADE_IR;
30962306a36Sopenharmony_ci	struct irq_domain *domain;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	insert_resource(&ioport_resource, &pic1_io_resource);
31262306a36Sopenharmony_ci	insert_resource(&ioport_resource, &pic2_io_resource);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	init_8259A(0);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	domain = irq_domain_add_legacy(node, 16, I8259A_IRQ_BASE, 0,
31762306a36Sopenharmony_ci				       &i8259A_ops, NULL);
31862306a36Sopenharmony_ci	if (!domain)
31962306a36Sopenharmony_ci		panic("Failed to add i8259 IRQ domain");
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade", NULL))
32262306a36Sopenharmony_ci		pr_err("Failed to register cascade interrupt\n");
32362306a36Sopenharmony_ci	register_syscore_ops(&i8259_syscore_ops);
32462306a36Sopenharmony_ci	return domain;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_civoid __init init_i8259_irqs(void)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	__init_i8259_irqs(NULL);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void i8259_irq_dispatch(struct irq_desc *desc)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct irq_domain *domain = irq_desc_get_handler_data(desc);
33562306a36Sopenharmony_ci	int hwirq = i8259_poll();
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (hwirq < 0)
33862306a36Sopenharmony_ci		return;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	generic_handle_domain_irq(domain, hwirq);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int __init i8259_of_init(struct device_node *node, struct device_node *parent)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct irq_domain *domain;
34662306a36Sopenharmony_ci	unsigned int parent_irq;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	domain = __init_i8259_irqs(node);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	parent_irq = irq_of_parse_and_map(node, 0);
35162306a36Sopenharmony_ci	if (!parent_irq) {
35262306a36Sopenharmony_ci		pr_err("Failed to map i8259 parent IRQ\n");
35362306a36Sopenharmony_ci		irq_domain_remove(domain);
35462306a36Sopenharmony_ci		return -ENODEV;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	irq_set_chained_handler_and_data(parent_irq, i8259_irq_dispatch,
35862306a36Sopenharmony_ci					 domain);
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ciIRQCHIP_DECLARE(i8259, "intel,i8259", i8259_of_init);
362