1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/arch/alpha/kernel/sys_nautilus.c |
4 | * |
5 | * Copyright (C) 1995 David A Rusling |
6 | * Copyright (C) 1998 Richard Henderson |
7 | * Copyright (C) 1999 Alpha Processor, Inc., |
8 | * (David Daniel, Stig Telfer, Soohoon Lee) |
9 | * |
10 | * Code supporting NAUTILUS systems. |
11 | * |
12 | * |
13 | * NAUTILUS has the following I/O features: |
14 | * |
15 | * a) Driven by AMD 751 aka IRONGATE (northbridge): |
16 | * 4 PCI slots |
17 | * 1 AGP slot |
18 | * |
19 | * b) Driven by ALI M1543C (southbridge) |
20 | * 2 ISA slots |
21 | * 2 IDE connectors |
22 | * 1 dual drive capable FDD controller |
23 | * 2 serial ports |
24 | * 1 ECP/EPP/SP parallel port |
25 | * 2 USB ports |
26 | */ |
27 | |
28 | #include <linux/kernel.h> |
29 | #include <linux/types.h> |
30 | #include <linux/mm.h> |
31 | #include <linux/sched.h> |
32 | #include <linux/pci.h> |
33 | #include <linux/init.h> |
34 | #include <linux/reboot.h> |
35 | #include <linux/memblock.h> |
36 | #include <linux/bitops.h> |
37 | |
38 | #include <asm/ptrace.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/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 |
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, val: &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, devfn: 0x38, where: 0x43, val: &t8); |
93 | pci_bus_write_config_byte(bus, devfn: 0x38, where: 0x43, val: t8 | 0x80); |
94 | outb(value: 1, port: 0x92); |
95 | outb(value: 0, port: 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, devfn: 0x88, where: 0x10, val: &pmuport); |
104 | if (!pmuport) { |
105 | /* M1535D/D+ */ |
106 | off = 0x3400; /* SLP_TYPE = 5, SLP_EN = 1 */ |
107 | pci_bus_read_config_dword(bus, devfn: 0x88, where: 0xe0, val: &pmuport); |
108 | } |
109 | pmuport &= 0xfffe; |
110 | outw(value: 0xffff, port: pmuport); /* Clear pending events. */ |
111 | outw(value: off, port: 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(port: 0x61); |
145 | nmi_ctl |= 0x0c; |
146 | outb(value: nmi_ctl, port: 0x61); |
147 | nmi_ctl &= ~0x0c; |
148 | outb(value: nmi_ctl, port: 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(mces: 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, regs: get_irq_regs()); |
180 | |
181 | /* Tell the PALcode to clear the machine check */ |
182 | draina(); |
183 | wrmces(mces: 0x7); |
184 | mb(); |
185 | } |
186 | |
187 | extern void pcibios_claim_one_bus(struct pci_bus *); |
188 | |
189 | static struct resource irongate_mem = { |
190 | .name = "Irongate PCI MEM" , |
191 | .flags = IORESOURCE_MEM, |
192 | }; |
193 | static struct resource busn_resource = { |
194 | .name = "PCI busn" , |
195 | .start = 0, |
196 | .end = 255, |
197 | .flags = IORESOURCE_BUS, |
198 | }; |
199 | |
200 | void __init |
201 | nautilus_init_pci(void) |
202 | { |
203 | struct pci_controller *hose = hose_head; |
204 | struct pci_host_bridge *bridge; |
205 | struct pci_bus *bus; |
206 | unsigned long bus_align, bus_size, pci_mem; |
207 | unsigned long memtop = max_low_pfn << PAGE_SHIFT; |
208 | |
209 | bridge = pci_alloc_host_bridge(priv: 0); |
210 | if (!bridge) |
211 | return; |
212 | |
213 | /* Use default IO. */ |
214 | pci_add_resource(resources: &bridge->windows, res: &ioport_resource); |
215 | /* Irongate PCI memory aperture, calculate required size before |
216 | setting it up. */ |
217 | pci_add_resource(resources: &bridge->windows, res: &irongate_mem); |
218 | |
219 | pci_add_resource(resources: &bridge->windows, res: &busn_resource); |
220 | bridge->dev.parent = NULL; |
221 | bridge->sysdata = hose; |
222 | bridge->busnr = 0; |
223 | bridge->ops = alpha_mv.pci_ops; |
224 | bridge->swizzle_irq = alpha_mv.pci_swizzle; |
225 | bridge->map_irq = alpha_mv.pci_map_irq; |
226 | bridge->size_windows = 1; |
227 | |
228 | /* Scan our single hose. */ |
229 | if (pci_scan_root_bus_bridge(bridge)) { |
230 | pci_free_host_bridge(bridge); |
231 | return; |
232 | } |
233 | bus = hose->bus = bridge->bus; |
234 | pcibios_claim_one_bus(bus); |
235 | |
236 | pci_bus_size_bridges(bus); |
237 | |
238 | /* Now we've got the size and alignment of PCI memory resources |
239 | stored in irongate_mem. Set up the PCI memory range: limit is |
240 | hardwired to 0xffffffff, base must be aligned to 16Mb. */ |
241 | bus_align = irongate_mem.start; |
242 | bus_size = irongate_mem.end + 1 - bus_align; |
243 | if (bus_align < 0x1000000UL) |
244 | bus_align = 0x1000000UL; |
245 | |
246 | pci_mem = (0x100000000UL - bus_size) & -bus_align; |
247 | irongate_mem.start = pci_mem; |
248 | irongate_mem.end = 0xffffffffUL; |
249 | |
250 | /* Register our newly calculated PCI memory window in the resource |
251 | tree. */ |
252 | if (request_resource(root: &iomem_resource, new: &irongate_mem) < 0) |
253 | printk(KERN_ERR "Failed to request MEM on hose 0\n" ); |
254 | |
255 | printk(KERN_INFO "Irongate pci_mem %pR\n" , &irongate_mem); |
256 | |
257 | if (pci_mem < memtop) |
258 | memtop = pci_mem; |
259 | if (memtop > alpha_mv.min_mem_address) { |
260 | free_reserved_area(__va(alpha_mv.min_mem_address), |
261 | __va(memtop), -1, NULL); |
262 | printk(KERN_INFO "nautilus_init_pci: %ldk freed\n" , |
263 | (memtop - alpha_mv.min_mem_address) >> 10); |
264 | } |
265 | if ((IRONGATE0->dev_vendor >> 16) > 0x7006) /* Albacore? */ |
266 | IRONGATE0->pci_mem = pci_mem; |
267 | |
268 | pci_bus_assign_resources(bus); |
269 | pci_bus_add_devices(bus); |
270 | } |
271 | |
272 | /* |
273 | * The System Vectors |
274 | */ |
275 | |
276 | struct alpha_machine_vector nautilus_mv __initmv = { |
277 | .vector_name = "Nautilus" , |
278 | DO_EV6_MMU, |
279 | DO_DEFAULT_RTC, |
280 | DO_IRONGATE_IO, |
281 | .machine_check = nautilus_machine_check, |
282 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, |
283 | .min_io_address = DEFAULT_IO_BASE, |
284 | .min_mem_address = IRONGATE_DEFAULT_MEM_BASE, |
285 | |
286 | .nr_irqs = 16, |
287 | .device_interrupt = isa_device_interrupt, |
288 | |
289 | .init_arch = irongate_init_arch, |
290 | .init_irq = nautilus_init_irq, |
291 | .init_rtc = common_init_rtc, |
292 | .init_pci = nautilus_init_pci, |
293 | .kill_arch = nautilus_kill_arch, |
294 | .pci_map_irq = nautilus_map_irq, |
295 | .pci_swizzle = common_swizzle, |
296 | }; |
297 | ALIAS_MV(nautilus) |
298 | |