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