| 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; | 
| Ivan Kokshaysky | 7fc1a1a | 2007-04-16 22:53:21 -0700 | [diff] [blame] | 73 | /* UP1500: AGP INTA is actually routed to IRQ 5, not IRQ 10 as | 
|  | 74 | console reports. Check the device id of AGP bridge to distinguish | 
|  | 75 | UP1500 from UP1000/1100. Note: 'pin' is 2 due to bridge swizzle. */ | 
|  | 76 | if (slot == 1 && pin == 2 && | 
|  | 77 | dev->bus->self && dev->bus->self->device == 0x700f) | 
|  | 78 | return 5; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 79 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); | 
|  | 80 | return irq; | 
|  | 81 | } | 
|  | 82 |  | 
|  | 83 | void | 
|  | 84 | nautilus_kill_arch(int mode) | 
|  | 85 | { | 
|  | 86 | struct pci_bus *bus = pci_isa_hose->bus; | 
|  | 87 | u32 pmuport; | 
|  | 88 | int off; | 
|  | 89 |  | 
|  | 90 | switch (mode) { | 
|  | 91 | case LINUX_REBOOT_CMD_RESTART: | 
|  | 92 | if (! alpha_using_srm) { | 
|  | 93 | u8 t8; | 
|  | 94 | pci_bus_read_config_byte(bus, 0x38, 0x43, &t8); | 
|  | 95 | pci_bus_write_config_byte(bus, 0x38, 0x43, t8 | 0x80); | 
|  | 96 | outb(1, 0x92); | 
|  | 97 | outb(0, 0x92); | 
|  | 98 | /* NOTREACHED */ | 
|  | 99 | } | 
|  | 100 | break; | 
|  | 101 |  | 
|  | 102 | case LINUX_REBOOT_CMD_POWER_OFF: | 
|  | 103 | /* Assume M1543C */ | 
|  | 104 | off = 0x2000;		/* SLP_TYPE = 0, SLP_EN = 1 */ | 
|  | 105 | pci_bus_read_config_dword(bus, 0x88, 0x10, &pmuport); | 
|  | 106 | if (!pmuport) { | 
|  | 107 | /* M1535D/D+ */ | 
|  | 108 | off = 0x3400;	/* SLP_TYPE = 5, SLP_EN = 1 */ | 
|  | 109 | pci_bus_read_config_dword(bus, 0x88, 0xe0, &pmuport); | 
|  | 110 | } | 
|  | 111 | pmuport &= 0xfffe; | 
|  | 112 | outw(0xffff, pmuport);	/* Clear pending events. */ | 
|  | 113 | outw(off, pmuport + 4); | 
|  | 114 | /* NOTREACHED */ | 
|  | 115 | break; | 
|  | 116 | } | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | /* Perform analysis of a machine check that arrived from the system (NMI) */ | 
|  | 120 |  | 
|  | 121 | static void | 
|  | 122 | naut_sys_machine_check(unsigned long vector, unsigned long la_ptr, | 
|  | 123 | struct pt_regs *regs) | 
|  | 124 | { | 
|  | 125 | printk("PC %lx RA %lx\n", regs->pc, regs->r26); | 
|  | 126 | irongate_pci_clr_err(); | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | /* Machine checks can come from two sources - those on the CPU and those | 
|  | 130 | in the system.  They are analysed separately but all starts here.  */ | 
|  | 131 |  | 
|  | 132 | void | 
| Al Viro | 4fa1970 | 2006-10-08 14:44:38 +0100 | [diff] [blame] | 133 | nautilus_machine_check(unsigned long vector, unsigned long la_ptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 134 | { | 
|  | 135 | char *mchk_class; | 
|  | 136 |  | 
|  | 137 | /* Now for some analysis.  Machine checks fall into two classes -- | 
|  | 138 | those picked up by the system, and those picked up by the CPU. | 
|  | 139 | Add to that the two levels of severity - correctable or not.  */ | 
|  | 140 |  | 
|  | 141 | if (vector == SCB_Q_SYSMCHK | 
|  | 142 | && ((IRONGATE0->dramms & 0x300) == 0x300)) { | 
|  | 143 | unsigned long nmi_ctl; | 
|  | 144 |  | 
|  | 145 | /* Clear ALI NMI */ | 
|  | 146 | nmi_ctl = inb(0x61); | 
|  | 147 | nmi_ctl |= 0x0c; | 
|  | 148 | outb(nmi_ctl, 0x61); | 
|  | 149 | nmi_ctl &= ~0x0c; | 
|  | 150 | outb(nmi_ctl, 0x61); | 
|  | 151 |  | 
|  | 152 | /* Write again clears error bits.  */ | 
|  | 153 | IRONGATE0->stat_cmd = IRONGATE0->stat_cmd & ~0x100; | 
|  | 154 | mb(); | 
|  | 155 | IRONGATE0->stat_cmd; | 
|  | 156 |  | 
|  | 157 | /* Write again clears error bits.  */ | 
|  | 158 | IRONGATE0->dramms = IRONGATE0->dramms; | 
|  | 159 | mb(); | 
|  | 160 | IRONGATE0->dramms; | 
|  | 161 |  | 
|  | 162 | draina(); | 
|  | 163 | wrmces(0x7); | 
|  | 164 | mb(); | 
|  | 165 | return; | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | if (vector == SCB_Q_SYSERR) | 
|  | 169 | mchk_class = "Correctable"; | 
|  | 170 | else if (vector == SCB_Q_SYSMCHK) | 
|  | 171 | mchk_class = "Fatal"; | 
|  | 172 | else { | 
| Al Viro | 4fa1970 | 2006-10-08 14:44:38 +0100 | [diff] [blame] | 173 | ev6_machine_check(vector, la_ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 174 | return; | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | printk(KERN_CRIT "NAUTILUS Machine check 0x%lx " | 
|  | 178 | "[%s System Machine Check (NMI)]\n", | 
|  | 179 | vector, mchk_class); | 
|  | 180 |  | 
| Al Viro | 4fa1970 | 2006-10-08 14:44:38 +0100 | [diff] [blame] | 181 | naut_sys_machine_check(vector, la_ptr, get_irq_regs()); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 |  | 
|  | 183 | /* Tell the PALcode to clear the machine check */ | 
|  | 184 | draina(); | 
|  | 185 | wrmces(0x7); | 
|  | 186 | mb(); | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 | extern void free_reserved_mem(void *, void *); | 
| Ivan Kokshaysky | 72cff12 | 2008-04-24 16:51:55 +0400 | [diff] [blame] | 190 | extern void pcibios_claim_one_bus(struct pci_bus *); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 |  | 
|  | 192 | static struct resource irongate_mem = { | 
|  | 193 | .name	= "Irongate PCI MEM", | 
|  | 194 | .flags	= IORESOURCE_MEM, | 
|  | 195 | }; | 
|  | 196 |  | 
|  | 197 | void __init | 
|  | 198 | nautilus_init_pci(void) | 
|  | 199 | { | 
|  | 200 | struct pci_controller *hose = hose_head; | 
|  | 201 | struct pci_bus *bus; | 
|  | 202 | struct pci_dev *irongate; | 
|  | 203 | unsigned long bus_align, bus_size, pci_mem; | 
|  | 204 | unsigned long memtop = max_low_pfn << PAGE_SHIFT; | 
|  | 205 |  | 
|  | 206 | /* Scan our single hose.  */ | 
|  | 207 | bus = pci_scan_bus(0, alpha_mv.pci_ops, hose); | 
|  | 208 | hose->bus = bus; | 
| Ivan Kokshaysky | 72cff12 | 2008-04-24 16:51:55 +0400 | [diff] [blame] | 209 | pcibios_claim_one_bus(bus); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 |  | 
| Alan Cox | 074cec5 | 2006-12-06 20:33:59 -0800 | [diff] [blame] | 211 | irongate = pci_get_bus_and_slot(0, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 | bus->self = irongate; | 
|  | 213 | bus->resource[1] = &irongate_mem; | 
|  | 214 |  | 
|  | 215 | pci_bus_size_bridges(bus); | 
|  | 216 |  | 
|  | 217 | /* IO port range. */ | 
|  | 218 | bus->resource[0]->start = 0; | 
|  | 219 | bus->resource[0]->end = 0xffff; | 
|  | 220 |  | 
|  | 221 | /* Set up PCI memory range - limit is hardwired to 0xffffffff, | 
|  | 222 | base must be at aligned to 16Mb. */ | 
|  | 223 | bus_align = bus->resource[1]->start; | 
|  | 224 | bus_size = bus->resource[1]->end + 1 - bus_align; | 
|  | 225 | if (bus_align < 0x1000000UL) | 
|  | 226 | bus_align = 0x1000000UL; | 
|  | 227 |  | 
|  | 228 | pci_mem = (0x100000000UL - bus_size) & -bus_align; | 
|  | 229 |  | 
|  | 230 | bus->resource[1]->start = pci_mem; | 
|  | 231 | bus->resource[1]->end = 0xffffffffUL; | 
|  | 232 | if (request_resource(&iomem_resource, bus->resource[1]) < 0) | 
|  | 233 | printk(KERN_ERR "Failed to request MEM on hose 0\n"); | 
|  | 234 |  | 
|  | 235 | if (pci_mem < memtop) | 
|  | 236 | memtop = pci_mem; | 
|  | 237 | if (memtop > alpha_mv.min_mem_address) { | 
|  | 238 | free_reserved_mem(__va(alpha_mv.min_mem_address), | 
|  | 239 | __va(memtop)); | 
|  | 240 | printk("nautilus_init_pci: %ldk freed\n", | 
|  | 241 | (memtop - alpha_mv.min_mem_address) >> 10); | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 | if ((IRONGATE0->dev_vendor >> 16) > 0x7006)	/* Albacore? */ | 
|  | 245 | IRONGATE0->pci_mem = pci_mem; | 
|  | 246 |  | 
|  | 247 | pci_bus_assign_resources(bus); | 
|  | 248 | pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq); | 
|  | 249 | } | 
|  | 250 |  | 
|  | 251 | /* | 
|  | 252 | * The System Vectors | 
|  | 253 | */ | 
|  | 254 |  | 
|  | 255 | struct alpha_machine_vector nautilus_mv __initmv = { | 
|  | 256 | .vector_name		= "Nautilus", | 
|  | 257 | DO_EV6_MMU, | 
|  | 258 | DO_DEFAULT_RTC, | 
|  | 259 | DO_IRONGATE_IO, | 
|  | 260 | .machine_check		= nautilus_machine_check, | 
|  | 261 | .max_isa_dma_address	= ALPHA_MAX_ISA_DMA_ADDRESS, | 
|  | 262 | .min_io_address		= DEFAULT_IO_BASE, | 
|  | 263 | .min_mem_address	= IRONGATE_DEFAULT_MEM_BASE, | 
|  | 264 |  | 
|  | 265 | .nr_irqs		= 16, | 
|  | 266 | .device_interrupt	= isa_device_interrupt, | 
|  | 267 |  | 
|  | 268 | .init_arch		= irongate_init_arch, | 
|  | 269 | .init_irq		= nautilus_init_irq, | 
|  | 270 | .init_rtc		= common_init_rtc, | 
|  | 271 | .init_pci		= nautilus_init_pci, | 
|  | 272 | .kill_arch		= nautilus_kill_arch, | 
|  | 273 | .pci_map_irq		= nautilus_map_irq, | 
|  | 274 | .pci_swizzle		= common_swizzle, | 
|  | 275 | }; | 
|  | 276 | ALIAS_MV(nautilus) |