|  | /* | 
|  | *	linux/arch/alpha/kernel/sys_nautilus.c | 
|  | * | 
|  | *	Copyright (C) 1995 David A Rusling | 
|  | *	Copyright (C) 1998 Richard Henderson | 
|  | *	Copyright (C) 1999 Alpha Processor, Inc., | 
|  | *		(David Daniel, Stig Telfer, Soohoon Lee) | 
|  | * | 
|  | * Code supporting NAUTILUS systems. | 
|  | * | 
|  | * | 
|  | * NAUTILUS has the following I/O features: | 
|  | * | 
|  | * a) Driven by AMD 751 aka IRONGATE (northbridge): | 
|  | *     4 PCI slots | 
|  | *     1 AGP slot | 
|  | * | 
|  | * b) Driven by ALI M1543C (southbridge) | 
|  | *     2 ISA slots | 
|  | *     2 IDE connectors | 
|  | *     1 dual drive capable FDD controller | 
|  | *     2 serial ports | 
|  | *     1 ECP/EPP/SP parallel port | 
|  | *     2 USB ports | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/mm.h> | 
|  | #include <linux/sched.h> | 
|  | #include <linux/pci.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/reboot.h> | 
|  | #include <linux/bootmem.h> | 
|  | #include <linux/bitops.h> | 
|  |  | 
|  | #include <asm/ptrace.h> | 
|  | #include <asm/system.h> | 
|  | #include <asm/dma.h> | 
|  | #include <asm/irq.h> | 
|  | #include <asm/mmu_context.h> | 
|  | #include <asm/io.h> | 
|  | #include <asm/pci.h> | 
|  | #include <asm/pgtable.h> | 
|  | #include <asm/core_irongate.h> | 
|  | #include <asm/hwrpb.h> | 
|  | #include <asm/tlbflush.h> | 
|  |  | 
|  | #include "proto.h" | 
|  | #include "err_impl.h" | 
|  | #include "irq_impl.h" | 
|  | #include "pci_impl.h" | 
|  | #include "machvec_impl.h" | 
|  |  | 
|  |  | 
|  | static void __init | 
|  | nautilus_init_irq(void) | 
|  | { | 
|  | if (alpha_using_srm) { | 
|  | alpha_mv.device_interrupt = srm_device_interrupt; | 
|  | } | 
|  |  | 
|  | init_i8259a_irqs(); | 
|  | common_init_isa_dma(); | 
|  | } | 
|  |  | 
|  | static int __init | 
|  | nautilus_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | 
|  | { | 
|  | /* Preserve the IRQ set up by the console.  */ | 
|  |  | 
|  | u8 irq; | 
|  | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); | 
|  | return irq; | 
|  | } | 
|  |  | 
|  | void | 
|  | nautilus_kill_arch(int mode) | 
|  | { | 
|  | struct pci_bus *bus = pci_isa_hose->bus; | 
|  | u32 pmuport; | 
|  | int off; | 
|  |  | 
|  | switch (mode) { | 
|  | case LINUX_REBOOT_CMD_RESTART: | 
|  | if (! alpha_using_srm) { | 
|  | u8 t8; | 
|  | pci_bus_read_config_byte(bus, 0x38, 0x43, &t8); | 
|  | pci_bus_write_config_byte(bus, 0x38, 0x43, t8 | 0x80); | 
|  | outb(1, 0x92); | 
|  | outb(0, 0x92); | 
|  | /* NOTREACHED */ | 
|  | } | 
|  | break; | 
|  |  | 
|  | case LINUX_REBOOT_CMD_POWER_OFF: | 
|  | /* Assume M1543C */ | 
|  | off = 0x2000;		/* SLP_TYPE = 0, SLP_EN = 1 */ | 
|  | pci_bus_read_config_dword(bus, 0x88, 0x10, &pmuport); | 
|  | if (!pmuport) { | 
|  | /* M1535D/D+ */ | 
|  | off = 0x3400;	/* SLP_TYPE = 5, SLP_EN = 1 */ | 
|  | pci_bus_read_config_dword(bus, 0x88, 0xe0, &pmuport); | 
|  | } | 
|  | pmuport &= 0xfffe; | 
|  | outw(0xffff, pmuport);	/* Clear pending events. */ | 
|  | outw(off, pmuport + 4); | 
|  | /* NOTREACHED */ | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Perform analysis of a machine check that arrived from the system (NMI) */ | 
|  |  | 
|  | static void | 
|  | naut_sys_machine_check(unsigned long vector, unsigned long la_ptr, | 
|  | struct pt_regs *regs) | 
|  | { | 
|  | printk("PC %lx RA %lx\n", regs->pc, regs->r26); | 
|  | irongate_pci_clr_err(); | 
|  | } | 
|  |  | 
|  | /* Machine checks can come from two sources - those on the CPU and those | 
|  | in the system.  They are analysed separately but all starts here.  */ | 
|  |  | 
|  | void | 
|  | nautilus_machine_check(unsigned long vector, unsigned long la_ptr, | 
|  | struct pt_regs *regs) | 
|  | { | 
|  | char *mchk_class; | 
|  |  | 
|  | /* Now for some analysis.  Machine checks fall into two classes -- | 
|  | those picked up by the system, and those picked up by the CPU. | 
|  | Add to that the two levels of severity - correctable or not.  */ | 
|  |  | 
|  | if (vector == SCB_Q_SYSMCHK | 
|  | && ((IRONGATE0->dramms & 0x300) == 0x300)) { | 
|  | unsigned long nmi_ctl; | 
|  |  | 
|  | /* Clear ALI NMI */ | 
|  | nmi_ctl = inb(0x61); | 
|  | nmi_ctl |= 0x0c; | 
|  | outb(nmi_ctl, 0x61); | 
|  | nmi_ctl &= ~0x0c; | 
|  | outb(nmi_ctl, 0x61); | 
|  |  | 
|  | /* Write again clears error bits.  */ | 
|  | IRONGATE0->stat_cmd = IRONGATE0->stat_cmd & ~0x100; | 
|  | mb(); | 
|  | IRONGATE0->stat_cmd; | 
|  |  | 
|  | /* Write again clears error bits.  */ | 
|  | IRONGATE0->dramms = IRONGATE0->dramms; | 
|  | mb(); | 
|  | IRONGATE0->dramms; | 
|  |  | 
|  | draina(); | 
|  | wrmces(0x7); | 
|  | mb(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (vector == SCB_Q_SYSERR) | 
|  | mchk_class = "Correctable"; | 
|  | else if (vector == SCB_Q_SYSMCHK) | 
|  | mchk_class = "Fatal"; | 
|  | else { | 
|  | ev6_machine_check(vector, la_ptr, regs); | 
|  | return; | 
|  | } | 
|  |  | 
|  | printk(KERN_CRIT "NAUTILUS Machine check 0x%lx " | 
|  | "[%s System Machine Check (NMI)]\n", | 
|  | vector, mchk_class); | 
|  |  | 
|  | naut_sys_machine_check(vector, la_ptr, regs); | 
|  |  | 
|  | /* Tell the PALcode to clear the machine check */ | 
|  | draina(); | 
|  | wrmces(0x7); | 
|  | mb(); | 
|  | } | 
|  |  | 
|  | extern void free_reserved_mem(void *, void *); | 
|  |  | 
|  | static struct resource irongate_mem = { | 
|  | .name	= "Irongate PCI MEM", | 
|  | .flags	= IORESOURCE_MEM, | 
|  | }; | 
|  |  | 
|  | void __init | 
|  | nautilus_init_pci(void) | 
|  | { | 
|  | struct pci_controller *hose = hose_head; | 
|  | struct pci_bus *bus; | 
|  | struct pci_dev *irongate; | 
|  | unsigned long bus_align, bus_size, pci_mem; | 
|  | unsigned long memtop = max_low_pfn << PAGE_SHIFT; | 
|  |  | 
|  | /* Scan our single hose.  */ | 
|  | bus = pci_scan_bus(0, alpha_mv.pci_ops, hose); | 
|  | hose->bus = bus; | 
|  |  | 
|  | irongate = pci_find_slot(0, 0); | 
|  | bus->self = irongate; | 
|  | bus->resource[1] = &irongate_mem; | 
|  |  | 
|  | pci_bus_size_bridges(bus); | 
|  |  | 
|  | /* IO port range. */ | 
|  | bus->resource[0]->start = 0; | 
|  | bus->resource[0]->end = 0xffff; | 
|  |  | 
|  | /* Set up PCI memory range - limit is hardwired to 0xffffffff, | 
|  | base must be at aligned to 16Mb. */ | 
|  | bus_align = bus->resource[1]->start; | 
|  | bus_size = bus->resource[1]->end + 1 - bus_align; | 
|  | if (bus_align < 0x1000000UL) | 
|  | bus_align = 0x1000000UL; | 
|  |  | 
|  | pci_mem = (0x100000000UL - bus_size) & -bus_align; | 
|  |  | 
|  | bus->resource[1]->start = pci_mem; | 
|  | bus->resource[1]->end = 0xffffffffUL; | 
|  | if (request_resource(&iomem_resource, bus->resource[1]) < 0) | 
|  | printk(KERN_ERR "Failed to request MEM on hose 0\n"); | 
|  |  | 
|  | if (pci_mem < memtop) | 
|  | memtop = pci_mem; | 
|  | if (memtop > alpha_mv.min_mem_address) { | 
|  | free_reserved_mem(__va(alpha_mv.min_mem_address), | 
|  | __va(memtop)); | 
|  | printk("nautilus_init_pci: %ldk freed\n", | 
|  | (memtop - alpha_mv.min_mem_address) >> 10); | 
|  | } | 
|  |  | 
|  | if ((IRONGATE0->dev_vendor >> 16) > 0x7006)	/* Albacore? */ | 
|  | IRONGATE0->pci_mem = pci_mem; | 
|  |  | 
|  | pci_bus_assign_resources(bus); | 
|  | pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The System Vectors | 
|  | */ | 
|  |  | 
|  | struct alpha_machine_vector nautilus_mv __initmv = { | 
|  | .vector_name		= "Nautilus", | 
|  | DO_EV6_MMU, | 
|  | DO_DEFAULT_RTC, | 
|  | DO_IRONGATE_IO, | 
|  | .machine_check		= nautilus_machine_check, | 
|  | .max_isa_dma_address	= ALPHA_MAX_ISA_DMA_ADDRESS, | 
|  | .min_io_address		= DEFAULT_IO_BASE, | 
|  | .min_mem_address	= IRONGATE_DEFAULT_MEM_BASE, | 
|  |  | 
|  | .nr_irqs		= 16, | 
|  | .device_interrupt	= isa_device_interrupt, | 
|  |  | 
|  | .init_arch		= irongate_init_arch, | 
|  | .init_irq		= nautilus_init_irq, | 
|  | .init_rtc		= common_init_rtc, | 
|  | .init_pci		= nautilus_init_pci, | 
|  | .kill_arch		= nautilus_kill_arch, | 
|  | .pci_map_irq		= nautilus_map_irq, | 
|  | .pci_swizzle		= common_swizzle, | 
|  | }; | 
|  | ALIAS_MV(nautilus) |