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