| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *	linux/arch/alpha/kernel/sys_nautilus.c | 
|  | 3 | * | 
|  | 4 | *	Copyright (C) 1995 David A Rusling | 
|  | 5 | *	Copyright (C) 1998 Richard Henderson | 
|  | 6 | *	Copyright (C) 1999 Alpha Processor, Inc., | 
|  | 7 | *		(David Daniel, Stig Telfer, Soohoon Lee) | 
|  | 8 | * | 
|  | 9 | * Code supporting NAUTILUS systems. | 
|  | 10 | * | 
|  | 11 | * | 
|  | 12 | * NAUTILUS has the following I/O features: | 
|  | 13 | * | 
|  | 14 | * a) Driven by AMD 751 aka IRONGATE (northbridge): | 
|  | 15 | *     4 PCI slots | 
|  | 16 | *     1 AGP slot | 
|  | 17 | * | 
|  | 18 | * b) Driven by ALI M1543C (southbridge) | 
|  | 19 | *     2 ISA slots | 
|  | 20 | *     2 IDE connectors | 
|  | 21 | *     1 dual drive capable FDD controller | 
|  | 22 | *     2 serial ports | 
|  | 23 | *     1 ECP/EPP/SP parallel port | 
|  | 24 | *     2 USB ports | 
|  | 25 | */ | 
|  | 26 |  | 
|  | 27 | #include <linux/kernel.h> | 
|  | 28 | #include <linux/types.h> | 
|  | 29 | #include <linux/mm.h> | 
|  | 30 | #include <linux/sched.h> | 
|  | 31 | #include <linux/pci.h> | 
|  | 32 | #include <linux/init.h> | 
|  | 33 | #include <linux/reboot.h> | 
|  | 34 | #include <linux/bootmem.h> | 
|  | 35 | #include <linux/bitops.h> | 
|  | 36 |  | 
|  | 37 | #include <asm/ptrace.h> | 
|  | 38 | #include <asm/system.h> | 
|  | 39 | #include <asm/dma.h> | 
|  | 40 | #include <asm/irq.h> | 
|  | 41 | #include <asm/mmu_context.h> | 
|  | 42 | #include <asm/io.h> | 
|  | 43 | #include <asm/pci.h> | 
|  | 44 | #include <asm/pgtable.h> | 
|  | 45 | #include <asm/core_irongate.h> | 
|  | 46 | #include <asm/hwrpb.h> | 
|  | 47 | #include <asm/tlbflush.h> | 
|  | 48 |  | 
|  | 49 | #include "proto.h" | 
|  | 50 | #include "err_impl.h" | 
|  | 51 | #include "irq_impl.h" | 
|  | 52 | #include "pci_impl.h" | 
|  | 53 | #include "machvec_impl.h" | 
|  | 54 |  | 
|  | 55 |  | 
|  | 56 | static void __init | 
|  | 57 | nautilus_init_irq(void) | 
|  | 58 | { | 
|  | 59 | if (alpha_using_srm) { | 
|  | 60 | alpha_mv.device_interrupt = srm_device_interrupt; | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | init_i8259a_irqs(); | 
|  | 64 | common_init_isa_dma(); | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | static int __init | 
|  | 68 | nautilus_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | 
|  | 69 | { | 
|  | 70 | /* Preserve the IRQ set up by the console.  */ | 
|  | 71 |  | 
|  | 72 | u8 irq; | 
|  | 73 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); | 
|  | 74 | return irq; | 
|  | 75 | } | 
|  | 76 |  | 
|  | 77 | void | 
|  | 78 | nautilus_kill_arch(int mode) | 
|  | 79 | { | 
|  | 80 | struct pci_bus *bus = pci_isa_hose->bus; | 
|  | 81 | u32 pmuport; | 
|  | 82 | int off; | 
|  | 83 |  | 
|  | 84 | switch (mode) { | 
|  | 85 | case LINUX_REBOOT_CMD_RESTART: | 
|  | 86 | if (! alpha_using_srm) { | 
|  | 87 | u8 t8; | 
|  | 88 | pci_bus_read_config_byte(bus, 0x38, 0x43, &t8); | 
|  | 89 | pci_bus_write_config_byte(bus, 0x38, 0x43, t8 | 0x80); | 
|  | 90 | outb(1, 0x92); | 
|  | 91 | outb(0, 0x92); | 
|  | 92 | /* NOTREACHED */ | 
|  | 93 | } | 
|  | 94 | break; | 
|  | 95 |  | 
|  | 96 | case LINUX_REBOOT_CMD_POWER_OFF: | 
|  | 97 | /* Assume M1543C */ | 
|  | 98 | off = 0x2000;		/* SLP_TYPE = 0, SLP_EN = 1 */ | 
|  | 99 | pci_bus_read_config_dword(bus, 0x88, 0x10, &pmuport); | 
|  | 100 | if (!pmuport) { | 
|  | 101 | /* M1535D/D+ */ | 
|  | 102 | off = 0x3400;	/* SLP_TYPE = 5, SLP_EN = 1 */ | 
|  | 103 | pci_bus_read_config_dword(bus, 0x88, 0xe0, &pmuport); | 
|  | 104 | } | 
|  | 105 | pmuport &= 0xfffe; | 
|  | 106 | outw(0xffff, pmuport);	/* Clear pending events. */ | 
|  | 107 | outw(off, pmuport + 4); | 
|  | 108 | /* NOTREACHED */ | 
|  | 109 | break; | 
|  | 110 | } | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | /* Perform analysis of a machine check that arrived from the system (NMI) */ | 
|  | 114 |  | 
|  | 115 | static void | 
|  | 116 | naut_sys_machine_check(unsigned long vector, unsigned long la_ptr, | 
|  | 117 | struct pt_regs *regs) | 
|  | 118 | { | 
|  | 119 | printk("PC %lx RA %lx\n", regs->pc, regs->r26); | 
|  | 120 | irongate_pci_clr_err(); | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | /* Machine checks can come from two sources - those on the CPU and those | 
|  | 124 | in the system.  They are analysed separately but all starts here.  */ | 
|  | 125 |  | 
|  | 126 | void | 
|  | 127 | nautilus_machine_check(unsigned long vector, unsigned long la_ptr, | 
|  | 128 | struct pt_regs *regs) | 
|  | 129 | { | 
|  | 130 | char *mchk_class; | 
|  | 131 |  | 
|  | 132 | /* Now for some analysis.  Machine checks fall into two classes -- | 
|  | 133 | those picked up by the system, and those picked up by the CPU. | 
|  | 134 | Add to that the two levels of severity - correctable or not.  */ | 
|  | 135 |  | 
|  | 136 | if (vector == SCB_Q_SYSMCHK | 
|  | 137 | && ((IRONGATE0->dramms & 0x300) == 0x300)) { | 
|  | 138 | unsigned long nmi_ctl; | 
|  | 139 |  | 
|  | 140 | /* Clear ALI NMI */ | 
|  | 141 | nmi_ctl = inb(0x61); | 
|  | 142 | nmi_ctl |= 0x0c; | 
|  | 143 | outb(nmi_ctl, 0x61); | 
|  | 144 | nmi_ctl &= ~0x0c; | 
|  | 145 | outb(nmi_ctl, 0x61); | 
|  | 146 |  | 
|  | 147 | /* Write again clears error bits.  */ | 
|  | 148 | IRONGATE0->stat_cmd = IRONGATE0->stat_cmd & ~0x100; | 
|  | 149 | mb(); | 
|  | 150 | IRONGATE0->stat_cmd; | 
|  | 151 |  | 
|  | 152 | /* Write again clears error bits.  */ | 
|  | 153 | IRONGATE0->dramms = IRONGATE0->dramms; | 
|  | 154 | mb(); | 
|  | 155 | IRONGATE0->dramms; | 
|  | 156 |  | 
|  | 157 | draina(); | 
|  | 158 | wrmces(0x7); | 
|  | 159 | mb(); | 
|  | 160 | return; | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | if (vector == SCB_Q_SYSERR) | 
|  | 164 | mchk_class = "Correctable"; | 
|  | 165 | else if (vector == SCB_Q_SYSMCHK) | 
|  | 166 | mchk_class = "Fatal"; | 
|  | 167 | else { | 
|  | 168 | ev6_machine_check(vector, la_ptr, regs); | 
|  | 169 | return; | 
|  | 170 | } | 
|  | 171 |  | 
|  | 172 | printk(KERN_CRIT "NAUTILUS Machine check 0x%lx " | 
|  | 173 | "[%s System Machine Check (NMI)]\n", | 
|  | 174 | vector, mchk_class); | 
|  | 175 |  | 
|  | 176 | naut_sys_machine_check(vector, la_ptr, regs); | 
|  | 177 |  | 
|  | 178 | /* Tell the PALcode to clear the machine check */ | 
|  | 179 | draina(); | 
|  | 180 | wrmces(0x7); | 
|  | 181 | mb(); | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | extern void free_reserved_mem(void *, void *); | 
|  | 185 |  | 
|  | 186 | static struct resource irongate_mem = { | 
|  | 187 | .name	= "Irongate PCI MEM", | 
|  | 188 | .flags	= IORESOURCE_MEM, | 
|  | 189 | }; | 
|  | 190 |  | 
|  | 191 | void __init | 
|  | 192 | nautilus_init_pci(void) | 
|  | 193 | { | 
|  | 194 | struct pci_controller *hose = hose_head; | 
|  | 195 | struct pci_bus *bus; | 
|  | 196 | struct pci_dev *irongate; | 
|  | 197 | unsigned long bus_align, bus_size, pci_mem; | 
|  | 198 | unsigned long memtop = max_low_pfn << PAGE_SHIFT; | 
|  | 199 |  | 
|  | 200 | /* Scan our single hose.  */ | 
|  | 201 | bus = pci_scan_bus(0, alpha_mv.pci_ops, hose); | 
|  | 202 | hose->bus = bus; | 
|  | 203 |  | 
|  | 204 | irongate = pci_find_slot(0, 0); | 
|  | 205 | bus->self = irongate; | 
|  | 206 | bus->resource[1] = &irongate_mem; | 
|  | 207 |  | 
|  | 208 | pci_bus_size_bridges(bus); | 
|  | 209 |  | 
|  | 210 | /* IO port range. */ | 
|  | 211 | bus->resource[0]->start = 0; | 
|  | 212 | bus->resource[0]->end = 0xffff; | 
|  | 213 |  | 
|  | 214 | /* Set up PCI memory range - limit is hardwired to 0xffffffff, | 
|  | 215 | base must be at aligned to 16Mb. */ | 
|  | 216 | bus_align = bus->resource[1]->start; | 
|  | 217 | bus_size = bus->resource[1]->end + 1 - bus_align; | 
|  | 218 | if (bus_align < 0x1000000UL) | 
|  | 219 | bus_align = 0x1000000UL; | 
|  | 220 |  | 
|  | 221 | pci_mem = (0x100000000UL - bus_size) & -bus_align; | 
|  | 222 |  | 
|  | 223 | bus->resource[1]->start = pci_mem; | 
|  | 224 | bus->resource[1]->end = 0xffffffffUL; | 
|  | 225 | if (request_resource(&iomem_resource, bus->resource[1]) < 0) | 
|  | 226 | printk(KERN_ERR "Failed to request MEM on hose 0\n"); | 
|  | 227 |  | 
|  | 228 | if (pci_mem < memtop) | 
|  | 229 | memtop = pci_mem; | 
|  | 230 | if (memtop > alpha_mv.min_mem_address) { | 
|  | 231 | free_reserved_mem(__va(alpha_mv.min_mem_address), | 
|  | 232 | __va(memtop)); | 
|  | 233 | printk("nautilus_init_pci: %ldk freed\n", | 
|  | 234 | (memtop - alpha_mv.min_mem_address) >> 10); | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | if ((IRONGATE0->dev_vendor >> 16) > 0x7006)	/* Albacore? */ | 
|  | 238 | IRONGATE0->pci_mem = pci_mem; | 
|  | 239 |  | 
|  | 240 | pci_bus_assign_resources(bus); | 
|  | 241 | pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq); | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 | /* | 
|  | 245 | * The System Vectors | 
|  | 246 | */ | 
|  | 247 |  | 
|  | 248 | struct alpha_machine_vector nautilus_mv __initmv = { | 
|  | 249 | .vector_name		= "Nautilus", | 
|  | 250 | DO_EV6_MMU, | 
|  | 251 | DO_DEFAULT_RTC, | 
|  | 252 | DO_IRONGATE_IO, | 
|  | 253 | .machine_check		= nautilus_machine_check, | 
|  | 254 | .max_isa_dma_address	= ALPHA_MAX_ISA_DMA_ADDRESS, | 
|  | 255 | .min_io_address		= DEFAULT_IO_BASE, | 
|  | 256 | .min_mem_address	= IRONGATE_DEFAULT_MEM_BASE, | 
|  | 257 |  | 
|  | 258 | .nr_irqs		= 16, | 
|  | 259 | .device_interrupt	= isa_device_interrupt, | 
|  | 260 |  | 
|  | 261 | .init_arch		= irongate_init_arch, | 
|  | 262 | .init_irq		= nautilus_init_irq, | 
|  | 263 | .init_rtc		= common_init_rtc, | 
|  | 264 | .init_pci		= nautilus_init_pci, | 
|  | 265 | .kill_arch		= nautilus_kill_arch, | 
|  | 266 | .pci_map_irq		= nautilus_map_irq, | 
|  | 267 | .pci_swizzle		= common_swizzle, | 
|  | 268 | }; | 
|  | 269 | ALIAS_MV(nautilus) |