| /* | |
| * PCI Tower specific code | |
| * | |
| * This file is subject to the terms and conditions of the GNU General Public | |
| * License. See the file "COPYING" in the main directory of this archive | |
| * for more details. | |
| * | |
| * Copyright (C) 2006 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | |
| */ | |
| #include <linux/init.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/irq.h> | |
| #include <linux/pci.h> | |
| #include <linux/serial_8250.h> | |
| #include <asm/sni.h> | |
| #include <asm/time.h> | |
| #include <asm/irq_cpu.h> | |
| #define PORT(_base,_irq) \ | |
| { \ | |
| .iobase = _base, \ | |
| .irq = _irq, \ | |
| .uartclk = 1843200, \ | |
| .iotype = UPIO_PORT, \ | |
| .flags = UPF_BOOT_AUTOCONF, \ | |
| } | |
| static struct plat_serial8250_port pcit_data[] = { | |
| PORT(0x3f8, 0), | |
| PORT(0x2f8, 3), | |
| { }, | |
| }; | |
| static struct platform_device pcit_serial8250_device = { | |
| .name = "serial8250", | |
| .id = PLAT8250_DEV_PLATFORM, | |
| .dev = { | |
| .platform_data = pcit_data, | |
| }, | |
| }; | |
| static struct plat_serial8250_port pcit_cplus_data[] = { | |
| PORT(0x3f8, 0), | |
| PORT(0x2f8, 3), | |
| PORT(0x3e8, 4), | |
| PORT(0x2e8, 3), | |
| { }, | |
| }; | |
| static struct platform_device pcit_cplus_serial8250_device = { | |
| .name = "serial8250", | |
| .id = PLAT8250_DEV_PLATFORM, | |
| .dev = { | |
| .platform_data = pcit_cplus_data, | |
| }, | |
| }; | |
| static struct resource pcit_cmos_rsrc[] = { | |
| { | |
| .start = 0x70, | |
| .end = 0x71, | |
| .flags = IORESOURCE_IO | |
| }, | |
| { | |
| .start = 8, | |
| .end = 8, | |
| .flags = IORESOURCE_IRQ | |
| } | |
| }; | |
| static struct platform_device pcit_cmos_device = { | |
| .name = "rtc_cmos", | |
| .num_resources = ARRAY_SIZE(pcit_cmos_rsrc), | |
| .resource = pcit_cmos_rsrc | |
| }; | |
| static struct platform_device pcit_pcspeaker_pdev = { | |
| .name = "pcspkr", | |
| .id = -1, | |
| }; | |
| static struct resource sni_io_resource = { | |
| .start = 0x00000000UL, | |
| .end = 0x03bfffffUL, | |
| .name = "PCIT IO", | |
| .flags = IORESOURCE_IO, | |
| }; | |
| static struct resource pcit_io_resources[] = { | |
| { | |
| .start = 0x00, | |
| .end = 0x1f, | |
| .name = "dma1", | |
| .flags = IORESOURCE_BUSY | |
| }, { | |
| .start = 0x40, | |
| .end = 0x5f, | |
| .name = "timer", | |
| .flags = IORESOURCE_BUSY | |
| }, { | |
| .start = 0x60, | |
| .end = 0x6f, | |
| .name = "keyboard", | |
| .flags = IORESOURCE_BUSY | |
| }, { | |
| .start = 0x80, | |
| .end = 0x8f, | |
| .name = "dma page reg", | |
| .flags = IORESOURCE_BUSY | |
| }, { | |
| .start = 0xc0, | |
| .end = 0xdf, | |
| .name = "dma2", | |
| .flags = IORESOURCE_BUSY | |
| }, { | |
| .start = 0xcf8, | |
| .end = 0xcfb, | |
| .name = "PCI config addr", | |
| .flags = IORESOURCE_BUSY | |
| }, { | |
| .start = 0xcfc, | |
| .end = 0xcff, | |
| .name = "PCI config data", | |
| .flags = IORESOURCE_BUSY | |
| } | |
| }; | |
| static void __init sni_pcit_resource_init(void) | |
| { | |
| int i; | |
| /* request I/O space for devices used on all i[345]86 PCs */ | |
| for (i = 0; i < ARRAY_SIZE(pcit_io_resources); i++) | |
| request_resource(&sni_io_resource, pcit_io_resources + i); | |
| } | |
| extern struct pci_ops sni_pcit_ops; | |
| #ifdef CONFIG_PCI | |
| static struct resource sni_mem_resource = { | |
| .start = 0x18000000UL, | |
| .end = 0x1fbfffffUL, | |
| .name = "PCIT PCI MEM", | |
| .flags = IORESOURCE_MEM | |
| }; | |
| static struct pci_controller sni_pcit_controller = { | |
| .pci_ops = &sni_pcit_ops, | |
| .mem_resource = &sni_mem_resource, | |
| .mem_offset = 0x00000000UL, | |
| .io_resource = &sni_io_resource, | |
| .io_offset = 0x00000000UL, | |
| .io_map_base = SNI_PORT_BASE | |
| }; | |
| #endif /* CONFIG_PCI */ | |
| static void enable_pcit_irq(struct irq_data *d) | |
| { | |
| u32 mask = 1 << (d->irq - SNI_PCIT_INT_START + 24); | |
| *(volatile u32 *)SNI_PCIT_INT_REG |= mask; | |
| } | |
| void disable_pcit_irq(struct irq_data *d) | |
| { | |
| u32 mask = 1 << (d->irq - SNI_PCIT_INT_START + 24); | |
| *(volatile u32 *)SNI_PCIT_INT_REG &= ~mask; | |
| } | |
| static struct irq_chip pcit_irq_type = { | |
| .name = "PCIT", | |
| .irq_mask = disable_pcit_irq, | |
| .irq_unmask = enable_pcit_irq, | |
| }; | |
| static void pcit_hwint1(void) | |
| { | |
| u32 pending = *(volatile u32 *)SNI_PCIT_INT_REG; | |
| int irq; | |
| clear_c0_status(IE_IRQ1); | |
| irq = ffs((pending >> 16) & 0x7f); | |
| if (likely(irq > 0)) | |
| do_IRQ(irq + SNI_PCIT_INT_START - 1); | |
| set_c0_status(IE_IRQ1); | |
| } | |
| static void pcit_hwint0(void) | |
| { | |
| u32 pending = *(volatile u32 *)SNI_PCIT_INT_REG; | |
| int irq; | |
| clear_c0_status(IE_IRQ0); | |
| irq = ffs((pending >> 16) & 0x3f); | |
| if (likely(irq > 0)) | |
| do_IRQ(irq + SNI_PCIT_INT_START - 1); | |
| set_c0_status(IE_IRQ0); | |
| } | |
| static void sni_pcit_hwint(void) | |
| { | |
| u32 pending = read_c0_cause() & read_c0_status(); | |
| if (pending & C_IRQ1) | |
| pcit_hwint1(); | |
| else if (pending & C_IRQ2) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 4); | |
| else if (pending & C_IRQ3) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 5); | |
| else if (pending & C_IRQ5) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 7); | |
| } | |
| static void sni_pcit_hwint_cplus(void) | |
| { | |
| u32 pending = read_c0_cause() & read_c0_status(); | |
| if (pending & C_IRQ0) | |
| pcit_hwint0(); | |
| else if (pending & C_IRQ1) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 3); | |
| else if (pending & C_IRQ2) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 4); | |
| else if (pending & C_IRQ3) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 5); | |
| else if (pending & C_IRQ5) | |
| do_IRQ(MIPS_CPU_IRQ_BASE + 7); | |
| } | |
| void __init sni_pcit_irq_init(void) | |
| { | |
| int i; | |
| mips_cpu_irq_init(); | |
| for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++) | |
| irq_set_chip_and_handler(i, &pcit_irq_type, handle_level_irq); | |
| *(volatile u32 *)SNI_PCIT_INT_REG = 0; | |
| sni_hwint = sni_pcit_hwint; | |
| change_c0_status(ST0_IM, IE_IRQ1); | |
| setup_irq(SNI_PCIT_INT_START + 6, &sni_isa_irq); | |
| } | |
| void __init sni_pcit_cplus_irq_init(void) | |
| { | |
| int i; | |
| mips_cpu_irq_init(); | |
| for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++) | |
| irq_set_chip_and_handler(i, &pcit_irq_type, handle_level_irq); | |
| *(volatile u32 *)SNI_PCIT_INT_REG = 0x40000000; | |
| sni_hwint = sni_pcit_hwint_cplus; | |
| change_c0_status(ST0_IM, IE_IRQ0); | |
| setup_irq(MIPS_CPU_IRQ_BASE + 3, &sni_isa_irq); | |
| } | |
| void __init sni_pcit_init(void) | |
| { | |
| ioport_resource.end = sni_io_resource.end; | |
| #ifdef CONFIG_PCI | |
| PCIBIOS_MIN_IO = 0x9000; | |
| register_pci_controller(&sni_pcit_controller); | |
| #endif | |
| sni_pcit_resource_init(); | |
| } | |
| static int __init snirm_pcit_setup_devinit(void) | |
| { | |
| switch (sni_brd_type) { | |
| case SNI_BRD_PCI_TOWER: | |
| platform_device_register(&pcit_serial8250_device); | |
| platform_device_register(&pcit_cmos_device); | |
| platform_device_register(&pcit_pcspeaker_pdev); | |
| break; | |
| case SNI_BRD_PCI_TOWER_CPLUS: | |
| platform_device_register(&pcit_cplus_serial8250_device); | |
| platform_device_register(&pcit_cmos_device); | |
| platform_device_register(&pcit_pcspeaker_pdev); | |
| break; | |
| } | |
| return 0; | |
| } | |
| device_initcall(snirm_pcit_setup_devinit); |