xref: /kernel/linux/linux-6.6/arch/mips/sni/rm200.c (revision 62306a36)
162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * RM200 specific code
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
562306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
662306a36Sopenharmony_ci * for more details.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * i8259 parts ripped out of arch/mips/kernel/i8259.c
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/irq.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/serial_8250.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <asm/sni.h>
2262306a36Sopenharmony_ci#include <asm/time.h>
2362306a36Sopenharmony_ci#include <asm/irq_cpu.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define RM200_I8259A_IRQ_BASE 32
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define MEMPORT(_base,_irq)				\
2862306a36Sopenharmony_ci	{						\
2962306a36Sopenharmony_ci		.mapbase	= _base,		\
3062306a36Sopenharmony_ci		.irq		= _irq,			\
3162306a36Sopenharmony_ci		.uartclk	= 1843200,		\
3262306a36Sopenharmony_ci		.iotype		= UPIO_MEM,		\
3362306a36Sopenharmony_ci		.flags		= UPF_BOOT_AUTOCONF|UPF_IOREMAP, \
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct plat_serial8250_port rm200_data[] = {
3762306a36Sopenharmony_ci	MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
3862306a36Sopenharmony_ci	MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
3962306a36Sopenharmony_ci	{ },
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic struct platform_device rm200_serial8250_device = {
4362306a36Sopenharmony_ci	.name			= "serial8250",
4462306a36Sopenharmony_ci	.id			= PLAT8250_DEV_PLATFORM,
4562306a36Sopenharmony_ci	.dev			= {
4662306a36Sopenharmony_ci		.platform_data	= rm200_data,
4762306a36Sopenharmony_ci	},
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct resource rm200_ds1216_rsrc[] = {
5162306a36Sopenharmony_ci	{
5262306a36Sopenharmony_ci		.start = 0x1cd41ffc,
5362306a36Sopenharmony_ci		.end   = 0x1cd41fff,
5462306a36Sopenharmony_ci		.flags = IORESOURCE_MEM
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic struct platform_device rm200_ds1216_device = {
5962306a36Sopenharmony_ci	.name		= "rtc-ds1216",
6062306a36Sopenharmony_ci	.num_resources	= ARRAY_SIZE(rm200_ds1216_rsrc),
6162306a36Sopenharmony_ci	.resource	= rm200_ds1216_rsrc
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct resource snirm_82596_rm200_rsrc[] = {
6562306a36Sopenharmony_ci	{
6662306a36Sopenharmony_ci		.start = 0x18000000,
6762306a36Sopenharmony_ci		.end   = 0x180fffff,
6862306a36Sopenharmony_ci		.flags = IORESOURCE_MEM
6962306a36Sopenharmony_ci	},
7062306a36Sopenharmony_ci	{
7162306a36Sopenharmony_ci		.start = 0x1b000000,
7262306a36Sopenharmony_ci		.end   = 0x1b000004,
7362306a36Sopenharmony_ci		.flags = IORESOURCE_MEM
7462306a36Sopenharmony_ci	},
7562306a36Sopenharmony_ci	{
7662306a36Sopenharmony_ci		.start = 0x1ff00000,
7762306a36Sopenharmony_ci		.end   = 0x1ff00020,
7862306a36Sopenharmony_ci		.flags = IORESOURCE_MEM
7962306a36Sopenharmony_ci	},
8062306a36Sopenharmony_ci	{
8162306a36Sopenharmony_ci		.start = 27,
8262306a36Sopenharmony_ci		.end   = 27,
8362306a36Sopenharmony_ci		.flags = IORESOURCE_IRQ
8462306a36Sopenharmony_ci	},
8562306a36Sopenharmony_ci	{
8662306a36Sopenharmony_ci		.flags = 0x00
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct platform_device snirm_82596_rm200_pdev = {
9162306a36Sopenharmony_ci	.name		= "snirm_82596",
9262306a36Sopenharmony_ci	.num_resources	= ARRAY_SIZE(snirm_82596_rm200_rsrc),
9362306a36Sopenharmony_ci	.resource	= snirm_82596_rm200_rsrc
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic struct resource snirm_53c710_rm200_rsrc[] = {
9762306a36Sopenharmony_ci	{
9862306a36Sopenharmony_ci		.start = 0x19000000,
9962306a36Sopenharmony_ci		.end   = 0x190fffff,
10062306a36Sopenharmony_ci		.flags = IORESOURCE_MEM
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci	{
10362306a36Sopenharmony_ci		.start = 26,
10462306a36Sopenharmony_ci		.end   = 26,
10562306a36Sopenharmony_ci		.flags = IORESOURCE_IRQ
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic struct platform_device snirm_53c710_rm200_pdev = {
11062306a36Sopenharmony_ci	.name		= "snirm_53c710",
11162306a36Sopenharmony_ci	.num_resources	= ARRAY_SIZE(snirm_53c710_rm200_rsrc),
11262306a36Sopenharmony_ci	.resource	= snirm_53c710_rm200_rsrc
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int __init snirm_setup_devinit(void)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	if (sni_brd_type == SNI_BRD_RM200) {
11862306a36Sopenharmony_ci		platform_device_register(&rm200_serial8250_device);
11962306a36Sopenharmony_ci		platform_device_register(&rm200_ds1216_device);
12062306a36Sopenharmony_ci		platform_device_register(&snirm_82596_rm200_pdev);
12162306a36Sopenharmony_ci		platform_device_register(&snirm_53c710_rm200_pdev);
12262306a36Sopenharmony_ci		sni_eisa_root_init();
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cidevice_initcall(snirm_setup_devinit);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * RM200 has an ISA and an EISA bus. The iSA bus is only used
13162306a36Sopenharmony_ci * for onboard devices and also has twi i8259 PICs. Since these
13262306a36Sopenharmony_ci * PICs are no accessible via inb/outb the following code uses
13362306a36Sopenharmony_ci * readb/writeb to access them
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(sni_rm200_i8259A_lock);
13762306a36Sopenharmony_ci#define PIC_CMD	   0x00
13862306a36Sopenharmony_ci#define PIC_IMR	   0x01
13962306a36Sopenharmony_ci#define PIC_ISR	   PIC_CMD
14062306a36Sopenharmony_ci#define PIC_POLL   PIC_ISR
14162306a36Sopenharmony_ci#define PIC_OCW3   PIC_ISR
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* i8259A PIC related value */
14462306a36Sopenharmony_ci#define PIC_CASCADE_IR		2
14562306a36Sopenharmony_ci#define MASTER_ICW4_DEFAULT	0x01
14662306a36Sopenharmony_ci#define SLAVE_ICW4_DEFAULT	0x01
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci * This contains the irq mask for both 8259A irq controllers,
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic unsigned int rm200_cached_irq_mask = 0xffff;
15262306a36Sopenharmony_cistatic __iomem u8 *rm200_pic_master;
15362306a36Sopenharmony_cistatic __iomem u8 *rm200_pic_slave;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#define cached_master_mask	(rm200_cached_irq_mask)
15662306a36Sopenharmony_ci#define cached_slave_mask	(rm200_cached_irq_mask >> 8)
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void sni_rm200_disable_8259A_irq(struct irq_data *d)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE;
16162306a36Sopenharmony_ci	unsigned long flags;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	mask = 1 << irq;
16462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
16562306a36Sopenharmony_ci	rm200_cached_irq_mask |= mask;
16662306a36Sopenharmony_ci	if (irq & 8)
16762306a36Sopenharmony_ci		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
16862306a36Sopenharmony_ci	else
16962306a36Sopenharmony_ci		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
17062306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void sni_rm200_enable_8259A_irq(struct irq_data *d)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE;
17662306a36Sopenharmony_ci	unsigned long flags;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	mask = ~(1 << irq);
17962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
18062306a36Sopenharmony_ci	rm200_cached_irq_mask &= mask;
18162306a36Sopenharmony_ci	if (irq & 8)
18262306a36Sopenharmony_ci		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
18362306a36Sopenharmony_ci	else
18462306a36Sopenharmony_ci		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
18562306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic inline int sni_rm200_i8259A_irq_real(unsigned int irq)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int value;
19162306a36Sopenharmony_ci	int irqmask = 1 << irq;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (irq < 8) {
19462306a36Sopenharmony_ci		writeb(0x0B, rm200_pic_master + PIC_CMD);
19562306a36Sopenharmony_ci		value = readb(rm200_pic_master + PIC_CMD) & irqmask;
19662306a36Sopenharmony_ci		writeb(0x0A, rm200_pic_master + PIC_CMD);
19762306a36Sopenharmony_ci		return value;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	writeb(0x0B, rm200_pic_slave + PIC_CMD); /* ISR register */
20062306a36Sopenharmony_ci	value = readb(rm200_pic_slave + PIC_CMD) & (irqmask >> 8);
20162306a36Sopenharmony_ci	writeb(0x0A, rm200_pic_slave + PIC_CMD);
20262306a36Sopenharmony_ci	return value;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/*
20662306a36Sopenharmony_ci * Careful! The 8259A is a fragile beast, it pretty
20762306a36Sopenharmony_ci * much _has_ to be done exactly like this (mask it
20862306a36Sopenharmony_ci * first, _then_ send the EOI, and the order of EOI
20962306a36Sopenharmony_ci * to the two 8259s is important!
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_civoid sni_rm200_mask_and_ack_8259A(struct irq_data *d)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	unsigned int irqmask, irq = d->irq - RM200_I8259A_IRQ_BASE;
21462306a36Sopenharmony_ci	unsigned long flags;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	irqmask = 1 << irq;
21762306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
21862306a36Sopenharmony_ci	/*
21962306a36Sopenharmony_ci	 * Lightweight spurious IRQ detection. We do not want
22062306a36Sopenharmony_ci	 * to overdo spurious IRQ handling - it's usually a sign
22162306a36Sopenharmony_ci	 * of hardware problems, so we only do the checks we can
22262306a36Sopenharmony_ci	 * do without slowing down good hardware unnecessarily.
22362306a36Sopenharmony_ci	 *
22462306a36Sopenharmony_ci	 * Note that IRQ7 and IRQ15 (the two spurious IRQs
22562306a36Sopenharmony_ci	 * usually resulting from the 8259A-1|2 PICs) occur
22662306a36Sopenharmony_ci	 * even if the IRQ is masked in the 8259A. Thus we
22762306a36Sopenharmony_ci	 * can check spurious 8259A IRQs without doing the
22862306a36Sopenharmony_ci	 * quite slow i8259A_irq_real() call for every IRQ.
22962306a36Sopenharmony_ci	 * This does not cover 100% of spurious interrupts,
23062306a36Sopenharmony_ci	 * but should be enough to warn the user that there
23162306a36Sopenharmony_ci	 * is something bad going on ...
23262306a36Sopenharmony_ci	 */
23362306a36Sopenharmony_ci	if (rm200_cached_irq_mask & irqmask)
23462306a36Sopenharmony_ci		goto spurious_8259A_irq;
23562306a36Sopenharmony_ci	rm200_cached_irq_mask |= irqmask;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cihandle_real_irq:
23862306a36Sopenharmony_ci	if (irq & 8) {
23962306a36Sopenharmony_ci		readb(rm200_pic_slave + PIC_IMR);
24062306a36Sopenharmony_ci		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
24162306a36Sopenharmony_ci		writeb(0x60+(irq & 7), rm200_pic_slave + PIC_CMD);
24262306a36Sopenharmony_ci		writeb(0x60+PIC_CASCADE_IR, rm200_pic_master + PIC_CMD);
24362306a36Sopenharmony_ci	} else {
24462306a36Sopenharmony_ci		readb(rm200_pic_master + PIC_IMR);
24562306a36Sopenharmony_ci		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
24662306a36Sopenharmony_ci		writeb(0x60+irq, rm200_pic_master + PIC_CMD);
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
24962306a36Sopenharmony_ci	return;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cispurious_8259A_irq:
25262306a36Sopenharmony_ci	/*
25362306a36Sopenharmony_ci	 * this is the slow path - should happen rarely.
25462306a36Sopenharmony_ci	 */
25562306a36Sopenharmony_ci	if (sni_rm200_i8259A_irq_real(irq))
25662306a36Sopenharmony_ci		/*
25762306a36Sopenharmony_ci		 * oops, the IRQ _is_ in service according to the
25862306a36Sopenharmony_ci		 * 8259A - not spurious, go handle it.
25962306a36Sopenharmony_ci		 */
26062306a36Sopenharmony_ci		goto handle_real_irq;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	{
26362306a36Sopenharmony_ci		static int spurious_irq_mask;
26462306a36Sopenharmony_ci		/*
26562306a36Sopenharmony_ci		 * At this point we can be sure the IRQ is spurious,
26662306a36Sopenharmony_ci		 * let's ACK and report it. [once per IRQ]
26762306a36Sopenharmony_ci		 */
26862306a36Sopenharmony_ci		if (!(spurious_irq_mask & irqmask)) {
26962306a36Sopenharmony_ci			printk(KERN_DEBUG
27062306a36Sopenharmony_ci			       "spurious RM200 8259A interrupt: IRQ%d.\n", irq);
27162306a36Sopenharmony_ci			spurious_irq_mask |= irqmask;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci		atomic_inc(&irq_err_count);
27462306a36Sopenharmony_ci		/*
27562306a36Sopenharmony_ci		 * Theoretically we do not have to handle this IRQ,
27662306a36Sopenharmony_ci		 * but in Linux this does not cause problems and is
27762306a36Sopenharmony_ci		 * simpler for us.
27862306a36Sopenharmony_ci		 */
27962306a36Sopenharmony_ci		goto handle_real_irq;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct irq_chip sni_rm200_i8259A_chip = {
28462306a36Sopenharmony_ci	.name		= "RM200-XT-PIC",
28562306a36Sopenharmony_ci	.irq_mask	= sni_rm200_disable_8259A_irq,
28662306a36Sopenharmony_ci	.irq_unmask	= sni_rm200_enable_8259A_irq,
28762306a36Sopenharmony_ci	.irq_mask_ack	= sni_rm200_mask_and_ack_8259A,
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/*
29162306a36Sopenharmony_ci * Do the traditional i8259 interrupt polling thing.  This is for the few
29262306a36Sopenharmony_ci * cases where no better interrupt acknowledge method is available and we
29362306a36Sopenharmony_ci * absolutely must touch the i8259.
29462306a36Sopenharmony_ci */
29562306a36Sopenharmony_cistatic inline int sni_rm200_i8259_irq(void)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int irq;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	raw_spin_lock(&sni_rm200_i8259A_lock);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Perform an interrupt acknowledge cycle on controller 1. */
30262306a36Sopenharmony_ci	writeb(0x0C, rm200_pic_master + PIC_CMD);	/* prepare for poll */
30362306a36Sopenharmony_ci	irq = readb(rm200_pic_master + PIC_CMD) & 7;
30462306a36Sopenharmony_ci	if (irq == PIC_CASCADE_IR) {
30562306a36Sopenharmony_ci		/*
30662306a36Sopenharmony_ci		 * Interrupt is cascaded so perform interrupt
30762306a36Sopenharmony_ci		 * acknowledge on controller 2.
30862306a36Sopenharmony_ci		 */
30962306a36Sopenharmony_ci		writeb(0x0C, rm200_pic_slave + PIC_CMD); /* prepare for poll */
31062306a36Sopenharmony_ci		irq = (readb(rm200_pic_slave + PIC_CMD) & 7) + 8;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (unlikely(irq == 7)) {
31462306a36Sopenharmony_ci		/*
31562306a36Sopenharmony_ci		 * This may be a spurious interrupt.
31662306a36Sopenharmony_ci		 *
31762306a36Sopenharmony_ci		 * Read the interrupt status register (ISR). If the most
31862306a36Sopenharmony_ci		 * significant bit is not set then there is no valid
31962306a36Sopenharmony_ci		 * interrupt.
32062306a36Sopenharmony_ci		 */
32162306a36Sopenharmony_ci		writeb(0x0B, rm200_pic_master + PIC_ISR); /* ISR register */
32262306a36Sopenharmony_ci		if (~readb(rm200_pic_master + PIC_ISR) & 0x80)
32362306a36Sopenharmony_ci			irq = -1;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	raw_spin_unlock(&sni_rm200_i8259A_lock);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return likely(irq >= 0) ? irq + RM200_I8259A_IRQ_BASE : irq;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_civoid sni_rm200_init_8259A(void)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	unsigned long flags;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	writeb(0xff, rm200_pic_master + PIC_IMR);
33862306a36Sopenharmony_ci	writeb(0xff, rm200_pic_slave + PIC_IMR);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	writeb(0x11, rm200_pic_master + PIC_CMD);
34162306a36Sopenharmony_ci	writeb(0, rm200_pic_master + PIC_IMR);
34262306a36Sopenharmony_ci	writeb(1U << PIC_CASCADE_IR, rm200_pic_master + PIC_IMR);
34362306a36Sopenharmony_ci	writeb(MASTER_ICW4_DEFAULT, rm200_pic_master + PIC_IMR);
34462306a36Sopenharmony_ci	writeb(0x11, rm200_pic_slave + PIC_CMD);
34562306a36Sopenharmony_ci	writeb(8, rm200_pic_slave + PIC_IMR);
34662306a36Sopenharmony_ci	writeb(PIC_CASCADE_IR, rm200_pic_slave + PIC_IMR);
34762306a36Sopenharmony_ci	writeb(SLAVE_ICW4_DEFAULT, rm200_pic_slave + PIC_IMR);
34862306a36Sopenharmony_ci	udelay(100);		/* wait for 8259A to initialize */
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
35162306a36Sopenharmony_ci	writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/*
35762306a36Sopenharmony_ci * IRQ2 is cascade interrupt to second interrupt controller
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic struct resource sni_rm200_pic1_resource = {
36162306a36Sopenharmony_ci	.name = "onboard ISA pic1",
36262306a36Sopenharmony_ci	.start = 0x16000020,
36362306a36Sopenharmony_ci	.end = 0x16000023,
36462306a36Sopenharmony_ci	.flags = IORESOURCE_BUSY
36562306a36Sopenharmony_ci};
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic struct resource sni_rm200_pic2_resource = {
36862306a36Sopenharmony_ci	.name = "onboard ISA pic2",
36962306a36Sopenharmony_ci	.start = 0x160000a0,
37062306a36Sopenharmony_ci	.end = 0x160000a3,
37162306a36Sopenharmony_ci	.flags = IORESOURCE_BUSY
37262306a36Sopenharmony_ci};
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/* ISA irq handler */
37562306a36Sopenharmony_cistatic irqreturn_t sni_rm200_i8259A_irq_handler(int dummy, void *p)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	int irq;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	irq = sni_rm200_i8259_irq();
38062306a36Sopenharmony_ci	if (unlikely(irq < 0))
38162306a36Sopenharmony_ci		return IRQ_NONE;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	do_IRQ(irq);
38462306a36Sopenharmony_ci	return IRQ_HANDLED;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_civoid __init sni_rm200_i8259_irqs(void)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	int i;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	rm200_pic_master = ioremap(0x16000020, 4);
39262306a36Sopenharmony_ci	if (!rm200_pic_master)
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci	rm200_pic_slave = ioremap(0x160000a0, 4);
39562306a36Sopenharmony_ci	if (!rm200_pic_slave) {
39662306a36Sopenharmony_ci		iounmap(rm200_pic_master);
39762306a36Sopenharmony_ci		return;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	insert_resource(&iomem_resource, &sni_rm200_pic1_resource);
40162306a36Sopenharmony_ci	insert_resource(&iomem_resource, &sni_rm200_pic2_resource);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	sni_rm200_init_8259A();
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	for (i = RM200_I8259A_IRQ_BASE; i < RM200_I8259A_IRQ_BASE + 16; i++)
40662306a36Sopenharmony_ci		irq_set_chip_and_handler(i, &sni_rm200_i8259A_chip,
40762306a36Sopenharmony_ci					 handle_level_irq);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (request_irq(RM200_I8259A_IRQ_BASE + PIC_CASCADE_IR, no_action,
41062306a36Sopenharmony_ci			IRQF_NO_THREAD, "cascade", NULL))
41162306a36Sopenharmony_ci		pr_err("Failed to register cascade interrupt\n");
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci#define SNI_RM200_INT_STAT_REG	CKSEG1ADDR(0xbc000000)
41662306a36Sopenharmony_ci#define SNI_RM200_INT_ENA_REG	CKSEG1ADDR(0xbc080000)
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci#define SNI_RM200_INT_START  24
41962306a36Sopenharmony_ci#define SNI_RM200_INT_END    28
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void enable_rm200_irq(struct irq_data *d)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	*(volatile u8 *)SNI_RM200_INT_ENA_REG &= ~mask;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_civoid disable_rm200_irq(struct irq_data *d)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	*(volatile u8 *)SNI_RM200_INT_ENA_REG |= mask;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic struct irq_chip rm200_irq_type = {
43662306a36Sopenharmony_ci	.name = "RM200",
43762306a36Sopenharmony_ci	.irq_mask = disable_rm200_irq,
43862306a36Sopenharmony_ci	.irq_unmask = enable_rm200_irq,
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic void sni_rm200_hwint(void)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	u32 pending = read_c0_cause() & read_c0_status();
44462306a36Sopenharmony_ci	u8 mask;
44562306a36Sopenharmony_ci	u8 stat;
44662306a36Sopenharmony_ci	int irq;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (pending & C_IRQ5)
44962306a36Sopenharmony_ci		do_IRQ(MIPS_CPU_IRQ_BASE + 7);
45062306a36Sopenharmony_ci	else if (pending & C_IRQ0) {
45162306a36Sopenharmony_ci		clear_c0_status(IE_IRQ0);
45262306a36Sopenharmony_ci		mask = *(volatile u8 *)SNI_RM200_INT_ENA_REG ^ 0x1f;
45362306a36Sopenharmony_ci		stat = *(volatile u8 *)SNI_RM200_INT_STAT_REG ^ 0x14;
45462306a36Sopenharmony_ci		irq = ffs(stat & mask & 0x1f);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		if (likely(irq > 0))
45762306a36Sopenharmony_ci			do_IRQ(irq + SNI_RM200_INT_START - 1);
45862306a36Sopenharmony_ci		set_c0_status(IE_IRQ0);
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_civoid __init sni_rm200_irq_init(void)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int i;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	* (volatile u8 *)SNI_RM200_INT_ENA_REG = 0x1f;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	sni_rm200_i8259_irqs();
46962306a36Sopenharmony_ci	mips_cpu_irq_init();
47062306a36Sopenharmony_ci	/* Actually we've got more interrupts to handle ...  */
47162306a36Sopenharmony_ci	for (i = SNI_RM200_INT_START; i <= SNI_RM200_INT_END; i++)
47262306a36Sopenharmony_ci		irq_set_chip_and_handler(i, &rm200_irq_type, handle_level_irq);
47362306a36Sopenharmony_ci	sni_hwint = sni_rm200_hwint;
47462306a36Sopenharmony_ci	change_c0_status(ST0_IM, IE_IRQ0);
47562306a36Sopenharmony_ci	if (request_irq(SNI_RM200_INT_START + 0, sni_rm200_i8259A_irq_handler,
47662306a36Sopenharmony_ci			0, "onboard ISA", NULL))
47762306a36Sopenharmony_ci		pr_err("Failed to register onboard ISA interrupt\n");
47862306a36Sopenharmony_ci	if (request_irq(SNI_RM200_INT_START + 1, sni_isa_irq_handler, 0, "ISA",
47962306a36Sopenharmony_ci			NULL))
48062306a36Sopenharmony_ci		pr_err("Failed to register ISA interrupt\n");
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_civoid __init sni_rm200_init(void)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci}
486