xref: /kernel/linux/linux-5.10/arch/mips/sni/rm200.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * RM200 specific code
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
58c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
68c2ecf20Sopenharmony_ci * for more details.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * i8259 parts ripped out of arch/mips/kernel/i8259.c
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/serial_8250.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/sni.h>
228c2ecf20Sopenharmony_ci#include <asm/time.h>
238c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define RM200_I8259A_IRQ_BASE 32
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define MEMPORT(_base,_irq)				\
288c2ecf20Sopenharmony_ci	{						\
298c2ecf20Sopenharmony_ci		.mapbase	= _base,		\
308c2ecf20Sopenharmony_ci		.irq		= _irq,			\
318c2ecf20Sopenharmony_ci		.uartclk	= 1843200,		\
328c2ecf20Sopenharmony_ci		.iotype		= UPIO_MEM,		\
338c2ecf20Sopenharmony_ci		.flags		= UPF_BOOT_AUTOCONF|UPF_IOREMAP, \
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic struct plat_serial8250_port rm200_data[] = {
378c2ecf20Sopenharmony_ci	MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
388c2ecf20Sopenharmony_ci	MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
398c2ecf20Sopenharmony_ci	{ },
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic struct platform_device rm200_serial8250_device = {
438c2ecf20Sopenharmony_ci	.name			= "serial8250",
448c2ecf20Sopenharmony_ci	.id			= PLAT8250_DEV_PLATFORM,
458c2ecf20Sopenharmony_ci	.dev			= {
468c2ecf20Sopenharmony_ci		.platform_data	= rm200_data,
478c2ecf20Sopenharmony_ci	},
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct resource rm200_ds1216_rsrc[] = {
518c2ecf20Sopenharmony_ci	{
528c2ecf20Sopenharmony_ci		.start = 0x1cd41ffc,
538c2ecf20Sopenharmony_ci		.end   = 0x1cd41fff,
548c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct platform_device rm200_ds1216_device = {
598c2ecf20Sopenharmony_ci	.name		= "rtc-ds1216",
608c2ecf20Sopenharmony_ci	.num_resources	= ARRAY_SIZE(rm200_ds1216_rsrc),
618c2ecf20Sopenharmony_ci	.resource	= rm200_ds1216_rsrc
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct resource snirm_82596_rm200_rsrc[] = {
658c2ecf20Sopenharmony_ci	{
668c2ecf20Sopenharmony_ci		.start = 0x18000000,
678c2ecf20Sopenharmony_ci		.end   = 0x180fffff,
688c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM
698c2ecf20Sopenharmony_ci	},
708c2ecf20Sopenharmony_ci	{
718c2ecf20Sopenharmony_ci		.start = 0x1b000000,
728c2ecf20Sopenharmony_ci		.end   = 0x1b000004,
738c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM
748c2ecf20Sopenharmony_ci	},
758c2ecf20Sopenharmony_ci	{
768c2ecf20Sopenharmony_ci		.start = 0x1ff00000,
778c2ecf20Sopenharmony_ci		.end   = 0x1ff00020,
788c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM
798c2ecf20Sopenharmony_ci	},
808c2ecf20Sopenharmony_ci	{
818c2ecf20Sopenharmony_ci		.start = 27,
828c2ecf20Sopenharmony_ci		.end   = 27,
838c2ecf20Sopenharmony_ci		.flags = IORESOURCE_IRQ
848c2ecf20Sopenharmony_ci	},
858c2ecf20Sopenharmony_ci	{
868c2ecf20Sopenharmony_ci		.flags = 0x00
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct platform_device snirm_82596_rm200_pdev = {
918c2ecf20Sopenharmony_ci	.name		= "snirm_82596",
928c2ecf20Sopenharmony_ci	.num_resources	= ARRAY_SIZE(snirm_82596_rm200_rsrc),
938c2ecf20Sopenharmony_ci	.resource	= snirm_82596_rm200_rsrc
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic struct resource snirm_53c710_rm200_rsrc[] = {
978c2ecf20Sopenharmony_ci	{
988c2ecf20Sopenharmony_ci		.start = 0x19000000,
998c2ecf20Sopenharmony_ci		.end   = 0x190fffff,
1008c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci	{
1038c2ecf20Sopenharmony_ci		.start = 26,
1048c2ecf20Sopenharmony_ci		.end   = 26,
1058c2ecf20Sopenharmony_ci		.flags = IORESOURCE_IRQ
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic struct platform_device snirm_53c710_rm200_pdev = {
1108c2ecf20Sopenharmony_ci	.name		= "snirm_53c710",
1118c2ecf20Sopenharmony_ci	.num_resources	= ARRAY_SIZE(snirm_53c710_rm200_rsrc),
1128c2ecf20Sopenharmony_ci	.resource	= snirm_53c710_rm200_rsrc
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int __init snirm_setup_devinit(void)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (sni_brd_type == SNI_BRD_RM200) {
1188c2ecf20Sopenharmony_ci		platform_device_register(&rm200_serial8250_device);
1198c2ecf20Sopenharmony_ci		platform_device_register(&rm200_ds1216_device);
1208c2ecf20Sopenharmony_ci		platform_device_register(&snirm_82596_rm200_pdev);
1218c2ecf20Sopenharmony_ci		platform_device_register(&snirm_53c710_rm200_pdev);
1228c2ecf20Sopenharmony_ci		sni_eisa_root_init();
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cidevice_initcall(snirm_setup_devinit);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * RM200 has an ISA and an EISA bus. The iSA bus is only used
1318c2ecf20Sopenharmony_ci * for onboard devices and also has twi i8259 PICs. Since these
1328c2ecf20Sopenharmony_ci * PICs are no accessible via inb/outb the following code uses
1338c2ecf20Sopenharmony_ci * readb/writeb to access them
1348c2ecf20Sopenharmony_ci */
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(sni_rm200_i8259A_lock);
1378c2ecf20Sopenharmony_ci#define PIC_CMD	   0x00
1388c2ecf20Sopenharmony_ci#define PIC_IMR	   0x01
1398c2ecf20Sopenharmony_ci#define PIC_ISR	   PIC_CMD
1408c2ecf20Sopenharmony_ci#define PIC_POLL   PIC_ISR
1418c2ecf20Sopenharmony_ci#define PIC_OCW3   PIC_ISR
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* i8259A PIC related value */
1448c2ecf20Sopenharmony_ci#define PIC_CASCADE_IR		2
1458c2ecf20Sopenharmony_ci#define MASTER_ICW4_DEFAULT	0x01
1468c2ecf20Sopenharmony_ci#define SLAVE_ICW4_DEFAULT	0x01
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * This contains the irq mask for both 8259A irq controllers,
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_cistatic unsigned int rm200_cached_irq_mask = 0xffff;
1528c2ecf20Sopenharmony_cistatic __iomem u8 *rm200_pic_master;
1538c2ecf20Sopenharmony_cistatic __iomem u8 *rm200_pic_slave;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci#define cached_master_mask	(rm200_cached_irq_mask)
1568c2ecf20Sopenharmony_ci#define cached_slave_mask	(rm200_cached_irq_mask >> 8)
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void sni_rm200_disable_8259A_irq(struct irq_data *d)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE;
1618c2ecf20Sopenharmony_ci	unsigned long flags;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	mask = 1 << irq;
1648c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
1658c2ecf20Sopenharmony_ci	rm200_cached_irq_mask |= mask;
1668c2ecf20Sopenharmony_ci	if (irq & 8)
1678c2ecf20Sopenharmony_ci		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
1688c2ecf20Sopenharmony_ci	else
1698c2ecf20Sopenharmony_ci		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
1708c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void sni_rm200_enable_8259A_irq(struct irq_data *d)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE;
1768c2ecf20Sopenharmony_ci	unsigned long flags;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mask = ~(1 << irq);
1798c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
1808c2ecf20Sopenharmony_ci	rm200_cached_irq_mask &= mask;
1818c2ecf20Sopenharmony_ci	if (irq & 8)
1828c2ecf20Sopenharmony_ci		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
1838c2ecf20Sopenharmony_ci	else
1848c2ecf20Sopenharmony_ci		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
1858c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic inline int sni_rm200_i8259A_irq_real(unsigned int irq)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int value;
1918c2ecf20Sopenharmony_ci	int irqmask = 1 << irq;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (irq < 8) {
1948c2ecf20Sopenharmony_ci		writeb(0x0B, rm200_pic_master + PIC_CMD);
1958c2ecf20Sopenharmony_ci		value = readb(rm200_pic_master + PIC_CMD) & irqmask;
1968c2ecf20Sopenharmony_ci		writeb(0x0A, rm200_pic_master + PIC_CMD);
1978c2ecf20Sopenharmony_ci		return value;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	writeb(0x0B, rm200_pic_slave + PIC_CMD); /* ISR register */
2008c2ecf20Sopenharmony_ci	value = readb(rm200_pic_slave + PIC_CMD) & (irqmask >> 8);
2018c2ecf20Sopenharmony_ci	writeb(0x0A, rm200_pic_slave + PIC_CMD);
2028c2ecf20Sopenharmony_ci	return value;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci * Careful! The 8259A is a fragile beast, it pretty
2078c2ecf20Sopenharmony_ci * much _has_ to be done exactly like this (mask it
2088c2ecf20Sopenharmony_ci * first, _then_ send the EOI, and the order of EOI
2098c2ecf20Sopenharmony_ci * to the two 8259s is important!
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_civoid sni_rm200_mask_and_ack_8259A(struct irq_data *d)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	unsigned int irqmask, irq = d->irq - RM200_I8259A_IRQ_BASE;
2148c2ecf20Sopenharmony_ci	unsigned long flags;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	irqmask = 1 << irq;
2178c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
2188c2ecf20Sopenharmony_ci	/*
2198c2ecf20Sopenharmony_ci	 * Lightweight spurious IRQ detection. We do not want
2208c2ecf20Sopenharmony_ci	 * to overdo spurious IRQ handling - it's usually a sign
2218c2ecf20Sopenharmony_ci	 * of hardware problems, so we only do the checks we can
2228c2ecf20Sopenharmony_ci	 * do without slowing down good hardware unnecessarily.
2238c2ecf20Sopenharmony_ci	 *
2248c2ecf20Sopenharmony_ci	 * Note that IRQ7 and IRQ15 (the two spurious IRQs
2258c2ecf20Sopenharmony_ci	 * usually resulting from the 8259A-1|2 PICs) occur
2268c2ecf20Sopenharmony_ci	 * even if the IRQ is masked in the 8259A. Thus we
2278c2ecf20Sopenharmony_ci	 * can check spurious 8259A IRQs without doing the
2288c2ecf20Sopenharmony_ci	 * quite slow i8259A_irq_real() call for every IRQ.
2298c2ecf20Sopenharmony_ci	 * This does not cover 100% of spurious interrupts,
2308c2ecf20Sopenharmony_ci	 * but should be enough to warn the user that there
2318c2ecf20Sopenharmony_ci	 * is something bad going on ...
2328c2ecf20Sopenharmony_ci	 */
2338c2ecf20Sopenharmony_ci	if (rm200_cached_irq_mask & irqmask)
2348c2ecf20Sopenharmony_ci		goto spurious_8259A_irq;
2358c2ecf20Sopenharmony_ci	rm200_cached_irq_mask |= irqmask;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cihandle_real_irq:
2388c2ecf20Sopenharmony_ci	if (irq & 8) {
2398c2ecf20Sopenharmony_ci		readb(rm200_pic_slave + PIC_IMR);
2408c2ecf20Sopenharmony_ci		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
2418c2ecf20Sopenharmony_ci		writeb(0x60+(irq & 7), rm200_pic_slave + PIC_CMD);
2428c2ecf20Sopenharmony_ci		writeb(0x60+PIC_CASCADE_IR, rm200_pic_master + PIC_CMD);
2438c2ecf20Sopenharmony_ci	} else {
2448c2ecf20Sopenharmony_ci		readb(rm200_pic_master + PIC_IMR);
2458c2ecf20Sopenharmony_ci		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
2468c2ecf20Sopenharmony_ci		writeb(0x60+irq, rm200_pic_master + PIC_CMD);
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
2498c2ecf20Sopenharmony_ci	return;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cispurious_8259A_irq:
2528c2ecf20Sopenharmony_ci	/*
2538c2ecf20Sopenharmony_ci	 * this is the slow path - should happen rarely.
2548c2ecf20Sopenharmony_ci	 */
2558c2ecf20Sopenharmony_ci	if (sni_rm200_i8259A_irq_real(irq))
2568c2ecf20Sopenharmony_ci		/*
2578c2ecf20Sopenharmony_ci		 * oops, the IRQ _is_ in service according to the
2588c2ecf20Sopenharmony_ci		 * 8259A - not spurious, go handle it.
2598c2ecf20Sopenharmony_ci		 */
2608c2ecf20Sopenharmony_ci		goto handle_real_irq;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	{
2638c2ecf20Sopenharmony_ci		static int spurious_irq_mask;
2648c2ecf20Sopenharmony_ci		/*
2658c2ecf20Sopenharmony_ci		 * At this point we can be sure the IRQ is spurious,
2668c2ecf20Sopenharmony_ci		 * let's ACK and report it. [once per IRQ]
2678c2ecf20Sopenharmony_ci		 */
2688c2ecf20Sopenharmony_ci		if (!(spurious_irq_mask & irqmask)) {
2698c2ecf20Sopenharmony_ci			printk(KERN_DEBUG
2708c2ecf20Sopenharmony_ci			       "spurious RM200 8259A interrupt: IRQ%d.\n", irq);
2718c2ecf20Sopenharmony_ci			spurious_irq_mask |= irqmask;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci		atomic_inc(&irq_err_count);
2748c2ecf20Sopenharmony_ci		/*
2758c2ecf20Sopenharmony_ci		 * Theoretically we do not have to handle this IRQ,
2768c2ecf20Sopenharmony_ci		 * but in Linux this does not cause problems and is
2778c2ecf20Sopenharmony_ci		 * simpler for us.
2788c2ecf20Sopenharmony_ci		 */
2798c2ecf20Sopenharmony_ci		goto handle_real_irq;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic struct irq_chip sni_rm200_i8259A_chip = {
2848c2ecf20Sopenharmony_ci	.name		= "RM200-XT-PIC",
2858c2ecf20Sopenharmony_ci	.irq_mask	= sni_rm200_disable_8259A_irq,
2868c2ecf20Sopenharmony_ci	.irq_unmask	= sni_rm200_enable_8259A_irq,
2878c2ecf20Sopenharmony_ci	.irq_mask_ack	= sni_rm200_mask_and_ack_8259A,
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/*
2918c2ecf20Sopenharmony_ci * Do the traditional i8259 interrupt polling thing.  This is for the few
2928c2ecf20Sopenharmony_ci * cases where no better interrupt acknowledge method is available and we
2938c2ecf20Sopenharmony_ci * absolutely must touch the i8259.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_cistatic inline int sni_rm200_i8259_irq(void)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int irq;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	raw_spin_lock(&sni_rm200_i8259A_lock);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Perform an interrupt acknowledge cycle on controller 1. */
3028c2ecf20Sopenharmony_ci	writeb(0x0C, rm200_pic_master + PIC_CMD);	/* prepare for poll */
3038c2ecf20Sopenharmony_ci	irq = readb(rm200_pic_master + PIC_CMD) & 7;
3048c2ecf20Sopenharmony_ci	if (irq == PIC_CASCADE_IR) {
3058c2ecf20Sopenharmony_ci		/*
3068c2ecf20Sopenharmony_ci		 * Interrupt is cascaded so perform interrupt
3078c2ecf20Sopenharmony_ci		 * acknowledge on controller 2.
3088c2ecf20Sopenharmony_ci		 */
3098c2ecf20Sopenharmony_ci		writeb(0x0C, rm200_pic_slave + PIC_CMD); /* prepare for poll */
3108c2ecf20Sopenharmony_ci		irq = (readb(rm200_pic_slave + PIC_CMD) & 7) + 8;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (unlikely(irq == 7)) {
3148c2ecf20Sopenharmony_ci		/*
3158c2ecf20Sopenharmony_ci		 * This may be a spurious interrupt.
3168c2ecf20Sopenharmony_ci		 *
3178c2ecf20Sopenharmony_ci		 * Read the interrupt status register (ISR). If the most
3188c2ecf20Sopenharmony_ci		 * significant bit is not set then there is no valid
3198c2ecf20Sopenharmony_ci		 * interrupt.
3208c2ecf20Sopenharmony_ci		 */
3218c2ecf20Sopenharmony_ci		writeb(0x0B, rm200_pic_master + PIC_ISR); /* ISR register */
3228c2ecf20Sopenharmony_ci		if (~readb(rm200_pic_master + PIC_ISR) & 0x80)
3238c2ecf20Sopenharmony_ci			irq = -1;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	raw_spin_unlock(&sni_rm200_i8259A_lock);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return likely(irq >= 0) ? irq + RM200_I8259A_IRQ_BASE : irq;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_civoid sni_rm200_init_8259A(void)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	unsigned long flags;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	writeb(0xff, rm200_pic_master + PIC_IMR);
3388c2ecf20Sopenharmony_ci	writeb(0xff, rm200_pic_slave + PIC_IMR);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	writeb(0x11, rm200_pic_master + PIC_CMD);
3418c2ecf20Sopenharmony_ci	writeb(0, rm200_pic_master + PIC_IMR);
3428c2ecf20Sopenharmony_ci	writeb(1U << PIC_CASCADE_IR, rm200_pic_master + PIC_IMR);
3438c2ecf20Sopenharmony_ci	writeb(MASTER_ICW4_DEFAULT, rm200_pic_master + PIC_IMR);
3448c2ecf20Sopenharmony_ci	writeb(0x11, rm200_pic_slave + PIC_CMD);
3458c2ecf20Sopenharmony_ci	writeb(8, rm200_pic_slave + PIC_IMR);
3468c2ecf20Sopenharmony_ci	writeb(PIC_CASCADE_IR, rm200_pic_slave + PIC_IMR);
3478c2ecf20Sopenharmony_ci	writeb(SLAVE_ICW4_DEFAULT, rm200_pic_slave + PIC_IMR);
3488c2ecf20Sopenharmony_ci	udelay(100);		/* wait for 8259A to initialize */
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
3518c2ecf20Sopenharmony_ci	writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/*
3578c2ecf20Sopenharmony_ci * IRQ2 is cascade interrupt to second interrupt controller
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic struct resource sni_rm200_pic1_resource = {
3618c2ecf20Sopenharmony_ci	.name = "onboard ISA pic1",
3628c2ecf20Sopenharmony_ci	.start = 0x16000020,
3638c2ecf20Sopenharmony_ci	.end = 0x16000023,
3648c2ecf20Sopenharmony_ci	.flags = IORESOURCE_BUSY
3658c2ecf20Sopenharmony_ci};
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic struct resource sni_rm200_pic2_resource = {
3688c2ecf20Sopenharmony_ci	.name = "onboard ISA pic2",
3698c2ecf20Sopenharmony_ci	.start = 0x160000a0,
3708c2ecf20Sopenharmony_ci	.end = 0x160000a3,
3718c2ecf20Sopenharmony_ci	.flags = IORESOURCE_BUSY
3728c2ecf20Sopenharmony_ci};
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci/* ISA irq handler */
3758c2ecf20Sopenharmony_cistatic irqreturn_t sni_rm200_i8259A_irq_handler(int dummy, void *p)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	int irq;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	irq = sni_rm200_i8259_irq();
3808c2ecf20Sopenharmony_ci	if (unlikely(irq < 0))
3818c2ecf20Sopenharmony_ci		return IRQ_NONE;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	do_IRQ(irq);
3848c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_civoid __init sni_rm200_i8259_irqs(void)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	int i;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	rm200_pic_master = ioremap(0x16000020, 4);
3928c2ecf20Sopenharmony_ci	if (!rm200_pic_master)
3938c2ecf20Sopenharmony_ci		return;
3948c2ecf20Sopenharmony_ci	rm200_pic_slave = ioremap(0x160000a0, 4);
3958c2ecf20Sopenharmony_ci	if (!rm200_pic_slave) {
3968c2ecf20Sopenharmony_ci		iounmap(rm200_pic_master);
3978c2ecf20Sopenharmony_ci		return;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	insert_resource(&iomem_resource, &sni_rm200_pic1_resource);
4018c2ecf20Sopenharmony_ci	insert_resource(&iomem_resource, &sni_rm200_pic2_resource);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	sni_rm200_init_8259A();
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	for (i = RM200_I8259A_IRQ_BASE; i < RM200_I8259A_IRQ_BASE + 16; i++)
4068c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(i, &sni_rm200_i8259A_chip,
4078c2ecf20Sopenharmony_ci					 handle_level_irq);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (request_irq(RM200_I8259A_IRQ_BASE + PIC_CASCADE_IR, no_action,
4108c2ecf20Sopenharmony_ci			IRQF_NO_THREAD, "cascade", NULL))
4118c2ecf20Sopenharmony_ci		pr_err("Failed to register cascade interrupt\n");
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci#define SNI_RM200_INT_STAT_REG	CKSEG1ADDR(0xbc000000)
4168c2ecf20Sopenharmony_ci#define SNI_RM200_INT_ENA_REG	CKSEG1ADDR(0xbc080000)
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci#define SNI_RM200_INT_START  24
4198c2ecf20Sopenharmony_ci#define SNI_RM200_INT_END    28
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic void enable_rm200_irq(struct irq_data *d)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	*(volatile u8 *)SNI_RM200_INT_ENA_REG &= ~mask;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_civoid disable_rm200_irq(struct irq_data *d)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	*(volatile u8 *)SNI_RM200_INT_ENA_REG |= mask;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic struct irq_chip rm200_irq_type = {
4368c2ecf20Sopenharmony_ci	.name = "RM200",
4378c2ecf20Sopenharmony_ci	.irq_mask = disable_rm200_irq,
4388c2ecf20Sopenharmony_ci	.irq_unmask = enable_rm200_irq,
4398c2ecf20Sopenharmony_ci};
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic void sni_rm200_hwint(void)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	u32 pending = read_c0_cause() & read_c0_status();
4448c2ecf20Sopenharmony_ci	u8 mask;
4458c2ecf20Sopenharmony_ci	u8 stat;
4468c2ecf20Sopenharmony_ci	int irq;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (pending & C_IRQ5)
4498c2ecf20Sopenharmony_ci		do_IRQ(MIPS_CPU_IRQ_BASE + 7);
4508c2ecf20Sopenharmony_ci	else if (pending & C_IRQ0) {
4518c2ecf20Sopenharmony_ci		clear_c0_status(IE_IRQ0);
4528c2ecf20Sopenharmony_ci		mask = *(volatile u8 *)SNI_RM200_INT_ENA_REG ^ 0x1f;
4538c2ecf20Sopenharmony_ci		stat = *(volatile u8 *)SNI_RM200_INT_STAT_REG ^ 0x14;
4548c2ecf20Sopenharmony_ci		irq = ffs(stat & mask & 0x1f);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		if (likely(irq > 0))
4578c2ecf20Sopenharmony_ci			do_IRQ(irq + SNI_RM200_INT_START - 1);
4588c2ecf20Sopenharmony_ci		set_c0_status(IE_IRQ0);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_civoid __init sni_rm200_irq_init(void)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int i;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	* (volatile u8 *)SNI_RM200_INT_ENA_REG = 0x1f;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	sni_rm200_i8259_irqs();
4698c2ecf20Sopenharmony_ci	mips_cpu_irq_init();
4708c2ecf20Sopenharmony_ci	/* Actually we've got more interrupts to handle ...  */
4718c2ecf20Sopenharmony_ci	for (i = SNI_RM200_INT_START; i <= SNI_RM200_INT_END; i++)
4728c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(i, &rm200_irq_type, handle_level_irq);
4738c2ecf20Sopenharmony_ci	sni_hwint = sni_rm200_hwint;
4748c2ecf20Sopenharmony_ci	change_c0_status(ST0_IM, IE_IRQ0);
4758c2ecf20Sopenharmony_ci	if (request_irq(SNI_RM200_INT_START + 0, sni_rm200_i8259A_irq_handler,
4768c2ecf20Sopenharmony_ci			0, "onboard ISA", NULL))
4778c2ecf20Sopenharmony_ci		pr_err("Failed to register onboard ISA interrupt\n");
4788c2ecf20Sopenharmony_ci	if (request_irq(SNI_RM200_INT_START + 1, sni_isa_irq_handler, 0, "ISA",
4798c2ecf20Sopenharmony_ci			NULL))
4808c2ecf20Sopenharmony_ci		pr_err("Failed to register ISA interrupt\n");
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_civoid __init sni_rm200_init(void)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci}
486