diff options
Diffstat (limited to 'arch/alpha')
160 files changed, 49087 insertions, 0 deletions
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig new file mode 100644 index 000000000000..0c79b9d95f74 --- /dev/null +++ b/arch/alpha/Kconfig | |||
@@ -0,0 +1,606 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see Documentation/kbuild/kconfig-language.txt. | ||
4 | # | ||
5 | config ALPHA | ||
6 | bool | ||
7 | default y | ||
8 | help | ||
9 | The Alpha is a 64-bit general-purpose processor designed and | ||
10 | marketed by the Digital Equipment Corporation of blessed memory, | ||
11 | now Hewlett-Packard. The Alpha Linux project has a home page at | ||
12 | <http://www.alphalinux.org/>. | ||
13 | |||
14 | config 64BIT | ||
15 | def_bool y | ||
16 | |||
17 | config MMU | ||
18 | bool | ||
19 | default y | ||
20 | |||
21 | config UID16 | ||
22 | bool | ||
23 | |||
24 | config RWSEM_GENERIC_SPINLOCK | ||
25 | bool | ||
26 | |||
27 | config RWSEM_XCHGADD_ALGORITHM | ||
28 | bool | ||
29 | default y | ||
30 | |||
31 | config GENERIC_CALIBRATE_DELAY | ||
32 | bool | ||
33 | default y | ||
34 | |||
35 | config GENERIC_ISA_DMA | ||
36 | bool | ||
37 | default y | ||
38 | |||
39 | config GENERIC_IOMAP | ||
40 | bool | ||
41 | default n | ||
42 | |||
43 | source "init/Kconfig" | ||
44 | |||
45 | |||
46 | menu "System setup" | ||
47 | |||
48 | choice | ||
49 | prompt "Alpha system type" | ||
50 | default ALPHA_GENERIC | ||
51 | ---help--- | ||
52 | This is the system type of your hardware. A "generic" kernel will | ||
53 | run on any supported Alpha system. However, if you configure a | ||
54 | kernel for your specific system, it will be faster and smaller. | ||
55 | |||
56 | To find out what type of Alpha system you have, you may want to | ||
57 | check out the Linux/Alpha FAQ, accessible on the WWW from | ||
58 | <http://www.alphalinux.org/>. In summary: | ||
59 | |||
60 | Alcor/Alpha-XLT AS 600 | ||
61 | Alpha-XL XL-233, XL-266 | ||
62 | AlphaBook1 Alpha laptop | ||
63 | Avanti AS 200, AS 205, AS 250, AS 255, AS 300, AS 400 | ||
64 | Cabriolet AlphaPC64, AlphaPCI64 | ||
65 | DP264 DP264 | ||
66 | EB164 EB164 21164 evaluation board | ||
67 | EB64+ EB64+ 21064 evaluation board | ||
68 | EB66 EB66 21066 evaluation board | ||
69 | EB66+ EB66+ 21066 evaluation board | ||
70 | Jensen DECpc 150, DEC 2000 model 300, | ||
71 | DEC 2000 model 500 | ||
72 | LX164 AlphaPC164-LX | ||
73 | Lynx AS 2100A | ||
74 | Miata Personal Workstation 433a, 433au, 500a, | ||
75 | 500au, 600a, or 600au | ||
76 | Marvel AlphaServer ES47 / ES80 / GS1280 | ||
77 | Mikasa AS 1000 | ||
78 | Noname AXPpci33, UDB (Multia) | ||
79 | Noritake AS 1000A, AS 600A, AS 800 | ||
80 | PC164 AlphaPC164 | ||
81 | Rawhide AS 1200, AS 4000, AS 4100 | ||
82 | Ruffian RPX164-2, AlphaPC164-UX, AlphaPC164-BX | ||
83 | SX164 AlphaPC164-SX | ||
84 | Sable AS 2000, AS 2100 | ||
85 | Shark DS 20L | ||
86 | Takara Takara | ||
87 | Titan AlphaServer ES45 / DS25 | ||
88 | Wildfire AlphaServer GS 40/80/160/320 | ||
89 | |||
90 | If you don't know what to do, choose "generic". | ||
91 | |||
92 | config ALPHA_GENERIC | ||
93 | bool "Generic" | ||
94 | help | ||
95 | A generic kernel will run on all supported Alpha hardware. | ||
96 | |||
97 | config ALPHA_ALCOR | ||
98 | bool "Alcor/Alpha-XLT" | ||
99 | help | ||
100 | For systems using the Digital ALCOR chipset: 5 chips (4, 64-bit data | ||
101 | slices (Data Switch, DSW) - 208-pin PQFP and 1 control (Control, I/O | ||
102 | Address, CIA) - a 383 pin plastic PGA). It provides a DRAM | ||
103 | controller (256-bit memory bus) and a PCI interface. It also does | ||
104 | all the work required to support an external Bcache and to maintain | ||
105 | memory coherence when a PCI device DMAs into (or out of) memory. | ||
106 | |||
107 | config ALPHA_XL | ||
108 | bool "Alpha-XL" | ||
109 | help | ||
110 | XL-233 and XL-266-based Alpha systems. | ||
111 | |||
112 | config ALPHA_BOOK1 | ||
113 | bool "AlphaBook1" | ||
114 | help | ||
115 | Dec AlphaBook1/Burns Alpha-based laptops. | ||
116 | |||
117 | config ALPHA_AVANTI_CH | ||
118 | bool "Avanti" | ||
119 | |||
120 | config ALPHA_CABRIOLET | ||
121 | bool "Cabriolet" | ||
122 | help | ||
123 | Cabriolet AlphaPC64, AlphaPCI64 systems. Derived from EB64+ but now | ||
124 | baby-AT with Flash boot ROM, no on-board SCSI or Ethernet. 3 ISA | ||
125 | slots, 4 PCI slots (one pair are on a shared slot), uses plug-in | ||
126 | Bcache SIMMs. Requires power supply with 3.3V output. | ||
127 | |||
128 | config ALPHA_DP264 | ||
129 | bool "DP264" | ||
130 | help | ||
131 | Various 21264 systems with the tsunami core logic chipset. | ||
132 | API Networks: 264DP, UP2000(+), CS20; | ||
133 | Compaq: DS10(E,L), XP900, XP1000, DS20(E), ES40. | ||
134 | |||
135 | config ALPHA_EB164 | ||
136 | bool "EB164" | ||
137 | help | ||
138 | EB164 21164 evaluation board from DEC. Uses 21164 and ALCOR. Has | ||
139 | ISA and PCI expansion (3 ISA slots, 2 64-bit PCI slots (one is | ||
140 | shared with an ISA slot) and 2 32-bit PCI slots. Uses plus-in | ||
141 | Bcache SIMMs. I/O sub-system provides SuperI/O (2S, 1P, FD), KBD, | ||
142 | MOUSE (PS2 style), RTC/NVRAM. Boot ROM is Flash. PC-AT-sized | ||
143 | motherboard. Requires power supply with 3.3V output. | ||
144 | |||
145 | config ALPHA_EB64P_CH | ||
146 | bool "EB64+" | ||
147 | |||
148 | config ALPHA_EB66 | ||
149 | bool "EB66" | ||
150 | help | ||
151 | A Digital DS group board. Uses 21066 or 21066A. I/O sub-system is | ||
152 | identical to EB64+. Baby PC-AT size. Runs from standard PC power | ||
153 | supply. The EB66 schematic was published as a marketing poster | ||
154 | advertising the 21066 as "the first microprocessor in the world with | ||
155 | embedded PCI". | ||
156 | |||
157 | config ALPHA_EB66P | ||
158 | bool "EB66+" | ||
159 | help | ||
160 | Later variant of the EB66 board. | ||
161 | |||
162 | config ALPHA_EIGER | ||
163 | bool "Eiger" | ||
164 | help | ||
165 | Apparently an obscure OEM single-board computer based on the | ||
166 | Typhoon/Tsunami chipset family. Information on it is scanty. | ||
167 | |||
168 | config ALPHA_JENSEN | ||
169 | bool "Jensen" | ||
170 | help | ||
171 | DEC PC 150 AXP (aka Jensen): This is a very old Digital system - one | ||
172 | of the first-generation Alpha systems. A number of these systems | ||
173 | seem to be available on the second- hand market. The Jensen is a | ||
174 | floor-standing tower system which originally used a 150MHz 21064 It | ||
175 | used programmable logic to interface a 486 EISA I/O bridge to the | ||
176 | CPU. | ||
177 | |||
178 | config ALPHA_LX164 | ||
179 | bool "LX164" | ||
180 | help | ||
181 | A technical overview of this board is available at | ||
182 | <http://www.unix-ag.org/Linux-Alpha/Architectures/LX164.html>. | ||
183 | |||
184 | config ALPHA_LYNX | ||
185 | bool "Lynx" | ||
186 | help | ||
187 | AlphaServer 2100A-based systems. | ||
188 | |||
189 | config ALPHA_MARVEL | ||
190 | bool "Marvel" | ||
191 | help | ||
192 | AlphaServer ES47 / ES80 / GS1280 based on EV7. | ||
193 | |||
194 | config ALPHA_MIATA | ||
195 | bool "Miata" | ||
196 | help | ||
197 | The Digital PersonalWorkStation (PWS 433a, 433au, 500a, 500au, 600a, | ||
198 | or 600au). There is an Installation HOWTO for this hardware at | ||
199 | <http://eijk.homelinux.org/~stefan/miata.html>. | ||
200 | |||
201 | config ALPHA_MIKASA | ||
202 | bool "Mikasa" | ||
203 | help | ||
204 | AlphaServer 1000-based Alpha systems. | ||
205 | |||
206 | config ALPHA_NAUTILUS | ||
207 | bool "Nautilus" | ||
208 | help | ||
209 | Alpha systems based on the AMD 751 & ALI 1543C chipsets. | ||
210 | |||
211 | config ALPHA_NONAME_CH | ||
212 | bool "Noname" | ||
213 | |||
214 | config ALPHA_NORITAKE | ||
215 | bool "Noritake" | ||
216 | help | ||
217 | AlphaServer 1000A, AlphaServer 600A, and AlphaServer 800-based | ||
218 | systems. | ||
219 | |||
220 | config ALPHA_PC164 | ||
221 | bool "PC164" | ||
222 | |||
223 | config ALPHA_P2K | ||
224 | bool "Platform2000" | ||
225 | |||
226 | config ALPHA_RAWHIDE | ||
227 | bool "Rawhide" | ||
228 | help | ||
229 | AlphaServer 1200, AlphaServer 4000 and AlphaServer 4100 machines. | ||
230 | See HOWTO at | ||
231 | <http://www.alphalinux.org/docs/rawhide/4100_install.shtml>. | ||
232 | |||
233 | config ALPHA_RUFFIAN | ||
234 | bool "Ruffian" | ||
235 | help | ||
236 | Samsung APC164UX. There is a page on known problems and workarounds | ||
237 | at <http://www.alphalinux.org/faq/FAQ-11.html>. | ||
238 | |||
239 | config ALPHA_RX164 | ||
240 | bool "RX164" | ||
241 | |||
242 | config ALPHA_SX164 | ||
243 | bool "SX164" | ||
244 | |||
245 | config ALPHA_SABLE | ||
246 | bool "Sable" | ||
247 | help | ||
248 | Digital AlphaServer 2000 and 2100-based systems. | ||
249 | |||
250 | config ALPHA_SHARK | ||
251 | bool "Shark" | ||
252 | |||
253 | config ALPHA_TAKARA | ||
254 | bool "Takara" | ||
255 | help | ||
256 | Alpha 11164-based OEM single-board computer. | ||
257 | |||
258 | config ALPHA_TITAN | ||
259 | bool "Titan" | ||
260 | help | ||
261 | AlphaServer ES45/DS25 SMP based on EV68 and Titan chipset. | ||
262 | |||
263 | config ALPHA_WILDFIRE | ||
264 | bool "Wildfire" | ||
265 | help | ||
266 | AlphaServer GS 40/80/160/320 SMP based on the EV67 core. | ||
267 | |||
268 | endchoice | ||
269 | |||
270 | # clear all implied options (don't want default values for those): | ||
271 | # Most of these machines have ISA slots; not exactly sure which don't, | ||
272 | # and this doesn't activate hordes of code, so do it always. | ||
273 | config ISA | ||
274 | bool | ||
275 | default y | ||
276 | help | ||
277 | Find out whether you have ISA slots on your motherboard. ISA is the | ||
278 | name of a bus system, i.e. the way the CPU talks to the other stuff | ||
279 | inside your box. Other bus systems are PCI, EISA, MicroChannel | ||
280 | (MCA) or VESA. ISA is an older system, now being displaced by PCI; | ||
281 | newer boards don't support it. If you have ISA, say Y, otherwise N. | ||
282 | |||
283 | config PCI | ||
284 | bool | ||
285 | depends on !ALPHA_JENSEN | ||
286 | default y | ||
287 | help | ||
288 | Find out whether you have a PCI motherboard. PCI is the name of a | ||
289 | bus system, i.e. the way the CPU talks to the other stuff inside | ||
290 | your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or | ||
291 | VESA. If you have PCI, say Y, otherwise N. | ||
292 | |||
293 | The PCI-HOWTO, available from | ||
294 | <http://www.tldp.org/docs.html#howto>, contains valuable | ||
295 | information about which PCI hardware does work under Linux and which | ||
296 | doesn't. | ||
297 | |||
298 | config PCI_DOMAINS | ||
299 | bool | ||
300 | default y | ||
301 | |||
302 | config ALPHA_CORE_AGP | ||
303 | bool | ||
304 | depends on ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL | ||
305 | default y | ||
306 | |||
307 | config ALPHA_NONAME | ||
308 | bool | ||
309 | depends on ALPHA_BOOK1 || ALPHA_NONAME_CH | ||
310 | default y | ||
311 | help | ||
312 | The AXPpci33 (aka NoName), is based on the EB66 (includes the Multia | ||
313 | UDB). This design was produced by Digital's Technical OEM (TOEM) | ||
314 | group. It uses the 21066 processor running at 166MHz or 233MHz. It | ||
315 | is a baby-AT size, and runs from a standard PC power supply. It has | ||
316 | 5 ISA slots and 3 PCI slots (one pair are a shared slot). There are | ||
317 | 2 versions, with either PS/2 or large DIN connectors for the | ||
318 | keyboard. | ||
319 | |||
320 | config ALPHA_EV4 | ||
321 | bool | ||
322 | depends on ALPHA_JENSEN || (ALPHA_SABLE && !ALPHA_GAMMA) || ALPHA_LYNX || ALPHA_NORITAKE && !ALPHA_PRIMO || ALPHA_MIKASA && !ALPHA_PRIMO || ALPHA_CABRIOLET || ALPHA_AVANTI_CH || ALPHA_EB64P_CH || ALPHA_XL || ALPHA_NONAME || ALPHA_EB66 || ALPHA_EB66P || ALPHA_P2K | ||
323 | default y if !ALPHA_LYNX | ||
324 | |||
325 | config ALPHA_LCA | ||
326 | bool | ||
327 | depends on ALPHA_NONAME || ALPHA_EB66 || ALPHA_EB66P || ALPHA_P2K | ||
328 | default y | ||
329 | |||
330 | config ALPHA_APECS | ||
331 | bool | ||
332 | depends on !ALPHA_PRIMO && (ALPHA_NORITAKE || ALPHA_MIKASA) || ALPHA_CABRIOLET || ALPHA_AVANTI_CH || ALPHA_EB64P_CH || ALPHA_XL | ||
333 | default y | ||
334 | |||
335 | config ALPHA_EB64P | ||
336 | bool | ||
337 | depends on ALPHA_CABRIOLET || ALPHA_EB64P_CH | ||
338 | default y | ||
339 | help | ||
340 | Uses 21064 or 21064A and APECs. Has ISA and PCI expansion (3 ISA, | ||
341 | 2 PCI, one pair are on a shared slot). Supports 36-bit DRAM SIMs. | ||
342 | ISA bus generated by Intel SaturnI/O PCI-ISA bridge. On-board SCSI | ||
343 | (NCR 810 on PCI) Ethernet (Digital 21040), KBD, MOUSE (PS2 style), | ||
344 | SuperI/O (2S, 1P, FD), RTC/NVRAM. Boot ROM is EPROM. PC-AT size. | ||
345 | Runs from standard PC power supply. | ||
346 | |||
347 | config ALPHA_EV5 | ||
348 | bool "EV5 CPU(s) (model 5/xxx)?" if ALPHA_LYNX | ||
349 | default y if ALPHA_RX164 || ALPHA_RAWHIDE || ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_RUFFIAN || ALPHA_SABLE && ALPHA_GAMMA || ALPHA_NORITAKE && ALPHA_PRIMO || ALPHA_MIKASA && ALPHA_PRIMO || ALPHA_PC164 || ALPHA_TAKARA || ALPHA_EB164 || ALPHA_ALCOR | ||
350 | |||
351 | config ALPHA_EV4 | ||
352 | bool | ||
353 | default y if ALPHA_LYNX && !ALPHA_EV5 | ||
354 | |||
355 | config ALPHA_CIA | ||
356 | bool | ||
357 | depends on ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_RUFFIAN || ALPHA_NORITAKE && ALPHA_PRIMO || ALPHA_MIKASA && ALPHA_PRIMO || ALPHA_PC164 || ALPHA_TAKARA || ALPHA_EB164 || ALPHA_ALCOR | ||
358 | default y | ||
359 | |||
360 | config ALPHA_EV56 | ||
361 | bool "EV56 CPU (speed >= 366MHz)?" if ALPHA_ALCOR | ||
362 | default y if ALPHA_RX164 || ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_RUFFIAN || ALPHA_PC164 || ALPHA_TAKARA | ||
363 | |||
364 | config ALPHA_EV56 | ||
365 | prompt "EV56 CPU (speed >= 333MHz)?" | ||
366 | depends on ALPHA_NORITAKE && ALPHA_PRIMO | ||
367 | |||
368 | config ALPHA_EV56 | ||
369 | prompt "EV56 CPU (speed >= 400MHz)?" | ||
370 | depends on ALPHA_RAWHIDE | ||
371 | |||
372 | config ALPHA_PRIMO | ||
373 | bool "EV5 CPU daughtercard (model 5/xxx)?" | ||
374 | depends on ALPHA_NORITAKE || ALPHA_MIKASA | ||
375 | help | ||
376 | Say Y if you have an AS 1000 5/xxx or an AS 1000A 5/xxx. | ||
377 | |||
378 | config ALPHA_GAMMA | ||
379 | bool "EV5 CPU(s) (model 5/xxx)?" | ||
380 | depends on ALPHA_SABLE | ||
381 | help | ||
382 | Say Y if you have an AS 2000 5/xxx or an AS 2100 5/xxx. | ||
383 | |||
384 | config ALPHA_GAMMA | ||
385 | bool | ||
386 | depends on ALPHA_LYNX | ||
387 | default y | ||
388 | |||
389 | config ALPHA_T2 | ||
390 | bool | ||
391 | depends on ALPHA_SABLE || ALPHA_LYNX | ||
392 | default y | ||
393 | |||
394 | config ALPHA_PYXIS | ||
395 | bool | ||
396 | depends on ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_RUFFIAN | ||
397 | default y | ||
398 | |||
399 | config ALPHA_EV6 | ||
400 | bool | ||
401 | depends on ALPHA_NAUTILUS || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_SHARK || ALPHA_DP264 || ALPHA_EIGER || ALPHA_MARVEL | ||
402 | default y | ||
403 | |||
404 | config ALPHA_TSUNAMI | ||
405 | bool | ||
406 | depends on ALPHA_SHARK || ALPHA_DP264 || ALPHA_EIGER | ||
407 | default y | ||
408 | |||
409 | config ALPHA_EV67 | ||
410 | bool "EV67 (or later) CPU (speed > 600MHz)?" if ALPHA_DP264 || ALPHA_EIGER | ||
411 | default y if ALPHA_NAUTILUS || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_SHARK || ALPHA_MARVEL | ||
412 | help | ||
413 | Is this a machine based on the EV67 core? If in doubt, select N here | ||
414 | and the machine will be treated as an EV6. | ||
415 | |||
416 | config ALPHA_EV7 | ||
417 | bool | ||
418 | depends on ALPHA_MARVEL | ||
419 | default y | ||
420 | |||
421 | config ALPHA_MCPCIA | ||
422 | bool | ||
423 | depends on ALPHA_RAWHIDE | ||
424 | default y | ||
425 | |||
426 | config ALPHA_POLARIS | ||
427 | bool | ||
428 | depends on ALPHA_RX164 | ||
429 | default y | ||
430 | |||
431 | config ALPHA_IRONGATE | ||
432 | bool | ||
433 | depends on ALPHA_NAUTILUS | ||
434 | default y | ||
435 | |||
436 | config ALPHA_AVANTI | ||
437 | bool | ||
438 | depends on ALPHA_XL || ALPHA_AVANTI_CH | ||
439 | default y | ||
440 | help | ||
441 | Avanti AS 200, AS 205, AS 250, AS 255, AS 300, and AS 400-based | ||
442 | Alphas. Info at | ||
443 | <http://www.unix-ag.org/Linux-Alpha/Architectures/Avanti.html>. | ||
444 | |||
445 | config ALPHA_BROKEN_IRQ_MASK | ||
446 | bool | ||
447 | depends on ALPHA_GENERIC || ALPHA_PC164 | ||
448 | default y | ||
449 | |||
450 | config ALPHA_SRM | ||
451 | bool "Use SRM as bootloader" if ALPHA_CABRIOLET || ALPHA_AVANTI_CH || ALPHA_EB64P || ALPHA_PC164 || ALPHA_TAKARA || ALPHA_EB164 || ALPHA_ALCOR || ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_NAUTILUS || ALPHA_NONAME | ||
452 | default y if ALPHA_JENSEN || ALPHA_MIKASA || ALPHA_SABLE || ALPHA_LYNX || ALPHA_NORITAKE || ALPHA_DP264 || ALPHA_RAWHIDE || ALPHA_EIGER || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_SHARK || ALPHA_MARVEL | ||
453 | ---help--- | ||
454 | There are two different types of booting firmware on Alphas: SRM, | ||
455 | which is command line driven, and ARC, which uses menus and arrow | ||
456 | keys. Details about the Linux/Alpha booting process are contained in | ||
457 | the Linux/Alpha FAQ, accessible on the WWW from | ||
458 | <http://www.alphalinux.org/>. | ||
459 | |||
460 | The usual way to load Linux on an Alpha machine is to use MILO | ||
461 | (a bootloader that lets you pass command line parameters to the | ||
462 | kernel just like lilo does for the x86 architecture) which can be | ||
463 | loaded either from ARC or can be installed directly as a permanent | ||
464 | firmware replacement from floppy (which requires changing a certain | ||
465 | jumper on the motherboard). If you want to do either of these, say N | ||
466 | here. If MILO doesn't work on your system (true for Jensen | ||
467 | motherboards), you can bypass it altogether and boot Linux directly | ||
468 | from an SRM console; say Y here in order to do that. Note that you | ||
469 | won't be able to boot from an IDE disk using SRM. | ||
470 | |||
471 | If unsure, say N. | ||
472 | |||
473 | config EISA | ||
474 | bool | ||
475 | depends on ALPHA_GENERIC || ALPHA_JENSEN || ALPHA_ALCOR || ALPHA_MIKASA || ALPHA_SABLE || ALPHA_LYNX || ALPHA_NORITAKE || ALPHA_RAWHIDE | ||
476 | default y | ||
477 | |||
478 | config SMP | ||
479 | bool "Symmetric multi-processing support" | ||
480 | depends on ALPHA_SABLE || ALPHA_LYNX || ALPHA_RAWHIDE || ALPHA_DP264 || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_GENERIC || ALPHA_SHARK || ALPHA_MARVEL | ||
481 | ---help--- | ||
482 | This enables support for systems with more than one CPU. If you have | ||
483 | a system with only one CPU, like most personal computers, say N. If | ||
484 | you have a system with more than one CPU, say Y. | ||
485 | |||
486 | If you say N here, the kernel will run on single and multiprocessor | ||
487 | machines, but will use only one CPU of a multiprocessor machine. If | ||
488 | you say Y here, the kernel will run on many, but not all, | ||
489 | singleprocessor machines. On a singleprocessor machine, the kernel | ||
490 | will run faster if you say N here. | ||
491 | |||
492 | See also the <file:Documentation/smp.txt>, and the SMP-HOWTO | ||
493 | available at <http://www.tldp.org/docs.html#howto>. | ||
494 | |||
495 | If you don't know what to do here, say N. | ||
496 | |||
497 | config HAVE_DEC_LOCK | ||
498 | bool | ||
499 | depends on SMP | ||
500 | default y | ||
501 | |||
502 | config NR_CPUS | ||
503 | int "Maximum number of CPUs (2-64)" | ||
504 | range 2 64 | ||
505 | depends on SMP | ||
506 | default "64" | ||
507 | |||
508 | config DISCONTIGMEM | ||
509 | bool "Discontiguous Memory Support (EXPERIMENTAL)" | ||
510 | depends on EXPERIMENTAL | ||
511 | help | ||
512 | Say Y to upport efficient handling of discontiguous physical memory, | ||
513 | for architectures which are either NUMA (Non-Uniform Memory Access) | ||
514 | or have huge holes in the physical address space for other reasons. | ||
515 | See <file:Documentation/vm/numa> for more. | ||
516 | |||
517 | config NUMA | ||
518 | bool "NUMA Support (EXPERIMENTAL)" | ||
519 | depends on DISCONTIGMEM | ||
520 | help | ||
521 | Say Y to compile the kernel to support NUMA (Non-Uniform Memory | ||
522 | Access). This option is for configuring high-end multiprocessor | ||
523 | server machines. If in doubt, say N. | ||
524 | |||
525 | # LARGE_VMALLOC is racy, if you *really* need it then fix it first | ||
526 | config ALPHA_LARGE_VMALLOC | ||
527 | bool | ||
528 | ---help--- | ||
529 | Process creation and other aspects of virtual memory management can | ||
530 | be streamlined if we restrict the kernel to one PGD for all vmalloc | ||
531 | allocations. This equates to about 8GB. | ||
532 | |||
533 | Under normal circumstances, this is so far and above what is needed | ||
534 | as to be laughable. However, there are certain applications (such | ||
535 | as benchmark-grade in-kernel web serving) that can make use of as | ||
536 | much vmalloc space as is available. | ||
537 | |||
538 | Say N unless you know you need gobs and gobs of vmalloc space. | ||
539 | |||
540 | config VERBOSE_MCHECK | ||
541 | bool "Verbose Machine Checks" | ||
542 | |||
543 | config VERBOSE_MCHECK_ON | ||
544 | int "Verbose Printing Mode (0=off, 1=on, 2=all)" | ||
545 | depends on VERBOSE_MCHECK | ||
546 | default 1 | ||
547 | ---help--- | ||
548 | This option allows the default printing mode to be set, and then | ||
549 | possibly overridden by a boot command argument. | ||
550 | |||
551 | For example, if one wanted the option of printing verbose | ||
552 | machine checks, but wanted the default to be as if verbose | ||
553 | machine check printing was turned off, then one would choose | ||
554 | the printing mode to be 0. Then, upon reboot, one could add | ||
555 | the boot command line "verbose_mcheck=1" to get the normal | ||
556 | verbose machine check printing, or "verbose_mcheck=2" to get | ||
557 | the maximum information available. | ||
558 | |||
559 | Take the default (1) unless you want more control or more info. | ||
560 | |||
561 | source "drivers/pci/Kconfig" | ||
562 | source "drivers/eisa/Kconfig" | ||
563 | |||
564 | source "drivers/pcmcia/Kconfig" | ||
565 | |||
566 | config SRM_ENV | ||
567 | tristate "SRM environment through procfs" | ||
568 | depends on PROC_FS | ||
569 | ---help--- | ||
570 | If you enable this option, a subdirectory inside /proc called | ||
571 | /proc/srm_environment will give you access to the all important | ||
572 | SRM environment variables (those which have a name) and also | ||
573 | to all others (by their internal number). | ||
574 | |||
575 | SRM is something like a BIOS for Alpha machines. There are some | ||
576 | other such BIOSes, like AlphaBIOS, which this driver cannot | ||
577 | support (hey, that's not SRM!). | ||
578 | |||
579 | Despite the fact that this driver doesn't work on all Alphas (but | ||
580 | only on those which have SRM as their firmware), it's save to | ||
581 | build it even if your particular machine doesn't know about SRM | ||
582 | (or if you intend to compile a generic kernel). It will simply | ||
583 | not create those subdirectory in /proc (and give you some warning, | ||
584 | of course). | ||
585 | |||
586 | This driver is also available as a module and will be called | ||
587 | srm_env then. | ||
588 | |||
589 | source "fs/Kconfig.binfmt" | ||
590 | |||
591 | endmenu | ||
592 | |||
593 | source "drivers/Kconfig" | ||
594 | |||
595 | source "fs/Kconfig" | ||
596 | |||
597 | source "arch/alpha/oprofile/Kconfig" | ||
598 | |||
599 | source "arch/alpha/Kconfig.debug" | ||
600 | |||
601 | source "security/Kconfig" | ||
602 | |||
603 | source "crypto/Kconfig" | ||
604 | |||
605 | source "lib/Kconfig" | ||
606 | |||
diff --git a/arch/alpha/Kconfig.debug b/arch/alpha/Kconfig.debug new file mode 100644 index 000000000000..36d0106c32eb --- /dev/null +++ b/arch/alpha/Kconfig.debug | |||
@@ -0,0 +1,59 @@ | |||
1 | menu "Kernel hacking" | ||
2 | |||
3 | source "lib/Kconfig.debug" | ||
4 | |||
5 | config EARLY_PRINTK | ||
6 | bool | ||
7 | depends on ALPHA_GENERIC || ALPHA_SRM | ||
8 | default y | ||
9 | |||
10 | config DEBUG_RWLOCK | ||
11 | bool "Read-write spinlock debugging" | ||
12 | depends on DEBUG_KERNEL | ||
13 | help | ||
14 | If you say Y here then read-write lock processing will count how many | ||
15 | times it has tried to get the lock and issue an error message after | ||
16 | too many attempts. If you suspect a rwlock problem or a kernel | ||
17 | hacker asks for this option then say Y. Otherwise say N. | ||
18 | |||
19 | config DEBUG_SEMAPHORE | ||
20 | bool "Semaphore debugging" | ||
21 | depends on DEBUG_KERNEL | ||
22 | help | ||
23 | If you say Y here then semaphore processing will issue lots of | ||
24 | verbose debugging messages. If you suspect a semaphore problem or a | ||
25 | kernel hacker asks for this option then say Y. Otherwise say N. | ||
26 | |||
27 | config ALPHA_LEGACY_START_ADDRESS | ||
28 | bool "Legacy kernel start address" | ||
29 | depends on ALPHA_GENERIC | ||
30 | default n | ||
31 | ---help--- | ||
32 | The 2.4 kernel changed the kernel start address from 0x310000 | ||
33 | to 0x810000 to make room for the Wildfire's larger SRM console. | ||
34 | Recent consoles on Titan and Marvel machines also require the | ||
35 | extra room. | ||
36 | |||
37 | If you're using aboot 0.7 or later, the bootloader will examine the | ||
38 | ELF headers to determine where to transfer control. Unfortunately, | ||
39 | most older bootloaders -- APB or MILO -- hardcoded the kernel start | ||
40 | address rather than examining the ELF headers, and the result is a | ||
41 | hard lockup. | ||
42 | |||
43 | Say Y if you have a broken bootloader. Say N if you do not, or if | ||
44 | you wish to run on Wildfire, Titan, or Marvel. | ||
45 | |||
46 | config ALPHA_LEGACY_START_ADDRESS | ||
47 | bool | ||
48 | depends on !ALPHA_GENERIC && !ALPHA_TITAN && !ALPHA_MARVEL && !ALPHA_WILDFIRE | ||
49 | default y | ||
50 | |||
51 | config MATHEMU | ||
52 | tristate "Kernel FP software completion" if DEBUG_KERNEL && !SMP | ||
53 | default y if !DEBUG_KERNEL || SMP | ||
54 | help | ||
55 | This option is required for IEEE compliant floating point arithmetic | ||
56 | on the Alpha. The only time you would ever not say Y is to say M in | ||
57 | order to debug the code. Say Y unless you know what you are doing. | ||
58 | |||
59 | endmenu | ||
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile new file mode 100644 index 000000000000..22ebfb2be0e4 --- /dev/null +++ b/arch/alpha/Makefile | |||
@@ -0,0 +1,130 @@ | |||
1 | # | ||
2 | # alpha/Makefile | ||
3 | # | ||
4 | # This file is subject to the terms and conditions of the GNU General Public | ||
5 | # License. See the file "COPYING" in the main directory of this archive | ||
6 | # for more details. | ||
7 | # | ||
8 | # Copyright (C) 1994 by Linus Torvalds | ||
9 | # | ||
10 | |||
11 | NM := $(NM) -B | ||
12 | |||
13 | LDFLAGS_vmlinux := -static -N #-relax | ||
14 | CHECKFLAGS += -D__alpha__ -m64 | ||
15 | cflags-y := -pipe -mno-fp-regs -ffixed-8 | ||
16 | |||
17 | # Determine if we can use the BWX instructions with GAS. | ||
18 | old_gas := $(shell if $(AS) --version 2>&1 | grep 'version 2.7' > /dev/null; then echo y; else echo n; fi) | ||
19 | |||
20 | ifeq ($(old_gas),y) | ||
21 | $(error The assembler '$(AS)' does not support the BWX instruction) | ||
22 | endif | ||
23 | |||
24 | # Determine if GCC understands the -mcpu= option. | ||
25 | have_mcpu := $(call cc-option-yn, -mcpu=ev5) | ||
26 | have_mcpu_pca56 := $(call cc-option-yn, -mcpu=pca56) | ||
27 | have_mcpu_ev6 := $(call cc-option-yn, -mcpu=ev6) | ||
28 | have_mcpu_ev67 := $(call cc-option-yn, -mcpu=ev67) | ||
29 | have_msmall_data := $(call cc-option-yn, -msmall-data) | ||
30 | |||
31 | cflags-$(have_msmall_data) += -msmall-data | ||
32 | |||
33 | # Turn on the proper cpu optimizations. | ||
34 | ifeq ($(have_mcpu),y) | ||
35 | mcpu_done := n | ||
36 | # If GENERIC, make sure to turn off any instruction set extensions that | ||
37 | # the host compiler might have on by default. Given that EV4 and EV5 | ||
38 | # have the same instruction set, prefer EV5 because an EV5 schedule is | ||
39 | # more likely to keep an EV4 processor busy than vice-versa. | ||
40 | ifeq ($(CONFIG_ALPHA_GENERIC),y) | ||
41 | mcpu := ev5 | ||
42 | mcpu_done := y | ||
43 | endif | ||
44 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_SX164)$(have_mcpu_pca56),nyy) | ||
45 | mcpu := pca56 | ||
46 | mcpu_done := y | ||
47 | endif | ||
48 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS)$(have_mcpu_pca56),nyy) | ||
49 | mcpu := pca56 | ||
50 | mcpu_done := y | ||
51 | endif | ||
52 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV4),ny) | ||
53 | mcpu := ev4 | ||
54 | mcpu_done := y | ||
55 | endif | ||
56 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV56),ny) | ||
57 | mcpu := ev56 | ||
58 | mcpu_done := y | ||
59 | endif | ||
60 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV5),ny) | ||
61 | mcpu := ev5 | ||
62 | mcpu_done := y | ||
63 | endif | ||
64 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV67)$(have_mcpu_ev67),nyy) | ||
65 | mcpu := ev67 | ||
66 | mcpu_done := y | ||
67 | endif | ||
68 | ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV6),ny) | ||
69 | ifeq ($(have_mcpu_ev6),y) | ||
70 | mcpu := ev6 | ||
71 | else | ||
72 | ifeq ($(have_mcpu_pca56),y) | ||
73 | mcpu := pca56 | ||
74 | else | ||
75 | mcpu=ev56 | ||
76 | endif | ||
77 | endif | ||
78 | mcpu_done := y | ||
79 | endif | ||
80 | cflags-$(mcpu_done) += -mcpu=$(mcpu) | ||
81 | endif | ||
82 | |||
83 | |||
84 | # For TSUNAMI, we must have the assembler not emulate our instructions. | ||
85 | # The same is true for IRONGATE, POLARIS, PYXIS. | ||
86 | # BWX is most important, but we don't really want any emulation ever. | ||
87 | CFLAGS += $(cflags-y) -Wa,-mev6 | ||
88 | |||
89 | head-y := arch/alpha/kernel/head.o | ||
90 | |||
91 | core-y += arch/alpha/kernel/ arch/alpha/mm/ | ||
92 | core-$(CONFIG_MATHEMU) += arch/alpha/math-emu/ | ||
93 | drivers-$(CONFIG_OPROFILE) += arch/alpha/oprofile/ | ||
94 | libs-y += arch/alpha/lib/ | ||
95 | |||
96 | # export what is needed by arch/alpha/boot/Makefile | ||
97 | LIBS_Y := $(patsubst %/, %/lib.a, $(libs-y)) | ||
98 | export LIBS_Y | ||
99 | |||
100 | boot := arch/alpha/boot | ||
101 | |||
102 | #Default target when executing make with no arguments | ||
103 | all boot: $(boot)/vmlinux.gz | ||
104 | |||
105 | $(boot)/vmlinux.gz: vmlinux | ||
106 | $(Q)$(MAKE) $(build)=$(boot) $@ | ||
107 | |||
108 | bootimage bootpfile bootpzfile: vmlinux | ||
109 | $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ | ||
110 | |||
111 | |||
112 | prepare: include/asm-$(ARCH)/asm_offsets.h | ||
113 | |||
114 | arch/$(ARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \ | ||
115 | include/config/MARKER | ||
116 | |||
117 | include/asm-$(ARCH)/asm_offsets.h: arch/$(ARCH)/kernel/asm-offsets.s | ||
118 | $(call filechk,gen-asm-offsets) | ||
119 | |||
120 | archclean: | ||
121 | $(Q)$(MAKE) $(clean)=$(boot) | ||
122 | |||
123 | CLEAN_FILES += include/asm-$(ARCH)/asm_offsets.h | ||
124 | |||
125 | define archhelp | ||
126 | echo '* boot - Compressed kernel image (arch/alpha/boot/vmlinux.gz)' | ||
127 | echo ' bootimage - SRM bootable image (arch/alpha/boot/bootimage)' | ||
128 | echo ' bootpfile - BOOTP bootable image (arch/alpha/boot/bootpfile)' | ||
129 | echo ' bootpzfile - compressed kernel BOOTP image (arch/alpha/boot/bootpzfile)' | ||
130 | endef | ||
diff --git a/arch/alpha/boot/Makefile b/arch/alpha/boot/Makefile new file mode 100644 index 000000000000..e1ae14cd2b4e --- /dev/null +++ b/arch/alpha/boot/Makefile | |||
@@ -0,0 +1,116 @@ | |||
1 | # | ||
2 | # arch/alpha/boot/Makefile | ||
3 | # | ||
4 | # This file is subject to the terms and conditions of the GNU General Public | ||
5 | # License. See the file "COPYING" in the main directory of this archive | ||
6 | # for more details. | ||
7 | # | ||
8 | # Copyright (C) 1994 by Linus Torvalds | ||
9 | # | ||
10 | |||
11 | hostprogs-y := tools/mkbb tools/objstrip | ||
12 | targets := vmlinux.gz vmlinux \ | ||
13 | vmlinux.nh tools/lxboot tools/bootlx tools/bootph \ | ||
14 | tools/bootpzh bootloader bootpheader bootpzheader | ||
15 | OBJSTRIP := $(obj)/tools/objstrip | ||
16 | |||
17 | # SRM bootable image. Copy to offset 512 of a partition. | ||
18 | $(obj)/bootimage: $(addprefix $(obj)/tools/,mkbb lxboot bootlx) $(obj)/vmlinux.nh | ||
19 | ( cat $(obj)/tools/lxboot $(obj)/tools/bootlx $(obj)/vmlinux.nh ) > $@ | ||
20 | $(obj)/tools/mkbb $@ $(obj)/tools/lxboot | ||
21 | @echo ' Bootimage $@ is ready' | ||
22 | |||
23 | # BOOTP bootable image. Define INITRD during make to append initrd image. | ||
24 | $(obj)/bootpfile: $(obj)/tools/bootph $(obj)/vmlinux.nh | ||
25 | cat $(obj)/tools/bootph $(obj)/vmlinux.nh > $@ | ||
26 | ifdef INITRD | ||
27 | cat $(INITRD) >> $@ | ||
28 | endif | ||
29 | |||
30 | # Compressed kernel BOOTP bootable image. | ||
31 | # Define INITRD during make to append initrd image. | ||
32 | $(obj)/bootpzfile: $(obj)/tools/bootpzh $(obj)/vmlinux.nh.gz | ||
33 | cat $(obj)/tools/bootpzh $(obj)/vmlinux.nh.gz > $@ | ||
34 | ifdef INITRD | ||
35 | cat $(INITRD) >> $@ | ||
36 | endif | ||
37 | |||
38 | # Compressed kernel image | ||
39 | $(obj)/vmlinux.gz: $(obj)/vmlinux FORCE | ||
40 | $(call if_changed,gzip) | ||
41 | @echo ' Kernel $@ is ready' | ||
42 | |||
43 | $(obj)/main.o: $(obj)/ksize.h | ||
44 | $(obj)/bootp.o: $(obj)/ksize.h | ||
45 | $(obj)/bootpz.o: $(obj)/kzsize.h | ||
46 | |||
47 | $(obj)/ksize.h: $(obj)/vmlinux.nh FORCE | ||
48 | echo "#define KERNEL_SIZE `ls -l $(obj)/vmlinux.nh | awk '{print $$5}'`" > $@T | ||
49 | ifdef INITRD | ||
50 | [ -f $(INITRD) ] || exit 1 | ||
51 | echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T | ||
52 | endif | ||
53 | cmp -s $@T $@ || mv -f $@T $@ | ||
54 | rm -f $@T | ||
55 | |||
56 | $(obj)/kzsize.h: $(obj)/vmlinux.nh.gz FORCE | ||
57 | echo "#define KERNEL_SIZE `ls -l $(obj)/vmlinux.nh | awk '{print $$5}'`" > $@T | ||
58 | echo "#define KERNEL_Z_SIZE `ls -l $(obj)/vmlinux.nh.gz | awk '{print $$5}'`" >> $@T | ||
59 | ifdef INITRD | ||
60 | [ -f $(INITRD) ] || exit 1 | ||
61 | echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T | ||
62 | endif | ||
63 | cmp -s $@T $@ || mv -f $@T $@ | ||
64 | rm -f $@T | ||
65 | |||
66 | quiet_cmd_strip = STRIP $@ | ||
67 | cmd_strip = $(STRIP) -o $@ $< | ||
68 | |||
69 | $(obj)/vmlinux: vmlinux FORCE | ||
70 | $(call if_changed,strip) | ||
71 | |||
72 | quiet_cmd_objstrip = OBJSTRIP $@ | ||
73 | cmd_objstrip = $(OBJSTRIP) $(OSFLAGS_$(@F)) $< $@ | ||
74 | |||
75 | OSFLAGS_vmlinux.nh := -v | ||
76 | OSFLAGS_lxboot := -p | ||
77 | OSFLAGS_bootlx := -vb | ||
78 | OSFLAGS_bootph := -vb | ||
79 | OSFLAGS_bootpzh := -vb | ||
80 | |||
81 | $(obj)/vmlinux.nh: vmlinux $(OBJSTRIP) FORCE | ||
82 | $(call if_changed,objstrip) | ||
83 | |||
84 | $(obj)/vmlinux.nh.gz: $(obj)/vmlinux.nh FORCE | ||
85 | $(call if_changed,gzip) | ||
86 | |||
87 | $(obj)/tools/lxboot: $(obj)/bootloader $(OBJSTRIP) FORCE | ||
88 | $(call if_changed,objstrip) | ||
89 | |||
90 | $(obj)/tools/bootlx: $(obj)/bootloader $(OBJSTRIP) FORCE | ||
91 | $(call if_changed,objstrip) | ||
92 | |||
93 | $(obj)/tools/bootph: $(obj)/bootpheader $(OBJSTRIP) FORCE | ||
94 | $(call if_changed,objstrip) | ||
95 | |||
96 | $(obj)/tools/bootpzh: $(obj)/bootpzheader $(OBJSTRIP) FORCE | ||
97 | $(call if_changed,objstrip) | ||
98 | |||
99 | LDFLAGS_bootloader := -static -uvsprintf -T #-N -relax | ||
100 | LDFLAGS_bootpheader := -static -uvsprintf -T #-N -relax | ||
101 | LDFLAGS_bootpzheader := -static -uvsprintf -T #-N -relax | ||
102 | |||
103 | OBJ_bootlx := $(obj)/head.o $(obj)/main.o | ||
104 | OBJ_bootph := $(obj)/head.o $(obj)/bootp.o | ||
105 | OBJ_bootpzh := $(obj)/head.o $(obj)/bootpz.o $(obj)/misc.o | ||
106 | |||
107 | $(obj)/bootloader: $(obj)/bootloader.lds $(OBJ_bootlx) FORCE | ||
108 | $(call if_changed,ld) | ||
109 | |||
110 | $(obj)/bootpheader: $(obj)/bootloader.lds $(OBJ_bootph) $(LIBS_Y) FORCE | ||
111 | $(call if_changed,ld) | ||
112 | |||
113 | $(obj)/bootpzheader: $(obj)/bootloader.lds $(OBJ_bootpzh) $(LIBS_Y) FORCE | ||
114 | $(call if_changed,ld) | ||
115 | |||
116 | $(obj)/misc.o: lib/inflate.c | ||
diff --git a/arch/alpha/boot/bootloader.lds b/arch/alpha/boot/bootloader.lds new file mode 100644 index 000000000000..31c081ce1d50 --- /dev/null +++ b/arch/alpha/boot/bootloader.lds | |||
@@ -0,0 +1,24 @@ | |||
1 | OUTPUT_FORMAT("elf64-alpha") | ||
2 | ENTRY(__start) | ||
3 | printk = srm_printk; | ||
4 | SECTIONS | ||
5 | { | ||
6 | . = 0x20000000; | ||
7 | .text : { *(.text) } | ||
8 | _etext = .; | ||
9 | PROVIDE (etext = .); | ||
10 | .rodata : { *(.rodata) *(.rodata.*) } | ||
11 | .data : { *(.data) CONSTRUCTORS } | ||
12 | .got : { *(.got) } | ||
13 | .sdata : { *(.sdata) } | ||
14 | _edata = .; | ||
15 | PROVIDE (edata = .); | ||
16 | .sbss : { *(.sbss) *(.scommon) } | ||
17 | .bss : { *(.bss) *(COMMON) } | ||
18 | _end = . ; | ||
19 | PROVIDE (end = .); | ||
20 | |||
21 | .mdebug 0 : { *(.mdebug) } | ||
22 | .note 0 : { *(.note) } | ||
23 | .comment 0 : { *(.comment) } | ||
24 | } | ||
diff --git a/arch/alpha/boot/bootp.c b/arch/alpha/boot/bootp.c new file mode 100644 index 000000000000..ec53c28e33de --- /dev/null +++ b/arch/alpha/boot/bootp.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * arch/alpha/boot/bootp.c | ||
3 | * | ||
4 | * Copyright (C) 1997 Jay Estabrook | ||
5 | * | ||
6 | * This file is used for creating a bootp file for the Linux/AXP kernel | ||
7 | * | ||
8 | * based significantly on the arch/alpha/boot/main.c of Linus Torvalds | ||
9 | */ | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/string.h> | ||
12 | #include <linux/version.h> | ||
13 | #include <linux/mm.h> | ||
14 | |||
15 | #include <asm/system.h> | ||
16 | #include <asm/console.h> | ||
17 | #include <asm/hwrpb.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/io.h> | ||
20 | |||
21 | #include <stdarg.h> | ||
22 | |||
23 | #include "ksize.h" | ||
24 | |||
25 | extern unsigned long switch_to_osf_pal(unsigned long nr, | ||
26 | struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, | ||
27 | unsigned long *vptb); | ||
28 | |||
29 | extern void move_stack(unsigned long new_stack); | ||
30 | |||
31 | struct hwrpb_struct *hwrpb = INIT_HWRPB; | ||
32 | static struct pcb_struct pcb_va[1]; | ||
33 | |||
34 | /* | ||
35 | * Find a physical address of a virtual object.. | ||
36 | * | ||
37 | * This is easy using the virtual page table address. | ||
38 | */ | ||
39 | |||
40 | static inline void * | ||
41 | find_pa(unsigned long *vptb, void *ptr) | ||
42 | { | ||
43 | unsigned long address = (unsigned long) ptr; | ||
44 | unsigned long result; | ||
45 | |||
46 | result = vptb[address >> 13]; | ||
47 | result >>= 32; | ||
48 | result <<= 13; | ||
49 | result |= address & 0x1fff; | ||
50 | return (void *) result; | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * This function moves into OSF/1 pal-code, and has a temporary | ||
55 | * PCB for that. The kernel proper should replace this PCB with | ||
56 | * the real one as soon as possible. | ||
57 | * | ||
58 | * The page table muckery in here depends on the fact that the boot | ||
59 | * code has the L1 page table identity-map itself in the second PTE | ||
60 | * in the L1 page table. Thus the L1-page is virtually addressable | ||
61 | * itself (through three levels) at virtual address 0x200802000. | ||
62 | */ | ||
63 | |||
64 | #define VPTB ((unsigned long *) 0x200000000) | ||
65 | #define L1 ((unsigned long *) 0x200802000) | ||
66 | |||
67 | void | ||
68 | pal_init(void) | ||
69 | { | ||
70 | unsigned long i, rev; | ||
71 | struct percpu_struct * percpu; | ||
72 | struct pcb_struct * pcb_pa; | ||
73 | |||
74 | /* Create the dummy PCB. */ | ||
75 | pcb_va->ksp = 0; | ||
76 | pcb_va->usp = 0; | ||
77 | pcb_va->ptbr = L1[1] >> 32; | ||
78 | pcb_va->asn = 0; | ||
79 | pcb_va->pcc = 0; | ||
80 | pcb_va->unique = 0; | ||
81 | pcb_va->flags = 1; | ||
82 | pcb_va->res1 = 0; | ||
83 | pcb_va->res2 = 0; | ||
84 | pcb_pa = find_pa(VPTB, pcb_va); | ||
85 | |||
86 | /* | ||
87 | * a0 = 2 (OSF) | ||
88 | * a1 = return address, but we give the asm the vaddr of the PCB | ||
89 | * a2 = physical addr of PCB | ||
90 | * a3 = new virtual page table pointer | ||
91 | * a4 = KSP (but the asm sets it) | ||
92 | */ | ||
93 | srm_printk("Switching to OSF PAL-code .. "); | ||
94 | |||
95 | i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); | ||
96 | if (i) { | ||
97 | srm_printk("failed, code %ld\n", i); | ||
98 | __halt(); | ||
99 | } | ||
100 | |||
101 | percpu = (struct percpu_struct *) | ||
102 | (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); | ||
103 | rev = percpu->pal_revision = percpu->palcode_avail[2]; | ||
104 | |||
105 | srm_printk("Ok (rev %lx)\n", rev); | ||
106 | |||
107 | tbia(); /* do it directly in case we are SMP */ | ||
108 | } | ||
109 | |||
110 | static inline void | ||
111 | load(unsigned long dst, unsigned long src, unsigned long count) | ||
112 | { | ||
113 | memcpy((void *)dst, (void *)src, count); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Start the kernel. | ||
118 | */ | ||
119 | static inline void | ||
120 | runkernel(void) | ||
121 | { | ||
122 | __asm__ __volatile__( | ||
123 | "bis %0,%0,$27\n\t" | ||
124 | "jmp ($27)" | ||
125 | : /* no outputs: it doesn't even return */ | ||
126 | : "r" (START_ADDR)); | ||
127 | } | ||
128 | |||
129 | extern char _end; | ||
130 | #define KERNEL_ORIGIN \ | ||
131 | ((((unsigned long)&_end) + 511) & ~511) | ||
132 | |||
133 | void | ||
134 | start_kernel(void) | ||
135 | { | ||
136 | /* | ||
137 | * Note that this crufty stuff with static and envval | ||
138 | * and envbuf is because: | ||
139 | * | ||
140 | * 1. Frequently, the stack is short, and we don't want to overrun; | ||
141 | * 2. Frequently the stack is where we are going to copy the kernel to; | ||
142 | * 3. A certain SRM console required the GET_ENV output to stack. | ||
143 | * ??? A comment in the aboot sources indicates that the GET_ENV | ||
144 | * destination must be quadword aligned. Might this explain the | ||
145 | * behaviour, rather than requiring output to the stack, which | ||
146 | * seems rather far-fetched. | ||
147 | */ | ||
148 | static long nbytes; | ||
149 | static char envval[256] __attribute__((aligned(8))); | ||
150 | static unsigned long initrd_start; | ||
151 | |||
152 | srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); | ||
153 | if (INIT_HWRPB->pagesize != 8192) { | ||
154 | srm_printk("Expected 8kB pages, got %ldkB\n", | ||
155 | INIT_HWRPB->pagesize >> 10); | ||
156 | return; | ||
157 | } | ||
158 | if (INIT_HWRPB->vptb != (unsigned long) VPTB) { | ||
159 | srm_printk("Expected vptb at %p, got %p\n", | ||
160 | VPTB, (void *)INIT_HWRPB->vptb); | ||
161 | return; | ||
162 | } | ||
163 | pal_init(); | ||
164 | |||
165 | /* The initrd must be page-aligned. See below for the | ||
166 | cause of the magic number 5. */ | ||
167 | initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) | | ||
168 | (PAGE_SIZE-1)) + 1; | ||
169 | #ifdef INITRD_IMAGE_SIZE | ||
170 | srm_printk("Initrd positioned at %#lx\n", initrd_start); | ||
171 | #endif | ||
172 | |||
173 | /* | ||
174 | * Move the stack to a safe place to ensure it won't be | ||
175 | * overwritten by kernel image. | ||
176 | */ | ||
177 | move_stack(initrd_start - PAGE_SIZE); | ||
178 | |||
179 | nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); | ||
180 | if (nbytes < 0 || nbytes >= sizeof(envval)) { | ||
181 | nbytes = 0; | ||
182 | } | ||
183 | envval[nbytes] = '\0'; | ||
184 | srm_printk("Loading the kernel...'%s'\n", envval); | ||
185 | |||
186 | /* NOTE: *no* callbacks or printouts from here on out!!! */ | ||
187 | |||
188 | /* This is a hack, as some consoles seem to get virtual 20000000 (ie | ||
189 | * where the SRM console puts the kernel bootp image) memory | ||
190 | * overlapping physical memory where the kernel wants to be put, | ||
191 | * which causes real problems when attempting to copy the former to | ||
192 | * the latter... :-( | ||
193 | * | ||
194 | * So, we first move the kernel virtual-to-physical way above where | ||
195 | * we physically want the kernel to end up, then copy it from there | ||
196 | * to its final resting place... ;-} | ||
197 | * | ||
198 | * Sigh... */ | ||
199 | |||
200 | #ifdef INITRD_IMAGE_SIZE | ||
201 | load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE); | ||
202 | #endif | ||
203 | load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); | ||
204 | load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); | ||
205 | |||
206 | memset((char*)ZERO_PGE, 0, PAGE_SIZE); | ||
207 | strcpy((char*)ZERO_PGE, envval); | ||
208 | #ifdef INITRD_IMAGE_SIZE | ||
209 | ((long *)(ZERO_PGE+256))[0] = initrd_start; | ||
210 | ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; | ||
211 | #endif | ||
212 | |||
213 | runkernel(); | ||
214 | } | ||
diff --git a/arch/alpha/boot/bootpz.c b/arch/alpha/boot/bootpz.c new file mode 100644 index 000000000000..a6657f2cf9bd --- /dev/null +++ b/arch/alpha/boot/bootpz.c | |||
@@ -0,0 +1,469 @@ | |||
1 | /* | ||
2 | * arch/alpha/boot/bootpz.c | ||
3 | * | ||
4 | * Copyright (C) 1997 Jay Estabrook | ||
5 | * | ||
6 | * This file is used for creating a compressed BOOTP file for the | ||
7 | * Linux/AXP kernel | ||
8 | * | ||
9 | * based significantly on the arch/alpha/boot/main.c of Linus Torvalds | ||
10 | * and the decompression code from MILO. | ||
11 | */ | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/version.h> | ||
15 | #include <linux/mm.h> | ||
16 | |||
17 | #include <asm/system.h> | ||
18 | #include <asm/console.h> | ||
19 | #include <asm/hwrpb.h> | ||
20 | #include <asm/pgtable.h> | ||
21 | #include <asm/io.h> | ||
22 | |||
23 | #include <stdarg.h> | ||
24 | |||
25 | #include "kzsize.h" | ||
26 | |||
27 | /* FIXME FIXME FIXME */ | ||
28 | #define MALLOC_AREA_SIZE 0x200000 /* 2MB for now */ | ||
29 | /* FIXME FIXME FIXME */ | ||
30 | |||
31 | |||
32 | /* | ||
33 | WARNING NOTE | ||
34 | |||
35 | It is very possible that turning on additional messages may cause | ||
36 | kernel image corruption due to stack usage to do the printing. | ||
37 | |||
38 | */ | ||
39 | |||
40 | #undef DEBUG_CHECK_RANGE | ||
41 | #undef DEBUG_ADDRESSES | ||
42 | #undef DEBUG_LAST_STEPS | ||
43 | |||
44 | extern unsigned long switch_to_osf_pal(unsigned long nr, | ||
45 | struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, | ||
46 | unsigned long *vptb); | ||
47 | |||
48 | extern int decompress_kernel(void* destination, void *source, | ||
49 | size_t ksize, size_t kzsize); | ||
50 | |||
51 | extern void move_stack(unsigned long new_stack); | ||
52 | |||
53 | struct hwrpb_struct *hwrpb = INIT_HWRPB; | ||
54 | static struct pcb_struct pcb_va[1]; | ||
55 | |||
56 | /* | ||
57 | * Find a physical address of a virtual object.. | ||
58 | * | ||
59 | * This is easy using the virtual page table address. | ||
60 | */ | ||
61 | #define VPTB ((unsigned long *) 0x200000000) | ||
62 | |||
63 | static inline unsigned long | ||
64 | find_pa(unsigned long address) | ||
65 | { | ||
66 | unsigned long result; | ||
67 | |||
68 | result = VPTB[address >> 13]; | ||
69 | result >>= 32; | ||
70 | result <<= 13; | ||
71 | result |= address & 0x1fff; | ||
72 | return result; | ||
73 | } | ||
74 | |||
75 | int | ||
76 | check_range(unsigned long vstart, unsigned long vend, | ||
77 | unsigned long kstart, unsigned long kend) | ||
78 | { | ||
79 | unsigned long vaddr, kaddr; | ||
80 | |||
81 | #ifdef DEBUG_CHECK_RANGE | ||
82 | srm_printk("check_range: V[0x%lx:0x%lx] K[0x%lx:0x%lx]\n", | ||
83 | vstart, vend, kstart, kend); | ||
84 | #endif | ||
85 | /* do some range checking for detecting an overlap... */ | ||
86 | for (vaddr = vstart; vaddr <= vend; vaddr += PAGE_SIZE) | ||
87 | { | ||
88 | kaddr = (find_pa(vaddr) | PAGE_OFFSET); | ||
89 | if (kaddr >= kstart && kaddr <= kend) | ||
90 | { | ||
91 | #ifdef DEBUG_CHECK_RANGE | ||
92 | srm_printk("OVERLAP: vaddr 0x%lx kaddr 0x%lx" | ||
93 | " [0x%lx:0x%lx]\n", | ||
94 | vaddr, kaddr, kstart, kend); | ||
95 | #endif | ||
96 | return 1; | ||
97 | } | ||
98 | } | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * This function moves into OSF/1 pal-code, and has a temporary | ||
104 | * PCB for that. The kernel proper should replace this PCB with | ||
105 | * the real one as soon as possible. | ||
106 | * | ||
107 | * The page table muckery in here depends on the fact that the boot | ||
108 | * code has the L1 page table identity-map itself in the second PTE | ||
109 | * in the L1 page table. Thus the L1-page is virtually addressable | ||
110 | * itself (through three levels) at virtual address 0x200802000. | ||
111 | */ | ||
112 | |||
113 | #define L1 ((unsigned long *) 0x200802000) | ||
114 | |||
115 | void | ||
116 | pal_init(void) | ||
117 | { | ||
118 | unsigned long i, rev; | ||
119 | struct percpu_struct * percpu; | ||
120 | struct pcb_struct * pcb_pa; | ||
121 | |||
122 | /* Create the dummy PCB. */ | ||
123 | pcb_va->ksp = 0; | ||
124 | pcb_va->usp = 0; | ||
125 | pcb_va->ptbr = L1[1] >> 32; | ||
126 | pcb_va->asn = 0; | ||
127 | pcb_va->pcc = 0; | ||
128 | pcb_va->unique = 0; | ||
129 | pcb_va->flags = 1; | ||
130 | pcb_va->res1 = 0; | ||
131 | pcb_va->res2 = 0; | ||
132 | pcb_pa = (struct pcb_struct *)find_pa((unsigned long)pcb_va); | ||
133 | |||
134 | /* | ||
135 | * a0 = 2 (OSF) | ||
136 | * a1 = return address, but we give the asm the vaddr of the PCB | ||
137 | * a2 = physical addr of PCB | ||
138 | * a3 = new virtual page table pointer | ||
139 | * a4 = KSP (but the asm sets it) | ||
140 | */ | ||
141 | srm_printk("Switching to OSF PAL-code... "); | ||
142 | |||
143 | i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); | ||
144 | if (i) { | ||
145 | srm_printk("failed, code %ld\n", i); | ||
146 | __halt(); | ||
147 | } | ||
148 | |||
149 | percpu = (struct percpu_struct *) | ||
150 | (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); | ||
151 | rev = percpu->pal_revision = percpu->palcode_avail[2]; | ||
152 | |||
153 | srm_printk("OK (rev %lx)\n", rev); | ||
154 | |||
155 | tbia(); /* do it directly in case we are SMP */ | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Start the kernel. | ||
160 | */ | ||
161 | static inline void | ||
162 | runkernel(void) | ||
163 | { | ||
164 | __asm__ __volatile__( | ||
165 | "bis %0,%0,$27\n\t" | ||
166 | "jmp ($27)" | ||
167 | : /* no outputs: it doesn't even return */ | ||
168 | : "r" (START_ADDR)); | ||
169 | } | ||
170 | |||
171 | /* Must record the SP (it is virtual) on entry, so we can make sure | ||
172 | not to overwrite it during movement or decompression. */ | ||
173 | unsigned long SP_on_entry; | ||
174 | |||
175 | /* Calculate the kernel image address based on the end of the BOOTP | ||
176 | bootstrapper (ie this program). | ||
177 | */ | ||
178 | extern char _end; | ||
179 | #define KERNEL_ORIGIN \ | ||
180 | ((((unsigned long)&_end) + 511) & ~511) | ||
181 | |||
182 | /* Round address to next higher page boundary. */ | ||
183 | #define NEXT_PAGE(a) (((a) | (PAGE_SIZE - 1)) + 1) | ||
184 | |||
185 | #ifdef INITRD_IMAGE_SIZE | ||
186 | # define REAL_INITRD_SIZE INITRD_IMAGE_SIZE | ||
187 | #else | ||
188 | # define REAL_INITRD_SIZE 0 | ||
189 | #endif | ||
190 | |||
191 | /* Defines from include/asm-alpha/system.h | ||
192 | |||
193 | BOOT_ADDR Virtual address at which the consoles loads | ||
194 | the BOOTP image. | ||
195 | |||
196 | KERNEL_START KSEG address at which the kernel is built to run, | ||
197 | which includes some initial data pages before the | ||
198 | code. | ||
199 | |||
200 | START_ADDR KSEG address of the entry point of kernel code. | ||
201 | |||
202 | ZERO_PGE KSEG address of page full of zeroes, but | ||
203 | upon entry to kerne cvan be expected | ||
204 | to hold the parameter list and possible | ||
205 | INTRD information. | ||
206 | |||
207 | These are used in the local defines below. | ||
208 | */ | ||
209 | |||
210 | |||
211 | /* Virtual addresses for the BOOTP image. Note that this includes the | ||
212 | bootstrapper code as well as the compressed kernel image, and | ||
213 | possibly the INITRD image. | ||
214 | |||
215 | Oh, and do NOT forget the STACK, which appears to be placed virtually | ||
216 | beyond the end of the loaded image. | ||
217 | */ | ||
218 | #define V_BOOT_IMAGE_START BOOT_ADDR | ||
219 | #define V_BOOT_IMAGE_END SP_on_entry | ||
220 | |||
221 | /* Virtual addresses for just the bootstrapper part of the BOOTP image. */ | ||
222 | #define V_BOOTSTRAPPER_START BOOT_ADDR | ||
223 | #define V_BOOTSTRAPPER_END KERNEL_ORIGIN | ||
224 | |||
225 | /* Virtual addresses for just the data part of the BOOTP | ||
226 | image. This may also include the INITRD image, but always | ||
227 | includes the STACK. | ||
228 | */ | ||
229 | #define V_DATA_START KERNEL_ORIGIN | ||
230 | #define V_INITRD_START (KERNEL_ORIGIN + KERNEL_Z_SIZE) | ||
231 | #define V_INTRD_END (V_INITRD_START + REAL_INITRD_SIZE) | ||
232 | #define V_DATA_END V_BOOT_IMAGE_END | ||
233 | |||
234 | /* KSEG addresses for the uncompressed kernel. | ||
235 | |||
236 | Note that the end address includes workspace for the decompression. | ||
237 | Note also that the DATA_START address is ZERO_PGE, to which we write | ||
238 | just before jumping to the kernel image at START_ADDR. | ||
239 | */ | ||
240 | #define K_KERNEL_DATA_START ZERO_PGE | ||
241 | #define K_KERNEL_IMAGE_START START_ADDR | ||
242 | #define K_KERNEL_IMAGE_END (START_ADDR + KERNEL_SIZE) | ||
243 | |||
244 | /* Define to where we may have to decompress the kernel image, before | ||
245 | we move it to the final position, in case of overlap. This will be | ||
246 | above the final position of the kernel. | ||
247 | |||
248 | Regardless of overlap, we move the INITRD image to the end of this | ||
249 | copy area, because there needs to be a buffer area after the kernel | ||
250 | for "bootmem" anyway. | ||
251 | */ | ||
252 | #define K_COPY_IMAGE_START NEXT_PAGE(K_KERNEL_IMAGE_END) | ||
253 | /* Reserve one page below INITRD for the new stack. */ | ||
254 | #define K_INITRD_START \ | ||
255 | NEXT_PAGE(K_COPY_IMAGE_START + KERNEL_SIZE + PAGE_SIZE) | ||
256 | #define K_COPY_IMAGE_END \ | ||
257 | (K_INITRD_START + REAL_INITRD_SIZE + MALLOC_AREA_SIZE) | ||
258 | #define K_COPY_IMAGE_SIZE \ | ||
259 | NEXT_PAGE(K_COPY_IMAGE_END - K_COPY_IMAGE_START) | ||
260 | |||
261 | void | ||
262 | start_kernel(void) | ||
263 | { | ||
264 | int must_move = 0; | ||
265 | |||
266 | /* Initialize these for the decompression-in-place situation, | ||
267 | which is the smallest amount of work and most likely to | ||
268 | occur when using the normal START_ADDR of the kernel | ||
269 | (currently set to 16MB, to clear all console code. | ||
270 | */ | ||
271 | unsigned long uncompressed_image_start = K_KERNEL_IMAGE_START; | ||
272 | unsigned long uncompressed_image_end = K_KERNEL_IMAGE_END; | ||
273 | |||
274 | unsigned long initrd_image_start = K_INITRD_START; | ||
275 | |||
276 | /* | ||
277 | * Note that this crufty stuff with static and envval | ||
278 | * and envbuf is because: | ||
279 | * | ||
280 | * 1. Frequently, the stack is short, and we don't want to overrun; | ||
281 | * 2. Frequently the stack is where we are going to copy the kernel to; | ||
282 | * 3. A certain SRM console required the GET_ENV output to stack. | ||
283 | * ??? A comment in the aboot sources indicates that the GET_ENV | ||
284 | * destination must be quadword aligned. Might this explain the | ||
285 | * behaviour, rather than requiring output to the stack, which | ||
286 | * seems rather far-fetched. | ||
287 | */ | ||
288 | static long nbytes; | ||
289 | static char envval[256] __attribute__((aligned(8))); | ||
290 | register unsigned long asm_sp asm("30"); | ||
291 | |||
292 | SP_on_entry = asm_sp; | ||
293 | |||
294 | srm_printk("Linux/Alpha BOOTPZ Loader for Linux " UTS_RELEASE "\n"); | ||
295 | |||
296 | /* Validity check the HWRPB. */ | ||
297 | if (INIT_HWRPB->pagesize != 8192) { | ||
298 | srm_printk("Expected 8kB pages, got %ldkB\n", | ||
299 | INIT_HWRPB->pagesize >> 10); | ||
300 | return; | ||
301 | } | ||
302 | if (INIT_HWRPB->vptb != (unsigned long) VPTB) { | ||
303 | srm_printk("Expected vptb at %p, got %p\n", | ||
304 | VPTB, (void *)INIT_HWRPB->vptb); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | /* PALcode (re)initialization. */ | ||
309 | pal_init(); | ||
310 | |||
311 | /* Get the parameter list from the console environment variable. */ | ||
312 | nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); | ||
313 | if (nbytes < 0 || nbytes >= sizeof(envval)) { | ||
314 | nbytes = 0; | ||
315 | } | ||
316 | envval[nbytes] = '\0'; | ||
317 | |||
318 | #ifdef DEBUG_ADDRESSES | ||
319 | srm_printk("START_ADDR 0x%lx\n", START_ADDR); | ||
320 | srm_printk("KERNEL_ORIGIN 0x%lx\n", KERNEL_ORIGIN); | ||
321 | srm_printk("KERNEL_SIZE 0x%x\n", KERNEL_SIZE); | ||
322 | srm_printk("KERNEL_Z_SIZE 0x%x\n", KERNEL_Z_SIZE); | ||
323 | #endif | ||
324 | |||
325 | /* Since all the SRM consoles load the BOOTP image at virtual | ||
326 | * 0x20000000, we have to ensure that the physical memory | ||
327 | * pages occupied by that image do NOT overlap the physical | ||
328 | * address range where the kernel wants to be run. This | ||
329 | * causes real problems when attempting to cdecompress the | ||
330 | * former into the latter... :-( | ||
331 | * | ||
332 | * So, we may have to decompress/move the kernel/INITRD image | ||
333 | * virtual-to-physical someplace else first before moving | ||
334 | * kernel /INITRD to their final resting places... ;-} | ||
335 | * | ||
336 | * Sigh... | ||
337 | */ | ||
338 | |||
339 | /* First, check to see if the range of addresses occupied by | ||
340 | the bootstrapper part of the BOOTP image include any of the | ||
341 | physical pages into which the kernel will be placed for | ||
342 | execution. | ||
343 | |||
344 | We only need check on the final kernel image range, since we | ||
345 | will put the INITRD someplace that we can be sure is not | ||
346 | in conflict. | ||
347 | */ | ||
348 | if (check_range(V_BOOTSTRAPPER_START, V_BOOTSTRAPPER_END, | ||
349 | K_KERNEL_DATA_START, K_KERNEL_IMAGE_END)) | ||
350 | { | ||
351 | srm_printk("FATAL ERROR: overlap of bootstrapper code\n"); | ||
352 | __halt(); | ||
353 | } | ||
354 | |||
355 | /* Next, check to see if the range of addresses occupied by | ||
356 | the compressed kernel/INITRD/stack portion of the BOOTP | ||
357 | image include any of the physical pages into which the | ||
358 | decompressed kernel or the INITRD will be placed for | ||
359 | execution. | ||
360 | */ | ||
361 | if (check_range(V_DATA_START, V_DATA_END, | ||
362 | K_KERNEL_IMAGE_START, K_COPY_IMAGE_END)) | ||
363 | { | ||
364 | #ifdef DEBUG_ADDRESSES | ||
365 | srm_printk("OVERLAP: cannot decompress in place\n"); | ||
366 | #endif | ||
367 | uncompressed_image_start = K_COPY_IMAGE_START; | ||
368 | uncompressed_image_end = K_COPY_IMAGE_END; | ||
369 | must_move = 1; | ||
370 | |||
371 | /* Finally, check to see if the range of addresses | ||
372 | occupied by the compressed kernel/INITRD part of | ||
373 | the BOOTP image include any of the physical pages | ||
374 | into which that part is to be copied for | ||
375 | decompression. | ||
376 | */ | ||
377 | while (check_range(V_DATA_START, V_DATA_END, | ||
378 | uncompressed_image_start, | ||
379 | uncompressed_image_end)) | ||
380 | { | ||
381 | #if 0 | ||
382 | uncompressed_image_start += K_COPY_IMAGE_SIZE; | ||
383 | uncompressed_image_end += K_COPY_IMAGE_SIZE; | ||
384 | initrd_image_start += K_COPY_IMAGE_SIZE; | ||
385 | #else | ||
386 | /* Keep as close as possible to end of BOOTP image. */ | ||
387 | uncompressed_image_start += PAGE_SIZE; | ||
388 | uncompressed_image_end += PAGE_SIZE; | ||
389 | initrd_image_start += PAGE_SIZE; | ||
390 | #endif | ||
391 | } | ||
392 | } | ||
393 | |||
394 | srm_printk("Starting to load the kernel with args '%s'\n", envval); | ||
395 | |||
396 | #ifdef DEBUG_ADDRESSES | ||
397 | srm_printk("Decompressing the kernel...\n" | ||
398 | "...from 0x%lx to 0x%lx size 0x%x\n", | ||
399 | V_DATA_START, | ||
400 | uncompressed_image_start, | ||
401 | KERNEL_SIZE); | ||
402 | #endif | ||
403 | decompress_kernel((void *)uncompressed_image_start, | ||
404 | (void *)V_DATA_START, | ||
405 | KERNEL_SIZE, KERNEL_Z_SIZE); | ||
406 | |||
407 | /* | ||
408 | * Now, move things to their final positions, if/as required. | ||
409 | */ | ||
410 | |||
411 | #ifdef INITRD_IMAGE_SIZE | ||
412 | |||
413 | /* First, we always move the INITRD image, if present. */ | ||
414 | #ifdef DEBUG_ADDRESSES | ||
415 | srm_printk("Moving the INITRD image...\n" | ||
416 | " from 0x%lx to 0x%lx size 0x%x\n", | ||
417 | V_INITRD_START, | ||
418 | initrd_image_start, | ||
419 | INITRD_IMAGE_SIZE); | ||
420 | #endif | ||
421 | memcpy((void *)initrd_image_start, (void *)V_INITRD_START, | ||
422 | INITRD_IMAGE_SIZE); | ||
423 | |||
424 | #endif /* INITRD_IMAGE_SIZE */ | ||
425 | |||
426 | /* Next, we may have to move the uncompressed kernel to the | ||
427 | final destination. | ||
428 | */ | ||
429 | if (must_move) { | ||
430 | #ifdef DEBUG_ADDRESSES | ||
431 | srm_printk("Moving the uncompressed kernel...\n" | ||
432 | "...from 0x%lx to 0x%lx size 0x%x\n", | ||
433 | uncompressed_image_start, | ||
434 | K_KERNEL_IMAGE_START, | ||
435 | (unsigned)KERNEL_SIZE); | ||
436 | #endif | ||
437 | /* | ||
438 | * Move the stack to a safe place to ensure it won't be | ||
439 | * overwritten by kernel image. | ||
440 | */ | ||
441 | move_stack(initrd_image_start - PAGE_SIZE); | ||
442 | |||
443 | memcpy((void *)K_KERNEL_IMAGE_START, | ||
444 | (void *)uncompressed_image_start, KERNEL_SIZE); | ||
445 | } | ||
446 | |||
447 | /* Clear the zero page, then move the argument list in. */ | ||
448 | #ifdef DEBUG_LAST_STEPS | ||
449 | srm_printk("Preparing ZERO_PGE...\n"); | ||
450 | #endif | ||
451 | memset((char*)ZERO_PGE, 0, PAGE_SIZE); | ||
452 | strcpy((char*)ZERO_PGE, envval); | ||
453 | |||
454 | #ifdef INITRD_IMAGE_SIZE | ||
455 | |||
456 | #ifdef DEBUG_LAST_STEPS | ||
457 | srm_printk("Preparing INITRD info...\n"); | ||
458 | #endif | ||
459 | /* Finally, set the INITRD paramenters for the kernel. */ | ||
460 | ((long *)(ZERO_PGE+256))[0] = initrd_image_start; | ||
461 | ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; | ||
462 | |||
463 | #endif /* INITRD_IMAGE_SIZE */ | ||
464 | |||
465 | #ifdef DEBUG_LAST_STEPS | ||
466 | srm_printk("Doing 'runkernel()'...\n"); | ||
467 | #endif | ||
468 | runkernel(); | ||
469 | } | ||
diff --git a/arch/alpha/boot/head.S b/arch/alpha/boot/head.S new file mode 100644 index 000000000000..f3d98089b3dc --- /dev/null +++ b/arch/alpha/boot/head.S | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * arch/alpha/boot/head.S | ||
3 | * | ||
4 | * initial bootloader stuff.. | ||
5 | */ | ||
6 | |||
7 | #include <asm/system.h> | ||
8 | |||
9 | .set noreorder | ||
10 | .globl __start | ||
11 | .ent __start | ||
12 | __start: | ||
13 | br $29,2f | ||
14 | 2: ldgp $29,0($29) | ||
15 | jsr $26,start_kernel | ||
16 | call_pal PAL_halt | ||
17 | .end __start | ||
18 | |||
19 | .align 5 | ||
20 | .globl wrent | ||
21 | .ent wrent | ||
22 | wrent: | ||
23 | .prologue 0 | ||
24 | call_pal PAL_wrent | ||
25 | ret ($26) | ||
26 | .end wrent | ||
27 | |||
28 | .align 5 | ||
29 | .globl wrkgp | ||
30 | .ent wrkgp | ||
31 | wrkgp: | ||
32 | .prologue 0 | ||
33 | call_pal PAL_wrkgp | ||
34 | ret ($26) | ||
35 | .end wrkgp | ||
36 | |||
37 | .align 5 | ||
38 | .globl switch_to_osf_pal | ||
39 | .ent switch_to_osf_pal | ||
40 | switch_to_osf_pal: | ||
41 | subq $30,128,$30 | ||
42 | .frame $30,128,$26 | ||
43 | stq $26,0($30) | ||
44 | stq $1,8($30) | ||
45 | stq $2,16($30) | ||
46 | stq $3,24($30) | ||
47 | stq $4,32($30) | ||
48 | stq $5,40($30) | ||
49 | stq $6,48($30) | ||
50 | stq $7,56($30) | ||
51 | stq $8,64($30) | ||
52 | stq $9,72($30) | ||
53 | stq $10,80($30) | ||
54 | stq $11,88($30) | ||
55 | stq $12,96($30) | ||
56 | stq $13,104($30) | ||
57 | stq $14,112($30) | ||
58 | stq $15,120($30) | ||
59 | .prologue 0 | ||
60 | |||
61 | stq $30,0($17) /* save KSP in PCB */ | ||
62 | |||
63 | bis $30,$30,$20 /* a4 = KSP */ | ||
64 | br $17,1f | ||
65 | |||
66 | ldq $26,0($30) | ||
67 | ldq $1,8($30) | ||
68 | ldq $2,16($30) | ||
69 | ldq $3,24($30) | ||
70 | ldq $4,32($30) | ||
71 | ldq $5,40($30) | ||
72 | ldq $6,48($30) | ||
73 | ldq $7,56($30) | ||
74 | ldq $8,64($30) | ||
75 | ldq $9,72($30) | ||
76 | ldq $10,80($30) | ||
77 | ldq $11,88($30) | ||
78 | ldq $12,96($30) | ||
79 | ldq $13,104($30) | ||
80 | ldq $14,112($30) | ||
81 | ldq $15,120($30) | ||
82 | addq $30,128,$30 | ||
83 | ret ($26) | ||
84 | 1: call_pal PAL_swppal | ||
85 | .end switch_to_osf_pal | ||
86 | |||
87 | .align 3 | ||
88 | .globl tbi | ||
89 | .ent tbi | ||
90 | tbi: | ||
91 | .prologue 0 | ||
92 | call_pal PAL_tbi | ||
93 | ret ($26) | ||
94 | .end tbi | ||
95 | |||
96 | .align 3 | ||
97 | .globl halt | ||
98 | .ent halt | ||
99 | halt: | ||
100 | .prologue 0 | ||
101 | call_pal PAL_halt | ||
102 | .end halt | ||
103 | |||
104 | /* $16 - new stack page */ | ||
105 | .align 3 | ||
106 | .globl move_stack | ||
107 | .ent move_stack | ||
108 | move_stack: | ||
109 | .prologue 0 | ||
110 | lda $0, 0x1fff($31) | ||
111 | and $0, $30, $1 /* Stack offset */ | ||
112 | or $1, $16, $16 /* New stack pointer */ | ||
113 | mov $30, $1 | ||
114 | mov $16, $2 | ||
115 | 1: ldq $3, 0($1) /* Move the stack */ | ||
116 | addq $1, 8, $1 | ||
117 | stq $3, 0($2) | ||
118 | and $0, $1, $4 | ||
119 | addq $2, 8, $2 | ||
120 | bne $4, 1b | ||
121 | mov $16, $30 | ||
122 | ret ($26) | ||
123 | .end move_stack | ||
diff --git a/arch/alpha/boot/main.c b/arch/alpha/boot/main.c new file mode 100644 index 000000000000..78c9b0b6eea7 --- /dev/null +++ b/arch/alpha/boot/main.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * arch/alpha/boot/main.c | ||
3 | * | ||
4 | * Copyright (C) 1994, 1995 Linus Torvalds | ||
5 | * | ||
6 | * This file is the bootloader for the Linux/AXP kernel | ||
7 | */ | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/string.h> | ||
10 | #include <linux/version.h> | ||
11 | #include <linux/mm.h> | ||
12 | |||
13 | #include <asm/system.h> | ||
14 | #include <asm/console.h> | ||
15 | #include <asm/hwrpb.h> | ||
16 | #include <asm/pgtable.h> | ||
17 | |||
18 | #include <stdarg.h> | ||
19 | |||
20 | #include "ksize.h" | ||
21 | |||
22 | extern int vsprintf(char *, const char *, va_list); | ||
23 | extern unsigned long switch_to_osf_pal(unsigned long nr, | ||
24 | struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, | ||
25 | unsigned long *vptb); | ||
26 | struct hwrpb_struct *hwrpb = INIT_HWRPB; | ||
27 | static struct pcb_struct pcb_va[1]; | ||
28 | |||
29 | /* | ||
30 | * Find a physical address of a virtual object.. | ||
31 | * | ||
32 | * This is easy using the virtual page table address. | ||
33 | */ | ||
34 | |||
35 | static inline void * | ||
36 | find_pa(unsigned long *vptb, void *ptr) | ||
37 | { | ||
38 | unsigned long address = (unsigned long) ptr; | ||
39 | unsigned long result; | ||
40 | |||
41 | result = vptb[address >> 13]; | ||
42 | result >>= 32; | ||
43 | result <<= 13; | ||
44 | result |= address & 0x1fff; | ||
45 | return (void *) result; | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * This function moves into OSF/1 pal-code, and has a temporary | ||
50 | * PCB for that. The kernel proper should replace this PCB with | ||
51 | * the real one as soon as possible. | ||
52 | * | ||
53 | * The page table muckery in here depends on the fact that the boot | ||
54 | * code has the L1 page table identity-map itself in the second PTE | ||
55 | * in the L1 page table. Thus the L1-page is virtually addressable | ||
56 | * itself (through three levels) at virtual address 0x200802000. | ||
57 | */ | ||
58 | |||
59 | #define VPTB ((unsigned long *) 0x200000000) | ||
60 | #define L1 ((unsigned long *) 0x200802000) | ||
61 | |||
62 | void | ||
63 | pal_init(void) | ||
64 | { | ||
65 | unsigned long i, rev; | ||
66 | struct percpu_struct * percpu; | ||
67 | struct pcb_struct * pcb_pa; | ||
68 | |||
69 | /* Create the dummy PCB. */ | ||
70 | pcb_va->ksp = 0; | ||
71 | pcb_va->usp = 0; | ||
72 | pcb_va->ptbr = L1[1] >> 32; | ||
73 | pcb_va->asn = 0; | ||
74 | pcb_va->pcc = 0; | ||
75 | pcb_va->unique = 0; | ||
76 | pcb_va->flags = 1; | ||
77 | pcb_va->res1 = 0; | ||
78 | pcb_va->res2 = 0; | ||
79 | pcb_pa = find_pa(VPTB, pcb_va); | ||
80 | |||
81 | /* | ||
82 | * a0 = 2 (OSF) | ||
83 | * a1 = return address, but we give the asm the vaddr of the PCB | ||
84 | * a2 = physical addr of PCB | ||
85 | * a3 = new virtual page table pointer | ||
86 | * a4 = KSP (but the asm sets it) | ||
87 | */ | ||
88 | srm_printk("Switching to OSF PAL-code .. "); | ||
89 | |||
90 | i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); | ||
91 | if (i) { | ||
92 | srm_printk("failed, code %ld\n", i); | ||
93 | __halt(); | ||
94 | } | ||
95 | |||
96 | percpu = (struct percpu_struct *) | ||
97 | (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); | ||
98 | rev = percpu->pal_revision = percpu->palcode_avail[2]; | ||
99 | |||
100 | srm_printk("Ok (rev %lx)\n", rev); | ||
101 | |||
102 | tbia(); /* do it directly in case we are SMP */ | ||
103 | } | ||
104 | |||
105 | static inline long openboot(void) | ||
106 | { | ||
107 | char bootdev[256]; | ||
108 | long result; | ||
109 | |||
110 | result = callback_getenv(ENV_BOOTED_DEV, bootdev, 255); | ||
111 | if (result < 0) | ||
112 | return result; | ||
113 | return callback_open(bootdev, result & 255); | ||
114 | } | ||
115 | |||
116 | static inline long close(long dev) | ||
117 | { | ||
118 | return callback_close(dev); | ||
119 | } | ||
120 | |||
121 | static inline long load(long dev, unsigned long addr, unsigned long count) | ||
122 | { | ||
123 | char bootfile[256]; | ||
124 | extern char _end; | ||
125 | long result, boot_size = &_end - (char *) BOOT_ADDR; | ||
126 | |||
127 | result = callback_getenv(ENV_BOOTED_FILE, bootfile, 255); | ||
128 | if (result < 0) | ||
129 | return result; | ||
130 | result &= 255; | ||
131 | bootfile[result] = '\0'; | ||
132 | if (result) | ||
133 | srm_printk("Boot file specification (%s) not implemented\n", | ||
134 | bootfile); | ||
135 | return callback_read(dev, count, addr, boot_size/512 + 1); | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Start the kernel. | ||
140 | */ | ||
141 | static void runkernel(void) | ||
142 | { | ||
143 | __asm__ __volatile__( | ||
144 | "bis %1,%1,$30\n\t" | ||
145 | "bis %0,%0,$26\n\t" | ||
146 | "ret ($26)" | ||
147 | : /* no outputs: it doesn't even return */ | ||
148 | : "r" (START_ADDR), | ||
149 | "r" (PAGE_SIZE + INIT_STACK)); | ||
150 | } | ||
151 | |||
152 | void start_kernel(void) | ||
153 | { | ||
154 | long i; | ||
155 | long dev; | ||
156 | int nbytes; | ||
157 | char envval[256]; | ||
158 | |||
159 | srm_printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); | ||
160 | if (INIT_HWRPB->pagesize != 8192) { | ||
161 | srm_printk("Expected 8kB pages, got %ldkB\n", INIT_HWRPB->pagesize >> 10); | ||
162 | return; | ||
163 | } | ||
164 | pal_init(); | ||
165 | dev = openboot(); | ||
166 | if (dev < 0) { | ||
167 | srm_printk("Unable to open boot device: %016lx\n", dev); | ||
168 | return; | ||
169 | } | ||
170 | dev &= 0xffffffff; | ||
171 | srm_printk("Loading vmlinux ..."); | ||
172 | i = load(dev, START_ADDR, KERNEL_SIZE); | ||
173 | close(dev); | ||
174 | if (i != KERNEL_SIZE) { | ||
175 | srm_printk("Failed (%lx)\n", i); | ||
176 | return; | ||
177 | } | ||
178 | |||
179 | nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); | ||
180 | if (nbytes < 0) { | ||
181 | nbytes = 0; | ||
182 | } | ||
183 | envval[nbytes] = '\0'; | ||
184 | strcpy((char*)ZERO_PGE, envval); | ||
185 | |||
186 | srm_printk(" Ok\nNow booting the kernel\n"); | ||
187 | runkernel(); | ||
188 | for (i = 0 ; i < 0x100000000 ; i++) | ||
189 | /* nothing */; | ||
190 | __halt(); | ||
191 | } | ||
diff --git a/arch/alpha/boot/misc.c b/arch/alpha/boot/misc.c new file mode 100644 index 000000000000..1d65adf5691e --- /dev/null +++ b/arch/alpha/boot/misc.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * misc.c | ||
3 | * | ||
4 | * This is a collection of several routines from gzip-1.0.3 | ||
5 | * adapted for Linux. | ||
6 | * | ||
7 | * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 | ||
8 | * | ||
9 | * Modified for ARM Linux by Russell King | ||
10 | * | ||
11 | * Nicolas Pitre <nico@visuaide.com> 1999/04/14 : | ||
12 | * For this code to run directly from Flash, all constant variables must | ||
13 | * be marked with 'const' and all other variables initialized at run-time | ||
14 | * only. This way all non constant variables will end up in the bss segment, | ||
15 | * which should point to addresses in RAM and cleared to 0 on start. | ||
16 | * This allows for a much quicker boot time. | ||
17 | * | ||
18 | * Modified for Alpha, from the ARM version, by Jay Estabrook 2003. | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | |||
23 | #include <asm/uaccess.h> | ||
24 | |||
25 | #define memzero(s,n) memset ((s),0,(n)) | ||
26 | #define puts srm_printk | ||
27 | extern long srm_printk(const char *, ...) | ||
28 | __attribute__ ((format (printf, 1, 2))); | ||
29 | |||
30 | /* | ||
31 | * gzip delarations | ||
32 | */ | ||
33 | #define OF(args) args | ||
34 | #define STATIC static | ||
35 | |||
36 | typedef unsigned char uch; | ||
37 | typedef unsigned short ush; | ||
38 | typedef unsigned long ulg; | ||
39 | |||
40 | #define WSIZE 0x8000 /* Window size must be at least 32k, */ | ||
41 | /* and a power of two */ | ||
42 | |||
43 | static uch *inbuf; /* input buffer */ | ||
44 | static uch *window; /* Sliding window buffer */ | ||
45 | |||
46 | static unsigned insize; /* valid bytes in inbuf */ | ||
47 | static unsigned inptr; /* index of next byte to be processed in inbuf */ | ||
48 | static unsigned outcnt; /* bytes in output buffer */ | ||
49 | |||
50 | /* gzip flag byte */ | ||
51 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ | ||
52 | #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ | ||
53 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ | ||
54 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ | ||
55 | #define COMMENT 0x10 /* bit 4 set: file comment present */ | ||
56 | #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ | ||
57 | #define RESERVED 0xC0 /* bit 6,7: reserved */ | ||
58 | |||
59 | #define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) | ||
60 | |||
61 | /* Diagnostic functions */ | ||
62 | #ifdef DEBUG | ||
63 | # define Assert(cond,msg) {if(!(cond)) error(msg);} | ||
64 | # define Trace(x) fprintf x | ||
65 | # define Tracev(x) {if (verbose) fprintf x ;} | ||
66 | # define Tracevv(x) {if (verbose>1) fprintf x ;} | ||
67 | # define Tracec(c,x) {if (verbose && (c)) fprintf x ;} | ||
68 | # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} | ||
69 | #else | ||
70 | # define Assert(cond,msg) | ||
71 | # define Trace(x) | ||
72 | # define Tracev(x) | ||
73 | # define Tracevv(x) | ||
74 | # define Tracec(c,x) | ||
75 | # define Tracecv(c,x) | ||
76 | #endif | ||
77 | |||
78 | static int fill_inbuf(void); | ||
79 | static void flush_window(void); | ||
80 | static void error(char *m); | ||
81 | static void gzip_mark(void **); | ||
82 | static void gzip_release(void **); | ||
83 | |||
84 | static char *input_data; | ||
85 | static int input_data_size; | ||
86 | |||
87 | static uch *output_data; | ||
88 | static ulg output_ptr; | ||
89 | static ulg bytes_out; | ||
90 | |||
91 | static void *malloc(int size); | ||
92 | static void free(void *where); | ||
93 | static void error(char *m); | ||
94 | static void gzip_mark(void **); | ||
95 | static void gzip_release(void **); | ||
96 | |||
97 | extern int end; | ||
98 | static ulg free_mem_ptr; | ||
99 | static ulg free_mem_ptr_end; | ||
100 | |||
101 | #define HEAP_SIZE 0x2000 | ||
102 | |||
103 | #include "../../../lib/inflate.c" | ||
104 | |||
105 | static void *malloc(int size) | ||
106 | { | ||
107 | void *p; | ||
108 | |||
109 | if (size <0) error("Malloc error"); | ||
110 | if (free_mem_ptr <= 0) error("Memory error"); | ||
111 | |||
112 | free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ | ||
113 | |||
114 | p = (void *)free_mem_ptr; | ||
115 | free_mem_ptr += size; | ||
116 | |||
117 | if (free_mem_ptr >= free_mem_ptr_end) | ||
118 | error("Out of memory"); | ||
119 | return p; | ||
120 | } | ||
121 | |||
122 | static void free(void *where) | ||
123 | { /* gzip_mark & gzip_release do the free */ | ||
124 | } | ||
125 | |||
126 | static void gzip_mark(void **ptr) | ||
127 | { | ||
128 | *ptr = (void *) free_mem_ptr; | ||
129 | } | ||
130 | |||
131 | static void gzip_release(void **ptr) | ||
132 | { | ||
133 | free_mem_ptr = (long) *ptr; | ||
134 | } | ||
135 | |||
136 | /* =========================================================================== | ||
137 | * Fill the input buffer. This is called only when the buffer is empty | ||
138 | * and at least one byte is really needed. | ||
139 | */ | ||
140 | int fill_inbuf(void) | ||
141 | { | ||
142 | if (insize != 0) | ||
143 | error("ran out of input data"); | ||
144 | |||
145 | inbuf = input_data; | ||
146 | insize = input_data_size; | ||
147 | |||
148 | inptr = 1; | ||
149 | return inbuf[0]; | ||
150 | } | ||
151 | |||
152 | /* =========================================================================== | ||
153 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. | ||
154 | * (Used for the decompressed data only.) | ||
155 | */ | ||
156 | void flush_window(void) | ||
157 | { | ||
158 | ulg c = crc; | ||
159 | unsigned n; | ||
160 | uch *in, *out, ch; | ||
161 | |||
162 | in = window; | ||
163 | out = &output_data[output_ptr]; | ||
164 | for (n = 0; n < outcnt; n++) { | ||
165 | ch = *out++ = *in++; | ||
166 | c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); | ||
167 | } | ||
168 | crc = c; | ||
169 | bytes_out += (ulg)outcnt; | ||
170 | output_ptr += (ulg)outcnt; | ||
171 | outcnt = 0; | ||
172 | /* puts("."); */ | ||
173 | } | ||
174 | |||
175 | static void error(char *x) | ||
176 | { | ||
177 | puts("\n\n"); | ||
178 | puts(x); | ||
179 | puts("\n\n -- System halted"); | ||
180 | |||
181 | while(1); /* Halt */ | ||
182 | } | ||
183 | |||
184 | unsigned int | ||
185 | decompress_kernel(void *output_start, | ||
186 | void *input_start, | ||
187 | size_t ksize, | ||
188 | size_t kzsize) | ||
189 | { | ||
190 | output_data = (uch *)output_start; | ||
191 | input_data = (uch *)input_start; | ||
192 | input_data_size = kzsize; /* use compressed size */ | ||
193 | |||
194 | /* FIXME FIXME FIXME */ | ||
195 | free_mem_ptr = (ulg)output_start + ksize; | ||
196 | free_mem_ptr_end = (ulg)output_start + ksize + 0x200000; | ||
197 | /* FIXME FIXME FIXME */ | ||
198 | |||
199 | /* put in temp area to reduce initial footprint */ | ||
200 | window = malloc(WSIZE); | ||
201 | |||
202 | makecrc(); | ||
203 | /* puts("Uncompressing Linux..."); */ | ||
204 | gunzip(); | ||
205 | /* puts(" done, booting the kernel.\n"); */ | ||
206 | return output_ptr; | ||
207 | } | ||
diff --git a/arch/alpha/boot/tools/mkbb.c b/arch/alpha/boot/tools/mkbb.c new file mode 100644 index 000000000000..23c7190b047c --- /dev/null +++ b/arch/alpha/boot/tools/mkbb.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* This utility makes a bootblock suitable for the SRM console/miniloader */ | ||
2 | |||
3 | /* Usage: | ||
4 | * mkbb <device> <lxboot> | ||
5 | * | ||
6 | * Where <device> is the name of the device to install the bootblock on, | ||
7 | * and <lxboot> is the name of a bootblock to merge in. This bootblock | ||
8 | * contains the offset and size of the bootloader. It must be exactly | ||
9 | * 512 bytes long. | ||
10 | */ | ||
11 | |||
12 | #include <fcntl.h> | ||
13 | #include <unistd.h> | ||
14 | #include <stdio.h> | ||
15 | |||
16 | /* Minimal definition of disklabel, so we don't have to include | ||
17 | * asm/disklabel.h (confuses make) | ||
18 | */ | ||
19 | #ifndef MAXPARTITIONS | ||
20 | #define MAXPARTITIONS 8 /* max. # of partitions */ | ||
21 | #endif | ||
22 | |||
23 | #ifndef u8 | ||
24 | #define u8 unsigned char | ||
25 | #endif | ||
26 | |||
27 | #ifndef u16 | ||
28 | #define u16 unsigned short | ||
29 | #endif | ||
30 | |||
31 | #ifndef u32 | ||
32 | #define u32 unsigned int | ||
33 | #endif | ||
34 | |||
35 | struct disklabel { | ||
36 | u32 d_magic; /* must be DISKLABELMAGIC */ | ||
37 | u16 d_type, d_subtype; | ||
38 | u8 d_typename[16]; | ||
39 | u8 d_packname[16]; | ||
40 | u32 d_secsize; | ||
41 | u32 d_nsectors; | ||
42 | u32 d_ntracks; | ||
43 | u32 d_ncylinders; | ||
44 | u32 d_secpercyl; | ||
45 | u32 d_secprtunit; | ||
46 | u16 d_sparespertrack; | ||
47 | u16 d_sparespercyl; | ||
48 | u32 d_acylinders; | ||
49 | u16 d_rpm, d_interleave, d_trackskew, d_cylskew; | ||
50 | u32 d_headswitch, d_trkseek, d_flags; | ||
51 | u32 d_drivedata[5]; | ||
52 | u32 d_spare[5]; | ||
53 | u32 d_magic2; /* must be DISKLABELMAGIC */ | ||
54 | u16 d_checksum; | ||
55 | u16 d_npartitions; | ||
56 | u32 d_bbsize, d_sbsize; | ||
57 | struct d_partition { | ||
58 | u32 p_size; | ||
59 | u32 p_offset; | ||
60 | u32 p_fsize; | ||
61 | u8 p_fstype; | ||
62 | u8 p_frag; | ||
63 | u16 p_cpg; | ||
64 | } d_partitions[MAXPARTITIONS]; | ||
65 | }; | ||
66 | |||
67 | |||
68 | typedef union __bootblock { | ||
69 | struct { | ||
70 | char __pad1[64]; | ||
71 | struct disklabel __label; | ||
72 | } __u1; | ||
73 | struct { | ||
74 | unsigned long __pad2[63]; | ||
75 | unsigned long __checksum; | ||
76 | } __u2; | ||
77 | char bootblock_bytes[512]; | ||
78 | unsigned long bootblock_quadwords[64]; | ||
79 | } bootblock; | ||
80 | |||
81 | #define bootblock_label __u1.__label | ||
82 | #define bootblock_checksum __u2.__checksum | ||
83 | |||
84 | main(int argc, char ** argv) | ||
85 | { | ||
86 | bootblock bootblock_from_disk; | ||
87 | bootblock bootloader_image; | ||
88 | int dev, fd; | ||
89 | int i; | ||
90 | int nread; | ||
91 | |||
92 | /* Make sure of the arg count */ | ||
93 | if(argc != 3) { | ||
94 | fprintf(stderr, "Usage: %s device lxboot\n", argv[0]); | ||
95 | exit(0); | ||
96 | } | ||
97 | |||
98 | /* First, open the device and make sure it's accessible */ | ||
99 | dev = open(argv[1], O_RDWR); | ||
100 | if(dev < 0) { | ||
101 | perror(argv[1]); | ||
102 | exit(0); | ||
103 | } | ||
104 | |||
105 | /* Now open the lxboot and make sure it's reasonable */ | ||
106 | fd = open(argv[2], O_RDONLY); | ||
107 | if(fd < 0) { | ||
108 | perror(argv[2]); | ||
109 | close(dev); | ||
110 | exit(0); | ||
111 | } | ||
112 | |||
113 | /* Read in the lxboot */ | ||
114 | nread = read(fd, &bootloader_image, sizeof(bootblock)); | ||
115 | if(nread != sizeof(bootblock)) { | ||
116 | perror("lxboot read"); | ||
117 | fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread); | ||
118 | exit(0); | ||
119 | } | ||
120 | |||
121 | /* Read in the bootblock from disk. */ | ||
122 | nread = read(dev, &bootblock_from_disk, sizeof(bootblock)); | ||
123 | if(nread != sizeof(bootblock)) { | ||
124 | perror("bootblock read"); | ||
125 | fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread); | ||
126 | exit(0); | ||
127 | } | ||
128 | |||
129 | /* Swap the bootblock's disklabel into the bootloader */ | ||
130 | bootloader_image.bootblock_label = bootblock_from_disk.bootblock_label; | ||
131 | |||
132 | /* Calculate the bootblock checksum */ | ||
133 | bootloader_image.bootblock_checksum = 0; | ||
134 | for(i = 0; i < 63; i++) { | ||
135 | bootloader_image.bootblock_checksum += | ||
136 | bootloader_image.bootblock_quadwords[i]; | ||
137 | } | ||
138 | |||
139 | /* Write the whole thing out! */ | ||
140 | lseek(dev, 0L, SEEK_SET); | ||
141 | if(write(dev, &bootloader_image, sizeof(bootblock)) != sizeof(bootblock)) { | ||
142 | perror("bootblock write"); | ||
143 | exit(0); | ||
144 | } | ||
145 | |||
146 | close(fd); | ||
147 | close(dev); | ||
148 | exit(0); | ||
149 | } | ||
150 | |||
151 | |||
diff --git a/arch/alpha/boot/tools/objstrip.c b/arch/alpha/boot/tools/objstrip.c new file mode 100644 index 000000000000..67beb1b45e4f --- /dev/null +++ b/arch/alpha/boot/tools/objstrip.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* | ||
2 | * arch/alpha/boot/tools/objstrip.c | ||
3 | * | ||
4 | * Strip the object file headers/trailers from an executable (ELF or ECOFF). | ||
5 | * | ||
6 | * Copyright (C) 1996 David Mosberger-Tang. | ||
7 | */ | ||
8 | /* | ||
9 | * Converts an ECOFF or ELF object file into a bootable file. The | ||
10 | * object file must be a OMAGIC file (i.e., data and bss follow immediately | ||
11 | * behind the text). See DEC "Assembly Language Programmer's Guide" | ||
12 | * documentation for details. The SRM boot process is documented in | ||
13 | * the Alpha AXP Architecture Reference Manual, Second Edition by | ||
14 | * Richard L. Sites and Richard T. Witek. | ||
15 | */ | ||
16 | #include <stdio.h> | ||
17 | #include <string.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <unistd.h> | ||
20 | |||
21 | #include <sys/fcntl.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <sys/types.h> | ||
24 | |||
25 | #include <linux/a.out.h> | ||
26 | #include <linux/coff.h> | ||
27 | #include <linux/param.h> | ||
28 | #include <linux/string.h> | ||
29 | #ifdef __ELF__ | ||
30 | # include <linux/elf.h> | ||
31 | #endif | ||
32 | |||
33 | /* bootfile size must be multiple of BLOCK_SIZE: */ | ||
34 | #define BLOCK_SIZE 512 | ||
35 | |||
36 | const char * prog_name; | ||
37 | |||
38 | |||
39 | void | ||
40 | usage (void) | ||
41 | { | ||
42 | fprintf(stderr, | ||
43 | "usage: %s [-v] -p file primary\n" | ||
44 | " %s [-vb] file [secondary]\n", prog_name, prog_name); | ||
45 | exit(1); | ||
46 | } | ||
47 | |||
48 | |||
49 | int | ||
50 | main (int argc, char *argv[]) | ||
51 | { | ||
52 | size_t nwritten, tocopy, n, mem_size, fil_size, pad = 0; | ||
53 | int fd, ofd, i, j, verbose = 0, primary = 0; | ||
54 | char buf[8192], *inname; | ||
55 | struct exec * aout; /* includes file & aout header */ | ||
56 | long offset; | ||
57 | #ifdef __ELF__ | ||
58 | struct elfhdr *elf; | ||
59 | struct elf_phdr *elf_phdr; /* program header */ | ||
60 | unsigned long long e_entry; | ||
61 | #endif | ||
62 | |||
63 | prog_name = argv[0]; | ||
64 | |||
65 | for (i = 1; i < argc && argv[i][0] == '-'; ++i) { | ||
66 | for (j = 1; argv[i][j]; ++j) { | ||
67 | switch (argv[i][j]) { | ||
68 | case 'v': | ||
69 | verbose = ~verbose; | ||
70 | break; | ||
71 | |||
72 | case 'b': | ||
73 | pad = BLOCK_SIZE; | ||
74 | break; | ||
75 | |||
76 | case 'p': | ||
77 | primary = 1; /* make primary bootblock */ | ||
78 | break; | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | if (i >= argc) { | ||
84 | usage(); | ||
85 | } | ||
86 | inname = argv[i++]; | ||
87 | |||
88 | fd = open(inname, O_RDONLY); | ||
89 | if (fd == -1) { | ||
90 | perror("open"); | ||
91 | exit(1); | ||
92 | } | ||
93 | |||
94 | ofd = 1; | ||
95 | if (i < argc) { | ||
96 | ofd = open(argv[i++], O_WRONLY | O_CREAT | O_TRUNC, 0666); | ||
97 | if (fd == -1) { | ||
98 | perror("open"); | ||
99 | exit(1); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | if (primary) { | ||
104 | /* generate bootblock for primary loader */ | ||
105 | |||
106 | unsigned long bb[64], sum = 0; | ||
107 | struct stat st; | ||
108 | off_t size; | ||
109 | int i; | ||
110 | |||
111 | if (ofd == 1) { | ||
112 | usage(); | ||
113 | } | ||
114 | |||
115 | if (fstat(fd, &st) == -1) { | ||
116 | perror("fstat"); | ||
117 | exit(1); | ||
118 | } | ||
119 | |||
120 | size = (st.st_size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1); | ||
121 | memset(bb, 0, sizeof(bb)); | ||
122 | strcpy((char *) bb, "Linux SRM bootblock"); | ||
123 | bb[60] = size / BLOCK_SIZE; /* count */ | ||
124 | bb[61] = 1; /* starting sector # */ | ||
125 | bb[62] = 0; /* flags---must be 0 */ | ||
126 | for (i = 0; i < 63; ++i) { | ||
127 | sum += bb[i]; | ||
128 | } | ||
129 | bb[63] = sum; | ||
130 | if (write(ofd, bb, sizeof(bb)) != sizeof(bb)) { | ||
131 | perror("boot-block write"); | ||
132 | exit(1); | ||
133 | } | ||
134 | printf("%lu\n", size); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | /* read and inspect exec header: */ | ||
139 | |||
140 | if (read(fd, buf, sizeof(buf)) < 0) { | ||
141 | perror("read"); | ||
142 | exit(1); | ||
143 | } | ||
144 | |||
145 | #ifdef __ELF__ | ||
146 | elf = (struct elfhdr *) buf; | ||
147 | |||
148 | if (elf->e_ident[0] == 0x7f && strncmp(elf->e_ident + 1, "ELF", 3) == 0) { | ||
149 | if (elf->e_type != ET_EXEC) { | ||
150 | fprintf(stderr, "%s: %s is not an ELF executable\n", | ||
151 | prog_name, inname); | ||
152 | exit(1); | ||
153 | } | ||
154 | if (!elf_check_arch(elf)) { | ||
155 | fprintf(stderr, "%s: is not for this processor (e_machine=%d)\n", | ||
156 | prog_name, elf->e_machine); | ||
157 | exit(1); | ||
158 | } | ||
159 | if (elf->e_phnum != 1) { | ||
160 | fprintf(stderr, | ||
161 | "%s: %d program headers (forgot to link with -N?)\n", | ||
162 | prog_name, elf->e_phnum); | ||
163 | } | ||
164 | |||
165 | e_entry = elf->e_entry; | ||
166 | |||
167 | lseek(fd, elf->e_phoff, SEEK_SET); | ||
168 | if (read(fd, buf, sizeof(*elf_phdr)) != sizeof(*elf_phdr)) { | ||
169 | perror("read"); | ||
170 | exit(1); | ||
171 | } | ||
172 | |||
173 | elf_phdr = (struct elf_phdr *) buf; | ||
174 | offset = elf_phdr->p_offset; | ||
175 | mem_size = elf_phdr->p_memsz; | ||
176 | fil_size = elf_phdr->p_filesz; | ||
177 | |||
178 | /* work around ELF bug: */ | ||
179 | if (elf_phdr->p_vaddr < e_entry) { | ||
180 | unsigned long delta = e_entry - elf_phdr->p_vaddr; | ||
181 | offset += delta; | ||
182 | mem_size -= delta; | ||
183 | fil_size -= delta; | ||
184 | elf_phdr->p_vaddr += delta; | ||
185 | } | ||
186 | |||
187 | if (verbose) { | ||
188 | fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n", | ||
189 | prog_name, (long) elf_phdr->p_vaddr, | ||
190 | elf_phdr->p_vaddr + fil_size, offset); | ||
191 | } | ||
192 | } else | ||
193 | #endif | ||
194 | { | ||
195 | aout = (struct exec *) buf; | ||
196 | |||
197 | if (!(aout->fh.f_flags & COFF_F_EXEC)) { | ||
198 | fprintf(stderr, "%s: %s is not in executable format\n", | ||
199 | prog_name, inname); | ||
200 | exit(1); | ||
201 | } | ||
202 | |||
203 | if (aout->fh.f_opthdr != sizeof(aout->ah)) { | ||
204 | fprintf(stderr, "%s: %s has unexpected optional header size\n", | ||
205 | prog_name, inname); | ||
206 | exit(1); | ||
207 | } | ||
208 | |||
209 | if (N_MAGIC(*aout) != OMAGIC) { | ||
210 | fprintf(stderr, "%s: %s is not an OMAGIC file\n", | ||
211 | prog_name, inname); | ||
212 | exit(1); | ||
213 | } | ||
214 | offset = N_TXTOFF(*aout); | ||
215 | fil_size = aout->ah.tsize + aout->ah.dsize; | ||
216 | mem_size = fil_size + aout->ah.bsize; | ||
217 | |||
218 | if (verbose) { | ||
219 | fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n", | ||
220 | prog_name, aout->ah.text_start, | ||
221 | aout->ah.text_start + fil_size, offset); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | if (lseek(fd, offset, SEEK_SET) != offset) { | ||
226 | perror("lseek"); | ||
227 | exit(1); | ||
228 | } | ||
229 | |||
230 | if (verbose) { | ||
231 | fprintf(stderr, "%s: copying %lu byte from %s\n", | ||
232 | prog_name, (unsigned long) fil_size, inname); | ||
233 | } | ||
234 | |||
235 | tocopy = fil_size; | ||
236 | while (tocopy > 0) { | ||
237 | n = tocopy; | ||
238 | if (n > sizeof(buf)) { | ||
239 | n = sizeof(buf); | ||
240 | } | ||
241 | tocopy -= n; | ||
242 | if ((size_t) read(fd, buf, n) != n) { | ||
243 | perror("read"); | ||
244 | exit(1); | ||
245 | } | ||
246 | do { | ||
247 | nwritten = write(ofd, buf, n); | ||
248 | if ((ssize_t) nwritten == -1) { | ||
249 | perror("write"); | ||
250 | exit(1); | ||
251 | } | ||
252 | n -= nwritten; | ||
253 | } while (n > 0); | ||
254 | } | ||
255 | |||
256 | if (pad) { | ||
257 | mem_size = ((mem_size + pad - 1) / pad) * pad; | ||
258 | } | ||
259 | |||
260 | tocopy = mem_size - fil_size; | ||
261 | if (tocopy > 0) { | ||
262 | fprintf(stderr, | ||
263 | "%s: zero-filling bss and aligning to %lu with %lu bytes\n", | ||
264 | prog_name, pad, (unsigned long) tocopy); | ||
265 | |||
266 | memset(buf, 0x00, sizeof(buf)); | ||
267 | do { | ||
268 | n = tocopy; | ||
269 | if (n > sizeof(buf)) { | ||
270 | n = sizeof(buf); | ||
271 | } | ||
272 | nwritten = write(ofd, buf, n); | ||
273 | if ((ssize_t) nwritten == -1) { | ||
274 | perror("write"); | ||
275 | exit(1); | ||
276 | } | ||
277 | tocopy -= nwritten; | ||
278 | } while (tocopy > 0); | ||
279 | } | ||
280 | return 0; | ||
281 | } | ||
diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig new file mode 100644 index 000000000000..5e39b7a7c8f4 --- /dev/null +++ b/arch/alpha/defconfig | |||
@@ -0,0 +1,927 @@ | |||
1 | # | ||
2 | # Automatically generated make config: don't edit | ||
3 | # Linux kernel version: 2.6.9-rc2 | ||
4 | # Sat Sep 25 15:38:35 2004 | ||
5 | # | ||
6 | CONFIG_ALPHA=y | ||
7 | CONFIG_64BIT=y | ||
8 | CONFIG_MMU=y | ||
9 | CONFIG_RWSEM_XCHGADD_ALGORITHM=y | ||
10 | CONFIG_GENERIC_ISA_DMA=y | ||
11 | # CONFIG_GENERIC_IOMAP is not set | ||
12 | |||
13 | # | ||
14 | # Code maturity level options | ||
15 | # | ||
16 | CONFIG_EXPERIMENTAL=y | ||
17 | CONFIG_CLEAN_COMPILE=y | ||
18 | CONFIG_BROKEN_ON_SMP=y | ||
19 | |||
20 | # | ||
21 | # General setup | ||
22 | # | ||
23 | CONFIG_LOCALVERSION="" | ||
24 | CONFIG_SWAP=y | ||
25 | CONFIG_SYSVIPC=y | ||
26 | CONFIG_POSIX_MQUEUE=y | ||
27 | # CONFIG_BSD_PROCESS_ACCT is not set | ||
28 | CONFIG_SYSCTL=y | ||
29 | # CONFIG_AUDIT is not set | ||
30 | CONFIG_LOG_BUF_SHIFT=14 | ||
31 | # CONFIG_HOTPLUG is not set | ||
32 | # CONFIG_IKCONFIG is not set | ||
33 | # CONFIG_EMBEDDED is not set | ||
34 | CONFIG_KALLSYMS=y | ||
35 | CONFIG_KALLSYMS_ALL=y | ||
36 | # CONFIG_KALLSYMS_EXTRA_PASS is not set | ||
37 | CONFIG_FUTEX=y | ||
38 | CONFIG_EPOLL=y | ||
39 | CONFIG_IOSCHED_NOOP=y | ||
40 | CONFIG_IOSCHED_AS=y | ||
41 | CONFIG_IOSCHED_DEADLINE=y | ||
42 | CONFIG_IOSCHED_CFQ=y | ||
43 | # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set | ||
44 | CONFIG_SHMEM=y | ||
45 | # CONFIG_TINY_SHMEM is not set | ||
46 | |||
47 | # | ||
48 | # Loadable module support | ||
49 | # | ||
50 | CONFIG_MODULES=y | ||
51 | CONFIG_MODULE_UNLOAD=y | ||
52 | # CONFIG_MODULE_FORCE_UNLOAD is not set | ||
53 | CONFIG_OBSOLETE_MODPARM=y | ||
54 | # CONFIG_MODVERSIONS is not set | ||
55 | CONFIG_KMOD=y | ||
56 | |||
57 | # | ||
58 | # System setup | ||
59 | # | ||
60 | CONFIG_ALPHA_GENERIC=y | ||
61 | # CONFIG_ALPHA_ALCOR is not set | ||
62 | # CONFIG_ALPHA_XL is not set | ||
63 | # CONFIG_ALPHA_BOOK1 is not set | ||
64 | # CONFIG_ALPHA_AVANTI_CH is not set | ||
65 | # CONFIG_ALPHA_CABRIOLET is not set | ||
66 | # CONFIG_ALPHA_DP264 is not set | ||
67 | # CONFIG_ALPHA_EB164 is not set | ||
68 | # CONFIG_ALPHA_EB64P_CH is not set | ||
69 | # CONFIG_ALPHA_EB66 is not set | ||
70 | # CONFIG_ALPHA_EB66P is not set | ||
71 | # CONFIG_ALPHA_EIGER is not set | ||
72 | # CONFIG_ALPHA_JENSEN is not set | ||
73 | # CONFIG_ALPHA_LX164 is not set | ||
74 | # CONFIG_ALPHA_LYNX is not set | ||
75 | # CONFIG_ALPHA_MARVEL is not set | ||
76 | # CONFIG_ALPHA_MIATA is not set | ||
77 | # CONFIG_ALPHA_MIKASA is not set | ||
78 | # CONFIG_ALPHA_NAUTILUS is not set | ||
79 | # CONFIG_ALPHA_NONAME_CH is not set | ||
80 | # CONFIG_ALPHA_NORITAKE is not set | ||
81 | # CONFIG_ALPHA_PC164 is not set | ||
82 | # CONFIG_ALPHA_P2K is not set | ||
83 | # CONFIG_ALPHA_RAWHIDE is not set | ||
84 | # CONFIG_ALPHA_RUFFIAN is not set | ||
85 | # CONFIG_ALPHA_RX164 is not set | ||
86 | # CONFIG_ALPHA_SX164 is not set | ||
87 | # CONFIG_ALPHA_SABLE is not set | ||
88 | # CONFIG_ALPHA_SHARK is not set | ||
89 | # CONFIG_ALPHA_TAKARA is not set | ||
90 | # CONFIG_ALPHA_TITAN is not set | ||
91 | # CONFIG_ALPHA_WILDFIRE is not set | ||
92 | CONFIG_ISA=y | ||
93 | CONFIG_PCI=y | ||
94 | CONFIG_PCI_DOMAINS=y | ||
95 | CONFIG_ALPHA_CORE_AGP=y | ||
96 | CONFIG_ALPHA_BROKEN_IRQ_MASK=y | ||
97 | CONFIG_EISA=y | ||
98 | # CONFIG_SMP is not set | ||
99 | # CONFIG_DISCONTIGMEM is not set | ||
100 | CONFIG_VERBOSE_MCHECK=y | ||
101 | CONFIG_VERBOSE_MCHECK_ON=1 | ||
102 | CONFIG_PCI_LEGACY_PROC=y | ||
103 | CONFIG_PCI_NAMES=y | ||
104 | CONFIG_EISA_PCI_EISA=y | ||
105 | CONFIG_EISA_VIRTUAL_ROOT=y | ||
106 | CONFIG_EISA_NAMES=y | ||
107 | CONFIG_SRM_ENV=m | ||
108 | CONFIG_BINFMT_ELF=y | ||
109 | # CONFIG_BINFMT_AOUT is not set | ||
110 | # CONFIG_BINFMT_EM86 is not set | ||
111 | # CONFIG_BINFMT_MISC is not set | ||
112 | |||
113 | # | ||
114 | # Device Drivers | ||
115 | # | ||
116 | |||
117 | # | ||
118 | # Generic Driver Options | ||
119 | # | ||
120 | CONFIG_STANDALONE=y | ||
121 | CONFIG_PREVENT_FIRMWARE_BUILD=y | ||
122 | # CONFIG_DEBUG_DRIVER is not set | ||
123 | |||
124 | # | ||
125 | # Memory Technology Devices (MTD) | ||
126 | # | ||
127 | # CONFIG_MTD is not set | ||
128 | |||
129 | # | ||
130 | # Parallel port support | ||
131 | # | ||
132 | # CONFIG_PARPORT is not set | ||
133 | |||
134 | # | ||
135 | # Plug and Play support | ||
136 | # | ||
137 | CONFIG_PNP=y | ||
138 | # CONFIG_PNP_DEBUG is not set | ||
139 | |||
140 | # | ||
141 | # Protocols | ||
142 | # | ||
143 | CONFIG_ISAPNP=y | ||
144 | |||
145 | # | ||
146 | # Block devices | ||
147 | # | ||
148 | CONFIG_BLK_DEV_FD=y | ||
149 | # CONFIG_BLK_DEV_XD is not set | ||
150 | # CONFIG_BLK_CPQ_DA is not set | ||
151 | # CONFIG_BLK_CPQ_CISS_DA is not set | ||
152 | # CONFIG_BLK_DEV_DAC960 is not set | ||
153 | # CONFIG_BLK_DEV_UMEM is not set | ||
154 | CONFIG_BLK_DEV_LOOP=m | ||
155 | # CONFIG_BLK_DEV_CRYPTOLOOP is not set | ||
156 | # CONFIG_BLK_DEV_NBD is not set | ||
157 | # CONFIG_BLK_DEV_SX8 is not set | ||
158 | # CONFIG_BLK_DEV_RAM is not set | ||
159 | |||
160 | # | ||
161 | # ATA/ATAPI/MFM/RLL support | ||
162 | # | ||
163 | CONFIG_IDE=y | ||
164 | CONFIG_IDE_MAX_HWIFS=4 | ||
165 | CONFIG_BLK_DEV_IDE=y | ||
166 | |||
167 | # | ||
168 | # Please see Documentation/ide.txt for help/info on IDE drives | ||
169 | # | ||
170 | # CONFIG_BLK_DEV_IDE_SATA is not set | ||
171 | CONFIG_BLK_DEV_IDEDISK=y | ||
172 | CONFIG_IDEDISK_MULTI_MODE=y | ||
173 | CONFIG_BLK_DEV_IDECD=y | ||
174 | # CONFIG_BLK_DEV_IDETAPE is not set | ||
175 | # CONFIG_BLK_DEV_IDEFLOPPY is not set | ||
176 | # CONFIG_BLK_DEV_IDESCSI is not set | ||
177 | # CONFIG_IDE_TASK_IOCTL is not set | ||
178 | # CONFIG_IDE_TASKFILE_IO is not set | ||
179 | |||
180 | # | ||
181 | # IDE chipset support/bugfixes | ||
182 | # | ||
183 | CONFIG_IDE_GENERIC=y | ||
184 | # CONFIG_BLK_DEV_IDEPNP is not set | ||
185 | CONFIG_BLK_DEV_IDEPCI=y | ||
186 | # CONFIG_IDEPCI_SHARE_IRQ is not set | ||
187 | # CONFIG_BLK_DEV_OFFBOARD is not set | ||
188 | CONFIG_BLK_DEV_GENERIC=y | ||
189 | # CONFIG_BLK_DEV_OPTI621 is not set | ||
190 | CONFIG_BLK_DEV_IDEDMA_PCI=y | ||
191 | # CONFIG_BLK_DEV_IDEDMA_FORCED is not set | ||
192 | CONFIG_IDEDMA_PCI_AUTO=y | ||
193 | # CONFIG_IDEDMA_ONLYDISK is not set | ||
194 | # CONFIG_BLK_DEV_AEC62XX is not set | ||
195 | CONFIG_BLK_DEV_ALI15X3=y | ||
196 | # CONFIG_WDC_ALI15X3 is not set | ||
197 | # CONFIG_BLK_DEV_AMD74XX is not set | ||
198 | CONFIG_BLK_DEV_CMD64X=y | ||
199 | # CONFIG_BLK_DEV_TRIFLEX is not set | ||
200 | CONFIG_BLK_DEV_CY82C693=y | ||
201 | # CONFIG_BLK_DEV_CS5520 is not set | ||
202 | # CONFIG_BLK_DEV_CS5530 is not set | ||
203 | # CONFIG_BLK_DEV_HPT34X is not set | ||
204 | # CONFIG_BLK_DEV_HPT366 is not set | ||
205 | # CONFIG_BLK_DEV_SC1200 is not set | ||
206 | # CONFIG_BLK_DEV_PIIX is not set | ||
207 | # CONFIG_BLK_DEV_NS87415 is not set | ||
208 | # CONFIG_BLK_DEV_PDC202XX_OLD is not set | ||
209 | # CONFIG_BLK_DEV_PDC202XX_NEW is not set | ||
210 | # CONFIG_BLK_DEV_SVWKS is not set | ||
211 | # CONFIG_BLK_DEV_SIIMAGE is not set | ||
212 | # CONFIG_BLK_DEV_SLC90E66 is not set | ||
213 | # CONFIG_BLK_DEV_TRM290 is not set | ||
214 | # CONFIG_BLK_DEV_VIA82CXXX is not set | ||
215 | # CONFIG_IDE_ARM is not set | ||
216 | # CONFIG_IDE_CHIPSETS is not set | ||
217 | CONFIG_BLK_DEV_IDEDMA=y | ||
218 | # CONFIG_IDEDMA_IVB is not set | ||
219 | CONFIG_IDEDMA_AUTO=y | ||
220 | # CONFIG_BLK_DEV_HD is not set | ||
221 | |||
222 | # | ||
223 | # SCSI device support | ||
224 | # | ||
225 | CONFIG_SCSI=y | ||
226 | CONFIG_SCSI_PROC_FS=y | ||
227 | |||
228 | # | ||
229 | # SCSI support type (disk, tape, CD-ROM) | ||
230 | # | ||
231 | CONFIG_BLK_DEV_SD=y | ||
232 | # CONFIG_CHR_DEV_ST is not set | ||
233 | # CONFIG_CHR_DEV_OSST is not set | ||
234 | CONFIG_BLK_DEV_SR=y | ||
235 | CONFIG_BLK_DEV_SR_VENDOR=y | ||
236 | # CONFIG_CHR_DEV_SG is not set | ||
237 | |||
238 | # | ||
239 | # Some SCSI devices (e.g. CD jukebox) support multiple LUNs | ||
240 | # | ||
241 | # CONFIG_SCSI_MULTI_LUN is not set | ||
242 | # CONFIG_SCSI_CONSTANTS is not set | ||
243 | # CONFIG_SCSI_LOGGING is not set | ||
244 | |||
245 | # | ||
246 | # SCSI Transport Attributes | ||
247 | # | ||
248 | # CONFIG_SCSI_SPI_ATTRS is not set | ||
249 | # CONFIG_SCSI_FC_ATTRS is not set | ||
250 | |||
251 | # | ||
252 | # SCSI low-level drivers | ||
253 | # | ||
254 | # CONFIG_BLK_DEV_3W_XXXX_RAID is not set | ||
255 | # CONFIG_SCSI_3W_9XXX is not set | ||
256 | # CONFIG_SCSI_7000FASST is not set | ||
257 | # CONFIG_SCSI_ACARD is not set | ||
258 | # CONFIG_SCSI_AHA1542 is not set | ||
259 | # CONFIG_SCSI_AHA1740 is not set | ||
260 | # CONFIG_SCSI_AACRAID is not set | ||
261 | CONFIG_SCSI_AIC7XXX=m | ||
262 | CONFIG_AIC7XXX_CMDS_PER_DEVICE=253 | ||
263 | CONFIG_AIC7XXX_RESET_DELAY_MS=5000 | ||
264 | # CONFIG_AIC7XXX_PROBE_EISA_VL is not set | ||
265 | # CONFIG_AIC7XXX_DEBUG_ENABLE is not set | ||
266 | CONFIG_AIC7XXX_DEBUG_MASK=0 | ||
267 | CONFIG_AIC7XXX_REG_PRETTY_PRINT=y | ||
268 | # CONFIG_SCSI_AIC7XXX_OLD is not set | ||
269 | # CONFIG_SCSI_AIC79XX is not set | ||
270 | # CONFIG_SCSI_IN2000 is not set | ||
271 | # CONFIG_MEGARAID_NEWGEN is not set | ||
272 | # CONFIG_MEGARAID_LEGACY is not set | ||
273 | # CONFIG_SCSI_SATA is not set | ||
274 | # CONFIG_SCSI_BUSLOGIC is not set | ||
275 | # CONFIG_SCSI_DMX3191D is not set | ||
276 | # CONFIG_SCSI_DTC3280 is not set | ||
277 | # CONFIG_SCSI_EATA is not set | ||
278 | # CONFIG_SCSI_EATA_PIO is not set | ||
279 | # CONFIG_SCSI_FUTURE_DOMAIN is not set | ||
280 | # CONFIG_SCSI_GDTH is not set | ||
281 | # CONFIG_SCSI_GENERIC_NCR5380 is not set | ||
282 | # CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set | ||
283 | # CONFIG_SCSI_IPS is not set | ||
284 | # CONFIG_SCSI_INIA100 is not set | ||
285 | # CONFIG_SCSI_NCR53C406A is not set | ||
286 | # CONFIG_SCSI_SYM53C8XX_2 is not set | ||
287 | # CONFIG_SCSI_IPR is not set | ||
288 | # CONFIG_SCSI_PAS16 is not set | ||
289 | # CONFIG_SCSI_PSI240I is not set | ||
290 | # CONFIG_SCSI_QLOGIC_FAS is not set | ||
291 | # CONFIG_SCSI_QLOGIC_ISP is not set | ||
292 | # CONFIG_SCSI_QLOGIC_FC is not set | ||
293 | # CONFIG_SCSI_QLOGIC_1280 is not set | ||
294 | CONFIG_SCSI_QLA2XXX=y | ||
295 | # CONFIG_SCSI_QLA21XX is not set | ||
296 | # CONFIG_SCSI_QLA22XX is not set | ||
297 | # CONFIG_SCSI_QLA2300 is not set | ||
298 | # CONFIG_SCSI_QLA2322 is not set | ||
299 | # CONFIG_SCSI_QLA6312 is not set | ||
300 | # CONFIG_SCSI_QLA6322 is not set | ||
301 | # CONFIG_SCSI_SIM710 is not set | ||
302 | # CONFIG_SCSI_SYM53C416 is not set | ||
303 | # CONFIG_SCSI_DC395x is not set | ||
304 | # CONFIG_SCSI_DC390T is not set | ||
305 | # CONFIG_SCSI_T128 is not set | ||
306 | # CONFIG_SCSI_U14_34F is not set | ||
307 | # CONFIG_SCSI_DEBUG is not set | ||
308 | |||
309 | # | ||
310 | # Old CD-ROM drivers (not SCSI, not IDE) | ||
311 | # | ||
312 | # CONFIG_CD_NO_IDESCSI is not set | ||
313 | |||
314 | # | ||
315 | # Multi-device support (RAID and LVM) | ||
316 | # | ||
317 | # CONFIG_MD is not set | ||
318 | |||
319 | # | ||
320 | # Fusion MPT device support | ||
321 | # | ||
322 | # CONFIG_FUSION is not set | ||
323 | |||
324 | # | ||
325 | # IEEE 1394 (FireWire) support | ||
326 | # | ||
327 | # CONFIG_IEEE1394 is not set | ||
328 | |||
329 | # | ||
330 | # I2O device support | ||
331 | # | ||
332 | # CONFIG_I2O is not set | ||
333 | |||
334 | # | ||
335 | # Networking support | ||
336 | # | ||
337 | CONFIG_NET=y | ||
338 | |||
339 | # | ||
340 | # Networking options | ||
341 | # | ||
342 | CONFIG_PACKET=y | ||
343 | # CONFIG_PACKET_MMAP is not set | ||
344 | CONFIG_NETLINK_DEV=y | ||
345 | CONFIG_UNIX=y | ||
346 | CONFIG_NET_KEY=m | ||
347 | CONFIG_INET=y | ||
348 | CONFIG_IP_MULTICAST=y | ||
349 | # CONFIG_IP_ADVANCED_ROUTER is not set | ||
350 | # CONFIG_IP_PNP is not set | ||
351 | # CONFIG_NET_IPIP is not set | ||
352 | # CONFIG_NET_IPGRE is not set | ||
353 | # CONFIG_IP_MROUTE is not set | ||
354 | # CONFIG_ARPD is not set | ||
355 | # CONFIG_SYN_COOKIES is not set | ||
356 | CONFIG_INET_AH=m | ||
357 | CONFIG_INET_ESP=m | ||
358 | # CONFIG_INET_IPCOMP is not set | ||
359 | # CONFIG_INET_TUNNEL is not set | ||
360 | |||
361 | # | ||
362 | # IP: Virtual Server Configuration | ||
363 | # | ||
364 | # CONFIG_IP_VS is not set | ||
365 | # CONFIG_IPV6 is not set | ||
366 | CONFIG_NETFILTER=y | ||
367 | # CONFIG_NETFILTER_DEBUG is not set | ||
368 | |||
369 | # | ||
370 | # IP: Netfilter Configuration | ||
371 | # | ||
372 | CONFIG_IP_NF_CONNTRACK=m | ||
373 | # CONFIG_IP_NF_CT_ACCT is not set | ||
374 | # CONFIG_IP_NF_CT_PROTO_SCTP is not set | ||
375 | CONFIG_IP_NF_FTP=m | ||
376 | CONFIG_IP_NF_IRC=m | ||
377 | # CONFIG_IP_NF_TFTP is not set | ||
378 | # CONFIG_IP_NF_AMANDA is not set | ||
379 | CONFIG_IP_NF_QUEUE=m | ||
380 | CONFIG_IP_NF_IPTABLES=m | ||
381 | # CONFIG_IP_NF_MATCH_LIMIT is not set | ||
382 | # CONFIG_IP_NF_MATCH_IPRANGE is not set | ||
383 | # CONFIG_IP_NF_MATCH_MAC is not set | ||
384 | # CONFIG_IP_NF_MATCH_PKTTYPE is not set | ||
385 | # CONFIG_IP_NF_MATCH_MARK is not set | ||
386 | # CONFIG_IP_NF_MATCH_MULTIPORT is not set | ||
387 | # CONFIG_IP_NF_MATCH_TOS is not set | ||
388 | # CONFIG_IP_NF_MATCH_RECENT is not set | ||
389 | # CONFIG_IP_NF_MATCH_ECN is not set | ||
390 | # CONFIG_IP_NF_MATCH_DSCP is not set | ||
391 | # CONFIG_IP_NF_MATCH_AH_ESP is not set | ||
392 | # CONFIG_IP_NF_MATCH_LENGTH is not set | ||
393 | # CONFIG_IP_NF_MATCH_TTL is not set | ||
394 | # CONFIG_IP_NF_MATCH_TCPMSS is not set | ||
395 | # CONFIG_IP_NF_MATCH_HELPER is not set | ||
396 | # CONFIG_IP_NF_MATCH_STATE is not set | ||
397 | # CONFIG_IP_NF_MATCH_CONNTRACK is not set | ||
398 | # CONFIG_IP_NF_MATCH_OWNER is not set | ||
399 | # CONFIG_IP_NF_MATCH_ADDRTYPE is not set | ||
400 | # CONFIG_IP_NF_MATCH_REALM is not set | ||
401 | # CONFIG_IP_NF_MATCH_SCTP is not set | ||
402 | # CONFIG_IP_NF_MATCH_COMMENT is not set | ||
403 | CONFIG_IP_NF_FILTER=m | ||
404 | # CONFIG_IP_NF_TARGET_REJECT is not set | ||
405 | # CONFIG_IP_NF_TARGET_LOG is not set | ||
406 | # CONFIG_IP_NF_TARGET_ULOG is not set | ||
407 | # CONFIG_IP_NF_TARGET_TCPMSS is not set | ||
408 | CONFIG_IP_NF_NAT=m | ||
409 | CONFIG_IP_NF_NAT_NEEDED=y | ||
410 | CONFIG_IP_NF_TARGET_MASQUERADE=m | ||
411 | # CONFIG_IP_NF_TARGET_REDIRECT is not set | ||
412 | # CONFIG_IP_NF_TARGET_NETMAP is not set | ||
413 | # CONFIG_IP_NF_TARGET_SAME is not set | ||
414 | # CONFIG_IP_NF_NAT_SNMP_BASIC is not set | ||
415 | CONFIG_IP_NF_NAT_IRC=m | ||
416 | CONFIG_IP_NF_NAT_FTP=m | ||
417 | # CONFIG_IP_NF_MANGLE is not set | ||
418 | # CONFIG_IP_NF_RAW is not set | ||
419 | # CONFIG_IP_NF_ARPTABLES is not set | ||
420 | CONFIG_IP_NF_COMPAT_IPCHAINS=y | ||
421 | CONFIG_XFRM=y | ||
422 | CONFIG_XFRM_USER=m | ||
423 | |||
424 | # | ||
425 | # SCTP Configuration (EXPERIMENTAL) | ||
426 | # | ||
427 | # CONFIG_IP_SCTP is not set | ||
428 | # CONFIG_ATM is not set | ||
429 | # CONFIG_BRIDGE is not set | ||
430 | CONFIG_VLAN_8021Q=m | ||
431 | # CONFIG_DECNET is not set | ||
432 | # CONFIG_LLC2 is not set | ||
433 | # CONFIG_IPX is not set | ||
434 | # CONFIG_ATALK is not set | ||
435 | # CONFIG_X25 is not set | ||
436 | # CONFIG_LAPB is not set | ||
437 | # CONFIG_NET_DIVERT is not set | ||
438 | # CONFIG_ECONET is not set | ||
439 | # CONFIG_WAN_ROUTER is not set | ||
440 | # CONFIG_NET_HW_FLOWCONTROL is not set | ||
441 | |||
442 | # | ||
443 | # QoS and/or fair queueing | ||
444 | # | ||
445 | # CONFIG_NET_SCHED is not set | ||
446 | # CONFIG_NET_CLS_ROUTE is not set | ||
447 | |||
448 | # | ||
449 | # Network testing | ||
450 | # | ||
451 | # CONFIG_NET_PKTGEN is not set | ||
452 | # CONFIG_NETPOLL is not set | ||
453 | # CONFIG_NET_POLL_CONTROLLER is not set | ||
454 | # CONFIG_HAMRADIO is not set | ||
455 | # CONFIG_IRDA is not set | ||
456 | # CONFIG_BT is not set | ||
457 | CONFIG_NETDEVICES=y | ||
458 | CONFIG_DUMMY=m | ||
459 | # CONFIG_BONDING is not set | ||
460 | # CONFIG_EQUALIZER is not set | ||
461 | # CONFIG_TUN is not set | ||
462 | # CONFIG_ETHERTAP is not set | ||
463 | # CONFIG_NET_SB1000 is not set | ||
464 | |||
465 | # | ||
466 | # ARCnet devices | ||
467 | # | ||
468 | # CONFIG_ARCNET is not set | ||
469 | |||
470 | # | ||
471 | # Ethernet (10 or 100Mbit) | ||
472 | # | ||
473 | CONFIG_NET_ETHERNET=y | ||
474 | CONFIG_MII=y | ||
475 | # CONFIG_HAPPYMEAL is not set | ||
476 | # CONFIG_SUNGEM is not set | ||
477 | CONFIG_NET_VENDOR_3COM=y | ||
478 | # CONFIG_EL1 is not set | ||
479 | # CONFIG_EL2 is not set | ||
480 | # CONFIG_ELPLUS is not set | ||
481 | # CONFIG_EL16 is not set | ||
482 | # CONFIG_EL3 is not set | ||
483 | # CONFIG_3C515 is not set | ||
484 | CONFIG_VORTEX=y | ||
485 | # CONFIG_TYPHOON is not set | ||
486 | # CONFIG_LANCE is not set | ||
487 | # CONFIG_NET_VENDOR_SMC is not set | ||
488 | # CONFIG_NET_VENDOR_RACAL is not set | ||
489 | |||
490 | # | ||
491 | # Tulip family network device support | ||
492 | # | ||
493 | CONFIG_NET_TULIP=y | ||
494 | CONFIG_DE2104X=m | ||
495 | CONFIG_TULIP=y | ||
496 | # CONFIG_TULIP_MWI is not set | ||
497 | CONFIG_TULIP_MMIO=y | ||
498 | # CONFIG_TULIP_NAPI is not set | ||
499 | # CONFIG_DE4X5 is not set | ||
500 | # CONFIG_WINBOND_840 is not set | ||
501 | # CONFIG_DM9102 is not set | ||
502 | # CONFIG_AT1700 is not set | ||
503 | # CONFIG_DEPCA is not set | ||
504 | # CONFIG_HP100 is not set | ||
505 | # CONFIG_NET_ISA is not set | ||
506 | CONFIG_NET_PCI=y | ||
507 | # CONFIG_PCNET32 is not set | ||
508 | # CONFIG_AMD8111_ETH is not set | ||
509 | # CONFIG_ADAPTEC_STARFIRE is not set | ||
510 | # CONFIG_AC3200 is not set | ||
511 | # CONFIG_APRICOT is not set | ||
512 | # CONFIG_B44 is not set | ||
513 | # CONFIG_FORCEDETH is not set | ||
514 | # CONFIG_CS89x0 is not set | ||
515 | # CONFIG_DGRS is not set | ||
516 | # CONFIG_EEPRO100 is not set | ||
517 | # CONFIG_E100 is not set | ||
518 | # CONFIG_LNE390 is not set | ||
519 | # CONFIG_FEALNX is not set | ||
520 | # CONFIG_NATSEMI is not set | ||
521 | # CONFIG_NE2K_PCI is not set | ||
522 | # CONFIG_NE3210 is not set | ||
523 | # CONFIG_ES3210 is not set | ||
524 | # CONFIG_8139CP is not set | ||
525 | # CONFIG_8139TOO is not set | ||
526 | # CONFIG_SIS900 is not set | ||
527 | # CONFIG_EPIC100 is not set | ||
528 | # CONFIG_SUNDANCE is not set | ||
529 | # CONFIG_VIA_RHINE is not set | ||
530 | # CONFIG_VIA_VELOCITY is not set | ||
531 | # CONFIG_NET_POCKET is not set | ||
532 | |||
533 | # | ||
534 | # Ethernet (1000 Mbit) | ||
535 | # | ||
536 | # CONFIG_ACENIC is not set | ||
537 | # CONFIG_DL2K is not set | ||
538 | # CONFIG_E1000 is not set | ||
539 | # CONFIG_NS83820 is not set | ||
540 | # CONFIG_HAMACHI is not set | ||
541 | CONFIG_YELLOWFIN=y | ||
542 | # CONFIG_R8169 is not set | ||
543 | # CONFIG_SK98LIN is not set | ||
544 | # CONFIG_TIGON3 is not set | ||
545 | |||
546 | # | ||
547 | # Ethernet (10000 Mbit) | ||
548 | # | ||
549 | # CONFIG_IXGB is not set | ||
550 | # CONFIG_S2IO is not set | ||
551 | |||
552 | # | ||
553 | # Token Ring devices | ||
554 | # | ||
555 | # CONFIG_TR is not set | ||
556 | |||
557 | # | ||
558 | # Wireless LAN (non-hamradio) | ||
559 | # | ||
560 | # CONFIG_NET_RADIO is not set | ||
561 | |||
562 | # | ||
563 | # Wan interfaces | ||
564 | # | ||
565 | # CONFIG_WAN is not set | ||
566 | # CONFIG_FDDI is not set | ||
567 | # CONFIG_HIPPI is not set | ||
568 | # CONFIG_PPP is not set | ||
569 | # CONFIG_SLIP is not set | ||
570 | # CONFIG_NET_FC is not set | ||
571 | # CONFIG_SHAPER is not set | ||
572 | # CONFIG_NETCONSOLE is not set | ||
573 | |||
574 | # | ||
575 | # ISDN subsystem | ||
576 | # | ||
577 | # CONFIG_ISDN is not set | ||
578 | |||
579 | # | ||
580 | # Telephony Support | ||
581 | # | ||
582 | # CONFIG_PHONE is not set | ||
583 | |||
584 | # | ||
585 | # Input device support | ||
586 | # | ||
587 | CONFIG_INPUT=y | ||
588 | |||
589 | # | ||
590 | # Userland interfaces | ||
591 | # | ||
592 | CONFIG_INPUT_MOUSEDEV=y | ||
593 | CONFIG_INPUT_MOUSEDEV_PSAUX=y | ||
594 | CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 | ||
595 | CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 | ||
596 | # CONFIG_INPUT_JOYDEV is not set | ||
597 | # CONFIG_INPUT_TSDEV is not set | ||
598 | # CONFIG_INPUT_EVDEV is not set | ||
599 | # CONFIG_INPUT_EVBUG is not set | ||
600 | |||
601 | # | ||
602 | # Input I/O drivers | ||
603 | # | ||
604 | # CONFIG_GAMEPORT is not set | ||
605 | CONFIG_SOUND_GAMEPORT=y | ||
606 | CONFIG_SERIO=y | ||
607 | CONFIG_SERIO_I8042=y | ||
608 | CONFIG_SERIO_SERPORT=y | ||
609 | # CONFIG_SERIO_CT82C710 is not set | ||
610 | # CONFIG_SERIO_PCIPS2 is not set | ||
611 | # CONFIG_SERIO_RAW is not set | ||
612 | |||
613 | # | ||
614 | # Input Device Drivers | ||
615 | # | ||
616 | CONFIG_INPUT_KEYBOARD=y | ||
617 | CONFIG_KEYBOARD_ATKBD=y | ||
618 | # CONFIG_KEYBOARD_SUNKBD is not set | ||
619 | # CONFIG_KEYBOARD_LKKBD is not set | ||
620 | # CONFIG_KEYBOARD_XTKBD is not set | ||
621 | # CONFIG_KEYBOARD_NEWTON is not set | ||
622 | CONFIG_INPUT_MOUSE=y | ||
623 | CONFIG_MOUSE_PS2=y | ||
624 | # CONFIG_MOUSE_SERIAL is not set | ||
625 | # CONFIG_MOUSE_INPORT is not set | ||
626 | # CONFIG_MOUSE_LOGIBM is not set | ||
627 | # CONFIG_MOUSE_PC110PAD is not set | ||
628 | # CONFIG_MOUSE_VSXXXAA is not set | ||
629 | # CONFIG_INPUT_JOYSTICK is not set | ||
630 | # CONFIG_INPUT_TOUCHSCREEN is not set | ||
631 | # CONFIG_INPUT_MISC is not set | ||
632 | |||
633 | # | ||
634 | # Character devices | ||
635 | # | ||
636 | CONFIG_VT=y | ||
637 | CONFIG_VT_CONSOLE=y | ||
638 | CONFIG_HW_CONSOLE=y | ||
639 | # CONFIG_SERIAL_NONSTANDARD is not set | ||
640 | |||
641 | # | ||
642 | # Serial drivers | ||
643 | # | ||
644 | CONFIG_SERIAL_8250=y | ||
645 | CONFIG_SERIAL_8250_CONSOLE=y | ||
646 | CONFIG_SERIAL_8250_NR_UARTS=4 | ||
647 | # CONFIG_SERIAL_8250_EXTENDED is not set | ||
648 | |||
649 | # | ||
650 | # Non-8250 serial port support | ||
651 | # | ||
652 | CONFIG_SERIAL_CORE=y | ||
653 | CONFIG_SERIAL_CORE_CONSOLE=y | ||
654 | CONFIG_UNIX98_PTYS=y | ||
655 | CONFIG_LEGACY_PTYS=y | ||
656 | CONFIG_LEGACY_PTY_COUNT=256 | ||
657 | |||
658 | # | ||
659 | # IPMI | ||
660 | # | ||
661 | # CONFIG_IPMI_HANDLER is not set | ||
662 | |||
663 | # | ||
664 | # Watchdog Cards | ||
665 | # | ||
666 | # CONFIG_WATCHDOG is not set | ||
667 | CONFIG_RTC=y | ||
668 | # CONFIG_DTLK is not set | ||
669 | # CONFIG_R3964 is not set | ||
670 | # CONFIG_APPLICOM is not set | ||
671 | |||
672 | # | ||
673 | # Ftape, the floppy tape device driver | ||
674 | # | ||
675 | # CONFIG_FTAPE is not set | ||
676 | # CONFIG_AGP is not set | ||
677 | # CONFIG_DRM is not set | ||
678 | # CONFIG_RAW_DRIVER is not set | ||
679 | |||
680 | # | ||
681 | # I2C support | ||
682 | # | ||
683 | # CONFIG_I2C is not set | ||
684 | |||
685 | # | ||
686 | # Dallas's 1-wire bus | ||
687 | # | ||
688 | # CONFIG_W1 is not set | ||
689 | |||
690 | # | ||
691 | # Misc devices | ||
692 | # | ||
693 | |||
694 | # | ||
695 | # Multimedia devices | ||
696 | # | ||
697 | # CONFIG_VIDEO_DEV is not set | ||
698 | |||
699 | # | ||
700 | # Digital Video Broadcasting Devices | ||
701 | # | ||
702 | # CONFIG_DVB is not set | ||
703 | |||
704 | # | ||
705 | # Graphics support | ||
706 | # | ||
707 | # CONFIG_FB is not set | ||
708 | |||
709 | # | ||
710 | # Console display driver support | ||
711 | # | ||
712 | CONFIG_VGA_CONSOLE=y | ||
713 | # CONFIG_MDA_CONSOLE is not set | ||
714 | CONFIG_DUMMY_CONSOLE=y | ||
715 | |||
716 | # | ||
717 | # Sound | ||
718 | # | ||
719 | # CONFIG_SOUND is not set | ||
720 | |||
721 | # | ||
722 | # USB support | ||
723 | # | ||
724 | # CONFIG_USB is not set | ||
725 | |||
726 | # | ||
727 | # USB Gadget Support | ||
728 | # | ||
729 | # CONFIG_USB_GADGET is not set | ||
730 | |||
731 | # | ||
732 | # File systems | ||
733 | # | ||
734 | CONFIG_EXT2_FS=y | ||
735 | # CONFIG_EXT2_FS_XATTR is not set | ||
736 | # CONFIG_EXT3_FS is not set | ||
737 | # CONFIG_JBD is not set | ||
738 | CONFIG_REISERFS_FS=m | ||
739 | # CONFIG_REISERFS_CHECK is not set | ||
740 | # CONFIG_REISERFS_PROC_INFO is not set | ||
741 | # CONFIG_REISERFS_FS_XATTR is not set | ||
742 | # CONFIG_JFS_FS is not set | ||
743 | # CONFIG_XFS_FS is not set | ||
744 | # CONFIG_MINIX_FS is not set | ||
745 | # CONFIG_ROMFS_FS is not set | ||
746 | # CONFIG_QUOTA is not set | ||
747 | CONFIG_AUTOFS_FS=m | ||
748 | # CONFIG_AUTOFS4_FS is not set | ||
749 | |||
750 | # | ||
751 | # CD-ROM/DVD Filesystems | ||
752 | # | ||
753 | CONFIG_ISO9660_FS=y | ||
754 | # CONFIG_JOLIET is not set | ||
755 | # CONFIG_ZISOFS is not set | ||
756 | # CONFIG_UDF_FS is not set | ||
757 | |||
758 | # | ||
759 | # DOS/FAT/NT Filesystems | ||
760 | # | ||
761 | CONFIG_FAT_FS=y | ||
762 | CONFIG_MSDOS_FS=y | ||
763 | CONFIG_VFAT_FS=y | ||
764 | CONFIG_FAT_DEFAULT_CODEPAGE=437 | ||
765 | CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" | ||
766 | # CONFIG_NTFS_FS is not set | ||
767 | |||
768 | # | ||
769 | # Pseudo filesystems | ||
770 | # | ||
771 | CONFIG_PROC_FS=y | ||
772 | CONFIG_PROC_KCORE=y | ||
773 | CONFIG_SYSFS=y | ||
774 | # CONFIG_DEVFS_FS is not set | ||
775 | # CONFIG_DEVPTS_FS_XATTR is not set | ||
776 | CONFIG_TMPFS=y | ||
777 | # CONFIG_HUGETLB_PAGE is not set | ||
778 | CONFIG_RAMFS=y | ||
779 | |||
780 | # | ||
781 | # Miscellaneous filesystems | ||
782 | # | ||
783 | # CONFIG_ADFS_FS is not set | ||
784 | # CONFIG_AFFS_FS is not set | ||
785 | # CONFIG_HFS_FS is not set | ||
786 | # CONFIG_HFSPLUS_FS is not set | ||
787 | # CONFIG_BEFS_FS is not set | ||
788 | # CONFIG_BFS_FS is not set | ||
789 | # CONFIG_EFS_FS is not set | ||
790 | # CONFIG_CRAMFS is not set | ||
791 | # CONFIG_VXFS_FS is not set | ||
792 | # CONFIG_HPFS_FS is not set | ||
793 | # CONFIG_QNX4FS_FS is not set | ||
794 | # CONFIG_SYSV_FS is not set | ||
795 | # CONFIG_UFS_FS is not set | ||
796 | |||
797 | # | ||
798 | # Network File Systems | ||
799 | # | ||
800 | CONFIG_NFS_FS=m | ||
801 | CONFIG_NFS_V3=y | ||
802 | # CONFIG_NFS_V4 is not set | ||
803 | # CONFIG_NFS_DIRECTIO is not set | ||
804 | CONFIG_NFSD=m | ||
805 | CONFIG_NFSD_V3=y | ||
806 | # CONFIG_NFSD_V4 is not set | ||
807 | CONFIG_NFSD_TCP=y | ||
808 | CONFIG_LOCKD=m | ||
809 | CONFIG_LOCKD_V4=y | ||
810 | CONFIG_EXPORTFS=m | ||
811 | CONFIG_SUNRPC=m | ||
812 | # CONFIG_RPCSEC_GSS_KRB5 is not set | ||
813 | # CONFIG_RPCSEC_GSS_SPKM3 is not set | ||
814 | # CONFIG_SMB_FS is not set | ||
815 | # CONFIG_CIFS is not set | ||
816 | # CONFIG_NCP_FS is not set | ||
817 | # CONFIG_CODA_FS is not set | ||
818 | # CONFIG_AFS_FS is not set | ||
819 | |||
820 | # | ||
821 | # Partition Types | ||
822 | # | ||
823 | # CONFIG_PARTITION_ADVANCED is not set | ||
824 | CONFIG_OSF_PARTITION=y | ||
825 | CONFIG_MSDOS_PARTITION=y | ||
826 | |||
827 | # | ||
828 | # Native Language Support | ||
829 | # | ||
830 | CONFIG_NLS=y | ||
831 | CONFIG_NLS_DEFAULT="iso8859-1" | ||
832 | CONFIG_NLS_CODEPAGE_437=y | ||
833 | # CONFIG_NLS_CODEPAGE_737 is not set | ||
834 | # CONFIG_NLS_CODEPAGE_775 is not set | ||
835 | # CONFIG_NLS_CODEPAGE_850 is not set | ||
836 | # CONFIG_NLS_CODEPAGE_852 is not set | ||
837 | # CONFIG_NLS_CODEPAGE_855 is not set | ||
838 | # CONFIG_NLS_CODEPAGE_857 is not set | ||
839 | # CONFIG_NLS_CODEPAGE_860 is not set | ||
840 | # CONFIG_NLS_CODEPAGE_861 is not set | ||
841 | # CONFIG_NLS_CODEPAGE_862 is not set | ||
842 | # CONFIG_NLS_CODEPAGE_863 is not set | ||
843 | # CONFIG_NLS_CODEPAGE_864 is not set | ||
844 | # CONFIG_NLS_CODEPAGE_865 is not set | ||
845 | # CONFIG_NLS_CODEPAGE_866 is not set | ||
846 | # CONFIG_NLS_CODEPAGE_869 is not set | ||
847 | # CONFIG_NLS_CODEPAGE_936 is not set | ||
848 | # CONFIG_NLS_CODEPAGE_950 is not set | ||
849 | # CONFIG_NLS_CODEPAGE_932 is not set | ||
850 | # CONFIG_NLS_CODEPAGE_949 is not set | ||
851 | # CONFIG_NLS_CODEPAGE_874 is not set | ||
852 | # CONFIG_NLS_ISO8859_8 is not set | ||
853 | # CONFIG_NLS_CODEPAGE_1250 is not set | ||
854 | # CONFIG_NLS_CODEPAGE_1251 is not set | ||
855 | # CONFIG_NLS_ASCII is not set | ||
856 | # CONFIG_NLS_ISO8859_1 is not set | ||
857 | # CONFIG_NLS_ISO8859_2 is not set | ||
858 | # CONFIG_NLS_ISO8859_3 is not set | ||
859 | # CONFIG_NLS_ISO8859_4 is not set | ||
860 | # CONFIG_NLS_ISO8859_5 is not set | ||
861 | # CONFIG_NLS_ISO8859_6 is not set | ||
862 | # CONFIG_NLS_ISO8859_7 is not set | ||
863 | # CONFIG_NLS_ISO8859_9 is not set | ||
864 | # CONFIG_NLS_ISO8859_13 is not set | ||
865 | # CONFIG_NLS_ISO8859_14 is not set | ||
866 | # CONFIG_NLS_ISO8859_15 is not set | ||
867 | # CONFIG_NLS_KOI8_R is not set | ||
868 | # CONFIG_NLS_KOI8_U is not set | ||
869 | # CONFIG_NLS_UTF8 is not set | ||
870 | |||
871 | # | ||
872 | # Profiling support | ||
873 | # | ||
874 | # CONFIG_PROFILING is not set | ||
875 | |||
876 | # | ||
877 | # Kernel hacking | ||
878 | # | ||
879 | CONFIG_DEBUG_KERNEL=y | ||
880 | CONFIG_MAGIC_SYSRQ=y | ||
881 | # CONFIG_DEBUG_SLAB is not set | ||
882 | # CONFIG_DEBUG_SPINLOCK is not set | ||
883 | CONFIG_DEBUG_INFO=y | ||
884 | CONFIG_EARLY_PRINTK=y | ||
885 | # CONFIG_DEBUG_RWLOCK is not set | ||
886 | # CONFIG_DEBUG_SEMAPHORE is not set | ||
887 | CONFIG_ALPHA_LEGACY_START_ADDRESS=y | ||
888 | CONFIG_MATHEMU=y | ||
889 | |||
890 | # | ||
891 | # Security options | ||
892 | # | ||
893 | # CONFIG_SECURITY is not set | ||
894 | |||
895 | # | ||
896 | # Cryptographic options | ||
897 | # | ||
898 | CONFIG_CRYPTO=y | ||
899 | CONFIG_CRYPTO_HMAC=y | ||
900 | # CONFIG_CRYPTO_NULL is not set | ||
901 | # CONFIG_CRYPTO_MD4 is not set | ||
902 | CONFIG_CRYPTO_MD5=m | ||
903 | CONFIG_CRYPTO_SHA1=m | ||
904 | # CONFIG_CRYPTO_SHA256 is not set | ||
905 | # CONFIG_CRYPTO_SHA512 is not set | ||
906 | # CONFIG_CRYPTO_WHIRLPOOL is not set | ||
907 | CONFIG_CRYPTO_DES=m | ||
908 | # CONFIG_CRYPTO_BLOWFISH is not set | ||
909 | # CONFIG_CRYPTO_TWOFISH is not set | ||
910 | # CONFIG_CRYPTO_SERPENT is not set | ||
911 | # CONFIG_CRYPTO_AES is not set | ||
912 | # CONFIG_CRYPTO_CAST5 is not set | ||
913 | # CONFIG_CRYPTO_CAST6 is not set | ||
914 | # CONFIG_CRYPTO_TEA is not set | ||
915 | # CONFIG_CRYPTO_ARC4 is not set | ||
916 | # CONFIG_CRYPTO_KHAZAD is not set | ||
917 | # CONFIG_CRYPTO_DEFLATE is not set | ||
918 | # CONFIG_CRYPTO_MICHAEL_MIC is not set | ||
919 | # CONFIG_CRYPTO_CRC32C is not set | ||
920 | # CONFIG_CRYPTO_TEST is not set | ||
921 | |||
922 | # | ||
923 | # Library routines | ||
924 | # | ||
925 | # CONFIG_CRC_CCITT is not set | ||
926 | CONFIG_CRC32=y | ||
927 | # CONFIG_LIBCRC32C is not set | ||
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile new file mode 100644 index 000000000000..ab6fa54b3860 --- /dev/null +++ b/arch/alpha/kernel/Makefile | |||
@@ -0,0 +1,104 @@ | |||
1 | # | ||
2 | # Makefile for the linux kernel. | ||
3 | # | ||
4 | |||
5 | extra-y := head.o vmlinux.lds | ||
6 | EXTRA_AFLAGS := $(CFLAGS) | ||
7 | EXTRA_CFLAGS := -Werror -Wno-sign-compare | ||
8 | |||
9 | obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \ | ||
10 | irq_alpha.o signal.o setup.o ptrace.o time.o semaphore.o \ | ||
11 | alpha_ksyms.o systbls.o err_common.o io.o | ||
12 | |||
13 | obj-$(CONFIG_VGA_HOSE) += console.o | ||
14 | obj-$(CONFIG_SMP) += smp.o | ||
15 | obj-$(CONFIG_PCI) += pci.o pci_iommu.o | ||
16 | obj-$(CONFIG_SRM_ENV) += srm_env.o | ||
17 | obj-$(CONFIG_MODULES) += module.o | ||
18 | |||
19 | ifdef CONFIG_ALPHA_GENERIC | ||
20 | |||
21 | obj-y += core_apecs.o core_cia.o core_irongate.o core_lca.o \ | ||
22 | core_mcpcia.o core_polaris.o core_t2.o \ | ||
23 | core_tsunami.o | ||
24 | |||
25 | obj-y += sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o sys_eiger.o \ | ||
26 | sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \ | ||
27 | sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \ | ||
28 | sys_sable.o sys_sio.o sys_sx164.o sys_takara.o | ||
29 | |||
30 | ifndef CONFIG_ALPHA_LEGACY_START_ADDRESS | ||
31 | obj-y += core_marvel.o core_titan.o core_wildfire.o | ||
32 | obj-y += sys_marvel.o sys_titan.o sys_wildfire.o | ||
33 | obj-y += err_ev7.o err_titan.o err_marvel.o | ||
34 | endif | ||
35 | |||
36 | obj-y += irq_pyxis.o irq_i8259.o irq_srm.o | ||
37 | obj-y += err_ev6.o | ||
38 | obj-y += es1888.o smc37c669.o smc37c93x.o ns87312.o gct.o | ||
39 | obj-y += srmcons.o | ||
40 | |||
41 | else | ||
42 | |||
43 | # Misc support | ||
44 | obj-$(CONFIG_ALPHA_SRM) += srmcons.o | ||
45 | |||
46 | # Core logic support | ||
47 | obj-$(CONFIG_ALPHA_APECS) += core_apecs.o | ||
48 | obj-$(CONFIG_ALPHA_CIA) += core_cia.o | ||
49 | obj-$(CONFIG_ALPHA_IRONGATE) += core_irongate.o | ||
50 | obj-$(CONFIG_ALPHA_LCA) += core_lca.o | ||
51 | obj-$(CONFIG_ALPHA_MARVEL) += core_marvel.o gct.o | ||
52 | obj-$(CONFIG_ALPHA_MCPCIA) += core_mcpcia.o | ||
53 | obj-$(CONFIG_ALPHA_POLARIS) += core_polaris.o | ||
54 | obj-$(CONFIG_ALPHA_T2) += core_t2.o | ||
55 | obj-$(CONFIG_ALPHA_TSUNAMI) += core_tsunami.o | ||
56 | obj-$(CONFIG_ALPHA_TITAN) += core_titan.o | ||
57 | obj-$(CONFIG_ALPHA_WILDFIRE) += core_wildfire.o | ||
58 | |||
59 | # Board support | ||
60 | obj-$(CONFIG_ALPHA_ALCOR) += sys_alcor.o irq_i8259.o irq_srm.o | ||
61 | obj-$(CONFIG_ALPHA_CABRIOLET) += sys_cabriolet.o irq_i8259.o irq_srm.o \ | ||
62 | ns87312.o | ||
63 | obj-$(CONFIG_ALPHA_EB164) += sys_cabriolet.o irq_i8259.o irq_srm.o \ | ||
64 | ns87312.o | ||
65 | obj-$(CONFIG_ALPHA_EB66P) += sys_cabriolet.o irq_i8259.o irq_srm.o \ | ||
66 | ns87312.o | ||
67 | obj-$(CONFIG_ALPHA_LX164) += sys_cabriolet.o irq_i8259.o irq_srm.o \ | ||
68 | smc37c93x.o | ||
69 | obj-$(CONFIG_ALPHA_PC164) += sys_cabriolet.o irq_i8259.o irq_srm.o \ | ||
70 | smc37c93x.o | ||
71 | obj-$(CONFIG_ALPHA_DP264) += sys_dp264.o irq_i8259.o es1888.o smc37c669.o | ||
72 | obj-$(CONFIG_ALPHA_SHARK) += sys_dp264.o irq_i8259.o es1888.o smc37c669.o | ||
73 | obj-$(CONFIG_ALPHA_TITAN) += sys_titan.o irq_i8259.o smc37c669.o | ||
74 | obj-$(CONFIG_ALPHA_EB64P) += sys_eb64p.o irq_i8259.o | ||
75 | obj-$(CONFIG_ALPHA_EB66) += sys_eb64p.o irq_i8259.o | ||
76 | obj-$(CONFIG_ALPHA_EIGER) += sys_eiger.o irq_i8259.o | ||
77 | obj-$(CONFIG_ALPHA_JENSEN) += sys_jensen.o pci-noop.o irq_i8259.o | ||
78 | obj-$(CONFIG_ALPHA_MARVEL) += sys_marvel.o | ||
79 | obj-$(CONFIG_ALPHA_MIATA) += sys_miata.o irq_pyxis.o irq_i8259.o \ | ||
80 | es1888.o smc37c669.o | ||
81 | obj-$(CONFIG_ALPHA_MIKASA) += sys_mikasa.o irq_i8259.o irq_srm.o | ||
82 | obj-$(CONFIG_ALPHA_NAUTILUS) += sys_nautilus.o irq_i8259.o irq_srm.o | ||
83 | obj-$(CONFIG_ALPHA_NORITAKE) += sys_noritake.o irq_i8259.o | ||
84 | obj-$(CONFIG_ALPHA_RAWHIDE) += sys_rawhide.o irq_i8259.o | ||
85 | obj-$(CONFIG_ALPHA_RUFFIAN) += sys_ruffian.o irq_pyxis.o irq_i8259.o | ||
86 | obj-$(CONFIG_ALPHA_RX164) += sys_rx164.o irq_i8259.o | ||
87 | obj-$(CONFIG_ALPHA_SABLE) += sys_sable.o | ||
88 | obj-$(CONFIG_ALPHA_LYNX) += sys_sable.o | ||
89 | obj-$(CONFIG_ALPHA_BOOK1) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o | ||
90 | obj-$(CONFIG_ALPHA_AVANTI) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o | ||
91 | obj-$(CONFIG_ALPHA_NONAME) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o | ||
92 | obj-$(CONFIG_ALPHA_P2K) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o | ||
93 | obj-$(CONFIG_ALPHA_XL) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o | ||
94 | obj-$(CONFIG_ALPHA_SX164) += sys_sx164.o irq_pyxis.o irq_i8259.o \ | ||
95 | irq_srm.o smc37c669.o | ||
96 | obj-$(CONFIG_ALPHA_TAKARA) += sys_takara.o irq_i8259.o ns87312.o | ||
97 | obj-$(CONFIG_ALPHA_WILDFIRE) += sys_wildfire.o irq_i8259.o | ||
98 | |||
99 | # Error support | ||
100 | obj-$(CONFIG_ALPHA_MARVEL) += err_ev7.o err_marvel.o | ||
101 | obj-$(CONFIG_ALPHA_NAUTILUS) += err_ev6.o | ||
102 | obj-$(CONFIG_ALPHA_TITAN) += err_ev6.o err_titan.o | ||
103 | |||
104 | endif # GENERIC | ||
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c new file mode 100644 index 000000000000..fc5ef90c4fc9 --- /dev/null +++ b/arch/alpha/kernel/alpha_ksyms.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/ksyms.c | ||
3 | * | ||
4 | * Export the alpha-specific functions that are needed for loadable | ||
5 | * modules. | ||
6 | */ | ||
7 | |||
8 | #include <linux/config.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/user.h> | ||
12 | #include <linux/elfcore.h> | ||
13 | #include <linux/socket.h> | ||
14 | #include <linux/syscalls.h> | ||
15 | #include <linux/in.h> | ||
16 | #include <linux/in6.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/tty.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | |||
23 | #include <asm/io.h> | ||
24 | #include <asm/console.h> | ||
25 | #include <asm/hwrpb.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/processor.h> | ||
28 | #include <asm/checksum.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <asm/fpu.h> | ||
31 | #include <asm/irq.h> | ||
32 | #include <asm/machvec.h> | ||
33 | #include <asm/pgalloc.h> | ||
34 | #include <asm/semaphore.h> | ||
35 | #include <asm/tlbflush.h> | ||
36 | #include <asm/cacheflush.h> | ||
37 | #include <asm/vga.h> | ||
38 | |||
39 | #define __KERNEL_SYSCALLS__ | ||
40 | #include <asm/unistd.h> | ||
41 | |||
42 | extern struct hwrpb_struct *hwrpb; | ||
43 | extern void dump_thread(struct pt_regs *, struct user *); | ||
44 | extern spinlock_t rtc_lock; | ||
45 | |||
46 | /* these are C runtime functions with special calling conventions: */ | ||
47 | extern void __divl (void); | ||
48 | extern void __reml (void); | ||
49 | extern void __divq (void); | ||
50 | extern void __remq (void); | ||
51 | extern void __divlu (void); | ||
52 | extern void __remlu (void); | ||
53 | extern void __divqu (void); | ||
54 | extern void __remqu (void); | ||
55 | |||
56 | EXPORT_SYMBOL(alpha_mv); | ||
57 | EXPORT_SYMBOL(enable_irq); | ||
58 | EXPORT_SYMBOL(disable_irq); | ||
59 | EXPORT_SYMBOL(disable_irq_nosync); | ||
60 | EXPORT_SYMBOL(probe_irq_mask); | ||
61 | EXPORT_SYMBOL(screen_info); | ||
62 | EXPORT_SYMBOL(perf_irq); | ||
63 | EXPORT_SYMBOL(callback_getenv); | ||
64 | EXPORT_SYMBOL(callback_setenv); | ||
65 | EXPORT_SYMBOL(callback_save_env); | ||
66 | #ifdef CONFIG_ALPHA_GENERIC | ||
67 | EXPORT_SYMBOL(alpha_using_srm); | ||
68 | #endif /* CONFIG_ALPHA_GENERIC */ | ||
69 | |||
70 | /* platform dependent support */ | ||
71 | EXPORT_SYMBOL(strcat); | ||
72 | EXPORT_SYMBOL(strcmp); | ||
73 | EXPORT_SYMBOL(strcpy); | ||
74 | EXPORT_SYMBOL(strlen); | ||
75 | EXPORT_SYMBOL(strncmp); | ||
76 | EXPORT_SYMBOL(strncpy); | ||
77 | EXPORT_SYMBOL(strnlen); | ||
78 | EXPORT_SYMBOL(strncat); | ||
79 | EXPORT_SYMBOL(strstr); | ||
80 | EXPORT_SYMBOL(strpbrk); | ||
81 | EXPORT_SYMBOL(strchr); | ||
82 | EXPORT_SYMBOL(strrchr); | ||
83 | EXPORT_SYMBOL(memcmp); | ||
84 | EXPORT_SYMBOL(memmove); | ||
85 | EXPORT_SYMBOL(memscan); | ||
86 | EXPORT_SYMBOL(__memcpy); | ||
87 | EXPORT_SYMBOL(__memset); | ||
88 | EXPORT_SYMBOL(__memsetw); | ||
89 | EXPORT_SYMBOL(__constant_c_memset); | ||
90 | EXPORT_SYMBOL(copy_page); | ||
91 | EXPORT_SYMBOL(clear_page); | ||
92 | |||
93 | EXPORT_SYMBOL(__direct_map_base); | ||
94 | EXPORT_SYMBOL(__direct_map_size); | ||
95 | |||
96 | #ifdef CONFIG_PCI | ||
97 | EXPORT_SYMBOL(pci_alloc_consistent); | ||
98 | EXPORT_SYMBOL(pci_free_consistent); | ||
99 | EXPORT_SYMBOL(pci_map_single); | ||
100 | EXPORT_SYMBOL(pci_map_page); | ||
101 | EXPORT_SYMBOL(pci_unmap_single); | ||
102 | EXPORT_SYMBOL(pci_unmap_page); | ||
103 | EXPORT_SYMBOL(pci_map_sg); | ||
104 | EXPORT_SYMBOL(pci_unmap_sg); | ||
105 | EXPORT_SYMBOL(pci_dma_supported); | ||
106 | EXPORT_SYMBOL(pci_dac_dma_supported); | ||
107 | EXPORT_SYMBOL(pci_dac_page_to_dma); | ||
108 | EXPORT_SYMBOL(pci_dac_dma_to_page); | ||
109 | EXPORT_SYMBOL(pci_dac_dma_to_offset); | ||
110 | EXPORT_SYMBOL(alpha_gendev_to_pci); | ||
111 | #endif | ||
112 | EXPORT_SYMBOL(dma_set_mask); | ||
113 | |||
114 | EXPORT_SYMBOL(dump_thread); | ||
115 | EXPORT_SYMBOL(dump_elf_thread); | ||
116 | EXPORT_SYMBOL(dump_elf_task); | ||
117 | EXPORT_SYMBOL(dump_elf_task_fp); | ||
118 | EXPORT_SYMBOL(hwrpb); | ||
119 | EXPORT_SYMBOL(start_thread); | ||
120 | EXPORT_SYMBOL(alpha_read_fp_reg); | ||
121 | EXPORT_SYMBOL(alpha_read_fp_reg_s); | ||
122 | EXPORT_SYMBOL(alpha_write_fp_reg); | ||
123 | EXPORT_SYMBOL(alpha_write_fp_reg_s); | ||
124 | |||
125 | /* In-kernel system calls. */ | ||
126 | EXPORT_SYMBOL(kernel_thread); | ||
127 | EXPORT_SYMBOL(sys_open); | ||
128 | EXPORT_SYMBOL(sys_dup); | ||
129 | EXPORT_SYMBOL(sys_exit); | ||
130 | EXPORT_SYMBOL(sys_write); | ||
131 | EXPORT_SYMBOL(sys_read); | ||
132 | EXPORT_SYMBOL(sys_lseek); | ||
133 | EXPORT_SYMBOL(execve); | ||
134 | EXPORT_SYMBOL(sys_setsid); | ||
135 | EXPORT_SYMBOL(sys_wait4); | ||
136 | |||
137 | /* Networking helper routines. */ | ||
138 | EXPORT_SYMBOL(csum_tcpudp_magic); | ||
139 | EXPORT_SYMBOL(ip_compute_csum); | ||
140 | EXPORT_SYMBOL(ip_fast_csum); | ||
141 | EXPORT_SYMBOL(csum_partial_copy_nocheck); | ||
142 | EXPORT_SYMBOL(csum_partial_copy_from_user); | ||
143 | EXPORT_SYMBOL(csum_ipv6_magic); | ||
144 | |||
145 | #ifdef CONFIG_MATHEMU_MODULE | ||
146 | extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); | ||
147 | extern long (*alpha_fp_emul) (unsigned long pc); | ||
148 | EXPORT_SYMBOL(alpha_fp_emul_imprecise); | ||
149 | EXPORT_SYMBOL(alpha_fp_emul); | ||
150 | #endif | ||
151 | |||
152 | #ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK | ||
153 | EXPORT_SYMBOL(__min_ipl); | ||
154 | #endif | ||
155 | |||
156 | /* | ||
157 | * The following are specially called from the uaccess assembly stubs. | ||
158 | */ | ||
159 | EXPORT_SYMBOL(__copy_user); | ||
160 | EXPORT_SYMBOL(__do_clear_user); | ||
161 | EXPORT_SYMBOL(__strncpy_from_user); | ||
162 | EXPORT_SYMBOL(__strnlen_user); | ||
163 | |||
164 | /* Semaphore helper functions. */ | ||
165 | EXPORT_SYMBOL(__down_failed); | ||
166 | EXPORT_SYMBOL(__down_failed_interruptible); | ||
167 | EXPORT_SYMBOL(__up_wakeup); | ||
168 | EXPORT_SYMBOL(down); | ||
169 | EXPORT_SYMBOL(down_interruptible); | ||
170 | EXPORT_SYMBOL(down_trylock); | ||
171 | EXPORT_SYMBOL(up); | ||
172 | |||
173 | /* | ||
174 | * SMP-specific symbols. | ||
175 | */ | ||
176 | |||
177 | #ifdef CONFIG_SMP | ||
178 | EXPORT_SYMBOL(synchronize_irq); | ||
179 | EXPORT_SYMBOL(flush_tlb_mm); | ||
180 | EXPORT_SYMBOL(flush_tlb_range); | ||
181 | EXPORT_SYMBOL(flush_tlb_page); | ||
182 | EXPORT_SYMBOL(smp_imb); | ||
183 | EXPORT_SYMBOL(cpu_data); | ||
184 | EXPORT_SYMBOL(smp_num_cpus); | ||
185 | EXPORT_SYMBOL(smp_call_function); | ||
186 | EXPORT_SYMBOL(smp_call_function_on_cpu); | ||
187 | EXPORT_SYMBOL(_atomic_dec_and_lock); | ||
188 | #ifdef CONFIG_DEBUG_SPINLOCK | ||
189 | EXPORT_SYMBOL(_raw_spin_unlock); | ||
190 | EXPORT_SYMBOL(debug_spin_lock); | ||
191 | EXPORT_SYMBOL(debug_spin_trylock); | ||
192 | #endif | ||
193 | #ifdef CONFIG_DEBUG_RWLOCK | ||
194 | EXPORT_SYMBOL(_raw_write_lock); | ||
195 | EXPORT_SYMBOL(_raw_read_lock); | ||
196 | #endif | ||
197 | EXPORT_SYMBOL(cpu_present_mask); | ||
198 | #endif /* CONFIG_SMP */ | ||
199 | |||
200 | /* | ||
201 | * NUMA specific symbols | ||
202 | */ | ||
203 | #ifdef CONFIG_DISCONTIGMEM | ||
204 | EXPORT_SYMBOL(node_data); | ||
205 | #endif /* CONFIG_DISCONTIGMEM */ | ||
206 | |||
207 | EXPORT_SYMBOL(rtc_lock); | ||
208 | |||
209 | /* | ||
210 | * The following are special because they're not called | ||
211 | * explicitly (the C compiler or assembler generates them in | ||
212 | * response to division operations). Fortunately, their | ||
213 | * interface isn't gonna change any time soon now, so it's OK | ||
214 | * to leave it out of version control. | ||
215 | */ | ||
216 | # undef memcpy | ||
217 | # undef memset | ||
218 | EXPORT_SYMBOL(__divl); | ||
219 | EXPORT_SYMBOL(__divlu); | ||
220 | EXPORT_SYMBOL(__divq); | ||
221 | EXPORT_SYMBOL(__divqu); | ||
222 | EXPORT_SYMBOL(__reml); | ||
223 | EXPORT_SYMBOL(__remlu); | ||
224 | EXPORT_SYMBOL(__remq); | ||
225 | EXPORT_SYMBOL(__remqu); | ||
226 | EXPORT_SYMBOL(memcpy); | ||
227 | EXPORT_SYMBOL(memset); | ||
228 | EXPORT_SYMBOL(memchr); | ||
229 | |||
230 | EXPORT_SYMBOL(get_wchan); | ||
231 | |||
232 | #ifdef CONFIG_ALPHA_IRONGATE | ||
233 | EXPORT_SYMBOL(irongate_ioremap); | ||
234 | EXPORT_SYMBOL(irongate_iounmap); | ||
235 | #endif | ||
diff --git a/arch/alpha/kernel/asm-offsets.c b/arch/alpha/kernel/asm-offsets.c new file mode 100644 index 000000000000..8f2e5c718b50 --- /dev/null +++ b/arch/alpha/kernel/asm-offsets.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Generate definitions needed by assembly language modules. | ||
3 | * This code generates raw asm output which is post-processed to extract | ||
4 | * and format the required data. | ||
5 | */ | ||
6 | |||
7 | #include <linux/types.h> | ||
8 | #include <linux/stddef.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/ptrace.h> | ||
11 | #include <asm/io.h> | ||
12 | |||
13 | #define DEFINE(sym, val) \ | ||
14 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) | ||
15 | |||
16 | #define BLANK() asm volatile("\n->" : : ) | ||
17 | |||
18 | void foo(void) | ||
19 | { | ||
20 | DEFINE(TI_TASK, offsetof(struct thread_info, task)); | ||
21 | DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); | ||
22 | DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); | ||
23 | BLANK(); | ||
24 | |||
25 | DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); | ||
26 | DEFINE(TASK_UID, offsetof(struct task_struct, uid)); | ||
27 | DEFINE(TASK_EUID, offsetof(struct task_struct, euid)); | ||
28 | DEFINE(TASK_GID, offsetof(struct task_struct, gid)); | ||
29 | DEFINE(TASK_EGID, offsetof(struct task_struct, egid)); | ||
30 | DEFINE(TASK_REAL_PARENT, offsetof(struct task_struct, real_parent)); | ||
31 | DEFINE(TASK_TGID, offsetof(struct task_struct, tgid)); | ||
32 | BLANK(); | ||
33 | |||
34 | DEFINE(SIZEOF_PT_REGS, sizeof(struct pt_regs)); | ||
35 | DEFINE(PT_PTRACED, PT_PTRACED); | ||
36 | DEFINE(CLONE_VM, CLONE_VM); | ||
37 | DEFINE(CLONE_UNTRACED, CLONE_UNTRACED); | ||
38 | DEFINE(SIGCHLD, SIGCHLD); | ||
39 | BLANK(); | ||
40 | |||
41 | DEFINE(HAE_CACHE, offsetof(struct alpha_machine_vector, hae_cache)); | ||
42 | DEFINE(HAE_REG, offsetof(struct alpha_machine_vector, hae_register)); | ||
43 | } | ||
diff --git a/arch/alpha/kernel/console.c b/arch/alpha/kernel/console.c new file mode 100644 index 000000000000..cb3e739fbad8 --- /dev/null +++ b/arch/alpha/kernel/console.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/console.c | ||
3 | * | ||
4 | * Architecture-specific specific support for VGA device on | ||
5 | * non-0 I/O hose | ||
6 | */ | ||
7 | |||
8 | #include <linux/config.h> | ||
9 | #include <linux/pci.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/tty.h> | ||
12 | #include <linux/console.h> | ||
13 | #include <asm/vga.h> | ||
14 | #include <asm/machvec.h> | ||
15 | |||
16 | #ifdef CONFIG_VGA_HOSE | ||
17 | |||
18 | /* | ||
19 | * Externally-visible vga hose bases | ||
20 | */ | ||
21 | unsigned long __vga_hose_io_base = 0; /* base for default hose */ | ||
22 | unsigned long __vga_hose_mem_base = 0; /* base for default hose */ | ||
23 | |||
24 | static struct pci_controller * __init | ||
25 | default_vga_hose_select(struct pci_controller *h1, struct pci_controller *h2) | ||
26 | { | ||
27 | if (h2->index < h1->index) | ||
28 | return h2; | ||
29 | |||
30 | return h1; | ||
31 | } | ||
32 | |||
33 | void __init | ||
34 | set_vga_hose(struct pci_controller *hose) | ||
35 | { | ||
36 | if (hose) { | ||
37 | __vga_hose_io_base = hose->io_space->start; | ||
38 | __vga_hose_mem_base = hose->mem_space->start; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | void __init | ||
43 | locate_and_init_vga(void *(*sel_func)(void *, void *)) | ||
44 | { | ||
45 | struct pci_controller *hose = NULL; | ||
46 | struct pci_dev *dev = NULL; | ||
47 | |||
48 | if (!sel_func) sel_func = (void *)default_vga_hose_select; | ||
49 | |||
50 | for(dev=NULL; (dev=pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, dev));) { | ||
51 | if (!hose) hose = dev->sysdata; | ||
52 | else hose = sel_func(hose, dev->sysdata); | ||
53 | } | ||
54 | |||
55 | /* Did we already inititialize the correct one? */ | ||
56 | if (conswitchp == &vga_con && | ||
57 | __vga_hose_io_base == hose->io_space->start && | ||
58 | __vga_hose_mem_base == hose->mem_space->start) | ||
59 | return; | ||
60 | |||
61 | /* Set the VGA hose and init the new console */ | ||
62 | set_vga_hose(hose); | ||
63 | take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); | ||
64 | } | ||
65 | |||
66 | #endif | ||
diff --git a/arch/alpha/kernel/core_apecs.c b/arch/alpha/kernel/core_apecs.c new file mode 100644 index 000000000000..a27ba12ba35e --- /dev/null +++ b/arch/alpha/kernel/core_apecs.c | |||
@@ -0,0 +1,418 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_apecs.c | ||
3 | * | ||
4 | * Rewritten for Apecs from the lca.c from: | ||
5 | * | ||
6 | * Written by David Mosberger (davidm@cs.arizona.edu) with some code | ||
7 | * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit | ||
8 | * bios code. | ||
9 | * | ||
10 | * Code common to all APECS core logic chips. | ||
11 | */ | ||
12 | |||
13 | #define __EXTERN_INLINE inline | ||
14 | #include <asm/io.h> | ||
15 | #include <asm/core_apecs.h> | ||
16 | #undef __EXTERN_INLINE | ||
17 | |||
18 | #include <linux/types.h> | ||
19 | #include <linux/pci.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | #include <asm/ptrace.h> | ||
23 | #include <asm/smp.h> | ||
24 | |||
25 | #include "proto.h" | ||
26 | #include "pci_impl.h" | ||
27 | |||
28 | /* | ||
29 | * NOTE: Herein lie back-to-back mb instructions. They are magic. | ||
30 | * One plausible explanation is that the i/o controller does not properly | ||
31 | * handle the system transaction. Another involves timing. Ho hum. | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * BIOS32-style PCI interface: | ||
36 | */ | ||
37 | |||
38 | #define DEBUG_CONFIG 0 | ||
39 | |||
40 | #if DEBUG_CONFIG | ||
41 | # define DBGC(args) printk args | ||
42 | #else | ||
43 | # define DBGC(args) | ||
44 | #endif | ||
45 | |||
46 | #define vuip volatile unsigned int * | ||
47 | |||
48 | /* | ||
49 | * Given a bus, device, and function number, compute resulting | ||
50 | * configuration space address and setup the APECS_HAXR2 register | ||
51 | * accordingly. It is therefore not safe to have concurrent | ||
52 | * invocations to configuration space access routines, but there | ||
53 | * really shouldn't be any need for this. | ||
54 | * | ||
55 | * Type 0: | ||
56 | * | ||
57 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
58 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
59 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
60 | * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| | ||
61 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
62 | * | ||
63 | * 31:11 Device select bit. | ||
64 | * 10:8 Function number | ||
65 | * 7:2 Register number | ||
66 | * | ||
67 | * Type 1: | ||
68 | * | ||
69 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
70 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
71 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
72 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
73 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
74 | * | ||
75 | * 31:24 reserved | ||
76 | * 23:16 bus number (8 bits = 128 possible buses) | ||
77 | * 15:11 Device number (5 bits) | ||
78 | * 10:8 function number | ||
79 | * 7:2 register number | ||
80 | * | ||
81 | * Notes: | ||
82 | * The function number selects which function of a multi-function device | ||
83 | * (e.g., SCSI and Ethernet). | ||
84 | * | ||
85 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
86 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
87 | * bits. | ||
88 | */ | ||
89 | |||
90 | static int | ||
91 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
92 | unsigned long *pci_addr, unsigned char *type1) | ||
93 | { | ||
94 | unsigned long addr; | ||
95 | u8 bus = pbus->number; | ||
96 | |||
97 | DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," | ||
98 | " pci_addr=0x%p, type1=0x%p)\n", | ||
99 | bus, device_fn, where, pci_addr, type1)); | ||
100 | |||
101 | if (bus == 0) { | ||
102 | int device = device_fn >> 3; | ||
103 | |||
104 | /* type 0 configuration cycle: */ | ||
105 | |||
106 | if (device > 20) { | ||
107 | DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n", | ||
108 | device)); | ||
109 | return -1; | ||
110 | } | ||
111 | |||
112 | *type1 = 0; | ||
113 | addr = (device_fn << 8) | (where); | ||
114 | } else { | ||
115 | /* type 1 configuration cycle: */ | ||
116 | *type1 = 1; | ||
117 | addr = (bus << 16) | (device_fn << 8) | (where); | ||
118 | } | ||
119 | *pci_addr = addr; | ||
120 | DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static unsigned int | ||
125 | conf_read(unsigned long addr, unsigned char type1) | ||
126 | { | ||
127 | unsigned long flags; | ||
128 | unsigned int stat0, value; | ||
129 | unsigned int haxr2 = 0; | ||
130 | |||
131 | local_irq_save(flags); /* avoid getting hit by machine check */ | ||
132 | |||
133 | DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); | ||
134 | |||
135 | /* Reset status register to avoid losing errors. */ | ||
136 | stat0 = *(vuip)APECS_IOC_DCSR; | ||
137 | *(vuip)APECS_IOC_DCSR = stat0; | ||
138 | mb(); | ||
139 | DBGC(("conf_read: APECS DCSR was 0x%x\n", stat0)); | ||
140 | |||
141 | /* If Type1 access, must set HAE #2. */ | ||
142 | if (type1) { | ||
143 | haxr2 = *(vuip)APECS_IOC_HAXR2; | ||
144 | mb(); | ||
145 | *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; | ||
146 | DBGC(("conf_read: TYPE1 access\n")); | ||
147 | } | ||
148 | |||
149 | draina(); | ||
150 | mcheck_expected(0) = 1; | ||
151 | mcheck_taken(0) = 0; | ||
152 | mb(); | ||
153 | |||
154 | /* Access configuration space. */ | ||
155 | |||
156 | /* Some SRMs step on these registers during a machine check. */ | ||
157 | asm volatile("ldl %0,%1; mb; mb" : "=r"(value) : "m"(*(vuip)addr) | ||
158 | : "$9", "$10", "$11", "$12", "$13", "$14", "memory"); | ||
159 | |||
160 | if (mcheck_taken(0)) { | ||
161 | mcheck_taken(0) = 0; | ||
162 | value = 0xffffffffU; | ||
163 | mb(); | ||
164 | } | ||
165 | mcheck_expected(0) = 0; | ||
166 | mb(); | ||
167 | |||
168 | #if 1 | ||
169 | /* | ||
170 | * david.rusling@reo.mts.dec.com. This code is needed for the | ||
171 | * EB64+ as it does not generate a machine check (why I don't | ||
172 | * know). When we build kernels for one particular platform | ||
173 | * then we can make this conditional on the type. | ||
174 | */ | ||
175 | draina(); | ||
176 | |||
177 | /* Now look for any errors. */ | ||
178 | stat0 = *(vuip)APECS_IOC_DCSR; | ||
179 | DBGC(("conf_read: APECS DCSR after read 0x%x\n", stat0)); | ||
180 | |||
181 | /* Is any error bit set? */ | ||
182 | if (stat0 & 0xffe0U) { | ||
183 | /* If not NDEV, print status. */ | ||
184 | if (!(stat0 & 0x0800)) { | ||
185 | printk("apecs.c:conf_read: got stat0=%x\n", stat0); | ||
186 | } | ||
187 | |||
188 | /* Reset error status. */ | ||
189 | *(vuip)APECS_IOC_DCSR = stat0; | ||
190 | mb(); | ||
191 | wrmces(0x7); /* reset machine check */ | ||
192 | value = 0xffffffff; | ||
193 | } | ||
194 | #endif | ||
195 | |||
196 | /* If Type1 access, must reset HAE #2 so normal IO space ops work. */ | ||
197 | if (type1) { | ||
198 | *(vuip)APECS_IOC_HAXR2 = haxr2 & ~1; | ||
199 | mb(); | ||
200 | } | ||
201 | local_irq_restore(flags); | ||
202 | |||
203 | return value; | ||
204 | } | ||
205 | |||
206 | static void | ||
207 | conf_write(unsigned long addr, unsigned int value, unsigned char type1) | ||
208 | { | ||
209 | unsigned long flags; | ||
210 | unsigned int stat0; | ||
211 | unsigned int haxr2 = 0; | ||
212 | |||
213 | local_irq_save(flags); /* avoid getting hit by machine check */ | ||
214 | |||
215 | /* Reset status register to avoid losing errors. */ | ||
216 | stat0 = *(vuip)APECS_IOC_DCSR; | ||
217 | *(vuip)APECS_IOC_DCSR = stat0; | ||
218 | mb(); | ||
219 | |||
220 | /* If Type1 access, must set HAE #2. */ | ||
221 | if (type1) { | ||
222 | haxr2 = *(vuip)APECS_IOC_HAXR2; | ||
223 | mb(); | ||
224 | *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; | ||
225 | } | ||
226 | |||
227 | draina(); | ||
228 | mcheck_expected(0) = 1; | ||
229 | mb(); | ||
230 | |||
231 | /* Access configuration space. */ | ||
232 | *(vuip)addr = value; | ||
233 | mb(); | ||
234 | mb(); /* magic */ | ||
235 | mcheck_expected(0) = 0; | ||
236 | mb(); | ||
237 | |||
238 | #if 1 | ||
239 | /* | ||
240 | * david.rusling@reo.mts.dec.com. This code is needed for the | ||
241 | * EB64+ as it does not generate a machine check (why I don't | ||
242 | * know). When we build kernels for one particular platform | ||
243 | * then we can make this conditional on the type. | ||
244 | */ | ||
245 | draina(); | ||
246 | |||
247 | /* Now look for any errors. */ | ||
248 | stat0 = *(vuip)APECS_IOC_DCSR; | ||
249 | |||
250 | /* Is any error bit set? */ | ||
251 | if (stat0 & 0xffe0U) { | ||
252 | /* If not NDEV, print status. */ | ||
253 | if (!(stat0 & 0x0800)) { | ||
254 | printk("apecs.c:conf_write: got stat0=%x\n", stat0); | ||
255 | } | ||
256 | |||
257 | /* Reset error status. */ | ||
258 | *(vuip)APECS_IOC_DCSR = stat0; | ||
259 | mb(); | ||
260 | wrmces(0x7); /* reset machine check */ | ||
261 | } | ||
262 | #endif | ||
263 | |||
264 | /* If Type1 access, must reset HAE #2 so normal IO space ops work. */ | ||
265 | if (type1) { | ||
266 | *(vuip)APECS_IOC_HAXR2 = haxr2 & ~1; | ||
267 | mb(); | ||
268 | } | ||
269 | local_irq_restore(flags); | ||
270 | } | ||
271 | |||
272 | static int | ||
273 | apecs_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
274 | int size, u32 *value) | ||
275 | { | ||
276 | unsigned long addr, pci_addr; | ||
277 | unsigned char type1; | ||
278 | long mask; | ||
279 | int shift; | ||
280 | |||
281 | if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) | ||
282 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
283 | |||
284 | mask = (size - 1) * 8; | ||
285 | shift = (where & 3) * 8; | ||
286 | addr = (pci_addr << 5) + mask + APECS_CONF; | ||
287 | *value = conf_read(addr, type1) >> (shift); | ||
288 | return PCIBIOS_SUCCESSFUL; | ||
289 | } | ||
290 | |||
291 | static int | ||
292 | apecs_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
293 | int size, u32 value) | ||
294 | { | ||
295 | unsigned long addr, pci_addr; | ||
296 | unsigned char type1; | ||
297 | long mask; | ||
298 | |||
299 | if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) | ||
300 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
301 | |||
302 | mask = (size - 1) * 8; | ||
303 | addr = (pci_addr << 5) + mask + APECS_CONF; | ||
304 | conf_write(addr, value << ((where & 3) * 8), type1); | ||
305 | return PCIBIOS_SUCCESSFUL; | ||
306 | } | ||
307 | |||
308 | struct pci_ops apecs_pci_ops = | ||
309 | { | ||
310 | .read = apecs_read_config, | ||
311 | .write = apecs_write_config, | ||
312 | }; | ||
313 | |||
314 | void | ||
315 | apecs_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
316 | { | ||
317 | wmb(); | ||
318 | *(vip)APECS_IOC_TBIA = 0; | ||
319 | mb(); | ||
320 | } | ||
321 | |||
322 | void __init | ||
323 | apecs_init_arch(void) | ||
324 | { | ||
325 | struct pci_controller *hose; | ||
326 | |||
327 | /* | ||
328 | * Create our single hose. | ||
329 | */ | ||
330 | |||
331 | pci_isa_hose = hose = alloc_pci_controller(); | ||
332 | hose->io_space = &ioport_resource; | ||
333 | hose->mem_space = &iomem_resource; | ||
334 | hose->index = 0; | ||
335 | |||
336 | hose->sparse_mem_base = APECS_SPARSE_MEM - IDENT_ADDR; | ||
337 | hose->dense_mem_base = APECS_DENSE_MEM - IDENT_ADDR; | ||
338 | hose->sparse_io_base = APECS_IO - IDENT_ADDR; | ||
339 | hose->dense_io_base = 0; | ||
340 | |||
341 | /* | ||
342 | * Set up the PCI to main memory translation windows. | ||
343 | * | ||
344 | * Window 1 is direct access 1GB at 1GB | ||
345 | * Window 2 is scatter-gather 8MB at 8MB (for isa) | ||
346 | */ | ||
347 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); | ||
348 | hose->sg_pci = NULL; | ||
349 | __direct_map_base = 0x40000000; | ||
350 | __direct_map_size = 0x40000000; | ||
351 | |||
352 | *(vuip)APECS_IOC_PB1R = __direct_map_base | 0x00080000; | ||
353 | *(vuip)APECS_IOC_PM1R = (__direct_map_size - 1) & 0xfff00000U; | ||
354 | *(vuip)APECS_IOC_TB1R = 0; | ||
355 | |||
356 | *(vuip)APECS_IOC_PB2R = hose->sg_isa->dma_base | 0x000c0000; | ||
357 | *(vuip)APECS_IOC_PM2R = (hose->sg_isa->size - 1) & 0xfff00000; | ||
358 | *(vuip)APECS_IOC_TB2R = virt_to_phys(hose->sg_isa->ptes) >> 1; | ||
359 | |||
360 | apecs_pci_tbi(hose, 0, -1); | ||
361 | |||
362 | /* | ||
363 | * Finally, clear the HAXR2 register, which gets used | ||
364 | * for PCI Config Space accesses. That is the way | ||
365 | * we want to use it, and we do not want to depend on | ||
366 | * what ARC or SRM might have left behind... | ||
367 | */ | ||
368 | *(vuip)APECS_IOC_HAXR2 = 0; | ||
369 | mb(); | ||
370 | } | ||
371 | |||
372 | void | ||
373 | apecs_pci_clr_err(void) | ||
374 | { | ||
375 | unsigned int jd; | ||
376 | |||
377 | jd = *(vuip)APECS_IOC_DCSR; | ||
378 | if (jd & 0xffe0L) { | ||
379 | *(vuip)APECS_IOC_SEAR; | ||
380 | *(vuip)APECS_IOC_DCSR = jd | 0xffe1L; | ||
381 | mb(); | ||
382 | *(vuip)APECS_IOC_DCSR; | ||
383 | } | ||
384 | *(vuip)APECS_IOC_TBIA = (unsigned int)APECS_IOC_TBIA; | ||
385 | mb(); | ||
386 | *(vuip)APECS_IOC_TBIA; | ||
387 | } | ||
388 | |||
389 | void | ||
390 | apecs_machine_check(unsigned long vector, unsigned long la_ptr, | ||
391 | struct pt_regs * regs) | ||
392 | { | ||
393 | struct el_common *mchk_header; | ||
394 | struct el_apecs_procdata *mchk_procdata; | ||
395 | struct el_apecs_sysdata_mcheck *mchk_sysdata; | ||
396 | |||
397 | mchk_header = (struct el_common *)la_ptr; | ||
398 | |||
399 | mchk_procdata = (struct el_apecs_procdata *) | ||
400 | (la_ptr + mchk_header->proc_offset | ||
401 | - sizeof(mchk_procdata->paltemp)); | ||
402 | |||
403 | mchk_sysdata = (struct el_apecs_sysdata_mcheck *) | ||
404 | (la_ptr + mchk_header->sys_offset); | ||
405 | |||
406 | |||
407 | /* Clear the error before any reporting. */ | ||
408 | mb(); | ||
409 | mb(); /* magic */ | ||
410 | draina(); | ||
411 | apecs_pci_clr_err(); | ||
412 | wrmces(0x7); /* reset machine check pending flag */ | ||
413 | mb(); | ||
414 | |||
415 | process_mcheck_info(vector, la_ptr, regs, "APECS", | ||
416 | (mcheck_expected(0) | ||
417 | && (mchk_sysdata->epic_dcsr & 0x0c00UL))); | ||
418 | } | ||
diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c new file mode 100644 index 000000000000..fd563064363c --- /dev/null +++ b/arch/alpha/kernel/core_cia.c | |||
@@ -0,0 +1,1212 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_cia.c | ||
3 | * | ||
4 | * Written by David A Rusling (david.rusling@reo.mts.dec.com). | ||
5 | * December 1995. | ||
6 | * | ||
7 | * Copyright (C) 1995 David A Rusling | ||
8 | * Copyright (C) 1997, 1998 Jay Estabrook | ||
9 | * Copyright (C) 1998, 1999, 2000 Richard Henderson | ||
10 | * | ||
11 | * Code common to all CIA core logic chips. | ||
12 | */ | ||
13 | |||
14 | #define __EXTERN_INLINE inline | ||
15 | #include <asm/io.h> | ||
16 | #include <asm/core_cia.h> | ||
17 | #undef __EXTERN_INLINE | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/bootmem.h> | ||
24 | |||
25 | #include <asm/ptrace.h> | ||
26 | |||
27 | #include "proto.h" | ||
28 | #include "pci_impl.h" | ||
29 | |||
30 | |||
31 | /* | ||
32 | * NOTE: Herein lie back-to-back mb instructions. They are magic. | ||
33 | * One plausible explanation is that the i/o controller does not properly | ||
34 | * handle the system transaction. Another involves timing. Ho hum. | ||
35 | */ | ||
36 | |||
37 | #define DEBUG_CONFIG 0 | ||
38 | #if DEBUG_CONFIG | ||
39 | # define DBGC(args) printk args | ||
40 | #else | ||
41 | # define DBGC(args) | ||
42 | #endif | ||
43 | |||
44 | #define vip volatile int * | ||
45 | |||
46 | /* | ||
47 | * Given a bus, device, and function number, compute resulting | ||
48 | * configuration space address. It is therefore not safe to have | ||
49 | * concurrent invocations to configuration space access routines, but | ||
50 | * there really shouldn't be any need for this. | ||
51 | * | ||
52 | * Type 0: | ||
53 | * | ||
54 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
55 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
56 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
57 | * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| | ||
58 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
59 | * | ||
60 | * 31:11 Device select bit. | ||
61 | * 10:8 Function number | ||
62 | * 7:2 Register number | ||
63 | * | ||
64 | * Type 1: | ||
65 | * | ||
66 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
67 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
68 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
69 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
70 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
71 | * | ||
72 | * 31:24 reserved | ||
73 | * 23:16 bus number (8 bits = 128 possible buses) | ||
74 | * 15:11 Device number (5 bits) | ||
75 | * 10:8 function number | ||
76 | * 7:2 register number | ||
77 | * | ||
78 | * Notes: | ||
79 | * The function number selects which function of a multi-function device | ||
80 | * (e.g., SCSI and Ethernet). | ||
81 | * | ||
82 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
83 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
84 | * bits. | ||
85 | */ | ||
86 | |||
87 | static int | ||
88 | mk_conf_addr(struct pci_bus *bus_dev, unsigned int device_fn, int where, | ||
89 | unsigned long *pci_addr, unsigned char *type1) | ||
90 | { | ||
91 | u8 bus = bus_dev->number; | ||
92 | |||
93 | *type1 = (bus != 0); | ||
94 | *pci_addr = (bus << 16) | (device_fn << 8) | where; | ||
95 | |||
96 | DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," | ||
97 | " returning address 0x%p\n" | ||
98 | bus, device_fn, where, *pci_addr)); | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static unsigned int | ||
104 | conf_read(unsigned long addr, unsigned char type1) | ||
105 | { | ||
106 | unsigned long flags; | ||
107 | int stat0, value; | ||
108 | int cia_cfg = 0; | ||
109 | |||
110 | DBGC(("conf_read(addr=0x%lx, type1=%d) ", addr, type1)); | ||
111 | local_irq_save(flags); | ||
112 | |||
113 | /* Reset status register to avoid losing errors. */ | ||
114 | stat0 = *(vip)CIA_IOC_CIA_ERR; | ||
115 | *(vip)CIA_IOC_CIA_ERR = stat0; | ||
116 | mb(); | ||
117 | *(vip)CIA_IOC_CIA_ERR; /* re-read to force write */ | ||
118 | |||
119 | /* If Type1 access, must set CIA CFG. */ | ||
120 | if (type1) { | ||
121 | cia_cfg = *(vip)CIA_IOC_CFG; | ||
122 | *(vip)CIA_IOC_CFG = (cia_cfg & ~3) | 1; | ||
123 | mb(); | ||
124 | *(vip)CIA_IOC_CFG; | ||
125 | } | ||
126 | |||
127 | mb(); | ||
128 | draina(); | ||
129 | mcheck_expected(0) = 1; | ||
130 | mcheck_taken(0) = 0; | ||
131 | mb(); | ||
132 | |||
133 | /* Access configuration space. */ | ||
134 | value = *(vip)addr; | ||
135 | mb(); | ||
136 | mb(); /* magic */ | ||
137 | if (mcheck_taken(0)) { | ||
138 | mcheck_taken(0) = 0; | ||
139 | value = 0xffffffff; | ||
140 | mb(); | ||
141 | } | ||
142 | mcheck_expected(0) = 0; | ||
143 | mb(); | ||
144 | |||
145 | /* If Type1 access, must reset IOC CFG so normal IO space ops work. */ | ||
146 | if (type1) { | ||
147 | *(vip)CIA_IOC_CFG = cia_cfg; | ||
148 | mb(); | ||
149 | *(vip)CIA_IOC_CFG; | ||
150 | } | ||
151 | |||
152 | local_irq_restore(flags); | ||
153 | DBGC(("done\n")); | ||
154 | |||
155 | return value; | ||
156 | } | ||
157 | |||
158 | static void | ||
159 | conf_write(unsigned long addr, unsigned int value, unsigned char type1) | ||
160 | { | ||
161 | unsigned long flags; | ||
162 | int stat0, cia_cfg = 0; | ||
163 | |||
164 | DBGC(("conf_write(addr=0x%lx, type1=%d) ", addr, type1)); | ||
165 | local_irq_save(flags); | ||
166 | |||
167 | /* Reset status register to avoid losing errors. */ | ||
168 | stat0 = *(vip)CIA_IOC_CIA_ERR; | ||
169 | *(vip)CIA_IOC_CIA_ERR = stat0; | ||
170 | mb(); | ||
171 | *(vip)CIA_IOC_CIA_ERR; /* re-read to force write */ | ||
172 | |||
173 | /* If Type1 access, must set CIA CFG. */ | ||
174 | if (type1) { | ||
175 | cia_cfg = *(vip)CIA_IOC_CFG; | ||
176 | *(vip)CIA_IOC_CFG = (cia_cfg & ~3) | 1; | ||
177 | mb(); | ||
178 | *(vip)CIA_IOC_CFG; | ||
179 | } | ||
180 | |||
181 | mb(); | ||
182 | draina(); | ||
183 | mcheck_expected(0) = 1; | ||
184 | mcheck_taken(0) = 0; | ||
185 | mb(); | ||
186 | |||
187 | /* Access configuration space. */ | ||
188 | *(vip)addr = value; | ||
189 | mb(); | ||
190 | *(vip)addr; /* read back to force the write */ | ||
191 | |||
192 | mcheck_expected(0) = 0; | ||
193 | mb(); | ||
194 | |||
195 | /* If Type1 access, must reset IOC CFG so normal IO space ops work. */ | ||
196 | if (type1) { | ||
197 | *(vip)CIA_IOC_CFG = cia_cfg; | ||
198 | mb(); | ||
199 | *(vip)CIA_IOC_CFG; | ||
200 | } | ||
201 | |||
202 | local_irq_restore(flags); | ||
203 | DBGC(("done\n")); | ||
204 | } | ||
205 | |||
206 | static int | ||
207 | cia_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, | ||
208 | u32 *value) | ||
209 | { | ||
210 | unsigned long addr, pci_addr; | ||
211 | long mask; | ||
212 | unsigned char type1; | ||
213 | int shift; | ||
214 | |||
215 | if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) | ||
216 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
217 | |||
218 | mask = (size - 1) * 8; | ||
219 | shift = (where & 3) * 8; | ||
220 | addr = (pci_addr << 5) + mask + CIA_CONF; | ||
221 | *value = conf_read(addr, type1) >> (shift); | ||
222 | return PCIBIOS_SUCCESSFUL; | ||
223 | } | ||
224 | |||
225 | static int | ||
226 | cia_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, | ||
227 | u32 value) | ||
228 | { | ||
229 | unsigned long addr, pci_addr; | ||
230 | long mask; | ||
231 | unsigned char type1; | ||
232 | |||
233 | if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) | ||
234 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
235 | |||
236 | mask = (size - 1) * 8; | ||
237 | addr = (pci_addr << 5) + mask + CIA_CONF; | ||
238 | conf_write(addr, value << ((where & 3) * 8), type1); | ||
239 | return PCIBIOS_SUCCESSFUL; | ||
240 | } | ||
241 | |||
242 | struct pci_ops cia_pci_ops = | ||
243 | { | ||
244 | .read = cia_read_config, | ||
245 | .write = cia_write_config, | ||
246 | }; | ||
247 | |||
248 | /* | ||
249 | * CIA Pass 1 and PYXIS Pass 1 and 2 have a broken scatter-gather tlb. | ||
250 | * It cannot be invalidated. Rather than hard code the pass numbers, | ||
251 | * actually try the tbia to see if it works. | ||
252 | */ | ||
253 | |||
254 | void | ||
255 | cia_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
256 | { | ||
257 | wmb(); | ||
258 | *(vip)CIA_IOC_PCI_TBIA = 3; /* Flush all locked and unlocked. */ | ||
259 | mb(); | ||
260 | *(vip)CIA_IOC_PCI_TBIA; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * On PYXIS, even if the tbia works, we cannot use it. It effectively locks | ||
265 | * the chip (as well as direct write to the tag registers) if there is a | ||
266 | * SG DMA operation in progress. This is true at least for PYXIS rev. 1, | ||
267 | * so always use the method below. | ||
268 | */ | ||
269 | /* | ||
270 | * This is the method NT and NetBSD use. | ||
271 | * | ||
272 | * Allocate mappings, and put the chip into DMA loopback mode to read a | ||
273 | * garbage page. This works by causing TLB misses, causing old entries to | ||
274 | * be purged to make room for the new entries coming in for the garbage page. | ||
275 | */ | ||
276 | |||
277 | #define CIA_BROKEN_TBIA_BASE 0x30000000 | ||
278 | #define CIA_BROKEN_TBIA_SIZE 1024 | ||
279 | |||
280 | /* Always called with interrupts disabled */ | ||
281 | void | ||
282 | cia_pci_tbi_try2(struct pci_controller *hose, | ||
283 | dma_addr_t start, dma_addr_t end) | ||
284 | { | ||
285 | void __iomem *bus_addr; | ||
286 | int ctrl; | ||
287 | |||
288 | /* Put the chip into PCI loopback mode. */ | ||
289 | mb(); | ||
290 | ctrl = *(vip)CIA_IOC_CIA_CTRL; | ||
291 | *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN; | ||
292 | mb(); | ||
293 | *(vip)CIA_IOC_CIA_CTRL; | ||
294 | mb(); | ||
295 | |||
296 | /* Read from PCI dense memory space at TBI_ADDR, skipping 32k on | ||
297 | each read. This forces SG TLB misses. NetBSD claims that the | ||
298 | TLB entries are not quite LRU, meaning that we need to read more | ||
299 | times than there are actual tags. The 2117x docs claim strict | ||
300 | round-robin. Oh well, we've come this far... */ | ||
301 | /* Even better - as seen on the PYXIS rev 1 the TLB tags 0-3 can | ||
302 | be filled by the TLB misses *only once* after being invalidated | ||
303 | (by tbia or direct write). Next misses won't update them even | ||
304 | though the lock bits are cleared. Tags 4-7 are "quite LRU" though, | ||
305 | so use them and read at window 3 base exactly 4 times. Reading | ||
306 | more sometimes makes the chip crazy. -ink */ | ||
307 | |||
308 | bus_addr = cia_ioremap(CIA_BROKEN_TBIA_BASE, 32768 * 4); | ||
309 | |||
310 | cia_readl(bus_addr + 0x00000); | ||
311 | cia_readl(bus_addr + 0x08000); | ||
312 | cia_readl(bus_addr + 0x10000); | ||
313 | cia_readl(bus_addr + 0x18000); | ||
314 | |||
315 | cia_iounmap(bus_addr); | ||
316 | |||
317 | /* Restore normal PCI operation. */ | ||
318 | mb(); | ||
319 | *(vip)CIA_IOC_CIA_CTRL = ctrl; | ||
320 | mb(); | ||
321 | *(vip)CIA_IOC_CIA_CTRL; | ||
322 | mb(); | ||
323 | } | ||
324 | |||
325 | static inline void | ||
326 | cia_prepare_tbia_workaround(int window) | ||
327 | { | ||
328 | unsigned long *ppte, pte; | ||
329 | long i; | ||
330 | |||
331 | /* Use minimal 1K map. */ | ||
332 | ppte = __alloc_bootmem(CIA_BROKEN_TBIA_SIZE, 32768, 0); | ||
333 | pte = (virt_to_phys(ppte) >> (PAGE_SHIFT - 1)) | 1; | ||
334 | |||
335 | for (i = 0; i < CIA_BROKEN_TBIA_SIZE / sizeof(unsigned long); ++i) | ||
336 | ppte[i] = pte; | ||
337 | |||
338 | *(vip)CIA_IOC_PCI_Wn_BASE(window) = CIA_BROKEN_TBIA_BASE | 3; | ||
339 | *(vip)CIA_IOC_PCI_Wn_MASK(window) | ||
340 | = (CIA_BROKEN_TBIA_SIZE*1024 - 1) & 0xfff00000; | ||
341 | *(vip)CIA_IOC_PCI_Tn_BASE(window) = virt_to_phys(ppte) >> 2; | ||
342 | } | ||
343 | |||
344 | static void __init | ||
345 | verify_tb_operation(void) | ||
346 | { | ||
347 | static int page[PAGE_SIZE/4] | ||
348 | __attribute__((aligned(PAGE_SIZE))) | ||
349 | __initdata = { 0 }; | ||
350 | |||
351 | struct pci_iommu_arena *arena = pci_isa_hose->sg_isa; | ||
352 | int ctrl, addr0, tag0, pte0, data0; | ||
353 | int temp, use_tbia_try2 = 0; | ||
354 | void __iomem *bus_addr; | ||
355 | |||
356 | /* pyxis -- tbia is broken */ | ||
357 | if (pci_isa_hose->dense_io_base) | ||
358 | use_tbia_try2 = 1; | ||
359 | |||
360 | /* Put the chip into PCI loopback mode. */ | ||
361 | mb(); | ||
362 | ctrl = *(vip)CIA_IOC_CIA_CTRL; | ||
363 | *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN; | ||
364 | mb(); | ||
365 | *(vip)CIA_IOC_CIA_CTRL; | ||
366 | mb(); | ||
367 | |||
368 | /* Write a valid entry directly into the TLB registers. */ | ||
369 | |||
370 | addr0 = arena->dma_base; | ||
371 | tag0 = addr0 | 1; | ||
372 | pte0 = (virt_to_phys(page) >> (PAGE_SHIFT - 1)) | 1; | ||
373 | |||
374 | *(vip)CIA_IOC_TB_TAGn(0) = tag0; | ||
375 | *(vip)CIA_IOC_TB_TAGn(1) = 0; | ||
376 | *(vip)CIA_IOC_TB_TAGn(2) = 0; | ||
377 | *(vip)CIA_IOC_TB_TAGn(3) = 0; | ||
378 | *(vip)CIA_IOC_TB_TAGn(4) = 0; | ||
379 | *(vip)CIA_IOC_TB_TAGn(5) = 0; | ||
380 | *(vip)CIA_IOC_TB_TAGn(6) = 0; | ||
381 | *(vip)CIA_IOC_TB_TAGn(7) = 0; | ||
382 | *(vip)CIA_IOC_TBn_PAGEm(0,0) = pte0; | ||
383 | *(vip)CIA_IOC_TBn_PAGEm(0,1) = 0; | ||
384 | *(vip)CIA_IOC_TBn_PAGEm(0,2) = 0; | ||
385 | *(vip)CIA_IOC_TBn_PAGEm(0,3) = 0; | ||
386 | mb(); | ||
387 | |||
388 | /* Get a usable bus address */ | ||
389 | bus_addr = cia_ioremap(addr0, 8*PAGE_SIZE); | ||
390 | |||
391 | /* First, verify we can read back what we've written. If | ||
392 | this fails, we can't be sure of any of the other testing | ||
393 | we're going to do, so bail. */ | ||
394 | /* ??? Actually, we could do the work with machine checks. | ||
395 | By passing this register update test, we pretty much | ||
396 | guarantee that cia_pci_tbi_try1 works. If this test | ||
397 | fails, cia_pci_tbi_try2 might still work. */ | ||
398 | |||
399 | temp = *(vip)CIA_IOC_TB_TAGn(0); | ||
400 | if (temp != tag0) { | ||
401 | printk("pci: failed tb register update test " | ||
402 | "(tag0 %#x != %#x)\n", temp, tag0); | ||
403 | goto failed; | ||
404 | } | ||
405 | temp = *(vip)CIA_IOC_TB_TAGn(1); | ||
406 | if (temp != 0) { | ||
407 | printk("pci: failed tb register update test " | ||
408 | "(tag1 %#x != 0)\n", temp); | ||
409 | goto failed; | ||
410 | } | ||
411 | temp = *(vip)CIA_IOC_TBn_PAGEm(0,0); | ||
412 | if (temp != pte0) { | ||
413 | printk("pci: failed tb register update test " | ||
414 | "(pte0 %#x != %#x)\n", temp, pte0); | ||
415 | goto failed; | ||
416 | } | ||
417 | printk("pci: passed tb register update test\n"); | ||
418 | |||
419 | /* Second, verify we can actually do I/O through this entry. */ | ||
420 | |||
421 | data0 = 0xdeadbeef; | ||
422 | page[0] = data0; | ||
423 | mcheck_expected(0) = 1; | ||
424 | mcheck_taken(0) = 0; | ||
425 | mb(); | ||
426 | temp = cia_readl(bus_addr); | ||
427 | mb(); | ||
428 | mcheck_expected(0) = 0; | ||
429 | mb(); | ||
430 | if (mcheck_taken(0)) { | ||
431 | printk("pci: failed sg loopback i/o read test (mcheck)\n"); | ||
432 | goto failed; | ||
433 | } | ||
434 | if (temp != data0) { | ||
435 | printk("pci: failed sg loopback i/o read test " | ||
436 | "(%#x != %#x)\n", temp, data0); | ||
437 | goto failed; | ||
438 | } | ||
439 | printk("pci: passed sg loopback i/o read test\n"); | ||
440 | |||
441 | /* Third, try to invalidate the TLB. */ | ||
442 | |||
443 | if (! use_tbia_try2) { | ||
444 | cia_pci_tbi(arena->hose, 0, -1); | ||
445 | temp = *(vip)CIA_IOC_TB_TAGn(0); | ||
446 | if (temp & 1) { | ||
447 | use_tbia_try2 = 1; | ||
448 | printk("pci: failed tbia test; workaround available\n"); | ||
449 | } else { | ||
450 | printk("pci: passed tbia test\n"); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /* Fourth, verify the TLB snoops the EV5's caches when | ||
455 | doing a tlb fill. */ | ||
456 | |||
457 | data0 = 0x5adda15e; | ||
458 | page[0] = data0; | ||
459 | arena->ptes[4] = pte0; | ||
460 | mcheck_expected(0) = 1; | ||
461 | mcheck_taken(0) = 0; | ||
462 | mb(); | ||
463 | temp = cia_readl(bus_addr + 4*PAGE_SIZE); | ||
464 | mb(); | ||
465 | mcheck_expected(0) = 0; | ||
466 | mb(); | ||
467 | if (mcheck_taken(0)) { | ||
468 | printk("pci: failed pte write cache snoop test (mcheck)\n"); | ||
469 | goto failed; | ||
470 | } | ||
471 | if (temp != data0) { | ||
472 | printk("pci: failed pte write cache snoop test " | ||
473 | "(%#x != %#x)\n", temp, data0); | ||
474 | goto failed; | ||
475 | } | ||
476 | printk("pci: passed pte write cache snoop test\n"); | ||
477 | |||
478 | /* Fifth, verify that a previously invalid PTE entry gets | ||
479 | filled from the page table. */ | ||
480 | |||
481 | data0 = 0xabcdef12; | ||
482 | page[0] = data0; | ||
483 | arena->ptes[5] = pte0; | ||
484 | mcheck_expected(0) = 1; | ||
485 | mcheck_taken(0) = 0; | ||
486 | mb(); | ||
487 | temp = cia_readl(bus_addr + 5*PAGE_SIZE); | ||
488 | mb(); | ||
489 | mcheck_expected(0) = 0; | ||
490 | mb(); | ||
491 | if (mcheck_taken(0)) { | ||
492 | printk("pci: failed valid tag invalid pte reload test " | ||
493 | "(mcheck; workaround available)\n"); | ||
494 | /* Work around this bug by aligning new allocations | ||
495 | on 4 page boundaries. */ | ||
496 | arena->align_entry = 4; | ||
497 | } else if (temp != data0) { | ||
498 | printk("pci: failed valid tag invalid pte reload test " | ||
499 | "(%#x != %#x)\n", temp, data0); | ||
500 | goto failed; | ||
501 | } else { | ||
502 | printk("pci: passed valid tag invalid pte reload test\n"); | ||
503 | } | ||
504 | |||
505 | /* Sixth, verify machine checks are working. Test invalid | ||
506 | pte under the same valid tag as we used above. */ | ||
507 | |||
508 | mcheck_expected(0) = 1; | ||
509 | mcheck_taken(0) = 0; | ||
510 | mb(); | ||
511 | temp = cia_readl(bus_addr + 6*PAGE_SIZE); | ||
512 | mb(); | ||
513 | mcheck_expected(0) = 0; | ||
514 | mb(); | ||
515 | printk("pci: %s pci machine check test\n", | ||
516 | mcheck_taken(0) ? "passed" : "failed"); | ||
517 | |||
518 | /* Clean up after the tests. */ | ||
519 | arena->ptes[4] = 0; | ||
520 | arena->ptes[5] = 0; | ||
521 | |||
522 | if (use_tbia_try2) { | ||
523 | alpha_mv.mv_pci_tbi = cia_pci_tbi_try2; | ||
524 | |||
525 | /* Tags 0-3 must be disabled if we use this workaraund. */ | ||
526 | wmb(); | ||
527 | *(vip)CIA_IOC_TB_TAGn(0) = 2; | ||
528 | *(vip)CIA_IOC_TB_TAGn(1) = 2; | ||
529 | *(vip)CIA_IOC_TB_TAGn(2) = 2; | ||
530 | *(vip)CIA_IOC_TB_TAGn(3) = 2; | ||
531 | |||
532 | printk("pci: tbia workaround enabled\n"); | ||
533 | } | ||
534 | alpha_mv.mv_pci_tbi(arena->hose, 0, -1); | ||
535 | |||
536 | exit: | ||
537 | /* unmap the bus addr */ | ||
538 | cia_iounmap(bus_addr); | ||
539 | |||
540 | /* Restore normal PCI operation. */ | ||
541 | mb(); | ||
542 | *(vip)CIA_IOC_CIA_CTRL = ctrl; | ||
543 | mb(); | ||
544 | *(vip)CIA_IOC_CIA_CTRL; | ||
545 | mb(); | ||
546 | return; | ||
547 | |||
548 | failed: | ||
549 | printk("pci: disabling sg translation window\n"); | ||
550 | *(vip)CIA_IOC_PCI_W0_BASE = 0; | ||
551 | *(vip)CIA_IOC_PCI_W1_BASE = 0; | ||
552 | pci_isa_hose->sg_isa = NULL; | ||
553 | alpha_mv.mv_pci_tbi = NULL; | ||
554 | goto exit; | ||
555 | } | ||
556 | |||
557 | #if defined(ALPHA_RESTORE_SRM_SETUP) | ||
558 | /* Save CIA configuration data as the console had it set up. */ | ||
559 | struct | ||
560 | { | ||
561 | unsigned int hae_mem; | ||
562 | unsigned int hae_io; | ||
563 | unsigned int pci_dac_offset; | ||
564 | unsigned int err_mask; | ||
565 | unsigned int cia_ctrl; | ||
566 | unsigned int cia_cnfg; | ||
567 | struct { | ||
568 | unsigned int w_base; | ||
569 | unsigned int w_mask; | ||
570 | unsigned int t_base; | ||
571 | } window[4]; | ||
572 | } saved_config __attribute((common)); | ||
573 | |||
574 | void | ||
575 | cia_save_srm_settings(int is_pyxis) | ||
576 | { | ||
577 | int i; | ||
578 | |||
579 | /* Save some important registers. */ | ||
580 | saved_config.err_mask = *(vip)CIA_IOC_ERR_MASK; | ||
581 | saved_config.cia_ctrl = *(vip)CIA_IOC_CIA_CTRL; | ||
582 | saved_config.hae_mem = *(vip)CIA_IOC_HAE_MEM; | ||
583 | saved_config.hae_io = *(vip)CIA_IOC_HAE_IO; | ||
584 | saved_config.pci_dac_offset = *(vip)CIA_IOC_PCI_W_DAC; | ||
585 | |||
586 | if (is_pyxis) | ||
587 | saved_config.cia_cnfg = *(vip)CIA_IOC_CIA_CNFG; | ||
588 | else | ||
589 | saved_config.cia_cnfg = 0; | ||
590 | |||
591 | /* Save DMA windows configuration. */ | ||
592 | for (i = 0; i < 4; i++) { | ||
593 | saved_config.window[i].w_base = *(vip)CIA_IOC_PCI_Wn_BASE(i); | ||
594 | saved_config.window[i].w_mask = *(vip)CIA_IOC_PCI_Wn_MASK(i); | ||
595 | saved_config.window[i].t_base = *(vip)CIA_IOC_PCI_Tn_BASE(i); | ||
596 | } | ||
597 | mb(); | ||
598 | } | ||
599 | |||
600 | void | ||
601 | cia_restore_srm_settings(void) | ||
602 | { | ||
603 | int i; | ||
604 | |||
605 | for (i = 0; i < 4; i++) { | ||
606 | *(vip)CIA_IOC_PCI_Wn_BASE(i) = saved_config.window[i].w_base; | ||
607 | *(vip)CIA_IOC_PCI_Wn_MASK(i) = saved_config.window[i].w_mask; | ||
608 | *(vip)CIA_IOC_PCI_Tn_BASE(i) = saved_config.window[i].t_base; | ||
609 | } | ||
610 | |||
611 | *(vip)CIA_IOC_HAE_MEM = saved_config.hae_mem; | ||
612 | *(vip)CIA_IOC_HAE_IO = saved_config.hae_io; | ||
613 | *(vip)CIA_IOC_PCI_W_DAC = saved_config.pci_dac_offset; | ||
614 | *(vip)CIA_IOC_ERR_MASK = saved_config.err_mask; | ||
615 | *(vip)CIA_IOC_CIA_CTRL = saved_config.cia_ctrl; | ||
616 | |||
617 | if (saved_config.cia_cnfg) /* Must be pyxis. */ | ||
618 | *(vip)CIA_IOC_CIA_CNFG = saved_config.cia_cnfg; | ||
619 | |||
620 | mb(); | ||
621 | } | ||
622 | #else /* ALPHA_RESTORE_SRM_SETUP */ | ||
623 | #define cia_save_srm_settings(p) do {} while (0) | ||
624 | #define cia_restore_srm_settings() do {} while (0) | ||
625 | #endif /* ALPHA_RESTORE_SRM_SETUP */ | ||
626 | |||
627 | |||
628 | static void __init | ||
629 | do_init_arch(int is_pyxis) | ||
630 | { | ||
631 | struct pci_controller *hose; | ||
632 | int temp, cia_rev, tbia_window; | ||
633 | |||
634 | cia_rev = *(vip)CIA_IOC_CIA_REV & CIA_REV_MASK; | ||
635 | printk("pci: cia revision %d%s\n", | ||
636 | cia_rev, is_pyxis ? " (pyxis)" : ""); | ||
637 | |||
638 | if (alpha_using_srm) | ||
639 | cia_save_srm_settings(is_pyxis); | ||
640 | |||
641 | /* Set up error reporting. */ | ||
642 | temp = *(vip)CIA_IOC_ERR_MASK; | ||
643 | temp &= ~(CIA_ERR_CPU_PE | CIA_ERR_MEM_NEM | CIA_ERR_PA_PTE_INV | ||
644 | | CIA_ERR_RCVD_MAS_ABT | CIA_ERR_RCVD_TAR_ABT); | ||
645 | *(vip)CIA_IOC_ERR_MASK = temp; | ||
646 | |||
647 | /* Clear all currently pending errors. */ | ||
648 | temp = *(vip)CIA_IOC_CIA_ERR; | ||
649 | *(vip)CIA_IOC_CIA_ERR = temp; | ||
650 | |||
651 | /* Turn on mchecks. */ | ||
652 | temp = *(vip)CIA_IOC_CIA_CTRL; | ||
653 | temp |= CIA_CTRL_FILL_ERR_EN | CIA_CTRL_MCHK_ERR_EN; | ||
654 | *(vip)CIA_IOC_CIA_CTRL = temp; | ||
655 | |||
656 | /* Clear the CFG register, which gets used for PCI config space | ||
657 | accesses. That is the way we want to use it, and we do not | ||
658 | want to depend on what ARC or SRM might have left behind. */ | ||
659 | *(vip)CIA_IOC_CFG = 0; | ||
660 | |||
661 | /* Zero the HAEs. */ | ||
662 | *(vip)CIA_IOC_HAE_MEM = 0; | ||
663 | *(vip)CIA_IOC_HAE_IO = 0; | ||
664 | |||
665 | /* For PYXIS, we always use BWX bus and i/o accesses. To that end, | ||
666 | make sure they're enabled on the controller. At the same time, | ||
667 | enable the monster window. */ | ||
668 | if (is_pyxis) { | ||
669 | temp = *(vip)CIA_IOC_CIA_CNFG; | ||
670 | temp |= CIA_CNFG_IOA_BWEN | CIA_CNFG_PCI_MWEN; | ||
671 | *(vip)CIA_IOC_CIA_CNFG = temp; | ||
672 | } | ||
673 | |||
674 | /* Synchronize with all previous changes. */ | ||
675 | mb(); | ||
676 | *(vip)CIA_IOC_CIA_REV; | ||
677 | |||
678 | /* | ||
679 | * Create our single hose. | ||
680 | */ | ||
681 | |||
682 | pci_isa_hose = hose = alloc_pci_controller(); | ||
683 | hose->io_space = &ioport_resource; | ||
684 | hose->mem_space = &iomem_resource; | ||
685 | hose->index = 0; | ||
686 | |||
687 | if (! is_pyxis) { | ||
688 | struct resource *hae_mem = alloc_resource(); | ||
689 | hose->mem_space = hae_mem; | ||
690 | |||
691 | hae_mem->start = 0; | ||
692 | hae_mem->end = CIA_MEM_R1_MASK; | ||
693 | hae_mem->name = pci_hae0_name; | ||
694 | hae_mem->flags = IORESOURCE_MEM; | ||
695 | |||
696 | if (request_resource(&iomem_resource, hae_mem) < 0) | ||
697 | printk(KERN_ERR "Failed to request HAE_MEM\n"); | ||
698 | |||
699 | hose->sparse_mem_base = CIA_SPARSE_MEM - IDENT_ADDR; | ||
700 | hose->dense_mem_base = CIA_DENSE_MEM - IDENT_ADDR; | ||
701 | hose->sparse_io_base = CIA_IO - IDENT_ADDR; | ||
702 | hose->dense_io_base = 0; | ||
703 | } else { | ||
704 | hose->sparse_mem_base = 0; | ||
705 | hose->dense_mem_base = CIA_BW_MEM - IDENT_ADDR; | ||
706 | hose->sparse_io_base = 0; | ||
707 | hose->dense_io_base = CIA_BW_IO - IDENT_ADDR; | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * Set up the PCI to main memory translation windows. | ||
712 | * | ||
713 | * Window 0 is S/G 8MB at 8MB (for isa) | ||
714 | * Window 1 is S/G 1MB at 768MB (for tbia) (unused for CIA rev 1) | ||
715 | * Window 2 is direct access 2GB at 2GB | ||
716 | * Window 3 is DAC access 4GB at 8GB (or S/G for tbia if CIA rev 1) | ||
717 | * | ||
718 | * ??? NetBSD hints that page tables must be aligned to 32K, | ||
719 | * possibly due to a hardware bug. This is over-aligned | ||
720 | * from the 8K alignment one would expect for an 8MB window. | ||
721 | * No description of what revisions affected. | ||
722 | */ | ||
723 | |||
724 | hose->sg_pci = NULL; | ||
725 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 32768); | ||
726 | |||
727 | __direct_map_base = 0x80000000; | ||
728 | __direct_map_size = 0x80000000; | ||
729 | |||
730 | *(vip)CIA_IOC_PCI_W0_BASE = hose->sg_isa->dma_base | 3; | ||
731 | *(vip)CIA_IOC_PCI_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000; | ||
732 | *(vip)CIA_IOC_PCI_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2; | ||
733 | |||
734 | *(vip)CIA_IOC_PCI_W2_BASE = __direct_map_base | 1; | ||
735 | *(vip)CIA_IOC_PCI_W2_MASK = (__direct_map_size - 1) & 0xfff00000; | ||
736 | *(vip)CIA_IOC_PCI_T2_BASE = 0 >> 2; | ||
737 | |||
738 | /* On PYXIS we have the monster window, selected by bit 40, so | ||
739 | there is no need for window3 to be enabled. | ||
740 | |||
741 | On CIA, we don't have true arbitrary addressing -- bits <39:32> | ||
742 | are compared against W_DAC. We can, however, directly map 4GB, | ||
743 | which is better than before. However, due to assumptions made | ||
744 | elsewhere, we should not claim that we support DAC unless that | ||
745 | 4GB covers all of physical memory. | ||
746 | |||
747 | On CIA rev 1, apparently W1 and W2 can't be used for SG. | ||
748 | At least, there are reports that it doesn't work for Alcor. | ||
749 | In that case, we have no choice but to use W3 for the TBIA | ||
750 | workaround, which means we can't use DAC at all. */ | ||
751 | |||
752 | tbia_window = 1; | ||
753 | if (is_pyxis) { | ||
754 | *(vip)CIA_IOC_PCI_W3_BASE = 0; | ||
755 | } else if (cia_rev == 1) { | ||
756 | *(vip)CIA_IOC_PCI_W1_BASE = 0; | ||
757 | tbia_window = 3; | ||
758 | } else if (max_low_pfn > (0x100000000UL >> PAGE_SHIFT)) { | ||
759 | *(vip)CIA_IOC_PCI_W3_BASE = 0; | ||
760 | } else { | ||
761 | *(vip)CIA_IOC_PCI_W3_BASE = 0x00000000 | 1 | 8; | ||
762 | *(vip)CIA_IOC_PCI_W3_MASK = 0xfff00000; | ||
763 | *(vip)CIA_IOC_PCI_T3_BASE = 0 >> 2; | ||
764 | |||
765 | alpha_mv.pci_dac_offset = 0x200000000UL; | ||
766 | *(vip)CIA_IOC_PCI_W_DAC = alpha_mv.pci_dac_offset >> 32; | ||
767 | } | ||
768 | |||
769 | /* Prepare workaround for apparently broken tbia. */ | ||
770 | cia_prepare_tbia_workaround(tbia_window); | ||
771 | } | ||
772 | |||
773 | void __init | ||
774 | cia_init_arch(void) | ||
775 | { | ||
776 | do_init_arch(0); | ||
777 | } | ||
778 | |||
779 | void __init | ||
780 | pyxis_init_arch(void) | ||
781 | { | ||
782 | /* On pyxis machines we can precisely calculate the | ||
783 | CPU clock frequency using pyxis real time counter. | ||
784 | It's especially useful for SX164 with broken RTC. | ||
785 | |||
786 | Both CPU and chipset are driven by the single 16.666M | ||
787 | or 16.667M crystal oscillator. PYXIS_RT_COUNT clock is | ||
788 | 66.66 MHz. -ink */ | ||
789 | |||
790 | unsigned int cc0, cc1; | ||
791 | unsigned long pyxis_cc; | ||
792 | |||
793 | __asm__ __volatile__ ("rpcc %0" : "=r"(cc0)); | ||
794 | pyxis_cc = *(vulp)PYXIS_RT_COUNT; | ||
795 | do { } while(*(vulp)PYXIS_RT_COUNT - pyxis_cc < 4096); | ||
796 | __asm__ __volatile__ ("rpcc %0" : "=r"(cc1)); | ||
797 | cc1 -= cc0; | ||
798 | hwrpb->cycle_freq = ((cc1 >> 11) * 100000000UL) / 3; | ||
799 | hwrpb_update_checksum(hwrpb); | ||
800 | |||
801 | do_init_arch(1); | ||
802 | } | ||
803 | |||
804 | void | ||
805 | cia_kill_arch(int mode) | ||
806 | { | ||
807 | if (alpha_using_srm) | ||
808 | cia_restore_srm_settings(); | ||
809 | } | ||
810 | |||
811 | void __init | ||
812 | cia_init_pci(void) | ||
813 | { | ||
814 | /* Must delay this from init_arch, as we need machine checks. */ | ||
815 | verify_tb_operation(); | ||
816 | common_init_pci(); | ||
817 | } | ||
818 | |||
819 | static inline void | ||
820 | cia_pci_clr_err(void) | ||
821 | { | ||
822 | int jd; | ||
823 | |||
824 | jd = *(vip)CIA_IOC_CIA_ERR; | ||
825 | *(vip)CIA_IOC_CIA_ERR = jd; | ||
826 | mb(); | ||
827 | *(vip)CIA_IOC_CIA_ERR; /* re-read to force write. */ | ||
828 | } | ||
829 | |||
830 | #ifdef CONFIG_VERBOSE_MCHECK | ||
831 | static void | ||
832 | cia_decode_pci_error(struct el_CIA_sysdata_mcheck *cia, const char *msg) | ||
833 | { | ||
834 | static const char * const pci_cmd_desc[16] = { | ||
835 | "Interrupt Acknowledge", "Special Cycle", "I/O Read", | ||
836 | "I/O Write", "Reserved 0x4", "Reserved 0x5", "Memory Read", | ||
837 | "Memory Write", "Reserved 0x8", "Reserved 0x9", | ||
838 | "Configuration Read", "Configuration Write", | ||
839 | "Memory Read Multiple", "Dual Address Cycle", | ||
840 | "Memory Read Line", "Memory Write and Invalidate" | ||
841 | }; | ||
842 | |||
843 | if (cia->cia_err & (CIA_ERR_COR_ERR | ||
844 | | CIA_ERR_UN_COR_ERR | ||
845 | | CIA_ERR_MEM_NEM | ||
846 | | CIA_ERR_PA_PTE_INV)) { | ||
847 | static const char * const window_desc[6] = { | ||
848 | "No window active", "Window 0 hit", "Window 1 hit", | ||
849 | "Window 2 hit", "Window 3 hit", "Monster window hit" | ||
850 | }; | ||
851 | |||
852 | const char *window; | ||
853 | const char *cmd; | ||
854 | unsigned long addr, tmp; | ||
855 | int lock, dac; | ||
856 | |||
857 | cmd = pci_cmd_desc[cia->pci_err0 & 0x7]; | ||
858 | lock = (cia->pci_err0 >> 4) & 1; | ||
859 | dac = (cia->pci_err0 >> 5) & 1; | ||
860 | |||
861 | tmp = (cia->pci_err0 >> 8) & 0x1F; | ||
862 | tmp = ffs(tmp); | ||
863 | window = window_desc[tmp]; | ||
864 | |||
865 | addr = cia->pci_err1; | ||
866 | if (dac) { | ||
867 | tmp = *(vip)CIA_IOC_PCI_W_DAC & 0xFFUL; | ||
868 | addr |= tmp << 32; | ||
869 | } | ||
870 | |||
871 | printk(KERN_CRIT "CIA machine check: %s\n", msg); | ||
872 | printk(KERN_CRIT " DMA command: %s\n", cmd); | ||
873 | printk(KERN_CRIT " PCI address: %#010lx\n", addr); | ||
874 | printk(KERN_CRIT " %s, Lock: %d, DAC: %d\n", | ||
875 | window, lock, dac); | ||
876 | } else if (cia->cia_err & (CIA_ERR_PERR | ||
877 | | CIA_ERR_PCI_ADDR_PE | ||
878 | | CIA_ERR_RCVD_MAS_ABT | ||
879 | | CIA_ERR_RCVD_TAR_ABT | ||
880 | | CIA_ERR_IOA_TIMEOUT)) { | ||
881 | static const char * const master_st_desc[16] = { | ||
882 | "Idle", "Drive bus", "Address step cycle", | ||
883 | "Address cycle", "Data cycle", "Last read data cycle", | ||
884 | "Last write data cycle", "Read stop cycle", | ||
885 | "Write stop cycle", "Read turnaround cycle", | ||
886 | "Write turnaround cycle", "Reserved 0xB", | ||
887 | "Reserved 0xC", "Reserved 0xD", "Reserved 0xE", | ||
888 | "Unknown state" | ||
889 | }; | ||
890 | static const char * const target_st_desc[16] = { | ||
891 | "Idle", "Busy", "Read data cycle", "Write data cycle", | ||
892 | "Read stop cycle", "Write stop cycle", | ||
893 | "Read turnaround cycle", "Write turnaround cycle", | ||
894 | "Read wait cycle", "Write wait cycle", | ||
895 | "Reserved 0xA", "Reserved 0xB", "Reserved 0xC", | ||
896 | "Reserved 0xD", "Reserved 0xE", "Unknown state" | ||
897 | }; | ||
898 | |||
899 | const char *cmd; | ||
900 | const char *master, *target; | ||
901 | unsigned long addr, tmp; | ||
902 | int dac; | ||
903 | |||
904 | master = master_st_desc[(cia->pci_err0 >> 16) & 0xF]; | ||
905 | target = target_st_desc[(cia->pci_err0 >> 20) & 0xF]; | ||
906 | cmd = pci_cmd_desc[(cia->pci_err0 >> 24) & 0xF]; | ||
907 | dac = (cia->pci_err0 >> 28) & 1; | ||
908 | |||
909 | addr = cia->pci_err2; | ||
910 | if (dac) { | ||
911 | tmp = *(volatile int *)CIA_IOC_PCI_W_DAC & 0xFFUL; | ||
912 | addr |= tmp << 32; | ||
913 | } | ||
914 | |||
915 | printk(KERN_CRIT "CIA machine check: %s\n", msg); | ||
916 | printk(KERN_CRIT " PCI command: %s\n", cmd); | ||
917 | printk(KERN_CRIT " Master state: %s, Target state: %s\n", | ||
918 | master, target); | ||
919 | printk(KERN_CRIT " PCI address: %#010lx, DAC: %d\n", | ||
920 | addr, dac); | ||
921 | } else { | ||
922 | printk(KERN_CRIT "CIA machine check: %s\n", msg); | ||
923 | printk(KERN_CRIT " Unknown PCI error\n"); | ||
924 | printk(KERN_CRIT " PCI_ERR0 = %#08lx", cia->pci_err0); | ||
925 | printk(KERN_CRIT " PCI_ERR1 = %#08lx", cia->pci_err1); | ||
926 | printk(KERN_CRIT " PCI_ERR2 = %#08lx", cia->pci_err2); | ||
927 | } | ||
928 | } | ||
929 | |||
930 | static void | ||
931 | cia_decode_mem_error(struct el_CIA_sysdata_mcheck *cia, const char *msg) | ||
932 | { | ||
933 | unsigned long mem_port_addr; | ||
934 | unsigned long mem_port_mask; | ||
935 | const char *mem_port_cmd; | ||
936 | const char *seq_state; | ||
937 | const char *set_select; | ||
938 | unsigned long tmp; | ||
939 | |||
940 | /* If this is a DMA command, also decode the PCI bits. */ | ||
941 | if ((cia->mem_err1 >> 20) & 1) | ||
942 | cia_decode_pci_error(cia, msg); | ||
943 | else | ||
944 | printk(KERN_CRIT "CIA machine check: %s\n", msg); | ||
945 | |||
946 | mem_port_addr = cia->mem_err0 & 0xfffffff0; | ||
947 | mem_port_addr |= (cia->mem_err1 & 0x83UL) << 32; | ||
948 | |||
949 | mem_port_mask = (cia->mem_err1 >> 12) & 0xF; | ||
950 | |||
951 | tmp = (cia->mem_err1 >> 8) & 0xF; | ||
952 | tmp |= ((cia->mem_err1 >> 20) & 1) << 4; | ||
953 | if ((tmp & 0x1E) == 0x06) | ||
954 | mem_port_cmd = "WRITE BLOCK or WRITE BLOCK LOCK"; | ||
955 | else if ((tmp & 0x1C) == 0x08) | ||
956 | mem_port_cmd = "READ MISS or READ MISS MODIFY"; | ||
957 | else if (tmp == 0x1C) | ||
958 | mem_port_cmd = "BC VICTIM"; | ||
959 | else if ((tmp & 0x1E) == 0x0E) | ||
960 | mem_port_cmd = "READ MISS MODIFY"; | ||
961 | else if ((tmp & 0x1C) == 0x18) | ||
962 | mem_port_cmd = "DMA READ or DMA READ MODIFY"; | ||
963 | else if ((tmp & 0x1E) == 0x12) | ||
964 | mem_port_cmd = "DMA WRITE"; | ||
965 | else | ||
966 | mem_port_cmd = "Unknown"; | ||
967 | |||
968 | tmp = (cia->mem_err1 >> 16) & 0xF; | ||
969 | switch (tmp) { | ||
970 | case 0x0: | ||
971 | seq_state = "Idle"; | ||
972 | break; | ||
973 | case 0x1: | ||
974 | seq_state = "DMA READ or DMA WRITE"; | ||
975 | break; | ||
976 | case 0x2: case 0x3: | ||
977 | seq_state = "READ MISS (or READ MISS MODIFY) with victim"; | ||
978 | break; | ||
979 | case 0x4: case 0x5: case 0x6: | ||
980 | seq_state = "READ MISS (or READ MISS MODIFY) with no victim"; | ||
981 | break; | ||
982 | case 0x8: case 0x9: case 0xB: | ||
983 | seq_state = "Refresh"; | ||
984 | break; | ||
985 | case 0xC: | ||
986 | seq_state = "Idle, waiting for DMA pending read"; | ||
987 | break; | ||
988 | case 0xE: case 0xF: | ||
989 | seq_state = "Idle, ras precharge"; | ||
990 | break; | ||
991 | default: | ||
992 | seq_state = "Unknown"; | ||
993 | break; | ||
994 | } | ||
995 | |||
996 | tmp = (cia->mem_err1 >> 24) & 0x1F; | ||
997 | switch (tmp) { | ||
998 | case 0x00: set_select = "Set 0 selected"; break; | ||
999 | case 0x01: set_select = "Set 1 selected"; break; | ||
1000 | case 0x02: set_select = "Set 2 selected"; break; | ||
1001 | case 0x03: set_select = "Set 3 selected"; break; | ||
1002 | case 0x04: set_select = "Set 4 selected"; break; | ||
1003 | case 0x05: set_select = "Set 5 selected"; break; | ||
1004 | case 0x06: set_select = "Set 6 selected"; break; | ||
1005 | case 0x07: set_select = "Set 7 selected"; break; | ||
1006 | case 0x08: set_select = "Set 8 selected"; break; | ||
1007 | case 0x09: set_select = "Set 9 selected"; break; | ||
1008 | case 0x0A: set_select = "Set A selected"; break; | ||
1009 | case 0x0B: set_select = "Set B selected"; break; | ||
1010 | case 0x0C: set_select = "Set C selected"; break; | ||
1011 | case 0x0D: set_select = "Set D selected"; break; | ||
1012 | case 0x0E: set_select = "Set E selected"; break; | ||
1013 | case 0x0F: set_select = "Set F selected"; break; | ||
1014 | case 0x10: set_select = "No set selected"; break; | ||
1015 | case 0x1F: set_select = "Refresh cycle"; break; | ||
1016 | default: set_select = "Unknown"; break; | ||
1017 | } | ||
1018 | |||
1019 | printk(KERN_CRIT " Memory port command: %s\n", mem_port_cmd); | ||
1020 | printk(KERN_CRIT " Memory port address: %#010lx, mask: %#lx\n", | ||
1021 | mem_port_addr, mem_port_mask); | ||
1022 | printk(KERN_CRIT " Memory sequencer state: %s\n", seq_state); | ||
1023 | printk(KERN_CRIT " Memory set: %s\n", set_select); | ||
1024 | } | ||
1025 | |||
1026 | static void | ||
1027 | cia_decode_ecc_error(struct el_CIA_sysdata_mcheck *cia, const char *msg) | ||
1028 | { | ||
1029 | long syn; | ||
1030 | long i; | ||
1031 | const char *fmt; | ||
1032 | |||
1033 | cia_decode_mem_error(cia, msg); | ||
1034 | |||
1035 | syn = cia->cia_syn & 0xff; | ||
1036 | if (syn == (syn & -syn)) { | ||
1037 | fmt = KERN_CRIT " ECC syndrome %#x -- check bit %d\n"; | ||
1038 | i = ffs(syn) - 1; | ||
1039 | } else { | ||
1040 | static unsigned char const data_bit[64] = { | ||
1041 | 0xCE, 0xCB, 0xD3, 0xD5, | ||
1042 | 0xD6, 0xD9, 0xDA, 0xDC, | ||
1043 | 0x23, 0x25, 0x26, 0x29, | ||
1044 | 0x2A, 0x2C, 0x31, 0x34, | ||
1045 | 0x0E, 0x0B, 0x13, 0x15, | ||
1046 | 0x16, 0x19, 0x1A, 0x1C, | ||
1047 | 0xE3, 0xE5, 0xE6, 0xE9, | ||
1048 | 0xEA, 0xEC, 0xF1, 0xF4, | ||
1049 | 0x4F, 0x4A, 0x52, 0x54, | ||
1050 | 0x57, 0x58, 0x5B, 0x5D, | ||
1051 | 0xA2, 0xA4, 0xA7, 0xA8, | ||
1052 | 0xAB, 0xAD, 0xB0, 0xB5, | ||
1053 | 0x8F, 0x8A, 0x92, 0x94, | ||
1054 | 0x97, 0x98, 0x9B, 0x9D, | ||
1055 | 0x62, 0x64, 0x67, 0x68, | ||
1056 | 0x6B, 0x6D, 0x70, 0x75 | ||
1057 | }; | ||
1058 | |||
1059 | for (i = 0; i < 64; ++i) | ||
1060 | if (data_bit[i] == syn) | ||
1061 | break; | ||
1062 | |||
1063 | if (i < 64) | ||
1064 | fmt = KERN_CRIT " ECC syndrome %#x -- data bit %d\n"; | ||
1065 | else | ||
1066 | fmt = KERN_CRIT " ECC syndrome %#x -- unknown bit\n"; | ||
1067 | } | ||
1068 | |||
1069 | printk (fmt, syn, i); | ||
1070 | } | ||
1071 | |||
1072 | static void | ||
1073 | cia_decode_parity_error(struct el_CIA_sysdata_mcheck *cia) | ||
1074 | { | ||
1075 | static const char * const cmd_desc[16] = { | ||
1076 | "NOP", "LOCK", "FETCH", "FETCH_M", "MEMORY BARRIER", | ||
1077 | "SET DIRTY", "WRITE BLOCK", "WRITE BLOCK LOCK", | ||
1078 | "READ MISS0", "READ MISS1", "READ MISS MOD0", | ||
1079 | "READ MISS MOD1", "BCACHE VICTIM", "Spare", | ||
1080 | "READ MISS MOD STC0", "READ MISS MOD STC1" | ||
1081 | }; | ||
1082 | |||
1083 | unsigned long addr; | ||
1084 | unsigned long mask; | ||
1085 | const char *cmd; | ||
1086 | int par; | ||
1087 | |||
1088 | addr = cia->cpu_err0 & 0xfffffff0; | ||
1089 | addr |= (cia->cpu_err1 & 0x83UL) << 32; | ||
1090 | cmd = cmd_desc[(cia->cpu_err1 >> 8) & 0xF]; | ||
1091 | mask = (cia->cpu_err1 >> 12) & 0xF; | ||
1092 | par = (cia->cpu_err1 >> 21) & 1; | ||
1093 | |||
1094 | printk(KERN_CRIT "CIA machine check: System bus parity error\n"); | ||
1095 | printk(KERN_CRIT " Command: %s, Parity bit: %d\n", cmd, par); | ||
1096 | printk(KERN_CRIT " Address: %#010lx, Mask: %#lx\n", addr, mask); | ||
1097 | } | ||
1098 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
1099 | |||
1100 | |||
1101 | static int | ||
1102 | cia_decode_mchk(unsigned long la_ptr) | ||
1103 | { | ||
1104 | struct el_common *com; | ||
1105 | struct el_CIA_sysdata_mcheck *cia; | ||
1106 | |||
1107 | com = (void *)la_ptr; | ||
1108 | cia = (void *)(la_ptr + com->sys_offset); | ||
1109 | |||
1110 | if ((cia->cia_err & CIA_ERR_VALID) == 0) | ||
1111 | return 0; | ||
1112 | |||
1113 | #ifdef CONFIG_VERBOSE_MCHECK | ||
1114 | if (!alpha_verbose_mcheck) | ||
1115 | return 1; | ||
1116 | |||
1117 | switch (ffs(cia->cia_err & 0xfff) - 1) { | ||
1118 | case 0: /* CIA_ERR_COR_ERR */ | ||
1119 | cia_decode_ecc_error(cia, "Corrected ECC error"); | ||
1120 | break; | ||
1121 | case 1: /* CIA_ERR_UN_COR_ERR */ | ||
1122 | cia_decode_ecc_error(cia, "Uncorrected ECC error"); | ||
1123 | break; | ||
1124 | case 2: /* CIA_ERR_CPU_PE */ | ||
1125 | cia_decode_parity_error(cia); | ||
1126 | break; | ||
1127 | case 3: /* CIA_ERR_MEM_NEM */ | ||
1128 | cia_decode_mem_error(cia, "Access to nonexistent memory"); | ||
1129 | break; | ||
1130 | case 4: /* CIA_ERR_PCI_SERR */ | ||
1131 | cia_decode_pci_error(cia, "PCI bus system error"); | ||
1132 | break; | ||
1133 | case 5: /* CIA_ERR_PERR */ | ||
1134 | cia_decode_pci_error(cia, "PCI data parity error"); | ||
1135 | break; | ||
1136 | case 6: /* CIA_ERR_PCI_ADDR_PE */ | ||
1137 | cia_decode_pci_error(cia, "PCI address parity error"); | ||
1138 | break; | ||
1139 | case 7: /* CIA_ERR_RCVD_MAS_ABT */ | ||
1140 | cia_decode_pci_error(cia, "PCI master abort"); | ||
1141 | break; | ||
1142 | case 8: /* CIA_ERR_RCVD_TAR_ABT */ | ||
1143 | cia_decode_pci_error(cia, "PCI target abort"); | ||
1144 | break; | ||
1145 | case 9: /* CIA_ERR_PA_PTE_INV */ | ||
1146 | cia_decode_pci_error(cia, "PCI invalid PTE"); | ||
1147 | break; | ||
1148 | case 10: /* CIA_ERR_FROM_WRT_ERR */ | ||
1149 | cia_decode_mem_error(cia, "Write to flash ROM attempted"); | ||
1150 | break; | ||
1151 | case 11: /* CIA_ERR_IOA_TIMEOUT */ | ||
1152 | cia_decode_pci_error(cia, "I/O timeout"); | ||
1153 | break; | ||
1154 | } | ||
1155 | |||
1156 | if (cia->cia_err & CIA_ERR_LOST_CORR_ERR) | ||
1157 | printk(KERN_CRIT "CIA lost machine check: " | ||
1158 | "Correctable ECC error\n"); | ||
1159 | if (cia->cia_err & CIA_ERR_LOST_UN_CORR_ERR) | ||
1160 | printk(KERN_CRIT "CIA lost machine check: " | ||
1161 | "Uncorrectable ECC error\n"); | ||
1162 | if (cia->cia_err & CIA_ERR_LOST_CPU_PE) | ||
1163 | printk(KERN_CRIT "CIA lost machine check: " | ||
1164 | "System bus parity error\n"); | ||
1165 | if (cia->cia_err & CIA_ERR_LOST_MEM_NEM) | ||
1166 | printk(KERN_CRIT "CIA lost machine check: " | ||
1167 | "Access to nonexistent memory\n"); | ||
1168 | if (cia->cia_err & CIA_ERR_LOST_PERR) | ||
1169 | printk(KERN_CRIT "CIA lost machine check: " | ||
1170 | "PCI data parity error\n"); | ||
1171 | if (cia->cia_err & CIA_ERR_LOST_PCI_ADDR_PE) | ||
1172 | printk(KERN_CRIT "CIA lost machine check: " | ||
1173 | "PCI address parity error\n"); | ||
1174 | if (cia->cia_err & CIA_ERR_LOST_RCVD_MAS_ABT) | ||
1175 | printk(KERN_CRIT "CIA lost machine check: " | ||
1176 | "PCI master abort\n"); | ||
1177 | if (cia->cia_err & CIA_ERR_LOST_RCVD_TAR_ABT) | ||
1178 | printk(KERN_CRIT "CIA lost machine check: " | ||
1179 | "PCI target abort\n"); | ||
1180 | if (cia->cia_err & CIA_ERR_LOST_PA_PTE_INV) | ||
1181 | printk(KERN_CRIT "CIA lost machine check: " | ||
1182 | "PCI invalid PTE\n"); | ||
1183 | if (cia->cia_err & CIA_ERR_LOST_FROM_WRT_ERR) | ||
1184 | printk(KERN_CRIT "CIA lost machine check: " | ||
1185 | "Write to flash ROM attempted\n"); | ||
1186 | if (cia->cia_err & CIA_ERR_LOST_IOA_TIMEOUT) | ||
1187 | printk(KERN_CRIT "CIA lost machine check: " | ||
1188 | "I/O timeout\n"); | ||
1189 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
1190 | |||
1191 | return 1; | ||
1192 | } | ||
1193 | |||
1194 | void | ||
1195 | cia_machine_check(unsigned long vector, unsigned long la_ptr, | ||
1196 | struct pt_regs * regs) | ||
1197 | { | ||
1198 | int expected; | ||
1199 | |||
1200 | /* Clear the error before any reporting. */ | ||
1201 | mb(); | ||
1202 | mb(); /* magic */ | ||
1203 | draina(); | ||
1204 | cia_pci_clr_err(); | ||
1205 | wrmces(rdmces()); /* reset machine check pending flag. */ | ||
1206 | mb(); | ||
1207 | |||
1208 | expected = mcheck_expected(0); | ||
1209 | if (!expected && vector == 0x660) | ||
1210 | expected = cia_decode_mchk(la_ptr); | ||
1211 | process_mcheck_info(vector, la_ptr, regs, "CIA", expected); | ||
1212 | } | ||
diff --git a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c new file mode 100644 index 000000000000..138d497d1cca --- /dev/null +++ b/arch/alpha/kernel/core_irongate.c | |||
@@ -0,0 +1,416 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_irongate.c | ||
3 | * | ||
4 | * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com). | ||
5 | * | ||
6 | * Copyright (C) 1999 Alpha Processor, Inc., | ||
7 | * (David Daniel, Stig Telfer, Soohoon Lee) | ||
8 | * | ||
9 | * Code common to all IRONGATE core logic chips. | ||
10 | */ | ||
11 | |||
12 | #define __EXTERN_INLINE inline | ||
13 | #include <asm/io.h> | ||
14 | #include <asm/core_irongate.h> | ||
15 | #undef __EXTERN_INLINE | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/initrd.h> | ||
22 | #include <linux/bootmem.h> | ||
23 | |||
24 | #include <asm/ptrace.h> | ||
25 | #include <asm/pci.h> | ||
26 | #include <asm/cacheflush.h> | ||
27 | #include <asm/tlbflush.h> | ||
28 | |||
29 | #include "proto.h" | ||
30 | #include "pci_impl.h" | ||
31 | |||
32 | /* | ||
33 | * BIOS32-style PCI interface: | ||
34 | */ | ||
35 | |||
36 | #define DEBUG_CONFIG 0 | ||
37 | |||
38 | #if DEBUG_CONFIG | ||
39 | # define DBG_CFG(args) printk args | ||
40 | #else | ||
41 | # define DBG_CFG(args) | ||
42 | #endif | ||
43 | |||
44 | igcsr32 *IronECC; | ||
45 | |||
46 | /* | ||
47 | * Given a bus, device, and function number, compute resulting | ||
48 | * configuration space address accordingly. It is therefore not safe | ||
49 | * to have concurrent invocations to configuration space access | ||
50 | * routines, but there really shouldn't be any need for this. | ||
51 | * | ||
52 | * addr[31:24] reserved | ||
53 | * addr[23:16] bus number (8 bits = 128 possible buses) | ||
54 | * addr[15:11] Device number (5 bits) | ||
55 | * addr[10: 8] function number | ||
56 | * addr[ 7: 2] register number | ||
57 | * | ||
58 | * For IRONGATE: | ||
59 | * if (bus = addr[23:16]) == 0 | ||
60 | * then | ||
61 | * type 0 config cycle: | ||
62 | * addr_on_pci[31:11] = id selection for device = addr[15:11] | ||
63 | * addr_on_pci[10: 2] = addr[10: 2] ??? | ||
64 | * addr_on_pci[ 1: 0] = 00 | ||
65 | * else | ||
66 | * type 1 config cycle (pass on with no decoding): | ||
67 | * addr_on_pci[31:24] = 0 | ||
68 | * addr_on_pci[23: 2] = addr[23: 2] | ||
69 | * addr_on_pci[ 1: 0] = 01 | ||
70 | * fi | ||
71 | * | ||
72 | * Notes: | ||
73 | * The function number selects which function of a multi-function device | ||
74 | * (e.g., SCSI and Ethernet). | ||
75 | * | ||
76 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
77 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
78 | * bits. | ||
79 | */ | ||
80 | |||
81 | static int | ||
82 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
83 | unsigned long *pci_addr, unsigned char *type1) | ||
84 | { | ||
85 | unsigned long addr; | ||
86 | u8 bus = pbus->number; | ||
87 | |||
88 | DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " | ||
89 | "pci_addr=0x%p, type1=0x%p)\n", | ||
90 | bus, device_fn, where, pci_addr, type1)); | ||
91 | |||
92 | *type1 = (bus != 0); | ||
93 | |||
94 | addr = (bus << 16) | (device_fn << 8) | where; | ||
95 | addr |= IRONGATE_CONF; | ||
96 | |||
97 | *pci_addr = addr; | ||
98 | DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int | ||
103 | irongate_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
104 | int size, u32 *value) | ||
105 | { | ||
106 | unsigned long addr; | ||
107 | unsigned char type1; | ||
108 | |||
109 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
110 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
111 | |||
112 | switch (size) { | ||
113 | case 1: | ||
114 | *value = __kernel_ldbu(*(vucp)addr); | ||
115 | break; | ||
116 | case 2: | ||
117 | *value = __kernel_ldwu(*(vusp)addr); | ||
118 | break; | ||
119 | case 4: | ||
120 | *value = *(vuip)addr; | ||
121 | break; | ||
122 | } | ||
123 | |||
124 | return PCIBIOS_SUCCESSFUL; | ||
125 | } | ||
126 | |||
127 | static int | ||
128 | irongate_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
129 | int size, u32 value) | ||
130 | { | ||
131 | unsigned long addr; | ||
132 | unsigned char type1; | ||
133 | |||
134 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
135 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
136 | |||
137 | switch (size) { | ||
138 | case 1: | ||
139 | __kernel_stb(value, *(vucp)addr); | ||
140 | mb(); | ||
141 | __kernel_ldbu(*(vucp)addr); | ||
142 | break; | ||
143 | case 2: | ||
144 | __kernel_stw(value, *(vusp)addr); | ||
145 | mb(); | ||
146 | __kernel_ldwu(*(vusp)addr); | ||
147 | break; | ||
148 | case 4: | ||
149 | *(vuip)addr = value; | ||
150 | mb(); | ||
151 | *(vuip)addr; | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | return PCIBIOS_SUCCESSFUL; | ||
156 | } | ||
157 | |||
158 | struct pci_ops irongate_pci_ops = | ||
159 | { | ||
160 | .read = irongate_read_config, | ||
161 | .write = irongate_write_config, | ||
162 | }; | ||
163 | |||
164 | int | ||
165 | irongate_pci_clr_err(void) | ||
166 | { | ||
167 | unsigned int nmi_ctl=0; | ||
168 | unsigned int IRONGATE_jd; | ||
169 | |||
170 | again: | ||
171 | IRONGATE_jd = IRONGATE0->stat_cmd; | ||
172 | printk("Iron stat_cmd %x\n", IRONGATE_jd); | ||
173 | IRONGATE0->stat_cmd = IRONGATE_jd; /* write again clears error bits */ | ||
174 | mb(); | ||
175 | IRONGATE_jd = IRONGATE0->stat_cmd; /* re-read to force write */ | ||
176 | |||
177 | IRONGATE_jd = *IronECC; | ||
178 | printk("Iron ECC %x\n", IRONGATE_jd); | ||
179 | *IronECC = IRONGATE_jd; /* write again clears error bits */ | ||
180 | mb(); | ||
181 | IRONGATE_jd = *IronECC; /* re-read to force write */ | ||
182 | |||
183 | /* Clear ALI NMI */ | ||
184 | nmi_ctl = inb(0x61); | ||
185 | nmi_ctl |= 0x0c; | ||
186 | outb(nmi_ctl, 0x61); | ||
187 | nmi_ctl &= ~0x0c; | ||
188 | outb(nmi_ctl, 0x61); | ||
189 | |||
190 | IRONGATE_jd = *IronECC; | ||
191 | if (IRONGATE_jd & 0x300) goto again; | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | #define IRONGATE_3GB 0xc0000000UL | ||
197 | |||
198 | /* On Albacore (aka UP1500) with 4Gb of RAM we have to reserve some | ||
199 | memory for PCI. At this point we just reserve memory above 3Gb. Most | ||
200 | of this memory will be freed after PCI setup is done. */ | ||
201 | static void __init | ||
202 | albacore_init_arch(void) | ||
203 | { | ||
204 | unsigned long memtop = max_low_pfn << PAGE_SHIFT; | ||
205 | unsigned long pci_mem = (memtop + 0x1000000UL) & ~0xffffffUL; | ||
206 | struct percpu_struct *cpu; | ||
207 | int pal_rev, pal_var; | ||
208 | |||
209 | cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); | ||
210 | pal_rev = cpu->pal_revision & 0xffff; | ||
211 | pal_var = (cpu->pal_revision >> 16) & 0xff; | ||
212 | |||
213 | /* Consoles earlier than A5.6-18 (OSF PALcode v1.62-2) set up | ||
214 | the CPU incorrectly (leave speculative stores enabled), | ||
215 | which causes memory corruption under certain conditions. | ||
216 | Issue a warning for such consoles. */ | ||
217 | if (alpha_using_srm && | ||
218 | (pal_rev < 0x13e || (pal_rev == 0x13e && pal_var < 2))) | ||
219 | printk(KERN_WARNING "WARNING! Upgrade to SRM A5.6-19 " | ||
220 | "or later\n"); | ||
221 | |||
222 | if (pci_mem > IRONGATE_3GB) | ||
223 | pci_mem = IRONGATE_3GB; | ||
224 | IRONGATE0->pci_mem = pci_mem; | ||
225 | alpha_mv.min_mem_address = pci_mem; | ||
226 | if (memtop > pci_mem) { | ||
227 | #ifdef CONFIG_BLK_DEV_INITRD | ||
228 | extern unsigned long initrd_start, initrd_end; | ||
229 | extern void *move_initrd(unsigned long); | ||
230 | |||
231 | /* Move the initrd out of the way. */ | ||
232 | if (initrd_end && __pa(initrd_end) > pci_mem) { | ||
233 | unsigned long size; | ||
234 | |||
235 | size = initrd_end - initrd_start; | ||
236 | free_bootmem_node(NODE_DATA(0), __pa(initrd_start), | ||
237 | PAGE_ALIGN(size)); | ||
238 | if (!move_initrd(pci_mem)) | ||
239 | printk("irongate_init_arch: initrd too big " | ||
240 | "(%ldK)\ndisabling initrd\n", | ||
241 | size / 1024); | ||
242 | } | ||
243 | #endif | ||
244 | reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - pci_mem); | ||
245 | printk("irongate_init_arch: temporarily reserving " | ||
246 | "region %08lx-%08lx for PCI\n", pci_mem, memtop - 1); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | static void __init | ||
251 | irongate_setup_agp(void) | ||
252 | { | ||
253 | /* Disable the GART window. AGPGART doesn't work due to yet | ||
254 | unresolved memory coherency issues... */ | ||
255 | IRONGATE0->agpva = IRONGATE0->agpva & ~0xf; | ||
256 | alpha_agpgart_size = 0; | ||
257 | } | ||
258 | |||
259 | void __init | ||
260 | irongate_init_arch(void) | ||
261 | { | ||
262 | struct pci_controller *hose; | ||
263 | int amd761 = (IRONGATE0->dev_vendor >> 16) > 0x7006; /* Albacore? */ | ||
264 | |||
265 | IronECC = amd761 ? &IRONGATE0->bacsr54_eccms761 : &IRONGATE0->dramms; | ||
266 | |||
267 | irongate_pci_clr_err(); | ||
268 | |||
269 | if (amd761) | ||
270 | albacore_init_arch(); | ||
271 | |||
272 | irongate_setup_agp(); | ||
273 | |||
274 | /* | ||
275 | * Create our single hose. | ||
276 | */ | ||
277 | |||
278 | pci_isa_hose = hose = alloc_pci_controller(); | ||
279 | hose->io_space = &ioport_resource; | ||
280 | hose->mem_space = &iomem_resource; | ||
281 | hose->index = 0; | ||
282 | |||
283 | /* This is for userland consumption. For some reason, the 40-bit | ||
284 | PIO bias that we use in the kernel through KSEG didn't work for | ||
285 | the page table based user mappings. So make sure we get the | ||
286 | 43-bit PIO bias. */ | ||
287 | hose->sparse_mem_base = 0; | ||
288 | hose->sparse_io_base = 0; | ||
289 | hose->dense_mem_base | ||
290 | = (IRONGATE_MEM & 0xffffffffffUL) | 0x80000000000UL; | ||
291 | hose->dense_io_base | ||
292 | = (IRONGATE_IO & 0xffffffffffUL) | 0x80000000000UL; | ||
293 | |||
294 | hose->sg_isa = hose->sg_pci = NULL; | ||
295 | __direct_map_base = 0; | ||
296 | __direct_map_size = 0xffffffff; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * IO map and AGP support | ||
301 | */ | ||
302 | #include <linux/vmalloc.h> | ||
303 | #include <linux/agp_backend.h> | ||
304 | #include <linux/agpgart.h> | ||
305 | #include <asm/pgalloc.h> | ||
306 | |||
307 | #define GET_PAGE_DIR_OFF(addr) (addr >> 22) | ||
308 | #define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr)) | ||
309 | |||
310 | #define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) | ||
311 | #define GET_GATT(addr) (gatt_pages[GET_PAGE_DIR_IDX(addr)]) | ||
312 | |||
313 | void __iomem * | ||
314 | irongate_ioremap(unsigned long addr, unsigned long size) | ||
315 | { | ||
316 | struct vm_struct *area; | ||
317 | unsigned long vaddr; | ||
318 | unsigned long baddr, last; | ||
319 | u32 *mmio_regs, *gatt_pages, *cur_gatt, pte; | ||
320 | unsigned long gart_bus_addr; | ||
321 | |||
322 | if (!alpha_agpgart_size) | ||
323 | return (void __iomem *)(addr + IRONGATE_MEM); | ||
324 | |||
325 | gart_bus_addr = (unsigned long)IRONGATE0->bar0 & | ||
326 | PCI_BASE_ADDRESS_MEM_MASK; | ||
327 | |||
328 | /* | ||
329 | * Check for within the AGP aperture... | ||
330 | */ | ||
331 | do { | ||
332 | /* | ||
333 | * Check the AGP area | ||
334 | */ | ||
335 | if (addr >= gart_bus_addr && addr + size - 1 < | ||
336 | gart_bus_addr + alpha_agpgart_size) | ||
337 | break; | ||
338 | |||
339 | /* | ||
340 | * Not found - assume legacy ioremap | ||
341 | */ | ||
342 | return (void __iomem *)(addr + IRONGATE_MEM); | ||
343 | } while(0); | ||
344 | |||
345 | mmio_regs = (u32 *)(((unsigned long)IRONGATE0->bar1 & | ||
346 | PCI_BASE_ADDRESS_MEM_MASK) + IRONGATE_MEM); | ||
347 | |||
348 | gatt_pages = (u32 *)(phys_to_virt(mmio_regs[1])); /* FIXME */ | ||
349 | |||
350 | /* | ||
351 | * Adjust the limits (mappings must be page aligned) | ||
352 | */ | ||
353 | if (addr & ~PAGE_MASK) { | ||
354 | printk("AGP ioremap failed... addr not page aligned (0x%lx)\n", | ||
355 | addr); | ||
356 | return (void __iomem *)(addr + IRONGATE_MEM); | ||
357 | } | ||
358 | last = addr + size - 1; | ||
359 | size = PAGE_ALIGN(last) - addr; | ||
360 | |||
361 | #if 0 | ||
362 | printk("irongate_ioremap(0x%lx, 0x%lx)\n", addr, size); | ||
363 | printk("irongate_ioremap: gart_bus_addr 0x%lx\n", gart_bus_addr); | ||
364 | printk("irongate_ioremap: gart_aper_size 0x%lx\n", gart_aper_size); | ||
365 | printk("irongate_ioremap: mmio_regs %p\n", mmio_regs); | ||
366 | printk("irongate_ioremap: gatt_pages %p\n", gatt_pages); | ||
367 | |||
368 | for(baddr = addr; baddr <= last; baddr += PAGE_SIZE) | ||
369 | { | ||
370 | cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1); | ||
371 | pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1; | ||
372 | printk("irongate_ioremap: cur_gatt %p pte 0x%x\n", | ||
373 | cur_gatt, pte); | ||
374 | } | ||
375 | #endif | ||
376 | |||
377 | /* | ||
378 | * Map it | ||
379 | */ | ||
380 | area = get_vm_area(size, VM_IOREMAP); | ||
381 | if (!area) return NULL; | ||
382 | |||
383 | for(baddr = addr, vaddr = (unsigned long)area->addr; | ||
384 | baddr <= last; | ||
385 | baddr += PAGE_SIZE, vaddr += PAGE_SIZE) | ||
386 | { | ||
387 | cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1); | ||
388 | pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1; | ||
389 | |||
390 | if (__alpha_remap_area_pages(vaddr, | ||
391 | pte, PAGE_SIZE, 0)) { | ||
392 | printk("AGP ioremap: FAILED to map...\n"); | ||
393 | vfree(area->addr); | ||
394 | return NULL; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | flush_tlb_all(); | ||
399 | |||
400 | vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); | ||
401 | #if 0 | ||
402 | printk("irongate_ioremap(0x%lx, 0x%lx) returning 0x%lx\n", | ||
403 | addr, size, vaddr); | ||
404 | #endif | ||
405 | return (void __iomem *)vaddr; | ||
406 | } | ||
407 | |||
408 | void | ||
409 | irongate_iounmap(volatile void __iomem *xaddr) | ||
410 | { | ||
411 | unsigned long addr = (unsigned long) xaddr; | ||
412 | if (((long)addr >> 41) == -2) | ||
413 | return; /* kseg map, nothing to do */ | ||
414 | if (addr) | ||
415 | return vfree((void *)(PAGE_MASK & addr)); | ||
416 | } | ||
diff --git a/arch/alpha/kernel/core_lca.c b/arch/alpha/kernel/core_lca.c new file mode 100644 index 000000000000..6a5a9145c676 --- /dev/null +++ b/arch/alpha/kernel/core_lca.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_lca.c | ||
3 | * | ||
4 | * Written by David Mosberger (davidm@cs.arizona.edu) with some code | ||
5 | * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit | ||
6 | * bios code. | ||
7 | * | ||
8 | * Code common to all LCA core logic chips. | ||
9 | */ | ||
10 | |||
11 | #define __EXTERN_INLINE inline | ||
12 | #include <asm/io.h> | ||
13 | #include <asm/core_lca.h> | ||
14 | #undef __EXTERN_INLINE | ||
15 | |||
16 | #include <linux/types.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/tty.h> | ||
20 | |||
21 | #include <asm/ptrace.h> | ||
22 | #include <asm/smp.h> | ||
23 | |||
24 | #include "proto.h" | ||
25 | #include "pci_impl.h" | ||
26 | |||
27 | |||
28 | /* | ||
29 | * BIOS32-style PCI interface: | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * Machine check reasons. Defined according to PALcode sources | ||
34 | * (osf.h and platform.h). | ||
35 | */ | ||
36 | #define MCHK_K_TPERR 0x0080 | ||
37 | #define MCHK_K_TCPERR 0x0082 | ||
38 | #define MCHK_K_HERR 0x0084 | ||
39 | #define MCHK_K_ECC_C 0x0086 | ||
40 | #define MCHK_K_ECC_NC 0x0088 | ||
41 | #define MCHK_K_UNKNOWN 0x008A | ||
42 | #define MCHK_K_CACKSOFT 0x008C | ||
43 | #define MCHK_K_BUGCHECK 0x008E | ||
44 | #define MCHK_K_OS_BUGCHECK 0x0090 | ||
45 | #define MCHK_K_DCPERR 0x0092 | ||
46 | #define MCHK_K_ICPERR 0x0094 | ||
47 | |||
48 | |||
49 | /* | ||
50 | * Platform-specific machine-check reasons: | ||
51 | */ | ||
52 | #define MCHK_K_SIO_SERR 0x204 /* all platforms so far */ | ||
53 | #define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */ | ||
54 | #define MCHK_K_DCSR 0x208 /* all but Noname */ | ||
55 | |||
56 | |||
57 | /* | ||
58 | * Given a bus, device, and function number, compute resulting | ||
59 | * configuration space address and setup the LCA_IOC_CONF register | ||
60 | * accordingly. It is therefore not safe to have concurrent | ||
61 | * invocations to configuration space access routines, but there | ||
62 | * really shouldn't be any need for this. | ||
63 | * | ||
64 | * Type 0: | ||
65 | * | ||
66 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
67 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
68 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
69 | * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| | ||
70 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
71 | * | ||
72 | * 31:11 Device select bit. | ||
73 | * 10:8 Function number | ||
74 | * 7:2 Register number | ||
75 | * | ||
76 | * Type 1: | ||
77 | * | ||
78 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
79 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
80 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
81 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
82 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
83 | * | ||
84 | * 31:24 reserved | ||
85 | * 23:16 bus number (8 bits = 128 possible buses) | ||
86 | * 15:11 Device number (5 bits) | ||
87 | * 10:8 function number | ||
88 | * 7:2 register number | ||
89 | * | ||
90 | * Notes: | ||
91 | * The function number selects which function of a multi-function device | ||
92 | * (e.g., SCSI and Ethernet). | ||
93 | * | ||
94 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
95 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
96 | * bits. | ||
97 | */ | ||
98 | |||
99 | static int | ||
100 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
101 | unsigned long *pci_addr) | ||
102 | { | ||
103 | unsigned long addr; | ||
104 | u8 bus = pbus->number; | ||
105 | |||
106 | if (bus == 0) { | ||
107 | int device = device_fn >> 3; | ||
108 | int func = device_fn & 0x7; | ||
109 | |||
110 | /* Type 0 configuration cycle. */ | ||
111 | |||
112 | if (device > 12) { | ||
113 | return -1; | ||
114 | } | ||
115 | |||
116 | *(vulp)LCA_IOC_CONF = 0; | ||
117 | addr = (1 << (11 + device)) | (func << 8) | where; | ||
118 | } else { | ||
119 | /* Type 1 configuration cycle. */ | ||
120 | *(vulp)LCA_IOC_CONF = 1; | ||
121 | addr = (bus << 16) | (device_fn << 8) | where; | ||
122 | } | ||
123 | *pci_addr = addr; | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static unsigned int | ||
128 | conf_read(unsigned long addr) | ||
129 | { | ||
130 | unsigned long flags, code, stat0; | ||
131 | unsigned int value; | ||
132 | |||
133 | local_irq_save(flags); | ||
134 | |||
135 | /* Reset status register to avoid loosing errors. */ | ||
136 | stat0 = *(vulp)LCA_IOC_STAT0; | ||
137 | *(vulp)LCA_IOC_STAT0 = stat0; | ||
138 | mb(); | ||
139 | |||
140 | /* Access configuration space. */ | ||
141 | value = *(vuip)addr; | ||
142 | draina(); | ||
143 | |||
144 | stat0 = *(vulp)LCA_IOC_STAT0; | ||
145 | if (stat0 & LCA_IOC_STAT0_ERR) { | ||
146 | code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) | ||
147 | & LCA_IOC_STAT0_CODE_MASK); | ||
148 | if (code != 1) { | ||
149 | printk("lca.c:conf_read: got stat0=%lx\n", stat0); | ||
150 | } | ||
151 | |||
152 | /* Reset error status. */ | ||
153 | *(vulp)LCA_IOC_STAT0 = stat0; | ||
154 | mb(); | ||
155 | |||
156 | /* Reset machine check. */ | ||
157 | wrmces(0x7); | ||
158 | |||
159 | value = 0xffffffff; | ||
160 | } | ||
161 | local_irq_restore(flags); | ||
162 | return value; | ||
163 | } | ||
164 | |||
165 | static void | ||
166 | conf_write(unsigned long addr, unsigned int value) | ||
167 | { | ||
168 | unsigned long flags, code, stat0; | ||
169 | |||
170 | local_irq_save(flags); /* avoid getting hit by machine check */ | ||
171 | |||
172 | /* Reset status register to avoid loosing errors. */ | ||
173 | stat0 = *(vulp)LCA_IOC_STAT0; | ||
174 | *(vulp)LCA_IOC_STAT0 = stat0; | ||
175 | mb(); | ||
176 | |||
177 | /* Access configuration space. */ | ||
178 | *(vuip)addr = value; | ||
179 | draina(); | ||
180 | |||
181 | stat0 = *(vulp)LCA_IOC_STAT0; | ||
182 | if (stat0 & LCA_IOC_STAT0_ERR) { | ||
183 | code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) | ||
184 | & LCA_IOC_STAT0_CODE_MASK); | ||
185 | if (code != 1) { | ||
186 | printk("lca.c:conf_write: got stat0=%lx\n", stat0); | ||
187 | } | ||
188 | |||
189 | /* Reset error status. */ | ||
190 | *(vulp)LCA_IOC_STAT0 = stat0; | ||
191 | mb(); | ||
192 | |||
193 | /* Reset machine check. */ | ||
194 | wrmces(0x7); | ||
195 | } | ||
196 | local_irq_restore(flags); | ||
197 | } | ||
198 | |||
199 | static int | ||
200 | lca_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
201 | int size, u32 *value) | ||
202 | { | ||
203 | unsigned long addr, pci_addr; | ||
204 | long mask; | ||
205 | int shift; | ||
206 | |||
207 | if (mk_conf_addr(bus, devfn, where, &pci_addr)) | ||
208 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
209 | |||
210 | shift = (where & 3) * 8; | ||
211 | mask = (size - 1) * 8; | ||
212 | addr = (pci_addr << 5) + mask + LCA_CONF; | ||
213 | *value = conf_read(addr) >> (shift); | ||
214 | return PCIBIOS_SUCCESSFUL; | ||
215 | } | ||
216 | |||
217 | static int | ||
218 | lca_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, | ||
219 | u32 value) | ||
220 | { | ||
221 | unsigned long addr, pci_addr; | ||
222 | long mask; | ||
223 | |||
224 | if (mk_conf_addr(bus, devfn, where, &pci_addr)) | ||
225 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
226 | |||
227 | mask = (size - 1) * 8; | ||
228 | addr = (pci_addr << 5) + mask + LCA_CONF; | ||
229 | conf_write(addr, value << ((where & 3) * 8)); | ||
230 | return PCIBIOS_SUCCESSFUL; | ||
231 | } | ||
232 | |||
233 | struct pci_ops lca_pci_ops = | ||
234 | { | ||
235 | .read = lca_read_config, | ||
236 | .write = lca_write_config, | ||
237 | }; | ||
238 | |||
239 | void | ||
240 | lca_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
241 | { | ||
242 | wmb(); | ||
243 | *(vulp)LCA_IOC_TBIA = 0; | ||
244 | mb(); | ||
245 | } | ||
246 | |||
247 | void __init | ||
248 | lca_init_arch(void) | ||
249 | { | ||
250 | struct pci_controller *hose; | ||
251 | |||
252 | /* | ||
253 | * Create our single hose. | ||
254 | */ | ||
255 | |||
256 | pci_isa_hose = hose = alloc_pci_controller(); | ||
257 | hose->io_space = &ioport_resource; | ||
258 | hose->mem_space = &iomem_resource; | ||
259 | hose->index = 0; | ||
260 | |||
261 | hose->sparse_mem_base = LCA_SPARSE_MEM - IDENT_ADDR; | ||
262 | hose->dense_mem_base = LCA_DENSE_MEM - IDENT_ADDR; | ||
263 | hose->sparse_io_base = LCA_IO - IDENT_ADDR; | ||
264 | hose->dense_io_base = 0; | ||
265 | |||
266 | /* | ||
267 | * Set up the PCI to main memory translation windows. | ||
268 | * | ||
269 | * Mimic the SRM settings for the direct-map window. | ||
270 | * Window 0 is scatter-gather 8MB at 8MB (for isa). | ||
271 | * Window 1 is direct access 1GB at 1GB. | ||
272 | * | ||
273 | * Note that we do not try to save any of the DMA window CSRs | ||
274 | * before setting them, since we cannot read those CSRs on LCA. | ||
275 | */ | ||
276 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); | ||
277 | hose->sg_pci = NULL; | ||
278 | __direct_map_base = 0x40000000; | ||
279 | __direct_map_size = 0x40000000; | ||
280 | |||
281 | *(vulp)LCA_IOC_W_BASE0 = hose->sg_isa->dma_base | (3UL << 32); | ||
282 | *(vulp)LCA_IOC_W_MASK0 = (hose->sg_isa->size - 1) & 0xfff00000; | ||
283 | *(vulp)LCA_IOC_T_BASE0 = virt_to_phys(hose->sg_isa->ptes); | ||
284 | |||
285 | *(vulp)LCA_IOC_W_BASE1 = __direct_map_base | (2UL << 32); | ||
286 | *(vulp)LCA_IOC_W_MASK1 = (__direct_map_size - 1) & 0xfff00000; | ||
287 | *(vulp)LCA_IOC_T_BASE1 = 0; | ||
288 | |||
289 | *(vulp)LCA_IOC_TB_ENA = 0x80; | ||
290 | |||
291 | lca_pci_tbi(hose, 0, -1); | ||
292 | |||
293 | /* | ||
294 | * Disable PCI parity for now. The NCR53c810 chip has | ||
295 | * troubles meeting the PCI spec which results in | ||
296 | * data parity errors. | ||
297 | */ | ||
298 | *(vulp)LCA_IOC_PAR_DIS = 1UL<<5; | ||
299 | |||
300 | /* | ||
301 | * Finally, set up for restoring the correct HAE if using SRM. | ||
302 | * Again, since we cannot read many of the CSRs on the LCA, | ||
303 | * one of which happens to be the HAE, we save the value that | ||
304 | * the SRM will expect... | ||
305 | */ | ||
306 | if (alpha_using_srm) | ||
307 | srm_hae = 0x80000000UL; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Constants used during machine-check handling. I suppose these | ||
312 | * could be moved into lca.h but I don't see much reason why anybody | ||
313 | * else would want to use them. | ||
314 | */ | ||
315 | |||
316 | #define ESR_EAV (1UL<< 0) /* error address valid */ | ||
317 | #define ESR_CEE (1UL<< 1) /* correctable error */ | ||
318 | #define ESR_UEE (1UL<< 2) /* uncorrectable error */ | ||
319 | #define ESR_WRE (1UL<< 3) /* write-error */ | ||
320 | #define ESR_SOR (1UL<< 4) /* error source */ | ||
321 | #define ESR_CTE (1UL<< 7) /* cache-tag error */ | ||
322 | #define ESR_MSE (1UL<< 9) /* multiple soft errors */ | ||
323 | #define ESR_MHE (1UL<<10) /* multiple hard errors */ | ||
324 | #define ESR_NXM (1UL<<12) /* non-existent memory */ | ||
325 | |||
326 | #define IOC_ERR ( 1<<4) /* ioc logs an error */ | ||
327 | #define IOC_CMD_SHIFT 0 | ||
328 | #define IOC_CMD (0xf<<IOC_CMD_SHIFT) | ||
329 | #define IOC_CODE_SHIFT 8 | ||
330 | #define IOC_CODE (0xf<<IOC_CODE_SHIFT) | ||
331 | #define IOC_LOST ( 1<<5) | ||
332 | #define IOC_P_NBR ((__u32) ~((1<<13) - 1)) | ||
333 | |||
334 | static void | ||
335 | mem_error(unsigned long esr, unsigned long ear) | ||
336 | { | ||
337 | printk(" %s %s error to %s occurred at address %x\n", | ||
338 | ((esr & ESR_CEE) ? "Correctable" : | ||
339 | (esr & ESR_UEE) ? "Uncorrectable" : "A"), | ||
340 | (esr & ESR_WRE) ? "write" : "read", | ||
341 | (esr & ESR_SOR) ? "memory" : "b-cache", | ||
342 | (unsigned) (ear & 0x1ffffff8)); | ||
343 | if (esr & ESR_CTE) { | ||
344 | printk(" A b-cache tag parity error was detected.\n"); | ||
345 | } | ||
346 | if (esr & ESR_MSE) { | ||
347 | printk(" Several other correctable errors occurred.\n"); | ||
348 | } | ||
349 | if (esr & ESR_MHE) { | ||
350 | printk(" Several other uncorrectable errors occurred.\n"); | ||
351 | } | ||
352 | if (esr & ESR_NXM) { | ||
353 | printk(" Attempted to access non-existent memory.\n"); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | static void | ||
358 | ioc_error(__u32 stat0, __u32 stat1) | ||
359 | { | ||
360 | static const char * const pci_cmd[] = { | ||
361 | "Interrupt Acknowledge", "Special", "I/O Read", "I/O Write", | ||
362 | "Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", | ||
363 | "Rsvd4", "Configuration Read", "Configuration Write", | ||
364 | "Memory Read Multiple", "Dual Address", "Memory Read Line", | ||
365 | "Memory Write and Invalidate" | ||
366 | }; | ||
367 | static const char * const err_name[] = { | ||
368 | "exceeded retry limit", "no device", "bad data parity", | ||
369 | "target abort", "bad address parity", "page table read error", | ||
370 | "invalid page", "data error" | ||
371 | }; | ||
372 | unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT; | ||
373 | unsigned cmd = (stat0 & IOC_CMD) >> IOC_CMD_SHIFT; | ||
374 | |||
375 | printk(" %s initiated PCI %s cycle to address %x" | ||
376 | " failed due to %s.\n", | ||
377 | code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]); | ||
378 | |||
379 | if (code == 5 || code == 6) { | ||
380 | printk(" (Error occurred at PCI memory address %x.)\n", | ||
381 | (stat0 & ~IOC_P_NBR)); | ||
382 | } | ||
383 | if (stat0 & IOC_LOST) { | ||
384 | printk(" Other PCI errors occurred simultaneously.\n"); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | void | ||
389 | lca_machine_check(unsigned long vector, unsigned long la_ptr, | ||
390 | struct pt_regs *regs) | ||
391 | { | ||
392 | const char * reason; | ||
393 | union el_lca el; | ||
394 | |||
395 | el.c = (struct el_common *) la_ptr; | ||
396 | |||
397 | wrmces(rdmces()); /* reset machine check pending flag */ | ||
398 | |||
399 | printk(KERN_CRIT "LCA machine check: vector=%#lx pc=%#lx code=%#x\n", | ||
400 | vector, regs->pc, (unsigned int) el.c->code); | ||
401 | |||
402 | /* | ||
403 | * The first quadword after the common header always seems to | ||
404 | * be the machine check reason---don't know why this isn't | ||
405 | * part of the common header instead. In the case of a long | ||
406 | * logout frame, the upper 32 bits is the machine check | ||
407 | * revision level, which we ignore for now. | ||
408 | */ | ||
409 | switch ((unsigned int) el.c->code) { | ||
410 | case MCHK_K_TPERR: reason = "tag parity error"; break; | ||
411 | case MCHK_K_TCPERR: reason = "tag control parity error"; break; | ||
412 | case MCHK_K_HERR: reason = "access to non-existent memory"; break; | ||
413 | case MCHK_K_ECC_C: reason = "correctable ECC error"; break; | ||
414 | case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; | ||
415 | case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; | ||
416 | case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break; | ||
417 | case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; | ||
418 | case MCHK_K_DCPERR: reason = "d-cache parity error"; break; | ||
419 | case MCHK_K_ICPERR: reason = "i-cache parity error"; break; | ||
420 | case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on PCI bus"; break; | ||
421 | case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; | ||
422 | case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; | ||
423 | case MCHK_K_UNKNOWN: | ||
424 | default: reason = "unknown"; break; | ||
425 | } | ||
426 | |||
427 | switch (el.c->size) { | ||
428 | case sizeof(struct el_lca_mcheck_short): | ||
429 | printk(KERN_CRIT | ||
430 | " Reason: %s (short frame%s, dc_stat=%#lx):\n", | ||
431 | reason, el.c->retry ? ", retryable" : "", | ||
432 | el.s->dc_stat); | ||
433 | if (el.s->esr & ESR_EAV) { | ||
434 | mem_error(el.s->esr, el.s->ear); | ||
435 | } | ||
436 | if (el.s->ioc_stat0 & IOC_ERR) { | ||
437 | ioc_error(el.s->ioc_stat0, el.s->ioc_stat1); | ||
438 | } | ||
439 | break; | ||
440 | |||
441 | case sizeof(struct el_lca_mcheck_long): | ||
442 | printk(KERN_CRIT " Reason: %s (long frame%s):\n", | ||
443 | reason, el.c->retry ? ", retryable" : ""); | ||
444 | printk(KERN_CRIT | ||
445 | " reason: %#lx exc_addr: %#lx dc_stat: %#lx\n", | ||
446 | el.l->pt[0], el.l->exc_addr, el.l->dc_stat); | ||
447 | printk(KERN_CRIT " car: %#lx\n", el.l->car); | ||
448 | if (el.l->esr & ESR_EAV) { | ||
449 | mem_error(el.l->esr, el.l->ear); | ||
450 | } | ||
451 | if (el.l->ioc_stat0 & IOC_ERR) { | ||
452 | ioc_error(el.l->ioc_stat0, el.l->ioc_stat1); | ||
453 | } | ||
454 | break; | ||
455 | |||
456 | default: | ||
457 | printk(KERN_CRIT " Unknown errorlog size %d\n", el.c->size); | ||
458 | } | ||
459 | |||
460 | /* Dump the logout area to give all info. */ | ||
461 | #ifdef CONFIG_VERBOSE_MCHECK | ||
462 | if (alpha_verbose_mcheck > 1) { | ||
463 | unsigned long * ptr = (unsigned long *) la_ptr; | ||
464 | long i; | ||
465 | for (i = 0; i < el.c->size / sizeof(long); i += 2) { | ||
466 | printk(KERN_CRIT " +%8lx %016lx %016lx\n", | ||
467 | i*sizeof(long), ptr[i], ptr[i+1]); | ||
468 | } | ||
469 | } | ||
470 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * The following routines are needed to support the SPEED changing | ||
475 | * necessary to successfully manage the thermal problem on the AlphaBook1. | ||
476 | */ | ||
477 | |||
478 | void | ||
479 | lca_clock_print(void) | ||
480 | { | ||
481 | long pmr_reg; | ||
482 | |||
483 | pmr_reg = LCA_READ_PMR; | ||
484 | |||
485 | printk("Status of clock control:\n"); | ||
486 | printk("\tPrimary clock divisor\t0x%lx\n", LCA_GET_PRIMARY(pmr_reg)); | ||
487 | printk("\tOverride clock divisor\t0x%lx\n", LCA_GET_OVERRIDE(pmr_reg)); | ||
488 | printk("\tInterrupt override is %s\n", | ||
489 | (pmr_reg & LCA_PMR_INTO) ? "on" : "off"); | ||
490 | printk("\tDMA override is %s\n", | ||
491 | (pmr_reg & LCA_PMR_DMAO) ? "on" : "off"); | ||
492 | |||
493 | } | ||
494 | |||
495 | int | ||
496 | lca_get_clock(void) | ||
497 | { | ||
498 | long pmr_reg; | ||
499 | |||
500 | pmr_reg = LCA_READ_PMR; | ||
501 | return(LCA_GET_PRIMARY(pmr_reg)); | ||
502 | |||
503 | } | ||
504 | |||
505 | void | ||
506 | lca_clock_fiddle(int divisor) | ||
507 | { | ||
508 | long pmr_reg; | ||
509 | |||
510 | pmr_reg = LCA_READ_PMR; | ||
511 | LCA_SET_PRIMARY_CLOCK(pmr_reg, divisor); | ||
512 | /* lca_norm_clock = divisor; */ | ||
513 | LCA_WRITE_PMR(pmr_reg); | ||
514 | mb(); | ||
515 | } | ||
diff --git a/arch/alpha/kernel/core_marvel.c b/arch/alpha/kernel/core_marvel.c new file mode 100644 index 000000000000..44866cb26a80 --- /dev/null +++ b/arch/alpha/kernel/core_marvel.c | |||
@@ -0,0 +1,1154 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_marvel.c | ||
3 | * | ||
4 | * Code common to all Marvel based systems. | ||
5 | */ | ||
6 | |||
7 | #define __EXTERN_INLINE inline | ||
8 | #include <asm/io.h> | ||
9 | #include <asm/core_marvel.h> | ||
10 | #undef __EXTERN_INLINE | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/mc146818rtc.h> | ||
18 | #include <linux/rtc.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/bootmem.h> | ||
21 | |||
22 | #include <asm/ptrace.h> | ||
23 | #include <asm/smp.h> | ||
24 | #include <asm/gct.h> | ||
25 | #include <asm/pgalloc.h> | ||
26 | #include <asm/tlbflush.h> | ||
27 | #include <asm/rtc.h> | ||
28 | |||
29 | #include "proto.h" | ||
30 | #include "pci_impl.h" | ||
31 | |||
32 | |||
33 | /* | ||
34 | * Debug helpers | ||
35 | */ | ||
36 | #define DEBUG_CONFIG 0 | ||
37 | |||
38 | #if DEBUG_CONFIG | ||
39 | # define DBG_CFG(args) printk args | ||
40 | #else | ||
41 | # define DBG_CFG(args) | ||
42 | #endif | ||
43 | |||
44 | |||
45 | /* | ||
46 | * Private data | ||
47 | */ | ||
48 | static struct io7 *io7_head = NULL; | ||
49 | |||
50 | |||
51 | /* | ||
52 | * Helper functions | ||
53 | */ | ||
54 | static unsigned long __attribute__ ((unused)) | ||
55 | read_ev7_csr(int pe, unsigned long offset) | ||
56 | { | ||
57 | ev7_csr *ev7csr = EV7_CSR_KERN(pe, offset); | ||
58 | unsigned long q; | ||
59 | |||
60 | mb(); | ||
61 | q = ev7csr->csr; | ||
62 | mb(); | ||
63 | |||
64 | return q; | ||
65 | } | ||
66 | |||
67 | static void __attribute__ ((unused)) | ||
68 | write_ev7_csr(int pe, unsigned long offset, unsigned long q) | ||
69 | { | ||
70 | ev7_csr *ev7csr = EV7_CSR_KERN(pe, offset); | ||
71 | |||
72 | mb(); | ||
73 | ev7csr->csr = q; | ||
74 | mb(); | ||
75 | } | ||
76 | |||
77 | static char * __init | ||
78 | mk_resource_name(int pe, int port, char *str) | ||
79 | { | ||
80 | char tmp[80]; | ||
81 | char *name; | ||
82 | |||
83 | sprintf(tmp, "PCI %s PE %d PORT %d", str, pe, port); | ||
84 | name = alloc_bootmem(strlen(tmp) + 1); | ||
85 | strcpy(name, tmp); | ||
86 | |||
87 | return name; | ||
88 | } | ||
89 | |||
90 | inline struct io7 * | ||
91 | marvel_next_io7(struct io7 *prev) | ||
92 | { | ||
93 | return (prev ? prev->next : io7_head); | ||
94 | } | ||
95 | |||
96 | struct io7 * | ||
97 | marvel_find_io7(int pe) | ||
98 | { | ||
99 | struct io7 *io7; | ||
100 | |||
101 | for (io7 = io7_head; io7 && io7->pe != pe; io7 = io7->next) | ||
102 | continue; | ||
103 | |||
104 | return io7; | ||
105 | } | ||
106 | |||
107 | static struct io7 * __init | ||
108 | alloc_io7(unsigned int pe) | ||
109 | { | ||
110 | struct io7 *io7; | ||
111 | struct io7 *insp; | ||
112 | int h; | ||
113 | |||
114 | if (marvel_find_io7(pe)) { | ||
115 | printk(KERN_WARNING "IO7 at PE %d already allocated!\n", pe); | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | io7 = alloc_bootmem(sizeof(*io7)); | ||
120 | io7->pe = pe; | ||
121 | spin_lock_init(&io7->irq_lock); | ||
122 | |||
123 | for (h = 0; h < 4; h++) { | ||
124 | io7->ports[h].io7 = io7; | ||
125 | io7->ports[h].port = h; | ||
126 | io7->ports[h].enabled = 0; /* default to disabled */ | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Insert in pe sorted order. | ||
131 | */ | ||
132 | if (NULL == io7_head) /* empty list */ | ||
133 | io7_head = io7; | ||
134 | else if (io7_head->pe > io7->pe) { /* insert at head */ | ||
135 | io7->next = io7_head; | ||
136 | io7_head = io7; | ||
137 | } else { /* insert at position */ | ||
138 | for (insp = io7_head; insp; insp = insp->next) { | ||
139 | if (insp->pe == io7->pe) { | ||
140 | printk(KERN_ERR "Too many IO7s at PE %d\n", | ||
141 | io7->pe); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | if (NULL == insp->next || | ||
146 | insp->next->pe > io7->pe) { /* insert here */ | ||
147 | io7->next = insp->next; | ||
148 | insp->next = io7; | ||
149 | break; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | if (NULL == insp) { /* couldn't insert ?!? */ | ||
154 | printk(KERN_WARNING "Failed to insert IO7 at PE %d " | ||
155 | " - adding at head of list\n", io7->pe); | ||
156 | io7->next = io7_head; | ||
157 | io7_head = io7; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | return io7; | ||
162 | } | ||
163 | |||
164 | void | ||
165 | io7_clear_errors(struct io7 *io7) | ||
166 | { | ||
167 | io7_port7_csrs *p7csrs; | ||
168 | io7_ioport_csrs *csrs; | ||
169 | int port; | ||
170 | |||
171 | |||
172 | /* | ||
173 | * First the IO ports. | ||
174 | */ | ||
175 | for (port = 0; port < 4; port++) { | ||
176 | csrs = IO7_CSRS_KERN(io7->pe, port); | ||
177 | |||
178 | csrs->POx_ERR_SUM.csr = -1UL; | ||
179 | csrs->POx_TLB_ERR.csr = -1UL; | ||
180 | csrs->POx_SPL_COMPLT.csr = -1UL; | ||
181 | csrs->POx_TRANS_SUM.csr = -1UL; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Then the common ones. | ||
186 | */ | ||
187 | p7csrs = IO7_PORT7_CSRS_KERN(io7->pe); | ||
188 | |||
189 | p7csrs->PO7_ERROR_SUM.csr = -1UL; | ||
190 | p7csrs->PO7_UNCRR_SYM.csr = -1UL; | ||
191 | p7csrs->PO7_CRRCT_SYM.csr = -1UL; | ||
192 | } | ||
193 | |||
194 | |||
195 | /* | ||
196 | * IO7 PCI, PCI/X, AGP configuration. | ||
197 | */ | ||
198 | static void __init | ||
199 | io7_init_hose(struct io7 *io7, int port) | ||
200 | { | ||
201 | static int hose_index = 0; | ||
202 | |||
203 | struct pci_controller *hose = alloc_pci_controller(); | ||
204 | struct io7_port *io7_port = &io7->ports[port]; | ||
205 | io7_ioport_csrs *csrs = IO7_CSRS_KERN(io7->pe, port); | ||
206 | int i; | ||
207 | |||
208 | hose->index = hose_index++; /* arbitrary */ | ||
209 | |||
210 | /* | ||
211 | * We don't have an isa or legacy hose, but glibc expects to be | ||
212 | * able to use the bus == 0 / dev == 0 form of the iobase syscall | ||
213 | * to determine information about the i/o system. Since XFree86 | ||
214 | * relies on glibc's determination to tell whether or not to use | ||
215 | * sparse access, we need to point the pci_isa_hose at a real hose | ||
216 | * so at least that determination is correct. | ||
217 | */ | ||
218 | if (hose->index == 0) | ||
219 | pci_isa_hose = hose; | ||
220 | |||
221 | io7_port->csrs = csrs; | ||
222 | io7_port->hose = hose; | ||
223 | hose->sysdata = io7_port; | ||
224 | |||
225 | hose->io_space = alloc_resource(); | ||
226 | hose->mem_space = alloc_resource(); | ||
227 | |||
228 | /* | ||
229 | * Base addresses for userland consumption. Since these are going | ||
230 | * to be mapped, they are pure physical addresses. | ||
231 | */ | ||
232 | hose->sparse_mem_base = hose->sparse_io_base = 0; | ||
233 | hose->dense_mem_base = IO7_MEM_PHYS(io7->pe, port); | ||
234 | hose->dense_io_base = IO7_IO_PHYS(io7->pe, port); | ||
235 | |||
236 | /* | ||
237 | * Base addresses and resource ranges for kernel consumption. | ||
238 | */ | ||
239 | hose->config_space_base = (unsigned long)IO7_CONF_KERN(io7->pe, port); | ||
240 | |||
241 | hose->io_space->start = (unsigned long)IO7_IO_KERN(io7->pe, port); | ||
242 | hose->io_space->end = hose->io_space->start + IO7_IO_SPACE - 1; | ||
243 | hose->io_space->name = mk_resource_name(io7->pe, port, "IO"); | ||
244 | hose->io_space->flags = IORESOURCE_IO; | ||
245 | |||
246 | hose->mem_space->start = (unsigned long)IO7_MEM_KERN(io7->pe, port); | ||
247 | hose->mem_space->end = hose->mem_space->start + IO7_MEM_SPACE - 1; | ||
248 | hose->mem_space->name = mk_resource_name(io7->pe, port, "MEM"); | ||
249 | hose->mem_space->flags = IORESOURCE_MEM; | ||
250 | |||
251 | if (request_resource(&ioport_resource, hose->io_space) < 0) | ||
252 | printk(KERN_ERR "Failed to request IO on hose %d\n", | ||
253 | hose->index); | ||
254 | if (request_resource(&iomem_resource, hose->mem_space) < 0) | ||
255 | printk(KERN_ERR "Failed to request MEM on hose %d\n", | ||
256 | hose->index); | ||
257 | |||
258 | /* | ||
259 | * Save the existing DMA window settings for later restoration. | ||
260 | */ | ||
261 | for (i = 0; i < 4; i++) { | ||
262 | io7_port->saved_wbase[i] = csrs->POx_WBASE[i].csr; | ||
263 | io7_port->saved_wmask[i] = csrs->POx_WMASK[i].csr; | ||
264 | io7_port->saved_tbase[i] = csrs->POx_TBASE[i].csr; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Set up the PCI to main memory translation windows. | ||
269 | * | ||
270 | * Window 0 is scatter-gather 8MB at 8MB | ||
271 | * Window 1 is direct access 1GB at 2GB | ||
272 | * Window 2 is scatter-gather (up-to) 1GB at 3GB | ||
273 | * Window 3 is disabled | ||
274 | */ | ||
275 | |||
276 | /* | ||
277 | * TBIA before modifying windows. | ||
278 | */ | ||
279 | marvel_pci_tbi(hose, 0, -1); | ||
280 | |||
281 | /* | ||
282 | * Set up window 0 for scatter-gather 8MB at 8MB. | ||
283 | */ | ||
284 | hose->sg_isa = iommu_arena_new_node(marvel_cpuid_to_nid(io7->pe), | ||
285 | hose, 0x00800000, 0x00800000, 0); | ||
286 | hose->sg_isa->align_entry = 8; /* cache line boundary */ | ||
287 | csrs->POx_WBASE[0].csr = | ||
288 | hose->sg_isa->dma_base | wbase_m_ena | wbase_m_sg; | ||
289 | csrs->POx_WMASK[0].csr = (hose->sg_isa->size - 1) & wbase_m_addr; | ||
290 | csrs->POx_TBASE[0].csr = virt_to_phys(hose->sg_isa->ptes); | ||
291 | |||
292 | /* | ||
293 | * Set up window 1 for direct-mapped 1GB at 2GB. | ||
294 | */ | ||
295 | csrs->POx_WBASE[1].csr = __direct_map_base | wbase_m_ena; | ||
296 | csrs->POx_WMASK[1].csr = (__direct_map_size - 1) & wbase_m_addr; | ||
297 | csrs->POx_TBASE[1].csr = 0; | ||
298 | |||
299 | /* | ||
300 | * Set up window 2 for scatter-gather (up-to) 1GB at 3GB. | ||
301 | */ | ||
302 | hose->sg_pci = iommu_arena_new_node(marvel_cpuid_to_nid(io7->pe), | ||
303 | hose, 0xc0000000, 0x40000000, 0); | ||
304 | hose->sg_pci->align_entry = 8; /* cache line boundary */ | ||
305 | csrs->POx_WBASE[2].csr = | ||
306 | hose->sg_pci->dma_base | wbase_m_ena | wbase_m_sg; | ||
307 | csrs->POx_WMASK[2].csr = (hose->sg_pci->size - 1) & wbase_m_addr; | ||
308 | csrs->POx_TBASE[2].csr = virt_to_phys(hose->sg_pci->ptes); | ||
309 | |||
310 | /* | ||
311 | * Disable window 3. | ||
312 | */ | ||
313 | csrs->POx_WBASE[3].csr = 0; | ||
314 | |||
315 | /* | ||
316 | * Make sure that the AGP Monster Window is disabled. | ||
317 | */ | ||
318 | csrs->POx_CTRL.csr &= ~(1UL << 61); | ||
319 | |||
320 | #if 1 | ||
321 | printk("FIXME: disabling master aborts\n"); | ||
322 | csrs->POx_MSK_HEI.csr &= ~(3UL << 14); | ||
323 | #endif | ||
324 | /* | ||
325 | * TBIA after modifying windows. | ||
326 | */ | ||
327 | marvel_pci_tbi(hose, 0, -1); | ||
328 | } | ||
329 | |||
330 | static void __init | ||
331 | marvel_init_io7(struct io7 *io7) | ||
332 | { | ||
333 | int i; | ||
334 | |||
335 | printk("Initializing IO7 at PID %d\n", io7->pe); | ||
336 | |||
337 | /* | ||
338 | * Get the Port 7 CSR pointer. | ||
339 | */ | ||
340 | io7->csrs = IO7_PORT7_CSRS_KERN(io7->pe); | ||
341 | |||
342 | /* | ||
343 | * Init this IO7's hoses. | ||
344 | */ | ||
345 | for (i = 0; i < IO7_NUM_PORTS; i++) { | ||
346 | io7_ioport_csrs *csrs = IO7_CSRS_KERN(io7->pe, i); | ||
347 | if (csrs->POx_CACHE_CTL.csr == 8) { | ||
348 | io7->ports[i].enabled = 1; | ||
349 | io7_init_hose(io7, i); | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | |||
354 | void | ||
355 | marvel_io7_present(gct6_node *node) | ||
356 | { | ||
357 | int pe; | ||
358 | |||
359 | if (node->type != GCT_TYPE_HOSE || | ||
360 | node->subtype != GCT_SUBTYPE_IO_PORT_MODULE) | ||
361 | return; | ||
362 | |||
363 | pe = (node->id >> 8) & 0xff; | ||
364 | printk("Found an IO7 at PID %d\n", pe); | ||
365 | |||
366 | alloc_io7(pe); | ||
367 | } | ||
368 | |||
369 | static void __init | ||
370 | marvel_init_vga_hose(void) | ||
371 | { | ||
372 | #ifdef CONFIG_VGA_HOSE | ||
373 | u64 *pu64 = (u64 *)((u64)hwrpb + hwrpb->ctbt_offset); | ||
374 | |||
375 | if (pu64[7] == 3) { /* TERM_TYPE == graphics */ | ||
376 | struct pci_controller *hose = NULL; | ||
377 | int h = (pu64[30] >> 24) & 0xff; /* TERM_OUT_LOC, hose # */ | ||
378 | struct io7 *io7; | ||
379 | int pid, port; | ||
380 | |||
381 | /* FIXME - encoding is going to have to change for Marvel | ||
382 | * since hose will be able to overflow a byte... | ||
383 | * need to fix this decode when the console | ||
384 | * changes its encoding | ||
385 | */ | ||
386 | printk("console graphics is on hose %d (console)\n", h); | ||
387 | |||
388 | /* | ||
389 | * The console's hose numbering is: | ||
390 | * | ||
391 | * hose<n:2>: PID | ||
392 | * hose<1:0>: PORT | ||
393 | * | ||
394 | * We need to find the hose at that pid and port | ||
395 | */ | ||
396 | pid = h >> 2; | ||
397 | port = h & 3; | ||
398 | if ((io7 = marvel_find_io7(pid))) | ||
399 | hose = io7->ports[port].hose; | ||
400 | |||
401 | if (hose) { | ||
402 | printk("Console graphics on hose %d\n", hose->index); | ||
403 | pci_vga_hose = hose; | ||
404 | } | ||
405 | } | ||
406 | #endif /* CONFIG_VGA_HOSE */ | ||
407 | } | ||
408 | |||
409 | gct6_search_struct gct_wanted_node_list[] = { | ||
410 | { GCT_TYPE_HOSE, GCT_SUBTYPE_IO_PORT_MODULE, marvel_io7_present }, | ||
411 | { 0, 0, NULL } | ||
412 | }; | ||
413 | |||
414 | /* | ||
415 | * In case the GCT is not complete, let the user specify PIDs with IO7s | ||
416 | * at boot time. Syntax is 'io7=a,b,c,...,n' where a-n are the PIDs (decimal) | ||
417 | * where IO7s are connected | ||
418 | */ | ||
419 | static int __init | ||
420 | marvel_specify_io7(char *str) | ||
421 | { | ||
422 | unsigned long pid; | ||
423 | struct io7 *io7; | ||
424 | char *pchar; | ||
425 | |||
426 | do { | ||
427 | pid = simple_strtoul(str, &pchar, 0); | ||
428 | if (pchar != str) { | ||
429 | printk("User-specified IO7 at PID %lu\n", pid); | ||
430 | io7 = alloc_io7(pid); | ||
431 | if (io7) marvel_init_io7(io7); | ||
432 | } | ||
433 | |||
434 | if (pchar == str) pchar++; | ||
435 | str = pchar; | ||
436 | } while(*str); | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | __setup("io7=", marvel_specify_io7); | ||
441 | |||
442 | void __init | ||
443 | marvel_init_arch(void) | ||
444 | { | ||
445 | struct io7 *io7; | ||
446 | |||
447 | /* With multiple PCI busses, we play with I/O as physical addrs. */ | ||
448 | ioport_resource.end = ~0UL; | ||
449 | |||
450 | /* PCI DMA Direct Mapping is 1GB at 2GB. */ | ||
451 | __direct_map_base = 0x80000000; | ||
452 | __direct_map_size = 0x40000000; | ||
453 | |||
454 | /* Parse the config tree. */ | ||
455 | gct6_find_nodes(GCT_NODE_PTR(0), gct_wanted_node_list); | ||
456 | |||
457 | /* Init the io7s. */ | ||
458 | for (io7 = NULL; NULL != (io7 = marvel_next_io7(io7)); ) | ||
459 | marvel_init_io7(io7); | ||
460 | |||
461 | /* Check for graphic console location (if any). */ | ||
462 | marvel_init_vga_hose(); | ||
463 | } | ||
464 | |||
465 | void | ||
466 | marvel_kill_arch(int mode) | ||
467 | { | ||
468 | } | ||
469 | |||
470 | |||
471 | /* | ||
472 | * PCI Configuration Space access functions | ||
473 | * | ||
474 | * Configuration space addresses have the following format: | ||
475 | * | ||
476 | * |2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
477 | * |3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
478 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
479 | * |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|R|R| | ||
480 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
481 | * | ||
482 | * n:24 reserved for hose base | ||
483 | * 23:16 bus number (8 bits = 128 possible buses) | ||
484 | * 15:11 Device number (5 bits) | ||
485 | * 10:8 function number | ||
486 | * 7:2 register number | ||
487 | * | ||
488 | * Notes: | ||
489 | * IO7 determines whether to use a type 0 or type 1 config cycle | ||
490 | * based on the bus number. Therefore the bus number must be set | ||
491 | * to 0 for the root bus on any hose. | ||
492 | * | ||
493 | * The function number selects which function of a multi-function device | ||
494 | * (e.g., SCSI and Ethernet). | ||
495 | * | ||
496 | */ | ||
497 | |||
498 | static inline unsigned long | ||
499 | build_conf_addr(struct pci_controller *hose, u8 bus, | ||
500 | unsigned int devfn, int where) | ||
501 | { | ||
502 | return (hose->config_space_base | (bus << 16) | (devfn << 8) | where); | ||
503 | } | ||
504 | |||
505 | static unsigned long | ||
506 | mk_conf_addr(struct pci_bus *pbus, unsigned int devfn, int where) | ||
507 | { | ||
508 | struct pci_controller *hose = pbus->sysdata; | ||
509 | struct io7_port *io7_port; | ||
510 | unsigned long addr = 0; | ||
511 | u8 bus = pbus->number; | ||
512 | |||
513 | if (!hose) | ||
514 | return addr; | ||
515 | |||
516 | /* Check for enabled. */ | ||
517 | io7_port = hose->sysdata; | ||
518 | if (!io7_port->enabled) | ||
519 | return addr; | ||
520 | |||
521 | if (!pbus->parent) { /* No parent means peer PCI bus. */ | ||
522 | /* Don't support idsel > 20 on primary bus. */ | ||
523 | if (devfn >= PCI_DEVFN(21, 0)) | ||
524 | return addr; | ||
525 | bus = 0; | ||
526 | } | ||
527 | |||
528 | addr = build_conf_addr(hose, bus, devfn, where); | ||
529 | |||
530 | DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
531 | return addr; | ||
532 | } | ||
533 | |||
534 | static int | ||
535 | marvel_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
536 | int size, u32 *value) | ||
537 | { | ||
538 | unsigned long addr; | ||
539 | |||
540 | if (0 == (addr = mk_conf_addr(bus, devfn, where))) | ||
541 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
542 | |||
543 | switch(size) { | ||
544 | case 1: | ||
545 | *value = __kernel_ldbu(*(vucp)addr); | ||
546 | break; | ||
547 | case 2: | ||
548 | *value = __kernel_ldwu(*(vusp)addr); | ||
549 | break; | ||
550 | case 4: | ||
551 | *value = *(vuip)addr; | ||
552 | break; | ||
553 | default: | ||
554 | return PCIBIOS_FUNC_NOT_SUPPORTED; | ||
555 | } | ||
556 | |||
557 | return PCIBIOS_SUCCESSFUL; | ||
558 | } | ||
559 | |||
560 | static int | ||
561 | marvel_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
562 | int size, u32 value) | ||
563 | { | ||
564 | unsigned long addr; | ||
565 | |||
566 | if (0 == (addr = mk_conf_addr(bus, devfn, where))) | ||
567 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
568 | |||
569 | switch (size) { | ||
570 | case 1: | ||
571 | __kernel_stb(value, *(vucp)addr); | ||
572 | mb(); | ||
573 | __kernel_ldbu(*(vucp)addr); | ||
574 | break; | ||
575 | case 2: | ||
576 | __kernel_stw(value, *(vusp)addr); | ||
577 | mb(); | ||
578 | __kernel_ldwu(*(vusp)addr); | ||
579 | break; | ||
580 | case 4: | ||
581 | *(vuip)addr = value; | ||
582 | mb(); | ||
583 | *(vuip)addr; | ||
584 | break; | ||
585 | default: | ||
586 | return PCIBIOS_FUNC_NOT_SUPPORTED; | ||
587 | } | ||
588 | |||
589 | return PCIBIOS_SUCCESSFUL; | ||
590 | } | ||
591 | |||
592 | struct pci_ops marvel_pci_ops = | ||
593 | { | ||
594 | .read = marvel_read_config, | ||
595 | .write = marvel_write_config, | ||
596 | }; | ||
597 | |||
598 | |||
599 | /* | ||
600 | * Other PCI helper functions. | ||
601 | */ | ||
602 | void | ||
603 | marvel_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
604 | { | ||
605 | io7_ioport_csrs *csrs = ((struct io7_port *)hose->sysdata)->csrs; | ||
606 | |||
607 | wmb(); | ||
608 | csrs->POx_SG_TBIA.csr = 0; | ||
609 | mb(); | ||
610 | csrs->POx_SG_TBIA.csr; | ||
611 | } | ||
612 | |||
613 | |||
614 | |||
615 | /* | ||
616 | * RTC Support | ||
617 | */ | ||
618 | struct marvel_rtc_access_info { | ||
619 | unsigned long function; | ||
620 | unsigned long index; | ||
621 | unsigned long data; | ||
622 | }; | ||
623 | |||
624 | static void | ||
625 | __marvel_access_rtc(void *info) | ||
626 | { | ||
627 | struct marvel_rtc_access_info *rtc_access = info; | ||
628 | |||
629 | register unsigned long __r0 __asm__("$0"); | ||
630 | register unsigned long __r16 __asm__("$16") = rtc_access->function; | ||
631 | register unsigned long __r17 __asm__("$17") = rtc_access->index; | ||
632 | register unsigned long __r18 __asm__("$18") = rtc_access->data; | ||
633 | |||
634 | __asm__ __volatile__( | ||
635 | "call_pal %4 # cserve rtc" | ||
636 | : "=r"(__r16), "=r"(__r17), "=r"(__r18), "=r"(__r0) | ||
637 | : "i"(PAL_cserve), "0"(__r16), "1"(__r17), "2"(__r18) | ||
638 | : "$1", "$22", "$23", "$24", "$25"); | ||
639 | |||
640 | rtc_access->data = __r0; | ||
641 | } | ||
642 | |||
643 | static u8 | ||
644 | __marvel_rtc_io(u8 b, unsigned long addr, int write) | ||
645 | { | ||
646 | static u8 index = 0; | ||
647 | |||
648 | struct marvel_rtc_access_info rtc_access; | ||
649 | u8 ret = 0; | ||
650 | |||
651 | switch(addr) { | ||
652 | case 0x70: /* RTC_PORT(0) */ | ||
653 | if (write) index = b; | ||
654 | ret = index; | ||
655 | break; | ||
656 | |||
657 | case 0x71: /* RTC_PORT(1) */ | ||
658 | rtc_access.index = index; | ||
659 | rtc_access.data = BCD_TO_BIN(b); | ||
660 | rtc_access.function = 0x48 + !write; /* GET/PUT_TOY */ | ||
661 | |||
662 | #ifdef CONFIG_SMP | ||
663 | if (smp_processor_id() != boot_cpuid) | ||
664 | smp_call_function_on_cpu(__marvel_access_rtc, | ||
665 | &rtc_access, 1, 1, | ||
666 | cpumask_of_cpu(boot_cpuid)); | ||
667 | else | ||
668 | __marvel_access_rtc(&rtc_access); | ||
669 | #else | ||
670 | __marvel_access_rtc(&rtc_access); | ||
671 | #endif | ||
672 | ret = BIN_TO_BCD(rtc_access.data); | ||
673 | break; | ||
674 | |||
675 | default: | ||
676 | printk(KERN_WARNING "Illegal RTC port %lx\n", addr); | ||
677 | break; | ||
678 | } | ||
679 | |||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | |||
684 | /* | ||
685 | * IO map support. | ||
686 | */ | ||
687 | |||
688 | #define __marvel_is_mem_vga(a) (((a) >= 0xa0000) && ((a) <= 0xc0000)) | ||
689 | |||
690 | void __iomem * | ||
691 | marvel_ioremap(unsigned long addr, unsigned long size) | ||
692 | { | ||
693 | struct pci_controller *hose; | ||
694 | unsigned long baddr, last; | ||
695 | struct vm_struct *area; | ||
696 | unsigned long vaddr; | ||
697 | unsigned long *ptes; | ||
698 | unsigned long pfn; | ||
699 | |||
700 | /* | ||
701 | * Adjust the addr. | ||
702 | */ | ||
703 | #ifdef CONFIG_VGA_HOSE | ||
704 | if (pci_vga_hose && __marvel_is_mem_vga(addr)) { | ||
705 | addr += pci_vga_hose->mem_space->start; | ||
706 | } | ||
707 | #endif | ||
708 | |||
709 | /* | ||
710 | * Find the hose. | ||
711 | */ | ||
712 | for (hose = hose_head; hose; hose = hose->next) { | ||
713 | if ((addr >> 32) == (hose->mem_space->start >> 32)) | ||
714 | break; | ||
715 | } | ||
716 | if (!hose) | ||
717 | return NULL; | ||
718 | |||
719 | /* | ||
720 | * We have the hose - calculate the bus limits. | ||
721 | */ | ||
722 | baddr = addr - hose->mem_space->start; | ||
723 | last = baddr + size - 1; | ||
724 | |||
725 | /* | ||
726 | * Is it direct-mapped? | ||
727 | */ | ||
728 | if ((baddr >= __direct_map_base) && | ||
729 | ((baddr + size - 1) < __direct_map_base + __direct_map_size)) { | ||
730 | addr = IDENT_ADDR | (baddr - __direct_map_base); | ||
731 | return (void __iomem *) addr; | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * Check the scatter-gather arena. | ||
736 | */ | ||
737 | if (hose->sg_pci && | ||
738 | baddr >= (unsigned long)hose->sg_pci->dma_base && | ||
739 | last < (unsigned long)hose->sg_pci->dma_base + hose->sg_pci->size) { | ||
740 | |||
741 | /* | ||
742 | * Adjust the limits (mappings must be page aligned) | ||
743 | */ | ||
744 | baddr -= hose->sg_pci->dma_base; | ||
745 | last -= hose->sg_pci->dma_base; | ||
746 | baddr &= PAGE_MASK; | ||
747 | size = PAGE_ALIGN(last) - baddr; | ||
748 | |||
749 | /* | ||
750 | * Map it. | ||
751 | */ | ||
752 | area = get_vm_area(size, VM_IOREMAP); | ||
753 | if (!area) | ||
754 | return NULL; | ||
755 | |||
756 | ptes = hose->sg_pci->ptes; | ||
757 | for (vaddr = (unsigned long)area->addr; | ||
758 | baddr <= last; | ||
759 | baddr += PAGE_SIZE, vaddr += PAGE_SIZE) { | ||
760 | pfn = ptes[baddr >> PAGE_SHIFT]; | ||
761 | if (!(pfn & 1)) { | ||
762 | printk("ioremap failed... pte not valid...\n"); | ||
763 | vfree(area->addr); | ||
764 | return NULL; | ||
765 | } | ||
766 | pfn >>= 1; /* make it a true pfn */ | ||
767 | |||
768 | if (__alpha_remap_area_pages(vaddr, | ||
769 | pfn << PAGE_SHIFT, | ||
770 | PAGE_SIZE, 0)) { | ||
771 | printk("FAILED to map...\n"); | ||
772 | vfree(area->addr); | ||
773 | return NULL; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | flush_tlb_all(); | ||
778 | |||
779 | vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); | ||
780 | |||
781 | return (void __iomem *) vaddr; | ||
782 | } | ||
783 | |||
784 | return NULL; | ||
785 | } | ||
786 | |||
787 | void | ||
788 | marvel_iounmap(volatile void __iomem *xaddr) | ||
789 | { | ||
790 | unsigned long addr = (unsigned long) xaddr; | ||
791 | if (addr >= VMALLOC_START) | ||
792 | vfree((void *)(PAGE_MASK & addr)); | ||
793 | } | ||
794 | |||
795 | int | ||
796 | marvel_is_mmio(const volatile void __iomem *xaddr) | ||
797 | { | ||
798 | unsigned long addr = (unsigned long) xaddr; | ||
799 | |||
800 | if (addr >= VMALLOC_START) | ||
801 | return 1; | ||
802 | else | ||
803 | return (addr & 0xFF000000UL) == 0; | ||
804 | } | ||
805 | |||
806 | #define __marvel_is_port_vga(a) \ | ||
807 | (((a) >= 0x3b0) && ((a) < 0x3e0) && ((a) != 0x3b3) && ((a) != 0x3d3)) | ||
808 | #define __marvel_is_port_kbd(a) (((a) == 0x60) || ((a) == 0x64)) | ||
809 | #define __marvel_is_port_rtc(a) (((a) == 0x70) || ((a) == 0x71)) | ||
810 | |||
811 | void __iomem *marvel_ioportmap (unsigned long addr) | ||
812 | { | ||
813 | if (__marvel_is_port_rtc (addr) || __marvel_is_port_kbd(addr)) | ||
814 | ; | ||
815 | #ifdef CONFIG_VGA_HOSE | ||
816 | else if (__marvel_is_port_vga (addr) && pci_vga_hose) | ||
817 | addr += pci_vga_hose->io_space->start; | ||
818 | #endif | ||
819 | else | ||
820 | return NULL; | ||
821 | return (void __iomem *)addr; | ||
822 | } | ||
823 | |||
824 | unsigned int | ||
825 | marvel_ioread8(void __iomem *xaddr) | ||
826 | { | ||
827 | unsigned long addr = (unsigned long) xaddr; | ||
828 | if (__marvel_is_port_kbd(addr)) | ||
829 | return 0; | ||
830 | else if (__marvel_is_port_rtc(addr)) | ||
831 | return __marvel_rtc_io(0, addr, 0); | ||
832 | else | ||
833 | return __kernel_ldbu(*(vucp)addr); | ||
834 | } | ||
835 | |||
836 | void | ||
837 | marvel_iowrite8(u8 b, void __iomem *xaddr) | ||
838 | { | ||
839 | unsigned long addr = (unsigned long) xaddr; | ||
840 | if (__marvel_is_port_kbd(addr)) | ||
841 | return; | ||
842 | else if (__marvel_is_port_rtc(addr)) | ||
843 | __marvel_rtc_io(b, addr, 1); | ||
844 | else | ||
845 | __kernel_stb(b, *(vucp)addr); | ||
846 | } | ||
847 | |||
848 | #ifndef CONFIG_ALPHA_GENERIC | ||
849 | EXPORT_SYMBOL(marvel_ioremap); | ||
850 | EXPORT_SYMBOL(marvel_iounmap); | ||
851 | EXPORT_SYMBOL(marvel_is_mmio); | ||
852 | EXPORT_SYMBOL(marvel_ioportmap); | ||
853 | EXPORT_SYMBOL(marvel_ioread8); | ||
854 | EXPORT_SYMBOL(marvel_iowrite8); | ||
855 | #endif | ||
856 | |||
857 | /* | ||
858 | * NUMA Support | ||
859 | */ | ||
860 | /********** | ||
861 | * FIXME - for now each cpu is a node by itself | ||
862 | * -- no real support for striped mode | ||
863 | ********** | ||
864 | */ | ||
865 | int | ||
866 | marvel_pa_to_nid(unsigned long pa) | ||
867 | { | ||
868 | int cpuid; | ||
869 | |||
870 | if ((pa >> 43) & 1) /* I/O */ | ||
871 | cpuid = (~(pa >> 35) & 0xff); | ||
872 | else /* mem */ | ||
873 | cpuid = ((pa >> 34) & 0x3) | ((pa >> (37 - 2)) & (0x1f << 2)); | ||
874 | |||
875 | return marvel_cpuid_to_nid(cpuid); | ||
876 | } | ||
877 | |||
878 | int | ||
879 | marvel_cpuid_to_nid(int cpuid) | ||
880 | { | ||
881 | return cpuid; | ||
882 | } | ||
883 | |||
884 | unsigned long | ||
885 | marvel_node_mem_start(int nid) | ||
886 | { | ||
887 | unsigned long pa; | ||
888 | |||
889 | pa = (nid & 0x3) | ((nid & (0x1f << 2)) << 1); | ||
890 | pa <<= 34; | ||
891 | |||
892 | return pa; | ||
893 | } | ||
894 | |||
895 | unsigned long | ||
896 | marvel_node_mem_size(int nid) | ||
897 | { | ||
898 | return 16UL * 1024 * 1024 * 1024; /* 16GB */ | ||
899 | } | ||
900 | |||
901 | |||
902 | /* | ||
903 | * AGP GART Support. | ||
904 | */ | ||
905 | #include <linux/agp_backend.h> | ||
906 | #include <asm/agp_backend.h> | ||
907 | #include <linux/slab.h> | ||
908 | #include <linux/delay.h> | ||
909 | |||
910 | struct marvel_agp_aperture { | ||
911 | struct pci_iommu_arena *arena; | ||
912 | long pg_start; | ||
913 | long pg_count; | ||
914 | }; | ||
915 | |||
916 | static int | ||
917 | marvel_agp_setup(alpha_agp_info *agp) | ||
918 | { | ||
919 | struct marvel_agp_aperture *aper; | ||
920 | |||
921 | if (!alpha_agpgart_size) | ||
922 | return -ENOMEM; | ||
923 | |||
924 | aper = kmalloc(sizeof(*aper), GFP_KERNEL); | ||
925 | if (aper == NULL) return -ENOMEM; | ||
926 | |||
927 | aper->arena = agp->hose->sg_pci; | ||
928 | aper->pg_count = alpha_agpgart_size / PAGE_SIZE; | ||
929 | aper->pg_start = iommu_reserve(aper->arena, aper->pg_count, | ||
930 | aper->pg_count - 1); | ||
931 | |||
932 | if (aper->pg_start < 0) { | ||
933 | printk(KERN_ERR "Failed to reserve AGP memory\n"); | ||
934 | kfree(aper); | ||
935 | return -ENOMEM; | ||
936 | } | ||
937 | |||
938 | agp->aperture.bus_base = | ||
939 | aper->arena->dma_base + aper->pg_start * PAGE_SIZE; | ||
940 | agp->aperture.size = aper->pg_count * PAGE_SIZE; | ||
941 | agp->aperture.sysdata = aper; | ||
942 | |||
943 | return 0; | ||
944 | } | ||
945 | |||
946 | static void | ||
947 | marvel_agp_cleanup(alpha_agp_info *agp) | ||
948 | { | ||
949 | struct marvel_agp_aperture *aper = agp->aperture.sysdata; | ||
950 | int status; | ||
951 | |||
952 | status = iommu_release(aper->arena, aper->pg_start, aper->pg_count); | ||
953 | if (status == -EBUSY) { | ||
954 | printk(KERN_WARNING | ||
955 | "Attempted to release bound AGP memory - unbinding\n"); | ||
956 | iommu_unbind(aper->arena, aper->pg_start, aper->pg_count); | ||
957 | status = iommu_release(aper->arena, aper->pg_start, | ||
958 | aper->pg_count); | ||
959 | } | ||
960 | if (status < 0) | ||
961 | printk(KERN_ERR "Failed to release AGP memory\n"); | ||
962 | |||
963 | kfree(aper); | ||
964 | kfree(agp); | ||
965 | } | ||
966 | |||
967 | static int | ||
968 | marvel_agp_configure(alpha_agp_info *agp) | ||
969 | { | ||
970 | io7_ioport_csrs *csrs = ((struct io7_port *)agp->hose->sysdata)->csrs; | ||
971 | struct io7 *io7 = ((struct io7_port *)agp->hose->sysdata)->io7; | ||
972 | unsigned int new_rate = 0; | ||
973 | unsigned long agp_pll; | ||
974 | |||
975 | /* | ||
976 | * Check the requested mode against the PLL setting. | ||
977 | * The agpgart_be code has not programmed the card yet, | ||
978 | * so we can still tweak mode here. | ||
979 | */ | ||
980 | agp_pll = io7->csrs->POx_RST[IO7_AGP_PORT].csr; | ||
981 | switch(IO7_PLL_RNGB(agp_pll)) { | ||
982 | case 0x4: /* 2x only */ | ||
983 | /* | ||
984 | * The PLL is only programmed for 2x, so adjust the | ||
985 | * rate to 2x, if necessary. | ||
986 | */ | ||
987 | if (agp->mode.bits.rate != 2) | ||
988 | new_rate = 2; | ||
989 | break; | ||
990 | |||
991 | case 0x6: /* 1x / 4x */ | ||
992 | /* | ||
993 | * The PLL is programmed for 1x or 4x. Don't go faster | ||
994 | * than requested, so if the requested rate is 2x, use 1x. | ||
995 | */ | ||
996 | if (agp->mode.bits.rate == 2) | ||
997 | new_rate = 1; | ||
998 | break; | ||
999 | |||
1000 | default: /* ??????? */ | ||
1001 | /* | ||
1002 | * Don't know what this PLL setting is, take the requested | ||
1003 | * rate, but warn the user. | ||
1004 | */ | ||
1005 | printk("%s: unknown PLL setting RNGB=%lx (PLL6_CTL=%016lx)\n", | ||
1006 | __FUNCTION__, IO7_PLL_RNGB(agp_pll), agp_pll); | ||
1007 | break; | ||
1008 | } | ||
1009 | |||
1010 | /* | ||
1011 | * Set the new rate, if necessary. | ||
1012 | */ | ||
1013 | if (new_rate) { | ||
1014 | printk("Requested AGP Rate %dX not compatible " | ||
1015 | "with PLL setting - using %dX\n", | ||
1016 | agp->mode.bits.rate, | ||
1017 | new_rate); | ||
1018 | |||
1019 | agp->mode.bits.rate = new_rate; | ||
1020 | } | ||
1021 | |||
1022 | printk("Enabling AGP on hose %d: %dX%s RQ %d\n", | ||
1023 | agp->hose->index, agp->mode.bits.rate, | ||
1024 | agp->mode.bits.sba ? " - SBA" : "", agp->mode.bits.rq); | ||
1025 | |||
1026 | csrs->AGP_CMD.csr = agp->mode.lw; | ||
1027 | |||
1028 | return 0; | ||
1029 | } | ||
1030 | |||
1031 | static int | ||
1032 | marvel_agp_bind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) | ||
1033 | { | ||
1034 | struct marvel_agp_aperture *aper = agp->aperture.sysdata; | ||
1035 | return iommu_bind(aper->arena, aper->pg_start + pg_start, | ||
1036 | mem->page_count, mem->memory); | ||
1037 | } | ||
1038 | |||
1039 | static int | ||
1040 | marvel_agp_unbind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) | ||
1041 | { | ||
1042 | struct marvel_agp_aperture *aper = agp->aperture.sysdata; | ||
1043 | return iommu_unbind(aper->arena, aper->pg_start + pg_start, | ||
1044 | mem->page_count); | ||
1045 | } | ||
1046 | |||
1047 | static unsigned long | ||
1048 | marvel_agp_translate(alpha_agp_info *agp, dma_addr_t addr) | ||
1049 | { | ||
1050 | struct marvel_agp_aperture *aper = agp->aperture.sysdata; | ||
1051 | unsigned long baddr = addr - aper->arena->dma_base; | ||
1052 | unsigned long pte; | ||
1053 | |||
1054 | if (addr < agp->aperture.bus_base || | ||
1055 | addr >= agp->aperture.bus_base + agp->aperture.size) { | ||
1056 | printk("%s: addr out of range\n", __FUNCTION__); | ||
1057 | return -EINVAL; | ||
1058 | } | ||
1059 | |||
1060 | pte = aper->arena->ptes[baddr >> PAGE_SHIFT]; | ||
1061 | if (!(pte & 1)) { | ||
1062 | printk("%s: pte not valid\n", __FUNCTION__); | ||
1063 | return -EINVAL; | ||
1064 | } | ||
1065 | return (pte >> 1) << PAGE_SHIFT; | ||
1066 | } | ||
1067 | |||
1068 | struct alpha_agp_ops marvel_agp_ops = | ||
1069 | { | ||
1070 | .setup = marvel_agp_setup, | ||
1071 | .cleanup = marvel_agp_cleanup, | ||
1072 | .configure = marvel_agp_configure, | ||
1073 | .bind = marvel_agp_bind_memory, | ||
1074 | .unbind = marvel_agp_unbind_memory, | ||
1075 | .translate = marvel_agp_translate | ||
1076 | }; | ||
1077 | |||
1078 | alpha_agp_info * | ||
1079 | marvel_agp_info(void) | ||
1080 | { | ||
1081 | struct pci_controller *hose; | ||
1082 | io7_ioport_csrs *csrs; | ||
1083 | alpha_agp_info *agp; | ||
1084 | struct io7 *io7; | ||
1085 | |||
1086 | /* | ||
1087 | * Find the first IO7 with an AGP card. | ||
1088 | * | ||
1089 | * FIXME -- there should be a better way (we want to be able to | ||
1090 | * specify and what if the agp card is not video???) | ||
1091 | */ | ||
1092 | hose = NULL; | ||
1093 | for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; ) { | ||
1094 | struct pci_controller *h; | ||
1095 | vuip addr; | ||
1096 | |||
1097 | if (!io7->ports[IO7_AGP_PORT].enabled) | ||
1098 | continue; | ||
1099 | |||
1100 | h = io7->ports[IO7_AGP_PORT].hose; | ||
1101 | addr = (vuip)build_conf_addr(h, 0, PCI_DEVFN(5, 0), 0); | ||
1102 | |||
1103 | if (*addr != 0xffffffffu) { | ||
1104 | hose = h; | ||
1105 | break; | ||
1106 | } | ||
1107 | } | ||
1108 | |||
1109 | if (!hose || !hose->sg_pci) | ||
1110 | return NULL; | ||
1111 | |||
1112 | printk("MARVEL - using hose %d as AGP\n", hose->index); | ||
1113 | |||
1114 | /* | ||
1115 | * Get the csrs from the hose. | ||
1116 | */ | ||
1117 | csrs = ((struct io7_port *)hose->sysdata)->csrs; | ||
1118 | |||
1119 | /* | ||
1120 | * Allocate the info structure. | ||
1121 | */ | ||
1122 | agp = kmalloc(sizeof(*agp), GFP_KERNEL); | ||
1123 | |||
1124 | /* | ||
1125 | * Fill it in. | ||
1126 | */ | ||
1127 | agp->hose = hose; | ||
1128 | agp->private = NULL; | ||
1129 | agp->ops = &marvel_agp_ops; | ||
1130 | |||
1131 | /* | ||
1132 | * Aperture - not configured until ops.setup(). | ||
1133 | */ | ||
1134 | agp->aperture.bus_base = 0; | ||
1135 | agp->aperture.size = 0; | ||
1136 | agp->aperture.sysdata = NULL; | ||
1137 | |||
1138 | /* | ||
1139 | * Capabilities. | ||
1140 | * | ||
1141 | * NOTE: IO7 reports through AGP_STAT that it can support a read queue | ||
1142 | * depth of 17 (rq = 0x10). It actually only supports a depth of | ||
1143 | * 16 (rq = 0xf). | ||
1144 | */ | ||
1145 | agp->capability.lw = csrs->AGP_STAT.csr; | ||
1146 | agp->capability.bits.rq = 0xf; | ||
1147 | |||
1148 | /* | ||
1149 | * Mode. | ||
1150 | */ | ||
1151 | agp->mode.lw = csrs->AGP_CMD.csr; | ||
1152 | |||
1153 | return agp; | ||
1154 | } | ||
diff --git a/arch/alpha/kernel/core_mcpcia.c b/arch/alpha/kernel/core_mcpcia.c new file mode 100644 index 000000000000..28849c894153 --- /dev/null +++ b/arch/alpha/kernel/core_mcpcia.c | |||
@@ -0,0 +1,618 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_mcpcia.c | ||
3 | * | ||
4 | * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). | ||
5 | * | ||
6 | * Code common to all MCbus-PCI Adaptor core logic chipsets | ||
7 | */ | ||
8 | |||
9 | #define __EXTERN_INLINE inline | ||
10 | #include <asm/io.h> | ||
11 | #include <asm/core_mcpcia.h> | ||
12 | #undef __EXTERN_INLINE | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/delay.h> | ||
19 | |||
20 | #include <asm/ptrace.h> | ||
21 | |||
22 | #include "proto.h" | ||
23 | #include "pci_impl.h" | ||
24 | |||
25 | /* | ||
26 | * NOTE: Herein lie back-to-back mb instructions. They are magic. | ||
27 | * One plausible explanation is that the i/o controller does not properly | ||
28 | * handle the system transaction. Another involves timing. Ho hum. | ||
29 | */ | ||
30 | |||
31 | /* | ||
32 | * BIOS32-style PCI interface: | ||
33 | */ | ||
34 | |||
35 | #define DEBUG_CFG 0 | ||
36 | |||
37 | #if DEBUG_CFG | ||
38 | # define DBG_CFG(args) printk args | ||
39 | #else | ||
40 | # define DBG_CFG(args) | ||
41 | #endif | ||
42 | |||
43 | #define MCPCIA_MAX_HOSES 4 | ||
44 | |||
45 | /* | ||
46 | * Given a bus, device, and function number, compute resulting | ||
47 | * configuration space address and setup the MCPCIA_HAXR2 register | ||
48 | * accordingly. It is therefore not safe to have concurrent | ||
49 | * invocations to configuration space access routines, but there | ||
50 | * really shouldn't be any need for this. | ||
51 | * | ||
52 | * Type 0: | ||
53 | * | ||
54 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
55 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
56 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
57 | * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| | ||
58 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
59 | * | ||
60 | * 31:11 Device select bit. | ||
61 | * 10:8 Function number | ||
62 | * 7:2 Register number | ||
63 | * | ||
64 | * Type 1: | ||
65 | * | ||
66 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
67 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
68 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
69 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
70 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
71 | * | ||
72 | * 31:24 reserved | ||
73 | * 23:16 bus number (8 bits = 128 possible buses) | ||
74 | * 15:11 Device number (5 bits) | ||
75 | * 10:8 function number | ||
76 | * 7:2 register number | ||
77 | * | ||
78 | * Notes: | ||
79 | * The function number selects which function of a multi-function device | ||
80 | * (e.g., SCSI and Ethernet). | ||
81 | * | ||
82 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
83 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
84 | * bits. | ||
85 | */ | ||
86 | |||
87 | static unsigned int | ||
88 | conf_read(unsigned long addr, unsigned char type1, | ||
89 | struct pci_controller *hose) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | unsigned long mid = MCPCIA_HOSE2MID(hose->index); | ||
93 | unsigned int stat0, value, temp, cpu; | ||
94 | |||
95 | cpu = smp_processor_id(); | ||
96 | |||
97 | local_irq_save(flags); | ||
98 | |||
99 | DBG_CFG(("conf_read(addr=0x%lx, type1=%d, hose=%d)\n", | ||
100 | addr, type1, mid)); | ||
101 | |||
102 | /* Reset status register to avoid losing errors. */ | ||
103 | stat0 = *(vuip)MCPCIA_CAP_ERR(mid); | ||
104 | *(vuip)MCPCIA_CAP_ERR(mid) = stat0; | ||
105 | mb(); | ||
106 | temp = *(vuip)MCPCIA_CAP_ERR(mid); | ||
107 | DBG_CFG(("conf_read: MCPCIA_CAP_ERR(%d) was 0x%x\n", mid, stat0)); | ||
108 | |||
109 | mb(); | ||
110 | draina(); | ||
111 | mcheck_expected(cpu) = 1; | ||
112 | mcheck_taken(cpu) = 0; | ||
113 | mcheck_extra(cpu) = mid; | ||
114 | mb(); | ||
115 | |||
116 | /* Access configuration space. */ | ||
117 | value = *((vuip)addr); | ||
118 | mb(); | ||
119 | mb(); /* magic */ | ||
120 | |||
121 | if (mcheck_taken(cpu)) { | ||
122 | mcheck_taken(cpu) = 0; | ||
123 | value = 0xffffffffU; | ||
124 | mb(); | ||
125 | } | ||
126 | mcheck_expected(cpu) = 0; | ||
127 | mb(); | ||
128 | |||
129 | DBG_CFG(("conf_read(): finished\n")); | ||
130 | |||
131 | local_irq_restore(flags); | ||
132 | return value; | ||
133 | } | ||
134 | |||
135 | static void | ||
136 | conf_write(unsigned long addr, unsigned int value, unsigned char type1, | ||
137 | struct pci_controller *hose) | ||
138 | { | ||
139 | unsigned long flags; | ||
140 | unsigned long mid = MCPCIA_HOSE2MID(hose->index); | ||
141 | unsigned int stat0, temp, cpu; | ||
142 | |||
143 | cpu = smp_processor_id(); | ||
144 | |||
145 | local_irq_save(flags); /* avoid getting hit by machine check */ | ||
146 | |||
147 | /* Reset status register to avoid losing errors. */ | ||
148 | stat0 = *(vuip)MCPCIA_CAP_ERR(mid); | ||
149 | *(vuip)MCPCIA_CAP_ERR(mid) = stat0; mb(); | ||
150 | temp = *(vuip)MCPCIA_CAP_ERR(mid); | ||
151 | DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", mid, stat0)); | ||
152 | |||
153 | draina(); | ||
154 | mcheck_expected(cpu) = 1; | ||
155 | mcheck_extra(cpu) = mid; | ||
156 | mb(); | ||
157 | |||
158 | /* Access configuration space. */ | ||
159 | *((vuip)addr) = value; | ||
160 | mb(); | ||
161 | mb(); /* magic */ | ||
162 | temp = *(vuip)MCPCIA_CAP_ERR(mid); /* read to force the write */ | ||
163 | mcheck_expected(cpu) = 0; | ||
164 | mb(); | ||
165 | |||
166 | DBG_CFG(("conf_write(): finished\n")); | ||
167 | local_irq_restore(flags); | ||
168 | } | ||
169 | |||
170 | static int | ||
171 | mk_conf_addr(struct pci_bus *pbus, unsigned int devfn, int where, | ||
172 | struct pci_controller *hose, unsigned long *pci_addr, | ||
173 | unsigned char *type1) | ||
174 | { | ||
175 | u8 bus = pbus->number; | ||
176 | unsigned long addr; | ||
177 | |||
178 | DBG_CFG(("mk_conf_addr(bus=%d,devfn=0x%x,hose=%d,where=0x%x," | ||
179 | " pci_addr=0x%p, type1=0x%p)\n", | ||
180 | bus, devfn, hose->index, where, pci_addr, type1)); | ||
181 | |||
182 | /* Type 1 configuration cycle for *ALL* busses. */ | ||
183 | *type1 = 1; | ||
184 | |||
185 | if (!pbus->parent) /* No parent means peer PCI bus. */ | ||
186 | bus = 0; | ||
187 | addr = (bus << 16) | (devfn << 8) | (where); | ||
188 | addr <<= 5; /* swizzle for SPARSE */ | ||
189 | addr |= hose->config_space_base; | ||
190 | |||
191 | *pci_addr = addr; | ||
192 | DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int | ||
197 | mcpcia_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
198 | int size, u32 *value) | ||
199 | { | ||
200 | struct pci_controller *hose = bus->sysdata; | ||
201 | unsigned long addr, w; | ||
202 | unsigned char type1; | ||
203 | |||
204 | if (mk_conf_addr(bus, devfn, where, hose, &addr, &type1)) | ||
205 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
206 | |||
207 | addr |= (size - 1) * 8; | ||
208 | w = conf_read(addr, type1, hose); | ||
209 | switch (size) { | ||
210 | case 1: | ||
211 | *value = __kernel_extbl(w, where & 3); | ||
212 | break; | ||
213 | case 2: | ||
214 | *value = __kernel_extwl(w, where & 3); | ||
215 | break; | ||
216 | case 4: | ||
217 | *value = w; | ||
218 | break; | ||
219 | } | ||
220 | return PCIBIOS_SUCCESSFUL; | ||
221 | } | ||
222 | |||
223 | static int | ||
224 | mcpcia_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
225 | int size, u32 value) | ||
226 | { | ||
227 | struct pci_controller *hose = bus->sysdata; | ||
228 | unsigned long addr; | ||
229 | unsigned char type1; | ||
230 | |||
231 | if (mk_conf_addr(bus, devfn, where, hose, &addr, &type1)) | ||
232 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
233 | |||
234 | addr |= (size - 1) * 8; | ||
235 | value = __kernel_insql(value, where & 3); | ||
236 | conf_write(addr, value, type1, hose); | ||
237 | return PCIBIOS_SUCCESSFUL; | ||
238 | } | ||
239 | |||
240 | struct pci_ops mcpcia_pci_ops = | ||
241 | { | ||
242 | .read = mcpcia_read_config, | ||
243 | .write = mcpcia_write_config, | ||
244 | }; | ||
245 | |||
246 | void | ||
247 | mcpcia_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
248 | { | ||
249 | wmb(); | ||
250 | *(vuip)MCPCIA_SG_TBIA(MCPCIA_HOSE2MID(hose->index)) = 0; | ||
251 | mb(); | ||
252 | } | ||
253 | |||
254 | static int __init | ||
255 | mcpcia_probe_hose(int h) | ||
256 | { | ||
257 | int cpu = smp_processor_id(); | ||
258 | int mid = MCPCIA_HOSE2MID(h); | ||
259 | unsigned int pci_rev; | ||
260 | |||
261 | /* Gotta be REAL careful. If hose is absent, we get an mcheck. */ | ||
262 | |||
263 | mb(); | ||
264 | mb(); | ||
265 | draina(); | ||
266 | wrmces(7); | ||
267 | |||
268 | mcheck_expected(cpu) = 2; /* indicates probing */ | ||
269 | mcheck_taken(cpu) = 0; | ||
270 | mcheck_extra(cpu) = mid; | ||
271 | mb(); | ||
272 | |||
273 | /* Access the bus revision word. */ | ||
274 | pci_rev = *(vuip)MCPCIA_REV(mid); | ||
275 | |||
276 | mb(); | ||
277 | mb(); /* magic */ | ||
278 | if (mcheck_taken(cpu)) { | ||
279 | mcheck_taken(cpu) = 0; | ||
280 | pci_rev = 0xffffffff; | ||
281 | mb(); | ||
282 | } | ||
283 | mcheck_expected(cpu) = 0; | ||
284 | mb(); | ||
285 | |||
286 | return (pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST; | ||
287 | } | ||
288 | |||
289 | static void __init | ||
290 | mcpcia_new_hose(int h) | ||
291 | { | ||
292 | struct pci_controller *hose; | ||
293 | struct resource *io, *mem, *hae_mem; | ||
294 | int mid = MCPCIA_HOSE2MID(h); | ||
295 | |||
296 | hose = alloc_pci_controller(); | ||
297 | if (h == 0) | ||
298 | pci_isa_hose = hose; | ||
299 | io = alloc_resource(); | ||
300 | mem = alloc_resource(); | ||
301 | hae_mem = alloc_resource(); | ||
302 | |||
303 | hose->io_space = io; | ||
304 | hose->mem_space = hae_mem; | ||
305 | hose->sparse_mem_base = MCPCIA_SPARSE(mid) - IDENT_ADDR; | ||
306 | hose->dense_mem_base = MCPCIA_DENSE(mid) - IDENT_ADDR; | ||
307 | hose->sparse_io_base = MCPCIA_IO(mid) - IDENT_ADDR; | ||
308 | hose->dense_io_base = 0; | ||
309 | hose->config_space_base = MCPCIA_CONF(mid); | ||
310 | hose->index = h; | ||
311 | |||
312 | io->start = MCPCIA_IO(mid) - MCPCIA_IO_BIAS; | ||
313 | io->end = io->start + 0xffff; | ||
314 | io->name = pci_io_names[h]; | ||
315 | io->flags = IORESOURCE_IO; | ||
316 | |||
317 | mem->start = MCPCIA_DENSE(mid) - MCPCIA_MEM_BIAS; | ||
318 | mem->end = mem->start + 0xffffffff; | ||
319 | mem->name = pci_mem_names[h]; | ||
320 | mem->flags = IORESOURCE_MEM; | ||
321 | |||
322 | hae_mem->start = mem->start; | ||
323 | hae_mem->end = mem->start + MCPCIA_MEM_MASK; | ||
324 | hae_mem->name = pci_hae0_name; | ||
325 | hae_mem->flags = IORESOURCE_MEM; | ||
326 | |||
327 | if (request_resource(&ioport_resource, io) < 0) | ||
328 | printk(KERN_ERR "Failed to request IO on hose %d\n", h); | ||
329 | if (request_resource(&iomem_resource, mem) < 0) | ||
330 | printk(KERN_ERR "Failed to request MEM on hose %d\n", h); | ||
331 | if (request_resource(mem, hae_mem) < 0) | ||
332 | printk(KERN_ERR "Failed to request HAE_MEM on hose %d\n", h); | ||
333 | } | ||
334 | |||
335 | static void | ||
336 | mcpcia_pci_clr_err(int mid) | ||
337 | { | ||
338 | *(vuip)MCPCIA_CAP_ERR(mid); | ||
339 | *(vuip)MCPCIA_CAP_ERR(mid) = 0xffffffff; /* Clear them all. */ | ||
340 | mb(); | ||
341 | *(vuip)MCPCIA_CAP_ERR(mid); /* Re-read for force write. */ | ||
342 | } | ||
343 | |||
344 | static void __init | ||
345 | mcpcia_startup_hose(struct pci_controller *hose) | ||
346 | { | ||
347 | int mid = MCPCIA_HOSE2MID(hose->index); | ||
348 | unsigned int tmp; | ||
349 | |||
350 | mcpcia_pci_clr_err(mid); | ||
351 | |||
352 | /* | ||
353 | * Set up error reporting. | ||
354 | */ | ||
355 | tmp = *(vuip)MCPCIA_CAP_ERR(mid); | ||
356 | tmp |= 0x0006; /* master/target abort */ | ||
357 | *(vuip)MCPCIA_CAP_ERR(mid) = tmp; | ||
358 | mb(); | ||
359 | tmp = *(vuip)MCPCIA_CAP_ERR(mid); | ||
360 | |||
361 | /* | ||
362 | * Set up the PCI->physical memory translation windows. | ||
363 | * | ||
364 | * Window 0 is scatter-gather 8MB at 8MB (for isa) | ||
365 | * Window 1 is scatter-gather (up to) 1GB at 1GB (for pci) | ||
366 | * Window 2 is direct access 2GB at 2GB | ||
367 | */ | ||
368 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); | ||
369 | hose->sg_pci = iommu_arena_new(hose, 0x40000000, | ||
370 | size_for_memory(0x40000000), 0); | ||
371 | |||
372 | __direct_map_base = 0x80000000; | ||
373 | __direct_map_size = 0x80000000; | ||
374 | |||
375 | *(vuip)MCPCIA_W0_BASE(mid) = hose->sg_isa->dma_base | 3; | ||
376 | *(vuip)MCPCIA_W0_MASK(mid) = (hose->sg_isa->size - 1) & 0xfff00000; | ||
377 | *(vuip)MCPCIA_T0_BASE(mid) = virt_to_phys(hose->sg_isa->ptes) >> 8; | ||
378 | |||
379 | *(vuip)MCPCIA_W1_BASE(mid) = hose->sg_pci->dma_base | 3; | ||
380 | *(vuip)MCPCIA_W1_MASK(mid) = (hose->sg_pci->size - 1) & 0xfff00000; | ||
381 | *(vuip)MCPCIA_T1_BASE(mid) = virt_to_phys(hose->sg_pci->ptes) >> 8; | ||
382 | |||
383 | *(vuip)MCPCIA_W2_BASE(mid) = __direct_map_base | 1; | ||
384 | *(vuip)MCPCIA_W2_MASK(mid) = (__direct_map_size - 1) & 0xfff00000; | ||
385 | *(vuip)MCPCIA_T2_BASE(mid) = 0; | ||
386 | |||
387 | *(vuip)MCPCIA_W3_BASE(mid) = 0x0; | ||
388 | |||
389 | mcpcia_pci_tbi(hose, 0, -1); | ||
390 | |||
391 | *(vuip)MCPCIA_HBASE(mid) = 0x0; | ||
392 | mb(); | ||
393 | |||
394 | *(vuip)MCPCIA_HAE_MEM(mid) = 0U; | ||
395 | mb(); | ||
396 | *(vuip)MCPCIA_HAE_MEM(mid); /* read it back. */ | ||
397 | *(vuip)MCPCIA_HAE_IO(mid) = 0; | ||
398 | mb(); | ||
399 | *(vuip)MCPCIA_HAE_IO(mid); /* read it back. */ | ||
400 | } | ||
401 | |||
402 | void __init | ||
403 | mcpcia_init_arch(void) | ||
404 | { | ||
405 | /* With multiple PCI busses, we play with I/O as physical addrs. */ | ||
406 | ioport_resource.end = ~0UL; | ||
407 | |||
408 | /* Allocate hose 0. That's the one that all the ISA junk hangs | ||
409 | off of, from which we'll be registering stuff here in a bit. | ||
410 | Other hose detection is done in mcpcia_init_hoses, which is | ||
411 | called from init_IRQ. */ | ||
412 | |||
413 | mcpcia_new_hose(0); | ||
414 | } | ||
415 | |||
416 | /* This is called from init_IRQ, since we cannot take interrupts | ||
417 | before then. Which means we cannot do this in init_arch. */ | ||
418 | |||
419 | void __init | ||
420 | mcpcia_init_hoses(void) | ||
421 | { | ||
422 | struct pci_controller *hose; | ||
423 | int hose_count; | ||
424 | int h; | ||
425 | |||
426 | /* First, find how many hoses we have. */ | ||
427 | hose_count = 0; | ||
428 | for (h = 0; h < MCPCIA_MAX_HOSES; ++h) { | ||
429 | if (mcpcia_probe_hose(h)) { | ||
430 | if (h != 0) | ||
431 | mcpcia_new_hose(h); | ||
432 | hose_count++; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | printk("mcpcia_init_hoses: found %d hoses\n", hose_count); | ||
437 | |||
438 | /* Now do init for each hose. */ | ||
439 | for (hose = hose_head; hose; hose = hose->next) | ||
440 | mcpcia_startup_hose(hose); | ||
441 | } | ||
442 | |||
443 | static void | ||
444 | mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout) | ||
445 | { | ||
446 | struct el_common_EV5_uncorrectable_mcheck *frame; | ||
447 | int i; | ||
448 | |||
449 | frame = &logout->procdata; | ||
450 | |||
451 | /* Print PAL fields */ | ||
452 | for (i = 0; i < 24; i += 2) { | ||
453 | printk(" paltmp[%d-%d] = %16lx %16lx\n", | ||
454 | i, i+1, frame->paltemp[i], frame->paltemp[i+1]); | ||
455 | } | ||
456 | for (i = 0; i < 8; i += 2) { | ||
457 | printk(" shadow[%d-%d] = %16lx %16lx\n", | ||
458 | i, i+1, frame->shadow[i], | ||
459 | frame->shadow[i+1]); | ||
460 | } | ||
461 | printk(" Addr of excepting instruction = %16lx\n", | ||
462 | frame->exc_addr); | ||
463 | printk(" Summary of arithmetic traps = %16lx\n", | ||
464 | frame->exc_sum); | ||
465 | printk(" Exception mask = %16lx\n", | ||
466 | frame->exc_mask); | ||
467 | printk(" Base address for PALcode = %16lx\n", | ||
468 | frame->pal_base); | ||
469 | printk(" Interrupt Status Reg = %16lx\n", | ||
470 | frame->isr); | ||
471 | printk(" CURRENT SETUP OF EV5 IBOX = %16lx\n", | ||
472 | frame->icsr); | ||
473 | printk(" I-CACHE Reg %s parity error = %16lx\n", | ||
474 | (frame->ic_perr_stat & 0x800L) ? | ||
475 | "Data" : "Tag", | ||
476 | frame->ic_perr_stat); | ||
477 | printk(" D-CACHE error Reg = %16lx\n", | ||
478 | frame->dc_perr_stat); | ||
479 | if (frame->dc_perr_stat & 0x2) { | ||
480 | switch (frame->dc_perr_stat & 0x03c) { | ||
481 | case 8: | ||
482 | printk(" Data error in bank 1\n"); | ||
483 | break; | ||
484 | case 4: | ||
485 | printk(" Data error in bank 0\n"); | ||
486 | break; | ||
487 | case 20: | ||
488 | printk(" Tag error in bank 1\n"); | ||
489 | break; | ||
490 | case 10: | ||
491 | printk(" Tag error in bank 0\n"); | ||
492 | break; | ||
493 | } | ||
494 | } | ||
495 | printk(" Effective VA = %16lx\n", | ||
496 | frame->va); | ||
497 | printk(" Reason for D-stream = %16lx\n", | ||
498 | frame->mm_stat); | ||
499 | printk(" EV5 SCache address = %16lx\n", | ||
500 | frame->sc_addr); | ||
501 | printk(" EV5 SCache TAG/Data parity = %16lx\n", | ||
502 | frame->sc_stat); | ||
503 | printk(" EV5 BC_TAG_ADDR = %16lx\n", | ||
504 | frame->bc_tag_addr); | ||
505 | printk(" EV5 EI_ADDR: Phys addr of Xfer = %16lx\n", | ||
506 | frame->ei_addr); | ||
507 | printk(" Fill Syndrome = %16lx\n", | ||
508 | frame->fill_syndrome); | ||
509 | printk(" EI_STAT reg = %16lx\n", | ||
510 | frame->ei_stat); | ||
511 | printk(" LD_LOCK = %16lx\n", | ||
512 | frame->ld_lock); | ||
513 | } | ||
514 | |||
515 | static void | ||
516 | mcpcia_print_system_area(unsigned long la_ptr) | ||
517 | { | ||
518 | struct el_common *frame; | ||
519 | struct pci_controller *hose; | ||
520 | |||
521 | struct IOD_subpacket { | ||
522 | unsigned long base; | ||
523 | unsigned int whoami; | ||
524 | unsigned int rsvd1; | ||
525 | unsigned int pci_rev; | ||
526 | unsigned int cap_ctrl; | ||
527 | unsigned int hae_mem; | ||
528 | unsigned int hae_io; | ||
529 | unsigned int int_ctl; | ||
530 | unsigned int int_reg; | ||
531 | unsigned int int_mask0; | ||
532 | unsigned int int_mask1; | ||
533 | unsigned int mc_err0; | ||
534 | unsigned int mc_err1; | ||
535 | unsigned int cap_err; | ||
536 | unsigned int rsvd2; | ||
537 | unsigned int pci_err1; | ||
538 | unsigned int mdpa_stat; | ||
539 | unsigned int mdpa_syn; | ||
540 | unsigned int mdpb_stat; | ||
541 | unsigned int mdpb_syn; | ||
542 | unsigned int rsvd3; | ||
543 | unsigned int rsvd4; | ||
544 | unsigned int rsvd5; | ||
545 | } *iodpp; | ||
546 | |||
547 | frame = (struct el_common *)la_ptr; | ||
548 | iodpp = (struct IOD_subpacket *) (la_ptr + frame->sys_offset); | ||
549 | |||
550 | for (hose = hose_head; hose; hose = hose->next, iodpp++) { | ||
551 | |||
552 | printk("IOD %d Register Subpacket - Bridge Base Address %16lx\n", | ||
553 | hose->index, iodpp->base); | ||
554 | printk(" WHOAMI = %8x\n", iodpp->whoami); | ||
555 | printk(" PCI_REV = %8x\n", iodpp->pci_rev); | ||
556 | printk(" CAP_CTRL = %8x\n", iodpp->cap_ctrl); | ||
557 | printk(" HAE_MEM = %8x\n", iodpp->hae_mem); | ||
558 | printk(" HAE_IO = %8x\n", iodpp->hae_io); | ||
559 | printk(" INT_CTL = %8x\n", iodpp->int_ctl); | ||
560 | printk(" INT_REG = %8x\n", iodpp->int_reg); | ||
561 | printk(" INT_MASK0 = %8x\n", iodpp->int_mask0); | ||
562 | printk(" INT_MASK1 = %8x\n", iodpp->int_mask1); | ||
563 | printk(" MC_ERR0 = %8x\n", iodpp->mc_err0); | ||
564 | printk(" MC_ERR1 = %8x\n", iodpp->mc_err1); | ||
565 | printk(" CAP_ERR = %8x\n", iodpp->cap_err); | ||
566 | printk(" PCI_ERR1 = %8x\n", iodpp->pci_err1); | ||
567 | printk(" MDPA_STAT = %8x\n", iodpp->mdpa_stat); | ||
568 | printk(" MDPA_SYN = %8x\n", iodpp->mdpa_syn); | ||
569 | printk(" MDPB_STAT = %8x\n", iodpp->mdpb_stat); | ||
570 | printk(" MDPB_SYN = %8x\n", iodpp->mdpb_syn); | ||
571 | } | ||
572 | } | ||
573 | |||
574 | void | ||
575 | mcpcia_machine_check(unsigned long vector, unsigned long la_ptr, | ||
576 | struct pt_regs * regs) | ||
577 | { | ||
578 | struct el_common *mchk_header; | ||
579 | struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout; | ||
580 | unsigned int cpu = smp_processor_id(); | ||
581 | int expected; | ||
582 | |||
583 | mchk_header = (struct el_common *)la_ptr; | ||
584 | mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr; | ||
585 | expected = mcheck_expected(cpu); | ||
586 | |||
587 | mb(); | ||
588 | mb(); /* magic */ | ||
589 | draina(); | ||
590 | |||
591 | switch (expected) { | ||
592 | case 0: | ||
593 | { | ||
594 | /* FIXME: how do we figure out which hose the | ||
595 | error was on? */ | ||
596 | struct pci_controller *hose; | ||
597 | for (hose = hose_head; hose; hose = hose->next) | ||
598 | mcpcia_pci_clr_err(MCPCIA_HOSE2MID(hose->index)); | ||
599 | break; | ||
600 | } | ||
601 | case 1: | ||
602 | mcpcia_pci_clr_err(mcheck_extra(cpu)); | ||
603 | break; | ||
604 | default: | ||
605 | /* Otherwise, we're being called from mcpcia_probe_hose | ||
606 | and there's no hose clear an error from. */ | ||
607 | break; | ||
608 | } | ||
609 | |||
610 | wrmces(0x7); | ||
611 | mb(); | ||
612 | |||
613 | process_mcheck_info(vector, la_ptr, regs, "MCPCIA", expected != 0); | ||
614 | if (!expected && vector != 0x620 && vector != 0x630) { | ||
615 | mcpcia_print_uncorrectable(mchk_logout); | ||
616 | mcpcia_print_system_area(la_ptr); | ||
617 | } | ||
618 | } | ||
diff --git a/arch/alpha/kernel/core_polaris.c b/arch/alpha/kernel/core_polaris.c new file mode 100644 index 000000000000..277674a500ff --- /dev/null +++ b/arch/alpha/kernel/core_polaris.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_polaris.c | ||
3 | * | ||
4 | * POLARIS chip-specific code | ||
5 | */ | ||
6 | |||
7 | #define __EXTERN_INLINE inline | ||
8 | #include <asm/io.h> | ||
9 | #include <asm/core_polaris.h> | ||
10 | #undef __EXTERN_INLINE | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/init.h> | ||
16 | |||
17 | #include <asm/ptrace.h> | ||
18 | |||
19 | #include "proto.h" | ||
20 | #include "pci_impl.h" | ||
21 | |||
22 | /* | ||
23 | * BIOS32-style PCI interface: | ||
24 | */ | ||
25 | |||
26 | #define DEBUG_CONFIG 0 | ||
27 | |||
28 | #if DEBUG_CONFIG | ||
29 | # define DBG_CFG(args) printk args | ||
30 | #else | ||
31 | # define DBG_CFG(args) | ||
32 | #endif | ||
33 | |||
34 | |||
35 | /* | ||
36 | * Given a bus, device, and function number, compute resulting | ||
37 | * configuration space address. This is fairly straightforward | ||
38 | * on POLARIS, since the chip itself generates Type 0 or Type 1 | ||
39 | * cycles automatically depending on the bus number (Bus 0 is | ||
40 | * hardwired to Type 0, all others are Type 1. Peer bridges | ||
41 | * are not supported). | ||
42 | * | ||
43 | * All types: | ||
44 | * | ||
45 | * 3 3 3 3|3 3 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
46 | * 9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
47 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
48 | * |1|1|1|1|1|0|0|1|1|1|1|1|1|1|1|0|B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|x|x| | ||
49 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
50 | * | ||
51 | * 23:16 bus number (8 bits = 128 possible buses) | ||
52 | * 15:11 Device number (5 bits) | ||
53 | * 10:8 function number | ||
54 | * 7:2 register number | ||
55 | * | ||
56 | * Notes: | ||
57 | * The function number selects which function of a multi-function device | ||
58 | * (e.g., scsi and ethernet). | ||
59 | * | ||
60 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
61 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
62 | * bits. | ||
63 | */ | ||
64 | |||
65 | static int | ||
66 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
67 | unsigned long *pci_addr, u8 *type1) | ||
68 | { | ||
69 | u8 bus = pbus->number; | ||
70 | |||
71 | *type1 = (bus == 0) ? 0 : 1; | ||
72 | *pci_addr = (bus << 16) | (device_fn << 8) | (where) | | ||
73 | POLARIS_DENSE_CONFIG_BASE; | ||
74 | |||
75 | DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," | ||
76 | " returning address 0x%p\n" | ||
77 | bus, device_fn, where, *pci_addr)); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int | ||
83 | polaris_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
84 | int size, u32 *value) | ||
85 | { | ||
86 | unsigned long addr; | ||
87 | unsigned char type1; | ||
88 | |||
89 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
90 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
91 | |||
92 | switch (size) { | ||
93 | case 1: | ||
94 | *value = __kernel_ldbu(*(vucp)addr); | ||
95 | break; | ||
96 | case 2: | ||
97 | *value = __kernel_ldwu(*(vusp)addr); | ||
98 | break; | ||
99 | case 4: | ||
100 | *value = *(vuip)addr; | ||
101 | break; | ||
102 | } | ||
103 | |||
104 | return PCIBIOS_SUCCESSFUL; | ||
105 | } | ||
106 | |||
107 | |||
108 | static int | ||
109 | polaris_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
110 | int size, u32 value) | ||
111 | { | ||
112 | unsigned long addr; | ||
113 | unsigned char type1; | ||
114 | |||
115 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
116 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
117 | |||
118 | switch (size) { | ||
119 | case 1: | ||
120 | __kernel_stb(value, *(vucp)addr); | ||
121 | mb(); | ||
122 | __kernel_ldbu(*(vucp)addr); | ||
123 | break; | ||
124 | case 2: | ||
125 | __kernel_stw(value, *(vusp)addr); | ||
126 | mb(); | ||
127 | __kernel_ldwu(*(vusp)addr); | ||
128 | break; | ||
129 | case 4: | ||
130 | *(vuip)addr = value; | ||
131 | mb(); | ||
132 | *(vuip)addr; | ||
133 | break; | ||
134 | } | ||
135 | |||
136 | return PCIBIOS_SUCCESSFUL; | ||
137 | } | ||
138 | |||
139 | struct pci_ops polaris_pci_ops = | ||
140 | { | ||
141 | .read = polaris_read_config, | ||
142 | .write = polaris_write_config, | ||
143 | }; | ||
144 | |||
145 | void __init | ||
146 | polaris_init_arch(void) | ||
147 | { | ||
148 | struct pci_controller *hose; | ||
149 | |||
150 | /* May need to initialize error reporting (see PCICTL0/1), but | ||
151 | * for now assume that the firmware has done the right thing | ||
152 | * already. | ||
153 | */ | ||
154 | #if 0 | ||
155 | printk("polaris_init_arch(): trusting firmware for setup\n"); | ||
156 | #endif | ||
157 | |||
158 | /* | ||
159 | * Create our single hose. | ||
160 | */ | ||
161 | |||
162 | pci_isa_hose = hose = alloc_pci_controller(); | ||
163 | hose->io_space = &ioport_resource; | ||
164 | hose->mem_space = &iomem_resource; | ||
165 | hose->index = 0; | ||
166 | |||
167 | hose->sparse_mem_base = 0; | ||
168 | hose->dense_mem_base = POLARIS_DENSE_MEM_BASE - IDENT_ADDR; | ||
169 | hose->sparse_io_base = 0; | ||
170 | hose->dense_io_base = POLARIS_DENSE_IO_BASE - IDENT_ADDR; | ||
171 | |||
172 | hose->sg_isa = hose->sg_pci = NULL; | ||
173 | |||
174 | /* The I/O window is fixed at 2G @ 2G. */ | ||
175 | __direct_map_base = 0x80000000; | ||
176 | __direct_map_size = 0x80000000; | ||
177 | } | ||
178 | |||
179 | static inline void | ||
180 | polaris_pci_clr_err(void) | ||
181 | { | ||
182 | *(vusp)POLARIS_W_STATUS; | ||
183 | /* Write 1's to settable bits to clear errors */ | ||
184 | *(vusp)POLARIS_W_STATUS = 0x7800; | ||
185 | mb(); | ||
186 | *(vusp)POLARIS_W_STATUS; | ||
187 | } | ||
188 | |||
189 | void | ||
190 | polaris_machine_check(unsigned long vector, unsigned long la_ptr, | ||
191 | struct pt_regs * regs) | ||
192 | { | ||
193 | /* Clear the error before any reporting. */ | ||
194 | mb(); | ||
195 | mb(); | ||
196 | draina(); | ||
197 | polaris_pci_clr_err(); | ||
198 | wrmces(0x7); | ||
199 | mb(); | ||
200 | |||
201 | process_mcheck_info(vector, la_ptr, regs, "POLARIS", | ||
202 | mcheck_expected(0)); | ||
203 | } | ||
diff --git a/arch/alpha/kernel/core_t2.c b/arch/alpha/kernel/core_t2.c new file mode 100644 index 000000000000..ecce09e3626a --- /dev/null +++ b/arch/alpha/kernel/core_t2.c | |||
@@ -0,0 +1,622 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_t2.c | ||
3 | * | ||
4 | * Written by Jay A Estabrook (jestabro@amt.tay1.dec.com). | ||
5 | * December 1996. | ||
6 | * | ||
7 | * based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com) | ||
8 | * | ||
9 | * Code common to all T2 core logic chips. | ||
10 | */ | ||
11 | |||
12 | #define __EXTERN_INLINE | ||
13 | #include <asm/io.h> | ||
14 | #include <asm/core_t2.h> | ||
15 | #undef __EXTERN_INLINE | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | #include <asm/ptrace.h> | ||
23 | #include <asm/delay.h> | ||
24 | |||
25 | #include "proto.h" | ||
26 | #include "pci_impl.h" | ||
27 | |||
28 | /* For dumping initial DMA window settings. */ | ||
29 | #define DEBUG_PRINT_INITIAL_SETTINGS 0 | ||
30 | |||
31 | /* For dumping final DMA window settings. */ | ||
32 | #define DEBUG_PRINT_FINAL_SETTINGS 0 | ||
33 | |||
34 | /* | ||
35 | * By default, we direct-map starting at 2GB, in order to allow the | ||
36 | * maximum size direct-map window (2GB) to match the maximum amount of | ||
37 | * memory (2GB) that can be present on SABLEs. But that limits the | ||
38 | * floppy to DMA only via the scatter/gather window set up for 8MB | ||
39 | * ISA DMA, since the maximum ISA DMA address is 2GB-1. | ||
40 | * | ||
41 | * For now, this seems a reasonable trade-off: even though most SABLEs | ||
42 | * have less than 1GB of memory, floppy usage/performance will not | ||
43 | * really be affected by forcing it to go via scatter/gather... | ||
44 | */ | ||
45 | #define T2_DIRECTMAP_2G 1 | ||
46 | |||
47 | #if T2_DIRECTMAP_2G | ||
48 | # define T2_DIRECTMAP_START 0x80000000UL | ||
49 | # define T2_DIRECTMAP_LENGTH 0x80000000UL | ||
50 | #else | ||
51 | # define T2_DIRECTMAP_START 0x40000000UL | ||
52 | # define T2_DIRECTMAP_LENGTH 0x40000000UL | ||
53 | #endif | ||
54 | |||
55 | /* The ISA scatter/gather window settings. */ | ||
56 | #define T2_ISA_SG_START 0x00800000UL | ||
57 | #define T2_ISA_SG_LENGTH 0x00800000UL | ||
58 | |||
59 | /* | ||
60 | * NOTE: Herein lie back-to-back mb instructions. They are magic. | ||
61 | * One plausible explanation is that the i/o controller does not properly | ||
62 | * handle the system transaction. Another involves timing. Ho hum. | ||
63 | */ | ||
64 | |||
65 | /* | ||
66 | * BIOS32-style PCI interface: | ||
67 | */ | ||
68 | |||
69 | #define DEBUG_CONFIG 0 | ||
70 | |||
71 | #if DEBUG_CONFIG | ||
72 | # define DBG(args) printk args | ||
73 | #else | ||
74 | # define DBG(args) | ||
75 | #endif | ||
76 | |||
77 | static volatile unsigned int t2_mcheck_any_expected; | ||
78 | static volatile unsigned int t2_mcheck_last_taken; | ||
79 | |||
80 | /* Place to save the DMA Window registers as set up by SRM | ||
81 | for restoration during shutdown. */ | ||
82 | static struct | ||
83 | { | ||
84 | struct { | ||
85 | unsigned long wbase; | ||
86 | unsigned long wmask; | ||
87 | unsigned long tbase; | ||
88 | } window[2]; | ||
89 | unsigned long hae_1; | ||
90 | unsigned long hae_2; | ||
91 | unsigned long hae_3; | ||
92 | unsigned long hae_4; | ||
93 | unsigned long hbase; | ||
94 | } t2_saved_config __attribute((common)); | ||
95 | |||
96 | /* | ||
97 | * Given a bus, device, and function number, compute resulting | ||
98 | * configuration space address and setup the T2_HAXR2 register | ||
99 | * accordingly. It is therefore not safe to have concurrent | ||
100 | * invocations to configuration space access routines, but there | ||
101 | * really shouldn't be any need for this. | ||
102 | * | ||
103 | * Type 0: | ||
104 | * | ||
105 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
106 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
107 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
108 | * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| | ||
109 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
110 | * | ||
111 | * 31:11 Device select bit. | ||
112 | * 10:8 Function number | ||
113 | * 7:2 Register number | ||
114 | * | ||
115 | * Type 1: | ||
116 | * | ||
117 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
118 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
119 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
120 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
121 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
122 | * | ||
123 | * 31:24 reserved | ||
124 | * 23:16 bus number (8 bits = 128 possible buses) | ||
125 | * 15:11 Device number (5 bits) | ||
126 | * 10:8 function number | ||
127 | * 7:2 register number | ||
128 | * | ||
129 | * Notes: | ||
130 | * The function number selects which function of a multi-function device | ||
131 | * (e.g., SCSI and Ethernet). | ||
132 | * | ||
133 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
134 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
135 | * bits. | ||
136 | */ | ||
137 | |||
138 | static int | ||
139 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
140 | unsigned long *pci_addr, unsigned char *type1) | ||
141 | { | ||
142 | unsigned long addr; | ||
143 | u8 bus = pbus->number; | ||
144 | |||
145 | DBG(("mk_conf_addr(bus=%d, dfn=0x%x, where=0x%x," | ||
146 | " addr=0x%lx, type1=0x%x)\n", | ||
147 | bus, device_fn, where, pci_addr, type1)); | ||
148 | |||
149 | if (bus == 0) { | ||
150 | int device = device_fn >> 3; | ||
151 | |||
152 | /* Type 0 configuration cycle. */ | ||
153 | |||
154 | if (device > 8) { | ||
155 | DBG(("mk_conf_addr: device (%d)>20, returning -1\n", | ||
156 | device)); | ||
157 | return -1; | ||
158 | } | ||
159 | |||
160 | *type1 = 0; | ||
161 | addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where); | ||
162 | } else { | ||
163 | /* Type 1 configuration cycle. */ | ||
164 | *type1 = 1; | ||
165 | addr = (bus << 16) | (device_fn << 8) | (where); | ||
166 | } | ||
167 | *pci_addr = addr; | ||
168 | DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * NOTE: both conf_read() and conf_write() may set HAE_3 when needing | ||
174 | * to do type1 access. This is protected by the use of spinlock IRQ | ||
175 | * primitives in the wrapper functions pci_{read,write}_config_*() | ||
176 | * defined in drivers/pci/pci.c. | ||
177 | */ | ||
178 | static unsigned int | ||
179 | conf_read(unsigned long addr, unsigned char type1) | ||
180 | { | ||
181 | unsigned int value, cpu, taken; | ||
182 | unsigned long t2_cfg = 0; | ||
183 | |||
184 | cpu = smp_processor_id(); | ||
185 | |||
186 | DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); | ||
187 | |||
188 | /* If Type1 access, must set T2 CFG. */ | ||
189 | if (type1) { | ||
190 | t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL; | ||
191 | *(vulp)T2_HAE_3 = 0x40000000UL | t2_cfg; | ||
192 | mb(); | ||
193 | } | ||
194 | mb(); | ||
195 | draina(); | ||
196 | |||
197 | mcheck_expected(cpu) = 1; | ||
198 | mcheck_taken(cpu) = 0; | ||
199 | t2_mcheck_any_expected |= (1 << cpu); | ||
200 | mb(); | ||
201 | |||
202 | /* Access configuration space. */ | ||
203 | value = *(vuip)addr; | ||
204 | mb(); | ||
205 | mb(); /* magic */ | ||
206 | |||
207 | /* Wait for possible mcheck. Also, this lets other CPUs clear | ||
208 | their mchecks as well, as they can reliably tell when | ||
209 | another CPU is in the midst of handling a real mcheck via | ||
210 | the "taken" function. */ | ||
211 | udelay(100); | ||
212 | |||
213 | if ((taken = mcheck_taken(cpu))) { | ||
214 | mcheck_taken(cpu) = 0; | ||
215 | t2_mcheck_last_taken |= (1 << cpu); | ||
216 | value = 0xffffffffU; | ||
217 | mb(); | ||
218 | } | ||
219 | mcheck_expected(cpu) = 0; | ||
220 | t2_mcheck_any_expected = 0; | ||
221 | mb(); | ||
222 | |||
223 | /* If Type1 access, must reset T2 CFG so normal IO space ops work. */ | ||
224 | if (type1) { | ||
225 | *(vulp)T2_HAE_3 = t2_cfg; | ||
226 | mb(); | ||
227 | } | ||
228 | |||
229 | return value; | ||
230 | } | ||
231 | |||
232 | static void | ||
233 | conf_write(unsigned long addr, unsigned int value, unsigned char type1) | ||
234 | { | ||
235 | unsigned int cpu, taken; | ||
236 | unsigned long t2_cfg = 0; | ||
237 | |||
238 | cpu = smp_processor_id(); | ||
239 | |||
240 | /* If Type1 access, must set T2 CFG. */ | ||
241 | if (type1) { | ||
242 | t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL; | ||
243 | *(vulp)T2_HAE_3 = t2_cfg | 0x40000000UL; | ||
244 | mb(); | ||
245 | } | ||
246 | mb(); | ||
247 | draina(); | ||
248 | |||
249 | mcheck_expected(cpu) = 1; | ||
250 | mcheck_taken(cpu) = 0; | ||
251 | t2_mcheck_any_expected |= (1 << cpu); | ||
252 | mb(); | ||
253 | |||
254 | /* Access configuration space. */ | ||
255 | *(vuip)addr = value; | ||
256 | mb(); | ||
257 | mb(); /* magic */ | ||
258 | |||
259 | /* Wait for possible mcheck. Also, this lets other CPUs clear | ||
260 | their mchecks as well, as they can reliably tell when | ||
261 | this CPU is in the midst of handling a real mcheck via | ||
262 | the "taken" function. */ | ||
263 | udelay(100); | ||
264 | |||
265 | if ((taken = mcheck_taken(cpu))) { | ||
266 | mcheck_taken(cpu) = 0; | ||
267 | t2_mcheck_last_taken |= (1 << cpu); | ||
268 | mb(); | ||
269 | } | ||
270 | mcheck_expected(cpu) = 0; | ||
271 | t2_mcheck_any_expected = 0; | ||
272 | mb(); | ||
273 | |||
274 | /* If Type1 access, must reset T2 CFG so normal IO space ops work. */ | ||
275 | if (type1) { | ||
276 | *(vulp)T2_HAE_3 = t2_cfg; | ||
277 | mb(); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | static int | ||
282 | t2_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
283 | int size, u32 *value) | ||
284 | { | ||
285 | unsigned long addr, pci_addr; | ||
286 | unsigned char type1; | ||
287 | int shift; | ||
288 | long mask; | ||
289 | |||
290 | if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) | ||
291 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
292 | |||
293 | mask = (size - 1) * 8; | ||
294 | shift = (where & 3) * 8; | ||
295 | addr = (pci_addr << 5) + mask + T2_CONF; | ||
296 | *value = conf_read(addr, type1) >> (shift); | ||
297 | return PCIBIOS_SUCCESSFUL; | ||
298 | } | ||
299 | |||
300 | static int | ||
301 | t2_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, | ||
302 | u32 value) | ||
303 | { | ||
304 | unsigned long addr, pci_addr; | ||
305 | unsigned char type1; | ||
306 | long mask; | ||
307 | |||
308 | if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) | ||
309 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
310 | |||
311 | mask = (size - 1) * 8; | ||
312 | addr = (pci_addr << 5) + mask + T2_CONF; | ||
313 | conf_write(addr, value << ((where & 3) * 8), type1); | ||
314 | return PCIBIOS_SUCCESSFUL; | ||
315 | } | ||
316 | |||
317 | struct pci_ops t2_pci_ops = | ||
318 | { | ||
319 | .read = t2_read_config, | ||
320 | .write = t2_write_config, | ||
321 | }; | ||
322 | |||
323 | static void __init | ||
324 | t2_direct_map_window1(unsigned long base, unsigned long length) | ||
325 | { | ||
326 | unsigned long temp; | ||
327 | |||
328 | __direct_map_base = base; | ||
329 | __direct_map_size = length; | ||
330 | |||
331 | temp = (base & 0xfff00000UL) | ((base + length - 1) >> 20); | ||
332 | *(vulp)T2_WBASE1 = temp | 0x80000UL; /* OR in ENABLE bit */ | ||
333 | temp = (length - 1) & 0xfff00000UL; | ||
334 | *(vulp)T2_WMASK1 = temp; | ||
335 | *(vulp)T2_TBASE1 = 0; | ||
336 | |||
337 | #if DEBUG_PRINT_FINAL_SETTINGS | ||
338 | printk("%s: setting WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", | ||
339 | __FUNCTION__, | ||
340 | *(vulp)T2_WBASE1, | ||
341 | *(vulp)T2_WMASK1, | ||
342 | *(vulp)T2_TBASE1); | ||
343 | #endif | ||
344 | } | ||
345 | |||
346 | static void __init | ||
347 | t2_sg_map_window2(struct pci_controller *hose, | ||
348 | unsigned long base, | ||
349 | unsigned long length) | ||
350 | { | ||
351 | unsigned long temp; | ||
352 | |||
353 | /* Note we can only do 1 SG window, as the other is for direct, so | ||
354 | do an ISA SG area, especially for the floppy. */ | ||
355 | hose->sg_isa = iommu_arena_new(hose, base, length, 0); | ||
356 | hose->sg_pci = NULL; | ||
357 | |||
358 | temp = (base & 0xfff00000UL) | ((base + length - 1) >> 20); | ||
359 | *(vulp)T2_WBASE2 = temp | 0xc0000UL; /* OR in ENABLE/SG bits */ | ||
360 | temp = (length - 1) & 0xfff00000UL; | ||
361 | *(vulp)T2_WMASK2 = temp; | ||
362 | *(vulp)T2_TBASE2 = virt_to_phys(hose->sg_isa->ptes) >> 1; | ||
363 | mb(); | ||
364 | |||
365 | t2_pci_tbi(hose, 0, -1); /* flush TLB all */ | ||
366 | |||
367 | #if DEBUG_PRINT_FINAL_SETTINGS | ||
368 | printk("%s: setting WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", | ||
369 | __FUNCTION__, | ||
370 | *(vulp)T2_WBASE2, | ||
371 | *(vulp)T2_WMASK2, | ||
372 | *(vulp)T2_TBASE2); | ||
373 | #endif | ||
374 | } | ||
375 | |||
376 | static void __init | ||
377 | t2_save_configuration(void) | ||
378 | { | ||
379 | #if DEBUG_PRINT_INITIAL_SETTINGS | ||
380 | printk("%s: HAE_1 was 0x%lx\n", __FUNCTION__, srm_hae); /* HW is 0 */ | ||
381 | printk("%s: HAE_2 was 0x%lx\n", __FUNCTION__, *(vulp)T2_HAE_2); | ||
382 | printk("%s: HAE_3 was 0x%lx\n", __FUNCTION__, *(vulp)T2_HAE_3); | ||
383 | printk("%s: HAE_4 was 0x%lx\n", __FUNCTION__, *(vulp)T2_HAE_4); | ||
384 | printk("%s: HBASE was 0x%lx\n", __FUNCTION__, *(vulp)T2_HBASE); | ||
385 | |||
386 | printk("%s: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", __FUNCTION__, | ||
387 | *(vulp)T2_WBASE1, *(vulp)T2_WMASK1, *(vulp)T2_TBASE1); | ||
388 | printk("%s: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", __FUNCTION__, | ||
389 | *(vulp)T2_WBASE2, *(vulp)T2_WMASK2, *(vulp)T2_TBASE2); | ||
390 | #endif | ||
391 | |||
392 | /* | ||
393 | * Save the DMA Window registers. | ||
394 | */ | ||
395 | t2_saved_config.window[0].wbase = *(vulp)T2_WBASE1; | ||
396 | t2_saved_config.window[0].wmask = *(vulp)T2_WMASK1; | ||
397 | t2_saved_config.window[0].tbase = *(vulp)T2_TBASE1; | ||
398 | t2_saved_config.window[1].wbase = *(vulp)T2_WBASE2; | ||
399 | t2_saved_config.window[1].wmask = *(vulp)T2_WMASK2; | ||
400 | t2_saved_config.window[1].tbase = *(vulp)T2_TBASE2; | ||
401 | |||
402 | t2_saved_config.hae_1 = srm_hae; /* HW is already set to 0 */ | ||
403 | t2_saved_config.hae_2 = *(vulp)T2_HAE_2; | ||
404 | t2_saved_config.hae_3 = *(vulp)T2_HAE_3; | ||
405 | t2_saved_config.hae_4 = *(vulp)T2_HAE_4; | ||
406 | t2_saved_config.hbase = *(vulp)T2_HBASE; | ||
407 | } | ||
408 | |||
409 | void __init | ||
410 | t2_init_arch(void) | ||
411 | { | ||
412 | struct pci_controller *hose; | ||
413 | unsigned long temp; | ||
414 | unsigned int i; | ||
415 | |||
416 | for (i = 0; i < NR_CPUS; i++) { | ||
417 | mcheck_expected(i) = 0; | ||
418 | mcheck_taken(i) = 0; | ||
419 | } | ||
420 | t2_mcheck_any_expected = 0; | ||
421 | t2_mcheck_last_taken = 0; | ||
422 | |||
423 | /* Enable scatter/gather TLB use. */ | ||
424 | temp = *(vulp)T2_IOCSR; | ||
425 | if (!(temp & (0x1UL << 26))) { | ||
426 | printk("t2_init_arch: enabling SG TLB, IOCSR was 0x%lx\n", | ||
427 | temp); | ||
428 | *(vulp)T2_IOCSR = temp | (0x1UL << 26); | ||
429 | mb(); | ||
430 | *(vulp)T2_IOCSR; /* read it back to make sure */ | ||
431 | } | ||
432 | |||
433 | t2_save_configuration(); | ||
434 | |||
435 | /* | ||
436 | * Create our single hose. | ||
437 | */ | ||
438 | pci_isa_hose = hose = alloc_pci_controller(); | ||
439 | hose->io_space = &ioport_resource; | ||
440 | hose->mem_space = &iomem_resource; | ||
441 | hose->index = 0; | ||
442 | |||
443 | hose->sparse_mem_base = T2_SPARSE_MEM - IDENT_ADDR; | ||
444 | hose->dense_mem_base = T2_DENSE_MEM - IDENT_ADDR; | ||
445 | hose->sparse_io_base = T2_IO - IDENT_ADDR; | ||
446 | hose->dense_io_base = 0; | ||
447 | |||
448 | /* | ||
449 | * Set up the PCI->physical memory translation windows. | ||
450 | * | ||
451 | * Window 1 is direct mapped. | ||
452 | * Window 2 is scatter/gather (for ISA). | ||
453 | */ | ||
454 | |||
455 | t2_direct_map_window1(T2_DIRECTMAP_START, T2_DIRECTMAP_LENGTH); | ||
456 | |||
457 | /* Always make an ISA DMA window. */ | ||
458 | t2_sg_map_window2(hose, T2_ISA_SG_START, T2_ISA_SG_LENGTH); | ||
459 | |||
460 | *(vulp)T2_HBASE = 0x0; /* Disable HOLES. */ | ||
461 | |||
462 | /* Zero HAE. */ | ||
463 | *(vulp)T2_HAE_1 = 0; mb(); /* Sparse MEM HAE */ | ||
464 | *(vulp)T2_HAE_2 = 0; mb(); /* Sparse I/O HAE */ | ||
465 | *(vulp)T2_HAE_3 = 0; mb(); /* Config Space HAE */ | ||
466 | |||
467 | /* | ||
468 | * We also now zero out HAE_4, the dense memory HAE, so that | ||
469 | * we need not account for its "offset" when accessing dense | ||
470 | * memory resources which we allocated in our normal way. This | ||
471 | * HAE would need to stay untouched were we to keep the SRM | ||
472 | * resource settings. | ||
473 | * | ||
474 | * Thus we can now run standard X servers on SABLE/LYNX. :-) | ||
475 | */ | ||
476 | *(vulp)T2_HAE_4 = 0; mb(); | ||
477 | } | ||
478 | |||
479 | void | ||
480 | t2_kill_arch(int mode) | ||
481 | { | ||
482 | /* | ||
483 | * Restore the DMA Window registers. | ||
484 | */ | ||
485 | *(vulp)T2_WBASE1 = t2_saved_config.window[0].wbase; | ||
486 | *(vulp)T2_WMASK1 = t2_saved_config.window[0].wmask; | ||
487 | *(vulp)T2_TBASE1 = t2_saved_config.window[0].tbase; | ||
488 | *(vulp)T2_WBASE2 = t2_saved_config.window[1].wbase; | ||
489 | *(vulp)T2_WMASK2 = t2_saved_config.window[1].wmask; | ||
490 | *(vulp)T2_TBASE2 = t2_saved_config.window[1].tbase; | ||
491 | mb(); | ||
492 | |||
493 | *(vulp)T2_HAE_1 = srm_hae; | ||
494 | *(vulp)T2_HAE_2 = t2_saved_config.hae_2; | ||
495 | *(vulp)T2_HAE_3 = t2_saved_config.hae_3; | ||
496 | *(vulp)T2_HAE_4 = t2_saved_config.hae_4; | ||
497 | *(vulp)T2_HBASE = t2_saved_config.hbase; | ||
498 | mb(); | ||
499 | *(vulp)T2_HBASE; /* READ it back to ensure WRITE occurred. */ | ||
500 | } | ||
501 | |||
502 | void | ||
503 | t2_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
504 | { | ||
505 | unsigned long t2_iocsr; | ||
506 | |||
507 | t2_iocsr = *(vulp)T2_IOCSR; | ||
508 | |||
509 | /* set the TLB Clear bit */ | ||
510 | *(vulp)T2_IOCSR = t2_iocsr | (0x1UL << 28); | ||
511 | mb(); | ||
512 | *(vulp)T2_IOCSR; /* read it back to make sure */ | ||
513 | |||
514 | /* clear the TLB Clear bit */ | ||
515 | *(vulp)T2_IOCSR = t2_iocsr & ~(0x1UL << 28); | ||
516 | mb(); | ||
517 | *(vulp)T2_IOCSR; /* read it back to make sure */ | ||
518 | } | ||
519 | |||
520 | #define SIC_SEIC (1UL << 33) /* System Event Clear */ | ||
521 | |||
522 | static void | ||
523 | t2_clear_errors(int cpu) | ||
524 | { | ||
525 | struct sable_cpu_csr *cpu_regs; | ||
526 | |||
527 | cpu_regs = (struct sable_cpu_csr *)T2_CPUn_BASE(cpu); | ||
528 | |||
529 | cpu_regs->sic &= ~SIC_SEIC; | ||
530 | |||
531 | /* Clear CPU errors. */ | ||
532 | cpu_regs->bcce |= cpu_regs->bcce; | ||
533 | cpu_regs->cbe |= cpu_regs->cbe; | ||
534 | cpu_regs->bcue |= cpu_regs->bcue; | ||
535 | cpu_regs->dter |= cpu_regs->dter; | ||
536 | |||
537 | *(vulp)T2_CERR1 |= *(vulp)T2_CERR1; | ||
538 | *(vulp)T2_PERR1 |= *(vulp)T2_PERR1; | ||
539 | |||
540 | mb(); | ||
541 | mb(); /* magic */ | ||
542 | } | ||
543 | |||
544 | /* | ||
545 | * SABLE seems to have a "broadcast" style machine check, in that all | ||
546 | * CPUs receive it. And, the issuing CPU, in the case of PCI Config | ||
547 | * space read/write faults, will also receive a second mcheck, upon | ||
548 | * lowering IPL during completion processing in pci_read_config_byte() | ||
549 | * et al. | ||
550 | * | ||
551 | * Hence all the taken/expected/any_expected/last_taken stuff... | ||
552 | */ | ||
553 | void | ||
554 | t2_machine_check(unsigned long vector, unsigned long la_ptr, | ||
555 | struct pt_regs * regs) | ||
556 | { | ||
557 | int cpu = smp_processor_id(); | ||
558 | #ifdef CONFIG_VERBOSE_MCHECK | ||
559 | struct el_common *mchk_header = (struct el_common *)la_ptr; | ||
560 | #endif | ||
561 | |||
562 | /* Clear the error before any reporting. */ | ||
563 | mb(); | ||
564 | mb(); /* magic */ | ||
565 | draina(); | ||
566 | t2_clear_errors(cpu); | ||
567 | |||
568 | /* This should not actually be done until the logout frame is | ||
569 | examined, but, since we don't do that, go on and do this... */ | ||
570 | wrmces(0x7); | ||
571 | mb(); | ||
572 | |||
573 | /* Now, do testing for the anomalous conditions. */ | ||
574 | if (!mcheck_expected(cpu) && t2_mcheck_any_expected) { | ||
575 | /* | ||
576 | * FUNKY: Received mcheck on a CPU and not | ||
577 | * expecting it, but another CPU is expecting one. | ||
578 | * | ||
579 | * Just dismiss it for now on this CPU... | ||
580 | */ | ||
581 | #ifdef CONFIG_VERBOSE_MCHECK | ||
582 | if (alpha_verbose_mcheck > 1) { | ||
583 | printk("t2_machine_check(cpu%d): any_expected 0x%x -" | ||
584 | " (assumed) spurious -" | ||
585 | " code 0x%x\n", cpu, t2_mcheck_any_expected, | ||
586 | (unsigned int)mchk_header->code); | ||
587 | } | ||
588 | #endif | ||
589 | return; | ||
590 | } | ||
591 | |||
592 | if (!mcheck_expected(cpu) && !t2_mcheck_any_expected) { | ||
593 | if (t2_mcheck_last_taken & (1 << cpu)) { | ||
594 | #ifdef CONFIG_VERBOSE_MCHECK | ||
595 | if (alpha_verbose_mcheck > 1) { | ||
596 | printk("t2_machine_check(cpu%d): last_taken 0x%x - " | ||
597 | "unexpected mcheck - code 0x%x\n", | ||
598 | cpu, t2_mcheck_last_taken, | ||
599 | (unsigned int)mchk_header->code); | ||
600 | } | ||
601 | #endif | ||
602 | t2_mcheck_last_taken = 0; | ||
603 | mb(); | ||
604 | return; | ||
605 | } else { | ||
606 | t2_mcheck_last_taken = 0; | ||
607 | mb(); | ||
608 | } | ||
609 | } | ||
610 | |||
611 | #ifdef CONFIG_VERBOSE_MCHECK | ||
612 | if (alpha_verbose_mcheck > 1) { | ||
613 | printk("%s t2_mcheck(cpu%d): last_taken 0x%x - " | ||
614 | "any_expected 0x%x - code 0x%x\n", | ||
615 | (mcheck_expected(cpu) ? "EX" : "UN"), cpu, | ||
616 | t2_mcheck_last_taken, t2_mcheck_any_expected, | ||
617 | (unsigned int)mchk_header->code); | ||
618 | } | ||
619 | #endif | ||
620 | |||
621 | process_mcheck_info(vector, la_ptr, regs, "T2", mcheck_expected(cpu)); | ||
622 | } | ||
diff --git a/arch/alpha/kernel/core_titan.c b/arch/alpha/kernel/core_titan.c new file mode 100644 index 000000000000..3662fef7db9a --- /dev/null +++ b/arch/alpha/kernel/core_titan.c | |||
@@ -0,0 +1,806 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_titan.c | ||
3 | * | ||
4 | * Code common to all TITAN core logic chips. | ||
5 | */ | ||
6 | |||
7 | #define __EXTERN_INLINE inline | ||
8 | #include <asm/io.h> | ||
9 | #include <asm/core_titan.h> | ||
10 | #undef __EXTERN_INLINE | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/pci.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | #include <linux/bootmem.h> | ||
19 | |||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/smp.h> | ||
22 | #include <asm/pgalloc.h> | ||
23 | #include <asm/tlbflush.h> | ||
24 | |||
25 | #include "proto.h" | ||
26 | #include "pci_impl.h" | ||
27 | |||
28 | /* Save Titan configuration data as the console had it set up. */ | ||
29 | |||
30 | struct | ||
31 | { | ||
32 | unsigned long wsba[4]; | ||
33 | unsigned long wsm[4]; | ||
34 | unsigned long tba[4]; | ||
35 | } saved_config[4] __attribute__((common)); | ||
36 | |||
37 | /* | ||
38 | * BIOS32-style PCI interface: | ||
39 | */ | ||
40 | |||
41 | #define DEBUG_CONFIG 0 | ||
42 | |||
43 | #if DEBUG_CONFIG | ||
44 | # define DBG_CFG(args) printk args | ||
45 | #else | ||
46 | # define DBG_CFG(args) | ||
47 | #endif | ||
48 | |||
49 | |||
50 | /* | ||
51 | * Routines to access TIG registers. | ||
52 | */ | ||
53 | static inline volatile unsigned long * | ||
54 | mk_tig_addr(int offset) | ||
55 | { | ||
56 | return (volatile unsigned long *)(TITAN_TIG_SPACE + (offset << 6)); | ||
57 | } | ||
58 | |||
59 | static inline u8 | ||
60 | titan_read_tig(int offset, u8 value) | ||
61 | { | ||
62 | volatile unsigned long *tig_addr = mk_tig_addr(offset); | ||
63 | return (u8)(*tig_addr & 0xff); | ||
64 | } | ||
65 | |||
66 | static inline void | ||
67 | titan_write_tig(int offset, u8 value) | ||
68 | { | ||
69 | volatile unsigned long *tig_addr = mk_tig_addr(offset); | ||
70 | *tig_addr = (unsigned long)value; | ||
71 | } | ||
72 | |||
73 | |||
74 | /* | ||
75 | * Given a bus, device, and function number, compute resulting | ||
76 | * configuration space address | ||
77 | * accordingly. It is therefore not safe to have concurrent | ||
78 | * invocations to configuration space access routines, but there | ||
79 | * really shouldn't be any need for this. | ||
80 | * | ||
81 | * Note that all config space accesses use Type 1 address format. | ||
82 | * | ||
83 | * Note also that type 1 is determined by non-zero bus number. | ||
84 | * | ||
85 | * Type 1: | ||
86 | * | ||
87 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
88 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
89 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
90 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
91 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
92 | * | ||
93 | * 31:24 reserved | ||
94 | * 23:16 bus number (8 bits = 128 possible buses) | ||
95 | * 15:11 Device number (5 bits) | ||
96 | * 10:8 function number | ||
97 | * 7:2 register number | ||
98 | * | ||
99 | * Notes: | ||
100 | * The function number selects which function of a multi-function device | ||
101 | * (e.g., SCSI and Ethernet). | ||
102 | * | ||
103 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
104 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
105 | * bits. | ||
106 | */ | ||
107 | |||
108 | static int | ||
109 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
110 | unsigned long *pci_addr, unsigned char *type1) | ||
111 | { | ||
112 | struct pci_controller *hose = pbus->sysdata; | ||
113 | unsigned long addr; | ||
114 | u8 bus = pbus->number; | ||
115 | |||
116 | DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " | ||
117 | "pci_addr=0x%p, type1=0x%p)\n", | ||
118 | bus, device_fn, where, pci_addr, type1)); | ||
119 | |||
120 | if (!pbus->parent) /* No parent means peer PCI bus. */ | ||
121 | bus = 0; | ||
122 | *type1 = (bus != 0); | ||
123 | |||
124 | addr = (bus << 16) | (device_fn << 8) | where; | ||
125 | addr |= hose->config_space_base; | ||
126 | |||
127 | *pci_addr = addr; | ||
128 | DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static int | ||
133 | titan_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
134 | int size, u32 *value) | ||
135 | { | ||
136 | unsigned long addr; | ||
137 | unsigned char type1; | ||
138 | |||
139 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
140 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
141 | |||
142 | switch (size) { | ||
143 | case 1: | ||
144 | *value = __kernel_ldbu(*(vucp)addr); | ||
145 | break; | ||
146 | case 2: | ||
147 | *value = __kernel_ldwu(*(vusp)addr); | ||
148 | break; | ||
149 | case 4: | ||
150 | *value = *(vuip)addr; | ||
151 | break; | ||
152 | } | ||
153 | |||
154 | return PCIBIOS_SUCCESSFUL; | ||
155 | } | ||
156 | |||
157 | static int | ||
158 | titan_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
159 | int size, u32 value) | ||
160 | { | ||
161 | unsigned long addr; | ||
162 | unsigned char type1; | ||
163 | |||
164 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
165 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
166 | |||
167 | switch (size) { | ||
168 | case 1: | ||
169 | __kernel_stb(value, *(vucp)addr); | ||
170 | mb(); | ||
171 | __kernel_ldbu(*(vucp)addr); | ||
172 | break; | ||
173 | case 2: | ||
174 | __kernel_stw(value, *(vusp)addr); | ||
175 | mb(); | ||
176 | __kernel_ldwu(*(vusp)addr); | ||
177 | break; | ||
178 | case 4: | ||
179 | *(vuip)addr = value; | ||
180 | mb(); | ||
181 | *(vuip)addr; | ||
182 | break; | ||
183 | } | ||
184 | |||
185 | return PCIBIOS_SUCCESSFUL; | ||
186 | } | ||
187 | |||
188 | struct pci_ops titan_pci_ops = | ||
189 | { | ||
190 | .read = titan_read_config, | ||
191 | .write = titan_write_config, | ||
192 | }; | ||
193 | |||
194 | |||
195 | void | ||
196 | titan_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
197 | { | ||
198 | titan_pachip *pachip = | ||
199 | (hose->index & 1) ? TITAN_pachip1 : TITAN_pachip0; | ||
200 | titan_pachip_port *port; | ||
201 | volatile unsigned long *csr; | ||
202 | unsigned long value; | ||
203 | |||
204 | /* Get the right hose. */ | ||
205 | port = &pachip->g_port; | ||
206 | if (hose->index & 2) | ||
207 | port = &pachip->a_port; | ||
208 | |||
209 | /* We can invalidate up to 8 tlb entries in a go. The flush | ||
210 | matches against <31:16> in the pci address. | ||
211 | Note that gtlbi* and atlbi* are in the same place in the g_port | ||
212 | and a_port, respectively, so the g_port offset can be used | ||
213 | even if hose is an a_port */ | ||
214 | csr = &port->port_specific.g.gtlbia.csr; | ||
215 | if (((start ^ end) & 0xffff0000) == 0) | ||
216 | csr = &port->port_specific.g.gtlbiv.csr; | ||
217 | |||
218 | /* For TBIA, it doesn't matter what value we write. For TBI, | ||
219 | it's the shifted tag bits. */ | ||
220 | value = (start & 0xffff0000) >> 12; | ||
221 | |||
222 | wmb(); | ||
223 | *csr = value; | ||
224 | mb(); | ||
225 | *csr; | ||
226 | } | ||
227 | |||
228 | static int | ||
229 | titan_query_agp(titan_pachip_port *port) | ||
230 | { | ||
231 | union TPAchipPCTL pctl; | ||
232 | |||
233 | /* set up APCTL */ | ||
234 | pctl.pctl_q_whole = port->pctl.csr; | ||
235 | |||
236 | return pctl.pctl_r_bits.apctl_v_agp_present; | ||
237 | |||
238 | } | ||
239 | |||
240 | static void __init | ||
241 | titan_init_one_pachip_port(titan_pachip_port *port, int index) | ||
242 | { | ||
243 | struct pci_controller *hose; | ||
244 | |||
245 | hose = alloc_pci_controller(); | ||
246 | if (index == 0) | ||
247 | pci_isa_hose = hose; | ||
248 | hose->io_space = alloc_resource(); | ||
249 | hose->mem_space = alloc_resource(); | ||
250 | |||
251 | /* | ||
252 | * This is for userland consumption. The 40-bit PIO bias that we | ||
253 | * use in the kernel through KSEG doesn't work in the page table | ||
254 | * based user mappings. (43-bit KSEG sign extends the physical | ||
255 | * address from bit 40 to hit the I/O bit - mapped addresses don't). | ||
256 | * So make sure we get the 43-bit PIO bias. | ||
257 | */ | ||
258 | hose->sparse_mem_base = 0; | ||
259 | hose->sparse_io_base = 0; | ||
260 | hose->dense_mem_base | ||
261 | = (TITAN_MEM(index) & 0xffffffffffUL) | 0x80000000000UL; | ||
262 | hose->dense_io_base | ||
263 | = (TITAN_IO(index) & 0xffffffffffUL) | 0x80000000000UL; | ||
264 | |||
265 | hose->config_space_base = TITAN_CONF(index); | ||
266 | hose->index = index; | ||
267 | |||
268 | hose->io_space->start = TITAN_IO(index) - TITAN_IO_BIAS; | ||
269 | hose->io_space->end = hose->io_space->start + TITAN_IO_SPACE - 1; | ||
270 | hose->io_space->name = pci_io_names[index]; | ||
271 | hose->io_space->flags = IORESOURCE_IO; | ||
272 | |||
273 | hose->mem_space->start = TITAN_MEM(index) - TITAN_MEM_BIAS; | ||
274 | hose->mem_space->end = hose->mem_space->start + 0xffffffff; | ||
275 | hose->mem_space->name = pci_mem_names[index]; | ||
276 | hose->mem_space->flags = IORESOURCE_MEM; | ||
277 | |||
278 | if (request_resource(&ioport_resource, hose->io_space) < 0) | ||
279 | printk(KERN_ERR "Failed to request IO on hose %d\n", index); | ||
280 | if (request_resource(&iomem_resource, hose->mem_space) < 0) | ||
281 | printk(KERN_ERR "Failed to request MEM on hose %d\n", index); | ||
282 | |||
283 | /* | ||
284 | * Save the existing PCI window translations. SRM will | ||
285 | * need them when we go to reboot. | ||
286 | */ | ||
287 | saved_config[index].wsba[0] = port->wsba[0].csr; | ||
288 | saved_config[index].wsm[0] = port->wsm[0].csr; | ||
289 | saved_config[index].tba[0] = port->tba[0].csr; | ||
290 | |||
291 | saved_config[index].wsba[1] = port->wsba[1].csr; | ||
292 | saved_config[index].wsm[1] = port->wsm[1].csr; | ||
293 | saved_config[index].tba[1] = port->tba[1].csr; | ||
294 | |||
295 | saved_config[index].wsba[2] = port->wsba[2].csr; | ||
296 | saved_config[index].wsm[2] = port->wsm[2].csr; | ||
297 | saved_config[index].tba[2] = port->tba[2].csr; | ||
298 | |||
299 | saved_config[index].wsba[3] = port->wsba[3].csr; | ||
300 | saved_config[index].wsm[3] = port->wsm[3].csr; | ||
301 | saved_config[index].tba[3] = port->tba[3].csr; | ||
302 | |||
303 | /* | ||
304 | * Set up the PCI to main memory translation windows. | ||
305 | * | ||
306 | * Note: Window 3 on Titan is Scatter-Gather ONLY. | ||
307 | * | ||
308 | * Window 0 is scatter-gather 8MB at 8MB (for isa) | ||
309 | * Window 1 is direct access 1GB at 2GB | ||
310 | * Window 2 is scatter-gather 1GB at 3GB | ||
311 | */ | ||
312 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); | ||
313 | hose->sg_isa->align_entry = 8; /* 64KB for ISA */ | ||
314 | |||
315 | hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x40000000, 0); | ||
316 | hose->sg_pci->align_entry = 4; /* Titan caches 4 PTEs at a time */ | ||
317 | |||
318 | port->wsba[0].csr = hose->sg_isa->dma_base | 3; | ||
319 | port->wsm[0].csr = (hose->sg_isa->size - 1) & 0xfff00000; | ||
320 | port->tba[0].csr = virt_to_phys(hose->sg_isa->ptes); | ||
321 | |||
322 | port->wsba[1].csr = __direct_map_base | 1; | ||
323 | port->wsm[1].csr = (__direct_map_size - 1) & 0xfff00000; | ||
324 | port->tba[1].csr = 0; | ||
325 | |||
326 | port->wsba[2].csr = hose->sg_pci->dma_base | 3; | ||
327 | port->wsm[2].csr = (hose->sg_pci->size - 1) & 0xfff00000; | ||
328 | port->tba[2].csr = virt_to_phys(hose->sg_pci->ptes); | ||
329 | |||
330 | port->wsba[3].csr = 0; | ||
331 | |||
332 | /* Enable the Monster Window to make DAC pci64 possible. */ | ||
333 | port->pctl.csr |= pctl_m_mwin; | ||
334 | |||
335 | /* | ||
336 | * If it's an AGP port, initialize agplastwr. | ||
337 | */ | ||
338 | if (titan_query_agp(port)) | ||
339 | port->port_specific.a.agplastwr.csr = __direct_map_base; | ||
340 | |||
341 | titan_pci_tbi(hose, 0, -1); | ||
342 | } | ||
343 | |||
344 | static void __init | ||
345 | titan_init_pachips(titan_pachip *pachip0, titan_pachip *pachip1) | ||
346 | { | ||
347 | int pchip1_present = TITAN_cchip->csc.csr & 1L<<14; | ||
348 | |||
349 | /* Init the ports in hose order... */ | ||
350 | titan_init_one_pachip_port(&pachip0->g_port, 0); /* hose 0 */ | ||
351 | if (pchip1_present) | ||
352 | titan_init_one_pachip_port(&pachip1->g_port, 1);/* hose 1 */ | ||
353 | titan_init_one_pachip_port(&pachip0->a_port, 2); /* hose 2 */ | ||
354 | if (pchip1_present) | ||
355 | titan_init_one_pachip_port(&pachip1->a_port, 3);/* hose 3 */ | ||
356 | } | ||
357 | |||
358 | static void __init | ||
359 | titan_init_vga_hose(void) | ||
360 | { | ||
361 | #ifdef CONFIG_VGA_HOSE | ||
362 | u64 *pu64 = (u64 *)((u64)hwrpb + hwrpb->ctbt_offset); | ||
363 | |||
364 | if (pu64[7] == 3) { /* TERM_TYPE == graphics */ | ||
365 | struct pci_controller *hose; | ||
366 | int h = (pu64[30] >> 24) & 0xff; /* console hose # */ | ||
367 | |||
368 | /* | ||
369 | * Our hose numbering matches the console's, so just find | ||
370 | * the right one... | ||
371 | */ | ||
372 | for (hose = hose_head; hose; hose = hose->next) { | ||
373 | if (hose->index == h) break; | ||
374 | } | ||
375 | |||
376 | if (hose) { | ||
377 | printk("Console graphics on hose %d\n", hose->index); | ||
378 | pci_vga_hose = hose; | ||
379 | } | ||
380 | } | ||
381 | #endif /* CONFIG_VGA_HOSE */ | ||
382 | } | ||
383 | |||
384 | void __init | ||
385 | titan_init_arch(void) | ||
386 | { | ||
387 | #if 0 | ||
388 | printk("%s: titan_init_arch()\n", __FUNCTION__); | ||
389 | printk("%s: CChip registers:\n", __FUNCTION__); | ||
390 | printk("%s: CSR_CSC 0x%lx\n", __FUNCTION__, TITAN_cchip->csc.csr); | ||
391 | printk("%s: CSR_MTR 0x%lx\n", __FUNCTION__, TITAN_cchip->mtr.csr); | ||
392 | printk("%s: CSR_MISC 0x%lx\n", __FUNCTION__, TITAN_cchip->misc.csr); | ||
393 | printk("%s: CSR_DIM0 0x%lx\n", __FUNCTION__, TITAN_cchip->dim0.csr); | ||
394 | printk("%s: CSR_DIM1 0x%lx\n", __FUNCTION__, TITAN_cchip->dim1.csr); | ||
395 | printk("%s: CSR_DIR0 0x%lx\n", __FUNCTION__, TITAN_cchip->dir0.csr); | ||
396 | printk("%s: CSR_DIR1 0x%lx\n", __FUNCTION__, TITAN_cchip->dir1.csr); | ||
397 | printk("%s: CSR_DRIR 0x%lx\n", __FUNCTION__, TITAN_cchip->drir.csr); | ||
398 | |||
399 | printk("%s: DChip registers:\n", __FUNCTION__); | ||
400 | printk("%s: CSR_DSC 0x%lx\n", __FUNCTION__, TITAN_dchip->dsc.csr); | ||
401 | printk("%s: CSR_STR 0x%lx\n", __FUNCTION__, TITAN_dchip->str.csr); | ||
402 | printk("%s: CSR_DREV 0x%lx\n", __FUNCTION__, TITAN_dchip->drev.csr); | ||
403 | #endif | ||
404 | |||
405 | boot_cpuid = __hard_smp_processor_id(); | ||
406 | |||
407 | /* With multiple PCI busses, we play with I/O as physical addrs. */ | ||
408 | ioport_resource.end = ~0UL; | ||
409 | |||
410 | /* PCI DMA Direct Mapping is 1GB at 2GB. */ | ||
411 | __direct_map_base = 0x80000000; | ||
412 | __direct_map_size = 0x40000000; | ||
413 | |||
414 | /* Init the PA chip(s). */ | ||
415 | titan_init_pachips(TITAN_pachip0, TITAN_pachip1); | ||
416 | |||
417 | /* Check for graphic console location (if any). */ | ||
418 | titan_init_vga_hose(); | ||
419 | } | ||
420 | |||
421 | static void | ||
422 | titan_kill_one_pachip_port(titan_pachip_port *port, int index) | ||
423 | { | ||
424 | port->wsba[0].csr = saved_config[index].wsba[0]; | ||
425 | port->wsm[0].csr = saved_config[index].wsm[0]; | ||
426 | port->tba[0].csr = saved_config[index].tba[0]; | ||
427 | |||
428 | port->wsba[1].csr = saved_config[index].wsba[1]; | ||
429 | port->wsm[1].csr = saved_config[index].wsm[1]; | ||
430 | port->tba[1].csr = saved_config[index].tba[1]; | ||
431 | |||
432 | port->wsba[2].csr = saved_config[index].wsba[2]; | ||
433 | port->wsm[2].csr = saved_config[index].wsm[2]; | ||
434 | port->tba[2].csr = saved_config[index].tba[2]; | ||
435 | |||
436 | port->wsba[3].csr = saved_config[index].wsba[3]; | ||
437 | port->wsm[3].csr = saved_config[index].wsm[3]; | ||
438 | port->tba[3].csr = saved_config[index].tba[3]; | ||
439 | } | ||
440 | |||
441 | static void | ||
442 | titan_kill_pachips(titan_pachip *pachip0, titan_pachip *pachip1) | ||
443 | { | ||
444 | int pchip1_present = TITAN_cchip->csc.csr & 1L<<14; | ||
445 | |||
446 | if (pchip1_present) { | ||
447 | titan_kill_one_pachip_port(&pachip1->g_port, 1); | ||
448 | titan_kill_one_pachip_port(&pachip1->a_port, 3); | ||
449 | } | ||
450 | titan_kill_one_pachip_port(&pachip0->g_port, 0); | ||
451 | titan_kill_one_pachip_port(&pachip0->a_port, 2); | ||
452 | } | ||
453 | |||
454 | void | ||
455 | titan_kill_arch(int mode) | ||
456 | { | ||
457 | titan_kill_pachips(TITAN_pachip0, TITAN_pachip1); | ||
458 | } | ||
459 | |||
460 | |||
461 | /* | ||
462 | * IO map support. | ||
463 | */ | ||
464 | |||
465 | void __iomem * | ||
466 | titan_ioremap(unsigned long addr, unsigned long size) | ||
467 | { | ||
468 | int h = (addr & TITAN_HOSE_MASK) >> TITAN_HOSE_SHIFT; | ||
469 | unsigned long baddr = addr & ~TITAN_HOSE_MASK; | ||
470 | unsigned long last = baddr + size - 1; | ||
471 | struct pci_controller *hose; | ||
472 | struct vm_struct *area; | ||
473 | unsigned long vaddr; | ||
474 | unsigned long *ptes; | ||
475 | unsigned long pfn; | ||
476 | |||
477 | /* | ||
478 | * Adjust the addr. | ||
479 | */ | ||
480 | #ifdef CONFIG_VGA_HOSE | ||
481 | if (pci_vga_hose && __titan_is_mem_vga(addr)) { | ||
482 | h = pci_vga_hose->index; | ||
483 | addr += pci_vga_hose->mem_space->start; | ||
484 | } | ||
485 | #endif | ||
486 | |||
487 | /* | ||
488 | * Find the hose. | ||
489 | */ | ||
490 | for (hose = hose_head; hose; hose = hose->next) | ||
491 | if (hose->index == h) | ||
492 | break; | ||
493 | if (!hose) | ||
494 | return NULL; | ||
495 | |||
496 | /* | ||
497 | * Is it direct-mapped? | ||
498 | */ | ||
499 | if ((baddr >= __direct_map_base) && | ||
500 | ((baddr + size - 1) < __direct_map_base + __direct_map_size)) { | ||
501 | vaddr = addr - __direct_map_base + TITAN_MEM_BIAS; | ||
502 | return (void __iomem *) vaddr; | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * Check the scatter-gather arena. | ||
507 | */ | ||
508 | if (hose->sg_pci && | ||
509 | baddr >= (unsigned long)hose->sg_pci->dma_base && | ||
510 | last < (unsigned long)hose->sg_pci->dma_base + hose->sg_pci->size){ | ||
511 | |||
512 | /* | ||
513 | * Adjust the limits (mappings must be page aligned) | ||
514 | */ | ||
515 | baddr -= hose->sg_pci->dma_base; | ||
516 | last -= hose->sg_pci->dma_base; | ||
517 | baddr &= PAGE_MASK; | ||
518 | size = PAGE_ALIGN(last) - baddr; | ||
519 | |||
520 | /* | ||
521 | * Map it | ||
522 | */ | ||
523 | area = get_vm_area(size, VM_IOREMAP); | ||
524 | if (!area) | ||
525 | return NULL; | ||
526 | |||
527 | ptes = hose->sg_pci->ptes; | ||
528 | for (vaddr = (unsigned long)area->addr; | ||
529 | baddr <= last; | ||
530 | baddr += PAGE_SIZE, vaddr += PAGE_SIZE) { | ||
531 | pfn = ptes[baddr >> PAGE_SHIFT]; | ||
532 | if (!(pfn & 1)) { | ||
533 | printk("ioremap failed... pte not valid...\n"); | ||
534 | vfree(area->addr); | ||
535 | return NULL; | ||
536 | } | ||
537 | pfn >>= 1; /* make it a true pfn */ | ||
538 | |||
539 | if (__alpha_remap_area_pages(vaddr, | ||
540 | pfn << PAGE_SHIFT, | ||
541 | PAGE_SIZE, 0)) { | ||
542 | printk("FAILED to map...\n"); | ||
543 | vfree(area->addr); | ||
544 | return NULL; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | flush_tlb_all(); | ||
549 | |||
550 | vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); | ||
551 | return (void __iomem *) vaddr; | ||
552 | } | ||
553 | |||
554 | return NULL; | ||
555 | } | ||
556 | |||
557 | void | ||
558 | titan_iounmap(volatile void __iomem *xaddr) | ||
559 | { | ||
560 | unsigned long addr = (unsigned long) xaddr; | ||
561 | if (addr >= VMALLOC_START) | ||
562 | vfree((void *)(PAGE_MASK & addr)); | ||
563 | } | ||
564 | |||
565 | int | ||
566 | titan_is_mmio(const volatile void __iomem *xaddr) | ||
567 | { | ||
568 | unsigned long addr = (unsigned long) xaddr; | ||
569 | |||
570 | if (addr >= VMALLOC_START) | ||
571 | return 1; | ||
572 | else | ||
573 | return (addr & 0x100000000UL) == 0; | ||
574 | } | ||
575 | |||
576 | #ifndef CONFIG_ALPHA_GENERIC | ||
577 | EXPORT_SYMBOL(titan_ioremap); | ||
578 | EXPORT_SYMBOL(titan_iounmap); | ||
579 | EXPORT_SYMBOL(titan_is_mmio); | ||
580 | #endif | ||
581 | |||
582 | /* | ||
583 | * AGP GART Support. | ||
584 | */ | ||
585 | #include <linux/agp_backend.h> | ||
586 | #include <asm/agp_backend.h> | ||
587 | #include <linux/slab.h> | ||
588 | #include <linux/delay.h> | ||
589 | |||
590 | struct titan_agp_aperture { | ||
591 | struct pci_iommu_arena *arena; | ||
592 | long pg_start; | ||
593 | long pg_count; | ||
594 | }; | ||
595 | |||
596 | static int | ||
597 | titan_agp_setup(alpha_agp_info *agp) | ||
598 | { | ||
599 | struct titan_agp_aperture *aper; | ||
600 | |||
601 | if (!alpha_agpgart_size) | ||
602 | return -ENOMEM; | ||
603 | |||
604 | aper = kmalloc(sizeof(struct titan_agp_aperture), GFP_KERNEL); | ||
605 | if (aper == NULL) | ||
606 | return -ENOMEM; | ||
607 | |||
608 | aper->arena = agp->hose->sg_pci; | ||
609 | aper->pg_count = alpha_agpgart_size / PAGE_SIZE; | ||
610 | aper->pg_start = iommu_reserve(aper->arena, aper->pg_count, | ||
611 | aper->pg_count - 1); | ||
612 | if (aper->pg_start < 0) { | ||
613 | printk(KERN_ERR "Failed to reserve AGP memory\n"); | ||
614 | kfree(aper); | ||
615 | return -ENOMEM; | ||
616 | } | ||
617 | |||
618 | agp->aperture.bus_base = | ||
619 | aper->arena->dma_base + aper->pg_start * PAGE_SIZE; | ||
620 | agp->aperture.size = aper->pg_count * PAGE_SIZE; | ||
621 | agp->aperture.sysdata = aper; | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static void | ||
627 | titan_agp_cleanup(alpha_agp_info *agp) | ||
628 | { | ||
629 | struct titan_agp_aperture *aper = agp->aperture.sysdata; | ||
630 | int status; | ||
631 | |||
632 | status = iommu_release(aper->arena, aper->pg_start, aper->pg_count); | ||
633 | if (status == -EBUSY) { | ||
634 | printk(KERN_WARNING | ||
635 | "Attempted to release bound AGP memory - unbinding\n"); | ||
636 | iommu_unbind(aper->arena, aper->pg_start, aper->pg_count); | ||
637 | status = iommu_release(aper->arena, aper->pg_start, | ||
638 | aper->pg_count); | ||
639 | } | ||
640 | if (status < 0) | ||
641 | printk(KERN_ERR "Failed to release AGP memory\n"); | ||
642 | |||
643 | kfree(aper); | ||
644 | kfree(agp); | ||
645 | } | ||
646 | |||
647 | static int | ||
648 | titan_agp_configure(alpha_agp_info *agp) | ||
649 | { | ||
650 | union TPAchipPCTL pctl; | ||
651 | titan_pachip_port *port = agp->private; | ||
652 | pctl.pctl_q_whole = port->pctl.csr; | ||
653 | |||
654 | /* Side-Band Addressing? */ | ||
655 | pctl.pctl_r_bits.apctl_v_agp_sba_en = agp->mode.bits.sba; | ||
656 | |||
657 | /* AGP Rate? */ | ||
658 | pctl.pctl_r_bits.apctl_v_agp_rate = 0; /* 1x */ | ||
659 | if (agp->mode.bits.rate & 2) | ||
660 | pctl.pctl_r_bits.apctl_v_agp_rate = 1; /* 2x */ | ||
661 | #if 0 | ||
662 | if (agp->mode.bits.rate & 4) | ||
663 | pctl.pctl_r_bits.apctl_v_agp_rate = 2; /* 4x */ | ||
664 | #endif | ||
665 | |||
666 | /* RQ Depth? */ | ||
667 | pctl.pctl_r_bits.apctl_v_agp_hp_rd = 2; | ||
668 | pctl.pctl_r_bits.apctl_v_agp_lp_rd = 7; | ||
669 | |||
670 | /* | ||
671 | * AGP Enable. | ||
672 | */ | ||
673 | pctl.pctl_r_bits.apctl_v_agp_en = agp->mode.bits.enable; | ||
674 | |||
675 | /* Tell the user. */ | ||
676 | printk("Enabling AGP: %dX%s\n", | ||
677 | 1 << pctl.pctl_r_bits.apctl_v_agp_rate, | ||
678 | pctl.pctl_r_bits.apctl_v_agp_sba_en ? " - SBA" : ""); | ||
679 | |||
680 | /* Write it. */ | ||
681 | port->pctl.csr = pctl.pctl_q_whole; | ||
682 | |||
683 | /* And wait at least 5000 66MHz cycles (per Titan spec). */ | ||
684 | udelay(100); | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int | ||
690 | titan_agp_bind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) | ||
691 | { | ||
692 | struct titan_agp_aperture *aper = agp->aperture.sysdata; | ||
693 | return iommu_bind(aper->arena, aper->pg_start + pg_start, | ||
694 | mem->page_count, mem->memory); | ||
695 | } | ||
696 | |||
697 | static int | ||
698 | titan_agp_unbind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) | ||
699 | { | ||
700 | struct titan_agp_aperture *aper = agp->aperture.sysdata; | ||
701 | return iommu_unbind(aper->arena, aper->pg_start + pg_start, | ||
702 | mem->page_count); | ||
703 | } | ||
704 | |||
705 | static unsigned long | ||
706 | titan_agp_translate(alpha_agp_info *agp, dma_addr_t addr) | ||
707 | { | ||
708 | struct titan_agp_aperture *aper = agp->aperture.sysdata; | ||
709 | unsigned long baddr = addr - aper->arena->dma_base; | ||
710 | unsigned long pte; | ||
711 | |||
712 | if (addr < agp->aperture.bus_base || | ||
713 | addr >= agp->aperture.bus_base + agp->aperture.size) { | ||
714 | printk("%s: addr out of range\n", __FUNCTION__); | ||
715 | return -EINVAL; | ||
716 | } | ||
717 | |||
718 | pte = aper->arena->ptes[baddr >> PAGE_SHIFT]; | ||
719 | if (!(pte & 1)) { | ||
720 | printk("%s: pte not valid\n", __FUNCTION__); | ||
721 | return -EINVAL; | ||
722 | } | ||
723 | |||
724 | return (pte >> 1) << PAGE_SHIFT; | ||
725 | } | ||
726 | |||
727 | struct alpha_agp_ops titan_agp_ops = | ||
728 | { | ||
729 | .setup = titan_agp_setup, | ||
730 | .cleanup = titan_agp_cleanup, | ||
731 | .configure = titan_agp_configure, | ||
732 | .bind = titan_agp_bind_memory, | ||
733 | .unbind = titan_agp_unbind_memory, | ||
734 | .translate = titan_agp_translate | ||
735 | }; | ||
736 | |||
737 | alpha_agp_info * | ||
738 | titan_agp_info(void) | ||
739 | { | ||
740 | alpha_agp_info *agp; | ||
741 | struct pci_controller *hose; | ||
742 | titan_pachip_port *port; | ||
743 | int hosenum = -1; | ||
744 | union TPAchipPCTL pctl; | ||
745 | |||
746 | /* | ||
747 | * Find the AGP port. | ||
748 | */ | ||
749 | port = &TITAN_pachip0->a_port; | ||
750 | if (titan_query_agp(port)) | ||
751 | hosenum = 2; | ||
752 | if (hosenum < 0 && | ||
753 | titan_query_agp(port = &TITAN_pachip1->a_port)) | ||
754 | hosenum = 3; | ||
755 | |||
756 | /* | ||
757 | * Find the hose the port is on. | ||
758 | */ | ||
759 | for (hose = hose_head; hose; hose = hose->next) | ||
760 | if (hose->index == hosenum) | ||
761 | break; | ||
762 | |||
763 | if (!hose || !hose->sg_pci) | ||
764 | return NULL; | ||
765 | |||
766 | /* | ||
767 | * Allocate the info structure. | ||
768 | */ | ||
769 | agp = kmalloc(sizeof(*agp), GFP_KERNEL); | ||
770 | |||
771 | /* | ||
772 | * Fill it in. | ||
773 | */ | ||
774 | agp->hose = hose; | ||
775 | agp->private = port; | ||
776 | agp->ops = &titan_agp_ops; | ||
777 | |||
778 | /* | ||
779 | * Aperture - not configured until ops.setup(). | ||
780 | * | ||
781 | * FIXME - should we go ahead and allocate it here? | ||
782 | */ | ||
783 | agp->aperture.bus_base = 0; | ||
784 | agp->aperture.size = 0; | ||
785 | agp->aperture.sysdata = NULL; | ||
786 | |||
787 | /* | ||
788 | * Capabilities. | ||
789 | */ | ||
790 | agp->capability.lw = 0; | ||
791 | agp->capability.bits.rate = 3; /* 2x, 1x */ | ||
792 | agp->capability.bits.sba = 1; | ||
793 | agp->capability.bits.rq = 7; /* 8 - 1 */ | ||
794 | |||
795 | /* | ||
796 | * Mode. | ||
797 | */ | ||
798 | pctl.pctl_q_whole = port->pctl.csr; | ||
799 | agp->mode.lw = 0; | ||
800 | agp->mode.bits.rate = 1 << pctl.pctl_r_bits.apctl_v_agp_rate; | ||
801 | agp->mode.bits.sba = pctl.pctl_r_bits.apctl_v_agp_sba_en; | ||
802 | agp->mode.bits.rq = 7; /* RQ Depth? */ | ||
803 | agp->mode.bits.enable = pctl.pctl_r_bits.apctl_v_agp_en; | ||
804 | |||
805 | return agp; | ||
806 | } | ||
diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c new file mode 100644 index 000000000000..8aa305bd6a2c --- /dev/null +++ b/arch/alpha/kernel/core_tsunami.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_tsunami.c | ||
3 | * | ||
4 | * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com). | ||
5 | * | ||
6 | * Code common to all TSUNAMI core logic chips. | ||
7 | */ | ||
8 | |||
9 | #define __EXTERN_INLINE inline | ||
10 | #include <asm/io.h> | ||
11 | #include <asm/core_tsunami.h> | ||
12 | #undef __EXTERN_INLINE | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/bootmem.h> | ||
19 | |||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/smp.h> | ||
22 | |||
23 | #include "proto.h" | ||
24 | #include "pci_impl.h" | ||
25 | |||
26 | /* Save Tsunami configuration data as the console had it set up. */ | ||
27 | |||
28 | struct | ||
29 | { | ||
30 | unsigned long wsba[4]; | ||
31 | unsigned long wsm[4]; | ||
32 | unsigned long tba[4]; | ||
33 | } saved_config[2] __attribute__((common)); | ||
34 | |||
35 | /* | ||
36 | * NOTE: Herein lie back-to-back mb instructions. They are magic. | ||
37 | * One plausible explanation is that the I/O controller does not properly | ||
38 | * handle the system transaction. Another involves timing. Ho hum. | ||
39 | */ | ||
40 | |||
41 | /* | ||
42 | * BIOS32-style PCI interface: | ||
43 | */ | ||
44 | |||
45 | #define DEBUG_CONFIG 0 | ||
46 | |||
47 | #if DEBUG_CONFIG | ||
48 | # define DBG_CFG(args) printk args | ||
49 | #else | ||
50 | # define DBG_CFG(args) | ||
51 | #endif | ||
52 | |||
53 | |||
54 | /* | ||
55 | * Given a bus, device, and function number, compute resulting | ||
56 | * configuration space address | ||
57 | * accordingly. It is therefore not safe to have concurrent | ||
58 | * invocations to configuration space access routines, but there | ||
59 | * really shouldn't be any need for this. | ||
60 | * | ||
61 | * Note that all config space accesses use Type 1 address format. | ||
62 | * | ||
63 | * Note also that type 1 is determined by non-zero bus number. | ||
64 | * | ||
65 | * Type 1: | ||
66 | * | ||
67 | * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 | ||
68 | * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 | ||
69 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
70 | * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| | ||
71 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
72 | * | ||
73 | * 31:24 reserved | ||
74 | * 23:16 bus number (8 bits = 128 possible buses) | ||
75 | * 15:11 Device number (5 bits) | ||
76 | * 10:8 function number | ||
77 | * 7:2 register number | ||
78 | * | ||
79 | * Notes: | ||
80 | * The function number selects which function of a multi-function device | ||
81 | * (e.g., SCSI and Ethernet). | ||
82 | * | ||
83 | * The register selects a DWORD (32 bit) register offset. Hence it | ||
84 | * doesn't get shifted by 2 bits as we want to "drop" the bottom two | ||
85 | * bits. | ||
86 | */ | ||
87 | |||
88 | static int | ||
89 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
90 | unsigned long *pci_addr, unsigned char *type1) | ||
91 | { | ||
92 | struct pci_controller *hose = pbus->sysdata; | ||
93 | unsigned long addr; | ||
94 | u8 bus = pbus->number; | ||
95 | |||
96 | DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " | ||
97 | "pci_addr=0x%p, type1=0x%p)\n", | ||
98 | bus, device_fn, where, pci_addr, type1)); | ||
99 | |||
100 | if (!pbus->parent) /* No parent means peer PCI bus. */ | ||
101 | bus = 0; | ||
102 | *type1 = (bus != 0); | ||
103 | |||
104 | addr = (bus << 16) | (device_fn << 8) | where; | ||
105 | addr |= hose->config_space_base; | ||
106 | |||
107 | *pci_addr = addr; | ||
108 | DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int | ||
113 | tsunami_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
114 | int size, u32 *value) | ||
115 | { | ||
116 | unsigned long addr; | ||
117 | unsigned char type1; | ||
118 | |||
119 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
120 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
121 | |||
122 | switch (size) { | ||
123 | case 1: | ||
124 | *value = __kernel_ldbu(*(vucp)addr); | ||
125 | break; | ||
126 | case 2: | ||
127 | *value = __kernel_ldwu(*(vusp)addr); | ||
128 | break; | ||
129 | case 4: | ||
130 | *value = *(vuip)addr; | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | return PCIBIOS_SUCCESSFUL; | ||
135 | } | ||
136 | |||
137 | static int | ||
138 | tsunami_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
139 | int size, u32 value) | ||
140 | { | ||
141 | unsigned long addr; | ||
142 | unsigned char type1; | ||
143 | |||
144 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
145 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
146 | |||
147 | switch (size) { | ||
148 | case 1: | ||
149 | __kernel_stb(value, *(vucp)addr); | ||
150 | mb(); | ||
151 | __kernel_ldbu(*(vucp)addr); | ||
152 | break; | ||
153 | case 2: | ||
154 | __kernel_stw(value, *(vusp)addr); | ||
155 | mb(); | ||
156 | __kernel_ldwu(*(vusp)addr); | ||
157 | break; | ||
158 | case 4: | ||
159 | *(vuip)addr = value; | ||
160 | mb(); | ||
161 | *(vuip)addr; | ||
162 | break; | ||
163 | } | ||
164 | |||
165 | return PCIBIOS_SUCCESSFUL; | ||
166 | } | ||
167 | |||
168 | struct pci_ops tsunami_pci_ops = | ||
169 | { | ||
170 | .read = tsunami_read_config, | ||
171 | .write = tsunami_write_config, | ||
172 | }; | ||
173 | |||
174 | void | ||
175 | tsunami_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
176 | { | ||
177 | tsunami_pchip *pchip = hose->index ? TSUNAMI_pchip1 : TSUNAMI_pchip0; | ||
178 | volatile unsigned long *csr; | ||
179 | unsigned long value; | ||
180 | |||
181 | /* We can invalidate up to 8 tlb entries in a go. The flush | ||
182 | matches against <31:16> in the pci address. */ | ||
183 | csr = &pchip->tlbia.csr; | ||
184 | if (((start ^ end) & 0xffff0000) == 0) | ||
185 | csr = &pchip->tlbiv.csr; | ||
186 | |||
187 | /* For TBIA, it doesn't matter what value we write. For TBI, | ||
188 | it's the shifted tag bits. */ | ||
189 | value = (start & 0xffff0000) >> 12; | ||
190 | |||
191 | *csr = value; | ||
192 | mb(); | ||
193 | *csr; | ||
194 | } | ||
195 | |||
196 | #ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI | ||
197 | static long __init | ||
198 | tsunami_probe_read(volatile unsigned long *vaddr) | ||
199 | { | ||
200 | long dont_care, probe_result; | ||
201 | int cpu = smp_processor_id(); | ||
202 | int s = swpipl(IPL_MCHECK - 1); | ||
203 | |||
204 | mcheck_taken(cpu) = 0; | ||
205 | mcheck_expected(cpu) = 1; | ||
206 | mb(); | ||
207 | dont_care = *vaddr; | ||
208 | draina(); | ||
209 | mcheck_expected(cpu) = 0; | ||
210 | probe_result = !mcheck_taken(cpu); | ||
211 | mcheck_taken(cpu) = 0; | ||
212 | setipl(s); | ||
213 | |||
214 | printk("dont_care == 0x%lx\n", dont_care); | ||
215 | |||
216 | return probe_result; | ||
217 | } | ||
218 | |||
219 | static long __init | ||
220 | tsunami_probe_write(volatile unsigned long *vaddr) | ||
221 | { | ||
222 | long true_contents, probe_result = 1; | ||
223 | |||
224 | TSUNAMI_cchip->misc.csr |= (1L << 28); /* clear NXM... */ | ||
225 | true_contents = *vaddr; | ||
226 | *vaddr = 0; | ||
227 | draina(); | ||
228 | if (TSUNAMI_cchip->misc.csr & (1L << 28)) { | ||
229 | int source = (TSUNAMI_cchip->misc.csr >> 29) & 7; | ||
230 | TSUNAMI_cchip->misc.csr |= (1L << 28); /* ...and unlock NXS. */ | ||
231 | probe_result = 0; | ||
232 | printk("tsunami_probe_write: unit %d at 0x%016lx\n", source, | ||
233 | (unsigned long)vaddr); | ||
234 | } | ||
235 | if (probe_result) | ||
236 | *vaddr = true_contents; | ||
237 | return probe_result; | ||
238 | } | ||
239 | #else | ||
240 | #define tsunami_probe_read(ADDR) 1 | ||
241 | #endif /* NXM_MACHINE_CHECKS_ON_TSUNAMI */ | ||
242 | |||
243 | #define FN __FUNCTION__ | ||
244 | |||
245 | static void __init | ||
246 | tsunami_init_one_pchip(tsunami_pchip *pchip, int index) | ||
247 | { | ||
248 | struct pci_controller *hose; | ||
249 | |||
250 | if (tsunami_probe_read(&pchip->pctl.csr) == 0) | ||
251 | return; | ||
252 | |||
253 | hose = alloc_pci_controller(); | ||
254 | if (index == 0) | ||
255 | pci_isa_hose = hose; | ||
256 | hose->io_space = alloc_resource(); | ||
257 | hose->mem_space = alloc_resource(); | ||
258 | |||
259 | /* This is for userland consumption. For some reason, the 40-bit | ||
260 | PIO bias that we use in the kernel through KSEG didn't work for | ||
261 | the page table based user mappings. So make sure we get the | ||
262 | 43-bit PIO bias. */ | ||
263 | hose->sparse_mem_base = 0; | ||
264 | hose->sparse_io_base = 0; | ||
265 | hose->dense_mem_base | ||
266 | = (TSUNAMI_MEM(index) & 0xffffffffffL) | 0x80000000000L; | ||
267 | hose->dense_io_base | ||
268 | = (TSUNAMI_IO(index) & 0xffffffffffL) | 0x80000000000L; | ||
269 | |||
270 | hose->config_space_base = TSUNAMI_CONF(index); | ||
271 | hose->index = index; | ||
272 | |||
273 | hose->io_space->start = TSUNAMI_IO(index) - TSUNAMI_IO_BIAS; | ||
274 | hose->io_space->end = hose->io_space->start + TSUNAMI_IO_SPACE - 1; | ||
275 | hose->io_space->name = pci_io_names[index]; | ||
276 | hose->io_space->flags = IORESOURCE_IO; | ||
277 | |||
278 | hose->mem_space->start = TSUNAMI_MEM(index) - TSUNAMI_MEM_BIAS; | ||
279 | hose->mem_space->end = hose->mem_space->start + 0xffffffff; | ||
280 | hose->mem_space->name = pci_mem_names[index]; | ||
281 | hose->mem_space->flags = IORESOURCE_MEM; | ||
282 | |||
283 | if (request_resource(&ioport_resource, hose->io_space) < 0) | ||
284 | printk(KERN_ERR "Failed to request IO on hose %d\n", index); | ||
285 | if (request_resource(&iomem_resource, hose->mem_space) < 0) | ||
286 | printk(KERN_ERR "Failed to request MEM on hose %d\n", index); | ||
287 | |||
288 | /* | ||
289 | * Save the existing PCI window translations. SRM will | ||
290 | * need them when we go to reboot. | ||
291 | */ | ||
292 | |||
293 | saved_config[index].wsba[0] = pchip->wsba[0].csr; | ||
294 | saved_config[index].wsm[0] = pchip->wsm[0].csr; | ||
295 | saved_config[index].tba[0] = pchip->tba[0].csr; | ||
296 | |||
297 | saved_config[index].wsba[1] = pchip->wsba[1].csr; | ||
298 | saved_config[index].wsm[1] = pchip->wsm[1].csr; | ||
299 | saved_config[index].tba[1] = pchip->tba[1].csr; | ||
300 | |||
301 | saved_config[index].wsba[2] = pchip->wsba[2].csr; | ||
302 | saved_config[index].wsm[2] = pchip->wsm[2].csr; | ||
303 | saved_config[index].tba[2] = pchip->tba[2].csr; | ||
304 | |||
305 | saved_config[index].wsba[3] = pchip->wsba[3].csr; | ||
306 | saved_config[index].wsm[3] = pchip->wsm[3].csr; | ||
307 | saved_config[index].tba[3] = pchip->tba[3].csr; | ||
308 | |||
309 | /* | ||
310 | * Set up the PCI to main memory translation windows. | ||
311 | * | ||
312 | * Note: Window 3 is scatter-gather only | ||
313 | * | ||
314 | * Window 0 is scatter-gather 8MB at 8MB (for isa) | ||
315 | * Window 1 is scatter-gather (up to) 1GB at 1GB | ||
316 | * Window 2 is direct access 2GB at 2GB | ||
317 | * | ||
318 | * NOTE: we need the align_entry settings for Acer devices on ES40, | ||
319 | * specifically floppy and IDE when memory is larger than 2GB. | ||
320 | */ | ||
321 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); | ||
322 | /* Initially set for 4 PTEs, but will be overridden to 64K for ISA. */ | ||
323 | hose->sg_isa->align_entry = 4; | ||
324 | |||
325 | hose->sg_pci = iommu_arena_new(hose, 0x40000000, | ||
326 | size_for_memory(0x40000000), 0); | ||
327 | hose->sg_pci->align_entry = 4; /* Tsunami caches 4 PTEs at a time */ | ||
328 | |||
329 | __direct_map_base = 0x80000000; | ||
330 | __direct_map_size = 0x80000000; | ||
331 | |||
332 | pchip->wsba[0].csr = hose->sg_isa->dma_base | 3; | ||
333 | pchip->wsm[0].csr = (hose->sg_isa->size - 1) & 0xfff00000; | ||
334 | pchip->tba[0].csr = virt_to_phys(hose->sg_isa->ptes); | ||
335 | |||
336 | pchip->wsba[1].csr = hose->sg_pci->dma_base | 3; | ||
337 | pchip->wsm[1].csr = (hose->sg_pci->size - 1) & 0xfff00000; | ||
338 | pchip->tba[1].csr = virt_to_phys(hose->sg_pci->ptes); | ||
339 | |||
340 | pchip->wsba[2].csr = 0x80000000 | 1; | ||
341 | pchip->wsm[2].csr = (0x80000000 - 1) & 0xfff00000; | ||
342 | pchip->tba[2].csr = 0; | ||
343 | |||
344 | pchip->wsba[3].csr = 0; | ||
345 | |||
346 | /* Enable the Monster Window to make DAC pci64 possible. */ | ||
347 | pchip->pctl.csr |= pctl_m_mwin; | ||
348 | |||
349 | tsunami_pci_tbi(hose, 0, -1); | ||
350 | } | ||
351 | |||
352 | void __init | ||
353 | tsunami_init_arch(void) | ||
354 | { | ||
355 | #ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI | ||
356 | unsigned long tmp; | ||
357 | |||
358 | /* Ho hum.. init_arch is called before init_IRQ, but we need to be | ||
359 | able to handle machine checks. So install the handler now. */ | ||
360 | wrent(entInt, 0); | ||
361 | |||
362 | /* NXMs just don't matter to Tsunami--unless they make it | ||
363 | choke completely. */ | ||
364 | tmp = (unsigned long)(TSUNAMI_cchip - 1); | ||
365 | printk("%s: probing bogus address: 0x%016lx\n", FN, bogus_addr); | ||
366 | printk("\tprobe %s\n", | ||
367 | tsunami_probe_write((unsigned long *)bogus_addr) | ||
368 | ? "succeeded" : "failed"); | ||
369 | #endif /* NXM_MACHINE_CHECKS_ON_TSUNAMI */ | ||
370 | |||
371 | #if 0 | ||
372 | printk("%s: CChip registers:\n", FN); | ||
373 | printk("%s: CSR_CSC 0x%lx\n", FN, TSUNAMI_cchip->csc.csr); | ||
374 | printk("%s: CSR_MTR 0x%lx\n", FN, TSUNAMI_cchip.mtr.csr); | ||
375 | printk("%s: CSR_MISC 0x%lx\n", FN, TSUNAMI_cchip->misc.csr); | ||
376 | printk("%s: CSR_DIM0 0x%lx\n", FN, TSUNAMI_cchip->dim0.csr); | ||
377 | printk("%s: CSR_DIM1 0x%lx\n", FN, TSUNAMI_cchip->dim1.csr); | ||
378 | printk("%s: CSR_DIR0 0x%lx\n", FN, TSUNAMI_cchip->dir0.csr); | ||
379 | printk("%s: CSR_DIR1 0x%lx\n", FN, TSUNAMI_cchip->dir1.csr); | ||
380 | printk("%s: CSR_DRIR 0x%lx\n", FN, TSUNAMI_cchip->drir.csr); | ||
381 | |||
382 | printk("%s: DChip registers:\n"); | ||
383 | printk("%s: CSR_DSC 0x%lx\n", FN, TSUNAMI_dchip->dsc.csr); | ||
384 | printk("%s: CSR_STR 0x%lx\n", FN, TSUNAMI_dchip->str.csr); | ||
385 | printk("%s: CSR_DREV 0x%lx\n", FN, TSUNAMI_dchip->drev.csr); | ||
386 | #endif | ||
387 | /* With multiple PCI busses, we play with I/O as physical addrs. */ | ||
388 | ioport_resource.end = ~0UL; | ||
389 | |||
390 | /* Find how many hoses we have, and initialize them. TSUNAMI | ||
391 | and TYPHOON can have 2, but might only have 1 (DS10). */ | ||
392 | |||
393 | tsunami_init_one_pchip(TSUNAMI_pchip0, 0); | ||
394 | if (TSUNAMI_cchip->csc.csr & 1L<<14) | ||
395 | tsunami_init_one_pchip(TSUNAMI_pchip1, 1); | ||
396 | } | ||
397 | |||
398 | static void | ||
399 | tsunami_kill_one_pchip(tsunami_pchip *pchip, int index) | ||
400 | { | ||
401 | pchip->wsba[0].csr = saved_config[index].wsba[0]; | ||
402 | pchip->wsm[0].csr = saved_config[index].wsm[0]; | ||
403 | pchip->tba[0].csr = saved_config[index].tba[0]; | ||
404 | |||
405 | pchip->wsba[1].csr = saved_config[index].wsba[1]; | ||
406 | pchip->wsm[1].csr = saved_config[index].wsm[1]; | ||
407 | pchip->tba[1].csr = saved_config[index].tba[1]; | ||
408 | |||
409 | pchip->wsba[2].csr = saved_config[index].wsba[2]; | ||
410 | pchip->wsm[2].csr = saved_config[index].wsm[2]; | ||
411 | pchip->tba[2].csr = saved_config[index].tba[2]; | ||
412 | |||
413 | pchip->wsba[3].csr = saved_config[index].wsba[3]; | ||
414 | pchip->wsm[3].csr = saved_config[index].wsm[3]; | ||
415 | pchip->tba[3].csr = saved_config[index].tba[3]; | ||
416 | } | ||
417 | |||
418 | void | ||
419 | tsunami_kill_arch(int mode) | ||
420 | { | ||
421 | tsunami_kill_one_pchip(TSUNAMI_pchip0, 0); | ||
422 | if (TSUNAMI_cchip->csc.csr & 1L<<14) | ||
423 | tsunami_kill_one_pchip(TSUNAMI_pchip1, 1); | ||
424 | } | ||
425 | |||
426 | static inline void | ||
427 | tsunami_pci_clr_err_1(tsunami_pchip *pchip) | ||
428 | { | ||
429 | pchip->perror.csr; | ||
430 | pchip->perror.csr = 0x040; | ||
431 | mb(); | ||
432 | pchip->perror.csr; | ||
433 | } | ||
434 | |||
435 | static inline void | ||
436 | tsunami_pci_clr_err(void) | ||
437 | { | ||
438 | tsunami_pci_clr_err_1(TSUNAMI_pchip0); | ||
439 | |||
440 | /* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */ | ||
441 | if (TSUNAMI_cchip->csc.csr & 1L<<14) | ||
442 | tsunami_pci_clr_err_1(TSUNAMI_pchip1); | ||
443 | } | ||
444 | |||
445 | void | ||
446 | tsunami_machine_check(unsigned long vector, unsigned long la_ptr, | ||
447 | struct pt_regs * regs) | ||
448 | { | ||
449 | /* Clear error before any reporting. */ | ||
450 | mb(); | ||
451 | mb(); /* magic */ | ||
452 | draina(); | ||
453 | tsunami_pci_clr_err(); | ||
454 | wrmces(0x7); | ||
455 | mb(); | ||
456 | |||
457 | process_mcheck_info(vector, la_ptr, regs, "TSUNAMI", | ||
458 | mcheck_expected(smp_processor_id())); | ||
459 | } | ||
diff --git a/arch/alpha/kernel/core_wildfire.c b/arch/alpha/kernel/core_wildfire.c new file mode 100644 index 000000000000..2b767a1bad96 --- /dev/null +++ b/arch/alpha/kernel/core_wildfire.c | |||
@@ -0,0 +1,658 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/core_wildfire.c | ||
3 | * | ||
4 | * Wildfire support. | ||
5 | * | ||
6 | * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE | ||
7 | */ | ||
8 | |||
9 | #define __EXTERN_INLINE inline | ||
10 | #include <asm/io.h> | ||
11 | #include <asm/core_wildfire.h> | ||
12 | #undef __EXTERN_INLINE | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/init.h> | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | #include <asm/smp.h> | ||
21 | |||
22 | #include "proto.h" | ||
23 | #include "pci_impl.h" | ||
24 | |||
25 | #define DEBUG_CONFIG 0 | ||
26 | #define DEBUG_DUMP_REGS 0 | ||
27 | #define DEBUG_DUMP_CONFIG 1 | ||
28 | |||
29 | #if DEBUG_CONFIG | ||
30 | # define DBG_CFG(args) printk args | ||
31 | #else | ||
32 | # define DBG_CFG(args) | ||
33 | #endif | ||
34 | |||
35 | #if DEBUG_DUMP_REGS | ||
36 | static void wildfire_dump_pci_regs(int qbbno, int hoseno); | ||
37 | static void wildfire_dump_pca_regs(int qbbno, int pcano); | ||
38 | static void wildfire_dump_qsa_regs(int qbbno); | ||
39 | static void wildfire_dump_qsd_regs(int qbbno); | ||
40 | static void wildfire_dump_iop_regs(int qbbno); | ||
41 | static void wildfire_dump_gp_regs(int qbbno); | ||
42 | #endif | ||
43 | #if DEBUG_DUMP_CONFIG | ||
44 | static void wildfire_dump_hardware_config(void); | ||
45 | #endif | ||
46 | |||
47 | unsigned char wildfire_hard_qbb_map[WILDFIRE_MAX_QBB]; | ||
48 | unsigned char wildfire_soft_qbb_map[WILDFIRE_MAX_QBB]; | ||
49 | #define QBB_MAP_EMPTY 0xff | ||
50 | |||
51 | unsigned long wildfire_hard_qbb_mask; | ||
52 | unsigned long wildfire_soft_qbb_mask; | ||
53 | unsigned long wildfire_gp_mask; | ||
54 | unsigned long wildfire_hs_mask; | ||
55 | unsigned long wildfire_iop_mask; | ||
56 | unsigned long wildfire_ior_mask; | ||
57 | unsigned long wildfire_pca_mask; | ||
58 | unsigned long wildfire_cpu_mask; | ||
59 | unsigned long wildfire_mem_mask; | ||
60 | |||
61 | void __init | ||
62 | wildfire_init_hose(int qbbno, int hoseno) | ||
63 | { | ||
64 | struct pci_controller *hose; | ||
65 | wildfire_pci *pci; | ||
66 | |||
67 | hose = alloc_pci_controller(); | ||
68 | hose->io_space = alloc_resource(); | ||
69 | hose->mem_space = alloc_resource(); | ||
70 | |||
71 | /* This is for userland consumption. */ | ||
72 | hose->sparse_mem_base = 0; | ||
73 | hose->sparse_io_base = 0; | ||
74 | hose->dense_mem_base = WILDFIRE_MEM(qbbno, hoseno); | ||
75 | hose->dense_io_base = WILDFIRE_IO(qbbno, hoseno); | ||
76 | |||
77 | hose->config_space_base = WILDFIRE_CONF(qbbno, hoseno); | ||
78 | hose->index = (qbbno << 3) + hoseno; | ||
79 | |||
80 | hose->io_space->start = WILDFIRE_IO(qbbno, hoseno) - WILDFIRE_IO_BIAS; | ||
81 | hose->io_space->end = hose->io_space->start + WILDFIRE_IO_SPACE - 1; | ||
82 | hose->io_space->name = pci_io_names[hoseno]; | ||
83 | hose->io_space->flags = IORESOURCE_IO; | ||
84 | |||
85 | hose->mem_space->start = WILDFIRE_MEM(qbbno, hoseno)-WILDFIRE_MEM_BIAS; | ||
86 | hose->mem_space->end = hose->mem_space->start + 0xffffffff; | ||
87 | hose->mem_space->name = pci_mem_names[hoseno]; | ||
88 | hose->mem_space->flags = IORESOURCE_MEM; | ||
89 | |||
90 | if (request_resource(&ioport_resource, hose->io_space) < 0) | ||
91 | printk(KERN_ERR "Failed to request IO on qbb %d hose %d\n", | ||
92 | qbbno, hoseno); | ||
93 | if (request_resource(&iomem_resource, hose->mem_space) < 0) | ||
94 | printk(KERN_ERR "Failed to request MEM on qbb %d hose %d\n", | ||
95 | qbbno, hoseno); | ||
96 | |||
97 | #if DEBUG_DUMP_REGS | ||
98 | wildfire_dump_pci_regs(qbbno, hoseno); | ||
99 | #endif | ||
100 | |||
101 | /* | ||
102 | * Set up the PCI to main memory translation windows. | ||
103 | * | ||
104 | * Note: Window 3 is scatter-gather only | ||
105 | * | ||
106 | * Window 0 is scatter-gather 8MB at 8MB (for isa) | ||
107 | * Window 1 is direct access 1GB at 1GB | ||
108 | * Window 2 is direct access 1GB at 2GB | ||
109 | * Window 3 is scatter-gather 128MB at 3GB | ||
110 | * ??? We ought to scale window 3 memory. | ||
111 | * | ||
112 | */ | ||
113 | hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); | ||
114 | hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000, 0); | ||
115 | |||
116 | pci = WILDFIRE_pci(qbbno, hoseno); | ||
117 | |||
118 | pci->pci_window[0].wbase.csr = hose->sg_isa->dma_base | 3; | ||
119 | pci->pci_window[0].wmask.csr = (hose->sg_isa->size - 1) & 0xfff00000; | ||
120 | pci->pci_window[0].tbase.csr = virt_to_phys(hose->sg_isa->ptes); | ||
121 | |||
122 | pci->pci_window[1].wbase.csr = 0x40000000 | 1; | ||
123 | pci->pci_window[1].wmask.csr = (0x40000000 -1) & 0xfff00000; | ||
124 | pci->pci_window[1].tbase.csr = 0; | ||
125 | |||
126 | pci->pci_window[2].wbase.csr = 0x80000000 | 1; | ||
127 | pci->pci_window[2].wmask.csr = (0x40000000 -1) & 0xfff00000; | ||
128 | pci->pci_window[2].tbase.csr = 0x40000000; | ||
129 | |||
130 | pci->pci_window[3].wbase.csr = hose->sg_pci->dma_base | 3; | ||
131 | pci->pci_window[3].wmask.csr = (hose->sg_pci->size - 1) & 0xfff00000; | ||
132 | pci->pci_window[3].tbase.csr = virt_to_phys(hose->sg_pci->ptes); | ||
133 | |||
134 | wildfire_pci_tbi(hose, 0, 0); /* Flush TLB at the end. */ | ||
135 | } | ||
136 | |||
137 | void __init | ||
138 | wildfire_init_pca(int qbbno, int pcano) | ||
139 | { | ||
140 | |||
141 | /* Test for PCA existence first. */ | ||
142 | if (!WILDFIRE_PCA_EXISTS(qbbno, pcano)) | ||
143 | return; | ||
144 | |||
145 | #if DEBUG_DUMP_REGS | ||
146 | wildfire_dump_pca_regs(qbbno, pcano); | ||
147 | #endif | ||
148 | |||
149 | /* Do both hoses of the PCA. */ | ||
150 | wildfire_init_hose(qbbno, (pcano << 1) + 0); | ||
151 | wildfire_init_hose(qbbno, (pcano << 1) + 1); | ||
152 | } | ||
153 | |||
154 | void __init | ||
155 | wildfire_init_qbb(int qbbno) | ||
156 | { | ||
157 | int pcano; | ||
158 | |||
159 | /* Test for QBB existence first. */ | ||
160 | if (!WILDFIRE_QBB_EXISTS(qbbno)) | ||
161 | return; | ||
162 | |||
163 | #if DEBUG_DUMP_REGS | ||
164 | wildfire_dump_qsa_regs(qbbno); | ||
165 | wildfire_dump_qsd_regs(qbbno); | ||
166 | wildfire_dump_iop_regs(qbbno); | ||
167 | wildfire_dump_gp_regs(qbbno); | ||
168 | #endif | ||
169 | |||
170 | /* Init all PCAs here. */ | ||
171 | for (pcano = 0; pcano < WILDFIRE_PCA_PER_QBB; pcano++) { | ||
172 | wildfire_init_pca(qbbno, pcano); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | void __init | ||
177 | wildfire_hardware_probe(void) | ||
178 | { | ||
179 | unsigned long temp; | ||
180 | unsigned int hard_qbb, soft_qbb; | ||
181 | wildfire_fast_qsd *fast = WILDFIRE_fast_qsd(); | ||
182 | wildfire_qsd *qsd; | ||
183 | wildfire_qsa *qsa; | ||
184 | wildfire_iop *iop; | ||
185 | wildfire_gp *gp; | ||
186 | wildfire_ne *ne; | ||
187 | wildfire_fe *fe; | ||
188 | int i; | ||
189 | |||
190 | temp = fast->qsd_whami.csr; | ||
191 | #if 0 | ||
192 | printk(KERN_ERR "fast QSD_WHAMI at base %p is 0x%lx\n", fast, temp); | ||
193 | #endif | ||
194 | |||
195 | hard_qbb = (temp >> 8) & 7; | ||
196 | soft_qbb = (temp >> 4) & 7; | ||
197 | |||
198 | /* Init the HW configuration variables. */ | ||
199 | wildfire_hard_qbb_mask = (1 << hard_qbb); | ||
200 | wildfire_soft_qbb_mask = (1 << soft_qbb); | ||
201 | |||
202 | wildfire_gp_mask = 0; | ||
203 | wildfire_hs_mask = 0; | ||
204 | wildfire_iop_mask = 0; | ||
205 | wildfire_ior_mask = 0; | ||
206 | wildfire_pca_mask = 0; | ||
207 | |||
208 | wildfire_cpu_mask = 0; | ||
209 | wildfire_mem_mask = 0; | ||
210 | |||
211 | memset(wildfire_hard_qbb_map, QBB_MAP_EMPTY, WILDFIRE_MAX_QBB); | ||
212 | memset(wildfire_soft_qbb_map, QBB_MAP_EMPTY, WILDFIRE_MAX_QBB); | ||
213 | |||
214 | /* First, determine which QBBs are present. */ | ||
215 | qsa = WILDFIRE_qsa(soft_qbb); | ||
216 | |||
217 | temp = qsa->qsa_qbb_id.csr; | ||
218 | #if 0 | ||
219 | printk(KERN_ERR "QSA_QBB_ID at base %p is 0x%lx\n", qsa, temp); | ||
220 | #endif | ||
221 | |||
222 | if (temp & 0x40) /* Is there an HS? */ | ||
223 | wildfire_hs_mask = 1; | ||
224 | |||
225 | if (temp & 0x20) { /* Is there a GP? */ | ||
226 | gp = WILDFIRE_gp(soft_qbb); | ||
227 | temp = 0; | ||
228 | for (i = 0; i < 4; i++) { | ||
229 | temp |= gp->gpa_qbb_map[i].csr << (i * 8); | ||
230 | #if 0 | ||
231 | printk(KERN_ERR "GPA_QBB_MAP[%d] at base %p is 0x%lx\n", | ||
232 | i, gp, temp); | ||
233 | #endif | ||
234 | } | ||
235 | |||
236 | for (hard_qbb = 0; hard_qbb < WILDFIRE_MAX_QBB; hard_qbb++) { | ||
237 | if (temp & 8) { /* Is there a QBB? */ | ||
238 | soft_qbb = temp & 7; | ||
239 | wildfire_hard_qbb_mask |= (1 << hard_qbb); | ||
240 | wildfire_soft_qbb_mask |= (1 << soft_qbb); | ||
241 | } | ||
242 | temp >>= 4; | ||
243 | } | ||
244 | wildfire_gp_mask = wildfire_soft_qbb_mask; | ||
245 | } | ||
246 | |||
247 | /* Next determine each QBBs resources. */ | ||
248 | for (soft_qbb = 0; soft_qbb < WILDFIRE_MAX_QBB; soft_qbb++) { | ||
249 | if (WILDFIRE_QBB_EXISTS(soft_qbb)) { | ||
250 | qsd = WILDFIRE_qsd(soft_qbb); | ||
251 | temp = qsd->qsd_whami.csr; | ||
252 | #if 0 | ||
253 | printk(KERN_ERR "QSD_WHAMI at base %p is 0x%lx\n", qsd, temp); | ||
254 | #endif | ||
255 | hard_qbb = (temp >> 8) & 7; | ||
256 | wildfire_hard_qbb_map[hard_qbb] = soft_qbb; | ||
257 | wildfire_soft_qbb_map[soft_qbb] = hard_qbb; | ||
258 | |||
259 | qsa = WILDFIRE_qsa(soft_qbb); | ||
260 | temp = qsa->qsa_qbb_pop[0].csr; | ||
261 | #if 0 | ||
262 | printk(KERN_ERR "QSA_QBB_POP_0 at base %p is 0x%lx\n", qsa, temp); | ||
263 | #endif | ||
264 | wildfire_cpu_mask |= ((temp >> 0) & 0xf) << (soft_qbb << 2); | ||
265 | wildfire_mem_mask |= ((temp >> 4) & 0xf) << (soft_qbb << 2); | ||
266 | |||
267 | temp = qsa->qsa_qbb_pop[1].csr; | ||
268 | #if 0 | ||
269 | printk(KERN_ERR "QSA_QBB_POP_1 at base %p is 0x%lx\n", qsa, temp); | ||
270 | #endif | ||
271 | wildfire_iop_mask |= (1 << soft_qbb); | ||
272 | wildfire_ior_mask |= ((temp >> 4) & 0xf) << (soft_qbb << 2); | ||
273 | |||
274 | temp = qsa->qsa_qbb_id.csr; | ||
275 | #if 0 | ||
276 | printk(KERN_ERR "QSA_QBB_ID at %p is 0x%lx\n", qsa, temp); | ||
277 | #endif | ||
278 | if (temp & 0x20) | ||
279 | wildfire_gp_mask |= (1 << soft_qbb); | ||
280 | |||
281 | /* Probe for PCA existence here. */ | ||
282 | for (i = 0; i < WILDFIRE_PCA_PER_QBB; i++) { | ||
283 | iop = WILDFIRE_iop(soft_qbb); | ||
284 | ne = WILDFIRE_ne(soft_qbb, i); | ||
285 | fe = WILDFIRE_fe(soft_qbb, i); | ||
286 | |||
287 | if ((iop->iop_hose[i].init.csr & 1) == 1 && | ||
288 | ((ne->ne_what_am_i.csr & 0xf00000300UL) == 0x100000300UL) && | ||
289 | ((fe->fe_what_am_i.csr & 0xf00000300UL) == 0x100000200UL)) | ||
290 | { | ||
291 | wildfire_pca_mask |= 1 << ((soft_qbb << 2) + i); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | } | ||
296 | } | ||
297 | #if DEBUG_DUMP_CONFIG | ||
298 | wildfire_dump_hardware_config(); | ||
299 | #endif | ||
300 | } | ||
301 | |||
302 | void __init | ||
303 | wildfire_init_arch(void) | ||
304 | { | ||
305 | int qbbno; | ||
306 | |||
307 | /* With multiple PCI buses, we play with I/O as physical addrs. */ | ||
308 | ioport_resource.end = ~0UL; | ||
309 | |||
310 | |||
311 | /* Probe the hardware for info about configuration. */ | ||
312 | wildfire_hardware_probe(); | ||
313 | |||
314 | /* Now init all the found QBBs. */ | ||
315 | for (qbbno = 0; qbbno < WILDFIRE_MAX_QBB; qbbno++) { | ||
316 | wildfire_init_qbb(qbbno); | ||
317 | } | ||
318 | |||
319 | /* Normal direct PCI DMA mapping. */ | ||
320 | __direct_map_base = 0x40000000UL; | ||
321 | __direct_map_size = 0x80000000UL; | ||
322 | } | ||
323 | |||
324 | void | ||
325 | wildfire_machine_check(unsigned long vector, unsigned long la_ptr, | ||
326 | struct pt_regs * regs) | ||
327 | { | ||
328 | mb(); | ||
329 | mb(); /* magic */ | ||
330 | draina(); | ||
331 | /* FIXME: clear pci errors */ | ||
332 | wrmces(0x7); | ||
333 | mb(); | ||
334 | |||
335 | process_mcheck_info(vector, la_ptr, regs, "WILDFIRE", | ||
336 | mcheck_expected(smp_processor_id())); | ||
337 | } | ||
338 | |||
339 | void | ||
340 | wildfire_kill_arch(int mode) | ||
341 | { | ||
342 | } | ||
343 | |||
344 | void | ||
345 | wildfire_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) | ||
346 | { | ||
347 | int qbbno = hose->index >> 3; | ||
348 | int hoseno = hose->index & 7; | ||
349 | wildfire_pci *pci = WILDFIRE_pci(qbbno, hoseno); | ||
350 | |||
351 | mb(); | ||
352 | pci->pci_flush_tlb.csr; /* reading does the trick */ | ||
353 | } | ||
354 | |||
355 | static int | ||
356 | mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, | ||
357 | unsigned long *pci_addr, unsigned char *type1) | ||
358 | { | ||
359 | struct pci_controller *hose = pbus->sysdata; | ||
360 | unsigned long addr; | ||
361 | u8 bus = pbus->number; | ||
362 | |||
363 | DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " | ||
364 | "pci_addr=0x%p, type1=0x%p)\n", | ||
365 | bus, device_fn, where, pci_addr, type1)); | ||
366 | |||
367 | if (!pbus->parent) /* No parent means peer PCI bus. */ | ||
368 | bus = 0; | ||
369 | *type1 = (bus != 0); | ||
370 | |||
371 | addr = (bus << 16) | (device_fn << 8) | where; | ||
372 | addr |= hose->config_space_base; | ||
373 | |||
374 | *pci_addr = addr; | ||
375 | DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int | ||
380 | wildfire_read_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
381 | int size, u32 *value) | ||
382 | { | ||
383 | unsigned long addr; | ||
384 | unsigned char type1; | ||
385 | |||
386 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
387 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
388 | |||
389 | switch (size) { | ||
390 | case 1: | ||
391 | *value = __kernel_ldbu(*(vucp)addr); | ||
392 | break; | ||
393 | case 2: | ||
394 | *value = __kernel_ldwu(*(vusp)addr); | ||
395 | break; | ||
396 | case 4: | ||
397 | *value = *(vuip)addr; | ||
398 | break; | ||
399 | } | ||
400 | |||
401 | return PCIBIOS_SUCCESSFUL; | ||
402 | } | ||
403 | |||
404 | static int | ||
405 | wildfire_write_config(struct pci_bus *bus, unsigned int devfn, int where, | ||
406 | int size, u32 value) | ||
407 | { | ||
408 | unsigned long addr; | ||
409 | unsigned char type1; | ||
410 | |||
411 | if (mk_conf_addr(bus, devfn, where, &addr, &type1)) | ||
412 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
413 | |||
414 | switch (size) { | ||
415 | case 1: | ||
416 | __kernel_stb(value, *(vucp)addr); | ||
417 | mb(); | ||
418 | __kernel_ldbu(*(vucp)addr); | ||
419 | break; | ||
420 | case 2: | ||
421 | __kernel_stw(value, *(vusp)addr); | ||
422 | mb(); | ||
423 | __kernel_ldwu(*(vusp)addr); | ||
424 | break; | ||
425 | case 4: | ||
426 | *(vuip)addr = value; | ||
427 | mb(); | ||
428 | *(vuip)addr; | ||
429 | break; | ||
430 | } | ||
431 | |||
432 | return PCIBIOS_SUCCESSFUL; | ||
433 | } | ||
434 | |||
435 | struct pci_ops wildfire_pci_ops = | ||
436 | { | ||
437 | .read = wildfire_read_config, | ||
438 | .write = wildfire_write_config, | ||
439 | }; | ||
440 | |||
441 | |||
442 | /* | ||
443 | * NUMA Support | ||
444 | */ | ||
445 | int wildfire_pa_to_nid(unsigned long pa) | ||
446 | { | ||
447 | return pa >> 36; | ||
448 | } | ||
449 | |||
450 | int wildfire_cpuid_to_nid(int cpuid) | ||
451 | { | ||
452 | /* assume 4 CPUs per node */ | ||
453 | return cpuid >> 2; | ||
454 | } | ||
455 | |||
456 | unsigned long wildfire_node_mem_start(int nid) | ||
457 | { | ||
458 | /* 64GB per node */ | ||
459 | return (unsigned long)nid * (64UL * 1024 * 1024 * 1024); | ||
460 | } | ||
461 | |||
462 | unsigned long wildfire_node_mem_size(int nid) | ||
463 | { | ||
464 | /* 64GB per node */ | ||
465 | return 64UL * 1024 * 1024 * 1024; | ||
466 | } | ||
467 | |||
468 | #if DEBUG_DUMP_REGS | ||
469 | |||
470 | static void __init | ||
471 | wildfire_dump_pci_regs(int qbbno, int hoseno) | ||
472 | { | ||
473 | wildfire_pci *pci = WILDFIRE_pci(qbbno, hoseno); | ||
474 | int i; | ||
475 | |||
476 | printk(KERN_ERR "PCI registers for QBB %d hose %d (%p)\n", | ||
477 | qbbno, hoseno, pci); | ||
478 | |||
479 | printk(KERN_ERR " PCI_IO_ADDR_EXT: 0x%16lx\n", | ||
480 | pci->pci_io_addr_ext.csr); | ||
481 | printk(KERN_ERR " PCI_CTRL: 0x%16lx\n", pci->pci_ctrl.csr); | ||
482 | printk(KERN_ERR " PCI_ERR_SUM: 0x%16lx\n", pci->pci_err_sum.csr); | ||
483 | printk(KERN_ERR " PCI_ERR_ADDR: 0x%16lx\n", pci->pci_err_addr.csr); | ||
484 | printk(KERN_ERR " PCI_STALL_CNT: 0x%16lx\n", pci->pci_stall_cnt.csr); | ||
485 | printk(KERN_ERR " PCI_PEND_INT: 0x%16lx\n", pci->pci_pend_int.csr); | ||
486 | printk(KERN_ERR " PCI_SENT_INT: 0x%16lx\n", pci->pci_sent_int.csr); | ||
487 | |||
488 | printk(KERN_ERR " DMA window registers for QBB %d hose %d (%p)\n", | ||
489 | qbbno, hoseno, pci); | ||
490 | for (i = 0; i < 4; i++) { | ||
491 | printk(KERN_ERR " window %d: 0x%16lx 0x%16lx 0x%16lx\n", i, | ||
492 | pci->pci_window[i].wbase.csr, | ||
493 | pci->pci_window[i].wmask.csr, | ||
494 | pci->pci_window[i].tbase.csr); | ||
495 | } | ||
496 | printk(KERN_ERR "\n"); | ||
497 | } | ||
498 | |||
499 | static void __init | ||
500 | wildfire_dump_pca_regs(int qbbno, int pcano) | ||
501 | { | ||
502 | wildfire_pca *pca = WILDFIRE_pca(qbbno, pcano); | ||
503 | int i; | ||
504 | |||
505 | printk(KERN_ERR "PCA registers for QBB %d PCA %d (%p)\n", | ||
506 | qbbno, pcano, pca); | ||
507 | |||
508 | printk(KERN_ERR " PCA_WHAT_AM_I: 0x%16lx\n", pca->pca_what_am_i.csr); | ||
509 | printk(KERN_ERR " PCA_ERR_SUM: 0x%16lx\n", pca->pca_err_sum.csr); | ||
510 | printk(KERN_ERR " PCA_PEND_INT: 0x%16lx\n", pca->pca_pend_int.csr); | ||
511 | printk(KERN_ERR " PCA_SENT_INT: 0x%16lx\n", pca->pca_sent_int.csr); | ||
512 | printk(KERN_ERR " PCA_STDIO_EL: 0x%16lx\n", | ||
513 | pca->pca_stdio_edge_level.csr); | ||
514 | |||
515 | printk(KERN_ERR " PCA target registers for QBB %d PCA %d (%p)\n", | ||
516 | qbbno, pcano, pca); | ||
517 | for (i = 0; i < 4; i++) { | ||
518 | printk(KERN_ERR " target %d: 0x%16lx 0x%16lx\n", i, | ||
519 | pca->pca_int[i].target.csr, | ||
520 | pca->pca_int[i].enable.csr); | ||
521 | } | ||
522 | |||
523 | printk(KERN_ERR "\n"); | ||
524 | } | ||
525 | |||
526 | static void __init | ||
527 | wildfire_dump_qsa_regs(int qbbno) | ||
528 | { | ||
529 | wildfire_qsa *qsa = WILDFIRE_qsa(qbbno); | ||
530 | int i; | ||
531 | |||
532 | printk(KERN_ERR "QSA registers for QBB %d (%p)\n", qbbno, qsa); | ||
533 | |||
534 | printk(KERN_ERR " QSA_QBB_ID: 0x%16lx\n", qsa->qsa_qbb_id.csr); | ||
535 | printk(KERN_ERR " QSA_PORT_ENA: 0x%16lx\n", qsa->qsa_port_ena.csr); | ||
536 | printk(KERN_ERR " QSA_REF_INT: 0x%16lx\n", qsa->qsa_ref_int.csr); | ||
537 | |||
538 | for (i = 0; i < 5; i++) | ||
539 | printk(KERN_ERR " QSA_CONFIG_%d: 0x%16lx\n", | ||
540 | i, qsa->qsa_config[i].csr); | ||
541 | |||
542 | for (i = 0; i < 2; i++) | ||
543 | printk(KERN_ERR " QSA_QBB_POP_%d: 0x%16lx\n", | ||
544 | i, qsa->qsa_qbb_pop[0].csr); | ||
545 | |||
546 | printk(KERN_ERR "\n"); | ||
547 | } | ||
548 | |||
549 | static void __init | ||
550 | wildfire_dump_qsd_regs(int qbbno) | ||
551 | { | ||
552 | wildfire_qsd *qsd = WILDFIRE_qsd(qbbno); | ||
553 | |||
554 | printk(KERN_ERR "QSD registers for QBB %d (%p)\n", qbbno, qsd); | ||
555 | |||
556 | printk(KERN_ERR " QSD_WHAMI: 0x%16lx\n", qsd->qsd_whami.csr); | ||
557 | printk(KERN_ERR " QSD_REV: 0x%16lx\n", qsd->qsd_rev.csr); | ||
558 | printk(KERN_ERR " QSD_PORT_PRESENT: 0x%16lx\n", | ||
559 | qsd->qsd_port_present.csr); | ||
560 | printk(KERN_ERR " QSD_PORT_ACTUVE: 0x%16lx\n", | ||
561 | qsd->qsd_port_active.csr); | ||
562 | printk(KERN_ERR " QSD_FAULT_ENA: 0x%16lx\n", | ||
563 | qsd->qsd_fault_ena.csr); | ||
564 | printk(KERN_ERR " QSD_CPU_INT_ENA: 0x%16lx\n", | ||
565 | qsd->qsd_cpu_int_ena.csr); | ||
566 | printk(KERN_ERR " QSD_MEM_CONFIG: 0x%16lx\n", | ||
567 | qsd->qsd_mem_config.csr); | ||
568 | printk(KERN_ERR " QSD_ERR_SUM: 0x%16lx\n", | ||
569 | qsd->qsd_err_sum.csr); | ||
570 | |||
571 | printk(KERN_ERR "\n"); | ||
572 | } | ||
573 | |||
574 | static void __init | ||
575 | wildfire_dump_iop_regs(int qbbno) | ||
576 | { | ||
577 | wildfire_iop *iop = WILDFIRE_iop(qbbno); | ||
578 | int i; | ||
579 | |||
580 | printk(KERN_ERR "IOP registers for QBB %d (%p)\n", qbbno, iop); | ||
581 | |||
582 | printk(KERN_ERR " IOA_CONFIG: 0x%16lx\n", iop->ioa_config.csr); | ||
583 | printk(KERN_ERR " IOD_CONFIG: 0x%16lx\n", iop->iod_config.csr); | ||
584 | printk(KERN_ERR " IOP_SWITCH_CREDITS: 0x%16lx\n", | ||
585 | iop->iop_switch_credits.csr); | ||
586 | printk(KERN_ERR " IOP_HOSE_CREDITS: 0x%16lx\n", | ||
587 | iop->iop_hose_credits.csr); | ||
588 | |||
589 | for (i = 0; i < 4; i++) | ||
590 | printk(KERN_ERR " IOP_HOSE_%d_INIT: 0x%16lx\n", | ||
591 | i, iop->iop_hose[i].init.csr); | ||
592 | for (i = 0; i < 4; i++) | ||
593 | printk(KERN_ERR " IOP_DEV_INT_TARGET_%d: 0x%16lx\n", | ||
594 | i, iop->iop_dev_int[i].target.csr); | ||
595 | |||
596 | printk(KERN_ERR "\n"); | ||
597 | } | ||
598 | |||
599 | static void __init | ||
600 | wildfire_dump_gp_regs(int qbbno) | ||
601 | { | ||
602 | wildfire_gp *gp = WILDFIRE_gp(qbbno); | ||
603 | int i; | ||
604 | |||
605 | printk(KERN_ERR "GP registers for QBB %d (%p)\n", qbbno, gp); | ||
606 | for (i = 0; i < 4; i++) | ||
607 | printk(KERN_ERR " GPA_QBB_MAP_%d: 0x%16lx\n", | ||
608 | i, gp->gpa_qbb_map[i].csr); | ||
609 | |||
610 | printk(KERN_ERR " GPA_MEM_POP_MAP: 0x%16lx\n", | ||
611 | gp->gpa_mem_pop_map.csr); | ||
612 | printk(KERN_ERR " GPA_SCRATCH: 0x%16lx\n", gp->gpa_scratch.csr); | ||
613 | printk(KERN_ERR " GPA_DIAG: 0x%16lx\n", gp->gpa_diag.csr); | ||
614 | printk(KERN_ERR " GPA_CONFIG_0: 0x%16lx\n", gp->gpa_config_0.csr); | ||
615 | printk(KERN_ERR " GPA_INIT_ID: 0x%16lx\n", gp->gpa_init_id.csr); | ||
616 | printk(KERN_ERR " GPA_CONFIG_2: 0x%16lx\n", gp->gpa_config_2.csr); | ||
617 | |||
618 | printk(KERN_ERR "\n"); | ||
619 | } | ||
620 | #endif /* DUMP_REGS */ | ||
621 | |||
622 | #if DEBUG_DUMP_CONFIG | ||
623 | static void __init | ||
624 | wildfire_dump_hardware_config(void) | ||
625 | { | ||
626 | int i; | ||
627 | |||
628 | printk(KERN_ERR "Probed Hardware Configuration\n"); | ||
629 | |||
630 | printk(KERN_ERR " hard_qbb_mask: 0x%16lx\n", wildfire_hard_qbb_mask); | ||
631 | printk(KERN_ERR " soft_qbb_mask: 0x%16lx\n", wildfire_soft_qbb_mask); | ||
632 | |||
633 | printk(KERN_ERR " gp_mask: 0x%16lx\n", wildfire_gp_mask); | ||
634 | printk(KERN_ERR " hs_mask: 0x%16lx\n", wildfire_hs_mask); | ||
635 | printk(KERN_ERR " iop_mask: 0x%16lx\n", wildfire_iop_mask); | ||
636 | printk(KERN_ERR " ior_mask: 0x%16lx\n", wildfire_ior_mask); | ||
637 | printk(KERN_ERR " pca_mask: 0x%16lx\n", wildfire_pca_mask); | ||
638 | |||
639 | printk(KERN_ERR " cpu_mask: 0x%16lx\n", wildfire_cpu_mask); | ||
640 | printk(KERN_ERR " mem_mask: 0x%16lx\n", wildfire_mem_mask); | ||
641 | |||
642 | printk(" hard_qbb_map: "); | ||
643 | for (i = 0; i < WILDFIRE_MAX_QBB; i++) | ||
644 | if (wildfire_hard_qbb_map[i] == QBB_MAP_EMPTY) | ||
645 | printk("--- "); | ||
646 | else | ||
647 | printk("%3d ", wildfire_hard_qbb_map[i]); | ||
648 | printk("\n"); | ||
649 | |||
650 | printk(" soft_qbb_map: "); | ||
651 | for (i = 0; i < WILDFIRE_MAX_QBB; i++) | ||
652 | if (wildfire_soft_qbb_map[i] == QBB_MAP_EMPTY) | ||
653 | printk("--- "); | ||
654 | else | ||
655 | printk("%3d ", wildfire_soft_qbb_map[i]); | ||
656 | printk("\n"); | ||
657 | } | ||
658 | #endif /* DUMP_CONFIG */ | ||
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S new file mode 100644 index 000000000000..f0927ee53f29 --- /dev/null +++ b/arch/alpha/kernel/entry.S | |||
@@ -0,0 +1,957 @@ | |||
1 | /* | ||
2 | * arch/alpha/kernel/entry.S | ||
3 | * | ||
4 | * Kernel entry-points. | ||
5 | */ | ||
6 | |||
7 | #include <linux/config.h> | ||
8 | #include <asm/asm_offsets.h> | ||
9 | #include <asm/thread_info.h> | ||
10 | #include <asm/pal.h> | ||
11 | #include <asm/errno.h> | ||
12 | #include <asm/unistd.h> | ||
13 | |||
14 | .text | ||
15 | .set noat | ||
16 | |||
17 | /* Stack offsets. */ | ||
18 | #define SP_OFF 184 | ||
19 | #define SWITCH_STACK_SIZE 320 | ||
20 | |||
21 | /* | ||
22 | * This defines the normal kernel pt-regs layout. | ||
23 | * | ||
24 | * regs 9-15 preserved by C code | ||
25 | * regs 16-18 saved by PAL-code | ||
26 | * regs 29-30 saved and set up by PAL-code | ||
27 | * JRP - Save regs 16-18 in a special area of the stack, so that | ||
28 | * the palcode-provided values are available to the signal handler. | ||
29 | */ | ||
30 | |||
31 | #define SAVE_ALL \ | ||
32 | subq $sp, SP_OFF, $sp; \ | ||
33 | stq $0, 0($sp); \ | ||
34 | stq $1, 8($sp); \ | ||
35 | stq $2, 16($sp); \ | ||
36 | stq $3, 24($sp); \ | ||
37 | stq $4, 32($sp); \ | ||
38 | stq $28, 144($sp); \ | ||
39 | lda $2, alpha_mv; \ | ||
40 | stq $5, 40($sp); \ | ||
41 | stq $6, 48($sp); \ | ||
42 | stq $7, 56($sp); \ | ||
43 | stq $8, 64($sp); \ | ||
44 | stq $19, 72($sp); \ | ||
45 | stq $20, 80($sp); \ | ||
46 | stq $21, 88($sp); \ | ||
47 | ldq $2, HAE_CACHE($2); \ | ||
48 | stq $22, 96($sp); \ | ||
49 | stq $23, 104($sp); \ | ||
50 | stq $24, 112($sp); \ | ||
51 | stq $25, 120($sp); \ | ||
52 | stq $26, 128($sp); \ | ||
53 | stq $27, 136($sp); \ | ||
54 | stq $2, 152($sp); \ | ||
55 | stq $16, 160($sp); \ | ||
56 | stq $17, 168($sp); \ | ||
57 | stq $18, 176($sp) | ||
58 | |||
59 | #define RESTORE_ALL \ | ||
60 | lda $19, alpha_mv; \ | ||
61 | ldq $0, 0($sp); \ | ||
62 | ldq $1, 8($sp); \ | ||
63 | ldq $2, 16($sp); \ | ||
64 | ldq $3, 24($sp); \ | ||
65 | ldq $21, 152($sp); \ | ||
66 | ldq $20, HAE_CACHE($19); \ | ||
67 | ldq $4, 32($sp); \ | ||
68 | ldq $5, 40($sp); \ | ||
69 | ldq $6, 48($sp); \ | ||
70 | ldq $7, 56($sp); \ | ||
71 | subq $20, $21, $20; \ | ||
72 | ldq $8, 64($sp); \ | ||
73 | beq $20, 99f; \ | ||
74 | ldq $20, HAE_REG($19); \ | ||
75 | stq $21, HAE_CACHE($19); \ | ||
76 | stq $21, 0($20); \ | ||
77 | ldq $0, 0($sp); \ | ||
78 | ldq $1, 8($sp); \ | ||
79 | 99:; \ | ||
80 | ldq $19, 72($sp); \ | ||
81 | ldq $20, 80($sp); \ | ||
82 | ldq $21, 88($sp); \ | ||
83 | ldq $22, 96($sp); \ | ||
84 | ldq $23, 104($sp); \ | ||
85 | ldq $24, 112($sp); \ | ||
86 | ldq $25, 120($sp); \ | ||
87 | ldq $26, 128($sp); \ | ||
88 | ldq $27, 136($sp); \ | ||
89 | ldq $28, 144($sp); \ | ||
90 | addq $sp, SP_OFF, $sp | ||
91 | |||
92 | /* | ||
93 | * Non-syscall kernel entry points. | ||
94 | */ | ||
95 | |||
96 | .align 4 | ||
97 | .globl entInt | ||
98 | .ent entInt | ||
99 | entInt: | ||
100 | SAVE_ALL | ||
101 | lda $8, 0x3fff | ||
102 | lda $26, ret_from_sys_call | ||
103 | bic $sp, $8, $8 | ||
104 | mov $sp, $19 | ||
105 | jsr $31, do_entInt | ||
106 | .end entInt | ||
107 | |||
108 | .align 4 | ||
109 | .globl entArith | ||
110 | .ent entArith | ||
111 | entArith: | ||
112 | SAVE_ALL | ||
113 | lda $8, 0x3fff | ||
114 | lda $26, ret_from_sys_call | ||
115 | bic $sp, $8, $8 | ||
116 | mov $sp, $18 | ||
117 | jsr $31, do_entArith | ||
118 | .end entArith | ||
119 | |||
120 | .align 4 | ||
121 | .globl entMM | ||
122 | .ent entMM | ||
123 | entMM: | ||
124 | SAVE_ALL | ||
125 | /* save $9 - $15 so the inline exception code can manipulate them. */ | ||
126 | subq $sp, 56, $sp | ||
127 | stq $9, 0($sp) | ||
128 | stq $10, 8($sp) | ||
129 | stq $11, 16($sp) | ||
130 | stq $12, 24($sp) | ||
131 | stq $13, 32($sp) | ||
132 | stq $14, 40($sp) | ||
133 | stq $15, 48($sp) | ||
134 | addq $sp, 56, $19 | ||
135 | /* handle the fault */ | ||
136 | lda $8, 0x3fff | ||
137 | bic $sp, $8, $8 | ||
138 | jsr $26, do_page_fault | ||
139 | /* reload the registers after the exception code played. */ | ||
140 | ldq $9, 0($sp) | ||
141 | ldq $10, 8($sp) | ||
142 | ldq $11, 16($sp) | ||
143 | ldq $12, 24($sp) | ||
144 | ldq $13, 32($sp) | ||
145 | ldq $14, 40($sp) | ||
146 | ldq $15, 48($sp) | ||
147 | addq $sp, 56, $sp | ||
148 | /* finish up the syscall as normal. */ | ||
149 | br ret_from_sys_call | ||
150 | .end entMM | ||
151 | |||
152 | .align 4 | ||
153 | .globl entIF | ||
154 | .ent entIF | ||
155 | entIF: | ||
156 | SAVE_ALL | ||
157 | lda $8, 0x3fff | ||
158 | lda $26, ret_from_sys_call | ||
159 | bic $sp, $8, $8 | ||
160 | mov $sp, $17 | ||
161 | jsr $31, do_entIF | ||
162 | .end entIF | ||
163 | |||
164 | .align 4 | ||
165 | .globl entUna | ||
166 | .ent entUna | ||
167 | entUna: | ||
168 | lda $sp, -256($sp) | ||
169 | stq $0, 0($sp) | ||
170 | ldq $0, 256($sp) /* get PS */ | ||
171 | stq $1, 8($sp) | ||
172 | stq $2, 16($sp) | ||
173 | stq $3, 24($sp) | ||
174 | and $0, 8, $0 /* user mode? */ | ||
175 | stq $4, 32($sp) | ||
176 | bne $0, entUnaUser /* yup -> do user-level unaligned fault */ | ||
177 | stq $5, 40($sp) | ||
178 | stq $6, 48($sp) | ||
179 | stq $7, 56($sp) | ||
180 | stq $8, 64($sp) | ||
181 | stq $9, 72($sp) | ||
182 | stq $10, 80($sp) | ||
183 | stq $11, 88($sp) | ||
184 | stq $12, 96($sp) | ||
185 | stq $13, 104($sp) | ||
186 | stq $14, 112($sp) | ||
187 | stq $15, 120($sp) | ||
188 | /* 16-18 PAL-saved */ | ||
189 | stq $19, 152($sp) | ||
190 | stq $20, 160($sp) | ||
191 | stq $21, 168($sp) | ||
192 | stq $22, 176($sp) | ||
193 | stq $23, 184($sp) | ||
194 | stq $24, 192($sp) | ||
195 | stq $25, 200($sp) | ||
196 | stq $26, 208($sp) | ||
197 | stq $27, 216($sp) | ||
198 | stq $28, 224($sp) | ||
199 | stq $gp, 232($sp) | ||
200 | lda $8, 0x3fff | ||
201 | stq $31, 248($sp) | ||
202 | bic $sp, $8, $8 | ||
203 | jsr $26, do_entUna | ||
204 | ldq $0, 0($sp) | ||
205 | ldq $1, 8($sp) | ||
206 | ldq $2, 16($sp) | ||
207 | ldq $3, 24($sp) | ||
208 | ldq $4, 32($sp) | ||
209 | ldq $5, 40($sp) | ||
210 | ldq $6, 48($sp) | ||
211 | ldq $7, 56($sp) | ||
212 | ldq $8, 64($sp) | ||
213 | ldq $9, 72($sp) | ||
214 | ldq $10, 80($sp) | ||
215 | ldq $11, 88($sp) | ||
216 | ldq $12, 96($sp) | ||
217 | ldq $13, 104($sp) | ||
218 | ldq $14, 112($sp) | ||
219 | ldq $15, 120($sp) | ||
220 | /* 16-18 PAL-saved */ | ||
221 | ldq $19, 152($sp) | ||
222 | ldq $20, 160($sp) | ||
223 | ldq $21, 168($sp) | ||
224 | ldq $22, 176($sp) | ||
225 | ldq $23, 184($sp) | ||
226 | ldq $24, 192($sp) | ||
227 | ldq $25, 200($sp) | ||
228 | ldq $26, 208($sp) | ||
229 | ldq $27, 216($sp) | ||
230 | ldq $28, 224($sp) | ||
231 | ldq $gp, 232($sp) | ||
232 | lda $sp, 256($sp) | ||
233 | call_pal PAL_rti | ||
234 | .end entUna | ||
235 | |||
236 | .align 4 | ||
237 | .ent entUnaUser | ||
238 | entUnaUser: | ||
239 | ldq $0, 0($sp) /* restore original $0 */ | ||
240 | lda $sp, 256($sp) /* pop entUna's stack frame */ | ||
241 | SAVE_ALL /* setup normal kernel stack */ | ||
242 | lda $sp, -56($sp) | ||
243 | stq $9, 0($sp) | ||
244 | stq $10, 8($sp) | ||
245 | stq $11, 16($sp) | ||
246 | stq $12, 24($sp) | ||
247 | stq $13, 32($sp) | ||
248 | stq $14, 40($sp) | ||
249 | stq $15, 48($sp) | ||
250 | lda $8, 0x3fff | ||
251 | addq $sp, 56, $19 | ||
252 | bic $sp, $8, $8 | ||
253 | jsr $26, do_entUnaUser | ||
254 | ldq $9, 0($sp) | ||
255 | ldq $10, 8($sp) | ||
256 | ldq $11, 16($sp) | ||
257 | ldq $12, 24($sp) | ||
258 | ldq $13, 32($sp) | ||
259 | ldq $14, 40($sp) | ||
260 | ldq $15, 48($sp) | ||
261 | lda $sp, 56($sp) | ||
262 | br ret_from_sys_call | ||
263 | .end entUnaUser | ||
264 | |||
265 | .align 4 | ||
266 | .globl entDbg | ||
267 | .ent entDbg | ||
268 | entDbg: | ||
269 | SAVE_ALL | ||
270 | lda $8, 0x3fff | ||
271 | lda $26, ret_from_sys_call | ||
272 | bic $sp, $8, $8 | ||
273 | mov $sp, $16 | ||
274 | jsr $31, do_entDbg | ||
275 | .end entDbg | ||
276 | |||
277 | /* | ||
278 | * The system call entry point is special. Most importantly, it looks | ||
279 | * like a function call to userspace as far as clobbered registers. We | ||
280 | * do preserve the argument registers (for syscall restarts) and $26 | ||
281 | * (for leaf syscall functions). | ||
282 | * | ||
283 | * So much for theory. We don't take advantage of this yet. | ||
284 | * | ||
285 | * Note that a0-a2 are not saved by PALcode as with the other entry points. | ||
286 | */ | ||
287 | |||
288 | .align 4 | ||
289 | .globl entSys | ||
290 | .globl ret_from_sys_call | ||
291 | .ent entSys | ||
292 | entSys: | ||
293 | SAVE_ALL | ||
294 | lda $8, 0x3fff | ||
295 | bic $sp, $8, $8 | ||
296 | lda $4, NR_SYSCALLS($31) | ||
297 | stq $16, SP_OFF+24($sp) | ||
298 | lda $5, sys_call_table | ||
299 | lda $27, sys_ni_syscall | ||
300 | cmpult $0, $4, $4 | ||
301 | ldl $3, TI_FLAGS($8) | ||
302 | stq $17, SP_OFF+32($sp) | ||
303 | s8addq $0, $5, $5 | ||
304 | stq $18, SP_OFF+40($sp) | ||
305 | blbs $3, strace | ||
306 | beq $4, 1f | ||
307 | ldq $27, 0($5) | ||
308 | 1: jsr $26, ($27), alpha_ni_syscall | ||
309 | ldgp $gp, 0($26) | ||
310 | blt $0, $syscall_error /* the call failed */ | ||
311 | stq $0, 0($sp) | ||
312 | stq $31, 72($sp) /* a3=0 => no error */ | ||
313 | |||
314 | .align 4 | ||
315 | ret_from_sys_call: | ||
316 | cmovne $26, 0, $19 /* $19 = 0 => non-restartable */ | ||
317 | ldq $0, SP_OFF($sp) | ||
318 | and $0, 8, $0 | ||
319 | beq $0, restore_all | ||
320 | ret_from_reschedule: | ||
321 | /* Make sure need_resched and sigpending don't change between | ||
322 | sampling and the rti. */ | ||
323 | lda $16, 7 | ||
324 | call_pal PAL_swpipl | ||
325 | ldl $5, TI_FLAGS($8) | ||
326 | and $5, _TIF_WORK_MASK, $2 | ||
327 | bne $5, work_pending | ||
328 | restore_all: | ||
329 | RESTORE_ALL | ||
330 | call_pal PAL_rti | ||
331 | |||
332 | .align 3 | ||
333 | $syscall_error: | ||
334 | /* | ||
335 | * Some system calls (e.g., ptrace) can return arbitrary | ||
336 | * values which might normally be mistaken as error numbers. | ||
337 | * Those functions must zero $0 (v0) directly in the stack | ||
338 | * frame to indicate that a negative return value wasn't an | ||
339 | * error number.. | ||
340 | */ | ||
341 | ldq $19, 0($sp) /* old syscall nr (zero if success) */ | ||
342 | beq $19, $ret_success | ||
343 | |||
344 | ldq $20, 72($sp) /* .. and this a3 */ | ||
345 | subq $31, $0, $0 /* with error in v0 */ | ||
346 | addq $31, 1, $1 /* set a3 for errno return */ | ||
347 | stq $0, 0($sp) | ||
348 | mov $31, $26 /* tell "ret_from_sys_call" we can restart */ | ||
349 | stq $1, 72($sp) /* a3 for return */ | ||
350 | br ret_from_sys_call | ||
351 | |||
352 | $ret_success: | ||
353 | stq $0, 0($sp) | ||
354 | stq $31, 72($sp) /* a3=0 => no error */ | ||
355 | br ret_from_sys_call | ||
356 | .end entSys | ||
357 | |||
358 | /* | ||
359 | * Do all cleanup when returning from all interrupts and system calls. | ||
360 | * | ||
361 | * Arguments: | ||
362 | * $5: TI_FLAGS. | ||
363 | * $8: current. | ||
364 | * $19: The old syscall number, or zero if this is not a return | ||
365 | * from a syscall that errored and is possibly restartable. | ||
366 | * $20: Error indication. | ||
367 | */ | ||
368 | |||
369 | .align 4 | ||
370 | .ent work_pending | ||
371 | work_pending: | ||
372 | and $5, _TIF_NEED_RESCHED, $2 | ||
373 | beq $2, $work_notifysig | ||
374 | |||
375 | $work_resched: | ||
376 | subq $sp, 16, $sp | ||
377 | stq $19, 0($sp) /* save syscall nr */ | ||
378 | stq $20, 8($sp) /* and error indication (a3) */ | ||
379 | jsr $26, schedule | ||
380 | ldq $19, 0($sp) | ||
381 | ldq $20, 8($sp) | ||
382 | addq $sp, 16, $sp | ||
383 | /* Make sure need_resched and sigpending don't change between | ||
384 | sampling and the rti. */ | ||
385 | lda $16, 7 | ||
386 | call_pal PAL_swpipl | ||
387 | ldl $5, TI_FLAGS($8) | ||
388 | and $5, _TIF_WORK_MASK, $2 | ||
389 | beq $2, restore_all | ||
390 | and $5, _TIF_NEED_RESCHED, $2 | ||
391 | bne $2, $work_resched | ||
392 | |||
393 | $work_notifysig: | ||
394 | mov $sp, $17 | ||
395 | br $1, do_switch_stack | ||
396 | mov $5, $21 | ||
397 | mov $sp, $18 | ||
398 | mov $31, $16 | ||
399 | jsr $26, do_notify_resume | ||
400 | bsr $1, undo_switch_stack | ||
401 | br restore_all | ||
402 | .end work_pending | ||
403 | |||
404 | /* | ||
405 | * PTRACE syscall handler | ||
406 | */ | ||
407 | |||
408 | .align 4 | ||
409 | .ent strace | ||
410 | strace: | ||
411 | /* set up signal stack, call syscall_trace */ | ||
412 | bsr $1, do_switch_stack | ||
413 | jsr $26, syscall_trace | ||
414 | bsr $1, undo_switch_stack | ||
415 | |||
416 | /* get the system call number and the arguments back.. */ | ||
417 | ldq $0, 0($sp) | ||
418 | ldq $16, SP_OFF+24($sp) | ||
419 | ldq $17, SP_OFF+32($sp) | ||
420 | ldq $18, SP_OFF+40($sp) | ||
421 | ldq $19, 72($sp) | ||
422 | ldq $20, 80($sp) | ||
423 | ldq $21, 88($sp) | ||
424 | |||
425 | /* get the system call pointer.. */ | ||
426 | lda $1, NR_SYSCALLS($31) | ||
427 | lda $2, sys_call_table | ||
428 | lda $27, alpha_ni_syscall | ||
429 | cmpult $0, $1, $1 | ||
430 | s8addq $0, $2, $2 | ||
431 | beq $1, 1f | ||
432 | ldq $27, 0($2) | ||
433 | 1: jsr $26, ($27), sys_gettimeofday | ||
434 | ldgp $gp, 0($26) | ||
435 | |||
436 | /* check return.. */ | ||
437 | blt $0, $strace_error /* the call failed */ | ||
438 | stq $31, 72($sp) /* a3=0 => no error */ | ||
439 | $strace_success: | ||
440 | stq $0, 0($sp) /* save return value */ | ||
441 | |||
442 | bsr $1, do_switch_stack | ||
443 | jsr $26, syscall_trace | ||
444 | bsr $1, undo_switch_stack | ||
445 | br $31, ret_from_sys_call | ||
446 | |||
447 | .align 3 | ||
448 | $strace_error: | ||
449 | ldq $19, 0($sp) /* old syscall nr (zero if success) */ | ||
450 | beq $19, $strace_success | ||
451 | ldq $20, 72($sp) /* .. and this a3 */ | ||
452 | |||
453 | subq $31, $0, $0 /* with error in v0 */ | ||
454 | addq $31, 1, $1 /* set a3 for errno return */ | ||
455 | stq $0, 0($sp) | ||
456 | stq $1, 72($sp) /* a3 for return */ | ||
457 | |||
458 | bsr $1, do_switch_stack | ||
459 | mov $19, $9 /* save old syscall number */ | ||
460 | mov $20, $10 /* save old a3 */ | ||
461 | jsr $26, syscall_trace | ||
462 | mov $9, $19 | ||
463 | mov $10, $20 | ||
464 | bsr $1, undo_switch_stack | ||
465 | |||
466 | mov $31, $26 /* tell "ret_from_sys_call" we can restart */ | ||
467 | br ret_from_sys_call | ||
468 | .end strace | ||
469 | |||
470 | /* | ||
471 | * Save and restore the switch stack -- aka the balance of the user context. | ||
472 | */ | ||
473 | |||
474 | .align 4 | ||
475 | .ent do_switch_stack | ||
476 | do_switch_stack: | ||
477 | lda $sp, -SWITCH_STACK_SIZE($sp) | ||
478 | stq $9, 0($sp) | ||
479 | stq $10, 8($sp) | ||
480 | stq $11, 16($sp) | ||
481 | stq $12, 24($sp) | ||
482 | stq $13, 32($sp) | ||
483 | stq $14, 40($sp) | ||
484 | stq $15, 48($sp) | ||
485 | stq $26, 56($sp) | ||
486 | stt $f0, 64($sp) | ||
487 | stt $f1, 72($sp) | ||
488 | stt $f2, 80($sp) | ||
489 | stt $f3, 88($sp) | ||
490 | stt $f4, 96($sp) | ||
491 | stt $f5, 104($sp) | ||
492 | stt $f6, 112($sp) | ||
493 | stt $f7, 120($sp) | ||
494 | stt $f8, 128($sp) | ||
495 | stt $f9, 136($sp) | ||
496 | stt $f10, 144($sp) | ||
497 | stt $f11, 152($sp) | ||
498 | stt $f12, 160($sp) | ||
499 | stt $f13, 168($sp) | ||
500 | stt $f14, 176($sp) | ||
501 | stt $f15, 184($sp) | ||
502 | stt $f16, 192($sp) | ||
503 | stt $f17, 200($sp) | ||
504 | stt $f18, 208($sp) | ||
505 | stt $f19, 216($sp) | ||
506 | stt $f20, 224($sp) | ||
507 | stt $f21, 232($sp) | ||
508 | stt $f22, 240($sp) | ||
509 | stt $f23, 248($sp) | ||
510 | stt $f24, 256($sp) | ||
511 | stt $f25, 264($sp) | ||
512 | stt $f26, 272($sp) | ||
513 | stt $f27, 280($sp) | ||
514 | mf_fpcr $f0 # get fpcr | ||
515 | stt $f28, 288($sp) | ||
516 | stt $f29, 296($sp) | ||
517 | stt $f30, 304($sp) | ||
518 | stt $f0, 312($sp) # save fpcr in slot of $f31 | ||
519 | ldt $f0, 64($sp) # dont let "do_switch_stack" change fp state. | ||
520 | ret $31, ($1), 1 | ||
521 | .end do_switch_stack | ||
522 | |||
523 | .align 4 | ||
524 | .ent undo_switch_stack | ||
525 | undo_switch_stack: | ||
526 | ldq $9, 0($sp) | ||
527 | ldq $10, 8($sp) | ||
528 | ldq $11, 16($sp) | ||
529 | ldq $12, 24($sp) | ||
530 | ldq $13, 32($sp) | ||
531 | ldq $14, 40($sp) | ||
532 | ldq $15, 48($sp) | ||
533 | ldq $26, 56($sp) | ||
534 | ldt $f30, 312($sp) # get saved fpcr | ||
535 | ldt $f0, 64($sp) | ||
536 | ldt $f1, 72($sp) | ||
537 | ldt $f2, 80($sp) | ||
538 | ldt $f3, 88($sp) | ||
539 | mt_fpcr $f30 # install saved fpcr | ||
540 | ldt $f4, 96($sp) | ||
541 | ldt $f5, 104($sp) | ||
542 | ldt $f6, 112($sp) | ||
543 | ldt $f7, 120($sp) | ||
544 | ldt $f8, 128($sp) | ||
545 | ldt $f9, 136($sp) | ||
546 | ldt $f10, 144($sp) | ||
547 | ldt $f11, 152($sp) | ||
548 | ldt $f12, 160($sp) | ||
549 | ldt $f13, 168($sp) | ||
550 | ldt $f14, 176($sp) | ||
551 | ldt $f15, 184($sp) | ||
552 | ldt $f16, 192($sp) | ||
553 | ldt $f17, 200($sp) | ||
554 | ldt $f18, 208($sp) | ||
555 | ldt $f19, 216($sp) | ||
556 | ldt $f20, 224($sp) | ||
557 | ldt $f21, 232($sp) | ||
558 | ldt $f22, 240($sp) | ||
559 | ldt $f23, 248($sp) | ||
560 | ldt $f24, 256($sp) | ||
561 | ldt $f25, 264($sp) | ||
562 | ldt $f26, 272($sp) | ||
563 | ldt $f27, 280($sp) | ||
564 | ldt $f28, 288($sp) | ||
565 | ldt $f29, 296($sp) | ||
566 | ldt $f30, 304($sp) | ||
567 | lda $sp, SWITCH_STACK_SIZE($sp) | ||
568 | ret $31, ($1), 1 | ||
569 | .end undo_switch_stack | ||
570 | |||
571 | /* | ||
572 | * The meat of the context switch code. | ||
573 | */ | ||
574 | |||
575 | .align 4 | ||
576 | .globl alpha_switch_to | ||
577 | .ent alpha_switch_to | ||
578 | alpha_switch_to: | ||
579 | .prologue 0 | ||
580 | bsr $1, do_switch_stack | ||
581 | call_pal PAL_swpctx | ||
582 | lda $8, 0x3fff | ||
583 | bsr $1, undo_switch_stack | ||
584 | bic $sp, $8, $8 | ||
585 | mov $17, $0 | ||
586 | ret | ||
587 | .end alpha_switch_to | ||
588 | |||
589 | /* | ||
590 | * New processes begin life here. | ||
591 | */ | ||
592 | |||
593 | .globl ret_from_fork | ||
594 | .align 4 | ||
595 | .ent ret_from_fork | ||
596 | ret_from_fork: | ||
597 | lda $26, ret_from_sys_call | ||
598 | mov $17, $16 | ||
599 | jmp $31, schedule_tail | ||
600 | .end ret_from_fork | ||
601 | |||
602 | /* | ||
603 | * kernel_thread(fn, arg, clone_flags) | ||
604 | */ | ||
605 | .align 4 | ||
606 | .globl kernel_thread | ||
607 | .ent kernel_thread | ||
608 | kernel_thread: | ||
609 | /* We can be called from a module. */ | ||
610 | ldgp $gp, 0($27) | ||
611 | .prologue 1 | ||
612 | subq $sp, SP_OFF+6*8, $sp | ||
613 | br $1, 2f /* load start address */ | ||
614 | |||
615 | /* We've now "returned" from a fake system call. */ | ||
616 | unop | ||
617 | blt $0, 1f /* error? */ | ||
618 | ldi $1, 0x3fff | ||
619 | beq $20, 1f /* parent or child? */ | ||
620 | |||
621 | bic $sp, $1, $8 /* in child. */ | ||
622 | jsr $26, ($27) | ||
623 | ldgp $gp, 0($26) | ||
624 | mov $0, $16 | ||
625 | mov $31, $26 | ||
626 | jmp $31, sys_exit | ||
627 | |||
628 | 1: ret /* in parent. */ | ||
629 | |||
630 | .align 4 | ||
631 | 2: /* Fake a system call stack frame, as we can't do system calls | ||
632 | from kernel space. Note that we store FN and ARG as they | ||
633 | need to be set up in the child for the call. Also store $8 | ||
634 | and $26 for use in the parent. */ | ||
635 | stq $31, SP_OFF($sp) /* ps */ | ||
636 | stq $1, SP_OFF+8($sp) /* pc */ | ||
637 | stq $gp, SP_OFF+16($sp) /* gp */ | ||
638 | stq $16, 136($sp) /* $27; FN for child */ | ||
639 | stq $17, SP_OFF+24($sp) /* $16; ARG for child */ | ||
640 | stq $8, 64($sp) /* $8 */ | ||
641 | stq $26, 128($sp) /* $26 */ | ||
642 | /* Avoid the HAE being gratuitously wrong, to avoid restoring it. */ | ||
643 | ldq $2, alpha_mv+HAE_CACHE | ||
644 | stq $2, 152($sp) /* HAE */ | ||
645 | |||
646 | /* Shuffle FLAGS to the front; add CLONE_VM. */ | ||
647 | ldi $1, CLONE_VM|CLONE_UNTRACED | ||
648 | or $18, $1, $16 | ||
649 | bsr $26, sys_clone | ||
650 | |||
651 | /* We don't actually care for a3 success widgetry in the kernel. | ||
652 | Not for positive errno values. */ | ||
653 | stq $0, 0($sp) /* $0 */ | ||
654 | br restore_all | ||
655 | .end kernel_thread | ||
656 | |||
657 | /* | ||
658 | * execve(path, argv, envp) | ||
659 | */ | ||
660 | .align 4 | ||
661 | .globl execve | ||
662 | .ent execve | ||
663 | execve: | ||
664 | /* We can be called from a module. */ | ||
665 | ldgp $gp, 0($27) | ||
666 | lda $sp, -(32+SIZEOF_PT_REGS+8)($sp) | ||
667 | .frame $sp, 32+SIZEOF_PT_REGS+8, $26, 0 | ||
668 | stq $26, 0($sp) | ||
669 | stq $16, 8($sp) | ||
670 | stq $17, 16($sp) | ||
671 | stq $18, 24($sp) | ||
672 | .prologue 1 | ||
673 | |||
674 | lda $16, 32($sp) | ||
675 | lda $17, 0 | ||
676 | lda $18, SIZEOF_PT_REGS | ||
677 | bsr $26, memset !samegp | ||
678 | |||
679 | /* Avoid the HAE being gratuitously wrong, which would cause us | ||
680 | to do the whole turn off interrupts thing and restore it. */ | ||
681 | ldq $2, alpha_mv+HAE_CACHE | ||
682 | stq $2, 152+32($sp) | ||
683 | |||
684 | ldq $16, 8($sp) | ||
685 | ldq $17, 16($sp) | ||
686 | ldq $18, 24($sp) | ||
687 | lda $19, 32($sp) | ||
688 | bsr $26, do_execve !samegp | ||
689 | |||
690 | ldq $26, 0($sp) | ||
691 | bne $0, 1f /* error! */ | ||
692 | |||
693 | /* Move the temporary pt_regs struct from its current location | ||
694 | to the top of the kernel stack frame. See copy_thread for | ||
695 | details for a normal process. */ | ||
696 | lda $16, 0x4000 - SIZEOF_PT_REGS($8) | ||
697 | lda $17, 32($sp) | ||
698 | lda $18, SIZEOF_PT_REGS | ||
699 | bsr $26, memmove !samegp | ||
700 | |||
701 | /* Take that over as our new stack frame and visit userland! */ | ||
702 | lda $sp, 0x4000 - SIZEOF_PT_REGS($8) | ||
703 | br $31, ret_from_sys_call | ||
704 | |||
705 | 1: lda $sp, 32+SIZEOF_PT_REGS+8($sp) | ||
706 | ret | ||
707 | .end execve | ||
708 | |||
709 | |||
710 | /* | ||
711 | * Special system calls. Most of these are special in that they either | ||
712 | * have to play switch_stack games or in some way use the pt_regs struct. | ||
713 | */ | ||
714 | .align 4 | ||
715 | .globl sys_fork | ||
716 | .ent sys_fork | ||
717 | sys_fork: | ||
718 | .prologue 0 | ||
719 | mov $sp, $21 | ||
720 | bsr $1, do_switch_stack | ||
721 | bis $31, SIGCHLD, $16 | ||
722 | mov $31, $17 | ||
723 | mov $31, $18 | ||
724 | mov $31, $19 | ||
725 | mov $31, $20 | ||
726 | jsr $26, alpha_clone | ||
727 | bsr $1, undo_switch_stack | ||
728 | ret | ||
729 | .end sys_fork | ||
730 | |||
731 | .align 4 | ||
732 | .globl sys_clone | ||
733 | .ent sys_clone | ||
734 | sys_clone: | ||
735 | .prologue 0 | ||
736 | mov $sp, $21 | ||
737 | bsr $1, do_switch_stack | ||
738 | /* $16, $17, $18, $19, $20 come from the user. */ | ||
739 | jsr $26, alpha_clone | ||
740 | bsr $1, undo_switch_stack | ||
741 | ret | ||
742 | .end sys_clone | ||
743 | |||
744 | .align 4 | ||
745 | .globl sys_vfork | ||
746 | .ent sys_vfork | ||
747 | sys_vfork: | ||
748 | .prologue 0 | ||
749 | mov $sp, $16 | ||
750 | bsr $1, do_switch_stack | ||
751 | jsr $26, alpha_vfork | ||
752 | bsr $1, undo_switch_stack | ||
753 | ret | ||
754 | .end sys_vfork | ||
755 | |||
756 | .align 4 | ||
757 | .globl sys_sigreturn | ||
758 | .ent sys_sigreturn | ||
759 | sys_sigreturn: | ||
760 | .prologue 0 | ||
761 | mov $sp, $17 | ||
762 | lda $18, -SWITCH_STACK_SIZE($sp) | ||
763 | lda $sp, -SWITCH_STACK_SIZE($sp) | ||
764 | jsr $26, do_sigreturn | ||
765 | br $1, undo_switch_stack | ||
766 | br ret_from_sys_call | ||
767 | .end sys_sigreturn | ||
768 | |||
769 | .align 4 | ||
770 | .globl sys_rt_sigreturn | ||
771 | .ent sys_rt_sigreturn | ||
772 | sys_rt_sigreturn: | ||
773 | .prologue 0 | ||
774 | mov $sp, $17 | ||
775 | lda $18, -SWITCH_STACK_SIZE($sp) | ||
776 | lda $sp, -SWITCH_STACK_SIZE($sp) | ||
777 | jsr $26, do_rt_sigreturn | ||
778 | br $1, undo_switch_stack | ||
779 | br ret_from_sys_call | ||
780 | .end sys_rt_sigreturn | ||
781 | |||
782 | .align 4 | ||
783 | .globl sys_sigsuspend | ||
784 | .ent sys_sigsuspend | ||
785 | sys_sigsuspend: | ||
786 | .prologue 0 | ||
787 | mov $sp, $17 | ||
788 | br $1, do_switch_stack | ||
789 | mov $sp, $18 | ||
790 | subq $sp, 16, $sp | ||
791 | stq $26, 0($sp) | ||
792 | jsr $26, do_sigsuspend | ||
793 | ldq $26, 0($sp) | ||
794 | lda $sp, SWITCH_STACK_SIZE+16($sp) | ||
795 | ret | ||
796 | .end sys_sigsuspend | ||
797 | |||
798 | .align 4 | ||
799 | .globl sys_rt_sigsuspend | ||
800 | .ent sys_rt_sigsuspend | ||
801 | sys_rt_sigsuspend: | ||
802 | .prologue 0 | ||
803 | mov $sp, $18 | ||
804 | br $1, do_switch_stack | ||
805 | mov $sp, $19 | ||
806 | subq $sp, 16, $sp | ||
807 | stq $26, 0($sp) | ||
808 | jsr $26, do_rt_sigsuspend | ||
809 | ldq $26, 0($sp) | ||
810 | lda $sp, SWITCH_STACK_SIZE+16($sp) | ||
811 | ret | ||
812 | .end sys_rt_sigsuspend | ||
813 | |||
814 | .align 4 | ||
815 | .globl sys_sethae | ||
816 | .ent sys_sethae | ||
817 | sys_sethae: | ||
818 | .prologue 0 | ||
819 | stq $16, 152($sp) | ||
820 | ret | ||
821 | .end sys_sethae | ||
822 | |||
823 | .align 4 | ||
824 | .globl osf_getpriority | ||
825 | .ent osf_getpriority | ||
826 | osf_getpriority: | ||
827 | lda $sp, -16($sp) | ||
828 | stq $26, 0($sp) | ||
829 | .prologue 0 | ||
830 | |||
831 | jsr $26, sys_getpriority | ||
832 | |||
833 | ldq $26, 0($sp) | ||
834 | blt $0, 1f | ||
835 | |||
836 | /* Return value is the unbiased priority, i.e. 20 - prio. | ||
837 | This does result in negative return values, so signal | ||
838 | no error by writing into the R0 slot. */ | ||
839 | lda $1, 20 | ||
840 | stq $31, 16($sp) | ||
841 | subl $1, $0, $0 | ||
842 | unop | ||
843 | |||
844 | 1: lda $sp, 16($sp) | ||
845 | ret | ||
846 | .end osf_getpriority | ||
847 | |||
848 | .align 4 | ||
849 | .globl sys_getxuid | ||
850 | .ent sys_getxuid | ||
851 | sys_getxuid: | ||
852 | .prologue 0 | ||
853 | ldq $2, TI_TASK($8) | ||
854 | ldl $0, TASK_UID($2) | ||
855 | ldl $1, TASK_EUID($2) | ||
856 | stq $1, 80($sp) | ||
857 | ret | ||
858 | .end sys_getxuid | ||
859 | |||
860 | .align 4 | ||
861 | .globl sys_getxgid | ||
862 | .ent sys_getxgid | ||
863 | sys_getxgid: | ||
864 | .prologue 0 | ||
865 | ldq $2, TI_TASK($8) | ||
866 | ldl $0, TASK_GID($2) | ||
867 | ldl $1, TASK_EGID($2) | ||
868 | stq $1, 80($sp) | ||
869 | ret | ||
870 | .end sys_getxgid | ||
871 | |||
872 | .align 4 | ||
873 | .globl sys_getxpid | ||
874 | .ent sys_getxpid | ||
875 | sys_getxpid: | ||
876 | .prologue 0 | ||
877 | ldq $2, TI_TASK($8) | ||
878 | |||
879 | /* See linux/kernel/timer.c sys_getppid for discussion | ||
880 | about this loop. */ | ||
881 | ldq $3, TASK_REAL_PARENT($2) | ||
882 | 1: ldl $1, TASK_TGID($3) | ||
883 | #ifdef CONFIG_SMP | ||
884 | mov $3, $4 | ||
885 | mb | ||
886 | ldq $3, TASK_REAL_PARENT($2) | ||
887 | cmpeq $3, $4, $4 | ||
888 | beq $4, 1b | ||
889 | #endif | ||
890 | stq $1, 80($sp) | ||
891 | ldl $0, TASK_TGID($2) | ||
892 | ret | ||
893 | .end sys_getxpid | ||
894 | |||
895 | .align 4 | ||
896 | .globl sys_pipe | ||
897 | .ent sys_pipe | ||
898 | sys_pipe: | ||
899 | lda $sp, -16($sp) | ||
900 | stq $26, 0($sp) | ||
901 | .prologue 0 | ||
902 | |||
903 | lda $16, 8($sp) | ||
904 | jsr $26, do_pipe | ||
905 | |||
906 | ldq $26, 0($sp) | ||
907 | bne $0, 1f | ||
908 | |||
909 | /* The return values are in $0 and $20. */ | ||
910 | ldl $1, 12($sp) | ||
911 | ldl $0, 8($sp) | ||
912 | |||
913 | stq $1, 80+16($sp) | ||
914 | 1: lda $sp, 16($sp) | ||
915 | ret | ||
916 | .end sys_pipe | ||
917 | |||
918 | .align 4 | ||
919 | .globl sys_ptrace | ||
920 | .ent sys_ptrace | ||
921 | sys_ptrace: | ||
922 | .prologue 0 | ||
923 | mov $sp, $20 | ||
924 | jmp $31, do_sys_ptrace | ||
925 | .end sys_ptrace | ||
926 | |||
927 | .align 4 | ||
928 | .globl sys_execve | ||
929 | .ent sys_execve | ||
930 | sys_execve: | ||
931 | .prologue 0 | ||
932 | mov $sp, $19 | ||
933 | jmp $31, do_sys_execve | ||
934 | .end sys_execve | ||
935 | |||
936 | .align 4 | ||
937 | .globl osf_sigprocmask | ||
938 | .ent osf_sigprocmask | ||
939 | osf_sigprocmask: | ||
940 | .prologue 0 | ||
941 | mov $sp, $18 | ||
942 | jmp $31, do_osf_sigprocmask | ||
943 | .end osf_sigprocmask | ||
944 | |||
945 | .align 4 | ||
946 | .globl alpha_ni_syscall | ||
947 | .ent alpha_ni_syscall | ||
948 | alpha_ni_syscall: | ||
949 | .prologue 0 | ||
950 | /* Special because it also implements overflow handling via | ||
951 | syscall number 0. And if you recall, zero is a special | ||
952 | trigger for "not an error". Store large non-zero there. */ | ||
953 | lda $0, -ENOSYS | ||
954 | unop | ||
955 | stq $0, 0($sp) | ||
956 | ret | ||
957 | .end alpha_ni_syscall | ||
diff --git a/arch/alpha/kernel/err_common.c b/arch/alpha/kernel/err_common.c new file mode 100644 index 000000000000..687580b16b41 --- /dev/null +++ b/arch/alpha/kernel/err_common.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_common.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | * Error handling code supporting Alpha systems | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/sched.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/hwrpb.h> | ||
15 | #include <asm/smp.h> | ||
16 | #include <asm/err_common.h> | ||
17 | |||
18 | #include "err_impl.h" | ||
19 | #include "proto.h" | ||
20 | |||
21 | /* | ||
22 | * err_print_prefix -- error handling print routines should prefix | ||
23 | * all prints with this | ||
24 | */ | ||
25 | char *err_print_prefix = KERN_NOTICE; | ||
26 | |||
27 | |||
28 | /* | ||
29 | * Generic | ||
30 | */ | ||
31 | void | ||
32 | mchk_dump_mem(void *data, size_t length, char **annotation) | ||
33 | { | ||
34 | unsigned long *ldata = data; | ||
35 | size_t i; | ||
36 | |||
37 | for (i = 0; (i * sizeof(*ldata)) < length; i++) { | ||
38 | if (annotation && !annotation[i]) | ||
39 | annotation = NULL; | ||
40 | printk("%s %08x: %016lx %s\n", | ||
41 | err_print_prefix, | ||
42 | (unsigned)(i * sizeof(*ldata)), ldata[i], | ||
43 | annotation ? annotation[i] : ""); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | void | ||
48 | mchk_dump_logout_frame(struct el_common *mchk_header) | ||
49 | { | ||
50 | printk("%s -- Frame Header --\n" | ||
51 | " Frame Size: %d (0x%x) bytes\n" | ||
52 | " Flags: %s%s\n" | ||
53 | " MCHK Code: 0x%x\n" | ||
54 | " Frame Rev: %d\n" | ||
55 | " Proc Offset: 0x%08x\n" | ||
56 | " Sys Offset: 0x%08x\n" | ||
57 | " -- Processor Region --\n", | ||
58 | err_print_prefix, | ||
59 | mchk_header->size, mchk_header->size, | ||
60 | mchk_header->retry ? "RETRY " : "", | ||
61 | mchk_header->err2 ? "SECOND_ERR " : "", | ||
62 | mchk_header->code, | ||
63 | mchk_header->frame_rev, | ||
64 | mchk_header->proc_offset, | ||
65 | mchk_header->sys_offset); | ||
66 | |||
67 | mchk_dump_mem((void *) | ||
68 | ((unsigned long)mchk_header + mchk_header->proc_offset), | ||
69 | mchk_header->sys_offset - mchk_header->proc_offset, | ||
70 | NULL); | ||
71 | |||
72 | printk("%s -- System Region --\n", err_print_prefix); | ||
73 | mchk_dump_mem((void *) | ||
74 | ((unsigned long)mchk_header + mchk_header->sys_offset), | ||
75 | mchk_header->size - mchk_header->sys_offset, | ||
76 | NULL); | ||
77 | printk("%s -- End of Frame --\n", err_print_prefix); | ||
78 | } | ||
79 | |||
80 | |||
81 | /* | ||
82 | * Console Data Log | ||
83 | */ | ||
84 | /* Data */ | ||
85 | static struct el_subpacket_handler *subpacket_handler_list = NULL; | ||
86 | static struct el_subpacket_annotation *subpacket_annotation_list = NULL; | ||
87 | |||
88 | static struct el_subpacket * | ||
89 | el_process_header_subpacket(struct el_subpacket *header) | ||
90 | { | ||
91 | union el_timestamp timestamp; | ||
92 | char *name = "UNKNOWN EVENT"; | ||
93 | int packet_count = 0; | ||
94 | int length = 0; | ||
95 | |||
96 | if (header->class != EL_CLASS__HEADER) { | ||
97 | printk("%s** Unexpected header CLASS %d TYPE %d, aborting\n", | ||
98 | err_print_prefix, | ||
99 | header->class, header->type); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
103 | switch(header->type) { | ||
104 | case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME: | ||
105 | name = "SYSTEM ERROR"; | ||
106 | length = header->by_type.sys_err.frame_length; | ||
107 | packet_count = | ||
108 | header->by_type.sys_err.frame_packet_count; | ||
109 | timestamp.as_int = 0; | ||
110 | break; | ||
111 | case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME: | ||
112 | name = "SYSTEM EVENT"; | ||
113 | length = header->by_type.sys_event.frame_length; | ||
114 | packet_count = | ||
115 | header->by_type.sys_event.frame_packet_count; | ||
116 | timestamp = header->by_type.sys_event.timestamp; | ||
117 | break; | ||
118 | case EL_TYPE__HEADER__HALT_FRAME: | ||
119 | name = "ERROR HALT"; | ||
120 | length = header->by_type.err_halt.frame_length; | ||
121 | packet_count = | ||
122 | header->by_type.err_halt.frame_packet_count; | ||
123 | timestamp = header->by_type.err_halt.timestamp; | ||
124 | break; | ||
125 | case EL_TYPE__HEADER__LOGOUT_FRAME: | ||
126 | name = "LOGOUT FRAME"; | ||
127 | length = header->by_type.logout_header.frame_length; | ||
128 | packet_count = 1; | ||
129 | timestamp.as_int = 0; | ||
130 | break; | ||
131 | default: /* Unknown */ | ||
132 | printk("%s** Unknown header - CLASS %d TYPE %d, aborting\n", | ||
133 | err_print_prefix, | ||
134 | header->class, header->type); | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | printk("%s*** %s:\n" | ||
139 | " CLASS %d, TYPE %d\n", | ||
140 | err_print_prefix, | ||
141 | name, | ||
142 | header->class, header->type); | ||
143 | el_print_timestamp(×tamp); | ||
144 | |||
145 | /* | ||
146 | * Process the subpackets | ||
147 | */ | ||
148 | el_process_subpackets(header, packet_count); | ||
149 | |||
150 | /* return the next header */ | ||
151 | header = (struct el_subpacket *) | ||
152 | ((unsigned long)header + header->length + length); | ||
153 | return header; | ||
154 | } | ||
155 | |||
156 | static struct el_subpacket * | ||
157 | el_process_subpacket_reg(struct el_subpacket *header) | ||
158 | { | ||
159 | struct el_subpacket *next = NULL; | ||
160 | struct el_subpacket_handler *h = subpacket_handler_list; | ||
161 | |||
162 | for (; h && h->class != header->class; h = h->next); | ||
163 | if (h) next = h->handler(header); | ||
164 | |||
165 | return next; | ||
166 | } | ||
167 | |||
168 | void | ||
169 | el_print_timestamp(union el_timestamp *timestamp) | ||
170 | { | ||
171 | if (timestamp->as_int) | ||
172 | printk("%s TIMESTAMP: %d/%d/%02d %d:%02d:%0d\n", | ||
173 | err_print_prefix, | ||
174 | timestamp->b.month, timestamp->b.day, | ||
175 | timestamp->b.year, timestamp->b.hour, | ||
176 | timestamp->b.minute, timestamp->b.second); | ||
177 | } | ||
178 | |||
179 | void | ||
180 | el_process_subpackets(struct el_subpacket *header, int packet_count) | ||
181 | { | ||
182 | struct el_subpacket *subpacket; | ||
183 | int i; | ||
184 | |||
185 | subpacket = (struct el_subpacket *) | ||
186 | ((unsigned long)header + header->length); | ||
187 | |||
188 | for (i = 0; subpacket && i < packet_count; i++) { | ||
189 | printk("%sPROCESSING SUBPACKET %d\n", err_print_prefix, i); | ||
190 | subpacket = el_process_subpacket(subpacket); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | struct el_subpacket * | ||
195 | el_process_subpacket(struct el_subpacket *header) | ||
196 | { | ||
197 | struct el_subpacket *next = NULL; | ||
198 | |||
199 | switch(header->class) { | ||
200 | case EL_CLASS__TERMINATION: | ||
201 | /* Termination packet, there are no more */ | ||
202 | break; | ||
203 | case EL_CLASS__HEADER: | ||
204 | next = el_process_header_subpacket(header); | ||
205 | break; | ||
206 | default: | ||
207 | if (NULL == (next = el_process_subpacket_reg(header))) { | ||
208 | printk("%s** Unexpected header CLASS %d TYPE %d" | ||
209 | " -- aborting.\n", | ||
210 | err_print_prefix, | ||
211 | header->class, header->type); | ||
212 | } | ||
213 | break; | ||
214 | } | ||
215 | |||
216 | return next; | ||
217 | } | ||
218 | |||
219 | void | ||
220 | el_annotate_subpacket(struct el_subpacket *header) | ||
221 | { | ||
222 | struct el_subpacket_annotation *a; | ||
223 | char **annotation = NULL; | ||
224 | |||
225 | for (a = subpacket_annotation_list; a; a = a->next) { | ||
226 | if (a->class == header->class && | ||
227 | a->type == header->type && | ||
228 | a->revision == header->revision) { | ||
229 | /* | ||
230 | * We found the annotation | ||
231 | */ | ||
232 | annotation = a->annotation; | ||
233 | printk("%s %s\n", err_print_prefix, a->description); | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | mchk_dump_mem(header, header->length, annotation); | ||
239 | } | ||
240 | |||
241 | static void __init | ||
242 | cdl_process_console_data_log(int cpu, struct percpu_struct *pcpu) | ||
243 | { | ||
244 | struct el_subpacket *header = (struct el_subpacket *) | ||
245 | (IDENT_ADDR | pcpu->console_data_log_pa); | ||
246 | int err; | ||
247 | |||
248 | printk("%s******* CONSOLE DATA LOG FOR CPU %d. *******\n" | ||
249 | "*** Error(s) were logged on a previous boot\n", | ||
250 | err_print_prefix, cpu); | ||
251 | |||
252 | for (err = 0; header && (header->class != EL_CLASS__TERMINATION); err++) | ||
253 | header = el_process_subpacket(header); | ||
254 | |||
255 | /* let the console know it's ok to clear the error(s) at restart */ | ||
256 | pcpu->console_data_log_pa = 0; | ||
257 | |||
258 | printk("%s*** %d total error(s) logged\n" | ||
259 | "**** END OF CONSOLE DATA LOG FOR CPU %d ****\n", | ||
260 | err_print_prefix, err, cpu); | ||
261 | } | ||
262 | |||
263 | void __init | ||
264 | cdl_check_console_data_log(void) | ||
265 | { | ||
266 | struct percpu_struct *pcpu; | ||
267 | unsigned long cpu; | ||
268 | |||
269 | for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) { | ||
270 | pcpu = (struct percpu_struct *) | ||
271 | ((unsigned long)hwrpb + hwrpb->processor_offset | ||
272 | + cpu * hwrpb->processor_size); | ||
273 | if (pcpu->console_data_log_pa) | ||
274 | cdl_process_console_data_log(cpu, pcpu); | ||
275 | } | ||
276 | |||
277 | } | ||
278 | |||
279 | int __init | ||
280 | cdl_register_subpacket_annotation(struct el_subpacket_annotation *new) | ||
281 | { | ||
282 | struct el_subpacket_annotation *a = subpacket_annotation_list; | ||
283 | |||
284 | if (a == NULL) subpacket_annotation_list = new; | ||
285 | else { | ||
286 | for (; a->next != NULL; a = a->next) { | ||
287 | if ((a->class == new->class && a->type == new->type) || | ||
288 | a == new) { | ||
289 | printk("Attempted to re-register " | ||
290 | "subpacket annotation\n"); | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | } | ||
294 | a->next = new; | ||
295 | } | ||
296 | new->next = NULL; | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | int __init | ||
302 | cdl_register_subpacket_handler(struct el_subpacket_handler *new) | ||
303 | { | ||
304 | struct el_subpacket_handler *h = subpacket_handler_list; | ||
305 | |||
306 | if (h == NULL) subpacket_handler_list = new; | ||
307 | else { | ||
308 | for (; h->next != NULL; h = h->next) { | ||
309 | if (h->class == new->class || h == new) { | ||
310 | printk("Attempted to re-register " | ||
311 | "subpacket handler\n"); | ||
312 | return -EINVAL; | ||
313 | } | ||
314 | } | ||
315 | h->next = new; | ||
316 | } | ||
317 | new->next = NULL; | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
diff --git a/arch/alpha/kernel/err_ev6.c b/arch/alpha/kernel/err_ev6.c new file mode 100644 index 000000000000..64f59f2fcf5c --- /dev/null +++ b/arch/alpha/kernel/err_ev6.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_ev6.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | * Error handling code supporting Alpha systems | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/sched.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/hwrpb.h> | ||
15 | #include <asm/smp.h> | ||
16 | #include <asm/err_common.h> | ||
17 | #include <asm/err_ev6.h> | ||
18 | |||
19 | #include "err_impl.h" | ||
20 | #include "proto.h" | ||
21 | |||
22 | static int | ||
23 | ev6_parse_ibox(u64 i_stat, int print) | ||
24 | { | ||
25 | int status = MCHK_DISPOSITION_REPORT; | ||
26 | |||
27 | #define EV6__I_STAT__PAR (1UL << 29) | ||
28 | #define EV6__I_STAT__ERRMASK (EV6__I_STAT__PAR) | ||
29 | |||
30 | if (!(i_stat & EV6__I_STAT__ERRMASK)) | ||
31 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
32 | |||
33 | if (!print) | ||
34 | return status; | ||
35 | |||
36 | if (i_stat & EV6__I_STAT__PAR) | ||
37 | printk("%s Icache parity error\n", err_print_prefix); | ||
38 | |||
39 | return status; | ||
40 | } | ||
41 | |||
42 | static int | ||
43 | ev6_parse_mbox(u64 mm_stat, u64 d_stat, u64 c_stat, int print) | ||
44 | { | ||
45 | int status = MCHK_DISPOSITION_REPORT; | ||
46 | |||
47 | #define EV6__MM_STAT__DC_TAG_PERR (1UL << 10) | ||
48 | #define EV6__MM_STAT__ERRMASK (EV6__MM_STAT__DC_TAG_PERR) | ||
49 | #define EV6__D_STAT__TPERR_P0 (1UL << 0) | ||
50 | #define EV6__D_STAT__TPERR_P1 (1UL << 1) | ||
51 | #define EV6__D_STAT__ECC_ERR_ST (1UL << 2) | ||
52 | #define EV6__D_STAT__ECC_ERR_LD (1UL << 3) | ||
53 | #define EV6__D_STAT__SEO (1UL << 4) | ||
54 | #define EV6__D_STAT__ERRMASK (EV6__D_STAT__TPERR_P0 | \ | ||
55 | EV6__D_STAT__TPERR_P1 | \ | ||
56 | EV6__D_STAT__ECC_ERR_ST | \ | ||
57 | EV6__D_STAT__ECC_ERR_LD | \ | ||
58 | EV6__D_STAT__SEO) | ||
59 | |||
60 | if (!(d_stat & EV6__D_STAT__ERRMASK) && | ||
61 | !(mm_stat & EV6__MM_STAT__ERRMASK)) | ||
62 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
63 | |||
64 | if (!print) | ||
65 | return status; | ||
66 | |||
67 | if (mm_stat & EV6__MM_STAT__DC_TAG_PERR) | ||
68 | printk("%s Dcache tag parity error on probe\n", | ||
69 | err_print_prefix); | ||
70 | if (d_stat & EV6__D_STAT__TPERR_P0) | ||
71 | printk("%s Dcache tag parity error - pipe 0\n", | ||
72 | err_print_prefix); | ||
73 | if (d_stat & EV6__D_STAT__TPERR_P1) | ||
74 | printk("%s Dcache tag parity error - pipe 1\n", | ||
75 | err_print_prefix); | ||
76 | if (d_stat & EV6__D_STAT__ECC_ERR_ST) | ||
77 | printk("%s ECC error occurred on a store\n", | ||
78 | err_print_prefix); | ||
79 | if (d_stat & EV6__D_STAT__ECC_ERR_LD) | ||
80 | printk("%s ECC error occurred on a %s load\n", | ||
81 | err_print_prefix, | ||
82 | c_stat ? "" : "speculative "); | ||
83 | if (d_stat & EV6__D_STAT__SEO) | ||
84 | printk("%s Dcache second error\n", err_print_prefix); | ||
85 | |||
86 | return status; | ||
87 | } | ||
88 | |||
89 | static int | ||
90 | ev6_parse_cbox(u64 c_addr, u64 c1_syn, u64 c2_syn, | ||
91 | u64 c_stat, u64 c_sts, int print) | ||
92 | { | ||
93 | char *sourcename[] = { "UNKNOWN", "UNKNOWN", "UNKNOWN", | ||
94 | "MEMORY", "BCACHE", "DCACHE", | ||
95 | "BCACHE PROBE", "BCACHE PROBE" }; | ||
96 | char *streamname[] = { "D", "I" }; | ||
97 | char *bitsname[] = { "SINGLE", "DOUBLE" }; | ||
98 | int status = MCHK_DISPOSITION_REPORT; | ||
99 | int source = -1, stream = -1, bits = -1; | ||
100 | |||
101 | #define EV6__C_STAT__BC_PERR (0x01) | ||
102 | #define EV6__C_STAT__DC_PERR (0x02) | ||
103 | #define EV6__C_STAT__DSTREAM_MEM_ERR (0x03) | ||
104 | #define EV6__C_STAT__DSTREAM_BC_ERR (0x04) | ||
105 | #define EV6__C_STAT__DSTREAM_DC_ERR (0x05) | ||
106 | #define EV6__C_STAT__PROBE_BC_ERR0 (0x06) /* both 6 and 7 indicate... */ | ||
107 | #define EV6__C_STAT__PROBE_BC_ERR1 (0x07) /* ...probe bc error. */ | ||
108 | #define EV6__C_STAT__ISTREAM_MEM_ERR (0x0B) | ||
109 | #define EV6__C_STAT__ISTREAM_BC_ERR (0x0C) | ||
110 | #define EV6__C_STAT__DSTREAM_MEM_DBL (0x13) | ||
111 | #define EV6__C_STAT__DSTREAM_BC_DBL (0x14) | ||
112 | #define EV6__C_STAT__ISTREAM_MEM_DBL (0x1B) | ||
113 | #define EV6__C_STAT__ISTREAM_BC_DBL (0x1C) | ||
114 | #define EV6__C_STAT__SOURCE_MEMORY (0x03) | ||
115 | #define EV6__C_STAT__SOURCE_BCACHE (0x04) | ||
116 | #define EV6__C_STAT__SOURCE__S (0) | ||
117 | #define EV6__C_STAT__SOURCE__M (0x07) | ||
118 | #define EV6__C_STAT__ISTREAM__S (3) | ||
119 | #define EV6__C_STAT__ISTREAM__M (0x01) | ||
120 | #define EV6__C_STAT__DOUBLE__S (4) | ||
121 | #define EV6__C_STAT__DOUBLE__M (0x01) | ||
122 | #define EV6__C_STAT__ERRMASK (0x1F) | ||
123 | #define EV6__C_STS__SHARED (1 << 0) | ||
124 | #define EV6__C_STS__DIRTY (1 << 1) | ||
125 | #define EV6__C_STS__VALID (1 << 2) | ||
126 | #define EV6__C_STS__PARITY (1 << 3) | ||
127 | |||
128 | if (!(c_stat & EV6__C_STAT__ERRMASK)) | ||
129 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
130 | |||
131 | if (!print) | ||
132 | return status; | ||
133 | |||
134 | source = EXTRACT(c_stat, EV6__C_STAT__SOURCE); | ||
135 | stream = EXTRACT(c_stat, EV6__C_STAT__ISTREAM); | ||
136 | bits = EXTRACT(c_stat, EV6__C_STAT__DOUBLE); | ||
137 | |||
138 | if (c_stat & EV6__C_STAT__BC_PERR) { | ||
139 | printk("%s Bcache tag parity error\n", err_print_prefix); | ||
140 | source = -1; | ||
141 | } | ||
142 | |||
143 | if (c_stat & EV6__C_STAT__DC_PERR) { | ||
144 | printk("%s Dcache tag parity error\n", err_print_prefix); | ||
145 | source = -1; | ||
146 | } | ||
147 | |||
148 | if (c_stat == EV6__C_STAT__PROBE_BC_ERR0 || | ||
149 | c_stat == EV6__C_STAT__PROBE_BC_ERR1) { | ||
150 | printk("%s Bcache single-bit error on a probe hit\n", | ||
151 | err_print_prefix); | ||
152 | source = -1; | ||
153 | } | ||
154 | |||
155 | if (source != -1) | ||
156 | printk("%s %s-STREAM %s-BIT ECC error from %s\n", | ||
157 | err_print_prefix, | ||
158 | streamname[stream], bitsname[bits], sourcename[source]); | ||
159 | |||
160 | printk("%s Address: 0x%016lx\n" | ||
161 | " Syndrome[upper.lower]: %02lx.%02lx\n", | ||
162 | err_print_prefix, | ||
163 | c_addr, | ||
164 | c2_syn, c1_syn); | ||
165 | |||
166 | if (source == EV6__C_STAT__SOURCE_MEMORY || | ||
167 | source == EV6__C_STAT__SOURCE_BCACHE) | ||
168 | printk("%s Block status: %s%s%s%s\n", | ||
169 | err_print_prefix, | ||
170 | (c_sts & EV6__C_STS__SHARED) ? "SHARED " : "", | ||
171 | (c_sts & EV6__C_STS__DIRTY) ? "DIRTY " : "", | ||
172 | (c_sts & EV6__C_STS__VALID) ? "VALID " : "", | ||
173 | (c_sts & EV6__C_STS__PARITY) ? "PARITY " : ""); | ||
174 | |||
175 | return status; | ||
176 | } | ||
177 | |||
178 | void | ||
179 | ev6_register_error_handlers(void) | ||
180 | { | ||
181 | /* None right now. */ | ||
182 | } | ||
183 | |||
184 | int | ||
185 | ev6_process_logout_frame(struct el_common *mchk_header, int print) | ||
186 | { | ||
187 | struct el_common_EV6_mcheck *ev6mchk = | ||
188 | (struct el_common_EV6_mcheck *)mchk_header; | ||
189 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
190 | |||
191 | status |= ev6_parse_ibox(ev6mchk->I_STAT, print); | ||
192 | status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT, | ||
193 | ev6mchk->C_STAT, print); | ||
194 | status |= ev6_parse_cbox(ev6mchk->C_ADDR, ev6mchk->DC1_SYNDROME, | ||
195 | ev6mchk->DC0_SYNDROME, ev6mchk->C_STAT, | ||
196 | ev6mchk->C_STS, print); | ||
197 | |||
198 | if (!print) | ||
199 | return status; | ||
200 | |||
201 | if (status != MCHK_DISPOSITION_DISMISS) { | ||
202 | char *saved_err_prefix = err_print_prefix; | ||
203 | |||
204 | /* | ||
205 | * Dump some additional information from the frame | ||
206 | */ | ||
207 | printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" | ||
208 | " ISUM: 0x%016lx\n" | ||
209 | " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" | ||
210 | " PCTX: 0x%016lx\n", | ||
211 | err_print_prefix, | ||
212 | ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, | ||
213 | ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); | ||
214 | |||
215 | if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) { | ||
216 | printk("%s UNKNOWN error, frame follows:\n", | ||
217 | err_print_prefix); | ||
218 | } else { | ||
219 | /* had decode -- downgrade print level for frame */ | ||
220 | err_print_prefix = KERN_NOTICE; | ||
221 | } | ||
222 | |||
223 | mchk_dump_logout_frame(mchk_header); | ||
224 | |||
225 | err_print_prefix = saved_err_prefix; | ||
226 | } | ||
227 | |||
228 | return status; | ||
229 | } | ||
230 | |||
231 | void | ||
232 | ev6_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) | ||
233 | { | ||
234 | struct el_common *mchk_header = (struct el_common *)la_ptr; | ||
235 | |||
236 | /* | ||
237 | * Sync the processor | ||
238 | */ | ||
239 | mb(); | ||
240 | draina(); | ||
241 | |||
242 | /* | ||
243 | * Parse the logout frame without printing first. If the only error(s) | ||
244 | * found are have a disposition of "dismiss", then just dismiss them | ||
245 | * and don't print any message | ||
246 | */ | ||
247 | if (ev6_process_logout_frame(mchk_header, 0) != | ||
248 | MCHK_DISPOSITION_DISMISS) { | ||
249 | char *saved_err_prefix = err_print_prefix; | ||
250 | err_print_prefix = KERN_CRIT; | ||
251 | |||
252 | /* | ||
253 | * Either a nondismissable error was detected or no | ||
254 | * recognized error was detected in the logout frame | ||
255 | * -- report the error in either case | ||
256 | */ | ||
257 | printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d:\n", | ||
258 | err_print_prefix, | ||
259 | (vector == SCB_Q_PROCERR)?"Correctable":"Uncorrectable", | ||
260 | (unsigned int)vector, (int)smp_processor_id()); | ||
261 | |||
262 | ev6_process_logout_frame(mchk_header, 1); | ||
263 | dik_show_regs(regs, NULL); | ||
264 | |||
265 | err_print_prefix = saved_err_prefix; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * Release the logout frame | ||
270 | */ | ||
271 | wrmces(0x7); | ||
272 | mb(); | ||
273 | } | ||
274 | |||
diff --git a/arch/alpha/kernel/err_ev7.c b/arch/alpha/kernel/err_ev7.c new file mode 100644 index 000000000000..bf52ba691957 --- /dev/null +++ b/arch/alpha/kernel/err_ev7.c | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_ev7.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | * Error handling code supporting Alpha systems | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/sched.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/hwrpb.h> | ||
15 | #include <asm/smp.h> | ||
16 | #include <asm/err_common.h> | ||
17 | #include <asm/err_ev7.h> | ||
18 | |||
19 | #include "err_impl.h" | ||
20 | #include "proto.h" | ||
21 | |||
22 | struct ev7_lf_subpackets * | ||
23 | ev7_collect_logout_frame_subpackets(struct el_subpacket *el_ptr, | ||
24 | struct ev7_lf_subpackets *lf_subpackets) | ||
25 | { | ||
26 | struct el_subpacket *subpacket; | ||
27 | int i; | ||
28 | |||
29 | /* | ||
30 | * A Marvel machine check frame is always packaged in an | ||
31 | * el_subpacket of class HEADER, type LOGOUT_FRAME. | ||
32 | */ | ||
33 | if (el_ptr->class != EL_CLASS__HEADER || | ||
34 | el_ptr->type != EL_TYPE__HEADER__LOGOUT_FRAME) | ||
35 | return NULL; | ||
36 | |||
37 | /* | ||
38 | * It is a logout frame header. Look at the one subpacket. | ||
39 | */ | ||
40 | el_ptr = (struct el_subpacket *) | ||
41 | ((unsigned long)el_ptr + el_ptr->length); | ||
42 | |||
43 | /* | ||
44 | * It has to be class PAL, type LOGOUT_FRAME. | ||
45 | */ | ||
46 | if (el_ptr->class != EL_CLASS__PAL || | ||
47 | el_ptr->type != EL_TYPE__PAL__LOGOUT_FRAME) | ||
48 | return NULL; | ||
49 | |||
50 | lf_subpackets->logout = (struct ev7_pal_logout_subpacket *) | ||
51 | el_ptr->by_type.raw.data_start; | ||
52 | |||
53 | /* | ||
54 | * Process the subpackets. | ||
55 | */ | ||
56 | subpacket = (struct el_subpacket *) | ||
57 | ((unsigned long)el_ptr + el_ptr->length); | ||
58 | for (i = 0; | ||
59 | subpacket && i < lf_subpackets->logout->subpacket_count; | ||
60 | subpacket = (struct el_subpacket *) | ||
61 | ((unsigned long)subpacket + subpacket->length), i++) { | ||
62 | /* | ||
63 | * All subpackets should be class PAL. | ||
64 | */ | ||
65 | if (subpacket->class != EL_CLASS__PAL) { | ||
66 | printk("%s**UNEXPECTED SUBPACKET CLASS %d " | ||
67 | "IN LOGOUT FRAME (packet %d\n", | ||
68 | err_print_prefix, subpacket->class, i); | ||
69 | return NULL; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Remember the subpacket. | ||
74 | */ | ||
75 | switch(subpacket->type) { | ||
76 | case EL_TYPE__PAL__EV7_PROCESSOR: | ||
77 | lf_subpackets->ev7 = | ||
78 | (struct ev7_pal_processor_subpacket *) | ||
79 | subpacket->by_type.raw.data_start; | ||
80 | break; | ||
81 | |||
82 | case EL_TYPE__PAL__EV7_RBOX: | ||
83 | lf_subpackets->rbox = (struct ev7_pal_rbox_subpacket *) | ||
84 | subpacket->by_type.raw.data_start; | ||
85 | break; | ||
86 | |||
87 | case EL_TYPE__PAL__EV7_ZBOX: | ||
88 | lf_subpackets->zbox = (struct ev7_pal_zbox_subpacket *) | ||
89 | subpacket->by_type.raw.data_start; | ||
90 | break; | ||
91 | |||
92 | case EL_TYPE__PAL__EV7_IO: | ||
93 | lf_subpackets->io = (struct ev7_pal_io_subpacket *) | ||
94 | subpacket->by_type.raw.data_start; | ||
95 | break; | ||
96 | |||
97 | case EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE: | ||
98 | case EL_TYPE__PAL__ENV__AIRMOVER_FAN: | ||
99 | case EL_TYPE__PAL__ENV__VOLTAGE: | ||
100 | case EL_TYPE__PAL__ENV__INTRUSION: | ||
101 | case EL_TYPE__PAL__ENV__POWER_SUPPLY: | ||
102 | case EL_TYPE__PAL__ENV__LAN: | ||
103 | case EL_TYPE__PAL__ENV__HOT_PLUG: | ||
104 | lf_subpackets->env[ev7_lf_env_index(subpacket->type)] = | ||
105 | (struct ev7_pal_environmental_subpacket *) | ||
106 | subpacket->by_type.raw.data_start; | ||
107 | break; | ||
108 | |||
109 | default: | ||
110 | /* | ||
111 | * Don't know what kind of frame this is. | ||
112 | */ | ||
113 | return NULL; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | return lf_subpackets; | ||
118 | } | ||
119 | |||
120 | void | ||
121 | ev7_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) | ||
122 | { | ||
123 | struct el_subpacket *el_ptr = (struct el_subpacket *)la_ptr; | ||
124 | char *saved_err_prefix = err_print_prefix; | ||
125 | |||
126 | /* | ||
127 | * Sync the processor | ||
128 | */ | ||
129 | mb(); | ||
130 | draina(); | ||
131 | |||
132 | err_print_prefix = KERN_CRIT; | ||
133 | printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d\n", | ||
134 | err_print_prefix, | ||
135 | (vector == SCB_Q_PROCERR) ? "Correctable" : "Uncorrectable", | ||
136 | (unsigned int)vector, (int)smp_processor_id()); | ||
137 | el_process_subpacket(el_ptr); | ||
138 | err_print_prefix = saved_err_prefix; | ||
139 | |||
140 | /* | ||
141 | * Release the logout frame | ||
142 | */ | ||
143 | wrmces(0x7); | ||
144 | mb(); | ||
145 | } | ||
146 | |||
147 | static char *el_ev7_processor_subpacket_annotation[] = { | ||
148 | "Subpacket Header", "I_STAT", "DC_STAT", | ||
149 | "C_ADDR", "C_SYNDROME_1", "C_SYNDROME_0", | ||
150 | "C_STAT", "C_STS", "MM_STAT", | ||
151 | "EXC_ADDR", "IER_CM", "ISUM", | ||
152 | "PAL_BASE", "I_CTL", "PROCESS_CONTEXT", | ||
153 | "CBOX_CTL", "CBOX_STP_CTL", "CBOX_ACC_CTL", | ||
154 | "CBOX_LCL_SET", "CBOX_GLB_SET", "BBOX_CTL", | ||
155 | "BBOX_ERR_STS", "BBOX_ERR_IDX", "CBOX_DDP_ERR_STS", | ||
156 | "BBOX_DAT_RMP", NULL | ||
157 | }; | ||
158 | |||
159 | static char *el_ev7_zbox_subpacket_annotation[] = { | ||
160 | "Subpacket Header", | ||
161 | "ZBOX(0): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1", | ||
162 | "ZBOX(0): DRAM_ERROR_CTL / DRAM_ERR_STATUS_3", | ||
163 | "ZBOX(0): DIFT_TIMEOUT / DRAM_ERR_ADR", | ||
164 | "ZBOX(0): FRC_ERR_ADR / DRAM_MAPPER_CTL", | ||
165 | "ZBOX(0): reserved / DIFT_ERR_STATUS", | ||
166 | "ZBOX(1): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1", | ||
167 | "ZBOX(1): DRAM_ERROR_CTL / DRAM_ERR_STATUS_3", | ||
168 | "ZBOX(1): DIFT_TIMEOUT / DRAM_ERR_ADR", | ||
169 | "ZBOX(1): FRC_ERR_ADR / DRAM_MAPPER_CTL", | ||
170 | "ZBOX(1): reserved / DIFT_ERR_STATUS", | ||
171 | "CBOX_CTL", "CBOX_STP_CTL", | ||
172 | "ZBOX(0)_ERROR_PA", "ZBOX(1)_ERROR_PA", | ||
173 | "ZBOX(0)_ORED_SYNDROME","ZBOX(1)_ORED_SYNDROME", | ||
174 | NULL | ||
175 | }; | ||
176 | |||
177 | static char *el_ev7_rbox_subpacket_annotation[] = { | ||
178 | "Subpacket Header", "RBOX_CFG", "RBOX_N_CFG", | ||
179 | "RBOX_S_CFG", "RBOX_E_CFG", "RBOX_W_CFG", | ||
180 | "RBOX_N_ERR", "RBOX_S_ERR", "RBOX_E_ERR", | ||
181 | "RBOX_W_ERR", "RBOX_IO_CFG", "RBOX_IO_ERR", | ||
182 | "RBOX_L_ERR", "RBOX_WHOAMI", "RBOX_IMASL", | ||
183 | "RBOX_INTQ", "RBOX_INT", NULL | ||
184 | }; | ||
185 | |||
186 | static char *el_ev7_io_subpacket_annotation[] = { | ||
187 | "Subpacket Header", "IO_ASIC_REV", "IO_SYS_REV", | ||
188 | "IO7_UPH", "HPI_CTL", "CRD_CTL", | ||
189 | "HEI_CTL", "PO7_ERROR_SUM","PO7_UNCRR_SYM", | ||
190 | "PO7_CRRCT_SYM", "PO7_UGBGE_SYM","PO7_ERR_PKT0", | ||
191 | "PO7_ERR_PKT1", "reserved", "reserved", | ||
192 | "PO0_ERR_SUM", "PO0_TLB_ERR", "PO0_SPL_COMPLT", | ||
193 | "PO0_TRANS_SUM", "PO0_FIRST_ERR","PO0_MULT_ERR", | ||
194 | "DM CSR PH", "DM CSR PH", "DM CSR PH", | ||
195 | "DM CSR PH", "reserved", | ||
196 | "PO1_ERR_SUM", "PO1_TLB_ERR", "PO1_SPL_COMPLT", | ||
197 | "PO1_TRANS_SUM", "PO1_FIRST_ERR","PO1_MULT_ERR", | ||
198 | "DM CSR PH", "DM CSR PH", "DM CSR PH", | ||
199 | "DM CSR PH", "reserved", | ||
200 | "PO2_ERR_SUM", "PO2_TLB_ERR", "PO2_SPL_COMPLT", | ||
201 | "PO2_TRANS_SUM", "PO2_FIRST_ERR","PO2_MULT_ERR", | ||
202 | "DM CSR PH", "DM CSR PH", "DM CSR PH", | ||
203 | "DM CSR PH", "reserved", | ||
204 | "PO3_ERR_SUM", "PO3_TLB_ERR", "PO3_SPL_COMPLT", | ||
205 | "PO3_TRANS_SUM", "PO3_FIRST_ERR","PO3_MULT_ERR", | ||
206 | "DM CSR PH", "DM CSR PH", "DM CSR PH", | ||
207 | "DM CSR PH", "reserved", | ||
208 | NULL | ||
209 | }; | ||
210 | |||
211 | static struct el_subpacket_annotation el_ev7_pal_annotations[] = { | ||
212 | SUBPACKET_ANNOTATION(EL_CLASS__PAL, | ||
213 | EL_TYPE__PAL__EV7_PROCESSOR, | ||
214 | 1, | ||
215 | "EV7 Processor Subpacket", | ||
216 | el_ev7_processor_subpacket_annotation), | ||
217 | SUBPACKET_ANNOTATION(EL_CLASS__PAL, | ||
218 | EL_TYPE__PAL__EV7_ZBOX, | ||
219 | 1, | ||
220 | "EV7 ZBOX Subpacket", | ||
221 | el_ev7_zbox_subpacket_annotation), | ||
222 | SUBPACKET_ANNOTATION(EL_CLASS__PAL, | ||
223 | EL_TYPE__PAL__EV7_RBOX, | ||
224 | 1, | ||
225 | "EV7 RBOX Subpacket", | ||
226 | el_ev7_rbox_subpacket_annotation), | ||
227 | SUBPACKET_ANNOTATION(EL_CLASS__PAL, | ||
228 | EL_TYPE__PAL__EV7_IO, | ||
229 | 1, | ||
230 | "EV7 IO Subpacket", | ||
231 | el_ev7_io_subpacket_annotation) | ||
232 | }; | ||
233 | |||
234 | static struct el_subpacket * | ||
235 | ev7_process_pal_subpacket(struct el_subpacket *header) | ||
236 | { | ||
237 | struct ev7_pal_subpacket *packet; | ||
238 | |||
239 | if (header->class != EL_CLASS__PAL) { | ||
240 | printk("%s ** Unexpected header CLASS %d TYPE %d, aborting\n", | ||
241 | err_print_prefix, | ||
242 | header->class, header->type); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | packet = (struct ev7_pal_subpacket *)header->by_type.raw.data_start; | ||
247 | |||
248 | switch(header->type) { | ||
249 | case EL_TYPE__PAL__LOGOUT_FRAME: | ||
250 | printk("%s*** MCHK occurred on LPID %ld (RBOX %lx)\n", | ||
251 | err_print_prefix, | ||
252 | packet->by_type.logout.whami, | ||
253 | packet->by_type.logout.rbox_whami); | ||
254 | el_print_timestamp(&packet->by_type.logout.timestamp); | ||
255 | printk("%s EXC_ADDR: %016lx\n" | ||
256 | " HALT_CODE: %lx\n", | ||
257 | err_print_prefix, | ||
258 | packet->by_type.logout.exc_addr, | ||
259 | packet->by_type.logout.halt_code); | ||
260 | el_process_subpackets(header, | ||
261 | packet->by_type.logout.subpacket_count); | ||
262 | break; | ||
263 | default: | ||
264 | printk("%s ** PAL TYPE %d SUBPACKET\n", | ||
265 | err_print_prefix, | ||
266 | header->type); | ||
267 | el_annotate_subpacket(header); | ||
268 | break; | ||
269 | } | ||
270 | |||
271 | return (struct el_subpacket *)((unsigned long)header + header->length); | ||
272 | } | ||
273 | |||
274 | struct el_subpacket_handler ev7_pal_subpacket_handler = | ||
275 | SUBPACKET_HANDLER_INIT(EL_CLASS__PAL, ev7_process_pal_subpacket); | ||
276 | |||
277 | void | ||
278 | ev7_register_error_handlers(void) | ||
279 | { | ||
280 | int i; | ||
281 | |||
282 | for(i = 0; | ||
283 | i<sizeof(el_ev7_pal_annotations)/sizeof(el_ev7_pal_annotations[1]); | ||
284 | i++) { | ||
285 | cdl_register_subpacket_annotation(&el_ev7_pal_annotations[i]); | ||
286 | } | ||
287 | cdl_register_subpacket_handler(&ev7_pal_subpacket_handler); | ||
288 | } | ||
289 | |||
diff --git a/arch/alpha/kernel/err_impl.h b/arch/alpha/kernel/err_impl.h new file mode 100644 index 000000000000..64e9b73809fa --- /dev/null +++ b/arch/alpha/kernel/err_impl.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_impl.h | ||
3 | * | ||
4 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | * Contains declarations and macros to support Alpha error handling | ||
7 | * implementations. | ||
8 | */ | ||
9 | |||
10 | union el_timestamp; | ||
11 | struct el_subpacket; | ||
12 | struct ev7_lf_subpackets; | ||
13 | |||
14 | struct el_subpacket_annotation { | ||
15 | struct el_subpacket_annotation *next; | ||
16 | u16 class; | ||
17 | u16 type; | ||
18 | u16 revision; | ||
19 | char *description; | ||
20 | char **annotation; | ||
21 | }; | ||
22 | #define SUBPACKET_ANNOTATION(c, t, r, d, a) {NULL, (c), (t), (r), (d), (a)} | ||
23 | |||
24 | struct el_subpacket_handler { | ||
25 | struct el_subpacket_handler *next; | ||
26 | u16 class; | ||
27 | struct el_subpacket *(*handler)(struct el_subpacket *); | ||
28 | }; | ||
29 | #define SUBPACKET_HANDLER_INIT(c, h) {NULL, (c), (h)} | ||
30 | |||
31 | /* | ||
32 | * Manipulate a field from a register given it's name. defines | ||
33 | * for the LSB (__S - shift count) and bitmask (__M) are required | ||
34 | * | ||
35 | * EXTRACT(u, f) - extracts the field and places it at bit position 0 | ||
36 | * GEN_MASK(f) - creates an in-position mask for the field | ||
37 | */ | ||
38 | #define EXTRACT(u, f) (((u) >> f##__S) & f##__M) | ||
39 | #define GEN_MASK(f) ((u64)f##__M << f##__S) | ||
40 | |||
41 | /* | ||
42 | * err_common.c | ||
43 | */ | ||
44 | extern char *err_print_prefix; | ||
45 | |||
46 | extern void mchk_dump_mem(void *, size_t, char **); | ||
47 | extern void mchk_dump_logout_frame(struct el_common *); | ||
48 | extern void el_print_timestamp(union el_timestamp *); | ||
49 | extern void el_process_subpackets(struct el_subpacket *, int); | ||
50 | extern struct el_subpacket *el_process_subpacket(struct el_subpacket *); | ||
51 | extern void el_annotate_subpacket(struct el_subpacket *); | ||
52 | extern void cdl_check_console_data_log(void); | ||
53 | extern int cdl_register_subpacket_annotation(struct el_subpacket_annotation *); | ||
54 | extern int cdl_register_subpacket_handler(struct el_subpacket_handler *); | ||
55 | |||
56 | /* | ||
57 | * err_ev7.c | ||
58 | */ | ||
59 | extern struct ev7_lf_subpackets * | ||
60 | ev7_collect_logout_frame_subpackets(struct el_subpacket *, | ||
61 | struct ev7_lf_subpackets *); | ||
62 | extern void ev7_register_error_handlers(void); | ||
63 | extern void ev7_machine_check(u64, u64, struct pt_regs *); | ||
64 | |||
65 | /* | ||
66 | * err_ev6.c | ||
67 | */ | ||
68 | extern void ev6_register_error_handlers(void); | ||
69 | extern int ev6_process_logout_frame(struct el_common *, int); | ||
70 | extern void ev6_machine_check(u64, u64, struct pt_regs *); | ||
71 | |||
72 | /* | ||
73 | * err_marvel.c | ||
74 | */ | ||
75 | extern void marvel_machine_check(u64, u64, struct pt_regs *); | ||
76 | extern void marvel_register_error_handlers(void); | ||
77 | |||
78 | /* | ||
79 | * err_titan.c | ||
80 | */ | ||
81 | extern int titan_process_logout_frame(struct el_common *, int); | ||
82 | extern void titan_machine_check(u64, u64, struct pt_regs *); | ||
83 | extern void titan_register_error_handlers(void); | ||
84 | extern int privateer_process_logout_frame(struct el_common *, int); | ||
85 | extern void privateer_machine_check(u64, u64, struct pt_regs *); | ||
diff --git a/arch/alpha/kernel/err_marvel.c b/arch/alpha/kernel/err_marvel.c new file mode 100644 index 000000000000..70b38b1d2af3 --- /dev/null +++ b/arch/alpha/kernel/err_marvel.c | |||
@@ -0,0 +1,1159 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_marvel.c | ||
3 | * | ||
4 | * Copyright (C) 2001 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/pci.h> | ||
10 | #include <linux/sched.h> | ||
11 | |||
12 | #include <asm/io.h> | ||
13 | #include <asm/console.h> | ||
14 | #include <asm/core_marvel.h> | ||
15 | #include <asm/hwrpb.h> | ||
16 | #include <asm/smp.h> | ||
17 | #include <asm/err_common.h> | ||
18 | #include <asm/err_ev7.h> | ||
19 | |||
20 | #include "err_impl.h" | ||
21 | #include "proto.h" | ||
22 | |||
23 | static void | ||
24 | marvel_print_680_frame(struct ev7_lf_subpackets *lf_subpackets) | ||
25 | { | ||
26 | #ifdef CONFIG_VERBOSE_MCHECK | ||
27 | struct ev7_pal_environmental_subpacket *env; | ||
28 | struct { int type; char *name; } ev_packets[] = { | ||
29 | { EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE, | ||
30 | "Ambient Temperature" }, | ||
31 | { EL_TYPE__PAL__ENV__AIRMOVER_FAN, | ||
32 | "AirMover / Fan" }, | ||
33 | { EL_TYPE__PAL__ENV__VOLTAGE, | ||
34 | "Voltage" }, | ||
35 | { EL_TYPE__PAL__ENV__INTRUSION, | ||
36 | "Intrusion" }, | ||
37 | { EL_TYPE__PAL__ENV__POWER_SUPPLY, | ||
38 | "Power Supply" }, | ||
39 | { EL_TYPE__PAL__ENV__LAN, | ||
40 | "LAN" }, | ||
41 | { EL_TYPE__PAL__ENV__HOT_PLUG, | ||
42 | "Hot Plug" }, | ||
43 | { 0, NULL } | ||
44 | }; | ||
45 | int i; | ||
46 | |||
47 | for (i = 0; ev_packets[i].type != 0; i++) { | ||
48 | env = lf_subpackets->env[ev7_lf_env_index(ev_packets[i].type)]; | ||
49 | if (!env) | ||
50 | continue; | ||
51 | |||
52 | printk("%s**%s event (cabinet %d, drawer %d)\n", | ||
53 | err_print_prefix, | ||
54 | ev_packets[i].name, | ||
55 | env->cabinet, | ||
56 | env->drawer); | ||
57 | printk("%s Module Type: 0x%x - Unit ID 0x%x - " | ||
58 | "Condition 0x%x\n", | ||
59 | err_print_prefix, | ||
60 | env->module_type, | ||
61 | env->unit_id, | ||
62 | env->condition); | ||
63 | } | ||
64 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
65 | } | ||
66 | |||
67 | static int | ||
68 | marvel_process_680_frame(struct ev7_lf_subpackets *lf_subpackets, int print) | ||
69 | { | ||
70 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
71 | int i; | ||
72 | |||
73 | for (i = ev7_lf_env_index(EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE); | ||
74 | i <= ev7_lf_env_index(EL_TYPE__PAL__ENV__HOT_PLUG); | ||
75 | i++) { | ||
76 | if (lf_subpackets->env[i]) | ||
77 | status = MCHK_DISPOSITION_REPORT; | ||
78 | } | ||
79 | |||
80 | if (print) | ||
81 | marvel_print_680_frame(lf_subpackets); | ||
82 | |||
83 | return status; | ||
84 | } | ||
85 | |||
86 | #ifdef CONFIG_VERBOSE_MCHECK | ||
87 | |||
88 | static void | ||
89 | marvel_print_err_cyc(u64 err_cyc) | ||
90 | { | ||
91 | static char *packet_desc[] = { | ||
92 | "No Error", | ||
93 | "UNKNOWN", | ||
94 | "1 cycle (1 or 2 flit packet)", | ||
95 | "2 cycles (3 flit packet)", | ||
96 | "9 cycles (18 flit packet)", | ||
97 | "10 cycles (19 flit packet)", | ||
98 | "UNKNOWN", | ||
99 | "UNKNOWN", | ||
100 | "UNKNOWN" | ||
101 | }; | ||
102 | |||
103 | #define IO7__ERR_CYC__ODD_FLT (1UL << 0) | ||
104 | #define IO7__ERR_CYC__EVN_FLT (1UL << 1) | ||
105 | #define IO7__ERR_CYC__PACKET__S (6) | ||
106 | #define IO7__ERR_CYC__PACKET__M (0x7) | ||
107 | #define IO7__ERR_CYC__LOC (1UL << 5) | ||
108 | #define IO7__ERR_CYC__CYCLE__S (2) | ||
109 | #define IO7__ERR_CYC__CYCLE__M (0x7) | ||
110 | |||
111 | printk("%s Packet In Error: %s\n" | ||
112 | "%s Error in %s, cycle %ld%s%s\n", | ||
113 | err_print_prefix, | ||
114 | packet_desc[EXTRACT(err_cyc, IO7__ERR_CYC__PACKET)], | ||
115 | err_print_prefix, | ||
116 | (err_cyc & IO7__ERR_CYC__LOC) ? "DATA" : "HEADER", | ||
117 | EXTRACT(err_cyc, IO7__ERR_CYC__CYCLE), | ||
118 | (err_cyc & IO7__ERR_CYC__ODD_FLT) ? " [ODD Flit]": "", | ||
119 | (err_cyc & IO7__ERR_CYC__EVN_FLT) ? " [Even Flit]": ""); | ||
120 | } | ||
121 | |||
122 | static void | ||
123 | marvel_print_po7_crrct_sym(u64 crrct_sym) | ||
124 | { | ||
125 | #define IO7__PO7_CRRCT_SYM__SYN__S (0) | ||
126 | #define IO7__PO7_CRRCT_SYM__SYN__M (0x7f) | ||
127 | #define IO7__PO7_CRRCT_SYM__ERR_CYC__S (7) /* ERR_CYC + ODD_FLT + EVN_FLT */ | ||
128 | #define IO7__PO7_CRRCT_SYM__ERR_CYC__M (0x1ff) | ||
129 | |||
130 | |||
131 | printk("%s Correctable Error Symptoms:\n" | ||
132 | "%s Syndrome: 0x%lx\n", | ||
133 | err_print_prefix, | ||
134 | err_print_prefix, EXTRACT(crrct_sym, IO7__PO7_CRRCT_SYM__SYN)); | ||
135 | marvel_print_err_cyc(EXTRACT(crrct_sym, IO7__PO7_CRRCT_SYM__ERR_CYC)); | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | marvel_print_po7_uncrr_sym(u64 uncrr_sym, u64 valid_mask) | ||
140 | { | ||
141 | static char *clk_names[] = { "_h[0]", "_h[1]", "_n[0]", "_n[1]" }; | ||
142 | static char *clk_decode[] = { | ||
143 | "No Error", | ||
144 | "One extra rising edge", | ||
145 | "Two extra rising edges", | ||
146 | "Lost one clock" | ||
147 | }; | ||
148 | static char *port_names[] = { "Port 0", "Port 1", | ||
149 | "Port 2", "Port 3", | ||
150 | "Unknown Port", "Unknown Port", | ||
151 | "Unknown Port", "Port 7" }; | ||
152 | int scratch, i; | ||
153 | |||
154 | #define IO7__PO7_UNCRR_SYM__SYN__S (0) | ||
155 | #define IO7__PO7_UNCRR_SYM__SYN__M (0x7f) | ||
156 | #define IO7__PO7_UNCRR_SYM__ERR_CYC__S (7) /* ERR_CYC + ODD_FLT... */ | ||
157 | #define IO7__PO7_UNCRR_SYM__ERR_CYC__M (0x1ff) /* ... + EVN_FLT */ | ||
158 | #define IO7__PO7_UNCRR_SYM__CLK__S (16) | ||
159 | #define IO7__PO7_UNCRR_SYM__CLK__M (0xff) | ||
160 | #define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__REQ (1UL << 24) | ||
161 | #define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__RIO (1UL << 25) | ||
162 | #define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__WIO (1UL << 26) | ||
163 | #define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__BLK (1UL << 27) | ||
164 | #define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__NBK (1UL << 28) | ||
165 | #define IO7__PO7_UNCRR_SYM__OVF__READIO (1UL << 29) | ||
166 | #define IO7__PO7_UNCRR_SYM__OVF__WRITEIO (1UL << 30) | ||
167 | #define IO7__PO7_UNCRR_SYM__OVF__FWD (1UL << 31) | ||
168 | #define IO7__PO7_UNCRR_SYM__VICTIM_SP__S (32) | ||
169 | #define IO7__PO7_UNCRR_SYM__VICTIM_SP__M (0xff) | ||
170 | #define IO7__PO7_UNCRR_SYM__DETECT_SP__S (40) | ||
171 | #define IO7__PO7_UNCRR_SYM__DETECT_SP__M (0xff) | ||
172 | #define IO7__PO7_UNCRR_SYM__STRV_VTR__S (48) | ||
173 | #define IO7__PO7_UNCRR_SYM__STRV_VTR__M (0x3ff) | ||
174 | |||
175 | #define IO7__STRV_VTR__LSI__INTX__S (0) | ||
176 | #define IO7__STRV_VTR__LSI__INTX__M (0x3) | ||
177 | #define IO7__STRV_VTR__LSI__SLOT__S (2) | ||
178 | #define IO7__STRV_VTR__LSI__SLOT__M (0x7) | ||
179 | #define IO7__STRV_VTR__LSI__BUS__S (5) | ||
180 | #define IO7__STRV_VTR__LSI__BUS__M (0x3) | ||
181 | #define IO7__STRV_VTR__MSI__INTNUM__S (0) | ||
182 | #define IO7__STRV_VTR__MSI__INTNUM__M (0x1ff) | ||
183 | #define IO7__STRV_VTR__IS_MSI (1UL << 9) | ||
184 | |||
185 | printk("%s Uncorrectable Error Symptoms:\n", err_print_prefix); | ||
186 | uncrr_sym &= valid_mask; | ||
187 | |||
188 | if (EXTRACT(valid_mask, IO7__PO7_UNCRR_SYM__SYN)) | ||
189 | printk("%s Syndrome: 0x%lx\n", | ||
190 | err_print_prefix, | ||
191 | EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__SYN)); | ||
192 | |||
193 | if (EXTRACT(valid_mask, IO7__PO7_UNCRR_SYM__ERR_CYC)) | ||
194 | marvel_print_err_cyc(EXTRACT(uncrr_sym, | ||
195 | IO7__PO7_UNCRR_SYM__ERR_CYC)); | ||
196 | |||
197 | scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__CLK); | ||
198 | for (i = 0; i < 4; i++, scratch >>= 2) { | ||
199 | if (scratch & 0x3) | ||
200 | printk("%s Clock %s: %s\n", | ||
201 | err_print_prefix, | ||
202 | clk_names[i], clk_decode[scratch & 0x3]); | ||
203 | } | ||
204 | |||
205 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__REQ) | ||
206 | printk("%s REQ Credit Timeout or Overflow\n", | ||
207 | err_print_prefix); | ||
208 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__RIO) | ||
209 | printk("%s RIO Credit Timeout or Overflow\n", | ||
210 | err_print_prefix); | ||
211 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__WIO) | ||
212 | printk("%s WIO Credit Timeout or Overflow\n", | ||
213 | err_print_prefix); | ||
214 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__BLK) | ||
215 | printk("%s BLK Credit Timeout or Overflow\n", | ||
216 | err_print_prefix); | ||
217 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__NBK) | ||
218 | printk("%s NBK Credit Timeout or Overflow\n", | ||
219 | err_print_prefix); | ||
220 | |||
221 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__OVF__READIO) | ||
222 | printk("%s Read I/O Buffer Overflow\n", | ||
223 | err_print_prefix); | ||
224 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__OVF__WRITEIO) | ||
225 | printk("%s Write I/O Buffer Overflow\n", | ||
226 | err_print_prefix); | ||
227 | if (uncrr_sym & IO7__PO7_UNCRR_SYM__OVF__FWD) | ||
228 | printk("%s FWD Buffer Overflow\n", | ||
229 | err_print_prefix); | ||
230 | |||
231 | if ((scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__VICTIM_SP))) { | ||
232 | int lost = scratch & (1UL << 4); | ||
233 | scratch &= ~lost; | ||
234 | for (i = 0; i < 8; i++, scratch >>= 1) { | ||
235 | if (!(scratch & 1)) | ||
236 | continue; | ||
237 | printk("%s Error Response sent to %s", | ||
238 | err_print_prefix, port_names[i]); | ||
239 | } | ||
240 | if (lost) | ||
241 | printk("%s Lost Error sent somewhere else\n", | ||
242 | err_print_prefix); | ||
243 | } | ||
244 | |||
245 | if ((scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__DETECT_SP))) { | ||
246 | for (i = 0; i < 8; i++, scratch >>= 1) { | ||
247 | if (!(scratch & 1)) | ||
248 | continue; | ||
249 | printk("%s Error Reported by %s", | ||
250 | err_print_prefix, port_names[i]); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if (EXTRACT(valid_mask, IO7__PO7_UNCRR_SYM__STRV_VTR)) { | ||
255 | char starvation_message[80]; | ||
256 | |||
257 | scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__STRV_VTR); | ||
258 | if (scratch & IO7__STRV_VTR__IS_MSI) | ||
259 | sprintf(starvation_message, | ||
260 | "MSI Interrupt 0x%x", | ||
261 | EXTRACT(scratch, IO7__STRV_VTR__MSI__INTNUM)); | ||
262 | else | ||
263 | sprintf(starvation_message, | ||
264 | "LSI INT%c for Bus:Slot (%d:%d)\n", | ||
265 | 'A' + EXTRACT(scratch, | ||
266 | IO7__STRV_VTR__LSI__INTX), | ||
267 | EXTRACT(scratch, IO7__STRV_VTR__LSI__BUS), | ||
268 | EXTRACT(scratch, IO7__STRV_VTR__LSI__SLOT)); | ||
269 | |||
270 | printk("%s Starvation Int Trigger By: %s\n", | ||
271 | err_print_prefix, starvation_message); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | static void | ||
276 | marvel_print_po7_ugbge_sym(u64 ugbge_sym) | ||
277 | { | ||
278 | char opcode_str[10]; | ||
279 | |||
280 | #define IO7__PO7_UGBGE_SYM__UPH_PKT_OFF__S (6) | ||
281 | #define IO7__PO7_UGBGE_SYM__UPH_PKT_OFF__M (0xfffffffful) | ||
282 | #define IO7__PO7_UGBGE_SYM__UPH_OPCODE__S (40) | ||
283 | #define IO7__PO7_UGBGE_SYM__UPH_OPCODE__M (0xff) | ||
284 | #define IO7__PO7_UGBGE_SYM__UPH_SRC_PORT__S (48) | ||
285 | #define IO7__PO7_UGBGE_SYM__UPH_SRC_PORT__M (0xf) | ||
286 | #define IO7__PO7_UGBGE_SYM__UPH_DEST_PID__S (52) | ||
287 | #define IO7__PO7_UGBGE_SYM__UPH_DEST_PID__M (0x7ff) | ||
288 | #define IO7__PO7_UGBGE_SYM__VALID (1UL << 63) | ||
289 | |||
290 | if (!(ugbge_sym & IO7__PO7_UGBGE_SYM__VALID)) | ||
291 | return; | ||
292 | |||
293 | switch(EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_OPCODE)) { | ||
294 | case 0x51: | ||
295 | sprintf(opcode_str, "Wr32"); | ||
296 | break; | ||
297 | case 0x50: | ||
298 | sprintf(opcode_str, "WrQW"); | ||
299 | break; | ||
300 | case 0x54: | ||
301 | sprintf(opcode_str, "WrIPR"); | ||
302 | break; | ||
303 | case 0xD8: | ||
304 | sprintf(opcode_str, "Victim"); | ||
305 | break; | ||
306 | case 0xC5: | ||
307 | sprintf(opcode_str, "BlkIO"); | ||
308 | break; | ||
309 | default: | ||
310 | sprintf(opcode_str, "0x%lx\n", | ||
311 | EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_OPCODE)); | ||
312 | break; | ||
313 | } | ||
314 | |||
315 | printk("%s Up Hose Garbage Symptom:\n" | ||
316 | "%s Source Port: %ld - Dest PID: %ld - OpCode: %s\n", | ||
317 | err_print_prefix, | ||
318 | err_print_prefix, | ||
319 | EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_SRC_PORT), | ||
320 | EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_DEST_PID), | ||
321 | opcode_str); | ||
322 | |||
323 | if (0xC5 != EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_OPCODE)) | ||
324 | printk("%s Packet Offset 0x%08lx\n", | ||
325 | err_print_prefix, | ||
326 | EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_PKT_OFF)); | ||
327 | } | ||
328 | |||
329 | static void | ||
330 | marvel_print_po7_err_sum(struct ev7_pal_io_subpacket *io) | ||
331 | { | ||
332 | u64 uncrr_sym_valid = 0; | ||
333 | |||
334 | #define IO7__PO7_ERRSUM__CR_SBE (1UL << 32) | ||
335 | #define IO7__PO7_ERRSUM__CR_SBE2 (1UL << 33) | ||
336 | #define IO7__PO7_ERRSUM__CR_PIO_WBYTE (1UL << 34) | ||
337 | #define IO7__PO7_ERRSUM__CR_CSR_NXM (1UL << 35) | ||
338 | #define IO7__PO7_ERRSUM__CR_RPID_ACV (1UL << 36) | ||
339 | #define IO7__PO7_ERRSUM__CR_RSP_NXM (1UL << 37) | ||
340 | #define IO7__PO7_ERRSUM__CR_ERR_RESP (1UL << 38) | ||
341 | #define IO7__PO7_ERRSUM__CR_CLK_DERR (1UL << 39) | ||
342 | #define IO7__PO7_ERRSUM__CR_DAT_DBE (1UL << 40) | ||
343 | #define IO7__PO7_ERRSUM__CR_DAT_GRBG (1UL << 41) | ||
344 | #define IO7__PO7_ERRSUM__MAF_TO (1UL << 42) | ||
345 | #define IO7__PO7_ERRSUM__UGBGE (1UL << 43) | ||
346 | #define IO7__PO7_ERRSUM__UN_MAF_LOST (1UL << 44) | ||
347 | #define IO7__PO7_ERRSUM__UN_PKT_OVF (1UL << 45) | ||
348 | #define IO7__PO7_ERRSUM__UN_CDT_OVF (1UL << 46) | ||
349 | #define IO7__PO7_ERRSUM__UN_DEALLOC (1UL << 47) | ||
350 | #define IO7__PO7_ERRSUM__BH_CDT_TO (1UL << 51) | ||
351 | #define IO7__PO7_ERRSUM__BH_CLK_HDR (1UL << 52) | ||
352 | #define IO7__PO7_ERRSUM__BH_DBE_HDR (1UL << 53) | ||
353 | #define IO7__PO7_ERRSUM__BH_GBG_HDR (1UL << 54) | ||
354 | #define IO7__PO7_ERRSUM__BH_BAD_CMD (1UL << 55) | ||
355 | #define IO7__PO7_ERRSUM__HLT_INT (1UL << 56) | ||
356 | #define IO7__PO7_ERRSUM__HP_INT (1UL << 57) | ||
357 | #define IO7__PO7_ERRSUM__CRD_INT (1UL << 58) | ||
358 | #define IO7__PO7_ERRSUM__STV_INT (1UL << 59) | ||
359 | #define IO7__PO7_ERRSUM__HRD_INT (1UL << 60) | ||
360 | #define IO7__PO7_ERRSUM__BH_SUM (1UL << 61) | ||
361 | #define IO7__PO7_ERRSUM__ERR_LST (1UL << 62) | ||
362 | #define IO7__PO7_ERRSUM__ERR_VALID (1UL << 63) | ||
363 | |||
364 | #define IO7__PO7_ERRSUM__ERR_MASK (IO7__PO7_ERRSUM__ERR_VALID | \ | ||
365 | IO7__PO7_ERRSUM__CR_SBE) | ||
366 | |||
367 | /* | ||
368 | * Single bit errors aren't covered by ERR_VALID. | ||
369 | */ | ||
370 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_SBE) { | ||
371 | printk("%s %sSingle Bit Error(s) detected/corrected\n", | ||
372 | err_print_prefix, | ||
373 | (io->po7_error_sum & IO7__PO7_ERRSUM__CR_SBE2) | ||
374 | ? "Multiple " : ""); | ||
375 | marvel_print_po7_crrct_sym(io->po7_crrct_sym); | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * Neither are the interrupt status bits | ||
380 | */ | ||
381 | if (io->po7_error_sum & IO7__PO7_ERRSUM__HLT_INT) | ||
382 | printk("%s Halt Interrupt posted", err_print_prefix); | ||
383 | if (io->po7_error_sum & IO7__PO7_ERRSUM__HP_INT) { | ||
384 | printk("%s Hot Plug Event Interrupt posted", | ||
385 | err_print_prefix); | ||
386 | uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__DETECT_SP); | ||
387 | } | ||
388 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CRD_INT) | ||
389 | printk("%s Correctable Error Interrupt posted", | ||
390 | err_print_prefix); | ||
391 | if (io->po7_error_sum & IO7__PO7_ERRSUM__STV_INT) { | ||
392 | printk("%s Starvation Interrupt posted", err_print_prefix); | ||
393 | uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__STRV_VTR); | ||
394 | } | ||
395 | if (io->po7_error_sum & IO7__PO7_ERRSUM__HRD_INT) { | ||
396 | printk("%s Hard Error Interrupt posted", err_print_prefix); | ||
397 | uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__DETECT_SP); | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * Everything else is valid only with ERR_VALID, so skip to the end | ||
402 | * (uncrr_sym check) unless ERR_VALID is set. | ||
403 | */ | ||
404 | if (!(io->po7_error_sum & IO7__PO7_ERRSUM__ERR_VALID)) | ||
405 | goto check_uncrr_sym; | ||
406 | |||
407 | /* | ||
408 | * Since ERR_VALID is set, VICTIM_SP in uncrr_sym is valid. | ||
409 | * For bits [29:0] to also be valid, the following bits must | ||
410 | * not be set: | ||
411 | * CR_PIO_WBYTE CR_CSR_NXM CR_RSP_NXM | ||
412 | * CR_ERR_RESP MAF_TO | ||
413 | */ | ||
414 | uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__VICTIM_SP); | ||
415 | if (!(io->po7_error_sum & (IO7__PO7_ERRSUM__CR_PIO_WBYTE | | ||
416 | IO7__PO7_ERRSUM__CR_CSR_NXM | | ||
417 | IO7__PO7_ERRSUM__CR_RSP_NXM | | ||
418 | IO7__PO7_ERRSUM__CR_ERR_RESP | | ||
419 | IO7__PO7_ERRSUM__MAF_TO))) | ||
420 | uncrr_sym_valid |= 0x3ffffffful; | ||
421 | |||
422 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_PIO_WBYTE) | ||
423 | printk("%s Write byte into IO7 CSR\n", err_print_prefix); | ||
424 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_CSR_NXM) | ||
425 | printk("%s PIO to non-existent CSR\n", err_print_prefix); | ||
426 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_RPID_ACV) | ||
427 | printk("%s Bus Requester PID (Access Violation)\n", | ||
428 | err_print_prefix); | ||
429 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_RSP_NXM) | ||
430 | printk("%s Received NXM response from EV7\n", | ||
431 | err_print_prefix); | ||
432 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_ERR_RESP) | ||
433 | printk("%s Received ERROR RESPONSE\n", err_print_prefix); | ||
434 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_CLK_DERR) | ||
435 | printk("%s Clock error on data flit\n", err_print_prefix); | ||
436 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_DAT_DBE) | ||
437 | printk("%s Double Bit Error Data Error Detected\n", | ||
438 | err_print_prefix); | ||
439 | if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_DAT_GRBG) | ||
440 | printk("%s Garbage Encoding Detected on the data\n", | ||
441 | err_print_prefix); | ||
442 | if (io->po7_error_sum & IO7__PO7_ERRSUM__UGBGE) { | ||
443 | printk("%s Garbage Encoding sent up hose\n", | ||
444 | err_print_prefix); | ||
445 | marvel_print_po7_ugbge_sym(io->po7_ugbge_sym); | ||
446 | } | ||
447 | if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_MAF_LOST) | ||
448 | printk("%s Orphan response (unexpected response)\n", | ||
449 | err_print_prefix); | ||
450 | if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_PKT_OVF) | ||
451 | printk("%s Down hose packet overflow\n", err_print_prefix); | ||
452 | if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_CDT_OVF) | ||
453 | printk("%s Down hose credit overflow\n", err_print_prefix); | ||
454 | if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_DEALLOC) | ||
455 | printk("%s Unexpected or bad dealloc field\n", | ||
456 | err_print_prefix); | ||
457 | |||
458 | /* | ||
459 | * The black hole events. | ||
460 | */ | ||
461 | if (io->po7_error_sum & IO7__PO7_ERRSUM__MAF_TO) | ||
462 | printk("%s BLACK HOLE: Timeout for all responses\n", | ||
463 | err_print_prefix); | ||
464 | if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_CDT_TO) | ||
465 | printk("%s BLACK HOLE: Credit Timeout\n", err_print_prefix); | ||
466 | if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_CLK_HDR) | ||
467 | printk("%s BLACK HOLE: Clock check on header\n", | ||
468 | err_print_prefix); | ||
469 | if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_DBE_HDR) | ||
470 | printk("%s BLACK HOLE: Uncorrectable Error on header\n", | ||
471 | err_print_prefix); | ||
472 | if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_GBG_HDR) | ||
473 | printk("%s BLACK HOLE: Garbage on header\n", | ||
474 | err_print_prefix); | ||
475 | if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_BAD_CMD) | ||
476 | printk("%s BLACK HOLE: Bad EV7 command\n", | ||
477 | err_print_prefix); | ||
478 | |||
479 | if (io->po7_error_sum & IO7__PO7_ERRSUM__ERR_LST) | ||
480 | printk("%s Lost Error\n", err_print_prefix); | ||
481 | |||
482 | printk("%s Failing Packet:\n" | ||
483 | "%s Cycle 1: %016lx\n" | ||
484 | "%s Cycle 2: %016lx\n", | ||
485 | err_print_prefix, | ||
486 | err_print_prefix, io->po7_err_pkt0, | ||
487 | err_print_prefix, io->po7_err_pkt1); | ||
488 | /* | ||
489 | * If there are any valid bits in UNCRR sym for this err, | ||
490 | * print UNCRR_SYM as well. | ||
491 | */ | ||
492 | check_uncrr_sym: | ||
493 | if (uncrr_sym_valid) | ||
494 | marvel_print_po7_uncrr_sym(io->po7_uncrr_sym, uncrr_sym_valid); | ||
495 | } | ||
496 | |||
497 | static void | ||
498 | marvel_print_pox_tlb_err(u64 tlb_err) | ||
499 | { | ||
500 | static char *tlb_errors[] = { | ||
501 | "No Error", | ||
502 | "North Port Signaled Error fetching TLB entry", | ||
503 | "PTE invalid or UCC or GBG error on this entry", | ||
504 | "Address did not hit any DMA window" | ||
505 | }; | ||
506 | |||
507 | #define IO7__POX_TLBERR__ERR_VALID (1UL << 63) | ||
508 | #define IO7__POX_TLBERR__ERRCODE__S (0) | ||
509 | #define IO7__POX_TLBERR__ERRCODE__M (0x3) | ||
510 | #define IO7__POX_TLBERR__ERR_TLB_PTR__S (3) | ||
511 | #define IO7__POX_TLBERR__ERR_TLB_PTR__M (0x7) | ||
512 | #define IO7__POX_TLBERR__FADDR__S (6) | ||
513 | #define IO7__POX_TLBERR__FADDR__M (0x3fffffffffful) | ||
514 | |||
515 | if (!(tlb_err & IO7__POX_TLBERR__ERR_VALID)) | ||
516 | return; | ||
517 | |||
518 | printk("%s TLB Error on index 0x%lx:\n" | ||
519 | "%s - %s\n" | ||
520 | "%s - Addr: 0x%016lx\n", | ||
521 | err_print_prefix, | ||
522 | EXTRACT(tlb_err, IO7__POX_TLBERR__ERR_TLB_PTR), | ||
523 | err_print_prefix, | ||
524 | tlb_errors[EXTRACT(tlb_err, IO7__POX_TLBERR__ERRCODE)], | ||
525 | err_print_prefix, | ||
526 | EXTRACT(tlb_err, IO7__POX_TLBERR__FADDR) << 6); | ||
527 | } | ||
528 | |||
529 | static void | ||
530 | marvel_print_pox_spl_cmplt(u64 spl_cmplt) | ||
531 | { | ||
532 | char message[80]; | ||
533 | |||
534 | #define IO7__POX_SPLCMPLT__MESSAGE__S (0) | ||
535 | #define IO7__POX_SPLCMPLT__MESSAGE__M (0x0fffffffful) | ||
536 | #define IO7__POX_SPLCMPLT__SOURCE_BUS__S (40) | ||
537 | #define IO7__POX_SPLCMPLT__SOURCE_BUS__M (0xfful) | ||
538 | #define IO7__POX_SPLCMPLT__SOURCE_DEV__S (35) | ||
539 | #define IO7__POX_SPLCMPLT__SOURCE_DEV__M (0x1ful) | ||
540 | #define IO7__POX_SPLCMPLT__SOURCE_FUNC__S (32) | ||
541 | #define IO7__POX_SPLCMPLT__SOURCE_FUNC__M (0x07ul) | ||
542 | |||
543 | #define IO7__POX_SPLCMPLT__MSG_CLASS__S (28) | ||
544 | #define IO7__POX_SPLCMPLT__MSG_CLASS__M (0xf) | ||
545 | #define IO7__POX_SPLCMPLT__MSG_INDEX__S (20) | ||
546 | #define IO7__POX_SPLCMPLT__MSG_INDEX__M (0xff) | ||
547 | #define IO7__POX_SPLCMPLT__MSG_CLASSINDEX__S (20) | ||
548 | #define IO7__POX_SPLCMPLT__MSG_CLASSINDEX__M (0xfff) | ||
549 | #define IO7__POX_SPLCMPLT__REM_LOWER_ADDR__S (12) | ||
550 | #define IO7__POX_SPLCMPLT__REM_LOWER_ADDR__M (0x7f) | ||
551 | #define IO7__POX_SPLCMPLT__REM_BYTE_COUNT__S (0) | ||
552 | #define IO7__POX_SPLCMPLT__REM_BYTE_COUNT__M (0xfff) | ||
553 | |||
554 | printk("%s Split Completion Error:\n" | ||
555 | "%s Source (Bus:Dev:Func): %ld:%ld:%ld\n", | ||
556 | err_print_prefix, | ||
557 | err_print_prefix, | ||
558 | EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_BUS), | ||
559 | EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_DEV), | ||
560 | EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_FUNC)); | ||
561 | |||
562 | switch(EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__MSG_CLASSINDEX)) { | ||
563 | case 0x000: | ||
564 | sprintf(message, "Normal completion"); | ||
565 | break; | ||
566 | case 0x100: | ||
567 | sprintf(message, "Bridge - Master Abort"); | ||
568 | break; | ||
569 | case 0x101: | ||
570 | sprintf(message, "Bridge - Target Abort"); | ||
571 | break; | ||
572 | case 0x102: | ||
573 | sprintf(message, "Bridge - Uncorrectable Write Data Error"); | ||
574 | break; | ||
575 | case 0x200: | ||
576 | sprintf(message, "Byte Count Out of Range"); | ||
577 | break; | ||
578 | case 0x201: | ||
579 | sprintf(message, "Uncorrectable Split Write Data Error"); | ||
580 | break; | ||
581 | default: | ||
582 | sprintf(message, "%08lx\n", | ||
583 | EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__MESSAGE)); | ||
584 | break; | ||
585 | } | ||
586 | printk("%s Message: %s\n", err_print_prefix, message); | ||
587 | } | ||
588 | |||
589 | static void | ||
590 | marvel_print_pox_trans_sum(u64 trans_sum) | ||
591 | { | ||
592 | char *pcix_cmd[] = { "Interrupt Acknowledge", | ||
593 | "Special Cycle", | ||
594 | "I/O Read", | ||
595 | "I/O Write", | ||
596 | "Reserved", | ||
597 | "Reserved / Device ID Message", | ||
598 | "Memory Read", | ||
599 | "Memory Write", | ||
600 | "Reserved / Alias to Memory Read Block", | ||
601 | "Reserved / Alias to Memory Write Block", | ||
602 | "Configuration Read", | ||
603 | "Configuration Write", | ||
604 | "Memory Read Multiple / Split Completion", | ||
605 | "Dual Address Cycle", | ||
606 | "Memory Read Line / Memory Read Block", | ||
607 | "Memory Write and Invalidate / Memory Write Block" | ||
608 | }; | ||
609 | |||
610 | #define IO7__POX_TRANSUM__PCI_ADDR__S (0) | ||
611 | #define IO7__POX_TRANSUM__PCI_ADDR__M (0x3fffffffffffful) | ||
612 | #define IO7__POX_TRANSUM__DAC (1UL << 50) | ||
613 | #define IO7__POX_TRANSUM__PCIX_MASTER_SLOT__S (52) | ||
614 | #define IO7__POX_TRANSUM__PCIX_MASTER_SLOT__M (0xf) | ||
615 | #define IO7__POX_TRANSUM__PCIX_CMD__S (56) | ||
616 | #define IO7__POX_TRANSUM__PCIX_CMD__M (0xf) | ||
617 | #define IO7__POX_TRANSUM__ERR_VALID (1UL << 63) | ||
618 | |||
619 | if (!(trans_sum & IO7__POX_TRANSUM__ERR_VALID)) | ||
620 | return; | ||
621 | |||
622 | printk("%s Transaction Summary:\n" | ||
623 | "%s Command: 0x%lx - %s\n" | ||
624 | "%s Address: 0x%016lx%s\n" | ||
625 | "%s PCI-X Master Slot: 0x%lx\n", | ||
626 | err_print_prefix, | ||
627 | err_print_prefix, | ||
628 | EXTRACT(trans_sum, IO7__POX_TRANSUM__PCIX_CMD), | ||
629 | pcix_cmd[EXTRACT(trans_sum, IO7__POX_TRANSUM__PCIX_CMD)], | ||
630 | err_print_prefix, | ||
631 | EXTRACT(trans_sum, IO7__POX_TRANSUM__PCI_ADDR), | ||
632 | (trans_sum & IO7__POX_TRANSUM__DAC) ? " (DAC)" : "", | ||
633 | err_print_prefix, | ||
634 | EXTRACT(trans_sum, IO7__POX_TRANSUM__PCIX_MASTER_SLOT)); | ||
635 | } | ||
636 | |||
637 | static void | ||
638 | marvel_print_pox_err(u64 err_sum, struct ev7_pal_io_one_port *port) | ||
639 | { | ||
640 | #define IO7__POX_ERRSUM__AGP_REQQ_OVFL (1UL << 4) | ||
641 | #define IO7__POX_ERRSUM__AGP_SYNC_ERR (1UL << 5) | ||
642 | #define IO7__POX_ERRSUM__MRETRY_TO (1UL << 6) | ||
643 | #define IO7__POX_ERRSUM__PCIX_UX_SPL (1UL << 7) | ||
644 | #define IO7__POX_ERRSUM__PCIX_SPLIT_TO (1UL << 8) | ||
645 | #define IO7__POX_ERRSUM__PCIX_DISCARD_SPL (1UL << 9) | ||
646 | #define IO7__POX_ERRSUM__DMA_RD_TO (1UL << 10) | ||
647 | #define IO7__POX_ERRSUM__CSR_NXM_RD (1UL << 11) | ||
648 | #define IO7__POX_ERRSUM__CSR_NXM_WR (1UL << 12) | ||
649 | #define IO7__POX_ERRSUM__DMA_TO (1UL << 13) | ||
650 | #define IO7__POX_ERRSUM__ALL_MABORTS (1UL << 14) | ||
651 | #define IO7__POX_ERRSUM__MABORT (1UL << 15) | ||
652 | #define IO7__POX_ERRSUM__MABORT_MASK (IO7__POX_ERRSUM__ALL_MABORTS|\ | ||
653 | IO7__POX_ERRSUM__MABORT) | ||
654 | #define IO7__POX_ERRSUM__PT_TABORT (1UL << 16) | ||
655 | #define IO7__POX_ERRSUM__PM_TABORT (1UL << 17) | ||
656 | #define IO7__POX_ERRSUM__TABORT_MASK (IO7__POX_ERRSUM__PT_TABORT | \ | ||
657 | IO7__POX_ERRSUM__PM_TABORT) | ||
658 | #define IO7__POX_ERRSUM__SERR (1UL << 18) | ||
659 | #define IO7__POX_ERRSUM__ADDRERR_STB (1UL << 19) | ||
660 | #define IO7__POX_ERRSUM__DETECTED_SERR (1UL << 20) | ||
661 | #define IO7__POX_ERRSUM__PERR (1UL << 21) | ||
662 | #define IO7__POX_ERRSUM__DATAERR_STB_NIOW (1UL << 22) | ||
663 | #define IO7__POX_ERRSUM__DETECTED_PERR (1UL << 23) | ||
664 | #define IO7__POX_ERRSUM__PM_PERR (1UL << 24) | ||
665 | #define IO7__POX_ERRSUM__PT_SCERROR (1UL << 26) | ||
666 | #define IO7__POX_ERRSUM__HUNG_BUS (1UL << 28) | ||
667 | #define IO7__POX_ERRSUM__UPE_ERROR__S (51) | ||
668 | #define IO7__POX_ERRSUM__UPE_ERROR__M (0xffUL) | ||
669 | #define IO7__POX_ERRSUM__UPE_ERROR GEN_MASK(IO7__POX_ERRSUM__UPE_ERROR) | ||
670 | #define IO7__POX_ERRSUM__TLB_ERR (1UL << 59) | ||
671 | #define IO7__POX_ERRSUM__ERR_VALID (1UL << 63) | ||
672 | |||
673 | #define IO7__POX_ERRSUM__TRANS_SUM__MASK (IO7__POX_ERRSUM__MRETRY_TO | \ | ||
674 | IO7__POX_ERRSUM__PCIX_UX_SPL | \ | ||
675 | IO7__POX_ERRSUM__PCIX_SPLIT_TO | \ | ||
676 | IO7__POX_ERRSUM__DMA_TO | \ | ||
677 | IO7__POX_ERRSUM__MABORT_MASK | \ | ||
678 | IO7__POX_ERRSUM__TABORT_MASK | \ | ||
679 | IO7__POX_ERRSUM__SERR | \ | ||
680 | IO7__POX_ERRSUM__ADDRERR_STB | \ | ||
681 | IO7__POX_ERRSUM__PERR | \ | ||
682 | IO7__POX_ERRSUM__DATAERR_STB_NIOW |\ | ||
683 | IO7__POX_ERRSUM__DETECTED_PERR | \ | ||
684 | IO7__POX_ERRSUM__PM_PERR | \ | ||
685 | IO7__POX_ERRSUM__PT_SCERROR | \ | ||
686 | IO7__POX_ERRSUM__UPE_ERROR) | ||
687 | |||
688 | if (!(err_sum & IO7__POX_ERRSUM__ERR_VALID)) | ||
689 | return; | ||
690 | |||
691 | /* | ||
692 | * First the transaction summary errors | ||
693 | */ | ||
694 | if (err_sum & IO7__POX_ERRSUM__MRETRY_TO) | ||
695 | printk("%s IO7 Master Retry Timeout expired\n", | ||
696 | err_print_prefix); | ||
697 | if (err_sum & IO7__POX_ERRSUM__PCIX_UX_SPL) | ||
698 | printk("%s Unexpected Split Completion\n", | ||
699 | err_print_prefix); | ||
700 | if (err_sum & IO7__POX_ERRSUM__PCIX_SPLIT_TO) | ||
701 | printk("%s IO7 Split Completion Timeout expired\n", | ||
702 | err_print_prefix); | ||
703 | if (err_sum & IO7__POX_ERRSUM__DMA_TO) | ||
704 | printk("%s Hung bus during DMA transaction\n", | ||
705 | err_print_prefix); | ||
706 | if (err_sum & IO7__POX_ERRSUM__MABORT_MASK) | ||
707 | printk("%s Master Abort\n", err_print_prefix); | ||
708 | if (err_sum & IO7__POX_ERRSUM__PT_TABORT) | ||
709 | printk("%s IO7 Asserted Target Abort\n", err_print_prefix); | ||
710 | if (err_sum & IO7__POX_ERRSUM__PM_TABORT) | ||
711 | printk("%s IO7 Received Target Abort\n", err_print_prefix); | ||
712 | if (err_sum & IO7__POX_ERRSUM__ADDRERR_STB) { | ||
713 | printk("%s Address or PCI-X Attribute Parity Error\n", | ||
714 | err_print_prefix); | ||
715 | if (err_sum & IO7__POX_ERRSUM__SERR) | ||
716 | printk("%s IO7 Asserted SERR\n", err_print_prefix); | ||
717 | } | ||
718 | if (err_sum & IO7__POX_ERRSUM__PERR) { | ||
719 | if (err_sum & IO7__POX_ERRSUM__DATAERR_STB_NIOW) | ||
720 | printk("%s IO7 Detected Data Parity Error\n", | ||
721 | err_print_prefix); | ||
722 | else | ||
723 | printk("%s Split Completion Response with " | ||
724 | "Parity Error\n", err_print_prefix); | ||
725 | } | ||
726 | if (err_sum & IO7__POX_ERRSUM__DETECTED_PERR) | ||
727 | printk("%s PERR detected\n", err_print_prefix); | ||
728 | if (err_sum & IO7__POX_ERRSUM__PM_PERR) | ||
729 | printk("%s PERR while IO7 is master\n", err_print_prefix); | ||
730 | if (err_sum & IO7__POX_ERRSUM__PT_SCERROR) { | ||
731 | printk("%s IO7 Received Split Completion Error message\n", | ||
732 | err_print_prefix); | ||
733 | marvel_print_pox_spl_cmplt(port->pox_spl_cmplt); | ||
734 | } | ||
735 | if (err_sum & IO7__POX_ERRSUM__UPE_ERROR) { | ||
736 | unsigned int upe_error = EXTRACT(err_sum, | ||
737 | IO7__POX_ERRSUM__UPE_ERROR); | ||
738 | int i; | ||
739 | static char *upe_errors[] = { | ||
740 | "Parity Error on MSI write data", | ||
741 | "MSI read (MSI window is write only", | ||
742 | "TLB - Invalid WR transaction", | ||
743 | "TLB - Invalid RD transaction", | ||
744 | "DMA - WR error (see north port)", | ||
745 | "DMA - RD error (see north port)", | ||
746 | "PPR - WR error (see north port)", | ||
747 | "PPR - RD error (see north port)" | ||
748 | }; | ||
749 | |||
750 | printk("%s UPE Error:\n", err_print_prefix); | ||
751 | for (i = 0; i < 8; i++) { | ||
752 | if (upe_error & (1 << i)) | ||
753 | printk("%s %s\n", err_print_prefix, | ||
754 | upe_errors[i]); | ||
755 | } | ||
756 | } | ||
757 | |||
758 | /* | ||
759 | * POx_TRANS_SUM, if appropriate. | ||
760 | */ | ||
761 | if (err_sum & IO7__POX_ERRSUM__TRANS_SUM__MASK) | ||
762 | marvel_print_pox_trans_sum(port->pox_trans_sum); | ||
763 | |||
764 | /* | ||
765 | * Then TLB_ERR. | ||
766 | */ | ||
767 | if (err_sum & IO7__POX_ERRSUM__TLB_ERR) { | ||
768 | printk("%s TLB ERROR\n", err_print_prefix); | ||
769 | marvel_print_pox_tlb_err(port->pox_tlb_err); | ||
770 | } | ||
771 | |||
772 | /* | ||
773 | * And the single bit status errors. | ||
774 | */ | ||
775 | if (err_sum & IO7__POX_ERRSUM__AGP_REQQ_OVFL) | ||
776 | printk("%s AGP Request Queue Overflow\n", err_print_prefix); | ||
777 | if (err_sum & IO7__POX_ERRSUM__AGP_SYNC_ERR) | ||
778 | printk("%s AGP Sync Error\n", err_print_prefix); | ||
779 | if (err_sum & IO7__POX_ERRSUM__PCIX_DISCARD_SPL) | ||
780 | printk("%s Discarded split completion\n", err_print_prefix); | ||
781 | if (err_sum & IO7__POX_ERRSUM__DMA_RD_TO) | ||
782 | printk("%s DMA Read Timeout\n", err_print_prefix); | ||
783 | if (err_sum & IO7__POX_ERRSUM__CSR_NXM_RD) | ||
784 | printk("%s CSR NXM READ\n", err_print_prefix); | ||
785 | if (err_sum & IO7__POX_ERRSUM__CSR_NXM_WR) | ||
786 | printk("%s CSR NXM WRITE\n", err_print_prefix); | ||
787 | if (err_sum & IO7__POX_ERRSUM__DETECTED_SERR) | ||
788 | printk("%s SERR detected\n", err_print_prefix); | ||
789 | if (err_sum & IO7__POX_ERRSUM__HUNG_BUS) | ||
790 | printk("%s HUNG BUS detected\n", err_print_prefix); | ||
791 | } | ||
792 | |||
793 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
794 | |||
795 | static struct ev7_pal_io_subpacket * | ||
796 | marvel_find_io7_with_error(struct ev7_lf_subpackets *lf_subpackets) | ||
797 | { | ||
798 | struct ev7_pal_io_subpacket *io = lf_subpackets->io; | ||
799 | struct io7 *io7; | ||
800 | int i; | ||
801 | |||
802 | /* | ||
803 | * Caller must provide the packet to fill | ||
804 | */ | ||
805 | if (!io) | ||
806 | return NULL; | ||
807 | |||
808 | /* | ||
809 | * Fill the subpacket with the console's standard fill pattern | ||
810 | */ | ||
811 | memset(io, 0x55, sizeof(*io)); | ||
812 | |||
813 | for (io7 = NULL; NULL != (io7 = marvel_next_io7(io7)); ) { | ||
814 | unsigned long err_sum = 0; | ||
815 | |||
816 | err_sum |= io7->csrs->PO7_ERROR_SUM.csr; | ||
817 | for (i = 0; i < IO7_NUM_PORTS; i++) { | ||
818 | if (!io7->ports[i].enabled) | ||
819 | continue; | ||
820 | err_sum |= io7->ports[i].csrs->POx_ERR_SUM.csr; | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * Is there at least one error? | ||
825 | */ | ||
826 | if (err_sum & (1UL << 63)) | ||
827 | break; | ||
828 | } | ||
829 | |||
830 | /* | ||
831 | * Did we find an IO7 with an error? | ||
832 | */ | ||
833 | if (!io7) | ||
834 | return NULL; | ||
835 | |||
836 | /* | ||
837 | * We have an IO7 with an error. | ||
838 | * | ||
839 | * Fill in the IO subpacket. | ||
840 | */ | ||
841 | io->io_asic_rev = io7->csrs->IO_ASIC_REV.csr; | ||
842 | io->io_sys_rev = io7->csrs->IO_SYS_REV.csr; | ||
843 | io->io7_uph = io7->csrs->IO7_UPH.csr; | ||
844 | io->hpi_ctl = io7->csrs->HPI_CTL.csr; | ||
845 | io->crd_ctl = io7->csrs->CRD_CTL.csr; | ||
846 | io->hei_ctl = io7->csrs->HEI_CTL.csr; | ||
847 | io->po7_error_sum = io7->csrs->PO7_ERROR_SUM.csr; | ||
848 | io->po7_uncrr_sym = io7->csrs->PO7_UNCRR_SYM.csr; | ||
849 | io->po7_crrct_sym = io7->csrs->PO7_CRRCT_SYM.csr; | ||
850 | io->po7_ugbge_sym = io7->csrs->PO7_UGBGE_SYM.csr; | ||
851 | io->po7_err_pkt0 = io7->csrs->PO7_ERR_PKT[0].csr; | ||
852 | io->po7_err_pkt1 = io7->csrs->PO7_ERR_PKT[1].csr; | ||
853 | |||
854 | for (i = 0; i < IO7_NUM_PORTS; i++) { | ||
855 | io7_ioport_csrs *csrs = io7->ports[i].csrs; | ||
856 | |||
857 | if (!io7->ports[i].enabled) | ||
858 | continue; | ||
859 | |||
860 | io->ports[i].pox_err_sum = csrs->POx_ERR_SUM.csr; | ||
861 | io->ports[i].pox_tlb_err = csrs->POx_TLB_ERR.csr; | ||
862 | io->ports[i].pox_spl_cmplt = csrs->POx_SPL_COMPLT.csr; | ||
863 | io->ports[i].pox_trans_sum = csrs->POx_TRANS_SUM.csr; | ||
864 | io->ports[i].pox_first_err = csrs->POx_FIRST_ERR.csr; | ||
865 | io->ports[i].pox_mult_err = csrs->POx_MULT_ERR.csr; | ||
866 | io->ports[i].pox_dm_source = csrs->POx_DM_SOURCE.csr; | ||
867 | io->ports[i].pox_dm_dest = csrs->POx_DM_DEST.csr; | ||
868 | io->ports[i].pox_dm_size = csrs->POx_DM_SIZE.csr; | ||
869 | io->ports[i].pox_dm_ctrl = csrs->POx_DM_CTRL.csr; | ||
870 | |||
871 | /* | ||
872 | * Ack this port's errors, if any. POx_ERR_SUM must be last. | ||
873 | * | ||
874 | * Most of the error registers get cleared and unlocked when | ||
875 | * the associated bits in POx_ERR_SUM are cleared (by writing | ||
876 | * 1). POx_TLB_ERR is an exception and must be explicitly | ||
877 | * cleared. | ||
878 | */ | ||
879 | csrs->POx_TLB_ERR.csr = io->ports[i].pox_tlb_err; | ||
880 | csrs->POx_ERR_SUM.csr = io->ports[i].pox_err_sum; | ||
881 | mb(); | ||
882 | csrs->POx_ERR_SUM.csr; | ||
883 | } | ||
884 | |||
885 | /* | ||
886 | * Ack any port 7 error(s). | ||
887 | */ | ||
888 | io7->csrs->PO7_ERROR_SUM.csr = io->po7_error_sum; | ||
889 | mb(); | ||
890 | io7->csrs->PO7_ERROR_SUM.csr; | ||
891 | |||
892 | /* | ||
893 | * Correct the io7_pid. | ||
894 | */ | ||
895 | lf_subpackets->io_pid = io7->pe; | ||
896 | |||
897 | return io; | ||
898 | } | ||
899 | |||
900 | static int | ||
901 | marvel_process_io_error(struct ev7_lf_subpackets *lf_subpackets, int print) | ||
902 | { | ||
903 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
904 | |||
905 | #ifdef CONFIG_VERBOSE_MCHECK | ||
906 | struct ev7_pal_io_subpacket *io = lf_subpackets->io; | ||
907 | int i; | ||
908 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
909 | |||
910 | #define MARVEL_IO_ERR_VALID(x) ((x) & (1UL << 63)) | ||
911 | |||
912 | if (!lf_subpackets->logout || !lf_subpackets->io) | ||
913 | return status; | ||
914 | |||
915 | /* | ||
916 | * The PALcode only builds an IO subpacket if there is a | ||
917 | * locally connected IO7. In the cases of | ||
918 | * 1) a uniprocessor kernel | ||
919 | * 2) an mp kernel before the local secondary has called in | ||
920 | * error interrupts are all directed to the primary processor. | ||
921 | * In that case, we may not have an IO subpacket at all and, event | ||
922 | * if we do, it may not be the right now. | ||
923 | * | ||
924 | * If the RBOX indicates an I/O error interrupt, make sure we have | ||
925 | * the correct IO7 information. If we don't have an IO subpacket | ||
926 | * or it's the wrong one, try to find the right one. | ||
927 | * | ||
928 | * RBOX I/O error interrupts are indicated by RBOX_INT<29> and | ||
929 | * RBOX_INT<10>. | ||
930 | */ | ||
931 | if ((lf_subpackets->io->po7_error_sum & (1UL << 32)) || | ||
932 | ((lf_subpackets->io->po7_error_sum | | ||
933 | lf_subpackets->io->ports[0].pox_err_sum | | ||
934 | lf_subpackets->io->ports[1].pox_err_sum | | ||
935 | lf_subpackets->io->ports[2].pox_err_sum | | ||
936 | lf_subpackets->io->ports[3].pox_err_sum) & (1UL << 63))) { | ||
937 | /* | ||
938 | * Either we have no IO subpacket or no error is | ||
939 | * indicated in the one we do have. Try find the | ||
940 | * one with the error. | ||
941 | */ | ||
942 | if (!marvel_find_io7_with_error(lf_subpackets)) | ||
943 | return status; | ||
944 | } | ||
945 | |||
946 | /* | ||
947 | * We have an IO7 indicating an error - we're going to report it | ||
948 | */ | ||
949 | status = MCHK_DISPOSITION_REPORT; | ||
950 | |||
951 | #ifdef CONFIG_VERBOSE_MCHECK | ||
952 | |||
953 | if (!print) | ||
954 | return status; | ||
955 | |||
956 | printk("%s*Error occurred on IO7 at PID %u\n", | ||
957 | err_print_prefix, lf_subpackets->io_pid); | ||
958 | |||
959 | /* | ||
960 | * Check port 7 first | ||
961 | */ | ||
962 | if (lf_subpackets->io->po7_error_sum & IO7__PO7_ERRSUM__ERR_MASK) { | ||
963 | marvel_print_po7_err_sum(io); | ||
964 | |||
965 | #if 0 | ||
966 | printk("%s PORT 7 ERROR:\n" | ||
967 | "%s PO7_ERROR_SUM: %016lx\n" | ||
968 | "%s PO7_UNCRR_SYM: %016lx\n" | ||
969 | "%s PO7_CRRCT_SYM: %016lx\n" | ||
970 | "%s PO7_UGBGE_SYM: %016lx\n" | ||
971 | "%s PO7_ERR_PKT0: %016lx\n" | ||
972 | "%s PO7_ERR_PKT1: %016lx\n", | ||
973 | err_print_prefix, | ||
974 | err_print_prefix, io->po7_error_sum, | ||
975 | err_print_prefix, io->po7_uncrr_sym, | ||
976 | err_print_prefix, io->po7_crrct_sym, | ||
977 | err_print_prefix, io->po7_ugbge_sym, | ||
978 | err_print_prefix, io->po7_err_pkt0, | ||
979 | err_print_prefix, io->po7_err_pkt1); | ||
980 | #endif | ||
981 | } | ||
982 | |||
983 | /* | ||
984 | * Then loop through the ports | ||
985 | */ | ||
986 | for (i = 0; i < IO7_NUM_PORTS; i++) { | ||
987 | if (!MARVEL_IO_ERR_VALID(io->ports[i].pox_err_sum)) | ||
988 | continue; | ||
989 | |||
990 | printk("%s PID %u PORT %d POx_ERR_SUM: %016lx\n", | ||
991 | err_print_prefix, | ||
992 | lf_subpackets->io_pid, i, io->ports[i].pox_err_sum); | ||
993 | marvel_print_pox_err(io->ports[i].pox_err_sum, &io->ports[i]); | ||
994 | |||
995 | printk("%s [ POx_FIRST_ERR: %016lx ]\n", | ||
996 | err_print_prefix, io->ports[i].pox_first_err); | ||
997 | marvel_print_pox_err(io->ports[i].pox_first_err, | ||
998 | &io->ports[i]); | ||
999 | |||
1000 | } | ||
1001 | |||
1002 | |||
1003 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
1004 | |||
1005 | return status; | ||
1006 | } | ||
1007 | |||
1008 | static int | ||
1009 | marvel_process_logout_frame(struct ev7_lf_subpackets *lf_subpackets, int print) | ||
1010 | { | ||
1011 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
1012 | |||
1013 | /* | ||
1014 | * I/O error? | ||
1015 | */ | ||
1016 | #define EV7__RBOX_INT__IO_ERROR__MASK 0x20000400ul | ||
1017 | if (lf_subpackets->logout && | ||
1018 | (lf_subpackets->logout->rbox_int & 0x20000400ul)) | ||
1019 | status = marvel_process_io_error(lf_subpackets, print); | ||
1020 | |||
1021 | /* | ||
1022 | * Probing behind PCI-X bridges can cause machine checks on | ||
1023 | * Marvel when the probe is handled by the bridge as a split | ||
1024 | * completion transaction. The symptom is an ERROR_RESPONSE | ||
1025 | * to a CONFIG address. Since these errors will happen in | ||
1026 | * normal operation, dismiss them. | ||
1027 | * | ||
1028 | * Dismiss if: | ||
1029 | * C_STAT = 0x14 (Error Reponse) | ||
1030 | * C_STS<3> = 0 (C_ADDR valid) | ||
1031 | * C_ADDR<42> = 1 (I/O) | ||
1032 | * C_ADDR<31:22> = 111110xxb (PCI Config space) | ||
1033 | */ | ||
1034 | if (lf_subpackets->ev7 && | ||
1035 | (lf_subpackets->ev7->c_stat == 0x14) && | ||
1036 | !(lf_subpackets->ev7->c_sts & 0x8) && | ||
1037 | ((lf_subpackets->ev7->c_addr & 0x400ff000000ul) | ||
1038 | == 0x400fe000000ul)) | ||
1039 | status = MCHK_DISPOSITION_DISMISS; | ||
1040 | |||
1041 | return status; | ||
1042 | } | ||
1043 | |||
1044 | void | ||
1045 | marvel_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) | ||
1046 | { | ||
1047 | struct el_subpacket *el_ptr = (struct el_subpacket *)la_ptr; | ||
1048 | int (*process_frame)(struct ev7_lf_subpackets *, int) = NULL; | ||
1049 | struct ev7_lf_subpackets subpacket_collection = { NULL, }; | ||
1050 | struct ev7_pal_io_subpacket scratch_io_packet = { 0, }; | ||
1051 | struct ev7_lf_subpackets *lf_subpackets = NULL; | ||
1052 | int disposition = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
1053 | char *saved_err_prefix = err_print_prefix; | ||
1054 | char *error_type = NULL; | ||
1055 | |||
1056 | /* | ||
1057 | * Sync the processor | ||
1058 | */ | ||
1059 | mb(); | ||
1060 | draina(); | ||
1061 | |||
1062 | switch(vector) { | ||
1063 | case SCB_Q_SYSEVENT: | ||
1064 | process_frame = marvel_process_680_frame; | ||
1065 | error_type = "System Event"; | ||
1066 | break; | ||
1067 | |||
1068 | case SCB_Q_SYSMCHK: | ||
1069 | process_frame = marvel_process_logout_frame; | ||
1070 | error_type = "System Uncorrectable Error"; | ||
1071 | break; | ||
1072 | |||
1073 | case SCB_Q_SYSERR: | ||
1074 | process_frame = marvel_process_logout_frame; | ||
1075 | error_type = "System Correctable Error"; | ||
1076 | break; | ||
1077 | |||
1078 | default: | ||
1079 | /* Don't know it - pass it up. */ | ||
1080 | ev7_machine_check(vector, la_ptr, regs); | ||
1081 | return; | ||
1082 | } | ||
1083 | |||
1084 | /* | ||
1085 | * A system event or error has occured, handle it here. | ||
1086 | * | ||
1087 | * Any errors in the logout frame have already been cleared by the | ||
1088 | * PALcode, so just parse it. | ||
1089 | */ | ||
1090 | err_print_prefix = KERN_CRIT; | ||
1091 | |||
1092 | /* | ||
1093 | * Parse the logout frame without printing first. If the only error(s) | ||
1094 | * found are classified as "dismissable", then just dismiss them and | ||
1095 | * don't print any message | ||
1096 | */ | ||
1097 | lf_subpackets = | ||
1098 | ev7_collect_logout_frame_subpackets(el_ptr, | ||
1099 | &subpacket_collection); | ||
1100 | if (process_frame && lf_subpackets && lf_subpackets->logout) { | ||
1101 | /* | ||
1102 | * We might not have the correct (or any) I/O subpacket. | ||
1103 | * [ See marvel_process_io_error() for explanation. ] | ||
1104 | * If we don't have one, point the io subpacket in | ||
1105 | * lf_subpackets at scratch_io_packet so that | ||
1106 | * marvel_find_io7_with_error() will have someplace to | ||
1107 | * store the info. | ||
1108 | */ | ||
1109 | if (!lf_subpackets->io) | ||
1110 | lf_subpackets->io = &scratch_io_packet; | ||
1111 | |||
1112 | /* | ||
1113 | * Default io_pid to the processor reporting the error | ||
1114 | * [this will get changed in marvel_find_io7_with_error() | ||
1115 | * if a different one is needed] | ||
1116 | */ | ||
1117 | lf_subpackets->io_pid = lf_subpackets->logout->whami; | ||
1118 | |||
1119 | /* | ||
1120 | * Evaluate the frames. | ||
1121 | */ | ||
1122 | disposition = process_frame(lf_subpackets, 0); | ||
1123 | } | ||
1124 | switch(disposition) { | ||
1125 | case MCHK_DISPOSITION_DISMISS: | ||
1126 | /* Nothing to do. */ | ||
1127 | break; | ||
1128 | |||
1129 | case MCHK_DISPOSITION_REPORT: | ||
1130 | /* Recognized error, report it. */ | ||
1131 | printk("%s*%s (Vector 0x%x) reported on CPU %d\n", | ||
1132 | err_print_prefix, error_type, | ||
1133 | (unsigned int)vector, (int)smp_processor_id()); | ||
1134 | el_print_timestamp(&lf_subpackets->logout->timestamp); | ||
1135 | process_frame(lf_subpackets, 1); | ||
1136 | break; | ||
1137 | |||
1138 | default: | ||
1139 | /* Unknown - dump the annotated subpackets. */ | ||
1140 | printk("%s*%s (Vector 0x%x) reported on CPU %d\n", | ||
1141 | err_print_prefix, error_type, | ||
1142 | (unsigned int)vector, (int)smp_processor_id()); | ||
1143 | el_process_subpacket(el_ptr); | ||
1144 | break; | ||
1145 | |||
1146 | } | ||
1147 | |||
1148 | err_print_prefix = saved_err_prefix; | ||
1149 | |||
1150 | /* Release the logout frame. */ | ||
1151 | wrmces(0x7); | ||
1152 | mb(); | ||
1153 | } | ||
1154 | |||
1155 | void | ||
1156 | marvel_register_error_handlers(void) | ||
1157 | { | ||
1158 | ev7_register_error_handlers(); | ||
1159 | } | ||
diff --git a/arch/alpha/kernel/err_titan.c b/arch/alpha/kernel/err_titan.c new file mode 100644 index 000000000000..7e6720d45f02 --- /dev/null +++ b/arch/alpha/kernel/err_titan.c | |||
@@ -0,0 +1,756 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/err_titan.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) | ||
5 | * | ||
6 | * Error handling code supporting TITAN systems | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/sched.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/core_titan.h> | ||
15 | #include <asm/hwrpb.h> | ||
16 | #include <asm/smp.h> | ||
17 | #include <asm/err_common.h> | ||
18 | #include <asm/err_ev6.h> | ||
19 | |||
20 | #include "err_impl.h" | ||
21 | #include "proto.h" | ||
22 | |||
23 | |||
24 | static int | ||
25 | titan_parse_c_misc(u64 c_misc, int print) | ||
26 | { | ||
27 | #ifdef CONFIG_VERBOSE_MCHECK | ||
28 | char *src; | ||
29 | int nxs = 0; | ||
30 | #endif | ||
31 | int status = MCHK_DISPOSITION_REPORT; | ||
32 | |||
33 | #define TITAN__CCHIP_MISC__NXM (1UL << 28) | ||
34 | #define TITAN__CCHIP_MISC__NXS__S (29) | ||
35 | #define TITAN__CCHIP_MISC__NXS__M (0x7) | ||
36 | |||
37 | if (!(c_misc & TITAN__CCHIP_MISC__NXM)) | ||
38 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
39 | |||
40 | #ifdef CONFIG_VERBOSE_MCHECK | ||
41 | if (!print) | ||
42 | return status; | ||
43 | |||
44 | nxs = EXTRACT(c_misc, TITAN__CCHIP_MISC__NXS); | ||
45 | switch(nxs) { | ||
46 | case 0: /* CPU 0 */ | ||
47 | case 1: /* CPU 1 */ | ||
48 | case 2: /* CPU 2 */ | ||
49 | case 3: /* CPU 3 */ | ||
50 | src = "CPU"; | ||
51 | /* num is already the CPU number */ | ||
52 | break; | ||
53 | case 4: /* Pchip 0 */ | ||
54 | case 5: /* Pchip 1 */ | ||
55 | src = "Pchip"; | ||
56 | nxs -= 4; | ||
57 | break; | ||
58 | default:/* reserved */ | ||
59 | src = "Unknown, NXS ="; | ||
60 | /* leave num untouched */ | ||
61 | break; | ||
62 | } | ||
63 | |||
64 | printk("%s Non-existent memory access from: %s %d\n", | ||
65 | err_print_prefix, src, nxs); | ||
66 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
67 | |||
68 | return status; | ||
69 | } | ||
70 | |||
71 | static int | ||
72 | titan_parse_p_serror(int which, u64 serror, int print) | ||
73 | { | ||
74 | int status = MCHK_DISPOSITION_REPORT; | ||
75 | |||
76 | #ifdef CONFIG_VERBOSE_MCHECK | ||
77 | char *serror_src[] = {"GPCI", "APCI", "AGP HP", "AGP LP"}; | ||
78 | char *serror_cmd[] = {"DMA Read", "DMA RMW", "SGTE Read", "Reserved"}; | ||
79 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
80 | |||
81 | #define TITAN__PCHIP_SERROR__LOST_UECC (1UL << 0) | ||
82 | #define TITAN__PCHIP_SERROR__UECC (1UL << 1) | ||
83 | #define TITAN__PCHIP_SERROR__CRE (1UL << 2) | ||
84 | #define TITAN__PCHIP_SERROR__NXIO (1UL << 3) | ||
85 | #define TITAN__PCHIP_SERROR__LOST_CRE (1UL << 4) | ||
86 | #define TITAN__PCHIP_SERROR__ECCMASK (TITAN__PCHIP_SERROR__UECC | \ | ||
87 | TITAN__PCHIP_SERROR__CRE) | ||
88 | #define TITAN__PCHIP_SERROR__ERRMASK (TITAN__PCHIP_SERROR__LOST_UECC | \ | ||
89 | TITAN__PCHIP_SERROR__UECC | \ | ||
90 | TITAN__PCHIP_SERROR__CRE | \ | ||
91 | TITAN__PCHIP_SERROR__NXIO | \ | ||
92 | TITAN__PCHIP_SERROR__LOST_CRE) | ||
93 | #define TITAN__PCHIP_SERROR__SRC__S (52) | ||
94 | #define TITAN__PCHIP_SERROR__SRC__M (0x3) | ||
95 | #define TITAN__PCHIP_SERROR__CMD__S (54) | ||
96 | #define TITAN__PCHIP_SERROR__CMD__M (0x3) | ||
97 | #define TITAN__PCHIP_SERROR__SYN__S (56) | ||
98 | #define TITAN__PCHIP_SERROR__SYN__M (0xff) | ||
99 | #define TITAN__PCHIP_SERROR__ADDR__S (15) | ||
100 | #define TITAN__PCHIP_SERROR__ADDR__M (0xffffffffUL) | ||
101 | |||
102 | if (!(serror & TITAN__PCHIP_SERROR__ERRMASK)) | ||
103 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
104 | |||
105 | #ifdef CONFIG_VERBOSE_MCHECK | ||
106 | if (!print) | ||
107 | return status; | ||
108 | |||
109 | printk("%s PChip %d SERROR: %016lx\n", | ||
110 | err_print_prefix, which, serror); | ||
111 | if (serror & TITAN__PCHIP_SERROR__ECCMASK) { | ||
112 | printk("%s %sorrectable ECC Error:\n" | ||
113 | " Source: %-6s Command: %-8s Syndrome: 0x%08x\n" | ||
114 | " Address: 0x%lx\n", | ||
115 | err_print_prefix, | ||
116 | (serror & TITAN__PCHIP_SERROR__UECC) ? "Unc" : "C", | ||
117 | serror_src[EXTRACT(serror, TITAN__PCHIP_SERROR__SRC)], | ||
118 | serror_cmd[EXTRACT(serror, TITAN__PCHIP_SERROR__CMD)], | ||
119 | (unsigned)EXTRACT(serror, TITAN__PCHIP_SERROR__SYN), | ||
120 | EXTRACT(serror, TITAN__PCHIP_SERROR__ADDR)); | ||
121 | } | ||
122 | if (serror & TITAN__PCHIP_SERROR__NXIO) | ||
123 | printk("%s Non Existent I/O Error\n", err_print_prefix); | ||
124 | if (serror & TITAN__PCHIP_SERROR__LOST_UECC) | ||
125 | printk("%s Lost Uncorrectable ECC Error\n", | ||
126 | err_print_prefix); | ||
127 | if (serror & TITAN__PCHIP_SERROR__LOST_CRE) | ||
128 | printk("%s Lost Correctable ECC Error\n", err_print_prefix); | ||
129 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
130 | |||
131 | return status; | ||
132 | } | ||
133 | |||
134 | static int | ||
135 | titan_parse_p_perror(int which, int port, u64 perror, int print) | ||
136 | { | ||
137 | int cmd; | ||
138 | unsigned long addr; | ||
139 | int status = MCHK_DISPOSITION_REPORT; | ||
140 | |||
141 | #ifdef CONFIG_VERBOSE_MCHECK | ||
142 | char *perror_cmd[] = { "Interrupt Acknowledge", "Special Cycle", | ||
143 | "I/O Read", "I/O Write", | ||
144 | "Reserved", "Reserved", | ||
145 | "Memory Read", "Memory Write", | ||
146 | "Reserved", "Reserved", | ||
147 | "Configuration Read", "Configuration Write", | ||
148 | "Memory Read Multiple", "Dual Address Cycle", | ||
149 | "Memory Read Line","Memory Write and Invalidate" | ||
150 | }; | ||
151 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
152 | |||
153 | #define TITAN__PCHIP_PERROR__LOST (1UL << 0) | ||
154 | #define TITAN__PCHIP_PERROR__SERR (1UL << 1) | ||
155 | #define TITAN__PCHIP_PERROR__PERR (1UL << 2) | ||
156 | #define TITAN__PCHIP_PERROR__DCRTO (1UL << 3) | ||
157 | #define TITAN__PCHIP_PERROR__SGE (1UL << 4) | ||
158 | #define TITAN__PCHIP_PERROR__APE (1UL << 5) | ||
159 | #define TITAN__PCHIP_PERROR__TA (1UL << 6) | ||
160 | #define TITAN__PCHIP_PERROR__DPE (1UL << 7) | ||
161 | #define TITAN__PCHIP_PERROR__NDS (1UL << 8) | ||
162 | #define TITAN__PCHIP_PERROR__IPTPR (1UL << 9) | ||
163 | #define TITAN__PCHIP_PERROR__IPTPW (1UL << 10) | ||
164 | #define TITAN__PCHIP_PERROR__ERRMASK (TITAN__PCHIP_PERROR__LOST | \ | ||
165 | TITAN__PCHIP_PERROR__SERR | \ | ||
166 | TITAN__PCHIP_PERROR__PERR | \ | ||
167 | TITAN__PCHIP_PERROR__DCRTO | \ | ||
168 | TITAN__PCHIP_PERROR__SGE | \ | ||
169 | TITAN__PCHIP_PERROR__APE | \ | ||
170 | TITAN__PCHIP_PERROR__TA | \ | ||
171 | TITAN__PCHIP_PERROR__DPE | \ | ||
172 | TITAN__PCHIP_PERROR__NDS | \ | ||
173 | TITAN__PCHIP_PERROR__IPTPR | \ | ||
174 | TITAN__PCHIP_PERROR__IPTPW) | ||
175 | #define TITAN__PCHIP_PERROR__DAC (1UL << 47) | ||
176 | #define TITAN__PCHIP_PERROR__MWIN (1UL << 48) | ||
177 | #define TITAN__PCHIP_PERROR__CMD__S (52) | ||
178 | #define TITAN__PCHIP_PERROR__CMD__M (0x0f) | ||
179 | #define TITAN__PCHIP_PERROR__ADDR__S (14) | ||
180 | #define TITAN__PCHIP_PERROR__ADDR__M (0x1fffffffful) | ||
181 | |||
182 | if (!(perror & TITAN__PCHIP_PERROR__ERRMASK)) | ||
183 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
184 | |||
185 | cmd = EXTRACT(perror, TITAN__PCHIP_PERROR__CMD); | ||
186 | addr = EXTRACT(perror, TITAN__PCHIP_PERROR__ADDR) << 2; | ||
187 | |||
188 | /* | ||
189 | * Initializing the BIOS on a video card on a bus without | ||
190 | * a south bridge (subtractive decode agent) can result in | ||
191 | * master aborts as the BIOS probes the capabilities of the | ||
192 | * card. XFree86 does such initialization. If the error | ||
193 | * is a master abort (No DevSel as PCI Master) and the command | ||
194 | * is an I/O read or write below the address where we start | ||
195 | * assigning PCI I/O spaces (SRM uses 0x1000), then mark the | ||
196 | * error as dismissable so starting XFree86 doesn't result | ||
197 | * in a series of uncorrectable errors being reported. Also | ||
198 | * dismiss master aborts to VGA frame buffer space | ||
199 | * (0xA0000 - 0xC0000) and legacy BIOS space (0xC0000 - 0x100000) | ||
200 | * for the same reason. | ||
201 | * | ||
202 | * Also mark the error dismissible if it looks like the right | ||
203 | * error but only the Lost bit is set. Since the BIOS initialization | ||
204 | * can cause multiple master aborts and the error interrupt can | ||
205 | * be handled on a different CPU than the BIOS code is run on, | ||
206 | * it is possible for a second master abort to occur between the | ||
207 | * time the PALcode reads PERROR and the time it writes PERROR | ||
208 | * to acknowledge the error. If this timing happens, a second | ||
209 | * error will be signalled after the first, and if no additional | ||
210 | * errors occur, will look like a Lost error with no additional | ||
211 | * errors on the same transaction as the previous error. | ||
212 | */ | ||
213 | if (((perror & TITAN__PCHIP_PERROR__NDS) || | ||
214 | ((perror & TITAN__PCHIP_PERROR__ERRMASK) == | ||
215 | TITAN__PCHIP_PERROR__LOST)) && | ||
216 | ((((cmd & 0xE) == 2) && (addr < 0x1000)) || | ||
217 | (((cmd & 0xE) == 6) && (addr >= 0xA0000) && (addr < 0x100000)))) { | ||
218 | status = MCHK_DISPOSITION_DISMISS; | ||
219 | } | ||
220 | |||
221 | #ifdef CONFIG_VERBOSE_MCHECK | ||
222 | if (!print) | ||
223 | return status; | ||
224 | |||
225 | printk("%s PChip %d %cPERROR: %016lx\n", | ||
226 | err_print_prefix, which, | ||
227 | port ? 'A' : 'G', perror); | ||
228 | if (perror & TITAN__PCHIP_PERROR__IPTPW) | ||
229 | printk("%s Invalid Peer-to-Peer Write\n", err_print_prefix); | ||
230 | if (perror & TITAN__PCHIP_PERROR__IPTPR) | ||
231 | printk("%s Invalid Peer-to-Peer Read\n", err_print_prefix); | ||
232 | if (perror & TITAN__PCHIP_PERROR__NDS) | ||
233 | printk("%s No DEVSEL as PCI Master [Master Abort]\n", | ||
234 | err_print_prefix); | ||
235 | if (perror & TITAN__PCHIP_PERROR__DPE) | ||
236 | printk("%s Data Parity Error\n", err_print_prefix); | ||
237 | if (perror & TITAN__PCHIP_PERROR__TA) | ||
238 | printk("%s Target Abort\n", err_print_prefix); | ||
239 | if (perror & TITAN__PCHIP_PERROR__APE) | ||
240 | printk("%s Address Parity Error\n", err_print_prefix); | ||
241 | if (perror & TITAN__PCHIP_PERROR__SGE) | ||
242 | printk("%s Scatter-Gather Error, Invalid PTE\n", | ||
243 | err_print_prefix); | ||
244 | if (perror & TITAN__PCHIP_PERROR__DCRTO) | ||
245 | printk("%s Delayed-Completion Retry Timeout\n", | ||
246 | err_print_prefix); | ||
247 | if (perror & TITAN__PCHIP_PERROR__PERR) | ||
248 | printk("%s PERR Asserted\n", err_print_prefix); | ||
249 | if (perror & TITAN__PCHIP_PERROR__SERR) | ||
250 | printk("%s SERR Asserted\n", err_print_prefix); | ||
251 | if (perror & TITAN__PCHIP_PERROR__LOST) | ||
252 | printk("%s Lost Error\n", err_print_prefix); | ||
253 | printk("%s Command: 0x%x - %s\n" | ||
254 | " Address: 0x%lx\n", | ||
255 | err_print_prefix, | ||
256 | cmd, perror_cmd[cmd], | ||
257 | addr); | ||
258 | if (perror & TITAN__PCHIP_PERROR__DAC) | ||
259 | printk("%s Dual Address Cycle\n", err_print_prefix); | ||
260 | if (perror & TITAN__PCHIP_PERROR__MWIN) | ||
261 | printk("%s Hit in Monster Window\n", err_print_prefix); | ||
262 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
263 | |||
264 | return status; | ||
265 | } | ||
266 | |||
267 | static int | ||
268 | titan_parse_p_agperror(int which, u64 agperror, int print) | ||
269 | { | ||
270 | int status = MCHK_DISPOSITION_REPORT; | ||
271 | #ifdef CONFIG_VERBOSE_MCHECK | ||
272 | int cmd, len; | ||
273 | unsigned long addr; | ||
274 | |||
275 | char *agperror_cmd[] = { "Read (low-priority)", "Read (high-priority)", | ||
276 | "Write (low-priority)", | ||
277 | "Write (high-priority)", | ||
278 | "Reserved", "Reserved", | ||
279 | "Flush", "Fence" | ||
280 | }; | ||
281 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
282 | |||
283 | #define TITAN__PCHIP_AGPERROR__LOST (1UL << 0) | ||
284 | #define TITAN__PCHIP_AGPERROR__LPQFULL (1UL << 1) | ||
285 | #define TITAN__PCHIP_AGPERROR__HPQFULL (1UL << 2) | ||
286 | #define TITAN__PCHIP_AGPERROR__RESCMD (1UL << 3) | ||
287 | #define TITAN__PCHIP_AGPERROR__IPTE (1UL << 4) | ||
288 | #define TITAN__PCHIP_AGPERROR__PTP (1UL << 5) | ||
289 | #define TITAN__PCHIP_AGPERROR__NOWINDOW (1UL << 6) | ||
290 | #define TITAN__PCHIP_AGPERROR__ERRMASK (TITAN__PCHIP_AGPERROR__LOST | \ | ||
291 | TITAN__PCHIP_AGPERROR__LPQFULL | \ | ||
292 | TITAN__PCHIP_AGPERROR__HPQFULL | \ | ||
293 | TITAN__PCHIP_AGPERROR__RESCMD | \ | ||
294 | TITAN__PCHIP_AGPERROR__IPTE | \ | ||
295 | TITAN__PCHIP_AGPERROR__PTP | \ | ||
296 | TITAN__PCHIP_AGPERROR__NOWINDOW) | ||
297 | #define TITAN__PCHIP_AGPERROR__DAC (1UL << 48) | ||
298 | #define TITAN__PCHIP_AGPERROR__MWIN (1UL << 49) | ||
299 | #define TITAN__PCHIP_AGPERROR__FENCE (1UL << 59) | ||
300 | #define TITAN__PCHIP_AGPERROR__CMD__S (50) | ||
301 | #define TITAN__PCHIP_AGPERROR__CMD__M (0x07) | ||
302 | #define TITAN__PCHIP_AGPERROR__ADDR__S (15) | ||
303 | #define TITAN__PCHIP_AGPERROR__ADDR__M (0xffffffffUL) | ||
304 | #define TITAN__PCHIP_AGPERROR__LEN__S (53) | ||
305 | #define TITAN__PCHIP_AGPERROR__LEN__M (0x3f) | ||
306 | |||
307 | if (!(agperror & TITAN__PCHIP_AGPERROR__ERRMASK)) | ||
308 | return MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
309 | |||
310 | #ifdef CONFIG_VERBOSE_MCHECK | ||
311 | if (!print) | ||
312 | return status; | ||
313 | |||
314 | cmd = EXTRACT(agperror, TITAN__PCHIP_AGPERROR__CMD); | ||
315 | addr = EXTRACT(agperror, TITAN__PCHIP_AGPERROR__ADDR) << 3; | ||
316 | len = EXTRACT(agperror, TITAN__PCHIP_AGPERROR__LEN); | ||
317 | |||
318 | printk("%s PChip %d AGPERROR: %016lx\n", err_print_prefix, | ||
319 | which, agperror); | ||
320 | if (agperror & TITAN__PCHIP_AGPERROR__NOWINDOW) | ||
321 | printk("%s No Window\n", err_print_prefix); | ||
322 | if (agperror & TITAN__PCHIP_AGPERROR__PTP) | ||
323 | printk("%s Peer-to-Peer set\n", err_print_prefix); | ||
324 | if (agperror & TITAN__PCHIP_AGPERROR__IPTE) | ||
325 | printk("%s Invalid PTE\n", err_print_prefix); | ||
326 | if (agperror & TITAN__PCHIP_AGPERROR__RESCMD) | ||
327 | printk("%s Reserved Command\n", err_print_prefix); | ||
328 | if (agperror & TITAN__PCHIP_AGPERROR__HPQFULL) | ||
329 | printk("%s HP Transaction Received while Queue Full\n", | ||
330 | err_print_prefix); | ||
331 | if (agperror & TITAN__PCHIP_AGPERROR__LPQFULL) | ||
332 | printk("%s LP Transaction Received while Queue Full\n", | ||
333 | err_print_prefix); | ||
334 | if (agperror & TITAN__PCHIP_AGPERROR__LOST) | ||
335 | printk("%s Lost Error\n", err_print_prefix); | ||
336 | printk("%s Command: 0x%x - %s, %d Quadwords%s\n" | ||
337 | " Address: 0x%lx\n", | ||
338 | err_print_prefix, cmd, agperror_cmd[cmd], len, | ||
339 | (agperror & TITAN__PCHIP_AGPERROR__FENCE) ? ", FENCE" : "", | ||
340 | addr); | ||
341 | if (agperror & TITAN__PCHIP_AGPERROR__DAC) | ||
342 | printk("%s Dual Address Cycle\n", err_print_prefix); | ||
343 | if (agperror & TITAN__PCHIP_AGPERROR__MWIN) | ||
344 | printk("%s Hit in Monster Window\n", err_print_prefix); | ||
345 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
346 | |||
347 | return status; | ||
348 | } | ||
349 | |||
350 | static int | ||
351 | titan_parse_p_chip(int which, u64 serror, u64 gperror, | ||
352 | u64 aperror, u64 agperror, int print) | ||
353 | { | ||
354 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
355 | status |= titan_parse_p_serror(which, serror, print); | ||
356 | status |= titan_parse_p_perror(which, 0, gperror, print); | ||
357 | status |= titan_parse_p_perror(which, 1, aperror, print); | ||
358 | status |= titan_parse_p_agperror(which, agperror, print); | ||
359 | return status; | ||
360 | } | ||
361 | |||
362 | int | ||
363 | titan_process_logout_frame(struct el_common *mchk_header, int print) | ||
364 | { | ||
365 | struct el_TITAN_sysdata_mcheck *tmchk = | ||
366 | (struct el_TITAN_sysdata_mcheck *) | ||
367 | ((unsigned long)mchk_header + mchk_header->sys_offset); | ||
368 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
369 | |||
370 | status |= titan_parse_c_misc(tmchk->c_misc, print); | ||
371 | status |= titan_parse_p_chip(0, tmchk->p0_serror, tmchk->p0_gperror, | ||
372 | tmchk->p0_aperror, tmchk->p0_agperror, | ||
373 | print); | ||
374 | status |= titan_parse_p_chip(1, tmchk->p1_serror, tmchk->p1_gperror, | ||
375 | tmchk->p1_aperror, tmchk->p1_agperror, | ||
376 | print); | ||
377 | |||
378 | return status; | ||
379 | } | ||
380 | |||
381 | void | ||
382 | titan_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) | ||
383 | { | ||
384 | struct el_common *mchk_header = (struct el_common *)la_ptr; | ||
385 | struct el_TITAN_sysdata_mcheck *tmchk = | ||
386 | (struct el_TITAN_sysdata_mcheck *) | ||
387 | ((unsigned long)mchk_header + mchk_header->sys_offset); | ||
388 | u64 irqmask; | ||
389 | |||
390 | /* | ||
391 | * Mask of Titan interrupt sources which are reported as machine checks | ||
392 | * | ||
393 | * 63 - CChip Error | ||
394 | * 62 - PChip 0 H_Error | ||
395 | * 61 - PChip 1 H_Error | ||
396 | * 60 - PChip 0 C_Error | ||
397 | * 59 - PChip 1 C_Error | ||
398 | */ | ||
399 | #define TITAN_MCHECK_INTERRUPT_MASK 0xF800000000000000UL | ||
400 | |||
401 | /* | ||
402 | * Sync the processor | ||
403 | */ | ||
404 | mb(); | ||
405 | draina(); | ||
406 | |||
407 | /* | ||
408 | * Only handle system errors here | ||
409 | */ | ||
410 | if ((vector != SCB_Q_SYSMCHK) && (vector != SCB_Q_SYSERR)) { | ||
411 | ev6_machine_check(vector, la_ptr, regs); | ||
412 | return; | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * It's a system error, handle it here | ||
417 | * | ||
418 | * The PALcode has already cleared the error, so just parse it | ||
419 | */ | ||
420 | |||
421 | /* | ||
422 | * Parse the logout frame without printing first. If the only error(s) | ||
423 | * found are classified as "dismissable", then just dismiss them and | ||
424 | * don't print any message | ||
425 | */ | ||
426 | if (titan_process_logout_frame(mchk_header, 0) != | ||
427 | MCHK_DISPOSITION_DISMISS) { | ||
428 | char *saved_err_prefix = err_print_prefix; | ||
429 | err_print_prefix = KERN_CRIT; | ||
430 | |||
431 | /* | ||
432 | * Either a nondismissable error was detected or no | ||
433 | * recognized error was detected in the logout frame | ||
434 | * -- report the error in either case | ||
435 | */ | ||
436 | printk("%s" | ||
437 | "*System %s Error (Vector 0x%x) reported on CPU %d:\n", | ||
438 | err_print_prefix, | ||
439 | (vector == SCB_Q_SYSERR)?"Correctable":"Uncorrectable", | ||
440 | (unsigned int)vector, (int)smp_processor_id()); | ||
441 | |||
442 | #ifdef CONFIG_VERBOSE_MCHECK | ||
443 | titan_process_logout_frame(mchk_header, alpha_verbose_mcheck); | ||
444 | if (alpha_verbose_mcheck) | ||
445 | dik_show_regs(regs, NULL); | ||
446 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
447 | |||
448 | err_print_prefix = saved_err_prefix; | ||
449 | |||
450 | /* | ||
451 | * Convert any pending interrupts which report as system | ||
452 | * machine checks to interrupts | ||
453 | */ | ||
454 | irqmask = tmchk->c_dirx & TITAN_MCHECK_INTERRUPT_MASK; | ||
455 | titan_dispatch_irqs(irqmask, regs); | ||
456 | } | ||
457 | |||
458 | |||
459 | /* | ||
460 | * Release the logout frame | ||
461 | */ | ||
462 | wrmces(0x7); | ||
463 | mb(); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * Subpacket Annotations | ||
468 | */ | ||
469 | static char *el_titan_pchip0_extended_annotation[] = { | ||
470 | "Subpacket Header", "P0_SCTL", "P0_SERREN", | ||
471 | "P0_APCTL", "P0_APERREN", "P0_AGPERREN", | ||
472 | "P0_ASPRST", "P0_AWSBA0", "P0_AWSBA1", | ||
473 | "P0_AWSBA2", "P0_AWSBA3", "P0_AWSM0", | ||
474 | "P0_AWSM1", "P0_AWSM2", "P0_AWSM3", | ||
475 | "P0_ATBA0", "P0_ATBA1", "P0_ATBA2", | ||
476 | "P0_ATBA3", "P0_GPCTL", "P0_GPERREN", | ||
477 | "P0_GSPRST", "P0_GWSBA0", "P0_GWSBA1", | ||
478 | "P0_GWSBA2", "P0_GWSBA3", "P0_GWSM0", | ||
479 | "P0_GWSM1", "P0_GWSM2", "P0_GWSM3", | ||
480 | "P0_GTBA0", "P0_GTBA1", "P0_GTBA2", | ||
481 | "P0_GTBA3", NULL | ||
482 | }; | ||
483 | static char *el_titan_pchip1_extended_annotation[] = { | ||
484 | "Subpacket Header", "P1_SCTL", "P1_SERREN", | ||
485 | "P1_APCTL", "P1_APERREN", "P1_AGPERREN", | ||
486 | "P1_ASPRST", "P1_AWSBA0", "P1_AWSBA1", | ||
487 | "P1_AWSBA2", "P1_AWSBA3", "P1_AWSM0", | ||
488 | "P1_AWSM1", "P1_AWSM2", "P1_AWSM3", | ||
489 | "P1_ATBA0", "P1_ATBA1", "P1_ATBA2", | ||
490 | "P1_ATBA3", "P1_GPCTL", "P1_GPERREN", | ||
491 | "P1_GSPRST", "P1_GWSBA0", "P1_GWSBA1", | ||
492 | "P1_GWSBA2", "P1_GWSBA3", "P1_GWSM0", | ||
493 | "P1_GWSM1", "P1_GWSM2", "P1_GWSM3", | ||
494 | "P1_GTBA0", "P1_GTBA1", "P1_GTBA2", | ||
495 | "P1_GTBA3", NULL | ||
496 | }; | ||
497 | static char *el_titan_memory_extended_annotation[] = { | ||
498 | "Subpacket Header", "AAR0", "AAR1", | ||
499 | "AAR2", "AAR3", "P0_SCTL", | ||
500 | "P0_GPCTL", "P0_APCTL", "P1_SCTL", | ||
501 | "P1_GPCTL", "P1_SCTL", NULL | ||
502 | }; | ||
503 | |||
504 | static struct el_subpacket_annotation el_titan_annotations[] = { | ||
505 | SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, | ||
506 | EL_TYPE__REGATTA__TITAN_PCHIP0_EXTENDED, | ||
507 | 1, | ||
508 | "Titan PChip 0 Extended Frame", | ||
509 | el_titan_pchip0_extended_annotation), | ||
510 | SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, | ||
511 | EL_TYPE__REGATTA__TITAN_PCHIP1_EXTENDED, | ||
512 | 1, | ||
513 | "Titan PChip 1 Extended Frame", | ||
514 | el_titan_pchip1_extended_annotation), | ||
515 | SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, | ||
516 | EL_TYPE__REGATTA__TITAN_MEMORY_EXTENDED, | ||
517 | 1, | ||
518 | "Titan Memory Extended Frame", | ||
519 | el_titan_memory_extended_annotation), | ||
520 | SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, | ||
521 | EL_TYPE__TERMINATION__TERMINATION, | ||
522 | 1, | ||
523 | "Termination Subpacket", | ||
524 | NULL) | ||
525 | }; | ||
526 | |||
527 | static struct el_subpacket * | ||
528 | el_process_regatta_subpacket(struct el_subpacket *header) | ||
529 | { | ||
530 | int status; | ||
531 | |||
532 | if (header->class != EL_CLASS__REGATTA_FAMILY) { | ||
533 | printk("%s ** Unexpected header CLASS %d TYPE %d, aborting\n", | ||
534 | err_print_prefix, | ||
535 | header->class, header->type); | ||
536 | return NULL; | ||
537 | } | ||
538 | |||
539 | switch(header->type) { | ||
540 | case EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME: | ||
541 | case EL_TYPE__REGATTA__SYSTEM_ERROR_FRAME: | ||
542 | case EL_TYPE__REGATTA__ENVIRONMENTAL_FRAME: | ||
543 | case EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT: | ||
544 | case EL_TYPE__REGATTA__SYSTEM_DBL_ERROR_HALT: | ||
545 | printk("%s ** Occurred on CPU %d:\n", | ||
546 | err_print_prefix, | ||
547 | (int)header->by_type.regatta_frame.cpuid); | ||
548 | status = privateer_process_logout_frame((struct el_common *) | ||
549 | header->by_type.regatta_frame.data_start, 1); | ||
550 | break; | ||
551 | default: | ||
552 | printk("%s ** REGATTA TYPE %d SUBPACKET\n", | ||
553 | err_print_prefix, header->type); | ||
554 | el_annotate_subpacket(header); | ||
555 | break; | ||
556 | } | ||
557 | |||
558 | |||
559 | return (struct el_subpacket *)((unsigned long)header + header->length); | ||
560 | } | ||
561 | |||
562 | static struct el_subpacket_handler titan_subpacket_handler = | ||
563 | SUBPACKET_HANDLER_INIT(EL_CLASS__REGATTA_FAMILY, | ||
564 | el_process_regatta_subpacket); | ||
565 | |||
566 | void | ||
567 | titan_register_error_handlers(void) | ||
568 | { | ||
569 | size_t i; | ||
570 | |||
571 | for (i = 0; i < ARRAY_SIZE (el_titan_annotations); i++) | ||
572 | cdl_register_subpacket_annotation(&el_titan_annotations[i]); | ||
573 | |||
574 | cdl_register_subpacket_handler(&titan_subpacket_handler); | ||
575 | |||
576 | ev6_register_error_handlers(); | ||
577 | } | ||
578 | |||
579 | |||
580 | /* | ||
581 | * Privateer | ||
582 | */ | ||
583 | |||
584 | static int | ||
585 | privateer_process_680_frame(struct el_common *mchk_header, int print) | ||
586 | { | ||
587 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
588 | #ifdef CONFIG_VERBOSE_MCHECK | ||
589 | struct el_PRIVATEER_envdata_mcheck *emchk = | ||
590 | (struct el_PRIVATEER_envdata_mcheck *) | ||
591 | ((unsigned long)mchk_header + mchk_header->sys_offset); | ||
592 | |||
593 | /* TODO - catagorize errors, for now, no error */ | ||
594 | |||
595 | if (!print) | ||
596 | return status; | ||
597 | |||
598 | /* TODO - decode instead of just dumping... */ | ||
599 | printk("%s Summary Flags: %016lx\n" | ||
600 | " CChip DIRx: %016lx\n" | ||
601 | " System Management IR: %016lx\n" | ||
602 | " CPU IR: %016lx\n" | ||
603 | " Power Supply IR: %016lx\n" | ||
604 | " LM78 Fault Status: %016lx\n" | ||
605 | " System Doors: %016lx\n" | ||
606 | " Temperature Warning: %016lx\n" | ||
607 | " Fan Control: %016lx\n" | ||
608 | " Fatal Power Down Code: %016lx\n", | ||
609 | err_print_prefix, | ||
610 | emchk->summary, | ||
611 | emchk->c_dirx, | ||
612 | emchk->smir, | ||
613 | emchk->cpuir, | ||
614 | emchk->psir, | ||
615 | emchk->fault, | ||
616 | emchk->sys_doors, | ||
617 | emchk->temp_warn, | ||
618 | emchk->fan_ctrl, | ||
619 | emchk->code); | ||
620 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
621 | |||
622 | return status; | ||
623 | } | ||
624 | |||
625 | int | ||
626 | privateer_process_logout_frame(struct el_common *mchk_header, int print) | ||
627 | { | ||
628 | struct el_common_EV6_mcheck *ev6mchk = | ||
629 | (struct el_common_EV6_mcheck *)mchk_header; | ||
630 | int status = MCHK_DISPOSITION_UNKNOWN_ERROR; | ||
631 | |||
632 | /* | ||
633 | * Machine check codes | ||
634 | */ | ||
635 | #define PRIVATEER_MCHK__CORR_ECC 0x86 /* 630 */ | ||
636 | #define PRIVATEER_MCHK__DC_TAG_PERR 0x9E /* 630 */ | ||
637 | #define PRIVATEER_MCHK__PAL_BUGCHECK 0x8E /* 670 */ | ||
638 | #define PRIVATEER_MCHK__OS_BUGCHECK 0x90 /* 670 */ | ||
639 | #define PRIVATEER_MCHK__PROC_HRD_ERR 0x98 /* 670 */ | ||
640 | #define PRIVATEER_MCHK__ISTREAM_CMOV_PRX 0xA0 /* 670 */ | ||
641 | #define PRIVATEER_MCHK__ISTREAM_CMOV_FLT 0xA2 /* 670 */ | ||
642 | #define PRIVATEER_MCHK__SYS_HRD_ERR 0x202 /* 660 */ | ||
643 | #define PRIVATEER_MCHK__SYS_CORR_ERR 0x204 /* 620 */ | ||
644 | #define PRIVATEER_MCHK__SYS_ENVIRON 0x206 /* 680 */ | ||
645 | |||
646 | switch(ev6mchk->MCHK_Code) { | ||
647 | /* | ||
648 | * Vector 630 - Processor, Correctable | ||
649 | */ | ||
650 | case PRIVATEER_MCHK__CORR_ECC: | ||
651 | case PRIVATEER_MCHK__DC_TAG_PERR: | ||
652 | /* | ||
653 | * Fall through to vector 670 for processing... | ||
654 | */ | ||
655 | /* | ||
656 | * Vector 670 - Processor, Uncorrectable | ||
657 | */ | ||
658 | case PRIVATEER_MCHK__PAL_BUGCHECK: | ||
659 | case PRIVATEER_MCHK__OS_BUGCHECK: | ||
660 | case PRIVATEER_MCHK__PROC_HRD_ERR: | ||
661 | case PRIVATEER_MCHK__ISTREAM_CMOV_PRX: | ||
662 | case PRIVATEER_MCHK__ISTREAM_CMOV_FLT: | ||
663 | status |= ev6_process_logout_frame(mchk_header, print); | ||
664 | break; | ||
665 | |||
666 | /* | ||
667 | * Vector 620 - System, Correctable | ||
668 | */ | ||
669 | case PRIVATEER_MCHK__SYS_CORR_ERR: | ||
670 | /* | ||
671 | * Fall through to vector 660 for processing... | ||
672 | */ | ||
673 | /* | ||
674 | * Vector 660 - System, Uncorrectable | ||
675 | */ | ||
676 | case PRIVATEER_MCHK__SYS_HRD_ERR: | ||
677 | status |= titan_process_logout_frame(mchk_header, print); | ||
678 | break; | ||
679 | |||
680 | /* | ||
681 | * Vector 680 - System, Environmental | ||
682 | */ | ||
683 | case PRIVATEER_MCHK__SYS_ENVIRON: /* System, Environmental */ | ||
684 | status |= privateer_process_680_frame(mchk_header, print); | ||
685 | break; | ||
686 | |||
687 | /* | ||
688 | * Unknown | ||
689 | */ | ||
690 | default: | ||
691 | status |= MCHK_DISPOSITION_REPORT; | ||
692 | if (print) { | ||
693 | printk("%s** Unknown Error, frame follows\n", | ||
694 | err_print_prefix); | ||
695 | mchk_dump_logout_frame(mchk_header); | ||
696 | } | ||
697 | |||
698 | } | ||
699 | |||
700 | return status; | ||
701 | } | ||
702 | |||
703 | void | ||
704 | privateer_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) | ||
705 | { | ||
706 | struct el_common *mchk_header = (struct el_common *)la_ptr; | ||
707 | struct el_TITAN_sysdata_mcheck *tmchk = | ||
708 | (struct el_TITAN_sysdata_mcheck *) | ||
709 | (la_ptr + mchk_header->sys_offset); | ||
710 | u64 irqmask; | ||
711 | char *saved_err_prefix = err_print_prefix; | ||
712 | |||
713 | #define PRIVATEER_680_INTERRUPT_MASK (0xE00UL) | ||
714 | #define PRIVATEER_HOTPLUG_INTERRUPT_MASK (0xE00UL) | ||
715 | |||
716 | /* | ||
717 | * Sync the processor. | ||
718 | */ | ||
719 | mb(); | ||
720 | draina(); | ||
721 | |||
722 | /* | ||
723 | * Only handle system events here. | ||
724 | */ | ||
725 | if (vector != SCB_Q_SYSEVENT) | ||
726 | return titan_machine_check(vector, la_ptr, regs); | ||
727 | |||
728 | /* | ||
729 | * Report the event - System Events should be reported even if no | ||
730 | * error is indicated since the event could indicate the return | ||
731 | * to normal status. | ||
732 | */ | ||
733 | err_print_prefix = KERN_CRIT; | ||
734 | printk("%s*System Event (Vector 0x%x) reported on CPU %d:\n", | ||
735 | err_print_prefix, | ||
736 | (unsigned int)vector, (int)smp_processor_id()); | ||
737 | privateer_process_680_frame(mchk_header, 1); | ||
738 | err_print_prefix = saved_err_prefix; | ||
739 | |||
740 | /* | ||
741 | * Convert any pending interrupts which report as 680 machine | ||
742 | * checks to interrupts. | ||
743 | */ | ||
744 | irqmask = tmchk->c_dirx & PRIVATEER_680_INTERRUPT_MASK; | ||
745 | |||
746 | /* | ||
747 | * Dispatch the interrupt(s). | ||
748 | */ | ||
749 | titan_dispatch_irqs(irqmask, regs); | ||
750 | |||
751 | /* | ||
752 | * Release the logout frame. | ||
753 | */ | ||
754 | wrmces(0x7); | ||
755 | mb(); | ||
756 | } | ||
diff --git a/arch/alpha/kernel/es1888.c b/arch/alpha/kernel/es1888.c new file mode 100644 index 000000000000..d584c85fea7a --- /dev/null +++ b/arch/alpha/kernel/es1888.c | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/es1888.c | ||
3 | * | ||
4 | * Init the built-in ES1888 sound chip (SB16 compatible) | ||
5 | */ | ||
6 | |||
7 | #include <linux/init.h> | ||
8 | #include <asm/io.h> | ||
9 | #include "proto.h" | ||
10 | |||
11 | void __init | ||
12 | es1888_init(void) | ||
13 | { | ||
14 | /* Sequence of IO reads to init the audio controller */ | ||
15 | inb(0x0229); | ||
16 | inb(0x0229); | ||
17 | inb(0x0229); | ||
18 | inb(0x022b); | ||
19 | inb(0x0229); | ||
20 | inb(0x022b); | ||
21 | inb(0x0229); | ||
22 | inb(0x0229); | ||
23 | inb(0x022b); | ||
24 | inb(0x0229); | ||
25 | inb(0x0220); /* This sets the base address to 0x220 */ | ||
26 | |||
27 | /* Sequence to set DMA channels */ | ||
28 | outb(0x01, 0x0226); /* reset */ | ||
29 | inb(0x0226); /* pause */ | ||
30 | outb(0x00, 0x0226); /* release reset */ | ||
31 | while (!(inb(0x022e) & 0x80)) /* wait for bit 7 to assert*/ | ||
32 | continue; | ||
33 | inb(0x022a); /* pause */ | ||
34 | outb(0xc6, 0x022c); /* enable extended mode */ | ||
35 | inb(0x022a); /* pause, also forces the write */ | ||
36 | while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ | ||
37 | continue; | ||
38 | outb(0xb1, 0x022c); /* setup for write to Interrupt CR */ | ||
39 | while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ | ||
40 | continue; | ||
41 | outb(0x14, 0x022c); /* set IRQ 5 */ | ||
42 | while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ | ||
43 | continue; | ||
44 | outb(0xb2, 0x022c); /* setup for write to DMA CR */ | ||
45 | while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ | ||
46 | continue; | ||
47 | outb(0x18, 0x022c); /* set DMA channel 1 */ | ||
48 | inb(0x022c); /* force the write */ | ||
49 | } | ||
diff --git a/arch/alpha/kernel/gct.c b/arch/alpha/kernel/gct.c new file mode 100644 index 000000000000..8827687b9f89 --- /dev/null +++ b/arch/alpha/kernel/gct.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/gct.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/types.h> | ||
8 | #include <linux/errno.h> | ||
9 | |||
10 | #include <asm/hwrpb.h> | ||
11 | #include <asm/gct.h> | ||
12 | |||
13 | int | ||
14 | gct6_find_nodes(gct6_node *node, gct6_search_struct *search) | ||
15 | { | ||
16 | gct6_search_struct *wanted; | ||
17 | int status = 0; | ||
18 | |||
19 | /* First check the magic number. */ | ||
20 | if (node->magic != GCT_NODE_MAGIC) { | ||
21 | printk(KERN_ERR "GCT Node MAGIC incorrect - GCT invalid\n"); | ||
22 | return -EINVAL; | ||
23 | } | ||
24 | |||
25 | /* Check against the search struct. */ | ||
26 | for (wanted = search; | ||
27 | wanted && (wanted->type | wanted->subtype); | ||
28 | wanted++) { | ||
29 | if (node->type != wanted->type) | ||
30 | continue; | ||
31 | if (node->subtype != wanted->subtype) | ||
32 | continue; | ||
33 | |||
34 | /* Found it -- call out. */ | ||
35 | if (wanted->callout) | ||
36 | wanted->callout(node); | ||
37 | } | ||
38 | |||
39 | /* Now walk the tree, siblings first. */ | ||
40 | if (node->next) | ||
41 | status |= gct6_find_nodes(GCT_NODE_PTR(node->next), search); | ||
42 | |||
43 | /* Then the children. */ | ||
44 | if (node->child) | ||
45 | status |= gct6_find_nodes(GCT_NODE_PTR(node->child), search); | ||
46 | |||
47 | return status; | ||
48 | } | ||
diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S new file mode 100644 index 000000000000..4ca2e404708a --- /dev/null +++ b/arch/alpha/kernel/head.S | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * alpha/boot/head.S | ||
3 | * | ||
4 | * initial boot stuff.. At this point, the bootloader has already | ||
5 | * switched into OSF/1 PAL-code, and loaded us at the correct address | ||
6 | * (START_ADDR). So there isn't much left for us to do: just set up | ||
7 | * the kernel global pointer and jump to the kernel entry-point. | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <asm/system.h> | ||
12 | #include <asm/asm_offsets.h> | ||
13 | |||
14 | .globl swapper_pg_dir | ||
15 | .globl _stext | ||
16 | swapper_pg_dir=SWAPPER_PGD | ||
17 | |||
18 | .set noreorder | ||
19 | .globl __start | ||
20 | .ent __start | ||
21 | _stext: | ||
22 | __start: | ||
23 | .prologue 0 | ||
24 | br $27,1f | ||
25 | 1: ldgp $29,0($27) | ||
26 | /* We need to get current_task_info loaded up... */ | ||
27 | lda $8,init_thread_union | ||
28 | /* ... and find our stack ... */ | ||
29 | lda $30,0x4000 - SIZEOF_PT_REGS($8) | ||
30 | /* ... and then we can start the kernel. */ | ||
31 | jsr $26,start_kernel | ||
32 | call_pal PAL_halt | ||
33 | .end __start | ||
34 | |||
35 | #ifdef CONFIG_SMP | ||
36 | .align 3 | ||
37 | .globl __smp_callin | ||
38 | .ent __smp_callin | ||
39 | /* On entry here from SRM console, the HWPCB of the per-cpu | ||
40 | slot for this processor has been loaded. We've arranged | ||
41 | for the UNIQUE value for this process to contain the PCBB | ||
42 | of the target idle task. */ | ||
43 | __smp_callin: | ||
44 | .prologue 1 | ||
45 | ldgp $29,0($27) # First order of business, load the GP. | ||
46 | |||
47 | call_pal PAL_rduniq # Grab the target PCBB. | ||
48 | mov $0,$16 # Install it. | ||
49 | call_pal PAL_swpctx | ||
50 | |||
51 | lda $8,0x3fff # Find "current". | ||
52 | bic $30,$8,$8 | ||
53 | |||
54 | jsr $26,smp_callin | ||
55 | call_pal PAL_halt | ||
56 | .end __smp_callin | ||
57 | #endif /* CONFIG_SMP */ | ||
58 | |||
59 | # | ||
60 | # The following two functions are needed for supporting SRM PALcode | ||
61 | # on the PC164 (at least), since that PALcode manages the interrupt | ||
62 | # masking, and we cannot duplicate the effort without causing problems | ||
63 | # | ||
64 | |||
65 | .align 3 | ||
66 | .globl cserve_ena | ||
67 | .ent cserve_ena | ||
68 | cserve_ena: | ||
69 | .prologue 0 | ||
70 | bis $16,$16,$17 | ||
71 | lda $16,52($31) | ||
72 | call_pal PAL_cserve | ||
73 | ret ($26) | ||
74 | .end cserve_ena | ||
75 | |||
76 | .align 3 | ||
77 | .globl cserve_dis | ||
78 | .ent cserve_dis | ||
79 | cserve_dis: | ||
80 | .prologue 0 | ||
81 | bis $16,$16,$17 | ||
82 | lda $16,53($31) | ||
83 | call_pal PAL_cserve | ||
84 | ret ($26) | ||
85 | .end cserve_dis | ||
86 | |||
87 | # | ||
88 | # It is handy, on occasion, to make halt actually just loop. | ||
89 | # Putting it here means we dont have to recompile the whole | ||
90 | # kernel. | ||
91 | # | ||
92 | |||
93 | .align 3 | ||
94 | .globl halt | ||
95 | .ent halt | ||
96 | halt: | ||
97 | .prologue 0 | ||
98 | call_pal PAL_halt | ||
99 | .end halt | ||
diff --git a/arch/alpha/kernel/init_task.c b/arch/alpha/kernel/init_task.c new file mode 100644 index 000000000000..835d09a7b332 --- /dev/null +++ b/arch/alpha/kernel/init_task.c | |||
@@ -0,0 +1,23 @@ | |||
1 | #include <linux/mm.h> | ||
2 | #include <linux/module.h> | ||
3 | #include <linux/sched.h> | ||
4 | #include <linux/init.h> | ||
5 | #include <linux/init_task.h> | ||
6 | #include <linux/fs.h> | ||
7 | #include <linux/mqueue.h> | ||
8 | #include <asm/uaccess.h> | ||
9 | |||
10 | |||
11 | static struct fs_struct init_fs = INIT_FS; | ||
12 | static struct files_struct init_files = INIT_FILES; | ||
13 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
14 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
15 | struct mm_struct init_mm = INIT_MM(init_mm); | ||
16 | struct task_struct init_task = INIT_TASK(init_task); | ||
17 | |||
18 | EXPORT_SYMBOL(init_mm); | ||
19 | EXPORT_SYMBOL(init_task); | ||
20 | |||
21 | union thread_union init_thread_union | ||
22 | __attribute__((section(".data.init_thread"))) | ||
23 | = { INIT_THREAD_INFO(init_task) }; | ||
diff --git a/arch/alpha/kernel/io.c b/arch/alpha/kernel/io.c new file mode 100644 index 000000000000..19c5875ab398 --- /dev/null +++ b/arch/alpha/kernel/io.c | |||
@@ -0,0 +1,630 @@ | |||
1 | /* | ||
2 | * Alpha IO and memory functions. | ||
3 | */ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/types.h> | ||
7 | #include <linux/string.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <asm/io.h> | ||
10 | |||
11 | /* Out-of-line versions of the i/o routines that redirect into the | ||
12 | platform-specific version. Note that "platform-specific" may mean | ||
13 | "generic", which bumps through the machine vector. */ | ||
14 | |||
15 | unsigned int | ||
16 | ioread8(void __iomem *addr) | ||
17 | { | ||
18 | unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread8)(addr); | ||
19 | mb(); | ||
20 | return ret; | ||
21 | } | ||
22 | |||
23 | unsigned int ioread16(void __iomem *addr) | ||
24 | { | ||
25 | unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread16)(addr); | ||
26 | mb(); | ||
27 | return ret; | ||
28 | } | ||
29 | |||
30 | unsigned int ioread32(void __iomem *addr) | ||
31 | { | ||
32 | unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread32)(addr); | ||
33 | mb(); | ||
34 | return ret; | ||
35 | } | ||
36 | |||
37 | void iowrite8(u8 b, void __iomem *addr) | ||
38 | { | ||
39 | IO_CONCAT(__IO_PREFIX,iowrite8)(b, addr); | ||
40 | mb(); | ||
41 | } | ||
42 | |||
43 | void iowrite16(u16 b, void __iomem *addr) | ||
44 | { | ||
45 | IO_CONCAT(__IO_PREFIX,iowrite16)(b, addr); | ||
46 | mb(); | ||
47 | } | ||
48 | |||
49 | void iowrite32(u32 b, void __iomem *addr) | ||
50 | { | ||
51 | IO_CONCAT(__IO_PREFIX,iowrite32)(b, addr); | ||
52 | mb(); | ||
53 | } | ||
54 | |||
55 | EXPORT_SYMBOL(ioread8); | ||
56 | EXPORT_SYMBOL(ioread16); | ||
57 | EXPORT_SYMBOL(ioread32); | ||
58 | EXPORT_SYMBOL(iowrite8); | ||
59 | EXPORT_SYMBOL(iowrite16); | ||
60 | EXPORT_SYMBOL(iowrite32); | ||
61 | |||
62 | u8 inb(unsigned long port) | ||
63 | { | ||
64 | return ioread8(ioport_map(port, 1)); | ||
65 | } | ||
66 | |||
67 | u16 inw(unsigned long port) | ||
68 | { | ||
69 | return ioread16(ioport_map(port, 2)); | ||
70 | } | ||
71 | |||
72 | u32 inl(unsigned long port) | ||
73 | { | ||
74 | return ioread32(ioport_map(port, 4)); | ||
75 | } | ||
76 | |||
77 | void outb(u8 b, unsigned long port) | ||
78 | { | ||
79 | iowrite8(b, ioport_map(port, 1)); | ||
80 | } | ||
81 | |||
82 | void outw(u16 b, unsigned long port) | ||
83 | { | ||
84 | iowrite16(b, ioport_map(port, 2)); | ||
85 | } | ||
86 | |||
87 | void outl(u32 b, unsigned long port) | ||
88 | { | ||
89 | iowrite32(b, ioport_map(port, 4)); | ||
90 | } | ||
91 | |||
92 | EXPORT_SYMBOL(inb); | ||
93 | EXPORT_SYMBOL(inw); | ||
94 | EXPORT_SYMBOL(inl); | ||
95 | EXPORT_SYMBOL(outb); | ||
96 | EXPORT_SYMBOL(outw); | ||
97 | EXPORT_SYMBOL(outl); | ||
98 | |||
99 | u8 __raw_readb(const volatile void __iomem *addr) | ||
100 | { | ||
101 | return IO_CONCAT(__IO_PREFIX,readb)(addr); | ||
102 | } | ||
103 | |||
104 | u16 __raw_readw(const volatile void __iomem *addr) | ||
105 | { | ||
106 | return IO_CONCAT(__IO_PREFIX,readw)(addr); | ||
107 | } | ||
108 | |||
109 | u32 __raw_readl(const volatile void __iomem *addr) | ||
110 | { | ||
111 | return IO_CONCAT(__IO_PREFIX,readl)(addr); | ||
112 | } | ||
113 | |||
114 | u64 __raw_readq(const volatile void __iomem *addr) | ||
115 | { | ||
116 | return IO_CONCAT(__IO_PREFIX,readq)(addr); | ||
117 | } | ||
118 | |||
119 | void __raw_writeb(u8 b, volatile void __iomem *addr) | ||
120 | { | ||
121 | IO_CONCAT(__IO_PREFIX,writeb)(b, addr); | ||
122 | } | ||
123 | |||
124 | void __raw_writew(u16 b, volatile void __iomem *addr) | ||
125 | { | ||
126 | IO_CONCAT(__IO_PREFIX,writew)(b, addr); | ||
127 | } | ||
128 | |||
129 | void __raw_writel(u32 b, volatile void __iomem *addr) | ||
130 | { | ||
131 | IO_CONCAT(__IO_PREFIX,writel)(b, addr); | ||
132 | } | ||
133 | |||
134 | void __raw_writeq(u64 b, volatile void __iomem *addr) | ||
135 | { | ||
136 | IO_CONCAT(__IO_PREFIX,writeq)(b, addr); | ||
137 | } | ||
138 | |||
139 | EXPORT_SYMBOL(__raw_readb); | ||
140 | EXPORT_SYMBOL(__raw_readw); | ||
141 | EXPORT_SYMBOL(__raw_readl); | ||
142 | EXPORT_SYMBOL(__raw_readq); | ||
143 | EXPORT_SYMBOL(__raw_writeb); | ||
144 | EXPORT_SYMBOL(__raw_writew); | ||
145 | EXPORT_SYMBOL(__raw_writel); | ||
146 | EXPORT_SYMBOL(__raw_writeq); | ||
147 | |||
148 | u8 readb(const volatile void __iomem *addr) | ||
149 | { | ||
150 | u8 ret = __raw_readb(addr); | ||
151 | mb(); | ||
152 | return ret; | ||
153 | } | ||
154 | |||
155 | u16 readw(const volatile void __iomem *addr) | ||
156 | { | ||
157 | u16 ret = __raw_readw(addr); | ||
158 | mb(); | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | u32 readl(const volatile void __iomem *addr) | ||
163 | { | ||
164 | u32 ret = __raw_readl(addr); | ||
165 | mb(); | ||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | u64 readq(const volatile void __iomem *addr) | ||
170 | { | ||
171 | u64 ret = __raw_readq(addr); | ||
172 | mb(); | ||
173 | return ret; | ||
174 | } | ||
175 | |||
176 | void writeb(u8 b, volatile void __iomem *addr) | ||
177 | { | ||
178 | __raw_writeb(b, addr); | ||
179 | mb(); | ||
180 | } | ||
181 | |||
182 | void writew(u16 b, volatile void __iomem *addr) | ||
183 | { | ||
184 | __raw_writew(b, addr); | ||
185 | mb(); | ||
186 | } | ||
187 | |||
188 | void writel(u32 b, volatile void __iomem *addr) | ||
189 | { | ||
190 | __raw_writel(b, addr); | ||
191 | mb(); | ||
192 | } | ||
193 | |||
194 | void writeq(u64 b, volatile void __iomem *addr) | ||
195 | { | ||
196 | __raw_writeq(b, addr); | ||
197 | mb(); | ||
198 | } | ||
199 | |||
200 | EXPORT_SYMBOL(readb); | ||
201 | EXPORT_SYMBOL(readw); | ||
202 | EXPORT_SYMBOL(readl); | ||
203 | EXPORT_SYMBOL(readq); | ||
204 | EXPORT_SYMBOL(writeb); | ||
205 | EXPORT_SYMBOL(writew); | ||
206 | EXPORT_SYMBOL(writel); | ||
207 | EXPORT_SYMBOL(writeq); | ||
208 | |||
209 | |||
210 | /* | ||
211 | * Read COUNT 8-bit bytes from port PORT into memory starting at SRC. | ||
212 | */ | ||
213 | void ioread8_rep(void __iomem *port, void *dst, unsigned long count) | ||
214 | { | ||
215 | while ((unsigned long)dst & 0x3) { | ||
216 | if (!count) | ||
217 | return; | ||
218 | count--; | ||
219 | *(unsigned char *)dst = ioread8(port); | ||
220 | dst += 1; | ||
221 | } | ||
222 | |||
223 | while (count >= 4) { | ||
224 | unsigned int w; | ||
225 | count -= 4; | ||
226 | w = ioread8(port); | ||
227 | w |= ioread8(port) << 8; | ||
228 | w |= ioread8(port) << 16; | ||
229 | w |= ioread8(port) << 24; | ||
230 | *(unsigned int *)dst = w; | ||
231 | dst += 4; | ||
232 | } | ||
233 | |||
234 | while (count) { | ||
235 | --count; | ||
236 | *(unsigned char *)dst = ioread8(port); | ||
237 | dst += 1; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | void insb(unsigned long port, void *dst, unsigned long count) | ||
242 | { | ||
243 | ioread8_rep(ioport_map(port, 1), dst, count); | ||
244 | } | ||
245 | |||
246 | EXPORT_SYMBOL(ioread8_rep); | ||
247 | EXPORT_SYMBOL(insb); | ||
248 | |||
249 | /* | ||
250 | * Read COUNT 16-bit words from port PORT into memory starting at | ||
251 | * SRC. SRC must be at least short aligned. This is used by the | ||
252 | * IDE driver to read disk sectors. Performance is important, but | ||
253 | * the interfaces seems to be slow: just using the inlined version | ||
254 | * of the inw() breaks things. | ||
255 | */ | ||
256 | void ioread16_rep(void __iomem *port, void *dst, unsigned long count) | ||
257 | { | ||
258 | if (unlikely((unsigned long)dst & 0x3)) { | ||
259 | if (!count) | ||
260 | return; | ||
261 | BUG_ON((unsigned long)dst & 0x1); | ||
262 | count--; | ||
263 | *(unsigned short *)dst = ioread16(port); | ||
264 | dst += 2; | ||
265 | } | ||
266 | |||
267 | while (count >= 2) { | ||
268 | unsigned int w; | ||
269 | count -= 2; | ||
270 | w = ioread16(port); | ||
271 | w |= ioread16(port) << 16; | ||
272 | *(unsigned int *)dst = w; | ||
273 | dst += 4; | ||
274 | } | ||
275 | |||
276 | if (count) { | ||
277 | *(unsigned short*)dst = ioread16(port); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | void insw(unsigned long port, void *dst, unsigned long count) | ||
282 | { | ||
283 | ioread16_rep(ioport_map(port, 2), dst, count); | ||
284 | } | ||
285 | |||
286 | EXPORT_SYMBOL(ioread16_rep); | ||
287 | EXPORT_SYMBOL(insw); | ||
288 | |||
289 | |||
290 | /* | ||
291 | * Read COUNT 32-bit words from port PORT into memory starting at | ||
292 | * SRC. Now works with any alignment in SRC. Performance is important, | ||
293 | * but the interfaces seems to be slow: just using the inlined version | ||
294 | * of the inl() breaks things. | ||
295 | */ | ||
296 | void ioread32_rep(void __iomem *port, void *dst, unsigned long count) | ||
297 | { | ||
298 | if (unlikely((unsigned long)dst & 0x3)) { | ||
299 | while (count--) { | ||
300 | struct S { int x __attribute__((packed)); }; | ||
301 | ((struct S *)dst)->x = ioread32(port); | ||
302 | dst += 4; | ||
303 | } | ||
304 | } else { | ||
305 | /* Buffer 32-bit aligned. */ | ||
306 | while (count--) { | ||
307 | *(unsigned int *)dst = ioread32(port); | ||
308 | dst += 4; | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | |||
313 | void insl(unsigned long port, void *dst, unsigned long count) | ||
314 | { | ||
315 | ioread32_rep(ioport_map(port, 4), dst, count); | ||
316 | } | ||
317 | |||
318 | EXPORT_SYMBOL(ioread32_rep); | ||
319 | EXPORT_SYMBOL(insl); | ||
320 | |||
321 | |||
322 | /* | ||
323 | * Like insb but in the opposite direction. | ||
324 | * Don't worry as much about doing aligned memory transfers: | ||
325 | * doing byte reads the "slow" way isn't nearly as slow as | ||
326 | * doing byte writes the slow way (no r-m-w cycle). | ||
327 | */ | ||
328 | void iowrite8_rep(void __iomem *port, const void *xsrc, unsigned long count) | ||
329 | { | ||
330 | const unsigned char *src = xsrc; | ||
331 | while (count--) | ||
332 | iowrite8(*src++, port); | ||
333 | } | ||
334 | |||
335 | void outsb(unsigned long port, const void *src, unsigned long count) | ||
336 | { | ||
337 | iowrite8_rep(ioport_map(port, 1), src, count); | ||
338 | } | ||
339 | |||
340 | EXPORT_SYMBOL(iowrite8_rep); | ||
341 | EXPORT_SYMBOL(outsb); | ||
342 | |||
343 | |||
344 | /* | ||
345 | * Like insw but in the opposite direction. This is used by the IDE | ||
346 | * driver to write disk sectors. Performance is important, but the | ||
347 | * interfaces seems to be slow: just using the inlined version of the | ||
348 | * outw() breaks things. | ||
349 | */ | ||
350 | void iowrite16_rep(void __iomem *port, const void *src, unsigned long count) | ||
351 | { | ||
352 | if (unlikely((unsigned long)src & 0x3)) { | ||
353 | if (!count) | ||
354 | return; | ||
355 | BUG_ON((unsigned long)src & 0x1); | ||
356 | iowrite16(*(unsigned short *)src, port); | ||
357 | src += 2; | ||
358 | --count; | ||
359 | } | ||
360 | |||
361 | while (count >= 2) { | ||
362 | unsigned int w; | ||
363 | count -= 2; | ||
364 | w = *(unsigned int *)src; | ||
365 | src += 4; | ||
366 | iowrite16(w >> 0, port); | ||
367 | iowrite16(w >> 16, port); | ||
368 | } | ||
369 | |||
370 | if (count) { | ||
371 | iowrite16(*(unsigned short *)src, port); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | void outsw(unsigned long port, const void *src, unsigned long count) | ||
376 | { | ||
377 | iowrite16_rep(ioport_map(port, 2), src, count); | ||
378 | } | ||
379 | |||
380 | EXPORT_SYMBOL(iowrite16_rep); | ||
381 | EXPORT_SYMBOL(outsw); | ||
382 | |||
383 | |||
384 | /* | ||
385 | * Like insl but in the opposite direction. This is used by the IDE | ||
386 | * driver to write disk sectors. Works with any alignment in SRC. | ||
387 | * Performance is important, but the interfaces seems to be slow: | ||
388 | * just using the inlined version of the outl() breaks things. | ||
389 | */ | ||
390 | void iowrite32_rep(void __iomem *port, const void *src, unsigned long count) | ||
391 | { | ||
392 | if (unlikely((unsigned long)src & 0x3)) { | ||
393 | while (count--) { | ||
394 | struct S { int x __attribute__((packed)); }; | ||
395 | iowrite32(((struct S *)src)->x, port); | ||
396 | src += 4; | ||
397 | } | ||
398 | } else { | ||
399 | /* Buffer 32-bit aligned. */ | ||
400 | while (count--) { | ||
401 | iowrite32(*(unsigned int *)src, port); | ||
402 | src += 4; | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | |||
407 | void outsl(unsigned long port, const void *src, unsigned long count) | ||
408 | { | ||
409 | iowrite32_rep(ioport_map(port, 4), src, count); | ||
410 | } | ||
411 | |||
412 | EXPORT_SYMBOL(iowrite32_rep); | ||
413 | EXPORT_SYMBOL(outsl); | ||
414 | |||
415 | |||
416 | /* | ||
417 | * Copy data from IO memory space to "real" memory space. | ||
418 | * This needs to be optimized. | ||
419 | */ | ||
420 | void memcpy_fromio(void *to, const volatile void __iomem *from, long count) | ||
421 | { | ||
422 | /* Optimize co-aligned transfers. Everything else gets handled | ||
423 | a byte at a time. */ | ||
424 | |||
425 | if (count >= 8 && ((u64)to & 7) == ((u64)from & 7)) { | ||
426 | count -= 8; | ||
427 | do { | ||
428 | *(u64 *)to = __raw_readq(from); | ||
429 | count -= 8; | ||
430 | to += 8; | ||
431 | from += 8; | ||
432 | } while (count >= 0); | ||
433 | count += 8; | ||
434 | } | ||
435 | |||
436 | if (count >= 4 && ((u64)to & 3) == ((u64)from & 3)) { | ||
437 | count -= 4; | ||
438 | do { | ||
439 | *(u32 *)to = __raw_readl(from); | ||
440 | count -= 4; | ||
441 | to += 4; | ||
442 | from += 4; | ||
443 | } while (count >= 0); | ||
444 | count += 4; | ||
445 | } | ||
446 | |||
447 | if (count >= 2 && ((u64)to & 1) == ((u64)from & 1)) { | ||
448 | count -= 2; | ||
449 | do { | ||
450 | *(u16 *)to = __raw_readw(from); | ||
451 | count -= 2; | ||
452 | to += 2; | ||
453 | from += 2; | ||
454 | } while (count >= 0); | ||
455 | count += 2; | ||
456 | } | ||
457 | |||
458 | while (count > 0) { | ||
459 | *(u8 *) to = __raw_readb(from); | ||
460 | count--; | ||
461 | to++; | ||
462 | from++; | ||
463 | } | ||
464 | mb(); | ||
465 | } | ||
466 | |||
467 | EXPORT_SYMBOL(memcpy_fromio); | ||
468 | |||
469 | |||
470 | /* | ||
471 | * Copy data from "real" memory space to IO memory space. | ||
472 | * This needs to be optimized. | ||
473 | */ | ||
474 | void memcpy_toio(volatile void __iomem *to, const void *from, long count) | ||
475 | { | ||
476 | /* Optimize co-aligned transfers. Everything else gets handled | ||
477 | a byte at a time. */ | ||
478 | /* FIXME -- align FROM. */ | ||
479 | |||
480 | if (count >= 8 && ((u64)to & 7) == ((u64)from & 7)) { | ||
481 | count -= 8; | ||
482 | do { | ||
483 | __raw_writeq(*(const u64 *)from, to); | ||
484 | count -= 8; | ||
485 | to += 8; | ||
486 | from += 8; | ||
487 | } while (count >= 0); | ||
488 | count += 8; | ||
489 | } | ||
490 | |||
491 | if (count >= 4 && ((u64)to & 3) == ((u64)from & 3)) { | ||
492 | count -= 4; | ||
493 | do { | ||
494 | __raw_writel(*(const u32 *)from, to); | ||
495 | count -= 4; | ||
496 | to += 4; | ||
497 | from += 4; | ||
498 | } while (count >= 0); | ||
499 | count += 4; | ||
500 | } | ||
501 | |||
502 | if (count >= 2 && ((u64)to & 1) == ((u64)from & 1)) { | ||
503 | count -= 2; | ||
504 | do { | ||
505 | __raw_writew(*(const u16 *)from, to); | ||
506 | count -= 2; | ||
507 | to += 2; | ||
508 | from += 2; | ||
509 | } while (count >= 0); | ||
510 | count += 2; | ||
511 | } | ||
512 | |||
513 | while (count > 0) { | ||
514 | __raw_writeb(*(const u8 *) from, to); | ||
515 | count--; | ||
516 | to++; | ||
517 | from++; | ||
518 | } | ||
519 | mb(); | ||
520 | } | ||
521 | |||
522 | EXPORT_SYMBOL(memcpy_toio); | ||
523 | |||
524 | |||
525 | /* | ||
526 | * "memset" on IO memory space. | ||
527 | */ | ||
528 | void _memset_c_io(volatile void __iomem *to, unsigned long c, long count) | ||
529 | { | ||
530 | /* Handle any initial odd byte */ | ||
531 | if (count > 0 && ((u64)to & 1)) { | ||
532 | __raw_writeb(c, to); | ||
533 | to++; | ||
534 | count--; | ||
535 | } | ||
536 | |||
537 | /* Handle any initial odd halfword */ | ||
538 | if (count >= 2 && ((u64)to & 2)) { | ||
539 | __raw_writew(c, to); | ||
540 | to += 2; | ||
541 | count -= 2; | ||
542 | } | ||
543 | |||
544 | /* Handle any initial odd word */ | ||
545 | if (count >= 4 && ((u64)to & 4)) { | ||
546 | __raw_writel(c, to); | ||
547 | to += 4; | ||
548 | count -= 4; | ||
549 | } | ||
550 | |||
551 | /* Handle all full-sized quadwords: we're aligned | ||
552 | (or have a small count) */ | ||
553 | count -= 8; | ||
554 | if (count >= 0) { | ||
555 | do { | ||
556 | __raw_writeq(c, to); | ||
557 | to += 8; | ||
558 | count -= 8; | ||
559 | } while (count >= 0); | ||
560 | } | ||
561 | count += 8; | ||
562 | |||
563 | /* The tail is word-aligned if we still have count >= 4 */ | ||
564 | if (count >= 4) { | ||
565 | __raw_writel(c, to); | ||
566 | to += 4; | ||
567 | count -= 4; | ||
568 | } | ||
569 | |||
570 | /* The tail is half-word aligned if we have count >= 2 */ | ||
571 | if (count >= 2) { | ||
572 | __raw_writew(c, to); | ||
573 | to += 2; | ||
574 | count -= 2; | ||
575 | } | ||
576 | |||
577 | /* And finally, one last byte.. */ | ||
578 | if (count) { | ||
579 | __raw_writeb(c, to); | ||
580 | } | ||
581 | mb(); | ||
582 | } | ||
583 | |||
584 | EXPORT_SYMBOL(_memset_c_io); | ||
585 | |||
586 | /* A version of memcpy used by the vga console routines to move data around | ||
587 | arbitrarily between screen and main memory. */ | ||
588 | |||
589 | void | ||
590 | scr_memcpyw(u16 *d, const u16 *s, unsigned int count) | ||
591 | { | ||
592 | const u16 __iomem *ios = (const u16 __iomem *) s; | ||
593 | u16 __iomem *iod = (u16 __iomem *) d; | ||
594 | int s_isio = __is_ioaddr(s); | ||
595 | int d_isio = __is_ioaddr(d); | ||
596 | |||
597 | if (s_isio) { | ||
598 | if (d_isio) { | ||
599 | /* FIXME: Should handle unaligned ops and | ||
600 | operation widening. */ | ||
601 | |||
602 | count /= 2; | ||
603 | while (count--) { | ||
604 | u16 tmp = __raw_readw(ios++); | ||
605 | __raw_writew(tmp, iod++); | ||
606 | } | ||
607 | } | ||
608 | else | ||
609 | memcpy_fromio(d, ios, count); | ||
610 | } else { | ||
611 | if (d_isio) | ||
612 | memcpy_toio(iod, s, count); | ||
613 | else | ||
614 | memcpy(d, s, count); | ||
615 | } | ||
616 | } | ||
617 | |||
618 | EXPORT_SYMBOL(scr_memcpyw); | ||
619 | |||
620 | void __iomem *ioport_map(unsigned long port, unsigned int size) | ||
621 | { | ||
622 | return IO_CONCAT(__IO_PREFIX,ioportmap) (port); | ||
623 | } | ||
624 | |||
625 | void ioport_unmap(void __iomem *addr) | ||
626 | { | ||
627 | } | ||
628 | |||
629 | EXPORT_SYMBOL(ioport_map); | ||
630 | EXPORT_SYMBOL(ioport_unmap); | ||
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c new file mode 100644 index 000000000000..b6114f5c0d2b --- /dev/null +++ b/arch/alpha/kernel/irq.c | |||
@@ -0,0 +1,774 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/irq.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * | ||
6 | * This file contains the code used by various IRQ handling routines: | ||
7 | * asking for different IRQ's should be done through these routines | ||
8 | * instead of just grabbing them. Thus setups with different IRQ numbers | ||
9 | * shouldn't result in any weird surprises, and installing new handlers | ||
10 | * should be easier. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/kernel_stat.h> | ||
18 | #include <linux/signal.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/ptrace.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/random.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/proc_fs.h> | ||
27 | #include <linux/seq_file.h> | ||
28 | #include <linux/profile.h> | ||
29 | #include <linux/bitops.h> | ||
30 | |||
31 | #include <asm/system.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/uaccess.h> | ||
34 | |||
35 | /* | ||
36 | * Controller mappings for all interrupt sources: | ||
37 | */ | ||
38 | irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { | ||
39 | [0 ... NR_IRQS-1] = { | ||
40 | .handler = &no_irq_type, | ||
41 | .lock = SPIN_LOCK_UNLOCKED | ||
42 | } | ||
43 | }; | ||
44 | |||
45 | static void register_irq_proc(unsigned int irq); | ||
46 | |||
47 | volatile unsigned long irq_err_count; | ||
48 | |||
49 | /* | ||
50 | * Special irq handlers. | ||
51 | */ | ||
52 | |||
53 | irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) | ||
54 | { | ||
55 | return IRQ_NONE; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * Generic no controller code | ||
60 | */ | ||
61 | |||
62 | static void no_irq_enable_disable(unsigned int irq) { } | ||
63 | static unsigned int no_irq_startup(unsigned int irq) { return 0; } | ||
64 | |||
65 | static void | ||
66 | no_irq_ack(unsigned int irq) | ||
67 | { | ||
68 | irq_err_count++; | ||
69 | printk(KERN_CRIT "Unexpected IRQ trap at vector %u\n", irq); | ||
70 | } | ||
71 | |||
72 | struct hw_interrupt_type no_irq_type = { | ||
73 | .typename = "none", | ||
74 | .startup = no_irq_startup, | ||
75 | .shutdown = no_irq_enable_disable, | ||
76 | .enable = no_irq_enable_disable, | ||
77 | .disable = no_irq_enable_disable, | ||
78 | .ack = no_irq_ack, | ||
79 | .end = no_irq_enable_disable, | ||
80 | }; | ||
81 | |||
82 | int | ||
83 | handle_IRQ_event(unsigned int irq, struct pt_regs *regs, | ||
84 | struct irqaction *action) | ||
85 | { | ||
86 | int status = 1; /* Force the "do bottom halves" bit */ | ||
87 | int ret; | ||
88 | |||
89 | do { | ||
90 | if (!(action->flags & SA_INTERRUPT)) | ||
91 | local_irq_enable(); | ||
92 | else | ||
93 | local_irq_disable(); | ||
94 | |||
95 | ret = action->handler(irq, action->dev_id, regs); | ||
96 | if (ret == IRQ_HANDLED) | ||
97 | status |= action->flags; | ||
98 | action = action->next; | ||
99 | } while (action); | ||
100 | if (status & SA_SAMPLE_RANDOM) | ||
101 | add_interrupt_randomness(irq); | ||
102 | local_irq_disable(); | ||
103 | |||
104 | return status; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Generic enable/disable code: this just calls | ||
109 | * down into the PIC-specific version for the actual | ||
110 | * hardware disable after having gotten the irq | ||
111 | * controller lock. | ||
112 | */ | ||
113 | void inline | ||
114 | disable_irq_nosync(unsigned int irq) | ||
115 | { | ||
116 | irq_desc_t *desc = irq_desc + irq; | ||
117 | unsigned long flags; | ||
118 | |||
119 | spin_lock_irqsave(&desc->lock, flags); | ||
120 | if (!desc->depth++) { | ||
121 | desc->status |= IRQ_DISABLED; | ||
122 | desc->handler->disable(irq); | ||
123 | } | ||
124 | spin_unlock_irqrestore(&desc->lock, flags); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Synchronous version of the above, making sure the IRQ is | ||
129 | * no longer running on any other IRQ.. | ||
130 | */ | ||
131 | void | ||
132 | disable_irq(unsigned int irq) | ||
133 | { | ||
134 | disable_irq_nosync(irq); | ||
135 | synchronize_irq(irq); | ||
136 | } | ||
137 | |||
138 | void | ||
139 | enable_irq(unsigned int irq) | ||
140 | { | ||
141 | irq_desc_t *desc = irq_desc + irq; | ||
142 | unsigned long flags; | ||
143 | |||
144 | spin_lock_irqsave(&desc->lock, flags); | ||
145 | switch (desc->depth) { | ||
146 | case 1: { | ||
147 | unsigned int status = desc->status & ~IRQ_DISABLED; | ||
148 | desc->status = status; | ||
149 | if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { | ||
150 | desc->status = status | IRQ_REPLAY; | ||
151 | hw_resend_irq(desc->handler,irq); | ||
152 | } | ||
153 | desc->handler->enable(irq); | ||
154 | /* fall-through */ | ||
155 | } | ||
156 | default: | ||
157 | desc->depth--; | ||
158 | break; | ||
159 | case 0: | ||
160 | printk(KERN_ERR "enable_irq() unbalanced from %p\n", | ||
161 | __builtin_return_address(0)); | ||
162 | } | ||
163 | spin_unlock_irqrestore(&desc->lock, flags); | ||
164 | } | ||
165 | |||
166 | int | ||
167 | setup_irq(unsigned int irq, struct irqaction * new) | ||
168 | { | ||
169 | int shared = 0; | ||
170 | struct irqaction *old, **p; | ||
171 | unsigned long flags; | ||
172 | irq_desc_t *desc = irq_desc + irq; | ||
173 | |||
174 | if (desc->handler == &no_irq_type) | ||
175 | return -ENOSYS; | ||
176 | |||
177 | /* | ||
178 | * Some drivers like serial.c use request_irq() heavily, | ||
179 | * so we have to be careful not to interfere with a | ||
180 | * running system. | ||
181 | */ | ||
182 | if (new->flags & SA_SAMPLE_RANDOM) { | ||
183 | /* | ||
184 | * This function might sleep, we want to call it first, | ||
185 | * outside of the atomic block. | ||
186 | * Yes, this might clear the entropy pool if the wrong | ||
187 | * driver is attempted to be loaded, without actually | ||
188 | * installing a new handler, but is this really a problem, | ||
189 | * only the sysadmin is able to do this. | ||
190 | */ | ||
191 | rand_initialize_irq(irq); | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * The following block of code has to be executed atomically | ||
196 | */ | ||
197 | spin_lock_irqsave(&desc->lock,flags); | ||
198 | p = &desc->action; | ||
199 | if ((old = *p) != NULL) { | ||
200 | /* Can't share interrupts unless both agree to */ | ||
201 | if (!(old->flags & new->flags & SA_SHIRQ)) { | ||
202 | spin_unlock_irqrestore(&desc->lock,flags); | ||
203 | return -EBUSY; | ||
204 | } | ||
205 | |||
206 | /* add new interrupt at end of irq queue */ | ||
207 | do { | ||
208 | p = &old->next; | ||
209 | old = *p; | ||
210 | } while (old); | ||
211 | shared = 1; | ||
212 | } | ||
213 | |||
214 | *p = new; | ||
215 | |||
216 | if (!shared) { | ||
217 | desc->depth = 0; | ||
218 | desc->status &= | ||
219 | ~(IRQ_DISABLED|IRQ_AUTODETECT|IRQ_WAITING|IRQ_INPROGRESS); | ||
220 | desc->handler->startup(irq); | ||
221 | } | ||
222 | spin_unlock_irqrestore(&desc->lock,flags); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static struct proc_dir_entry * root_irq_dir; | ||
228 | static struct proc_dir_entry * irq_dir[NR_IRQS]; | ||
229 | |||
230 | #ifdef CONFIG_SMP | ||
231 | static struct proc_dir_entry * smp_affinity_entry[NR_IRQS]; | ||
232 | static char irq_user_affinity[NR_IRQS]; | ||
233 | static cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL }; | ||
234 | |||
235 | static void | ||
236 | select_smp_affinity(int irq) | ||
237 | { | ||
238 | static int last_cpu; | ||
239 | int cpu = last_cpu + 1; | ||
240 | |||
241 | if (! irq_desc[irq].handler->set_affinity || irq_user_affinity[irq]) | ||
242 | return; | ||
243 | |||
244 | while (!cpu_possible(cpu)) | ||
245 | cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0); | ||
246 | last_cpu = cpu; | ||
247 | |||
248 | irq_affinity[irq] = cpumask_of_cpu(cpu); | ||
249 | irq_desc[irq].handler->set_affinity(irq, cpumask_of_cpu(cpu)); | ||
250 | } | ||
251 | |||
252 | static int | ||
253 | irq_affinity_read_proc (char *page, char **start, off_t off, | ||
254 | int count, int *eof, void *data) | ||
255 | { | ||
256 | int len = cpumask_scnprintf(page, count, irq_affinity[(long)data]); | ||
257 | if (count - len < 2) | ||
258 | return -EINVAL; | ||
259 | len += sprintf(page + len, "\n"); | ||
260 | return len; | ||
261 | } | ||
262 | |||
263 | static int | ||
264 | irq_affinity_write_proc(struct file *file, const char __user *buffer, | ||
265 | unsigned long count, void *data) | ||
266 | { | ||
267 | int irq = (long) data, full_count = count, err; | ||
268 | cpumask_t new_value; | ||
269 | |||
270 | if (!irq_desc[irq].handler->set_affinity) | ||
271 | return -EIO; | ||
272 | |||
273 | err = cpumask_parse(buffer, count, new_value); | ||
274 | |||
275 | /* The special value 0 means release control of the | ||
276 | affinity to kernel. */ | ||
277 | cpus_and(new_value, new_value, cpu_online_map); | ||
278 | if (cpus_empty(new_value)) { | ||
279 | irq_user_affinity[irq] = 0; | ||
280 | select_smp_affinity(irq); | ||
281 | } | ||
282 | /* Do not allow disabling IRQs completely - it's a too easy | ||
283 | way to make the system unusable accidentally :-) At least | ||
284 | one online CPU still has to be targeted. */ | ||
285 | else { | ||
286 | irq_affinity[irq] = new_value; | ||
287 | irq_user_affinity[irq] = 1; | ||
288 | irq_desc[irq].handler->set_affinity(irq, new_value); | ||
289 | } | ||
290 | |||
291 | return full_count; | ||
292 | } | ||
293 | |||
294 | #endif /* CONFIG_SMP */ | ||
295 | |||
296 | #define MAX_NAMELEN 10 | ||
297 | |||
298 | static void | ||
299 | register_irq_proc (unsigned int irq) | ||
300 | { | ||
301 | char name [MAX_NAMELEN]; | ||
302 | |||
303 | if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || | ||
304 | irq_dir[irq]) | ||
305 | return; | ||
306 | |||
307 | memset(name, 0, MAX_NAMELEN); | ||
308 | sprintf(name, "%d", irq); | ||
309 | |||
310 | /* create /proc/irq/1234 */ | ||
311 | irq_dir[irq] = proc_mkdir(name, root_irq_dir); | ||
312 | |||
313 | #ifdef CONFIG_SMP | ||
314 | if (irq_desc[irq].handler->set_affinity) { | ||
315 | struct proc_dir_entry *entry; | ||
316 | /* create /proc/irq/1234/smp_affinity */ | ||
317 | entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); | ||
318 | |||
319 | if (entry) { | ||
320 | entry->nlink = 1; | ||
321 | entry->data = (void *)(long)irq; | ||
322 | entry->read_proc = irq_affinity_read_proc; | ||
323 | entry->write_proc = irq_affinity_write_proc; | ||
324 | } | ||
325 | |||
326 | smp_affinity_entry[irq] = entry; | ||
327 | } | ||
328 | #endif | ||
329 | } | ||
330 | |||
331 | void | ||
332 | init_irq_proc (void) | ||
333 | { | ||
334 | int i; | ||
335 | |||
336 | /* create /proc/irq */ | ||
337 | root_irq_dir = proc_mkdir("irq", NULL); | ||
338 | |||
339 | #ifdef CONFIG_SMP | ||
340 | /* create /proc/irq/prof_cpu_mask */ | ||
341 | create_prof_cpu_mask(root_irq_dir); | ||
342 | #endif | ||
343 | |||
344 | /* | ||
345 | * Create entries for all existing IRQs. | ||
346 | */ | ||
347 | for (i = 0; i < ACTUAL_NR_IRQS; i++) { | ||
348 | if (irq_desc[i].handler == &no_irq_type) | ||
349 | continue; | ||
350 | register_irq_proc(i); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | int | ||
355 | request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
356 | unsigned long irqflags, const char * devname, void *dev_id) | ||
357 | { | ||
358 | int retval; | ||
359 | struct irqaction * action; | ||
360 | |||
361 | if (irq >= ACTUAL_NR_IRQS) | ||
362 | return -EINVAL; | ||
363 | if (!handler) | ||
364 | return -EINVAL; | ||
365 | |||
366 | #if 1 | ||
367 | /* | ||
368 | * Sanity-check: shared interrupts should REALLY pass in | ||
369 | * a real dev-ID, otherwise we'll have trouble later trying | ||
370 | * to figure out which interrupt is which (messes up the | ||
371 | * interrupt freeing logic etc). | ||
372 | */ | ||
373 | if ((irqflags & SA_SHIRQ) && !dev_id) { | ||
374 | printk(KERN_ERR | ||
375 | "Bad boy: %s (at %p) called us without a dev_id!\n", | ||
376 | devname, __builtin_return_address(0)); | ||
377 | } | ||
378 | #endif | ||
379 | |||
380 | action = (struct irqaction *) | ||
381 | kmalloc(sizeof(struct irqaction), GFP_KERNEL); | ||
382 | if (!action) | ||
383 | return -ENOMEM; | ||
384 | |||
385 | action->handler = handler; | ||
386 | action->flags = irqflags; | ||
387 | cpus_clear(action->mask); | ||
388 | action->name = devname; | ||
389 | action->next = NULL; | ||
390 | action->dev_id = dev_id; | ||
391 | |||
392 | #ifdef CONFIG_SMP | ||
393 | select_smp_affinity(irq); | ||
394 | #endif | ||
395 | |||
396 | retval = setup_irq(irq, action); | ||
397 | if (retval) | ||
398 | kfree(action); | ||
399 | return retval; | ||
400 | } | ||
401 | |||
402 | EXPORT_SYMBOL(request_irq); | ||
403 | |||
404 | void | ||
405 | free_irq(unsigned int irq, void *dev_id) | ||
406 | { | ||
407 | irq_desc_t *desc; | ||
408 | struct irqaction **p; | ||
409 | unsigned long flags; | ||
410 | |||
411 | if (irq >= ACTUAL_NR_IRQS) { | ||
412 | printk(KERN_CRIT "Trying to free IRQ%d\n", irq); | ||
413 | return; | ||
414 | } | ||
415 | |||
416 | desc = irq_desc + irq; | ||
417 | spin_lock_irqsave(&desc->lock,flags); | ||
418 | p = &desc->action; | ||
419 | for (;;) { | ||
420 | struct irqaction * action = *p; | ||
421 | if (action) { | ||
422 | struct irqaction **pp = p; | ||
423 | p = &action->next; | ||
424 | if (action->dev_id != dev_id) | ||
425 | continue; | ||
426 | |||
427 | /* Found - now remove it from the list of entries. */ | ||
428 | *pp = action->next; | ||
429 | if (!desc->action) { | ||
430 | desc->status |= IRQ_DISABLED; | ||
431 | desc->handler->shutdown(irq); | ||
432 | } | ||
433 | spin_unlock_irqrestore(&desc->lock,flags); | ||
434 | |||
435 | #ifdef CONFIG_SMP | ||
436 | /* Wait to make sure it's not being used on | ||
437 | another CPU. */ | ||
438 | while (desc->status & IRQ_INPROGRESS) | ||
439 | barrier(); | ||
440 | #endif | ||
441 | kfree(action); | ||
442 | return; | ||
443 | } | ||
444 | printk(KERN_ERR "Trying to free free IRQ%d\n",irq); | ||
445 | spin_unlock_irqrestore(&desc->lock,flags); | ||
446 | return; | ||
447 | } | ||
448 | } | ||
449 | |||
450 | EXPORT_SYMBOL(free_irq); | ||
451 | |||
452 | int | ||
453 | show_interrupts(struct seq_file *p, void *v) | ||
454 | { | ||
455 | #ifdef CONFIG_SMP | ||
456 | int j; | ||
457 | #endif | ||
458 | int i = *(loff_t *) v; | ||
459 | struct irqaction * action; | ||
460 | unsigned long flags; | ||
461 | |||
462 | #ifdef CONFIG_SMP | ||
463 | if (i == 0) { | ||
464 | seq_puts(p, " "); | ||
465 | for (i = 0; i < NR_CPUS; i++) | ||
466 | if (cpu_online(i)) | ||
467 | seq_printf(p, "CPU%d ", i); | ||
468 | seq_putc(p, '\n'); | ||
469 | } | ||
470 | #endif | ||
471 | |||
472 | if (i < ACTUAL_NR_IRQS) { | ||
473 | spin_lock_irqsave(&irq_desc[i].lock, flags); | ||
474 | action = irq_desc[i].action; | ||
475 | if (!action) | ||
476 | goto unlock; | ||
477 | seq_printf(p, "%3d: ",i); | ||
478 | #ifndef CONFIG_SMP | ||
479 | seq_printf(p, "%10u ", kstat_irqs(i)); | ||
480 | #else | ||
481 | for (j = 0; j < NR_CPUS; j++) | ||
482 | if (cpu_online(j)) | ||
483 | seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); | ||
484 | #endif | ||
485 | seq_printf(p, " %14s", irq_desc[i].handler->typename); | ||
486 | seq_printf(p, " %c%s", | ||
487 | (action->flags & SA_INTERRUPT)?'+':' ', | ||
488 | action->name); | ||
489 | |||
490 | for (action=action->next; action; action = action->next) { | ||
491 | seq_printf(p, ", %c%s", | ||
492 | (action->flags & SA_INTERRUPT)?'+':' ', | ||
493 | action->name); | ||
494 | } | ||
495 | |||
496 | seq_putc(p, '\n'); | ||
497 | unlock: | ||
498 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | ||
499 | } else if (i == ACTUAL_NR_IRQS) { | ||
500 | #ifdef CONFIG_SMP | ||
501 | seq_puts(p, "IPI: "); | ||
502 | for (i = 0; i < NR_CPUS; i++) | ||
503 | if (cpu_online(i)) | ||
504 | seq_printf(p, "%10lu ", cpu_data[i].ipi_count); | ||
505 | seq_putc(p, '\n'); | ||
506 | #endif | ||
507 | seq_printf(p, "ERR: %10lu\n", irq_err_count); | ||
508 | } | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | |||
513 | /* | ||
514 | * handle_irq handles all normal device IRQ's (the special | ||
515 | * SMP cross-CPU interrupts have their own specific | ||
516 | * handlers). | ||
517 | */ | ||
518 | |||
519 | #define MAX_ILLEGAL_IRQS 16 | ||
520 | |||
521 | void | ||
522 | handle_irq(int irq, struct pt_regs * regs) | ||
523 | { | ||
524 | /* | ||
525 | * We ack quickly, we don't want the irq controller | ||
526 | * thinking we're snobs just because some other CPU has | ||
527 | * disabled global interrupts (we have already done the | ||
528 | * INT_ACK cycles, it's too late to try to pretend to the | ||
529 | * controller that we aren't taking the interrupt). | ||
530 | * | ||
531 | * 0 return value means that this irq is already being | ||
532 | * handled by some other CPU. (or is disabled) | ||
533 | */ | ||
534 | int cpu = smp_processor_id(); | ||
535 | irq_desc_t *desc = irq_desc + irq; | ||
536 | struct irqaction * action; | ||
537 | unsigned int status; | ||
538 | static unsigned int illegal_count=0; | ||
539 | |||
540 | if ((unsigned) irq > ACTUAL_NR_IRQS && illegal_count < MAX_ILLEGAL_IRQS ) { | ||
541 | irq_err_count++; | ||
542 | illegal_count++; | ||
543 | printk(KERN_CRIT "device_interrupt: invalid interrupt %d\n", | ||
544 | irq); | ||
545 | return; | ||
546 | } | ||
547 | |||
548 | irq_enter(); | ||
549 | kstat_cpu(cpu).irqs[irq]++; | ||
550 | spin_lock_irq(&desc->lock); /* mask also the higher prio events */ | ||
551 | desc->handler->ack(irq); | ||
552 | /* | ||
553 | * REPLAY is when Linux resends an IRQ that was dropped earlier. | ||
554 | * WAITING is used by probe to mark irqs that are being tested. | ||
555 | */ | ||
556 | status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); | ||
557 | status |= IRQ_PENDING; /* we _want_ to handle it */ | ||
558 | |||
559 | /* | ||
560 | * If the IRQ is disabled for whatever reason, we cannot | ||
561 | * use the action we have. | ||
562 | */ | ||
563 | action = NULL; | ||
564 | if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { | ||
565 | action = desc->action; | ||
566 | status &= ~IRQ_PENDING; /* we commit to handling */ | ||
567 | status |= IRQ_INPROGRESS; /* we are handling it */ | ||
568 | } | ||
569 | desc->status = status; | ||
570 | |||
571 | /* | ||
572 | * If there is no IRQ handler or it was disabled, exit early. | ||
573 | * Since we set PENDING, if another processor is handling | ||
574 | * a different instance of this same irq, the other processor | ||
575 | * will take care of it. | ||
576 | */ | ||
577 | if (!action) | ||
578 | goto out; | ||
579 | |||
580 | /* | ||
581 | * Edge triggered interrupts need to remember pending events. | ||
582 | * This applies to any hw interrupts that allow a second | ||
583 | * instance of the same irq to arrive while we are in handle_irq | ||
584 | * or in the handler. But the code here only handles the _second_ | ||
585 | * instance of the irq, not the third or fourth. So it is mostly | ||
586 | * useful for irq hardware that does not mask cleanly in an | ||
587 | * SMP environment. | ||
588 | */ | ||
589 | for (;;) { | ||
590 | spin_unlock(&desc->lock); | ||
591 | handle_IRQ_event(irq, regs, action); | ||
592 | spin_lock(&desc->lock); | ||
593 | |||
594 | if (!(desc->status & IRQ_PENDING) | ||
595 | || (desc->status & IRQ_LEVEL)) | ||
596 | break; | ||
597 | desc->status &= ~IRQ_PENDING; | ||
598 | } | ||
599 | desc->status &= ~IRQ_INPROGRESS; | ||
600 | out: | ||
601 | /* | ||
602 | * The ->end() handler has to deal with interrupts which got | ||
603 | * disabled while the handler was running. | ||
604 | */ | ||
605 | desc->handler->end(irq); | ||
606 | spin_unlock(&desc->lock); | ||
607 | |||
608 | irq_exit(); | ||
609 | } | ||
610 | |||
611 | /* | ||
612 | * IRQ autodetection code.. | ||
613 | * | ||
614 | * This depends on the fact that any interrupt that | ||
615 | * comes in on to an unassigned handler will get stuck | ||
616 | * with "IRQ_WAITING" cleared and the interrupt | ||
617 | * disabled. | ||
618 | */ | ||
619 | unsigned long | ||
620 | probe_irq_on(void) | ||
621 | { | ||
622 | int i; | ||
623 | irq_desc_t *desc; | ||
624 | unsigned long delay; | ||
625 | unsigned long val; | ||
626 | |||
627 | /* Something may have generated an irq long ago and we want to | ||
628 | flush such a longstanding irq before considering it as spurious. */ | ||
629 | for (i = NR_IRQS-1; i >= 0; i--) { | ||
630 | desc = irq_desc + i; | ||
631 | |||
632 | spin_lock_irq(&desc->lock); | ||
633 | if (!irq_desc[i].action) | ||
634 | irq_desc[i].handler->startup(i); | ||
635 | spin_unlock_irq(&desc->lock); | ||
636 | } | ||
637 | |||
638 | /* Wait for longstanding interrupts to trigger. */ | ||
639 | for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) | ||
640 | /* about 20ms delay */ barrier(); | ||
641 | |||
642 | /* enable any unassigned irqs (we must startup again here because | ||
643 | if a longstanding irq happened in the previous stage, it may have | ||
644 | masked itself) first, enable any unassigned irqs. */ | ||
645 | for (i = NR_IRQS-1; i >= 0; i--) { | ||
646 | desc = irq_desc + i; | ||
647 | |||
648 | spin_lock_irq(&desc->lock); | ||
649 | if (!desc->action) { | ||
650 | desc->status |= IRQ_AUTODETECT | IRQ_WAITING; | ||
651 | if (desc->handler->startup(i)) | ||
652 | desc->status |= IRQ_PENDING; | ||
653 | } | ||
654 | spin_unlock_irq(&desc->lock); | ||
655 | } | ||
656 | |||
657 | /* | ||
658 | * Wait for spurious interrupts to trigger | ||
659 | */ | ||
660 | for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) | ||
661 | /* about 100ms delay */ barrier(); | ||
662 | |||
663 | /* | ||
664 | * Now filter out any obviously spurious interrupts | ||
665 | */ | ||
666 | val = 0; | ||
667 | for (i=0; i<NR_IRQS; i++) { | ||
668 | irq_desc_t *desc = irq_desc + i; | ||
669 | unsigned int status; | ||
670 | |||
671 | spin_lock_irq(&desc->lock); | ||
672 | status = desc->status; | ||
673 | |||
674 | if (status & IRQ_AUTODETECT) { | ||
675 | /* It triggered already - consider it spurious. */ | ||
676 | if (!(status & IRQ_WAITING)) { | ||
677 | desc->status = status & ~IRQ_AUTODETECT; | ||
678 | desc->handler->shutdown(i); | ||
679 | } else | ||
680 | if (i < 32) | ||
681 | val |= 1 << i; | ||
682 | } | ||
683 | spin_unlock_irq(&desc->lock); | ||
684 | } | ||
685 | |||
686 | return val; | ||
687 | } | ||
688 | |||
689 | EXPORT_SYMBOL(probe_irq_on); | ||
690 | |||
691 | /* | ||
692 | * Return a mask of triggered interrupts (this | ||
693 | * can handle only legacy ISA interrupts). | ||
694 | */ | ||
695 | unsigned int | ||
696 | probe_irq_mask(unsigned long val) | ||
697 | { | ||
698 | int i; | ||
699 | unsigned int mask; | ||
700 | |||
701 | mask = 0; | ||
702 | for (i = 0; i < NR_IRQS; i++) { | ||
703 | irq_desc_t *desc = irq_desc + i; | ||
704 | unsigned int status; | ||
705 | |||
706 | spin_lock_irq(&desc->lock); | ||
707 | status = desc->status; | ||
708 | |||
709 | if (status & IRQ_AUTODETECT) { | ||
710 | /* We only react to ISA interrupts */ | ||
711 | if (!(status & IRQ_WAITING)) { | ||
712 | if (i < 16) | ||
713 | mask |= 1 << i; | ||
714 | } | ||
715 | |||
716 | desc->status = status & ~IRQ_AUTODETECT; | ||
717 | desc->handler->shutdown(i); | ||
718 | } | ||
719 | spin_unlock_irq(&desc->lock); | ||
720 | } | ||
721 | |||
722 | return mask & val; | ||
723 | } | ||
724 | |||
725 | /* | ||
726 | * Get the result of the IRQ probe.. A negative result means that | ||
727 | * we have several candidates (but we return the lowest-numbered | ||
728 | * one). | ||
729 | */ | ||
730 | |||
731 | int | ||
732 | probe_irq_off(unsigned long val) | ||
733 | { | ||
734 | int i, irq_found, nr_irqs; | ||
735 | |||
736 | nr_irqs = 0; | ||
737 | irq_found = 0; | ||
738 | for (i=0; i<NR_IRQS; i++) { | ||
739 | irq_desc_t *desc = irq_desc + i; | ||
740 | unsigned int status; | ||
741 | |||
742 | spin_lock_irq(&desc->lock); | ||
743 | status = desc->status; | ||
744 | |||
745 | if (status & IRQ_AUTODETECT) { | ||
746 | if (!(status & IRQ_WAITING)) { | ||
747 | if (!nr_irqs) | ||
748 | irq_found = i; | ||
749 | nr_irqs++; | ||
750 | } | ||
751 | desc->status = status & ~IRQ_AUTODETECT; | ||
752 | desc->handler->shutdown(i); | ||
753 | } | ||
754 | spin_unlock_irq(&desc->lock); | ||
755 | } | ||
756 | |||
757 | if (nr_irqs > 1) | ||
758 | irq_found = -irq_found; | ||
759 | return irq_found; | ||
760 | } | ||
761 | |||
762 | EXPORT_SYMBOL(probe_irq_off); | ||
763 | |||
764 | #ifdef CONFIG_SMP | ||
765 | void synchronize_irq(unsigned int irq) | ||
766 | { | ||
767 | /* is there anything to synchronize with? */ | ||
768 | if (!irq_desc[irq].action) | ||
769 | return; | ||
770 | |||
771 | while (irq_desc[irq].status & IRQ_INPROGRESS) | ||
772 | barrier(); | ||
773 | } | ||
774 | #endif | ||
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c new file mode 100644 index 000000000000..e6ded33c6e22 --- /dev/null +++ b/arch/alpha/kernel/irq_alpha.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * Alpha specific irq code. | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/init.h> | ||
7 | #include <linux/sched.h> | ||
8 | #include <linux/irq.h> | ||
9 | #include <linux/kernel_stat.h> | ||
10 | |||
11 | #include <asm/machvec.h> | ||
12 | #include <asm/dma.h> | ||
13 | |||
14 | #include "proto.h" | ||
15 | #include "irq_impl.h" | ||
16 | |||
17 | /* Hack minimum IPL during interrupt processing for broken hardware. */ | ||
18 | #ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK | ||
19 | int __min_ipl; | ||
20 | #endif | ||
21 | |||
22 | /* | ||
23 | * Performance counter hook. A module can override this to | ||
24 | * do something useful. | ||
25 | */ | ||
26 | static void | ||
27 | dummy_perf(unsigned long vector, struct pt_regs *regs) | ||
28 | { | ||
29 | irq_err_count++; | ||
30 | printk(KERN_CRIT "Performance counter interrupt!\n"); | ||
31 | } | ||
32 | |||
33 | void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf; | ||
34 | |||
35 | /* | ||
36 | * The main interrupt entry point. | ||
37 | */ | ||
38 | |||
39 | asmlinkage void | ||
40 | do_entInt(unsigned long type, unsigned long vector, | ||
41 | unsigned long la_ptr, struct pt_regs *regs) | ||
42 | { | ||
43 | switch (type) { | ||
44 | case 0: | ||
45 | #ifdef CONFIG_SMP | ||
46 | handle_ipi(regs); | ||
47 | return; | ||
48 | #else | ||
49 | irq_err_count++; | ||
50 | printk(KERN_CRIT "Interprocessor interrupt? " | ||
51 | "You must be kidding!\n"); | ||
52 | #endif | ||
53 | break; | ||
54 | case 1: | ||
55 | #ifdef CONFIG_SMP | ||
56 | { | ||
57 | long cpu; | ||
58 | smp_percpu_timer_interrupt(regs); | ||
59 | cpu = smp_processor_id(); | ||
60 | if (cpu != boot_cpuid) { | ||
61 | kstat_cpu(cpu).irqs[RTC_IRQ]++; | ||
62 | } else { | ||
63 | handle_irq(RTC_IRQ, regs); | ||
64 | } | ||
65 | } | ||
66 | #else | ||
67 | handle_irq(RTC_IRQ, regs); | ||
68 | #endif | ||
69 | return; | ||
70 | case 2: | ||
71 | alpha_mv.machine_check(vector, la_ptr, regs); | ||
72 | return; | ||
73 | case 3: | ||
74 | alpha_mv.device_interrupt(vector, regs); | ||
75 | return; | ||
76 | case 4: | ||
77 | perf_irq(la_ptr, regs); | ||
78 | return; | ||
79 | default: | ||
80 | printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n", | ||
81 | type, vector); | ||
82 | } | ||
83 | printk(KERN_CRIT "PC = %016lx PS=%04lx\n", regs->pc, regs->ps); | ||
84 | } | ||
85 | |||
86 | void __init | ||
87 | common_init_isa_dma(void) | ||
88 | { | ||
89 | outb(0, DMA1_RESET_REG); | ||
90 | outb(0, DMA2_RESET_REG); | ||
91 | outb(0, DMA1_CLR_MASK_REG); | ||
92 | outb(0, DMA2_CLR_MASK_REG); | ||
93 | } | ||
94 | |||
95 | void __init | ||
96 | init_IRQ(void) | ||
97 | { | ||
98 | /* Just in case the platform init_irq() causes interrupts/mchecks | ||
99 | (as is the case with RAWHIDE, at least). */ | ||
100 | wrent(entInt, 0); | ||
101 | |||
102 | alpha_mv.init_irq(); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * machine error checks | ||
107 | */ | ||
108 | #define MCHK_K_TPERR 0x0080 | ||
109 | #define MCHK_K_TCPERR 0x0082 | ||
110 | #define MCHK_K_HERR 0x0084 | ||
111 | #define MCHK_K_ECC_C 0x0086 | ||
112 | #define MCHK_K_ECC_NC 0x0088 | ||
113 | #define MCHK_K_OS_BUGCHECK 0x008A | ||
114 | #define MCHK_K_PAL_BUGCHECK 0x0090 | ||
115 | |||
116 | #ifndef CONFIG_SMP | ||
117 | struct mcheck_info __mcheck_info; | ||
118 | #endif | ||
119 | |||
120 | void | ||
121 | process_mcheck_info(unsigned long vector, unsigned long la_ptr, | ||
122 | struct pt_regs *regs, const char *machine, | ||
123 | int expected) | ||
124 | { | ||
125 | struct el_common *mchk_header; | ||
126 | const char *reason; | ||
127 | |||
128 | /* | ||
129 | * See if the machine check is due to a badaddr() and if so, | ||
130 | * ignore it. | ||
131 | */ | ||
132 | |||
133 | #ifdef CONFIG_VERBOSE_MCHECK | ||
134 | if (alpha_verbose_mcheck > 1) { | ||
135 | printk(KERN_CRIT "%s machine check %s\n", machine, | ||
136 | expected ? "expected." : "NOT expected!!!"); | ||
137 | } | ||
138 | #endif | ||
139 | |||
140 | if (expected) { | ||
141 | int cpu = smp_processor_id(); | ||
142 | mcheck_expected(cpu) = 0; | ||
143 | mcheck_taken(cpu) = 1; | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | mchk_header = (struct el_common *)la_ptr; | ||
148 | |||
149 | printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%x\n", | ||
150 | machine, vector, regs->pc, mchk_header->code); | ||
151 | |||
152 | switch (mchk_header->code) { | ||
153 | /* Machine check reasons. Defined according to PALcode sources. */ | ||
154 | case 0x80: reason = "tag parity error"; break; | ||
155 | case 0x82: reason = "tag control parity error"; break; | ||
156 | case 0x84: reason = "generic hard error"; break; | ||
157 | case 0x86: reason = "correctable ECC error"; break; | ||
158 | case 0x88: reason = "uncorrectable ECC error"; break; | ||
159 | case 0x8A: reason = "OS-specific PAL bugcheck"; break; | ||
160 | case 0x90: reason = "callsys in kernel mode"; break; | ||
161 | case 0x96: reason = "i-cache read retryable error"; break; | ||
162 | case 0x98: reason = "processor detected hard error"; break; | ||
163 | |||
164 | /* System specific (these are for Alcor, at least): */ | ||
165 | case 0x202: reason = "system detected hard error"; break; | ||
166 | case 0x203: reason = "system detected uncorrectable ECC error"; break; | ||
167 | case 0x204: reason = "SIO SERR occurred on PCI bus"; break; | ||
168 | case 0x205: reason = "parity error detected by core logic"; break; | ||
169 | case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break; | ||
170 | case 0x207: reason = "non-existent memory error"; break; | ||
171 | case 0x208: reason = "MCHK_K_DCSR"; break; | ||
172 | case 0x209: reason = "PCI SERR detected"; break; | ||
173 | case 0x20b: reason = "PCI data parity error detected"; break; | ||
174 | case 0x20d: reason = "PCI address parity error detected"; break; | ||
175 | case 0x20f: reason = "PCI master abort error"; break; | ||
176 | case 0x211: reason = "PCI target abort error"; break; | ||
177 | case 0x213: reason = "scatter/gather PTE invalid error"; break; | ||
178 | case 0x215: reason = "flash ROM write error"; break; | ||
179 | case 0x217: reason = "IOA timeout detected"; break; | ||
180 | case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; | ||
181 | case 0x21b: reason = "EISA fail-safe timer timeout"; break; | ||
182 | case 0x21d: reason = "EISA bus time-out"; break; | ||
183 | case 0x21f: reason = "EISA software generated NMI"; break; | ||
184 | case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; | ||
185 | default: reason = "unknown"; break; | ||
186 | } | ||
187 | |||
188 | printk(KERN_CRIT "machine check type: %s%s\n", | ||
189 | reason, mchk_header->retry ? " (retryable)" : ""); | ||
190 | |||
191 | dik_show_regs(regs, NULL); | ||
192 | |||
193 | #ifdef CONFIG_VERBOSE_MCHECK | ||
194 | if (alpha_verbose_mcheck > 1) { | ||
195 | /* Dump the logout area to give all info. */ | ||
196 | unsigned long *ptr = (unsigned long *)la_ptr; | ||
197 | long i; | ||
198 | for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { | ||
199 | printk(KERN_CRIT " +%8lx %016lx %016lx\n", | ||
200 | i*sizeof(long), ptr[i], ptr[i+1]); | ||
201 | } | ||
202 | } | ||
203 | #endif /* CONFIG_VERBOSE_MCHECK */ | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * The special RTC interrupt type. The interrupt itself was | ||
208 | * processed by PALcode, and comes in via entInt vector 1. | ||
209 | */ | ||
210 | |||
211 | static void rtc_enable_disable(unsigned int irq) { } | ||
212 | static unsigned int rtc_startup(unsigned int irq) { return 0; } | ||
213 | |||
214 | struct irqaction timer_irqaction = { | ||
215 | .handler = timer_interrupt, | ||
216 | .flags = SA_INTERRUPT, | ||
217 | .name = "timer", | ||
218 | }; | ||
219 | |||
220 | static struct hw_interrupt_type rtc_irq_type = { | ||
221 | .typename = "RTC", | ||
222 | .startup = rtc_startup, | ||
223 | .shutdown = rtc_enable_disable, | ||
224 | .enable = rtc_enable_disable, | ||
225 | .disable = rtc_enable_disable, | ||
226 | .ack = rtc_enable_disable, | ||
227 | .end = rtc_enable_disable, | ||
228 | }; | ||
229 | |||
230 | void __init | ||
231 | init_rtc_irq(void) | ||
232 | { | ||
233 | irq_desc[RTC_IRQ].status = IRQ_DISABLED; | ||
234 | irq_desc[RTC_IRQ].handler = &rtc_irq_type; | ||
235 | setup_irq(RTC_IRQ, &timer_irqaction); | ||
236 | } | ||
237 | |||
238 | /* Dummy irqactions. */ | ||
239 | struct irqaction isa_cascade_irqaction = { | ||
240 | .handler = no_action, | ||
241 | .name = "isa-cascade" | ||
242 | }; | ||
243 | |||
244 | struct irqaction timer_cascade_irqaction = { | ||
245 | .handler = no_action, | ||
246 | .name = "timer-cascade" | ||
247 | }; | ||
248 | |||
249 | struct irqaction halt_switch_irqaction = { | ||
250 | .handler = no_action, | ||
251 | .name = "halt-switch" | ||
252 | }; | ||
diff --git a/arch/alpha/kernel/irq_i8259.c b/arch/alpha/kernel/irq_i8259.c new file mode 100644 index 000000000000..b188683b83fd --- /dev/null +++ b/arch/alpha/kernel/irq_i8259.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/irq_i8259.c | ||
3 | * | ||
4 | * This is the 'legacy' 8259A Programmable Interrupt Controller, | ||
5 | * present in the majority of PC/AT boxes. | ||
6 | * | ||
7 | * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c. | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/cache.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | |||
17 | #include <asm/io.h> | ||
18 | |||
19 | #include "proto.h" | ||
20 | #include "irq_impl.h" | ||
21 | |||
22 | |||
23 | /* Note mask bit is true for DISABLED irqs. */ | ||
24 | static unsigned int cached_irq_mask = 0xffff; | ||
25 | static DEFINE_SPINLOCK(i8259_irq_lock); | ||
26 | |||
27 | static inline void | ||
28 | i8259_update_irq_hw(unsigned int irq, unsigned long mask) | ||
29 | { | ||
30 | int port = 0x21; | ||
31 | if (irq & 8) mask >>= 8; | ||
32 | if (irq & 8) port = 0xA1; | ||
33 | outb(mask, port); | ||
34 | } | ||
35 | |||
36 | inline void | ||
37 | i8259a_enable_irq(unsigned int irq) | ||
38 | { | ||
39 | spin_lock(&i8259_irq_lock); | ||
40 | i8259_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); | ||
41 | spin_unlock(&i8259_irq_lock); | ||
42 | } | ||
43 | |||
44 | static inline void | ||
45 | __i8259a_disable_irq(unsigned int irq) | ||
46 | { | ||
47 | i8259_update_irq_hw(irq, cached_irq_mask |= 1 << irq); | ||
48 | } | ||
49 | |||
50 | void | ||
51 | i8259a_disable_irq(unsigned int irq) | ||
52 | { | ||
53 | spin_lock(&i8259_irq_lock); | ||
54 | __i8259a_disable_irq(irq); | ||
55 | spin_unlock(&i8259_irq_lock); | ||
56 | } | ||
57 | |||
58 | void | ||
59 | i8259a_mask_and_ack_irq(unsigned int irq) | ||
60 | { | ||
61 | spin_lock(&i8259_irq_lock); | ||
62 | __i8259a_disable_irq(irq); | ||
63 | |||
64 | /* Ack the interrupt making it the lowest priority. */ | ||
65 | if (irq >= 8) { | ||
66 | outb(0xE0 | (irq - 8), 0xa0); /* ack the slave */ | ||
67 | irq = 2; | ||
68 | } | ||
69 | outb(0xE0 | irq, 0x20); /* ack the master */ | ||
70 | spin_unlock(&i8259_irq_lock); | ||
71 | } | ||
72 | |||
73 | unsigned int | ||
74 | i8259a_startup_irq(unsigned int irq) | ||
75 | { | ||
76 | i8259a_enable_irq(irq); | ||
77 | return 0; /* never anything pending */ | ||
78 | } | ||
79 | |||
80 | void | ||
81 | i8259a_end_irq(unsigned int irq) | ||
82 | { | ||
83 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
84 | i8259a_enable_irq(irq); | ||
85 | } | ||
86 | |||
87 | struct hw_interrupt_type i8259a_irq_type = { | ||
88 | .typename = "XT-PIC", | ||
89 | .startup = i8259a_startup_irq, | ||
90 | .shutdown = i8259a_disable_irq, | ||
91 | .enable = i8259a_enable_irq, | ||
92 | .disable = i8259a_disable_irq, | ||
93 | .ack = i8259a_mask_and_ack_irq, | ||
94 | .end = i8259a_end_irq, | ||
95 | }; | ||
96 | |||
97 | void __init | ||
98 | init_i8259a_irqs(void) | ||
99 | { | ||
100 | static struct irqaction cascade = { | ||
101 | .handler = no_action, | ||
102 | .name = "cascade", | ||
103 | }; | ||
104 | |||
105 | long i; | ||
106 | |||
107 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | ||
108 | outb(0xff, 0xA1); /* mask all of 8259A-2 */ | ||
109 | |||
110 | for (i = 0; i < 16; i++) { | ||
111 | irq_desc[i].status = IRQ_DISABLED; | ||
112 | irq_desc[i].handler = &i8259a_irq_type; | ||
113 | } | ||
114 | |||
115 | setup_irq(2, &cascade); | ||
116 | } | ||
117 | |||
118 | |||
119 | #if defined(CONFIG_ALPHA_GENERIC) | ||
120 | # define IACK_SC alpha_mv.iack_sc | ||
121 | #elif defined(CONFIG_ALPHA_APECS) | ||
122 | # define IACK_SC APECS_IACK_SC | ||
123 | #elif defined(CONFIG_ALPHA_LCA) | ||
124 | # define IACK_SC LCA_IACK_SC | ||
125 | #elif defined(CONFIG_ALPHA_CIA) | ||
126 | # define IACK_SC CIA_IACK_SC | ||
127 | #elif defined(CONFIG_ALPHA_PYXIS) | ||
128 | # define IACK_SC PYXIS_IACK_SC | ||
129 | #elif defined(CONFIG_ALPHA_TITAN) | ||
130 | # define IACK_SC TITAN_IACK_SC | ||
131 | #elif defined(CONFIG_ALPHA_TSUNAMI) | ||
132 | # define IACK_SC TSUNAMI_IACK_SC | ||
133 | #elif defined(CONFIG_ALPHA_IRONGATE) | ||
134 | # define IACK_SC IRONGATE_IACK_SC | ||
135 | #endif | ||
136 | /* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since | ||
137 | sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ | ||
138 | |||
139 | #if defined(IACK_SC) | ||
140 | void | ||
141 | isa_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
142 | { | ||
143 | /* | ||
144 | * Generate a PCI interrupt acknowledge cycle. The PIC will | ||
145 | * respond with the interrupt vector of the highest priority | ||
146 | * interrupt that is pending. The PALcode sets up the | ||
147 | * interrupts vectors such that irq level L generates vector L. | ||
148 | */ | ||
149 | int j = *(vuip) IACK_SC; | ||
150 | j &= 0xff; | ||
151 | handle_irq(j, regs); | ||
152 | } | ||
153 | #endif | ||
154 | |||
155 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) | ||
156 | void | ||
157 | isa_no_iack_sc_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
158 | { | ||
159 | unsigned long pic; | ||
160 | |||
161 | /* | ||
162 | * It seems to me that the probability of two or more *device* | ||
163 | * interrupts occurring at almost exactly the same time is | ||
164 | * pretty low. So why pay the price of checking for | ||
165 | * additional interrupts here if the common case can be | ||
166 | * handled so much easier? | ||
167 | */ | ||
168 | /* | ||
169 | * The first read of gives you *all* interrupting lines. | ||
170 | * Therefore, read the mask register and and out those lines | ||
171 | * not enabled. Note that some documentation has 21 and a1 | ||
172 | * write only. This is not true. | ||
173 | */ | ||
174 | pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ | ||
175 | pic &= 0xFFFB; /* mask out cascade & hibits */ | ||
176 | |||
177 | while (pic) { | ||
178 | int j = ffz(~pic); | ||
179 | pic &= pic - 1; | ||
180 | handle_irq(j, regs); | ||
181 | } | ||
182 | } | ||
183 | #endif | ||
diff --git a/arch/alpha/kernel/irq_impl.h b/arch/alpha/kernel/irq_impl.h new file mode 100644 index 000000000000..f201d8ffc0d9 --- /dev/null +++ b/arch/alpha/kernel/irq_impl.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/irq_impl.h | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Copyright (C) 1998, 2000 Richard Henderson | ||
6 | * | ||
7 | * This file contains declarations and inline functions for interfacing | ||
8 | * with the IRQ handling routines in irq.c. | ||
9 | */ | ||
10 | |||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/irq.h> | ||
13 | #include <linux/profile.h> | ||
14 | |||
15 | |||
16 | #define RTC_IRQ 8 | ||
17 | |||
18 | extern void isa_device_interrupt(unsigned long, struct pt_regs *); | ||
19 | extern void isa_no_iack_sc_device_interrupt(unsigned long, struct pt_regs *); | ||
20 | extern void srm_device_interrupt(unsigned long, struct pt_regs *); | ||
21 | extern void pyxis_device_interrupt(unsigned long, struct pt_regs *); | ||
22 | |||
23 | extern struct irqaction timer_irqaction; | ||
24 | extern struct irqaction isa_cascade_irqaction; | ||
25 | extern struct irqaction timer_cascade_irqaction; | ||
26 | extern struct irqaction halt_switch_irqaction; | ||
27 | |||
28 | extern void init_srm_irqs(long, unsigned long); | ||
29 | extern void init_pyxis_irqs(unsigned long); | ||
30 | extern void init_rtc_irq(void); | ||
31 | |||
32 | extern void common_init_isa_dma(void); | ||
33 | |||
34 | extern void i8259a_enable_irq(unsigned int); | ||
35 | extern void i8259a_disable_irq(unsigned int); | ||
36 | extern void i8259a_mask_and_ack_irq(unsigned int); | ||
37 | extern unsigned int i8259a_startup_irq(unsigned int); | ||
38 | extern void i8259a_end_irq(unsigned int); | ||
39 | extern struct hw_interrupt_type i8259a_irq_type; | ||
40 | extern void init_i8259a_irqs(void); | ||
41 | |||
42 | extern void handle_irq(int irq, struct pt_regs * regs); | ||
diff --git a/arch/alpha/kernel/irq_pyxis.c b/arch/alpha/kernel/irq_pyxis.c new file mode 100644 index 000000000000..146a20b9e3d5 --- /dev/null +++ b/arch/alpha/kernel/irq_pyxis.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/irq_pyxis.c | ||
3 | * | ||
4 | * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). | ||
5 | * | ||
6 | * IRQ Code common to all PYXIS core logic chips. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/irq.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/core_cia.h> | ||
15 | |||
16 | #include "proto.h" | ||
17 | #include "irq_impl.h" | ||
18 | |||
19 | |||
20 | /* Note mask bit is true for ENABLED irqs. */ | ||
21 | static unsigned long cached_irq_mask; | ||
22 | |||
23 | static inline void | ||
24 | pyxis_update_irq_hw(unsigned long mask) | ||
25 | { | ||
26 | *(vulp)PYXIS_INT_MASK = mask; | ||
27 | mb(); | ||
28 | *(vulp)PYXIS_INT_MASK; | ||
29 | } | ||
30 | |||
31 | static inline void | ||
32 | pyxis_enable_irq(unsigned int irq) | ||
33 | { | ||
34 | pyxis_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); | ||
35 | } | ||
36 | |||
37 | static void | ||
38 | pyxis_disable_irq(unsigned int irq) | ||
39 | { | ||
40 | pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); | ||
41 | } | ||
42 | |||
43 | static unsigned int | ||
44 | pyxis_startup_irq(unsigned int irq) | ||
45 | { | ||
46 | pyxis_enable_irq(irq); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static void | ||
51 | pyxis_end_irq(unsigned int irq) | ||
52 | { | ||
53 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
54 | pyxis_enable_irq(irq); | ||
55 | } | ||
56 | |||
57 | static void | ||
58 | pyxis_mask_and_ack_irq(unsigned int irq) | ||
59 | { | ||
60 | unsigned long bit = 1UL << (irq - 16); | ||
61 | unsigned long mask = cached_irq_mask &= ~bit; | ||
62 | |||
63 | /* Disable the interrupt. */ | ||
64 | *(vulp)PYXIS_INT_MASK = mask; | ||
65 | wmb(); | ||
66 | /* Ack PYXIS PCI interrupt. */ | ||
67 | *(vulp)PYXIS_INT_REQ = bit; | ||
68 | mb(); | ||
69 | /* Re-read to force both writes. */ | ||
70 | *(vulp)PYXIS_INT_MASK; | ||
71 | } | ||
72 | |||
73 | static struct hw_interrupt_type pyxis_irq_type = { | ||
74 | .typename = "PYXIS", | ||
75 | .startup = pyxis_startup_irq, | ||
76 | .shutdown = pyxis_disable_irq, | ||
77 | .enable = pyxis_enable_irq, | ||
78 | .disable = pyxis_disable_irq, | ||
79 | .ack = pyxis_mask_and_ack_irq, | ||
80 | .end = pyxis_end_irq, | ||
81 | }; | ||
82 | |||
83 | void | ||
84 | pyxis_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
85 | { | ||
86 | unsigned long pld; | ||
87 | unsigned int i; | ||
88 | |||
89 | /* Read the interrupt summary register of PYXIS */ | ||
90 | pld = *(vulp)PYXIS_INT_REQ; | ||
91 | pld &= cached_irq_mask; | ||
92 | |||
93 | /* | ||
94 | * Now for every possible bit set, work through them and call | ||
95 | * the appropriate interrupt handler. | ||
96 | */ | ||
97 | while (pld) { | ||
98 | i = ffz(~pld); | ||
99 | pld &= pld - 1; /* clear least bit set */ | ||
100 | if (i == 7) | ||
101 | isa_device_interrupt(vector, regs); | ||
102 | else | ||
103 | handle_irq(16+i, regs); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void __init | ||
108 | init_pyxis_irqs(unsigned long ignore_mask) | ||
109 | { | ||
110 | long i; | ||
111 | |||
112 | *(vulp)PYXIS_INT_MASK = 0; /* disable all */ | ||
113 | *(vulp)PYXIS_INT_REQ = -1; /* flush all */ | ||
114 | mb(); | ||
115 | |||
116 | /* Send -INTA pulses to clear any pending interrupts ...*/ | ||
117 | *(vuip) CIA_IACK_SC; | ||
118 | |||
119 | for (i = 16; i < 48; ++i) { | ||
120 | if ((ignore_mask >> i) & 1) | ||
121 | continue; | ||
122 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
123 | irq_desc[i].handler = &pyxis_irq_type; | ||
124 | } | ||
125 | |||
126 | setup_irq(16+7, &isa_cascade_irqaction); | ||
127 | } | ||
diff --git a/arch/alpha/kernel/irq_srm.c b/arch/alpha/kernel/irq_srm.c new file mode 100644 index 000000000000..0a87e466918c --- /dev/null +++ b/arch/alpha/kernel/irq_srm.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Handle interrupts from the SRM, assuming no additional weirdness. | ||
3 | */ | ||
4 | |||
5 | #include <linux/init.h> | ||
6 | #include <linux/sched.h> | ||
7 | #include <linux/irq.h> | ||
8 | |||
9 | #include "proto.h" | ||
10 | #include "irq_impl.h" | ||
11 | |||
12 | |||
13 | /* | ||
14 | * Is the palcode SMP safe? In other words: can we call cserve_ena/dis | ||
15 | * at the same time in multiple CPUs? To be safe I added a spinlock | ||
16 | * but it can be removed trivially if the palcode is robust against smp. | ||
17 | */ | ||
18 | DEFINE_SPINLOCK(srm_irq_lock); | ||
19 | |||
20 | static inline void | ||
21 | srm_enable_irq(unsigned int irq) | ||
22 | { | ||
23 | spin_lock(&srm_irq_lock); | ||
24 | cserve_ena(irq - 16); | ||
25 | spin_unlock(&srm_irq_lock); | ||
26 | } | ||
27 | |||
28 | static void | ||
29 | srm_disable_irq(unsigned int irq) | ||
30 | { | ||
31 | spin_lock(&srm_irq_lock); | ||
32 | cserve_dis(irq - 16); | ||
33 | spin_unlock(&srm_irq_lock); | ||
34 | } | ||
35 | |||
36 | static unsigned int | ||
37 | srm_startup_irq(unsigned int irq) | ||
38 | { | ||
39 | srm_enable_irq(irq); | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static void | ||
44 | srm_end_irq(unsigned int irq) | ||
45 | { | ||
46 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
47 | srm_enable_irq(irq); | ||
48 | } | ||
49 | |||
50 | /* Handle interrupts from the SRM, assuming no additional weirdness. */ | ||
51 | static struct hw_interrupt_type srm_irq_type = { | ||
52 | .typename = "SRM", | ||
53 | .startup = srm_startup_irq, | ||
54 | .shutdown = srm_disable_irq, | ||
55 | .enable = srm_enable_irq, | ||
56 | .disable = srm_disable_irq, | ||
57 | .ack = srm_disable_irq, | ||
58 | .end = srm_end_irq, | ||
59 | }; | ||
60 | |||
61 | void __init | ||
62 | init_srm_irqs(long max, unsigned long ignore_mask) | ||
63 | { | ||
64 | long i; | ||
65 | |||
66 | for (i = 16; i < max; ++i) { | ||
67 | if (i < 64 && ((ignore_mask >> i) & 1)) | ||
68 | continue; | ||
69 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
70 | irq_desc[i].handler = &srm_irq_type; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | void | ||
75 | srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
76 | { | ||
77 | int irq = (vector - 0x800) >> 4; | ||
78 | handle_irq(irq, regs); | ||
79 | } | ||
diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h new file mode 100644 index 000000000000..4959b7a3e1e6 --- /dev/null +++ b/arch/alpha/kernel/machvec_impl.h | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/machvec.h | ||
3 | * | ||
4 | * Copyright (C) 1997, 1998 Richard Henderson | ||
5 | * | ||
6 | * This file has goodies to help simplify instantiation of machine vectors. | ||
7 | */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <asm/pgalloc.h> | ||
11 | |||
12 | /* Whee. These systems don't have an HAE: | ||
13 | IRONGATE, MARVEL, POLARIS, TSUNAMI, TITAN, WILDFIRE | ||
14 | Fix things up for the GENERIC kernel by defining the HAE address | ||
15 | to be that of the cache. Now we can read and write it as we like. ;-) */ | ||
16 | #define IRONGATE_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
17 | #define MARVEL_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
18 | #define POLARIS_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
19 | #define TSUNAMI_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
20 | #define TITAN_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
21 | #define WILDFIRE_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
22 | |||
23 | #ifdef CIA_ONE_HAE_WINDOW | ||
24 | #define CIA_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
25 | #endif | ||
26 | #ifdef MCPCIA_ONE_HAE_WINDOW | ||
27 | #define MCPCIA_HAE_ADDRESS (&alpha_mv.hae_cache) | ||
28 | #endif | ||
29 | |||
30 | /* Only a few systems don't define IACK_SC, handling all interrupts through | ||
31 | the SRM console. But splitting out that one case from IO() below | ||
32 | seems like such a pain. Define this to get things to compile. */ | ||
33 | #define JENSEN_IACK_SC 1 | ||
34 | #define T2_IACK_SC 1 | ||
35 | #define WILDFIRE_IACK_SC 1 /* FIXME */ | ||
36 | |||
37 | /* | ||
38 | * Some helpful macros for filling in the blanks. | ||
39 | */ | ||
40 | |||
41 | #define CAT1(x,y) x##y | ||
42 | #define CAT(x,y) CAT1(x,y) | ||
43 | |||
44 | #define DO_DEFAULT_RTC rtc_port: 0x70 | ||
45 | |||
46 | #define DO_EV4_MMU \ | ||
47 | .max_asn = EV4_MAX_ASN, \ | ||
48 | .mv_switch_mm = ev4_switch_mm, \ | ||
49 | .mv_activate_mm = ev4_activate_mm, \ | ||
50 | .mv_flush_tlb_current = ev4_flush_tlb_current, \ | ||
51 | .mv_flush_tlb_current_page = ev4_flush_tlb_current_page | ||
52 | |||
53 | #define DO_EV5_MMU \ | ||
54 | .max_asn = EV5_MAX_ASN, \ | ||
55 | .mv_switch_mm = ev5_switch_mm, \ | ||
56 | .mv_activate_mm = ev5_activate_mm, \ | ||
57 | .mv_flush_tlb_current = ev5_flush_tlb_current, \ | ||
58 | .mv_flush_tlb_current_page = ev5_flush_tlb_current_page | ||
59 | |||
60 | #define DO_EV6_MMU \ | ||
61 | .max_asn = EV6_MAX_ASN, \ | ||
62 | .mv_switch_mm = ev5_switch_mm, \ | ||
63 | .mv_activate_mm = ev5_activate_mm, \ | ||
64 | .mv_flush_tlb_current = ev5_flush_tlb_current, \ | ||
65 | .mv_flush_tlb_current_page = ev5_flush_tlb_current_page | ||
66 | |||
67 | #define DO_EV7_MMU \ | ||
68 | .max_asn = EV6_MAX_ASN, \ | ||
69 | .mv_switch_mm = ev5_switch_mm, \ | ||
70 | .mv_activate_mm = ev5_activate_mm, \ | ||
71 | .mv_flush_tlb_current = ev5_flush_tlb_current, \ | ||
72 | .mv_flush_tlb_current_page = ev5_flush_tlb_current_page | ||
73 | |||
74 | #define IO_LITE(UP,low) \ | ||
75 | .hae_register = (unsigned long *) CAT(UP,_HAE_ADDRESS), \ | ||
76 | .iack_sc = CAT(UP,_IACK_SC), \ | ||
77 | .mv_ioread8 = CAT(low,_ioread8), \ | ||
78 | .mv_ioread16 = CAT(low,_ioread16), \ | ||
79 | .mv_ioread32 = CAT(low,_ioread32), \ | ||
80 | .mv_iowrite8 = CAT(low,_iowrite8), \ | ||
81 | .mv_iowrite16 = CAT(low,_iowrite16), \ | ||
82 | .mv_iowrite32 = CAT(low,_iowrite32), \ | ||
83 | .mv_readb = CAT(low,_readb), \ | ||
84 | .mv_readw = CAT(low,_readw), \ | ||
85 | .mv_readl = CAT(low,_readl), \ | ||
86 | .mv_readq = CAT(low,_readq), \ | ||
87 | .mv_writeb = CAT(low,_writeb), \ | ||
88 | .mv_writew = CAT(low,_writew), \ | ||
89 | .mv_writel = CAT(low,_writel), \ | ||
90 | .mv_writeq = CAT(low,_writeq), \ | ||
91 | .mv_ioportmap = CAT(low,_ioportmap), \ | ||
92 | .mv_ioremap = CAT(low,_ioremap), \ | ||
93 | .mv_iounmap = CAT(low,_iounmap), \ | ||
94 | .mv_is_ioaddr = CAT(low,_is_ioaddr), \ | ||
95 | .mv_is_mmio = CAT(low,_is_mmio) \ | ||
96 | |||
97 | #define IO(UP,low) \ | ||
98 | IO_LITE(UP,low), \ | ||
99 | .pci_ops = &CAT(low,_pci_ops), \ | ||
100 | .mv_pci_tbi = CAT(low,_pci_tbi) | ||
101 | |||
102 | #define DO_APECS_IO IO(APECS,apecs) | ||
103 | #define DO_CIA_IO IO(CIA,cia) | ||
104 | #define DO_IRONGATE_IO IO(IRONGATE,irongate) | ||
105 | #define DO_LCA_IO IO(LCA,lca) | ||
106 | #define DO_MARVEL_IO IO(MARVEL,marvel) | ||
107 | #define DO_MCPCIA_IO IO(MCPCIA,mcpcia) | ||
108 | #define DO_POLARIS_IO IO(POLARIS,polaris) | ||
109 | #define DO_T2_IO IO(T2,t2) | ||
110 | #define DO_TSUNAMI_IO IO(TSUNAMI,tsunami) | ||
111 | #define DO_TITAN_IO IO(TITAN,titan) | ||
112 | #define DO_WILDFIRE_IO IO(WILDFIRE,wildfire) | ||
113 | |||
114 | #define DO_PYXIS_IO IO_LITE(CIA,cia_bwx), \ | ||
115 | .pci_ops = &cia_pci_ops, \ | ||
116 | .mv_pci_tbi = cia_pci_tbi | ||
117 | |||
118 | /* | ||
119 | * In a GENERIC kernel, we have lots of these vectors floating about, | ||
120 | * all but one of which we want to go away. In a non-GENERIC kernel, | ||
121 | * we want only one, ever. | ||
122 | * | ||
123 | * Accomplish this in the GENERIC kernel by putting all of the vectors | ||
124 | * in the .init.data section where they'll go away. We'll copy the | ||
125 | * one we want to the real alpha_mv vector in setup_arch. | ||
126 | * | ||
127 | * Accomplish this in a non-GENERIC kernel by ifdef'ing out all but | ||
128 | * one of the vectors, which will not reside in .init.data. We then | ||
129 | * alias this one vector to alpha_mv, so no copy is needed. | ||
130 | * | ||
131 | * Upshot: set __initdata to nothing for non-GENERIC kernels. | ||
132 | */ | ||
133 | |||
134 | #ifdef CONFIG_ALPHA_GENERIC | ||
135 | #define __initmv __initdata | ||
136 | #define ALIAS_MV(x) | ||
137 | #else | ||
138 | #define __initmv | ||
139 | |||
140 | /* GCC actually has a syntax for defining aliases, but is under some | ||
141 | delusion that you shouldn't be able to declare it extern somewhere | ||
142 | else beforehand. Fine. We'll do it ourselves. */ | ||
143 | #if 0 | ||
144 | #define ALIAS_MV(system) \ | ||
145 | struct alpha_machine_vector alpha_mv __attribute__((alias(#system "_mv"))); | ||
146 | #else | ||
147 | #define ALIAS_MV(system) \ | ||
148 | asm(".global alpha_mv\nalpha_mv = " #system "_mv"); | ||
149 | #endif | ||
150 | #endif /* GENERIC */ | ||
diff --git a/arch/alpha/kernel/module.c b/arch/alpha/kernel/module.c new file mode 100644 index 000000000000..fc271e316a38 --- /dev/null +++ b/arch/alpha/kernel/module.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* Kernel module help for Alpha. | ||
2 | Copyright (C) 2002 Richard Henderson. | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or | ||
7 | (at your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | #include <linux/moduleloader.h> | ||
19 | #include <linux/elf.h> | ||
20 | #include <linux/vmalloc.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | #if 0 | ||
27 | #define DEBUGP printk | ||
28 | #else | ||
29 | #define DEBUGP(fmt...) | ||
30 | #endif | ||
31 | |||
32 | void * | ||
33 | module_alloc(unsigned long size) | ||
34 | { | ||
35 | if (size == 0) | ||
36 | return NULL; | ||
37 | return vmalloc(size); | ||
38 | } | ||
39 | |||
40 | void | ||
41 | module_free(struct module *mod, void *module_region) | ||
42 | { | ||
43 | vfree(module_region); | ||
44 | } | ||
45 | |||
46 | /* Allocate the GOT at the end of the core sections. */ | ||
47 | |||
48 | struct got_entry { | ||
49 | struct got_entry *next; | ||
50 | Elf64_Addr r_offset; | ||
51 | int got_offset; | ||
52 | }; | ||
53 | |||
54 | static inline void | ||
55 | process_reloc_for_got(Elf64_Rela *rela, | ||
56 | struct got_entry *chains, Elf64_Xword *poffset) | ||
57 | { | ||
58 | unsigned long r_sym = ELF64_R_SYM (rela->r_info); | ||
59 | unsigned long r_type = ELF64_R_TYPE (rela->r_info); | ||
60 | Elf64_Addr r_offset = rela->r_offset; | ||
61 | struct got_entry *g; | ||
62 | |||
63 | if (r_type != R_ALPHA_LITERAL) | ||
64 | return; | ||
65 | |||
66 | for (g = chains + r_sym; g ; g = g->next) | ||
67 | if (g->r_offset == r_offset) { | ||
68 | if (g->got_offset == 0) { | ||
69 | g->got_offset = *poffset; | ||
70 | *poffset += 8; | ||
71 | } | ||
72 | goto found_entry; | ||
73 | } | ||
74 | |||
75 | g = kmalloc (sizeof (*g), GFP_KERNEL); | ||
76 | g->next = chains[r_sym].next; | ||
77 | g->r_offset = r_offset; | ||
78 | g->got_offset = *poffset; | ||
79 | *poffset += 8; | ||
80 | chains[r_sym].next = g; | ||
81 | |||
82 | found_entry: | ||
83 | /* Trick: most of the ELF64_R_TYPE field is unused. There are | ||
84 | 42 valid relocation types, and a 32-bit field. Co-opt the | ||
85 | bits above 256 to store the got offset for this reloc. */ | ||
86 | rela->r_info |= g->got_offset << 8; | ||
87 | } | ||
88 | |||
89 | int | ||
90 | module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs, | ||
91 | char *secstrings, struct module *me) | ||
92 | { | ||
93 | struct got_entry *chains; | ||
94 | Elf64_Rela *rela; | ||
95 | Elf64_Shdr *esechdrs, *symtab, *s, *got; | ||
96 | unsigned long nsyms, nrela, i; | ||
97 | |||
98 | esechdrs = sechdrs + hdr->e_shnum; | ||
99 | symtab = got = NULL; | ||
100 | |||
101 | /* Find out how large the symbol table is. Allocate one got_entry | ||
102 | head per symbol. Normally this will be enough, but not always. | ||
103 | We'll chain different offsets for the symbol down each head. */ | ||
104 | for (s = sechdrs; s < esechdrs; ++s) | ||
105 | if (s->sh_type == SHT_SYMTAB) | ||
106 | symtab = s; | ||
107 | else if (!strcmp(".got", secstrings + s->sh_name)) { | ||
108 | got = s; | ||
109 | me->arch.gotsecindex = s - sechdrs; | ||
110 | } | ||
111 | |||
112 | if (!symtab) { | ||
113 | printk(KERN_ERR "module %s: no symbol table\n", me->name); | ||
114 | return -ENOEXEC; | ||
115 | } | ||
116 | if (!got) { | ||
117 | printk(KERN_ERR "module %s: no got section\n", me->name); | ||
118 | return -ENOEXEC; | ||
119 | } | ||
120 | |||
121 | nsyms = symtab->sh_size / sizeof(Elf64_Sym); | ||
122 | chains = kmalloc(nsyms * sizeof(struct got_entry), GFP_KERNEL); | ||
123 | memset(chains, 0, nsyms * sizeof(struct got_entry)); | ||
124 | |||
125 | got->sh_size = 0; | ||
126 | got->sh_addralign = 8; | ||
127 | got->sh_type = SHT_NOBITS; | ||
128 | |||
129 | /* Examine all LITERAL relocations to find out what GOT entries | ||
130 | are required. This sizes the GOT section as well. */ | ||
131 | for (s = sechdrs; s < esechdrs; ++s) | ||
132 | if (s->sh_type == SHT_RELA) { | ||
133 | nrela = s->sh_size / sizeof(Elf64_Rela); | ||
134 | rela = (void *)hdr + s->sh_offset; | ||
135 | for (i = 0; i < nrela; ++i) | ||
136 | process_reloc_for_got(rela+i, chains, | ||
137 | &got->sh_size); | ||
138 | } | ||
139 | |||
140 | /* Free the memory we allocated. */ | ||
141 | for (i = 0; i < nsyms; ++i) { | ||
142 | struct got_entry *g, *n; | ||
143 | for (g = chains[i].next; g ; g = n) { | ||
144 | n = g->next; | ||
145 | kfree(g); | ||
146 | } | ||
147 | } | ||
148 | kfree(chains); | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | int | ||
154 | apply_relocate(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, | ||
155 | unsigned int relsec, struct module *me) | ||
156 | { | ||
157 | printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); | ||
158 | return -ENOEXEC; | ||
159 | } | ||
160 | |||
161 | int | ||
162 | apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, | ||
163 | unsigned int symindex, unsigned int relsec, | ||
164 | struct module *me) | ||
165 | { | ||
166 | Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr; | ||
167 | unsigned long i, n = sechdrs[relsec].sh_size / sizeof(*rela); | ||
168 | Elf64_Sym *symtab, *sym; | ||
169 | void *base, *location; | ||
170 | unsigned long got, gp; | ||
171 | |||
172 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
173 | sechdrs[relsec].sh_info); | ||
174 | |||
175 | base = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr; | ||
176 | symtab = (Elf64_Sym *)sechdrs[symindex].sh_addr; | ||
177 | |||
178 | /* The small sections were sorted to the end of the segment. | ||
179 | The following should definitely cover them. */ | ||
180 | gp = (u64)me->module_core + me->core_size - 0x8000; | ||
181 | got = sechdrs[me->arch.gotsecindex].sh_addr; | ||
182 | |||
183 | for (i = 0; i < n; i++) { | ||
184 | unsigned long r_sym = ELF64_R_SYM (rela[i].r_info); | ||
185 | unsigned long r_type = ELF64_R_TYPE (rela[i].r_info); | ||
186 | unsigned long r_got_offset = r_type >> 8; | ||
187 | unsigned long value, hi, lo; | ||
188 | r_type &= 0xff; | ||
189 | |||
190 | /* This is where to make the change. */ | ||
191 | location = base + rela[i].r_offset; | ||
192 | |||
193 | /* This is the symbol it is referring to. Note that all | ||
194 | unresolved symbols have been resolved. */ | ||
195 | sym = symtab + r_sym; | ||
196 | value = sym->st_value + rela[i].r_addend; | ||
197 | |||
198 | switch (r_type) { | ||
199 | case R_ALPHA_NONE: | ||
200 | break; | ||
201 | case R_ALPHA_REFQUAD: | ||
202 | /* BUG() can produce misaligned relocations. */ | ||
203 | ((u32 *)location)[0] = value; | ||
204 | ((u32 *)location)[1] = value >> 32; | ||
205 | break; | ||
206 | case R_ALPHA_GPREL32: | ||
207 | value -= gp; | ||
208 | if ((int)value != value) | ||
209 | goto reloc_overflow; | ||
210 | *(u32 *)location = value; | ||
211 | break; | ||
212 | case R_ALPHA_LITERAL: | ||
213 | hi = got + r_got_offset; | ||
214 | lo = hi - gp; | ||
215 | if ((short)lo != lo) | ||
216 | goto reloc_overflow; | ||
217 | *(u16 *)location = lo; | ||
218 | *(u64 *)hi = value; | ||
219 | break; | ||
220 | case R_ALPHA_LITUSE: | ||
221 | break; | ||
222 | case R_ALPHA_GPDISP: | ||
223 | value = gp - (u64)location; | ||
224 | lo = (short)value; | ||
225 | hi = (int)(value - lo); | ||
226 | if (hi + lo != value) | ||
227 | goto reloc_overflow; | ||
228 | *(u16 *)location = hi >> 16; | ||
229 | *(u16 *)(location + rela[i].r_addend) = lo; | ||
230 | break; | ||
231 | case R_ALPHA_BRSGP: | ||
232 | /* BRSGP is only allowed to bind to local symbols. | ||
233 | If the section is undef, this means that the | ||
234 | value was resolved from somewhere else. */ | ||
235 | if (sym->st_shndx == SHN_UNDEF) | ||
236 | goto reloc_overflow; | ||
237 | if ((sym->st_other & STO_ALPHA_STD_GPLOAD) == | ||
238 | STO_ALPHA_STD_GPLOAD) | ||
239 | /* Omit the prologue. */ | ||
240 | value += 8; | ||
241 | /* FALLTHRU */ | ||
242 | case R_ALPHA_BRADDR: | ||
243 | value -= (u64)location + 4; | ||
244 | if (value & 3) | ||
245 | goto reloc_overflow; | ||
246 | value = (long)value >> 2; | ||
247 | if (value + (1<<21) >= 1<<22) | ||
248 | goto reloc_overflow; | ||
249 | value &= 0x1fffff; | ||
250 | value |= *(u32 *)location & ~0x1fffff; | ||
251 | *(u32 *)location = value; | ||
252 | break; | ||
253 | case R_ALPHA_HINT: | ||
254 | break; | ||
255 | case R_ALPHA_SREL32: | ||
256 | value -= (u64)location; | ||
257 | if ((int)value != value) | ||
258 | goto reloc_overflow; | ||
259 | *(u32 *)location = value; | ||
260 | break; | ||
261 | case R_ALPHA_SREL64: | ||
262 | value -= (u64)location; | ||
263 | *(u64 *)location = value; | ||
264 | break; | ||
265 | case R_ALPHA_GPRELHIGH: | ||
266 | value = (long)(value - gp + 0x8000) >> 16; | ||
267 | if ((short) value != value) | ||
268 | goto reloc_overflow; | ||
269 | *(u16 *)location = value; | ||
270 | break; | ||
271 | case R_ALPHA_GPRELLOW: | ||
272 | value -= gp; | ||
273 | *(u16 *)location = value; | ||
274 | break; | ||
275 | case R_ALPHA_GPREL16: | ||
276 | value -= gp; | ||
277 | if ((short) value != value) | ||
278 | goto reloc_overflow; | ||
279 | *(u16 *)location = value; | ||
280 | break; | ||
281 | default: | ||
282 | printk(KERN_ERR "module %s: Unknown relocation: %lu\n", | ||
283 | me->name, r_type); | ||
284 | return -ENOEXEC; | ||
285 | reloc_overflow: | ||
286 | if (ELF64_ST_TYPE (sym->st_info) == STT_SECTION) | ||
287 | printk(KERN_ERR | ||
288 | "module %s: Relocation overflow vs section %d\n", | ||
289 | me->name, sym->st_shndx); | ||
290 | else | ||
291 | printk(KERN_ERR | ||
292 | "module %s: Relocation overflow vs %s\n", | ||
293 | me->name, strtab + sym->st_name); | ||
294 | return -ENOEXEC; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | int | ||
302 | module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, | ||
303 | struct module *me) | ||
304 | { | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | void | ||
309 | module_arch_cleanup(struct module *mod) | ||
310 | { | ||
311 | } | ||
diff --git a/arch/alpha/kernel/ns87312.c b/arch/alpha/kernel/ns87312.c new file mode 100644 index 000000000000..342b56d24c20 --- /dev/null +++ b/arch/alpha/kernel/ns87312.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/ns87312.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/init.h> | ||
6 | #include <asm/io.h> | ||
7 | #include "proto.h" | ||
8 | |||
9 | |||
10 | /* | ||
11 | * The SRM console *disables* the IDE interface, this code ensures it's | ||
12 | * enabled. | ||
13 | * | ||
14 | * This code bangs on a control register of the 87312 Super I/O chip | ||
15 | * that implements parallel port/serial ports/IDE/FDI. Depending on | ||
16 | * the motherboard, the Super I/O chip can be configured through a | ||
17 | * pair of registers that are located either at I/O ports 0x26e/0x26f | ||
18 | * or 0x398/0x399. Unfortunately, autodetecting which base address is | ||
19 | * in use works only once (right after a reset). The Super I/O chip | ||
20 | * has the additional quirk that configuration register data must be | ||
21 | * written twice (I believe this is a safety feature to prevent | ||
22 | * accidental modification---fun, isn't it?). | ||
23 | */ | ||
24 | |||
25 | void __init | ||
26 | ns87312_enable_ide(long ide_base) | ||
27 | { | ||
28 | int data; | ||
29 | unsigned long flags; | ||
30 | |||
31 | local_irq_save(flags); | ||
32 | outb(0, ide_base); /* set the index register for reg #0 */ | ||
33 | data = inb(ide_base+1); /* read the current contents */ | ||
34 | outb(0, ide_base); /* set the index register for reg #0 */ | ||
35 | outb(data | 0x40, ide_base+1); /* turn on IDE */ | ||
36 | outb(data | 0x40, ide_base+1); /* turn on IDE, really! */ | ||
37 | local_irq_restore(flags); | ||
38 | } | ||
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c new file mode 100644 index 000000000000..b5d0fd2bb10a --- /dev/null +++ b/arch/alpha/kernel/osf_sys.c | |||
@@ -0,0 +1,1345 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/osf_sys.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This file handles some of the stranger OSF/1 system call interfaces. | ||
9 | * Some of the system calls expect a non-C calling standard, others have | ||
10 | * special parameter blocks.. | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/smp.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/stddef.h> | ||
20 | #include <linux/syscalls.h> | ||
21 | #include <linux/unistd.h> | ||
22 | #include <linux/ptrace.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/user.h> | ||
25 | #include <linux/a.out.h> | ||
26 | #include <linux/utsname.h> | ||
27 | #include <linux/time.h> | ||
28 | #include <linux/timex.h> | ||
29 | #include <linux/major.h> | ||
30 | #include <linux/stat.h> | ||
31 | #include <linux/mman.h> | ||
32 | #include <linux/shm.h> | ||
33 | #include <linux/poll.h> | ||
34 | #include <linux/file.h> | ||
35 | #include <linux/types.h> | ||
36 | #include <linux/ipc.h> | ||
37 | #include <linux/namei.h> | ||
38 | #include <linux/uio.h> | ||
39 | #include <linux/vfs.h> | ||
40 | |||
41 | #include <asm/fpu.h> | ||
42 | #include <asm/io.h> | ||
43 | #include <asm/uaccess.h> | ||
44 | #include <asm/system.h> | ||
45 | #include <asm/sysinfo.h> | ||
46 | #include <asm/hwrpb.h> | ||
47 | #include <asm/processor.h> | ||
48 | |||
49 | extern int do_pipe(int *); | ||
50 | |||
51 | /* | ||
52 | * Brk needs to return an error. Still support Linux's brk(0) query idiom, | ||
53 | * which OSF programs just shouldn't be doing. We're still not quite | ||
54 | * identical to OSF as we don't return 0 on success, but doing otherwise | ||
55 | * would require changes to libc. Hopefully this is good enough. | ||
56 | */ | ||
57 | asmlinkage unsigned long | ||
58 | osf_brk(unsigned long brk) | ||
59 | { | ||
60 | unsigned long retval = sys_brk(brk); | ||
61 | if (brk && brk != retval) | ||
62 | retval = -ENOMEM; | ||
63 | return retval; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * This is pure guess-work.. | ||
68 | */ | ||
69 | asmlinkage int | ||
70 | osf_set_program_attributes(unsigned long text_start, unsigned long text_len, | ||
71 | unsigned long bss_start, unsigned long bss_len) | ||
72 | { | ||
73 | struct mm_struct *mm; | ||
74 | |||
75 | lock_kernel(); | ||
76 | mm = current->mm; | ||
77 | mm->end_code = bss_start + bss_len; | ||
78 | mm->brk = bss_start + bss_len; | ||
79 | #if 0 | ||
80 | printk("set_program_attributes(%lx %lx %lx %lx)\n", | ||
81 | text_start, text_len, bss_start, bss_len); | ||
82 | #endif | ||
83 | unlock_kernel(); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * OSF/1 directory handling functions... | ||
89 | * | ||
90 | * The "getdents()" interface is much more sane: the "basep" stuff is | ||
91 | * braindamage (it can't really handle filesystems where the directory | ||
92 | * offset differences aren't the same as "d_reclen"). | ||
93 | */ | ||
94 | #define NAME_OFFSET offsetof (struct osf_dirent, d_name) | ||
95 | #define ROUND_UP(x) (((x)+3) & ~3) | ||
96 | |||
97 | struct osf_dirent { | ||
98 | unsigned int d_ino; | ||
99 | unsigned short d_reclen; | ||
100 | unsigned short d_namlen; | ||
101 | char d_name[1]; | ||
102 | }; | ||
103 | |||
104 | struct osf_dirent_callback { | ||
105 | struct osf_dirent __user *dirent; | ||
106 | long __user *basep; | ||
107 | unsigned int count; | ||
108 | int error; | ||
109 | }; | ||
110 | |||
111 | static int | ||
112 | osf_filldir(void *__buf, const char *name, int namlen, loff_t offset, | ||
113 | ino_t ino, unsigned int d_type) | ||
114 | { | ||
115 | struct osf_dirent __user *dirent; | ||
116 | struct osf_dirent_callback *buf = (struct osf_dirent_callback *) __buf; | ||
117 | unsigned int reclen = ROUND_UP(NAME_OFFSET + namlen + 1); | ||
118 | |||
119 | buf->error = -EINVAL; /* only used if we fail */ | ||
120 | if (reclen > buf->count) | ||
121 | return -EINVAL; | ||
122 | if (buf->basep) { | ||
123 | if (put_user(offset, buf->basep)) | ||
124 | return -EFAULT; | ||
125 | buf->basep = NULL; | ||
126 | } | ||
127 | dirent = buf->dirent; | ||
128 | put_user(ino, &dirent->d_ino); | ||
129 | put_user(namlen, &dirent->d_namlen); | ||
130 | put_user(reclen, &dirent->d_reclen); | ||
131 | if (copy_to_user(dirent->d_name, name, namlen) || | ||
132 | put_user(0, dirent->d_name + namlen)) | ||
133 | return -EFAULT; | ||
134 | dirent = (void __user *)dirent + reclen; | ||
135 | buf->dirent = dirent; | ||
136 | buf->count -= reclen; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | asmlinkage int | ||
141 | osf_getdirentries(unsigned int fd, struct osf_dirent __user *dirent, | ||
142 | unsigned int count, long __user *basep) | ||
143 | { | ||
144 | int error; | ||
145 | struct file *file; | ||
146 | struct osf_dirent_callback buf; | ||
147 | |||
148 | error = -EBADF; | ||
149 | file = fget(fd); | ||
150 | if (!file) | ||
151 | goto out; | ||
152 | |||
153 | buf.dirent = dirent; | ||
154 | buf.basep = basep; | ||
155 | buf.count = count; | ||
156 | buf.error = 0; | ||
157 | |||
158 | error = vfs_readdir(file, osf_filldir, &buf); | ||
159 | if (error < 0) | ||
160 | goto out_putf; | ||
161 | |||
162 | error = buf.error; | ||
163 | if (count != buf.count) | ||
164 | error = count - buf.count; | ||
165 | |||
166 | out_putf: | ||
167 | fput(file); | ||
168 | out: | ||
169 | return error; | ||
170 | } | ||
171 | |||
172 | #undef ROUND_UP | ||
173 | #undef NAME_OFFSET | ||
174 | |||
175 | asmlinkage unsigned long | ||
176 | osf_mmap(unsigned long addr, unsigned long len, unsigned long prot, | ||
177 | unsigned long flags, unsigned long fd, unsigned long off) | ||
178 | { | ||
179 | struct file *file = NULL; | ||
180 | unsigned long ret = -EBADF; | ||
181 | |||
182 | #if 0 | ||
183 | if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED)) | ||
184 | printk("%s: unimplemented OSF mmap flags %04lx\n", | ||
185 | current->comm, flags); | ||
186 | #endif | ||
187 | if (!(flags & MAP_ANONYMOUS)) { | ||
188 | file = fget(fd); | ||
189 | if (!file) | ||
190 | goto out; | ||
191 | } | ||
192 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
193 | down_write(¤t->mm->mmap_sem); | ||
194 | ret = do_mmap(file, addr, len, prot, flags, off); | ||
195 | up_write(¤t->mm->mmap_sem); | ||
196 | if (file) | ||
197 | fput(file); | ||
198 | out: | ||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | |||
203 | /* | ||
204 | * The OSF/1 statfs structure is much larger, but this should | ||
205 | * match the beginning, at least. | ||
206 | */ | ||
207 | struct osf_statfs { | ||
208 | short f_type; | ||
209 | short f_flags; | ||
210 | int f_fsize; | ||
211 | int f_bsize; | ||
212 | int f_blocks; | ||
213 | int f_bfree; | ||
214 | int f_bavail; | ||
215 | int f_files; | ||
216 | int f_ffree; | ||
217 | __kernel_fsid_t f_fsid; | ||
218 | }; | ||
219 | |||
220 | static int | ||
221 | linux_to_osf_statfs(struct kstatfs *linux_stat, struct osf_statfs __user *osf_stat, | ||
222 | unsigned long bufsiz) | ||
223 | { | ||
224 | struct osf_statfs tmp_stat; | ||
225 | |||
226 | tmp_stat.f_type = linux_stat->f_type; | ||
227 | tmp_stat.f_flags = 0; /* mount flags */ | ||
228 | tmp_stat.f_fsize = linux_stat->f_frsize; | ||
229 | tmp_stat.f_bsize = linux_stat->f_bsize; | ||
230 | tmp_stat.f_blocks = linux_stat->f_blocks; | ||
231 | tmp_stat.f_bfree = linux_stat->f_bfree; | ||
232 | tmp_stat.f_bavail = linux_stat->f_bavail; | ||
233 | tmp_stat.f_files = linux_stat->f_files; | ||
234 | tmp_stat.f_ffree = linux_stat->f_ffree; | ||
235 | tmp_stat.f_fsid = linux_stat->f_fsid; | ||
236 | if (bufsiz > sizeof(tmp_stat)) | ||
237 | bufsiz = sizeof(tmp_stat); | ||
238 | return copy_to_user(osf_stat, &tmp_stat, bufsiz) ? -EFAULT : 0; | ||
239 | } | ||
240 | |||
241 | static int | ||
242 | do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer, | ||
243 | unsigned long bufsiz) | ||
244 | { | ||
245 | struct kstatfs linux_stat; | ||
246 | int error = vfs_statfs(dentry->d_inode->i_sb, &linux_stat); | ||
247 | if (!error) | ||
248 | error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz); | ||
249 | return error; | ||
250 | } | ||
251 | |||
252 | asmlinkage int | ||
253 | osf_statfs(char __user *path, struct osf_statfs __user *buffer, unsigned long bufsiz) | ||
254 | { | ||
255 | struct nameidata nd; | ||
256 | int retval; | ||
257 | |||
258 | retval = user_path_walk(path, &nd); | ||
259 | if (!retval) { | ||
260 | retval = do_osf_statfs(nd.dentry, buffer, bufsiz); | ||
261 | path_release(&nd); | ||
262 | } | ||
263 | return retval; | ||
264 | } | ||
265 | |||
266 | asmlinkage int | ||
267 | osf_fstatfs(unsigned long fd, struct osf_statfs __user *buffer, unsigned long bufsiz) | ||
268 | { | ||
269 | struct file *file; | ||
270 | int retval; | ||
271 | |||
272 | retval = -EBADF; | ||
273 | file = fget(fd); | ||
274 | if (file) { | ||
275 | retval = do_osf_statfs(file->f_dentry, buffer, bufsiz); | ||
276 | fput(file); | ||
277 | } | ||
278 | return retval; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * Uhh.. OSF/1 mount parameters aren't exactly obvious.. | ||
283 | * | ||
284 | * Although to be frank, neither are the native Linux/i386 ones.. | ||
285 | */ | ||
286 | struct ufs_args { | ||
287 | char __user *devname; | ||
288 | int flags; | ||
289 | uid_t exroot; | ||
290 | }; | ||
291 | |||
292 | struct cdfs_args { | ||
293 | char __user *devname; | ||
294 | int flags; | ||
295 | uid_t exroot; | ||
296 | |||
297 | /* This has lots more here, which Linux handles with the option block | ||
298 | but I'm too lazy to do the translation into ASCII. */ | ||
299 | }; | ||
300 | |||
301 | struct procfs_args { | ||
302 | char __user *devname; | ||
303 | int flags; | ||
304 | uid_t exroot; | ||
305 | }; | ||
306 | |||
307 | /* | ||
308 | * We can't actually handle ufs yet, so we translate UFS mounts to | ||
309 | * ext2fs mounts. I wouldn't mind a UFS filesystem, but the UFS | ||
310 | * layout is so braindead it's a major headache doing it. | ||
311 | * | ||
312 | * Just how long ago was it written? OTOH our UFS driver may be still | ||
313 | * unhappy with OSF UFS. [CHECKME] | ||
314 | */ | ||
315 | static int | ||
316 | osf_ufs_mount(char *dirname, struct ufs_args __user *args, int flags) | ||
317 | { | ||
318 | int retval; | ||
319 | struct cdfs_args tmp; | ||
320 | char *devname; | ||
321 | |||
322 | retval = -EFAULT; | ||
323 | if (copy_from_user(&tmp, args, sizeof(tmp))) | ||
324 | goto out; | ||
325 | devname = getname(tmp.devname); | ||
326 | retval = PTR_ERR(devname); | ||
327 | if (IS_ERR(devname)) | ||
328 | goto out; | ||
329 | retval = do_mount(devname, dirname, "ext2", flags, NULL); | ||
330 | putname(devname); | ||
331 | out: | ||
332 | return retval; | ||
333 | } | ||
334 | |||
335 | static int | ||
336 | osf_cdfs_mount(char *dirname, struct cdfs_args __user *args, int flags) | ||
337 | { | ||
338 | int retval; | ||
339 | struct cdfs_args tmp; | ||
340 | char *devname; | ||
341 | |||
342 | retval = -EFAULT; | ||
343 | if (copy_from_user(&tmp, args, sizeof(tmp))) | ||
344 | goto out; | ||
345 | devname = getname(tmp.devname); | ||
346 | retval = PTR_ERR(devname); | ||
347 | if (IS_ERR(devname)) | ||
348 | goto out; | ||
349 | retval = do_mount(devname, dirname, "iso9660", flags, NULL); | ||
350 | putname(devname); | ||
351 | out: | ||
352 | return retval; | ||
353 | } | ||
354 | |||
355 | static int | ||
356 | osf_procfs_mount(char *dirname, struct procfs_args __user *args, int flags) | ||
357 | { | ||
358 | struct procfs_args tmp; | ||
359 | |||
360 | if (copy_from_user(&tmp, args, sizeof(tmp))) | ||
361 | return -EFAULT; | ||
362 | |||
363 | return do_mount("", dirname, "proc", flags, NULL); | ||
364 | } | ||
365 | |||
366 | asmlinkage int | ||
367 | osf_mount(unsigned long typenr, char __user *path, int flag, void __user *data) | ||
368 | { | ||
369 | int retval = -EINVAL; | ||
370 | char *name; | ||
371 | |||
372 | lock_kernel(); | ||
373 | |||
374 | name = getname(path); | ||
375 | retval = PTR_ERR(name); | ||
376 | if (IS_ERR(name)) | ||
377 | goto out; | ||
378 | switch (typenr) { | ||
379 | case 1: | ||
380 | retval = osf_ufs_mount(name, data, flag); | ||
381 | break; | ||
382 | case 6: | ||
383 | retval = osf_cdfs_mount(name, data, flag); | ||
384 | break; | ||
385 | case 9: | ||
386 | retval = osf_procfs_mount(name, data, flag); | ||
387 | break; | ||
388 | default: | ||
389 | printk("osf_mount(%ld, %x)\n", typenr, flag); | ||
390 | } | ||
391 | putname(name); | ||
392 | out: | ||
393 | unlock_kernel(); | ||
394 | return retval; | ||
395 | } | ||
396 | |||
397 | asmlinkage int | ||
398 | osf_utsname(char __user *name) | ||
399 | { | ||
400 | int error; | ||
401 | |||
402 | down_read(&uts_sem); | ||
403 | error = -EFAULT; | ||
404 | if (copy_to_user(name + 0, system_utsname.sysname, 32)) | ||
405 | goto out; | ||
406 | if (copy_to_user(name + 32, system_utsname.nodename, 32)) | ||
407 | goto out; | ||
408 | if (copy_to_user(name + 64, system_utsname.release, 32)) | ||
409 | goto out; | ||
410 | if (copy_to_user(name + 96, system_utsname.version, 32)) | ||
411 | goto out; | ||
412 | if (copy_to_user(name + 128, system_utsname.machine, 32)) | ||
413 | goto out; | ||
414 | |||
415 | error = 0; | ||
416 | out: | ||
417 | up_read(&uts_sem); | ||
418 | return error; | ||
419 | } | ||
420 | |||
421 | asmlinkage unsigned long | ||
422 | sys_getpagesize(void) | ||
423 | { | ||
424 | return PAGE_SIZE; | ||
425 | } | ||
426 | |||
427 | asmlinkage unsigned long | ||
428 | sys_getdtablesize(void) | ||
429 | { | ||
430 | return NR_OPEN; | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * For compatibility with OSF/1 only. Use utsname(2) instead. | ||
435 | */ | ||
436 | asmlinkage int | ||
437 | osf_getdomainname(char __user *name, int namelen) | ||
438 | { | ||
439 | unsigned len; | ||
440 | int i; | ||
441 | |||
442 | if (!access_ok(VERIFY_WRITE, name, namelen)) | ||
443 | return -EFAULT; | ||
444 | |||
445 | len = namelen; | ||
446 | if (namelen > 32) | ||
447 | len = 32; | ||
448 | |||
449 | down_read(&uts_sem); | ||
450 | for (i = 0; i < len; ++i) { | ||
451 | __put_user(system_utsname.domainname[i], name + i); | ||
452 | if (system_utsname.domainname[i] == '\0') | ||
453 | break; | ||
454 | } | ||
455 | up_read(&uts_sem); | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | asmlinkage long | ||
461 | osf_shmat(int shmid, void __user *shmaddr, int shmflg) | ||
462 | { | ||
463 | unsigned long raddr; | ||
464 | long err; | ||
465 | |||
466 | err = do_shmat(shmid, shmaddr, shmflg, &raddr); | ||
467 | |||
468 | /* | ||
469 | * This works because all user-level addresses are | ||
470 | * non-negative longs! | ||
471 | */ | ||
472 | return err ? err : (long)raddr; | ||
473 | } | ||
474 | |||
475 | |||
476 | /* | ||
477 | * The following stuff should move into a header file should it ever | ||
478 | * be labeled "officially supported." Right now, there is just enough | ||
479 | * support to avoid applications (such as tar) printing error | ||
480 | * messages. The attributes are not really implemented. | ||
481 | */ | ||
482 | |||
483 | /* | ||
484 | * Values for Property list entry flag | ||
485 | */ | ||
486 | #define PLE_PROPAGATE_ON_COPY 0x1 /* cp(1) will copy entry | ||
487 | by default */ | ||
488 | #define PLE_FLAG_MASK 0x1 /* Valid flag values */ | ||
489 | #define PLE_FLAG_ALL -1 /* All flag value */ | ||
490 | |||
491 | struct proplistname_args { | ||
492 | unsigned int pl_mask; | ||
493 | unsigned int pl_numnames; | ||
494 | char **pl_names; | ||
495 | }; | ||
496 | |||
497 | union pl_args { | ||
498 | struct setargs { | ||
499 | char __user *path; | ||
500 | long follow; | ||
501 | long nbytes; | ||
502 | char __user *buf; | ||
503 | } set; | ||
504 | struct fsetargs { | ||
505 | long fd; | ||
506 | long nbytes; | ||
507 | char __user *buf; | ||
508 | } fset; | ||
509 | struct getargs { | ||
510 | char __user *path; | ||
511 | long follow; | ||
512 | struct proplistname_args __user *name_args; | ||
513 | long nbytes; | ||
514 | char __user *buf; | ||
515 | int __user *min_buf_size; | ||
516 | } get; | ||
517 | struct fgetargs { | ||
518 | long fd; | ||
519 | struct proplistname_args __user *name_args; | ||
520 | long nbytes; | ||
521 | char __user *buf; | ||
522 | int __user *min_buf_size; | ||
523 | } fget; | ||
524 | struct delargs { | ||
525 | char __user *path; | ||
526 | long follow; | ||
527 | struct proplistname_args __user *name_args; | ||
528 | } del; | ||
529 | struct fdelargs { | ||
530 | long fd; | ||
531 | struct proplistname_args __user *name_args; | ||
532 | } fdel; | ||
533 | }; | ||
534 | |||
535 | enum pl_code { | ||
536 | PL_SET = 1, PL_FSET = 2, | ||
537 | PL_GET = 3, PL_FGET = 4, | ||
538 | PL_DEL = 5, PL_FDEL = 6 | ||
539 | }; | ||
540 | |||
541 | asmlinkage long | ||
542 | osf_proplist_syscall(enum pl_code code, union pl_args __user *args) | ||
543 | { | ||
544 | long error; | ||
545 | int __user *min_buf_size_ptr; | ||
546 | |||
547 | lock_kernel(); | ||
548 | switch (code) { | ||
549 | case PL_SET: | ||
550 | if (get_user(error, &args->set.nbytes)) | ||
551 | error = -EFAULT; | ||
552 | break; | ||
553 | case PL_FSET: | ||
554 | if (get_user(error, &args->fset.nbytes)) | ||
555 | error = -EFAULT; | ||
556 | break; | ||
557 | case PL_GET: | ||
558 | error = get_user(min_buf_size_ptr, &args->get.min_buf_size); | ||
559 | if (error) | ||
560 | break; | ||
561 | error = put_user(0, min_buf_size_ptr); | ||
562 | break; | ||
563 | case PL_FGET: | ||
564 | error = get_user(min_buf_size_ptr, &args->fget.min_buf_size); | ||
565 | if (error) | ||
566 | break; | ||
567 | error = put_user(0, min_buf_size_ptr); | ||
568 | break; | ||
569 | case PL_DEL: | ||
570 | case PL_FDEL: | ||
571 | error = 0; | ||
572 | break; | ||
573 | default: | ||
574 | error = -EOPNOTSUPP; | ||
575 | break; | ||
576 | }; | ||
577 | unlock_kernel(); | ||
578 | return error; | ||
579 | } | ||
580 | |||
581 | asmlinkage int | ||
582 | osf_sigstack(struct sigstack __user *uss, struct sigstack __user *uoss) | ||
583 | { | ||
584 | unsigned long usp = rdusp(); | ||
585 | unsigned long oss_sp = current->sas_ss_sp + current->sas_ss_size; | ||
586 | unsigned long oss_os = on_sig_stack(usp); | ||
587 | int error; | ||
588 | |||
589 | if (uss) { | ||
590 | void __user *ss_sp; | ||
591 | |||
592 | error = -EFAULT; | ||
593 | if (get_user(ss_sp, &uss->ss_sp)) | ||
594 | goto out; | ||
595 | |||
596 | /* If the current stack was set with sigaltstack, don't | ||
597 | swap stacks while we are on it. */ | ||
598 | error = -EPERM; | ||
599 | if (current->sas_ss_sp && on_sig_stack(usp)) | ||
600 | goto out; | ||
601 | |||
602 | /* Since we don't know the extent of the stack, and we don't | ||
603 | track onstack-ness, but rather calculate it, we must | ||
604 | presume a size. Ho hum this interface is lossy. */ | ||
605 | current->sas_ss_sp = (unsigned long)ss_sp - SIGSTKSZ; | ||
606 | current->sas_ss_size = SIGSTKSZ; | ||
607 | } | ||
608 | |||
609 | if (uoss) { | ||
610 | error = -EFAULT; | ||
611 | if (! access_ok(VERIFY_WRITE, uoss, sizeof(*uoss)) | ||
612 | || __put_user(oss_sp, &uoss->ss_sp) | ||
613 | || __put_user(oss_os, &uoss->ss_onstack)) | ||
614 | goto out; | ||
615 | } | ||
616 | |||
617 | error = 0; | ||
618 | out: | ||
619 | return error; | ||
620 | } | ||
621 | |||
622 | asmlinkage long | ||
623 | osf_sysinfo(int command, char __user *buf, long count) | ||
624 | { | ||
625 | static char * sysinfo_table[] = { | ||
626 | system_utsname.sysname, | ||
627 | system_utsname.nodename, | ||
628 | system_utsname.release, | ||
629 | system_utsname.version, | ||
630 | system_utsname.machine, | ||
631 | "alpha", /* instruction set architecture */ | ||
632 | "dummy", /* hardware serial number */ | ||
633 | "dummy", /* hardware manufacturer */ | ||
634 | "dummy", /* secure RPC domain */ | ||
635 | }; | ||
636 | unsigned long offset; | ||
637 | char *res; | ||
638 | long len, err = -EINVAL; | ||
639 | |||
640 | offset = command-1; | ||
641 | if (offset >= sizeof(sysinfo_table)/sizeof(char *)) { | ||
642 | /* Digital UNIX has a few unpublished interfaces here */ | ||
643 | printk("sysinfo(%d)", command); | ||
644 | goto out; | ||
645 | } | ||
646 | |||
647 | down_read(&uts_sem); | ||
648 | res = sysinfo_table[offset]; | ||
649 | len = strlen(res)+1; | ||
650 | if (len > count) | ||
651 | len = count; | ||
652 | if (copy_to_user(buf, res, len)) | ||
653 | err = -EFAULT; | ||
654 | else | ||
655 | err = 0; | ||
656 | up_read(&uts_sem); | ||
657 | out: | ||
658 | return err; | ||
659 | } | ||
660 | |||
661 | asmlinkage unsigned long | ||
662 | osf_getsysinfo(unsigned long op, void __user *buffer, unsigned long nbytes, | ||
663 | int __user *start, void __user *arg) | ||
664 | { | ||
665 | unsigned long w; | ||
666 | struct percpu_struct *cpu; | ||
667 | |||
668 | switch (op) { | ||
669 | case GSI_IEEE_FP_CONTROL: | ||
670 | /* Return current software fp control & status bits. */ | ||
671 | /* Note that DU doesn't verify available space here. */ | ||
672 | |||
673 | w = current_thread_info()->ieee_state & IEEE_SW_MASK; | ||
674 | w = swcr_update_status(w, rdfpcr()); | ||
675 | if (put_user(w, (unsigned long __user *) buffer)) | ||
676 | return -EFAULT; | ||
677 | return 0; | ||
678 | |||
679 | case GSI_IEEE_STATE_AT_SIGNAL: | ||
680 | /* | ||
681 | * Not sure anybody will ever use this weird stuff. These | ||
682 | * ops can be used (under OSF/1) to set the fpcr that should | ||
683 | * be used when a signal handler starts executing. | ||
684 | */ | ||
685 | break; | ||
686 | |||
687 | case GSI_UACPROC: | ||
688 | if (nbytes < sizeof(unsigned int)) | ||
689 | return -EINVAL; | ||
690 | w = (current_thread_info()->flags >> UAC_SHIFT) & UAC_BITMASK; | ||
691 | if (put_user(w, (unsigned int __user *)buffer)) | ||
692 | return -EFAULT; | ||
693 | return 1; | ||
694 | |||
695 | case GSI_PROC_TYPE: | ||
696 | if (nbytes < sizeof(unsigned long)) | ||
697 | return -EINVAL; | ||
698 | cpu = (struct percpu_struct*) | ||
699 | ((char*)hwrpb + hwrpb->processor_offset); | ||
700 | w = cpu->type; | ||
701 | if (put_user(w, (unsigned long __user*)buffer)) | ||
702 | return -EFAULT; | ||
703 | return 1; | ||
704 | |||
705 | case GSI_GET_HWRPB: | ||
706 | if (nbytes < sizeof(*hwrpb)) | ||
707 | return -EINVAL; | ||
708 | if (copy_to_user(buffer, hwrpb, nbytes) != 0) | ||
709 | return -EFAULT; | ||
710 | return 1; | ||
711 | |||
712 | default: | ||
713 | break; | ||
714 | } | ||
715 | |||
716 | return -EOPNOTSUPP; | ||
717 | } | ||
718 | |||
719 | asmlinkage unsigned long | ||
720 | osf_setsysinfo(unsigned long op, void __user *buffer, unsigned long nbytes, | ||
721 | int __user *start, void __user *arg) | ||
722 | { | ||
723 | switch (op) { | ||
724 | case SSI_IEEE_FP_CONTROL: { | ||
725 | unsigned long swcr, fpcr; | ||
726 | unsigned int *state; | ||
727 | |||
728 | /* | ||
729 | * Alpha Architecture Handbook 4.7.7.3: | ||
730 | * To be fully IEEE compiant, we must track the current IEEE | ||
731 | * exception state in software, because spurrious bits can be | ||
732 | * set in the trap shadow of a software-complete insn. | ||
733 | */ | ||
734 | |||
735 | if (get_user(swcr, (unsigned long __user *)buffer)) | ||
736 | return -EFAULT; | ||
737 | state = ¤t_thread_info()->ieee_state; | ||
738 | |||
739 | /* Update softare trap enable bits. */ | ||
740 | *state = (*state & ~IEEE_SW_MASK) | (swcr & IEEE_SW_MASK); | ||
741 | |||
742 | /* Update the real fpcr. */ | ||
743 | fpcr = rdfpcr() & FPCR_DYN_MASK; | ||
744 | fpcr |= ieee_swcr_to_fpcr(swcr); | ||
745 | wrfpcr(fpcr); | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | case SSI_IEEE_RAISE_EXCEPTION: { | ||
751 | unsigned long exc, swcr, fpcr, fex; | ||
752 | unsigned int *state; | ||
753 | |||
754 | if (get_user(exc, (unsigned long __user *)buffer)) | ||
755 | return -EFAULT; | ||
756 | state = ¤t_thread_info()->ieee_state; | ||
757 | exc &= IEEE_STATUS_MASK; | ||
758 | |||
759 | /* Update softare trap enable bits. */ | ||
760 | swcr = (*state & IEEE_SW_MASK) | exc; | ||
761 | *state |= exc; | ||
762 | |||
763 | /* Update the real fpcr. */ | ||
764 | fpcr = rdfpcr(); | ||
765 | fpcr |= ieee_swcr_to_fpcr(swcr); | ||
766 | wrfpcr(fpcr); | ||
767 | |||
768 | /* If any exceptions set by this call, and are unmasked, | ||
769 | send a signal. Old exceptions are not signaled. */ | ||
770 | fex = (exc >> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr; | ||
771 | if (fex) { | ||
772 | siginfo_t info; | ||
773 | int si_code = 0; | ||
774 | |||
775 | if (fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND; | ||
776 | if (fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES; | ||
777 | if (fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND; | ||
778 | if (fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF; | ||
779 | if (fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV; | ||
780 | if (fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV; | ||
781 | |||
782 | info.si_signo = SIGFPE; | ||
783 | info.si_errno = 0; | ||
784 | info.si_code = si_code; | ||
785 | info.si_addr = NULL; /* FIXME */ | ||
786 | send_sig_info(SIGFPE, &info, current); | ||
787 | } | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | case SSI_IEEE_STATE_AT_SIGNAL: | ||
792 | case SSI_IEEE_IGNORE_STATE_AT_SIGNAL: | ||
793 | /* | ||
794 | * Not sure anybody will ever use this weird stuff. These | ||
795 | * ops can be used (under OSF/1) to set the fpcr that should | ||
796 | * be used when a signal handler starts executing. | ||
797 | */ | ||
798 | break; | ||
799 | |||
800 | case SSI_NVPAIRS: { | ||
801 | unsigned long v, w, i; | ||
802 | unsigned int old, new; | ||
803 | |||
804 | for (i = 0; i < nbytes; ++i) { | ||
805 | |||
806 | if (get_user(v, 2*i + (unsigned int __user *)buffer)) | ||
807 | return -EFAULT; | ||
808 | if (get_user(w, 2*i + 1 + (unsigned int __user *)buffer)) | ||
809 | return -EFAULT; | ||
810 | switch (v) { | ||
811 | case SSIN_UACPROC: | ||
812 | again: | ||
813 | old = current_thread_info()->flags; | ||
814 | new = old & ~(UAC_BITMASK << UAC_SHIFT); | ||
815 | new = new | (w & UAC_BITMASK) << UAC_SHIFT; | ||
816 | if (cmpxchg(¤t_thread_info()->flags, | ||
817 | old, new) != old) | ||
818 | goto again; | ||
819 | break; | ||
820 | |||
821 | default: | ||
822 | return -EOPNOTSUPP; | ||
823 | } | ||
824 | } | ||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | default: | ||
829 | break; | ||
830 | } | ||
831 | |||
832 | return -EOPNOTSUPP; | ||
833 | } | ||
834 | |||
835 | /* Translations due to the fact that OSF's time_t is an int. Which | ||
836 | affects all sorts of things, like timeval and itimerval. */ | ||
837 | |||
838 | extern struct timezone sys_tz; | ||
839 | extern int do_adjtimex(struct timex *); | ||
840 | |||
841 | struct timeval32 | ||
842 | { | ||
843 | int tv_sec, tv_usec; | ||
844 | }; | ||
845 | |||
846 | struct itimerval32 | ||
847 | { | ||
848 | struct timeval32 it_interval; | ||
849 | struct timeval32 it_value; | ||
850 | }; | ||
851 | |||
852 | static inline long | ||
853 | get_tv32(struct timeval *o, struct timeval32 __user *i) | ||
854 | { | ||
855 | return (!access_ok(VERIFY_READ, i, sizeof(*i)) || | ||
856 | (__get_user(o->tv_sec, &i->tv_sec) | | ||
857 | __get_user(o->tv_usec, &i->tv_usec))); | ||
858 | } | ||
859 | |||
860 | static inline long | ||
861 | put_tv32(struct timeval32 __user *o, struct timeval *i) | ||
862 | { | ||
863 | return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || | ||
864 | (__put_user(i->tv_sec, &o->tv_sec) | | ||
865 | __put_user(i->tv_usec, &o->tv_usec))); | ||
866 | } | ||
867 | |||
868 | static inline long | ||
869 | get_it32(struct itimerval *o, struct itimerval32 __user *i) | ||
870 | { | ||
871 | return (!access_ok(VERIFY_READ, i, sizeof(*i)) || | ||
872 | (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | | ||
873 | __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | | ||
874 | __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | | ||
875 | __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); | ||
876 | } | ||
877 | |||
878 | static inline long | ||
879 | put_it32(struct itimerval32 __user *o, struct itimerval *i) | ||
880 | { | ||
881 | return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || | ||
882 | (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | | ||
883 | __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | | ||
884 | __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | | ||
885 | __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); | ||
886 | } | ||
887 | |||
888 | static inline void | ||
889 | jiffies_to_timeval32(unsigned long jiffies, struct timeval32 *value) | ||
890 | { | ||
891 | value->tv_usec = (jiffies % HZ) * (1000000L / HZ); | ||
892 | value->tv_sec = jiffies / HZ; | ||
893 | } | ||
894 | |||
895 | asmlinkage int | ||
896 | osf_gettimeofday(struct timeval32 __user *tv, struct timezone __user *tz) | ||
897 | { | ||
898 | if (tv) { | ||
899 | struct timeval ktv; | ||
900 | do_gettimeofday(&ktv); | ||
901 | if (put_tv32(tv, &ktv)) | ||
902 | return -EFAULT; | ||
903 | } | ||
904 | if (tz) { | ||
905 | if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) | ||
906 | return -EFAULT; | ||
907 | } | ||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | asmlinkage int | ||
912 | osf_settimeofday(struct timeval32 __user *tv, struct timezone __user *tz) | ||
913 | { | ||
914 | struct timespec kts; | ||
915 | struct timezone ktz; | ||
916 | |||
917 | if (tv) { | ||
918 | if (get_tv32((struct timeval *)&kts, tv)) | ||
919 | return -EFAULT; | ||
920 | } | ||
921 | if (tz) { | ||
922 | if (copy_from_user(&ktz, tz, sizeof(*tz))) | ||
923 | return -EFAULT; | ||
924 | } | ||
925 | |||
926 | kts.tv_nsec *= 1000; | ||
927 | |||
928 | return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); | ||
929 | } | ||
930 | |||
931 | asmlinkage int | ||
932 | osf_getitimer(int which, struct itimerval32 __user *it) | ||
933 | { | ||
934 | struct itimerval kit; | ||
935 | int error; | ||
936 | |||
937 | error = do_getitimer(which, &kit); | ||
938 | if (!error && put_it32(it, &kit)) | ||
939 | error = -EFAULT; | ||
940 | |||
941 | return error; | ||
942 | } | ||
943 | |||
944 | asmlinkage int | ||
945 | osf_setitimer(int which, struct itimerval32 __user *in, struct itimerval32 __user *out) | ||
946 | { | ||
947 | struct itimerval kin, kout; | ||
948 | int error; | ||
949 | |||
950 | if (in) { | ||
951 | if (get_it32(&kin, in)) | ||
952 | return -EFAULT; | ||
953 | } else | ||
954 | memset(&kin, 0, sizeof(kin)); | ||
955 | |||
956 | error = do_setitimer(which, &kin, out ? &kout : NULL); | ||
957 | if (error || !out) | ||
958 | return error; | ||
959 | |||
960 | if (put_it32(out, &kout)) | ||
961 | return -EFAULT; | ||
962 | |||
963 | return 0; | ||
964 | |||
965 | } | ||
966 | |||
967 | asmlinkage int | ||
968 | osf_utimes(char __user *filename, struct timeval32 __user *tvs) | ||
969 | { | ||
970 | struct timeval ktvs[2]; | ||
971 | |||
972 | if (tvs) { | ||
973 | if (get_tv32(&ktvs[0], &tvs[0]) || | ||
974 | get_tv32(&ktvs[1], &tvs[1])) | ||
975 | return -EFAULT; | ||
976 | } | ||
977 | |||
978 | return do_utimes(filename, tvs ? ktvs : NULL); | ||
979 | } | ||
980 | |||
981 | #define MAX_SELECT_SECONDS \ | ||
982 | ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) | ||
983 | |||
984 | asmlinkage int | ||
985 | osf_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, | ||
986 | struct timeval32 __user *tvp) | ||
987 | { | ||
988 | fd_set_bits fds; | ||
989 | char *bits; | ||
990 | size_t size; | ||
991 | long timeout; | ||
992 | int ret = -EINVAL; | ||
993 | |||
994 | timeout = MAX_SCHEDULE_TIMEOUT; | ||
995 | if (tvp) { | ||
996 | time_t sec, usec; | ||
997 | |||
998 | if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) | ||
999 | || __get_user(sec, &tvp->tv_sec) | ||
1000 | || __get_user(usec, &tvp->tv_usec)) { | ||
1001 | ret = -EFAULT; | ||
1002 | goto out_nofds; | ||
1003 | } | ||
1004 | |||
1005 | if (sec < 0 || usec < 0) | ||
1006 | goto out_nofds; | ||
1007 | |||
1008 | if ((unsigned long) sec < MAX_SELECT_SECONDS) { | ||
1009 | timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); | ||
1010 | timeout += sec * (unsigned long) HZ; | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | if (n < 0 || n > current->files->max_fdset) | ||
1015 | goto out_nofds; | ||
1016 | |||
1017 | /* | ||
1018 | * We need 6 bitmaps (in/out/ex for both incoming and outgoing), | ||
1019 | * since we used fdset we need to allocate memory in units of | ||
1020 | * long-words. | ||
1021 | */ | ||
1022 | ret = -ENOMEM; | ||
1023 | size = FDS_BYTES(n); | ||
1024 | bits = kmalloc(6 * size, GFP_KERNEL); | ||
1025 | if (!bits) | ||
1026 | goto out_nofds; | ||
1027 | fds.in = (unsigned long *) bits; | ||
1028 | fds.out = (unsigned long *) (bits + size); | ||
1029 | fds.ex = (unsigned long *) (bits + 2*size); | ||
1030 | fds.res_in = (unsigned long *) (bits + 3*size); | ||
1031 | fds.res_out = (unsigned long *) (bits + 4*size); | ||
1032 | fds.res_ex = (unsigned long *) (bits + 5*size); | ||
1033 | |||
1034 | if ((ret = get_fd_set(n, inp->fds_bits, fds.in)) || | ||
1035 | (ret = get_fd_set(n, outp->fds_bits, fds.out)) || | ||
1036 | (ret = get_fd_set(n, exp->fds_bits, fds.ex))) | ||
1037 | goto out; | ||
1038 | zero_fd_set(n, fds.res_in); | ||
1039 | zero_fd_set(n, fds.res_out); | ||
1040 | zero_fd_set(n, fds.res_ex); | ||
1041 | |||
1042 | ret = do_select(n, &fds, &timeout); | ||
1043 | |||
1044 | /* OSF does not copy back the remaining time. */ | ||
1045 | |||
1046 | if (ret < 0) | ||
1047 | goto out; | ||
1048 | if (!ret) { | ||
1049 | ret = -ERESTARTNOHAND; | ||
1050 | if (signal_pending(current)) | ||
1051 | goto out; | ||
1052 | ret = 0; | ||
1053 | } | ||
1054 | |||
1055 | if (set_fd_set(n, inp->fds_bits, fds.res_in) || | ||
1056 | set_fd_set(n, outp->fds_bits, fds.res_out) || | ||
1057 | set_fd_set(n, exp->fds_bits, fds.res_ex)) | ||
1058 | ret = -EFAULT; | ||
1059 | |||
1060 | out: | ||
1061 | kfree(bits); | ||
1062 | out_nofds: | ||
1063 | return ret; | ||
1064 | } | ||
1065 | |||
1066 | struct rusage32 { | ||
1067 | struct timeval32 ru_utime; /* user time used */ | ||
1068 | struct timeval32 ru_stime; /* system time used */ | ||
1069 | long ru_maxrss; /* maximum resident set size */ | ||
1070 | long ru_ixrss; /* integral shared memory size */ | ||
1071 | long ru_idrss; /* integral unshared data size */ | ||
1072 | long ru_isrss; /* integral unshared stack size */ | ||
1073 | long ru_minflt; /* page reclaims */ | ||
1074 | long ru_majflt; /* page faults */ | ||
1075 | long ru_nswap; /* swaps */ | ||
1076 | long ru_inblock; /* block input operations */ | ||
1077 | long ru_oublock; /* block output operations */ | ||
1078 | long ru_msgsnd; /* messages sent */ | ||
1079 | long ru_msgrcv; /* messages received */ | ||
1080 | long ru_nsignals; /* signals received */ | ||
1081 | long ru_nvcsw; /* voluntary context switches */ | ||
1082 | long ru_nivcsw; /* involuntary " */ | ||
1083 | }; | ||
1084 | |||
1085 | asmlinkage int | ||
1086 | osf_getrusage(int who, struct rusage32 __user *ru) | ||
1087 | { | ||
1088 | struct rusage32 r; | ||
1089 | |||
1090 | if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN) | ||
1091 | return -EINVAL; | ||
1092 | |||
1093 | memset(&r, 0, sizeof(r)); | ||
1094 | switch (who) { | ||
1095 | case RUSAGE_SELF: | ||
1096 | jiffies_to_timeval32(current->utime, &r.ru_utime); | ||
1097 | jiffies_to_timeval32(current->stime, &r.ru_stime); | ||
1098 | r.ru_minflt = current->min_flt; | ||
1099 | r.ru_majflt = current->maj_flt; | ||
1100 | break; | ||
1101 | case RUSAGE_CHILDREN: | ||
1102 | jiffies_to_timeval32(current->signal->cutime, &r.ru_utime); | ||
1103 | jiffies_to_timeval32(current->signal->cstime, &r.ru_stime); | ||
1104 | r.ru_minflt = current->signal->cmin_flt; | ||
1105 | r.ru_majflt = current->signal->cmaj_flt; | ||
1106 | break; | ||
1107 | } | ||
1108 | |||
1109 | return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; | ||
1110 | } | ||
1111 | |||
1112 | asmlinkage long | ||
1113 | osf_wait4(pid_t pid, int __user *ustatus, int options, | ||
1114 | struct rusage32 __user *ur) | ||
1115 | { | ||
1116 | struct rusage r; | ||
1117 | long ret, err; | ||
1118 | mm_segment_t old_fs; | ||
1119 | |||
1120 | if (!ur) | ||
1121 | return sys_wait4(pid, ustatus, options, NULL); | ||
1122 | |||
1123 | old_fs = get_fs(); | ||
1124 | |||
1125 | set_fs (KERNEL_DS); | ||
1126 | ret = sys_wait4(pid, ustatus, options, (struct rusage __user *) &r); | ||
1127 | set_fs (old_fs); | ||
1128 | |||
1129 | if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) | ||
1130 | return -EFAULT; | ||
1131 | |||
1132 | err = 0; | ||
1133 | err |= __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec); | ||
1134 | err |= __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec); | ||
1135 | err |= __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec); | ||
1136 | err |= __put_user(r.ru_stime.tv_usec, &ur->ru_stime.tv_usec); | ||
1137 | err |= __put_user(r.ru_maxrss, &ur->ru_maxrss); | ||
1138 | err |= __put_user(r.ru_ixrss, &ur->ru_ixrss); | ||
1139 | err |= __put_user(r.ru_idrss, &ur->ru_idrss); | ||
1140 | err |= __put_user(r.ru_isrss, &ur->ru_isrss); | ||
1141 | err |= __put_user(r.ru_minflt, &ur->ru_minflt); | ||
1142 | err |= __put_user(r.ru_majflt, &ur->ru_majflt); | ||
1143 | err |= __put_user(r.ru_nswap, &ur->ru_nswap); | ||
1144 | err |= __put_user(r.ru_inblock, &ur->ru_inblock); | ||
1145 | err |= __put_user(r.ru_oublock, &ur->ru_oublock); | ||
1146 | err |= __put_user(r.ru_msgsnd, &ur->ru_msgsnd); | ||
1147 | err |= __put_user(r.ru_msgrcv, &ur->ru_msgrcv); | ||
1148 | err |= __put_user(r.ru_nsignals, &ur->ru_nsignals); | ||
1149 | err |= __put_user(r.ru_nvcsw, &ur->ru_nvcsw); | ||
1150 | err |= __put_user(r.ru_nivcsw, &ur->ru_nivcsw); | ||
1151 | |||
1152 | return err ? err : ret; | ||
1153 | } | ||
1154 | |||
1155 | /* | ||
1156 | * I don't know what the parameters are: the first one | ||
1157 | * seems to be a timeval pointer, and I suspect the second | ||
1158 | * one is the time remaining.. Ho humm.. No documentation. | ||
1159 | */ | ||
1160 | asmlinkage int | ||
1161 | osf_usleep_thread(struct timeval32 __user *sleep, struct timeval32 __user *remain) | ||
1162 | { | ||
1163 | struct timeval tmp; | ||
1164 | unsigned long ticks; | ||
1165 | |||
1166 | if (get_tv32(&tmp, sleep)) | ||
1167 | goto fault; | ||
1168 | |||
1169 | ticks = tmp.tv_usec; | ||
1170 | ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ); | ||
1171 | ticks += tmp.tv_sec * HZ; | ||
1172 | |||
1173 | current->state = TASK_INTERRUPTIBLE; | ||
1174 | ticks = schedule_timeout(ticks); | ||
1175 | |||
1176 | if (remain) { | ||
1177 | tmp.tv_sec = ticks / HZ; | ||
1178 | tmp.tv_usec = ticks % HZ; | ||
1179 | if (put_tv32(remain, &tmp)) | ||
1180 | goto fault; | ||
1181 | } | ||
1182 | |||
1183 | return 0; | ||
1184 | fault: | ||
1185 | return -EFAULT; | ||
1186 | } | ||
1187 | |||
1188 | |||
1189 | struct timex32 { | ||
1190 | unsigned int modes; /* mode selector */ | ||
1191 | long offset; /* time offset (usec) */ | ||
1192 | long freq; /* frequency offset (scaled ppm) */ | ||
1193 | long maxerror; /* maximum error (usec) */ | ||
1194 | long esterror; /* estimated error (usec) */ | ||
1195 | int status; /* clock command/status */ | ||
1196 | long constant; /* pll time constant */ | ||
1197 | long precision; /* clock precision (usec) (read only) */ | ||
1198 | long tolerance; /* clock frequency tolerance (ppm) | ||
1199 | * (read only) | ||
1200 | */ | ||
1201 | struct timeval32 time; /* (read only) */ | ||
1202 | long tick; /* (modified) usecs between clock ticks */ | ||
1203 | |||
1204 | long ppsfreq; /* pps frequency (scaled ppm) (ro) */ | ||
1205 | long jitter; /* pps jitter (us) (ro) */ | ||
1206 | int shift; /* interval duration (s) (shift) (ro) */ | ||
1207 | long stabil; /* pps stability (scaled ppm) (ro) */ | ||
1208 | long jitcnt; /* jitter limit exceeded (ro) */ | ||
1209 | long calcnt; /* calibration intervals (ro) */ | ||
1210 | long errcnt; /* calibration errors (ro) */ | ||
1211 | long stbcnt; /* stability limit exceeded (ro) */ | ||
1212 | |||
1213 | int :32; int :32; int :32; int :32; | ||
1214 | int :32; int :32; int :32; int :32; | ||
1215 | int :32; int :32; int :32; int :32; | ||
1216 | }; | ||
1217 | |||
1218 | asmlinkage int | ||
1219 | sys_old_adjtimex(struct timex32 __user *txc_p) | ||
1220 | { | ||
1221 | struct timex txc; | ||
1222 | int ret; | ||
1223 | |||
1224 | /* copy relevant bits of struct timex. */ | ||
1225 | if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) || | ||
1226 | copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - | ||
1227 | offsetof(struct timex32, time))) | ||
1228 | return -EFAULT; | ||
1229 | |||
1230 | ret = do_adjtimex(&txc); | ||
1231 | if (ret < 0) | ||
1232 | return ret; | ||
1233 | |||
1234 | /* copy back to timex32 */ | ||
1235 | if (copy_to_user(txc_p, &txc, offsetof(struct timex32, time)) || | ||
1236 | (copy_to_user(&txc_p->tick, &txc.tick, sizeof(struct timex32) - | ||
1237 | offsetof(struct timex32, tick))) || | ||
1238 | (put_tv32(&txc_p->time, &txc.time))) | ||
1239 | return -EFAULT; | ||
1240 | |||
1241 | return ret; | ||
1242 | } | ||
1243 | |||
1244 | /* Get an address range which is currently unmapped. Similar to the | ||
1245 | generic version except that we know how to honor ADDR_LIMIT_32BIT. */ | ||
1246 | |||
1247 | static unsigned long | ||
1248 | arch_get_unmapped_area_1(unsigned long addr, unsigned long len, | ||
1249 | unsigned long limit) | ||
1250 | { | ||
1251 | struct vm_area_struct *vma = find_vma(current->mm, addr); | ||
1252 | |||
1253 | while (1) { | ||
1254 | /* At this point: (!vma || addr < vma->vm_end). */ | ||
1255 | if (limit - len < addr) | ||
1256 | return -ENOMEM; | ||
1257 | if (!vma || addr + len <= vma->vm_start) | ||
1258 | return addr; | ||
1259 | addr = vma->vm_end; | ||
1260 | vma = vma->vm_next; | ||
1261 | } | ||
1262 | } | ||
1263 | |||
1264 | unsigned long | ||
1265 | arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
1266 | unsigned long len, unsigned long pgoff, | ||
1267 | unsigned long flags) | ||
1268 | { | ||
1269 | unsigned long limit; | ||
1270 | |||
1271 | /* "32 bit" actually means 31 bit, since pointers sign extend. */ | ||
1272 | if (current->personality & ADDR_LIMIT_32BIT) | ||
1273 | limit = 0x80000000; | ||
1274 | else | ||
1275 | limit = TASK_SIZE; | ||
1276 | |||
1277 | if (len > limit) | ||
1278 | return -ENOMEM; | ||
1279 | |||
1280 | /* First, see if the given suggestion fits. | ||
1281 | |||
1282 | The OSF/1 loader (/sbin/loader) relies on us returning an | ||
1283 | address larger than the requested if one exists, which is | ||
1284 | a terribly broken way to program. | ||
1285 | |||
1286 | That said, I can see the use in being able to suggest not | ||
1287 | merely specific addresses, but regions of memory -- perhaps | ||
1288 | this feature should be incorporated into all ports? */ | ||
1289 | |||
1290 | if (addr) { | ||
1291 | addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit); | ||
1292 | if (addr != (unsigned long) -ENOMEM) | ||
1293 | return addr; | ||
1294 | } | ||
1295 | |||
1296 | /* Next, try allocating at TASK_UNMAPPED_BASE. */ | ||
1297 | addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE), | ||
1298 | len, limit); | ||
1299 | if (addr != (unsigned long) -ENOMEM) | ||
1300 | return addr; | ||
1301 | |||
1302 | /* Finally, try allocating in low memory. */ | ||
1303 | addr = arch_get_unmapped_area_1 (PAGE_SIZE, len, limit); | ||
1304 | |||
1305 | return addr; | ||
1306 | } | ||
1307 | |||
1308 | #ifdef CONFIG_OSF4_COMPAT | ||
1309 | |||
1310 | /* Clear top 32 bits of iov_len in the user's buffer for | ||
1311 | compatibility with old versions of OSF/1 where iov_len | ||
1312 | was defined as int. */ | ||
1313 | static int | ||
1314 | osf_fix_iov_len(const struct iovec __user *iov, unsigned long count) | ||
1315 | { | ||
1316 | unsigned long i; | ||
1317 | |||
1318 | for (i = 0 ; i < count ; i++) { | ||
1319 | int __user *iov_len_high = (int __user *)&iov[i].iov_len + 1; | ||
1320 | |||
1321 | if (put_user(0, iov_len_high)) | ||
1322 | return -EFAULT; | ||
1323 | } | ||
1324 | return 0; | ||
1325 | } | ||
1326 | |||
1327 | asmlinkage ssize_t | ||
1328 | osf_readv(unsigned long fd, const struct iovec __user * vector, unsigned long count) | ||
1329 | { | ||
1330 | if (unlikely(personality(current->personality) == PER_OSF4)) | ||
1331 | if (osf_fix_iov_len(vector, count)) | ||
1332 | return -EFAULT; | ||
1333 | return sys_readv(fd, vector, count); | ||
1334 | } | ||
1335 | |||
1336 | asmlinkage ssize_t | ||
1337 | osf_writev(unsigned long fd, const struct iovec __user * vector, unsigned long count) | ||
1338 | { | ||
1339 | if (unlikely(personality(current->personality) == PER_OSF4)) | ||
1340 | if (osf_fix_iov_len(vector, count)) | ||
1341 | return -EFAULT; | ||
1342 | return sys_writev(fd, vector, count); | ||
1343 | } | ||
1344 | |||
1345 | #endif | ||
diff --git a/arch/alpha/kernel/pci-noop.c b/arch/alpha/kernel/pci-noop.c new file mode 100644 index 000000000000..582a3519fb28 --- /dev/null +++ b/arch/alpha/kernel/pci-noop.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/pci-noop.c | ||
3 | * | ||
4 | * Stub PCI interfaces for Jensen-specific kernels. | ||
5 | */ | ||
6 | |||
7 | #include <linux/pci.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/bootmem.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/dma-mapping.h> | ||
14 | |||
15 | #include "proto.h" | ||
16 | |||
17 | |||
18 | /* | ||
19 | * The PCI controller list. | ||
20 | */ | ||
21 | |||
22 | struct pci_controller *hose_head, **hose_tail = &hose_head; | ||
23 | struct pci_controller *pci_isa_hose; | ||
24 | |||
25 | |||
26 | struct pci_controller * __init | ||
27 | alloc_pci_controller(void) | ||
28 | { | ||
29 | struct pci_controller *hose; | ||
30 | |||
31 | hose = alloc_bootmem(sizeof(*hose)); | ||
32 | |||
33 | *hose_tail = hose; | ||
34 | hose_tail = &hose->next; | ||
35 | |||
36 | return hose; | ||
37 | } | ||
38 | |||
39 | struct resource * __init | ||
40 | alloc_resource(void) | ||
41 | { | ||
42 | struct resource *res; | ||
43 | |||
44 | res = alloc_bootmem(sizeof(*res)); | ||
45 | |||
46 | return res; | ||
47 | } | ||
48 | |||
49 | asmlinkage long | ||
50 | sys_pciconfig_iobase(long which, unsigned long bus, unsigned long dfn) | ||
51 | { | ||
52 | struct pci_controller *hose; | ||
53 | |||
54 | /* from hose or from bus.devfn */ | ||
55 | if (which & IOBASE_FROM_HOSE) { | ||
56 | for (hose = hose_head; hose; hose = hose->next) | ||
57 | if (hose->index == bus) | ||
58 | break; | ||
59 | if (!hose) | ||
60 | return -ENODEV; | ||
61 | } else { | ||
62 | /* Special hook for ISA access. */ | ||
63 | if (bus == 0 && dfn == 0) | ||
64 | hose = pci_isa_hose; | ||
65 | else | ||
66 | return -ENODEV; | ||
67 | } | ||
68 | |||
69 | switch (which & ~IOBASE_FROM_HOSE) { | ||
70 | case IOBASE_HOSE: | ||
71 | return hose->index; | ||
72 | case IOBASE_SPARSE_MEM: | ||
73 | return hose->sparse_mem_base; | ||
74 | case IOBASE_DENSE_MEM: | ||
75 | return hose->dense_mem_base; | ||
76 | case IOBASE_SPARSE_IO: | ||
77 | return hose->sparse_io_base; | ||
78 | case IOBASE_DENSE_IO: | ||
79 | return hose->dense_io_base; | ||
80 | case IOBASE_ROOT_BUS: | ||
81 | return hose->bus->number; | ||
82 | } | ||
83 | |||
84 | return -EOPNOTSUPP; | ||
85 | } | ||
86 | |||
87 | asmlinkage long | ||
88 | sys_pciconfig_read(unsigned long bus, unsigned long dfn, | ||
89 | unsigned long off, unsigned long len, void *buf) | ||
90 | { | ||
91 | if (!capable(CAP_SYS_ADMIN)) | ||
92 | return -EPERM; | ||
93 | else | ||
94 | return -ENODEV; | ||
95 | } | ||
96 | |||
97 | asmlinkage long | ||
98 | sys_pciconfig_write(unsigned long bus, unsigned long dfn, | ||
99 | unsigned long off, unsigned long len, void *buf) | ||
100 | { | ||
101 | if (!capable(CAP_SYS_ADMIN)) | ||
102 | return -EPERM; | ||
103 | else | ||
104 | return -ENODEV; | ||
105 | } | ||
106 | |||
107 | /* Stubs for the routines in pci_iommu.c: */ | ||
108 | |||
109 | void * | ||
110 | pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) | ||
111 | { | ||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | void | ||
116 | pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr, | ||
117 | dma_addr_t dma_addr) | ||
118 | { | ||
119 | } | ||
120 | |||
121 | dma_addr_t | ||
122 | pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, | ||
123 | int direction) | ||
124 | { | ||
125 | return (dma_addr_t) 0; | ||
126 | } | ||
127 | |||
128 | void | ||
129 | pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, | ||
130 | int direction) | ||
131 | { | ||
132 | } | ||
133 | |||
134 | int | ||
135 | pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, | ||
136 | int direction) | ||
137 | { | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | void | ||
142 | pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, | ||
143 | int direction) | ||
144 | { | ||
145 | } | ||
146 | |||
147 | int | ||
148 | pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask) | ||
149 | { | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | /* Generic DMA mapping functions: */ | ||
154 | |||
155 | void * | ||
156 | dma_alloc_coherent(struct device *dev, size_t size, | ||
157 | dma_addr_t *dma_handle, int gfp) | ||
158 | { | ||
159 | void *ret; | ||
160 | |||
161 | if (!dev || *dev->dma_mask >= 0xffffffffUL) | ||
162 | gfp &= ~GFP_DMA; | ||
163 | ret = (void *)__get_free_pages(gfp, get_order(size)); | ||
164 | if (ret) { | ||
165 | memset(ret, 0, size); | ||
166 | *dma_handle = virt_to_bus(ret); | ||
167 | } | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
172 | |||
173 | int | ||
174 | dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
175 | enum dma_data_direction direction) | ||
176 | { | ||
177 | int i; | ||
178 | |||
179 | for (i = 0; i < nents; i++ ) { | ||
180 | void *va; | ||
181 | |||
182 | BUG_ON(!sg[i].page); | ||
183 | va = page_address(sg[i].page) + sg[i].offset; | ||
184 | sg_dma_address(sg + i) = (dma_addr_t)virt_to_bus(va); | ||
185 | sg_dma_len(sg + i) = sg[i].length; | ||
186 | } | ||
187 | |||
188 | return nents; | ||
189 | } | ||
190 | |||
191 | EXPORT_SYMBOL(dma_map_sg); | ||
192 | |||
193 | int | ||
194 | dma_set_mask(struct device *dev, u64 mask) | ||
195 | { | ||
196 | if (!dev->dma_mask || !dma_supported(dev, mask)) | ||
197 | return -EIO; | ||
198 | |||
199 | *dev->dma_mask = mask; | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) | ||
205 | { | ||
206 | return NULL; | ||
207 | } | ||
208 | |||
209 | void pci_iounmap(struct pci_dev *dev, void __iomem * addr) | ||
210 | { | ||
211 | } | ||
212 | |||
213 | EXPORT_SYMBOL(pci_iomap); | ||
214 | EXPORT_SYMBOL(pci_iounmap); | ||
diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c new file mode 100644 index 000000000000..1f36bbd0ed5d --- /dev/null +++ b/arch/alpha/kernel/pci.c | |||
@@ -0,0 +1,561 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/pci.c | ||
3 | * | ||
4 | * Extruded from code written by | ||
5 | * Dave Rusling (david.rusling@reo.mts.dec.com) | ||
6 | * David Mosberger (davidm@cs.arizona.edu) | ||
7 | */ | ||
8 | |||
9 | /* 2.3.x PCI/resources, 1999 Andrea Arcangeli <andrea@suse.de> */ | ||
10 | |||
11 | /* | ||
12 | * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> | ||
13 | * PCI-PCI bridges cleanup | ||
14 | */ | ||
15 | #include <linux/config.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/ioport.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/bootmem.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/cache.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <asm/machvec.h> | ||
26 | |||
27 | #include "proto.h" | ||
28 | #include "pci_impl.h" | ||
29 | |||
30 | |||
31 | /* | ||
32 | * Some string constants used by the various core logics. | ||
33 | */ | ||
34 | |||
35 | const char *const pci_io_names[] = { | ||
36 | "PCI IO bus 0", "PCI IO bus 1", "PCI IO bus 2", "PCI IO bus 3", | ||
37 | "PCI IO bus 4", "PCI IO bus 5", "PCI IO bus 6", "PCI IO bus 7" | ||
38 | }; | ||
39 | |||
40 | const char *const pci_mem_names[] = { | ||
41 | "PCI mem bus 0", "PCI mem bus 1", "PCI mem bus 2", "PCI mem bus 3", | ||
42 | "PCI mem bus 4", "PCI mem bus 5", "PCI mem bus 6", "PCI mem bus 7" | ||
43 | }; | ||
44 | |||
45 | const char pci_hae0_name[] = "HAE0"; | ||
46 | |||
47 | /* Indicate whether we respect the PCI setup left by console. */ | ||
48 | /* | ||
49 | * Make this long-lived so that we know when shutting down | ||
50 | * whether we probed only or not. | ||
51 | */ | ||
52 | int pci_probe_only; | ||
53 | |||
54 | /* | ||
55 | * The PCI controller list. | ||
56 | */ | ||
57 | |||
58 | struct pci_controller *hose_head, **hose_tail = &hose_head; | ||
59 | struct pci_controller *pci_isa_hose; | ||
60 | |||
61 | /* | ||
62 | * Quirks. | ||
63 | */ | ||
64 | |||
65 | static void __init | ||
66 | quirk_isa_bridge(struct pci_dev *dev) | ||
67 | { | ||
68 | dev->class = PCI_CLASS_BRIDGE_ISA << 8; | ||
69 | } | ||
70 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, quirk_isa_bridge); | ||
71 | |||
72 | static void __init | ||
73 | quirk_cypress(struct pci_dev *dev) | ||
74 | { | ||
75 | /* The Notorious Cy82C693 chip. */ | ||
76 | |||
77 | /* The Cypress IDE controller doesn't support native mode, but it | ||
78 | has programmable addresses of IDE command/control registers. | ||
79 | This violates PCI specifications, confuses the IDE subsystem and | ||
80 | causes resource conflicts between the primary HD_CMD register and | ||
81 | the floppy controller. Ugh. Fix that. */ | ||
82 | if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE) { | ||
83 | dev->resource[0].flags = 0; | ||
84 | dev->resource[1].flags = 0; | ||
85 | } | ||
86 | |||
87 | /* The Cypress bridge responds on the PCI bus in the address range | ||
88 | 0xffff0000-0xffffffff (conventional x86 BIOS ROM). There is no | ||
89 | way to turn this off. The bridge also supports several extended | ||
90 | BIOS ranges (disabled after power-up), and some consoles do turn | ||
91 | them on. So if we use a large direct-map window, or a large SG | ||
92 | window, we must avoid the entire 0xfff00000-0xffffffff region. */ | ||
93 | else if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA) { | ||
94 | if (__direct_map_base + __direct_map_size >= 0xfff00000UL) | ||
95 | __direct_map_size = 0xfff00000UL - __direct_map_base; | ||
96 | else { | ||
97 | struct pci_controller *hose = dev->sysdata; | ||
98 | struct pci_iommu_arena *pci = hose->sg_pci; | ||
99 | if (pci && pci->dma_base + pci->size >= 0xfff00000UL) | ||
100 | pci->size = 0xfff00000UL - pci->dma_base; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, quirk_cypress); | ||
105 | |||
106 | /* Called for each device after PCI setup is done. */ | ||
107 | static void __init | ||
108 | pcibios_fixup_final(struct pci_dev *dev) | ||
109 | { | ||
110 | unsigned int class = dev->class >> 8; | ||
111 | |||
112 | if (class == PCI_CLASS_BRIDGE_ISA || class == PCI_CLASS_BRIDGE_EISA) { | ||
113 | dev->dma_mask = MAX_ISA_DMA_ADDRESS - 1; | ||
114 | isa_bridge = dev; | ||
115 | } | ||
116 | } | ||
117 | DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final); | ||
118 | |||
119 | /* Just declaring that the power-of-ten prefixes are actually the | ||
120 | power-of-two ones doesn't make it true :) */ | ||
121 | #define KB 1024 | ||
122 | #define MB (1024*KB) | ||
123 | #define GB (1024*MB) | ||
124 | |||
125 | void | ||
126 | pcibios_align_resource(void *data, struct resource *res, | ||
127 | unsigned long size, unsigned long align) | ||
128 | { | ||
129 | struct pci_dev *dev = data; | ||
130 | struct pci_controller *hose = dev->sysdata; | ||
131 | unsigned long alignto; | ||
132 | unsigned long start = res->start; | ||
133 | |||
134 | if (res->flags & IORESOURCE_IO) { | ||
135 | /* Make sure we start at our min on all hoses */ | ||
136 | if (start - hose->io_space->start < PCIBIOS_MIN_IO) | ||
137 | start = PCIBIOS_MIN_IO + hose->io_space->start; | ||
138 | |||
139 | /* | ||
140 | * Put everything into 0x00-0xff region modulo 0x400 | ||
141 | */ | ||
142 | if (start & 0x300) | ||
143 | start = (start + 0x3ff) & ~0x3ff; | ||
144 | } | ||
145 | else if (res->flags & IORESOURCE_MEM) { | ||
146 | /* Make sure we start at our min on all hoses */ | ||
147 | if (start - hose->mem_space->start < PCIBIOS_MIN_MEM) | ||
148 | start = PCIBIOS_MIN_MEM + hose->mem_space->start; | ||
149 | |||
150 | /* | ||
151 | * The following holds at least for the Low Cost | ||
152 | * Alpha implementation of the PCI interface: | ||
153 | * | ||
154 | * In sparse memory address space, the first | ||
155 | * octant (16MB) of every 128MB segment is | ||
156 | * aliased to the very first 16 MB of the | ||
157 | * address space (i.e., it aliases the ISA | ||
158 | * memory address space). Thus, we try to | ||
159 | * avoid allocating PCI devices in that range. | ||
160 | * Can be allocated in 2nd-7th octant only. | ||
161 | * Devices that need more than 112MB of | ||
162 | * address space must be accessed through | ||
163 | * dense memory space only! | ||
164 | */ | ||
165 | |||
166 | /* Align to multiple of size of minimum base. */ | ||
167 | alignto = max(0x1000UL, align); | ||
168 | start = ALIGN(start, alignto); | ||
169 | if (hose->sparse_mem_base && size <= 7 * 16*MB) { | ||
170 | if (((start / (16*MB)) & 0x7) == 0) { | ||
171 | start &= ~(128*MB - 1); | ||
172 | start += 16*MB; | ||
173 | start = ALIGN(start, alignto); | ||
174 | } | ||
175 | if (start/(128*MB) != (start + size - 1)/(128*MB)) { | ||
176 | start &= ~(128*MB - 1); | ||
177 | start += (128 + 16)*MB; | ||
178 | start = ALIGN(start, alignto); | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | res->start = start; | ||
184 | } | ||
185 | #undef KB | ||
186 | #undef MB | ||
187 | #undef GB | ||
188 | |||
189 | static int __init | ||
190 | pcibios_init(void) | ||
191 | { | ||
192 | if (alpha_mv.init_pci) | ||
193 | alpha_mv.init_pci(); | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | subsys_initcall(pcibios_init); | ||
198 | |||
199 | char * __init | ||
200 | pcibios_setup(char *str) | ||
201 | { | ||
202 | return str; | ||
203 | } | ||
204 | |||
205 | #ifdef ALPHA_RESTORE_SRM_SETUP | ||
206 | static struct pdev_srm_saved_conf *srm_saved_configs; | ||
207 | |||
208 | void __init | ||
209 | pdev_save_srm_config(struct pci_dev *dev) | ||
210 | { | ||
211 | struct pdev_srm_saved_conf *tmp; | ||
212 | static int printed = 0; | ||
213 | |||
214 | if (!alpha_using_srm || pci_probe_only) | ||
215 | return; | ||
216 | |||
217 | if (!printed) { | ||
218 | printk(KERN_INFO "pci: enabling save/restore of SRM state\n"); | ||
219 | printed = 1; | ||
220 | } | ||
221 | |||
222 | tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); | ||
223 | if (!tmp) { | ||
224 | printk(KERN_ERR "%s: kmalloc() failed!\n", __FUNCTION__); | ||
225 | return; | ||
226 | } | ||
227 | tmp->next = srm_saved_configs; | ||
228 | tmp->dev = dev; | ||
229 | |||
230 | pci_save_state(dev); | ||
231 | |||
232 | srm_saved_configs = tmp; | ||
233 | } | ||
234 | |||
235 | void | ||
236 | pci_restore_srm_config(void) | ||
237 | { | ||
238 | struct pdev_srm_saved_conf *tmp; | ||
239 | |||
240 | /* No need to restore if probed only. */ | ||
241 | if (pci_probe_only) | ||
242 | return; | ||
243 | |||
244 | /* Restore SRM config. */ | ||
245 | for (tmp = srm_saved_configs; tmp; tmp = tmp->next) { | ||
246 | pci_restore_state(tmp->dev); | ||
247 | } | ||
248 | } | ||
249 | #endif | ||
250 | |||
251 | void __init | ||
252 | pcibios_fixup_resource(struct resource *res, struct resource *root) | ||
253 | { | ||
254 | res->start += root->start; | ||
255 | res->end += root->start; | ||
256 | } | ||
257 | |||
258 | void __init | ||
259 | pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus) | ||
260 | { | ||
261 | /* Update device resources. */ | ||
262 | struct pci_controller *hose = (struct pci_controller *)bus->sysdata; | ||
263 | int i; | ||
264 | |||
265 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
266 | if (!dev->resource[i].start) | ||
267 | continue; | ||
268 | if (dev->resource[i].flags & IORESOURCE_IO) | ||
269 | pcibios_fixup_resource(&dev->resource[i], | ||
270 | hose->io_space); | ||
271 | else if (dev->resource[i].flags & IORESOURCE_MEM) | ||
272 | pcibios_fixup_resource(&dev->resource[i], | ||
273 | hose->mem_space); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | void __init | ||
278 | pcibios_fixup_bus(struct pci_bus *bus) | ||
279 | { | ||
280 | /* Propagate hose info into the subordinate devices. */ | ||
281 | |||
282 | struct pci_controller *hose = bus->sysdata; | ||
283 | struct pci_dev *dev = bus->self; | ||
284 | |||
285 | if (!dev) { | ||
286 | /* Root bus. */ | ||
287 | u32 pci_mem_end; | ||
288 | u32 sg_base = hose->sg_pci ? hose->sg_pci->dma_base : ~0; | ||
289 | unsigned long end; | ||
290 | |||
291 | bus->resource[0] = hose->io_space; | ||
292 | bus->resource[1] = hose->mem_space; | ||
293 | |||
294 | /* Adjust hose mem_space limit to prevent PCI allocations | ||
295 | in the iommu windows. */ | ||
296 | pci_mem_end = min((u32)__direct_map_base, sg_base) - 1; | ||
297 | end = hose->mem_space->start + pci_mem_end; | ||
298 | if (hose->mem_space->end > end) | ||
299 | hose->mem_space->end = end; | ||
300 | } else if (pci_probe_only && | ||
301 | (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { | ||
302 | pci_read_bridge_bases(bus); | ||
303 | pcibios_fixup_device_resources(dev, bus); | ||
304 | } | ||
305 | |||
306 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
307 | pdev_save_srm_config(dev); | ||
308 | if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) | ||
309 | pcibios_fixup_device_resources(dev, bus); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | void __init | ||
314 | pcibios_update_irq(struct pci_dev *dev, int irq) | ||
315 | { | ||
316 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); | ||
317 | } | ||
318 | |||
319 | /* Most Alphas have straight-forward swizzling needs. */ | ||
320 | |||
321 | u8 __init | ||
322 | common_swizzle(struct pci_dev *dev, u8 *pinp) | ||
323 | { | ||
324 | u8 pin = *pinp; | ||
325 | |||
326 | while (dev->bus->parent) { | ||
327 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); | ||
328 | /* Move up the chain of bridges. */ | ||
329 | dev = dev->bus->self; | ||
330 | } | ||
331 | *pinp = pin; | ||
332 | |||
333 | /* The slot is the slot of the last bridge. */ | ||
334 | return PCI_SLOT(dev->devfn); | ||
335 | } | ||
336 | |||
337 | void __devinit | ||
338 | pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, | ||
339 | struct resource *res) | ||
340 | { | ||
341 | struct pci_controller *hose = (struct pci_controller *)dev->sysdata; | ||
342 | unsigned long offset = 0; | ||
343 | |||
344 | if (res->flags & IORESOURCE_IO) | ||
345 | offset = hose->io_space->start; | ||
346 | else if (res->flags & IORESOURCE_MEM) | ||
347 | offset = hose->mem_space->start; | ||
348 | |||
349 | region->start = res->start - offset; | ||
350 | region->end = res->end - offset; | ||
351 | } | ||
352 | |||
353 | #ifdef CONFIG_HOTPLUG | ||
354 | EXPORT_SYMBOL(pcibios_resource_to_bus); | ||
355 | #endif | ||
356 | |||
357 | int | ||
358 | pcibios_enable_device(struct pci_dev *dev, int mask) | ||
359 | { | ||
360 | u16 cmd, oldcmd; | ||
361 | int i; | ||
362 | |||
363 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | ||
364 | oldcmd = cmd; | ||
365 | |||
366 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
367 | struct resource *res = &dev->resource[i]; | ||
368 | |||
369 | if (res->flags & IORESOURCE_IO) | ||
370 | cmd |= PCI_COMMAND_IO; | ||
371 | else if (res->flags & IORESOURCE_MEM) | ||
372 | cmd |= PCI_COMMAND_MEMORY; | ||
373 | } | ||
374 | |||
375 | if (cmd != oldcmd) { | ||
376 | printk(KERN_DEBUG "PCI: Enabling device: (%s), cmd %x\n", | ||
377 | pci_name(dev), cmd); | ||
378 | /* Enable the appropriate bits in the PCI command register. */ | ||
379 | pci_write_config_word(dev, PCI_COMMAND, cmd); | ||
380 | } | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * If we set up a device for bus mastering, we need to check the latency | ||
386 | * timer as certain firmware forgets to set it properly, as seen | ||
387 | * on SX164 and LX164 with SRM. | ||
388 | */ | ||
389 | void | ||
390 | pcibios_set_master(struct pci_dev *dev) | ||
391 | { | ||
392 | u8 lat; | ||
393 | pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); | ||
394 | if (lat >= 16) return; | ||
395 | printk("PCI: Setting latency timer of device %s to 64\n", | ||
396 | pci_name(dev)); | ||
397 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); | ||
398 | } | ||
399 | |||
400 | static void __init | ||
401 | pcibios_claim_one_bus(struct pci_bus *b) | ||
402 | { | ||
403 | struct pci_dev *dev; | ||
404 | struct pci_bus *child_bus; | ||
405 | |||
406 | list_for_each_entry(dev, &b->devices, bus_list) { | ||
407 | int i; | ||
408 | |||
409 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
410 | struct resource *r = &dev->resource[i]; | ||
411 | |||
412 | if (r->parent || !r->start || !r->flags) | ||
413 | continue; | ||
414 | pci_claim_resource(dev, i); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | list_for_each_entry(child_bus, &b->children, node) | ||
419 | pcibios_claim_one_bus(child_bus); | ||
420 | } | ||
421 | |||
422 | static void __init | ||
423 | pcibios_claim_console_setup(void) | ||
424 | { | ||
425 | struct pci_bus *b; | ||
426 | |||
427 | list_for_each_entry(b, &pci_root_buses, node) | ||
428 | pcibios_claim_one_bus(b); | ||
429 | } | ||
430 | |||
431 | void __init | ||
432 | common_init_pci(void) | ||
433 | { | ||
434 | struct pci_controller *hose; | ||
435 | struct pci_bus *bus; | ||
436 | int next_busno; | ||
437 | int need_domain_info = 0; | ||
438 | |||
439 | /* Scan all of the recorded PCI controllers. */ | ||
440 | for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { | ||
441 | bus = pci_scan_bus(next_busno, alpha_mv.pci_ops, hose); | ||
442 | hose->bus = bus; | ||
443 | hose->need_domain_info = need_domain_info; | ||
444 | next_busno = bus->subordinate + 1; | ||
445 | /* Don't allow 8-bit bus number overflow inside the hose - | ||
446 | reserve some space for bridges. */ | ||
447 | if (next_busno > 224) { | ||
448 | next_busno = 0; | ||
449 | need_domain_info = 1; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | if (pci_probe_only) | ||
454 | pcibios_claim_console_setup(); | ||
455 | |||
456 | pci_assign_unassigned_resources(); | ||
457 | pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq); | ||
458 | } | ||
459 | |||
460 | |||
461 | struct pci_controller * __init | ||
462 | alloc_pci_controller(void) | ||
463 | { | ||
464 | struct pci_controller *hose; | ||
465 | |||
466 | hose = alloc_bootmem(sizeof(*hose)); | ||
467 | |||
468 | *hose_tail = hose; | ||
469 | hose_tail = &hose->next; | ||
470 | |||
471 | return hose; | ||
472 | } | ||
473 | |||
474 | struct resource * __init | ||
475 | alloc_resource(void) | ||
476 | { | ||
477 | struct resource *res; | ||
478 | |||
479 | res = alloc_bootmem(sizeof(*res)); | ||
480 | |||
481 | return res; | ||
482 | } | ||
483 | |||
484 | |||
485 | /* Provide information on locations of various I/O regions in physical | ||
486 | memory. Do this on a per-card basis so that we choose the right hose. */ | ||
487 | |||
488 | asmlinkage long | ||
489 | sys_pciconfig_iobase(long which, unsigned long bus, unsigned long dfn) | ||
490 | { | ||
491 | struct pci_controller *hose; | ||
492 | struct pci_dev *dev; | ||
493 | |||
494 | /* from hose or from bus.devfn */ | ||
495 | if (which & IOBASE_FROM_HOSE) { | ||
496 | for(hose = hose_head; hose; hose = hose->next) | ||
497 | if (hose->index == bus) break; | ||
498 | if (!hose) return -ENODEV; | ||
499 | } else { | ||
500 | /* Special hook for ISA access. */ | ||
501 | if (bus == 0 && dfn == 0) { | ||
502 | hose = pci_isa_hose; | ||
503 | } else { | ||
504 | dev = pci_find_slot(bus, dfn); | ||
505 | if (!dev) | ||
506 | return -ENODEV; | ||
507 | hose = dev->sysdata; | ||
508 | } | ||
509 | } | ||
510 | |||
511 | switch (which & ~IOBASE_FROM_HOSE) { | ||
512 | case IOBASE_HOSE: | ||
513 | return hose->index; | ||
514 | case IOBASE_SPARSE_MEM: | ||
515 | return hose->sparse_mem_base; | ||
516 | case IOBASE_DENSE_MEM: | ||
517 | return hose->dense_mem_base; | ||
518 | case IOBASE_SPARSE_IO: | ||
519 | return hose->sparse_io_base; | ||
520 | case IOBASE_DENSE_IO: | ||
521 | return hose->dense_io_base; | ||
522 | case IOBASE_ROOT_BUS: | ||
523 | return hose->bus->number; | ||
524 | } | ||
525 | |||
526 | return -EOPNOTSUPP; | ||
527 | } | ||
528 | |||
529 | /* Create an __iomem token from a PCI BAR. Copied from lib/iomap.c with | ||
530 | no changes, since we don't want the other things in that object file. */ | ||
531 | |||
532 | void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) | ||
533 | { | ||
534 | unsigned long start = pci_resource_start(dev, bar); | ||
535 | unsigned long len = pci_resource_len(dev, bar); | ||
536 | unsigned long flags = pci_resource_flags(dev, bar); | ||
537 | |||
538 | if (!len || !start) | ||
539 | return NULL; | ||
540 | if (maxlen && len > maxlen) | ||
541 | len = maxlen; | ||
542 | if (flags & IORESOURCE_IO) | ||
543 | return ioport_map(start, len); | ||
544 | if (flags & IORESOURCE_MEM) { | ||
545 | /* Not checking IORESOURCE_CACHEABLE because alpha does | ||
546 | not distinguish between ioremap and ioremap_nocache. */ | ||
547 | return ioremap(start, len); | ||
548 | } | ||
549 | return NULL; | ||
550 | } | ||
551 | |||
552 | /* Destroy that token. Not copied from lib/iomap.c. */ | ||
553 | |||
554 | void pci_iounmap(struct pci_dev *dev, void __iomem * addr) | ||
555 | { | ||
556 | if (__is_mmio(addr)) | ||
557 | iounmap(addr); | ||
558 | } | ||
559 | |||
560 | EXPORT_SYMBOL(pci_iomap); | ||
561 | EXPORT_SYMBOL(pci_iounmap); | ||
diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h new file mode 100644 index 000000000000..f8b74995a002 --- /dev/null +++ b/arch/alpha/kernel/pci_impl.h | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/pci_impl.h | ||
3 | * | ||
4 | * This file contains declarations and inline functions for interfacing | ||
5 | * with the PCI initialization routines. | ||
6 | */ | ||
7 | |||
8 | struct pci_dev; | ||
9 | struct pci_controller; | ||
10 | struct pci_iommu_arena; | ||
11 | |||
12 | /* | ||
13 | * We can't just blindly use 64K for machines with EISA busses; they | ||
14 | * may also have PCI-PCI bridges present, and then we'd configure the | ||
15 | * bridge incorrectly. | ||
16 | * | ||
17 | * Also, we start at 0x8000 or 0x9000, in hopes to get all devices' | ||
18 | * IO space areas allocated *before* 0xC000; this is because certain | ||
19 | * BIOSes (Millennium for one) use PCI Config space "mechanism #2" | ||
20 | * accesses to probe the bus. If a device's registers appear at 0xC000, | ||
21 | * it may see an INx/OUTx at that address during BIOS emulation of the | ||
22 | * VGA BIOS, and some cards, notably Adaptec 2940UW, take mortal offense. | ||
23 | */ | ||
24 | |||
25 | #define EISA_DEFAULT_IO_BASE 0x9000 /* start above 8th slot */ | ||
26 | #define DEFAULT_IO_BASE 0x8000 /* start at 8th slot */ | ||
27 | |||
28 | /* | ||
29 | * We try to make the DEFAULT_MEM_BASE addresses *always* have more than | ||
30 | * a single bit set. This is so that devices like the broken Myrinet card | ||
31 | * will always have a PCI memory address that will never match a IDSEL | ||
32 | * address in PCI Config space, which can cause problems with early rev cards. | ||
33 | */ | ||
34 | |||
35 | /* | ||
36 | * An XL is AVANTI (APECS) family, *but* it has only 27 bits of ISA address | ||
37 | * that get passed through the PCI<->ISA bridge chip. Although this causes | ||
38 | * us to set the PCI->Mem window bases lower than normal, we still allocate | ||
39 | * PCI bus devices' memory addresses *below* the low DMA mapping window, | ||
40 | * and hope they fit below 64Mb (to avoid conflicts), and so that they can | ||
41 | * be accessed via SPARSE space. | ||
42 | * | ||
43 | * We accept the risk that a broken Myrinet card will be put into a true XL | ||
44 | * and thus can more easily run into the problem described below. | ||
45 | */ | ||
46 | #define XL_DEFAULT_MEM_BASE ((16+2)*1024*1024) /* 16M to 64M-1 is avail */ | ||
47 | |||
48 | /* | ||
49 | * APECS and LCA have only 34 bits for physical addresses, thus limiting PCI | ||
50 | * bus memory addresses for SPARSE access to be less than 128Mb. | ||
51 | */ | ||
52 | #define APECS_AND_LCA_DEFAULT_MEM_BASE ((16+2)*1024*1024) | ||
53 | |||
54 | /* | ||
55 | * Because MCPCIA and T2 core logic support more bits for | ||
56 | * physical addresses, they should allow an expanded range of SPARSE | ||
57 | * memory addresses. However, we do not use them all, in order to | ||
58 | * avoid the HAE manipulation that would be needed. | ||
59 | */ | ||
60 | #define MCPCIA_DEFAULT_MEM_BASE ((32+2)*1024*1024) | ||
61 | #define T2_DEFAULT_MEM_BASE ((16+1)*1024*1024) | ||
62 | |||
63 | /* | ||
64 | * Because CIA and PYXIS have more bits for physical addresses, | ||
65 | * they support an expanded range of SPARSE memory addresses. | ||
66 | */ | ||
67 | #define DEFAULT_MEM_BASE ((128+16)*1024*1024) | ||
68 | |||
69 | /* ??? Experimenting with no HAE for CIA. */ | ||
70 | #define CIA_DEFAULT_MEM_BASE ((32+2)*1024*1024) | ||
71 | |||
72 | #define IRONGATE_DEFAULT_MEM_BASE ((256*8-16)*1024*1024) | ||
73 | |||
74 | #define DEFAULT_AGP_APER_SIZE (64*1024*1024) | ||
75 | |||
76 | /* | ||
77 | * A small note about bridges and interrupts. The DECchip 21050 (and | ||
78 | * later) adheres to the PCI-PCI bridge specification. This says that | ||
79 | * the interrupts on the other side of a bridge are swizzled in the | ||
80 | * following manner: | ||
81 | * | ||
82 | * Dev Interrupt Interrupt | ||
83 | * Pin on Pin on | ||
84 | * Device Connector | ||
85 | * | ||
86 | * 4 A A | ||
87 | * B B | ||
88 | * C C | ||
89 | * D D | ||
90 | * | ||
91 | * 5 A B | ||
92 | * B C | ||
93 | * C D | ||
94 | * D A | ||
95 | * | ||
96 | * 6 A C | ||
97 | * B D | ||
98 | * C A | ||
99 | * D B | ||
100 | * | ||
101 | * 7 A D | ||
102 | * B A | ||
103 | * C B | ||
104 | * D C | ||
105 | * | ||
106 | * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A. | ||
107 | * Thus, each swizzle is ((pin-1) + (device#-4)) % 4 | ||
108 | * | ||
109 | * The following code swizzles for exactly one bridge. The routine | ||
110 | * common_swizzle below handles multiple bridges. But there are a | ||
111 | * couple boards that do strange things, so we define this here. | ||
112 | */ | ||
113 | |||
114 | static inline u8 bridge_swizzle(u8 pin, u8 slot) | ||
115 | { | ||
116 | return (((pin-1) + slot) % 4) + 1; | ||
117 | } | ||
118 | |||
119 | |||
120 | /* The following macro is used to implement the table-based irq mapping | ||
121 | function for all single-bus Alphas. */ | ||
122 | |||
123 | #define COMMON_TABLE_LOOKUP \ | ||
124 | ({ long _ctl_ = -1; \ | ||
125 | if (slot >= min_idsel && slot <= max_idsel && pin < irqs_per_slot) \ | ||
126 | _ctl_ = irq_tab[slot - min_idsel][pin]; \ | ||
127 | _ctl_; }) | ||
128 | |||
129 | |||
130 | /* A PCI IOMMU allocation arena. There are typically two of these | ||
131 | regions per bus. */ | ||
132 | /* ??? The 8400 has a 32-byte pte entry, and the entire table apparently | ||
133 | lives directly on the host bridge (no tlb?). We don't support this | ||
134 | machine, but if we ever did, we'd need to parameterize all this quite | ||
135 | a bit further. Probably with per-bus operation tables. */ | ||
136 | |||
137 | struct pci_iommu_arena | ||
138 | { | ||
139 | spinlock_t lock; | ||
140 | struct pci_controller *hose; | ||
141 | #define IOMMU_INVALID_PTE 0x2 /* 32:63 bits MBZ */ | ||
142 | #define IOMMU_RESERVED_PTE 0xface | ||
143 | unsigned long *ptes; | ||
144 | dma_addr_t dma_base; | ||
145 | unsigned int size; | ||
146 | unsigned int next_entry; | ||
147 | unsigned int align_entry; | ||
148 | }; | ||
149 | |||
150 | #if defined(CONFIG_ALPHA_SRM) && \ | ||
151 | (defined(CONFIG_ALPHA_CIA) || defined(CONFIG_ALPHA_LCA)) | ||
152 | # define NEED_SRM_SAVE_RESTORE | ||
153 | #else | ||
154 | # undef NEED_SRM_SAVE_RESTORE | ||
155 | #endif | ||
156 | |||
157 | #if defined(CONFIG_ALPHA_GENERIC) || defined(NEED_SRM_SAVE_RESTORE) | ||
158 | # define ALPHA_RESTORE_SRM_SETUP | ||
159 | #else | ||
160 | # undef ALPHA_RESTORE_SRM_SETUP | ||
161 | #endif | ||
162 | |||
163 | #ifdef ALPHA_RESTORE_SRM_SETUP | ||
164 | /* Store PCI device configuration left by SRM here. */ | ||
165 | struct pdev_srm_saved_conf | ||
166 | { | ||
167 | struct pdev_srm_saved_conf *next; | ||
168 | struct pci_dev *dev; | ||
169 | }; | ||
170 | |||
171 | extern void pci_restore_srm_config(void); | ||
172 | #else | ||
173 | #define pdev_save_srm_config(dev) do {} while (0) | ||
174 | #define pci_restore_srm_config() do {} while (0) | ||
175 | #endif | ||
176 | |||
177 | /* The hose list. */ | ||
178 | extern struct pci_controller *hose_head, **hose_tail; | ||
179 | extern struct pci_controller *pci_isa_hose; | ||
180 | |||
181 | /* Indicate that we trust the console to configure things properly. */ | ||
182 | extern int pci_probe_only; | ||
183 | |||
184 | extern unsigned long alpha_agpgart_size; | ||
185 | |||
186 | extern void common_init_pci(void); | ||
187 | extern u8 common_swizzle(struct pci_dev *, u8 *); | ||
188 | extern struct pci_controller *alloc_pci_controller(void); | ||
189 | extern struct resource *alloc_resource(void); | ||
190 | |||
191 | extern struct pci_iommu_arena *iommu_arena_new_node(int, | ||
192 | struct pci_controller *, | ||
193 | dma_addr_t, unsigned long, | ||
194 | unsigned long); | ||
195 | extern struct pci_iommu_arena *iommu_arena_new(struct pci_controller *, | ||
196 | dma_addr_t, unsigned long, | ||
197 | unsigned long); | ||
198 | extern const char *const pci_io_names[]; | ||
199 | extern const char *const pci_mem_names[]; | ||
200 | extern const char pci_hae0_name[]; | ||
201 | |||
202 | extern unsigned long size_for_memory(unsigned long max); | ||
203 | |||
204 | extern int iommu_reserve(struct pci_iommu_arena *, long, long); | ||
205 | extern int iommu_release(struct pci_iommu_arena *, long, long); | ||
206 | extern int iommu_bind(struct pci_iommu_arena *, long, long, unsigned long *); | ||
207 | extern int iommu_unbind(struct pci_iommu_arena *, long, long); | ||
208 | |||
209 | |||
diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c new file mode 100644 index 000000000000..7cb23f12ecbd --- /dev/null +++ b/arch/alpha/kernel/pci_iommu.c | |||
@@ -0,0 +1,971 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/pci_iommu.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/mm.h> | ||
7 | #include <linux/pci.h> | ||
8 | #include <linux/slab.h> | ||
9 | #include <linux/bootmem.h> | ||
10 | |||
11 | #include <asm/io.h> | ||
12 | #include <asm/hwrpb.h> | ||
13 | |||
14 | #include "proto.h" | ||
15 | #include "pci_impl.h" | ||
16 | |||
17 | |||
18 | #define DEBUG_ALLOC 0 | ||
19 | #if DEBUG_ALLOC > 0 | ||
20 | # define DBGA(args...) printk(KERN_DEBUG args) | ||
21 | #else | ||
22 | # define DBGA(args...) | ||
23 | #endif | ||
24 | #if DEBUG_ALLOC > 1 | ||
25 | # define DBGA2(args...) printk(KERN_DEBUG args) | ||
26 | #else | ||
27 | # define DBGA2(args...) | ||
28 | #endif | ||
29 | |||
30 | #define DEBUG_NODIRECT 0 | ||
31 | #define DEBUG_FORCEDAC 0 | ||
32 | |||
33 | #define ISA_DMA_MASK 0x00ffffff | ||
34 | |||
35 | static inline unsigned long | ||
36 | mk_iommu_pte(unsigned long paddr) | ||
37 | { | ||
38 | return (paddr >> (PAGE_SHIFT-1)) | 1; | ||
39 | } | ||
40 | |||
41 | static inline long | ||
42 | calc_npages(long bytes) | ||
43 | { | ||
44 | return (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
45 | } | ||
46 | |||
47 | |||
48 | /* Return the minimum of MAX or the first power of two larger | ||
49 | than main memory. */ | ||
50 | |||
51 | unsigned long | ||
52 | size_for_memory(unsigned long max) | ||
53 | { | ||
54 | unsigned long mem = max_low_pfn << PAGE_SHIFT; | ||
55 | if (mem < max) | ||
56 | max = 1UL << ceil_log2(mem); | ||
57 | return max; | ||
58 | } | ||
59 | |||
60 | struct pci_iommu_arena * | ||
61 | iommu_arena_new_node(int nid, struct pci_controller *hose, dma_addr_t base, | ||
62 | unsigned long window_size, unsigned long align) | ||
63 | { | ||
64 | unsigned long mem_size; | ||
65 | struct pci_iommu_arena *arena; | ||
66 | |||
67 | mem_size = window_size / (PAGE_SIZE / sizeof(unsigned long)); | ||
68 | |||
69 | /* Note that the TLB lookup logic uses bitwise concatenation, | ||
70 | not addition, so the required arena alignment is based on | ||
71 | the size of the window. Retain the align parameter so that | ||
72 | particular systems can over-align the arena. */ | ||
73 | if (align < mem_size) | ||
74 | align = mem_size; | ||
75 | |||
76 | |||
77 | #ifdef CONFIG_DISCONTIGMEM | ||
78 | |||
79 | if (!NODE_DATA(nid) || | ||
80 | (NULL == (arena = alloc_bootmem_node(NODE_DATA(nid), | ||
81 | sizeof(*arena))))) { | ||
82 | printk("%s: couldn't allocate arena from node %d\n" | ||
83 | " falling back to system-wide allocation\n", | ||
84 | __FUNCTION__, nid); | ||
85 | arena = alloc_bootmem(sizeof(*arena)); | ||
86 | } | ||
87 | |||
88 | if (!NODE_DATA(nid) || | ||
89 | (NULL == (arena->ptes = __alloc_bootmem_node(NODE_DATA(nid), | ||
90 | mem_size, | ||
91 | align, | ||
92 | 0)))) { | ||
93 | printk("%s: couldn't allocate arena ptes from node %d\n" | ||
94 | " falling back to system-wide allocation\n", | ||
95 | __FUNCTION__, nid); | ||
96 | arena->ptes = __alloc_bootmem(mem_size, align, 0); | ||
97 | } | ||
98 | |||
99 | #else /* CONFIG_DISCONTIGMEM */ | ||
100 | |||
101 | arena = alloc_bootmem(sizeof(*arena)); | ||
102 | arena->ptes = __alloc_bootmem(mem_size, align, 0); | ||
103 | |||
104 | #endif /* CONFIG_DISCONTIGMEM */ | ||
105 | |||
106 | spin_lock_init(&arena->lock); | ||
107 | arena->hose = hose; | ||
108 | arena->dma_base = base; | ||
109 | arena->size = window_size; | ||
110 | arena->next_entry = 0; | ||
111 | |||
112 | /* Align allocations to a multiple of a page size. Not needed | ||
113 | unless there are chip bugs. */ | ||
114 | arena->align_entry = 1; | ||
115 | |||
116 | return arena; | ||
117 | } | ||
118 | |||
119 | struct pci_iommu_arena * | ||
120 | iommu_arena_new(struct pci_controller *hose, dma_addr_t base, | ||
121 | unsigned long window_size, unsigned long align) | ||
122 | { | ||
123 | return iommu_arena_new_node(0, hose, base, window_size, align); | ||
124 | } | ||
125 | |||
126 | /* Must be called with the arena lock held */ | ||
127 | static long | ||
128 | iommu_arena_find_pages(struct pci_iommu_arena *arena, long n, long mask) | ||
129 | { | ||
130 | unsigned long *ptes; | ||
131 | long i, p, nent; | ||
132 | |||
133 | /* Search forward for the first mask-aligned sequence of N free ptes */ | ||
134 | ptes = arena->ptes; | ||
135 | nent = arena->size >> PAGE_SHIFT; | ||
136 | p = (arena->next_entry + mask) & ~mask; | ||
137 | i = 0; | ||
138 | while (i < n && p+i < nent) { | ||
139 | if (ptes[p+i]) | ||
140 | p = (p + i + 1 + mask) & ~mask, i = 0; | ||
141 | else | ||
142 | i = i + 1; | ||
143 | } | ||
144 | |||
145 | if (i < n) { | ||
146 | /* Reached the end. Flush the TLB and restart the | ||
147 | search from the beginning. */ | ||
148 | alpha_mv.mv_pci_tbi(arena->hose, 0, -1); | ||
149 | |||
150 | p = 0, i = 0; | ||
151 | while (i < n && p+i < nent) { | ||
152 | if (ptes[p+i]) | ||
153 | p = (p + i + 1 + mask) & ~mask, i = 0; | ||
154 | else | ||
155 | i = i + 1; | ||
156 | } | ||
157 | |||
158 | if (i < n) | ||
159 | return -1; | ||
160 | } | ||
161 | |||
162 | /* Success. It's the responsibility of the caller to mark them | ||
163 | in use before releasing the lock */ | ||
164 | return p; | ||
165 | } | ||
166 | |||
167 | static long | ||
168 | iommu_arena_alloc(struct pci_iommu_arena *arena, long n, unsigned int align) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | unsigned long *ptes; | ||
172 | long i, p, mask; | ||
173 | |||
174 | spin_lock_irqsave(&arena->lock, flags); | ||
175 | |||
176 | /* Search for N empty ptes */ | ||
177 | ptes = arena->ptes; | ||
178 | mask = max(align, arena->align_entry) - 1; | ||
179 | p = iommu_arena_find_pages(arena, n, mask); | ||
180 | if (p < 0) { | ||
181 | spin_unlock_irqrestore(&arena->lock, flags); | ||
182 | return -1; | ||
183 | } | ||
184 | |||
185 | /* Success. Mark them all in use, ie not zero and invalid | ||
186 | for the iommu tlb that could load them from under us. | ||
187 | The chip specific bits will fill this in with something | ||
188 | kosher when we return. */ | ||
189 | for (i = 0; i < n; ++i) | ||
190 | ptes[p+i] = IOMMU_INVALID_PTE; | ||
191 | |||
192 | arena->next_entry = p + n; | ||
193 | spin_unlock_irqrestore(&arena->lock, flags); | ||
194 | |||
195 | return p; | ||
196 | } | ||
197 | |||
198 | static void | ||
199 | iommu_arena_free(struct pci_iommu_arena *arena, long ofs, long n) | ||
200 | { | ||
201 | unsigned long *p; | ||
202 | long i; | ||
203 | |||
204 | p = arena->ptes + ofs; | ||
205 | for (i = 0; i < n; ++i) | ||
206 | p[i] = 0; | ||
207 | } | ||
208 | |||
209 | /* Map a single buffer of the indicated size for PCI DMA in streaming | ||
210 | mode. The 32-bit PCI bus mastering address to use is returned. | ||
211 | Once the device is given the dma address, the device owns this memory | ||
212 | until either pci_unmap_single or pci_dma_sync_single is performed. */ | ||
213 | |||
214 | static dma_addr_t | ||
215 | pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size, | ||
216 | int dac_allowed) | ||
217 | { | ||
218 | struct pci_controller *hose = pdev ? pdev->sysdata : pci_isa_hose; | ||
219 | dma_addr_t max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK; | ||
220 | struct pci_iommu_arena *arena; | ||
221 | long npages, dma_ofs, i; | ||
222 | unsigned long paddr; | ||
223 | dma_addr_t ret; | ||
224 | unsigned int align = 0; | ||
225 | |||
226 | paddr = __pa(cpu_addr); | ||
227 | |||
228 | #if !DEBUG_NODIRECT | ||
229 | /* First check to see if we can use the direct map window. */ | ||
230 | if (paddr + size + __direct_map_base - 1 <= max_dma | ||
231 | && paddr + size <= __direct_map_size) { | ||
232 | ret = paddr + __direct_map_base; | ||
233 | |||
234 | DBGA2("pci_map_single: [%p,%lx] -> direct %lx from %p\n", | ||
235 | cpu_addr, size, ret, __builtin_return_address(0)); | ||
236 | |||
237 | return ret; | ||
238 | } | ||
239 | #endif | ||
240 | |||
241 | /* Next, use DAC if selected earlier. */ | ||
242 | if (dac_allowed) { | ||
243 | ret = paddr + alpha_mv.pci_dac_offset; | ||
244 | |||
245 | DBGA2("pci_map_single: [%p,%lx] -> DAC %lx from %p\n", | ||
246 | cpu_addr, size, ret, __builtin_return_address(0)); | ||
247 | |||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | /* If the machine doesn't define a pci_tbi routine, we have to | ||
252 | assume it doesn't support sg mapping, and, since we tried to | ||
253 | use direct_map above, it now must be considered an error. */ | ||
254 | if (! alpha_mv.mv_pci_tbi) { | ||
255 | static int been_here = 0; /* Only print the message once. */ | ||
256 | if (!been_here) { | ||
257 | printk(KERN_WARNING "pci_map_single: no HW sg\n"); | ||
258 | been_here = 1; | ||
259 | } | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | arena = hose->sg_pci; | ||
264 | if (!arena || arena->dma_base + arena->size - 1 > max_dma) | ||
265 | arena = hose->sg_isa; | ||
266 | |||
267 | npages = calc_npages((paddr & ~PAGE_MASK) + size); | ||
268 | |||
269 | /* Force allocation to 64KB boundary for ISA bridges. */ | ||
270 | if (pdev && pdev == isa_bridge) | ||
271 | align = 8; | ||
272 | dma_ofs = iommu_arena_alloc(arena, npages, align); | ||
273 | if (dma_ofs < 0) { | ||
274 | printk(KERN_WARNING "pci_map_single failed: " | ||
275 | "could not allocate dma page tables\n"); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | paddr &= PAGE_MASK; | ||
280 | for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) | ||
281 | arena->ptes[i + dma_ofs] = mk_iommu_pte(paddr); | ||
282 | |||
283 | ret = arena->dma_base + dma_ofs * PAGE_SIZE; | ||
284 | ret += (unsigned long)cpu_addr & ~PAGE_MASK; | ||
285 | |||
286 | DBGA2("pci_map_single: [%p,%lx] np %ld -> sg %lx from %p\n", | ||
287 | cpu_addr, size, npages, ret, __builtin_return_address(0)); | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | dma_addr_t | ||
293 | pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, int dir) | ||
294 | { | ||
295 | int dac_allowed; | ||
296 | |||
297 | if (dir == PCI_DMA_NONE) | ||
298 | BUG(); | ||
299 | |||
300 | dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; | ||
301 | return pci_map_single_1(pdev, cpu_addr, size, dac_allowed); | ||
302 | } | ||
303 | |||
304 | dma_addr_t | ||
305 | pci_map_page(struct pci_dev *pdev, struct page *page, unsigned long offset, | ||
306 | size_t size, int dir) | ||
307 | { | ||
308 | int dac_allowed; | ||
309 | |||
310 | if (dir == PCI_DMA_NONE) | ||
311 | BUG(); | ||
312 | |||
313 | dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; | ||
314 | return pci_map_single_1(pdev, (char *)page_address(page) + offset, | ||
315 | size, dac_allowed); | ||
316 | } | ||
317 | |||
318 | /* Unmap a single streaming mode DMA translation. The DMA_ADDR and | ||
319 | SIZE must match what was provided for in a previous pci_map_single | ||
320 | call. All other usages are undefined. After this call, reads by | ||
321 | the cpu to the buffer are guaranteed to see whatever the device | ||
322 | wrote there. */ | ||
323 | |||
324 | void | ||
325 | pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, | ||
326 | int direction) | ||
327 | { | ||
328 | unsigned long flags; | ||
329 | struct pci_controller *hose = pdev ? pdev->sysdata : pci_isa_hose; | ||
330 | struct pci_iommu_arena *arena; | ||
331 | long dma_ofs, npages; | ||
332 | |||
333 | if (direction == PCI_DMA_NONE) | ||
334 | BUG(); | ||
335 | |||
336 | if (dma_addr >= __direct_map_base | ||
337 | && dma_addr < __direct_map_base + __direct_map_size) { | ||
338 | /* Nothing to do. */ | ||
339 | |||
340 | DBGA2("pci_unmap_single: direct [%lx,%lx] from %p\n", | ||
341 | dma_addr, size, __builtin_return_address(0)); | ||
342 | |||
343 | return; | ||
344 | } | ||
345 | |||
346 | if (dma_addr > 0xffffffff) { | ||
347 | DBGA2("pci64_unmap_single: DAC [%lx,%lx] from %p\n", | ||
348 | dma_addr, size, __builtin_return_address(0)); | ||
349 | return; | ||
350 | } | ||
351 | |||
352 | arena = hose->sg_pci; | ||
353 | if (!arena || dma_addr < arena->dma_base) | ||
354 | arena = hose->sg_isa; | ||
355 | |||
356 | dma_ofs = (dma_addr - arena->dma_base) >> PAGE_SHIFT; | ||
357 | if (dma_ofs * PAGE_SIZE >= arena->size) { | ||
358 | printk(KERN_ERR "Bogus pci_unmap_single: dma_addr %lx " | ||
359 | " base %lx size %x\n", dma_addr, arena->dma_base, | ||
360 | arena->size); | ||
361 | return; | ||
362 | BUG(); | ||
363 | } | ||
364 | |||
365 | npages = calc_npages((dma_addr & ~PAGE_MASK) + size); | ||
366 | |||
367 | spin_lock_irqsave(&arena->lock, flags); | ||
368 | |||
369 | iommu_arena_free(arena, dma_ofs, npages); | ||
370 | |||
371 | /* If we're freeing ptes above the `next_entry' pointer (they | ||
372 | may have snuck back into the TLB since the last wrap flush), | ||
373 | we need to flush the TLB before reallocating the latter. */ | ||
374 | if (dma_ofs >= arena->next_entry) | ||
375 | alpha_mv.mv_pci_tbi(hose, dma_addr, dma_addr + size - 1); | ||
376 | |||
377 | spin_unlock_irqrestore(&arena->lock, flags); | ||
378 | |||
379 | DBGA2("pci_unmap_single: sg [%lx,%lx] np %ld from %p\n", | ||
380 | dma_addr, size, npages, __builtin_return_address(0)); | ||
381 | } | ||
382 | |||
383 | void | ||
384 | pci_unmap_page(struct pci_dev *pdev, dma_addr_t dma_addr, | ||
385 | size_t size, int direction) | ||
386 | { | ||
387 | pci_unmap_single(pdev, dma_addr, size, direction); | ||
388 | } | ||
389 | |||
390 | /* Allocate and map kernel buffer using consistent mode DMA for PCI | ||
391 | device. Returns non-NULL cpu-view pointer to the buffer if | ||
392 | successful and sets *DMA_ADDRP to the pci side dma address as well, | ||
393 | else DMA_ADDRP is undefined. */ | ||
394 | |||
395 | void * | ||
396 | pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) | ||
397 | { | ||
398 | void *cpu_addr; | ||
399 | long order = get_order(size); | ||
400 | int gfp = GFP_ATOMIC; | ||
401 | |||
402 | try_again: | ||
403 | cpu_addr = (void *)__get_free_pages(gfp, order); | ||
404 | if (! cpu_addr) { | ||
405 | printk(KERN_INFO "pci_alloc_consistent: " | ||
406 | "get_free_pages failed from %p\n", | ||
407 | __builtin_return_address(0)); | ||
408 | /* ??? Really atomic allocation? Otherwise we could play | ||
409 | with vmalloc and sg if we can't find contiguous memory. */ | ||
410 | return NULL; | ||
411 | } | ||
412 | memset(cpu_addr, 0, size); | ||
413 | |||
414 | *dma_addrp = pci_map_single_1(pdev, cpu_addr, size, 0); | ||
415 | if (*dma_addrp == 0) { | ||
416 | free_pages((unsigned long)cpu_addr, order); | ||
417 | if (alpha_mv.mv_pci_tbi || (gfp & GFP_DMA)) | ||
418 | return NULL; | ||
419 | /* The address doesn't fit required mask and we | ||
420 | do not have iommu. Try again with GFP_DMA. */ | ||
421 | gfp |= GFP_DMA; | ||
422 | goto try_again; | ||
423 | } | ||
424 | |||
425 | DBGA2("pci_alloc_consistent: %lx -> [%p,%x] from %p\n", | ||
426 | size, cpu_addr, *dma_addrp, __builtin_return_address(0)); | ||
427 | |||
428 | return cpu_addr; | ||
429 | } | ||
430 | |||
431 | /* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must | ||
432 | be values that were returned from pci_alloc_consistent. SIZE must | ||
433 | be the same as what as passed into pci_alloc_consistent. | ||
434 | References to the memory and mappings associated with CPU_ADDR or | ||
435 | DMA_ADDR past this call are illegal. */ | ||
436 | |||
437 | void | ||
438 | pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr, | ||
439 | dma_addr_t dma_addr) | ||
440 | { | ||
441 | pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); | ||
442 | free_pages((unsigned long)cpu_addr, get_order(size)); | ||
443 | |||
444 | DBGA2("pci_free_consistent: [%x,%lx] from %p\n", | ||
445 | dma_addr, size, __builtin_return_address(0)); | ||
446 | } | ||
447 | |||
448 | |||
449 | /* Classify the elements of the scatterlist. Write dma_address | ||
450 | of each element with: | ||
451 | 0 : Followers all physically adjacent. | ||
452 | 1 : Followers all virtually adjacent. | ||
453 | -1 : Not leader, physically adjacent to previous. | ||
454 | -2 : Not leader, virtually adjacent to previous. | ||
455 | Write dma_length of each leader with the combined lengths of | ||
456 | the mergable followers. */ | ||
457 | |||
458 | #define SG_ENT_VIRT_ADDRESS(SG) (page_address((SG)->page) + (SG)->offset) | ||
459 | #define SG_ENT_PHYS_ADDRESS(SG) __pa(SG_ENT_VIRT_ADDRESS(SG)) | ||
460 | |||
461 | static void | ||
462 | sg_classify(struct scatterlist *sg, struct scatterlist *end, int virt_ok) | ||
463 | { | ||
464 | unsigned long next_paddr; | ||
465 | struct scatterlist *leader; | ||
466 | long leader_flag, leader_length; | ||
467 | |||
468 | leader = sg; | ||
469 | leader_flag = 0; | ||
470 | leader_length = leader->length; | ||
471 | next_paddr = SG_ENT_PHYS_ADDRESS(leader) + leader_length; | ||
472 | |||
473 | for (++sg; sg < end; ++sg) { | ||
474 | unsigned long addr, len; | ||
475 | addr = SG_ENT_PHYS_ADDRESS(sg); | ||
476 | len = sg->length; | ||
477 | |||
478 | if (next_paddr == addr) { | ||
479 | sg->dma_address = -1; | ||
480 | leader_length += len; | ||
481 | } else if (((next_paddr | addr) & ~PAGE_MASK) == 0 && virt_ok) { | ||
482 | sg->dma_address = -2; | ||
483 | leader_flag = 1; | ||
484 | leader_length += len; | ||
485 | } else { | ||
486 | leader->dma_address = leader_flag; | ||
487 | leader->dma_length = leader_length; | ||
488 | leader = sg; | ||
489 | leader_flag = 0; | ||
490 | leader_length = len; | ||
491 | } | ||
492 | |||
493 | next_paddr = addr + len; | ||
494 | } | ||
495 | |||
496 | leader->dma_address = leader_flag; | ||
497 | leader->dma_length = leader_length; | ||
498 | } | ||
499 | |||
500 | /* Given a scatterlist leader, choose an allocation method and fill | ||
501 | in the blanks. */ | ||
502 | |||
503 | static int | ||
504 | sg_fill(struct scatterlist *leader, struct scatterlist *end, | ||
505 | struct scatterlist *out, struct pci_iommu_arena *arena, | ||
506 | dma_addr_t max_dma, int dac_allowed) | ||
507 | { | ||
508 | unsigned long paddr = SG_ENT_PHYS_ADDRESS(leader); | ||
509 | long size = leader->dma_length; | ||
510 | struct scatterlist *sg; | ||
511 | unsigned long *ptes; | ||
512 | long npages, dma_ofs, i; | ||
513 | |||
514 | #if !DEBUG_NODIRECT | ||
515 | /* If everything is physically contiguous, and the addresses | ||
516 | fall into the direct-map window, use it. */ | ||
517 | if (leader->dma_address == 0 | ||
518 | && paddr + size + __direct_map_base - 1 <= max_dma | ||
519 | && paddr + size <= __direct_map_size) { | ||
520 | out->dma_address = paddr + __direct_map_base; | ||
521 | out->dma_length = size; | ||
522 | |||
523 | DBGA(" sg_fill: [%p,%lx] -> direct %lx\n", | ||
524 | __va(paddr), size, out->dma_address); | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | #endif | ||
529 | |||
530 | /* If physically contiguous and DAC is available, use it. */ | ||
531 | if (leader->dma_address == 0 && dac_allowed) { | ||
532 | out->dma_address = paddr + alpha_mv.pci_dac_offset; | ||
533 | out->dma_length = size; | ||
534 | |||
535 | DBGA(" sg_fill: [%p,%lx] -> DAC %lx\n", | ||
536 | __va(paddr), size, out->dma_address); | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | /* Otherwise, we'll use the iommu to make the pages virtually | ||
542 | contiguous. */ | ||
543 | |||
544 | paddr &= ~PAGE_MASK; | ||
545 | npages = calc_npages(paddr + size); | ||
546 | dma_ofs = iommu_arena_alloc(arena, npages, 0); | ||
547 | if (dma_ofs < 0) { | ||
548 | /* If we attempted a direct map above but failed, die. */ | ||
549 | if (leader->dma_address == 0) | ||
550 | return -1; | ||
551 | |||
552 | /* Otherwise, break up the remaining virtually contiguous | ||
553 | hunks into individual direct maps and retry. */ | ||
554 | sg_classify(leader, end, 0); | ||
555 | return sg_fill(leader, end, out, arena, max_dma, dac_allowed); | ||
556 | } | ||
557 | |||
558 | out->dma_address = arena->dma_base + dma_ofs*PAGE_SIZE + paddr; | ||
559 | out->dma_length = size; | ||
560 | |||
561 | DBGA(" sg_fill: [%p,%lx] -> sg %lx np %ld\n", | ||
562 | __va(paddr), size, out->dma_address, npages); | ||
563 | |||
564 | /* All virtually contiguous. We need to find the length of each | ||
565 | physically contiguous subsegment to fill in the ptes. */ | ||
566 | ptes = &arena->ptes[dma_ofs]; | ||
567 | sg = leader; | ||
568 | do { | ||
569 | #if DEBUG_ALLOC > 0 | ||
570 | struct scatterlist *last_sg = sg; | ||
571 | #endif | ||
572 | |||
573 | size = sg->length; | ||
574 | paddr = SG_ENT_PHYS_ADDRESS(sg); | ||
575 | |||
576 | while (sg+1 < end && (int) sg[1].dma_address == -1) { | ||
577 | size += sg[1].length; | ||
578 | sg++; | ||
579 | } | ||
580 | |||
581 | npages = calc_npages((paddr & ~PAGE_MASK) + size); | ||
582 | |||
583 | paddr &= PAGE_MASK; | ||
584 | for (i = 0; i < npages; ++i, paddr += PAGE_SIZE) | ||
585 | *ptes++ = mk_iommu_pte(paddr); | ||
586 | |||
587 | #if DEBUG_ALLOC > 0 | ||
588 | DBGA(" (%ld) [%p,%x] np %ld\n", | ||
589 | last_sg - leader, SG_ENT_VIRT_ADDRESS(last_sg), | ||
590 | last_sg->length, npages); | ||
591 | while (++last_sg <= sg) { | ||
592 | DBGA(" (%ld) [%p,%x] cont\n", | ||
593 | last_sg - leader, SG_ENT_VIRT_ADDRESS(last_sg), | ||
594 | last_sg->length); | ||
595 | } | ||
596 | #endif | ||
597 | } while (++sg < end && (int) sg->dma_address < 0); | ||
598 | |||
599 | return 1; | ||
600 | } | ||
601 | |||
602 | int | ||
603 | pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, | ||
604 | int direction) | ||
605 | { | ||
606 | struct scatterlist *start, *end, *out; | ||
607 | struct pci_controller *hose; | ||
608 | struct pci_iommu_arena *arena; | ||
609 | dma_addr_t max_dma; | ||
610 | int dac_allowed; | ||
611 | |||
612 | if (direction == PCI_DMA_NONE) | ||
613 | BUG(); | ||
614 | |||
615 | dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; | ||
616 | |||
617 | /* Fast path single entry scatterlists. */ | ||
618 | if (nents == 1) { | ||
619 | sg->dma_length = sg->length; | ||
620 | sg->dma_address | ||
621 | = pci_map_single_1(pdev, SG_ENT_VIRT_ADDRESS(sg), | ||
622 | sg->length, dac_allowed); | ||
623 | return sg->dma_address != 0; | ||
624 | } | ||
625 | |||
626 | start = sg; | ||
627 | end = sg + nents; | ||
628 | |||
629 | /* First, prepare information about the entries. */ | ||
630 | sg_classify(sg, end, alpha_mv.mv_pci_tbi != 0); | ||
631 | |||
632 | /* Second, figure out where we're going to map things. */ | ||
633 | if (alpha_mv.mv_pci_tbi) { | ||
634 | hose = pdev ? pdev->sysdata : pci_isa_hose; | ||
635 | max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK; | ||
636 | arena = hose->sg_pci; | ||
637 | if (!arena || arena->dma_base + arena->size - 1 > max_dma) | ||
638 | arena = hose->sg_isa; | ||
639 | } else { | ||
640 | max_dma = -1; | ||
641 | arena = NULL; | ||
642 | hose = NULL; | ||
643 | } | ||
644 | |||
645 | /* Third, iterate over the scatterlist leaders and allocate | ||
646 | dma space as needed. */ | ||
647 | for (out = sg; sg < end; ++sg) { | ||
648 | if ((int) sg->dma_address < 0) | ||
649 | continue; | ||
650 | if (sg_fill(sg, end, out, arena, max_dma, dac_allowed) < 0) | ||
651 | goto error; | ||
652 | out++; | ||
653 | } | ||
654 | |||
655 | /* Mark the end of the list for pci_unmap_sg. */ | ||
656 | if (out < end) | ||
657 | out->dma_length = 0; | ||
658 | |||
659 | if (out - start == 0) | ||
660 | printk(KERN_WARNING "pci_map_sg failed: no entries?\n"); | ||
661 | DBGA("pci_map_sg: %ld entries\n", out - start); | ||
662 | |||
663 | return out - start; | ||
664 | |||
665 | error: | ||
666 | printk(KERN_WARNING "pci_map_sg failed: " | ||
667 | "could not allocate dma page tables\n"); | ||
668 | |||
669 | /* Some allocation failed while mapping the scatterlist | ||
670 | entries. Unmap them now. */ | ||
671 | if (out > start) | ||
672 | pci_unmap_sg(pdev, start, out - start, direction); | ||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | /* Unmap a set of streaming mode DMA translations. Again, cpu read | ||
677 | rules concerning calls here are the same as for pci_unmap_single() | ||
678 | above. */ | ||
679 | |||
680 | void | ||
681 | pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, | ||
682 | int direction) | ||
683 | { | ||
684 | unsigned long flags; | ||
685 | struct pci_controller *hose; | ||
686 | struct pci_iommu_arena *arena; | ||
687 | struct scatterlist *end; | ||
688 | dma_addr_t max_dma; | ||
689 | dma_addr_t fbeg, fend; | ||
690 | |||
691 | if (direction == PCI_DMA_NONE) | ||
692 | BUG(); | ||
693 | |||
694 | if (! alpha_mv.mv_pci_tbi) | ||
695 | return; | ||
696 | |||
697 | hose = pdev ? pdev->sysdata : pci_isa_hose; | ||
698 | max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK; | ||
699 | arena = hose->sg_pci; | ||
700 | if (!arena || arena->dma_base + arena->size - 1 > max_dma) | ||
701 | arena = hose->sg_isa; | ||
702 | |||
703 | fbeg = -1, fend = 0; | ||
704 | |||
705 | spin_lock_irqsave(&arena->lock, flags); | ||
706 | |||
707 | for (end = sg + nents; sg < end; ++sg) { | ||
708 | dma64_addr_t addr; | ||
709 | size_t size; | ||
710 | long npages, ofs; | ||
711 | dma_addr_t tend; | ||
712 | |||
713 | addr = sg->dma_address; | ||
714 | size = sg->dma_length; | ||
715 | if (!size) | ||
716 | break; | ||
717 | |||
718 | if (addr > 0xffffffff) { | ||
719 | /* It's a DAC address -- nothing to do. */ | ||
720 | DBGA(" (%ld) DAC [%lx,%lx]\n", | ||
721 | sg - end + nents, addr, size); | ||
722 | continue; | ||
723 | } | ||
724 | |||
725 | if (addr >= __direct_map_base | ||
726 | && addr < __direct_map_base + __direct_map_size) { | ||
727 | /* Nothing to do. */ | ||
728 | DBGA(" (%ld) direct [%lx,%lx]\n", | ||
729 | sg - end + nents, addr, size); | ||
730 | continue; | ||
731 | } | ||
732 | |||
733 | DBGA(" (%ld) sg [%lx,%lx]\n", | ||
734 | sg - end + nents, addr, size); | ||
735 | |||
736 | npages = calc_npages((addr & ~PAGE_MASK) + size); | ||
737 | ofs = (addr - arena->dma_base) >> PAGE_SHIFT; | ||
738 | iommu_arena_free(arena, ofs, npages); | ||
739 | |||
740 | tend = addr + size - 1; | ||
741 | if (fbeg > addr) fbeg = addr; | ||
742 | if (fend < tend) fend = tend; | ||
743 | } | ||
744 | |||
745 | /* If we're freeing ptes above the `next_entry' pointer (they | ||
746 | may have snuck back into the TLB since the last wrap flush), | ||
747 | we need to flush the TLB before reallocating the latter. */ | ||
748 | if ((fend - arena->dma_base) >> PAGE_SHIFT >= arena->next_entry) | ||
749 | alpha_mv.mv_pci_tbi(hose, fbeg, fend); | ||
750 | |||
751 | spin_unlock_irqrestore(&arena->lock, flags); | ||
752 | |||
753 | DBGA("pci_unmap_sg: %ld entries\n", nents - (end - sg)); | ||
754 | } | ||
755 | |||
756 | |||
757 | /* Return whether the given PCI device DMA address mask can be | ||
758 | supported properly. */ | ||
759 | |||
760 | int | ||
761 | pci_dma_supported(struct pci_dev *pdev, u64 mask) | ||
762 | { | ||
763 | struct pci_controller *hose; | ||
764 | struct pci_iommu_arena *arena; | ||
765 | |||
766 | /* If there exists a direct map, and the mask fits either | ||
767 | the entire direct mapped space or the total system memory as | ||
768 | shifted by the map base */ | ||
769 | if (__direct_map_size != 0 | ||
770 | && (__direct_map_base + __direct_map_size - 1 <= mask || | ||
771 | __direct_map_base + (max_low_pfn << PAGE_SHIFT) - 1 <= mask)) | ||
772 | return 1; | ||
773 | |||
774 | /* Check that we have a scatter-gather arena that fits. */ | ||
775 | hose = pdev ? pdev->sysdata : pci_isa_hose; | ||
776 | arena = hose->sg_isa; | ||
777 | if (arena && arena->dma_base + arena->size - 1 <= mask) | ||
778 | return 1; | ||
779 | arena = hose->sg_pci; | ||
780 | if (arena && arena->dma_base + arena->size - 1 <= mask) | ||
781 | return 1; | ||
782 | |||
783 | /* As last resort try ZONE_DMA. */ | ||
784 | if (!__direct_map_base && MAX_DMA_ADDRESS - IDENT_ADDR - 1 <= mask) | ||
785 | return 1; | ||
786 | |||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | |||
791 | /* | ||
792 | * AGP GART extensions to the IOMMU | ||
793 | */ | ||
794 | int | ||
795 | iommu_reserve(struct pci_iommu_arena *arena, long pg_count, long align_mask) | ||
796 | { | ||
797 | unsigned long flags; | ||
798 | unsigned long *ptes; | ||
799 | long i, p; | ||
800 | |||
801 | if (!arena) return -EINVAL; | ||
802 | |||
803 | spin_lock_irqsave(&arena->lock, flags); | ||
804 | |||
805 | /* Search for N empty ptes. */ | ||
806 | ptes = arena->ptes; | ||
807 | p = iommu_arena_find_pages(arena, pg_count, align_mask); | ||
808 | if (p < 0) { | ||
809 | spin_unlock_irqrestore(&arena->lock, flags); | ||
810 | return -1; | ||
811 | } | ||
812 | |||
813 | /* Success. Mark them all reserved (ie not zero and invalid) | ||
814 | for the iommu tlb that could load them from under us. | ||
815 | They will be filled in with valid bits by _bind() */ | ||
816 | for (i = 0; i < pg_count; ++i) | ||
817 | ptes[p+i] = IOMMU_RESERVED_PTE; | ||
818 | |||
819 | arena->next_entry = p + pg_count; | ||
820 | spin_unlock_irqrestore(&arena->lock, flags); | ||
821 | |||
822 | return p; | ||
823 | } | ||
824 | |||
825 | int | ||
826 | iommu_release(struct pci_iommu_arena *arena, long pg_start, long pg_count) | ||
827 | { | ||
828 | unsigned long *ptes; | ||
829 | long i; | ||
830 | |||
831 | if (!arena) return -EINVAL; | ||
832 | |||
833 | ptes = arena->ptes; | ||
834 | |||
835 | /* Make sure they're all reserved first... */ | ||
836 | for(i = pg_start; i < pg_start + pg_count; i++) | ||
837 | if (ptes[i] != IOMMU_RESERVED_PTE) | ||
838 | return -EBUSY; | ||
839 | |||
840 | iommu_arena_free(arena, pg_start, pg_count); | ||
841 | return 0; | ||
842 | } | ||
843 | |||
844 | int | ||
845 | iommu_bind(struct pci_iommu_arena *arena, long pg_start, long pg_count, | ||
846 | unsigned long *physaddrs) | ||
847 | { | ||
848 | unsigned long flags; | ||
849 | unsigned long *ptes; | ||
850 | long i, j; | ||
851 | |||
852 | if (!arena) return -EINVAL; | ||
853 | |||
854 | spin_lock_irqsave(&arena->lock, flags); | ||
855 | |||
856 | ptes = arena->ptes; | ||
857 | |||
858 | for(j = pg_start; j < pg_start + pg_count; j++) { | ||
859 | if (ptes[j] != IOMMU_RESERVED_PTE) { | ||
860 | spin_unlock_irqrestore(&arena->lock, flags); | ||
861 | return -EBUSY; | ||
862 | } | ||
863 | } | ||
864 | |||
865 | for(i = 0, j = pg_start; i < pg_count; i++, j++) | ||
866 | ptes[j] = mk_iommu_pte(physaddrs[i]); | ||
867 | |||
868 | spin_unlock_irqrestore(&arena->lock, flags); | ||
869 | |||
870 | return 0; | ||
871 | } | ||
872 | |||
873 | int | ||
874 | iommu_unbind(struct pci_iommu_arena *arena, long pg_start, long pg_count) | ||
875 | { | ||
876 | unsigned long *p; | ||
877 | long i; | ||
878 | |||
879 | if (!arena) return -EINVAL; | ||
880 | |||
881 | p = arena->ptes + pg_start; | ||
882 | for(i = 0; i < pg_count; i++) | ||
883 | p[i] = IOMMU_RESERVED_PTE; | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | /* True if the machine supports DAC addressing, and DEV can | ||
889 | make use of it given MASK. */ | ||
890 | |||
891 | int | ||
892 | pci_dac_dma_supported(struct pci_dev *dev, u64 mask) | ||
893 | { | ||
894 | dma64_addr_t dac_offset = alpha_mv.pci_dac_offset; | ||
895 | int ok = 1; | ||
896 | |||
897 | /* If this is not set, the machine doesn't support DAC at all. */ | ||
898 | if (dac_offset == 0) | ||
899 | ok = 0; | ||
900 | |||
901 | /* The device has to be able to address our DAC bit. */ | ||
902 | if ((dac_offset & dev->dma_mask) != dac_offset) | ||
903 | ok = 0; | ||
904 | |||
905 | /* If both conditions above are met, we are fine. */ | ||
906 | DBGA("pci_dac_dma_supported %s from %p\n", | ||
907 | ok ? "yes" : "no", __builtin_return_address(0)); | ||
908 | |||
909 | return ok; | ||
910 | } | ||
911 | |||
912 | dma64_addr_t | ||
913 | pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, | ||
914 | unsigned long offset, int direction) | ||
915 | { | ||
916 | return (alpha_mv.pci_dac_offset | ||
917 | + __pa(page_address(page)) | ||
918 | + (dma64_addr_t) offset); | ||
919 | } | ||
920 | |||
921 | struct page * | ||
922 | pci_dac_dma_to_page(struct pci_dev *pdev, dma64_addr_t dma_addr) | ||
923 | { | ||
924 | unsigned long paddr = (dma_addr & PAGE_MASK) - alpha_mv.pci_dac_offset; | ||
925 | return virt_to_page(__va(paddr)); | ||
926 | } | ||
927 | |||
928 | unsigned long | ||
929 | pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr) | ||
930 | { | ||
931 | return (dma_addr & ~PAGE_MASK); | ||
932 | } | ||
933 | |||
934 | |||
935 | /* Helper for generic DMA-mapping functions. */ | ||
936 | |||
937 | struct pci_dev * | ||
938 | alpha_gendev_to_pci(struct device *dev) | ||
939 | { | ||
940 | if (dev && dev->bus == &pci_bus_type) | ||
941 | return to_pci_dev(dev); | ||
942 | |||
943 | /* Assume that non-PCI devices asking for DMA are either ISA or EISA, | ||
944 | BUG() otherwise. */ | ||
945 | BUG_ON(!isa_bridge); | ||
946 | |||
947 | /* Assume non-busmaster ISA DMA when dma_mask is not set (the ISA | ||
948 | bridge is bus master then). */ | ||
949 | if (!dev || !dev->dma_mask || !*dev->dma_mask) | ||
950 | return isa_bridge; | ||
951 | |||
952 | /* For EISA bus masters, return isa_bridge (it might have smaller | ||
953 | dma_mask due to wiring limitations). */ | ||
954 | if (*dev->dma_mask >= isa_bridge->dma_mask) | ||
955 | return isa_bridge; | ||
956 | |||
957 | /* This assumes ISA bus master with dma_mask 0xffffff. */ | ||
958 | return NULL; | ||
959 | } | ||
960 | |||
961 | int | ||
962 | dma_set_mask(struct device *dev, u64 mask) | ||
963 | { | ||
964 | if (!dev->dma_mask || | ||
965 | !pci_dma_supported(alpha_gendev_to_pci(dev), mask)) | ||
966 | return -EIO; | ||
967 | |||
968 | *dev->dma_mask = mask; | ||
969 | |||
970 | return 0; | ||
971 | } | ||
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c new file mode 100644 index 000000000000..4933f3ce5833 --- /dev/null +++ b/arch/alpha/kernel/process.c | |||
@@ -0,0 +1,528 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/process.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This file handles the architecture-dependent parts of process handling. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/smp.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/stddef.h> | ||
20 | #include <linux/unistd.h> | ||
21 | #include <linux/ptrace.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/user.h> | ||
24 | #include <linux/a.out.h> | ||
25 | #include <linux/utsname.h> | ||
26 | #include <linux/time.h> | ||
27 | #include <linux/major.h> | ||
28 | #include <linux/stat.h> | ||
29 | #include <linux/mman.h> | ||
30 | #include <linux/elfcore.h> | ||
31 | #include <linux/reboot.h> | ||
32 | #include <linux/tty.h> | ||
33 | #include <linux/console.h> | ||
34 | |||
35 | #include <asm/reg.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #include <asm/system.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/pgtable.h> | ||
40 | #include <asm/hwrpb.h> | ||
41 | #include <asm/fpu.h> | ||
42 | |||
43 | #include "proto.h" | ||
44 | #include "pci_impl.h" | ||
45 | |||
46 | void default_idle(void) | ||
47 | { | ||
48 | barrier(); | ||
49 | } | ||
50 | |||
51 | void | ||
52 | cpu_idle(void) | ||
53 | { | ||
54 | while (1) { | ||
55 | void (*idle)(void) = default_idle; | ||
56 | /* FIXME -- EV6 and LCA45 know how to power down | ||
57 | the CPU. */ | ||
58 | |||
59 | while (!need_resched()) | ||
60 | idle(); | ||
61 | schedule(); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | |||
66 | struct halt_info { | ||
67 | int mode; | ||
68 | char *restart_cmd; | ||
69 | }; | ||
70 | |||
71 | static void | ||
72 | common_shutdown_1(void *generic_ptr) | ||
73 | { | ||
74 | struct halt_info *how = (struct halt_info *)generic_ptr; | ||
75 | struct percpu_struct *cpup; | ||
76 | unsigned long *pflags, flags; | ||
77 | int cpuid = smp_processor_id(); | ||
78 | |||
79 | /* No point in taking interrupts anymore. */ | ||
80 | local_irq_disable(); | ||
81 | |||
82 | cpup = (struct percpu_struct *) | ||
83 | ((unsigned long)hwrpb + hwrpb->processor_offset | ||
84 | + hwrpb->processor_size * cpuid); | ||
85 | pflags = &cpup->flags; | ||
86 | flags = *pflags; | ||
87 | |||
88 | /* Clear reason to "default"; clear "bootstrap in progress". */ | ||
89 | flags &= ~0x00ff0001UL; | ||
90 | |||
91 | #ifdef CONFIG_SMP | ||
92 | /* Secondaries halt here. */ | ||
93 | if (cpuid != boot_cpuid) { | ||
94 | flags |= 0x00040000UL; /* "remain halted" */ | ||
95 | *pflags = flags; | ||
96 | clear_bit(cpuid, &cpu_present_mask); | ||
97 | halt(); | ||
98 | } | ||
99 | #endif | ||
100 | |||
101 | if (how->mode == LINUX_REBOOT_CMD_RESTART) { | ||
102 | if (!how->restart_cmd) { | ||
103 | flags |= 0x00020000UL; /* "cold bootstrap" */ | ||
104 | } else { | ||
105 | /* For SRM, we could probably set environment | ||
106 | variables to get this to work. We'd have to | ||
107 | delay this until after srm_paging_stop unless | ||
108 | we ever got srm_fixup working. | ||
109 | |||
110 | At the moment, SRM will use the last boot device, | ||
111 | but the file and flags will be the defaults, when | ||
112 | doing a "warm" bootstrap. */ | ||
113 | flags |= 0x00030000UL; /* "warm bootstrap" */ | ||
114 | } | ||
115 | } else { | ||
116 | flags |= 0x00040000UL; /* "remain halted" */ | ||
117 | } | ||
118 | *pflags = flags; | ||
119 | |||
120 | #ifdef CONFIG_SMP | ||
121 | /* Wait for the secondaries to halt. */ | ||
122 | cpu_clear(boot_cpuid, cpu_possible_map); | ||
123 | while (cpus_weight(cpu_possible_map)) | ||
124 | barrier(); | ||
125 | #endif | ||
126 | |||
127 | /* If booted from SRM, reset some of the original environment. */ | ||
128 | if (alpha_using_srm) { | ||
129 | #ifdef CONFIG_DUMMY_CONSOLE | ||
130 | /* This has the effect of resetting the VGA video origin. */ | ||
131 | take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); | ||
132 | #endif | ||
133 | pci_restore_srm_config(); | ||
134 | set_hae(srm_hae); | ||
135 | } | ||
136 | |||
137 | if (alpha_mv.kill_arch) | ||
138 | alpha_mv.kill_arch(how->mode); | ||
139 | |||
140 | if (! alpha_using_srm && how->mode != LINUX_REBOOT_CMD_RESTART) { | ||
141 | /* Unfortunately, since MILO doesn't currently understand | ||
142 | the hwrpb bits above, we can't reliably halt the | ||
143 | processor and keep it halted. So just loop. */ | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | if (alpha_using_srm) | ||
148 | srm_paging_stop(); | ||
149 | |||
150 | halt(); | ||
151 | } | ||
152 | |||
153 | static void | ||
154 | common_shutdown(int mode, char *restart_cmd) | ||
155 | { | ||
156 | struct halt_info args; | ||
157 | args.mode = mode; | ||
158 | args.restart_cmd = restart_cmd; | ||
159 | on_each_cpu(common_shutdown_1, &args, 1, 0); | ||
160 | } | ||
161 | |||
162 | void | ||
163 | machine_restart(char *restart_cmd) | ||
164 | { | ||
165 | common_shutdown(LINUX_REBOOT_CMD_RESTART, restart_cmd); | ||
166 | } | ||
167 | |||
168 | EXPORT_SYMBOL(machine_restart); | ||
169 | |||
170 | void | ||
171 | machine_halt(void) | ||
172 | { | ||
173 | common_shutdown(LINUX_REBOOT_CMD_HALT, NULL); | ||
174 | } | ||
175 | |||
176 | EXPORT_SYMBOL(machine_halt); | ||
177 | |||
178 | void | ||
179 | machine_power_off(void) | ||
180 | { | ||
181 | common_shutdown(LINUX_REBOOT_CMD_POWER_OFF, NULL); | ||
182 | } | ||
183 | |||
184 | EXPORT_SYMBOL(machine_power_off); | ||
185 | |||
186 | /* Used by sysrq-p, among others. I don't believe r9-r15 are ever | ||
187 | saved in the context it's used. */ | ||
188 | |||
189 | void | ||
190 | show_regs(struct pt_regs *regs) | ||
191 | { | ||
192 | dik_show_regs(regs, NULL); | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * Re-start a thread when doing execve() | ||
197 | */ | ||
198 | void | ||
199 | start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) | ||
200 | { | ||
201 | set_fs(USER_DS); | ||
202 | regs->pc = pc; | ||
203 | regs->ps = 8; | ||
204 | wrusp(sp); | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Free current thread data structures etc.. | ||
209 | */ | ||
210 | void | ||
211 | exit_thread(void) | ||
212 | { | ||
213 | } | ||
214 | |||
215 | void | ||
216 | flush_thread(void) | ||
217 | { | ||
218 | /* Arrange for each exec'ed process to start off with a clean slate | ||
219 | with respect to the FPU. This is all exceptions disabled. */ | ||
220 | current_thread_info()->ieee_state = 0; | ||
221 | wrfpcr(FPCR_DYN_NORMAL | ieee_swcr_to_fpcr(0)); | ||
222 | |||
223 | /* Clean slate for TLS. */ | ||
224 | current_thread_info()->pcb.unique = 0; | ||
225 | } | ||
226 | |||
227 | void | ||
228 | release_thread(struct task_struct *dead_task) | ||
229 | { | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * "alpha_clone()".. By the time we get here, the | ||
234 | * non-volatile registers have also been saved on the | ||
235 | * stack. We do some ugly pointer stuff here.. (see | ||
236 | * also copy_thread) | ||
237 | * | ||
238 | * Notice that "fork()" is implemented in terms of clone, | ||
239 | * with parameters (SIGCHLD, 0). | ||
240 | */ | ||
241 | int | ||
242 | alpha_clone(unsigned long clone_flags, unsigned long usp, | ||
243 | int __user *parent_tid, int __user *child_tid, | ||
244 | unsigned long tls_value, struct pt_regs *regs) | ||
245 | { | ||
246 | if (!usp) | ||
247 | usp = rdusp(); | ||
248 | |||
249 | return do_fork(clone_flags, usp, regs, 0, parent_tid, child_tid); | ||
250 | } | ||
251 | |||
252 | int | ||
253 | alpha_vfork(struct pt_regs *regs) | ||
254 | { | ||
255 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), | ||
256 | regs, 0, NULL, NULL); | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * Copy an alpha thread.. | ||
261 | * | ||
262 | * Note the "stack_offset" stuff: when returning to kernel mode, we need | ||
263 | * to have some extra stack-space for the kernel stack that still exists | ||
264 | * after the "ret_from_fork". When returning to user mode, we only want | ||
265 | * the space needed by the syscall stack frame (ie "struct pt_regs"). | ||
266 | * Use the passed "regs" pointer to determine how much space we need | ||
267 | * for a kernel fork(). | ||
268 | */ | ||
269 | |||
270 | int | ||
271 | copy_thread(int nr, unsigned long clone_flags, unsigned long usp, | ||
272 | unsigned long unused, | ||
273 | struct task_struct * p, struct pt_regs * regs) | ||
274 | { | ||
275 | extern void ret_from_fork(void); | ||
276 | |||
277 | struct thread_info *childti = p->thread_info; | ||
278 | struct pt_regs * childregs; | ||
279 | struct switch_stack * childstack, *stack; | ||
280 | unsigned long stack_offset, settls; | ||
281 | |||
282 | stack_offset = PAGE_SIZE - sizeof(struct pt_regs); | ||
283 | if (!(regs->ps & 8)) | ||
284 | stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; | ||
285 | childregs = (struct pt_regs *) | ||
286 | (stack_offset + PAGE_SIZE + (long) childti); | ||
287 | |||
288 | *childregs = *regs; | ||
289 | settls = regs->r20; | ||
290 | childregs->r0 = 0; | ||
291 | childregs->r19 = 0; | ||
292 | childregs->r20 = 1; /* OSF/1 has some strange fork() semantics. */ | ||
293 | regs->r20 = 0; | ||
294 | stack = ((struct switch_stack *) regs) - 1; | ||
295 | childstack = ((struct switch_stack *) childregs) - 1; | ||
296 | *childstack = *stack; | ||
297 | childstack->r26 = (unsigned long) ret_from_fork; | ||
298 | childti->pcb.usp = usp; | ||
299 | childti->pcb.ksp = (unsigned long) childstack; | ||
300 | childti->pcb.flags = 1; /* set FEN, clear everything else */ | ||
301 | |||
302 | /* Set a new TLS for the child thread? Peek back into the | ||
303 | syscall arguments that we saved on syscall entry. Oops, | ||
304 | except we'd have clobbered it with the parent/child set | ||
305 | of r20. Read the saved copy. */ | ||
306 | /* Note: if CLONE_SETTLS is not set, then we must inherit the | ||
307 | value from the parent, which will have been set by the block | ||
308 | copy in dup_task_struct. This is non-intuitive, but is | ||
309 | required for proper operation in the case of a threaded | ||
310 | application calling fork. */ | ||
311 | if (clone_flags & CLONE_SETTLS) | ||
312 | childti->pcb.unique = settls; | ||
313 | |||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * Fill in the user structure for an ECOFF core dump. | ||
319 | */ | ||
320 | void | ||
321 | dump_thread(struct pt_regs * pt, struct user * dump) | ||
322 | { | ||
323 | /* switch stack follows right below pt_regs: */ | ||
324 | struct switch_stack * sw = ((struct switch_stack *) pt) - 1; | ||
325 | |||
326 | dump->magic = CMAGIC; | ||
327 | dump->start_code = current->mm->start_code; | ||
328 | dump->start_data = current->mm->start_data; | ||
329 | dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); | ||
330 | dump->u_tsize = ((current->mm->end_code - dump->start_code) | ||
331 | >> PAGE_SHIFT); | ||
332 | dump->u_dsize = ((current->mm->brk + PAGE_SIZE-1 - dump->start_data) | ||
333 | >> PAGE_SHIFT); | ||
334 | dump->u_ssize = (current->mm->start_stack - dump->start_stack | ||
335 | + PAGE_SIZE-1) >> PAGE_SHIFT; | ||
336 | |||
337 | /* | ||
338 | * We store the registers in an order/format that is | ||
339 | * compatible with DEC Unix/OSF/1 as this makes life easier | ||
340 | * for gdb. | ||
341 | */ | ||
342 | dump->regs[EF_V0] = pt->r0; | ||
343 | dump->regs[EF_T0] = pt->r1; | ||
344 | dump->regs[EF_T1] = pt->r2; | ||
345 | dump->regs[EF_T2] = pt->r3; | ||
346 | dump->regs[EF_T3] = pt->r4; | ||
347 | dump->regs[EF_T4] = pt->r5; | ||
348 | dump->regs[EF_T5] = pt->r6; | ||
349 | dump->regs[EF_T6] = pt->r7; | ||
350 | dump->regs[EF_T7] = pt->r8; | ||
351 | dump->regs[EF_S0] = sw->r9; | ||
352 | dump->regs[EF_S1] = sw->r10; | ||
353 | dump->regs[EF_S2] = sw->r11; | ||
354 | dump->regs[EF_S3] = sw->r12; | ||
355 | dump->regs[EF_S4] = sw->r13; | ||
356 | dump->regs[EF_S5] = sw->r14; | ||
357 | dump->regs[EF_S6] = sw->r15; | ||
358 | dump->regs[EF_A3] = pt->r19; | ||
359 | dump->regs[EF_A4] = pt->r20; | ||
360 | dump->regs[EF_A5] = pt->r21; | ||
361 | dump->regs[EF_T8] = pt->r22; | ||
362 | dump->regs[EF_T9] = pt->r23; | ||
363 | dump->regs[EF_T10] = pt->r24; | ||
364 | dump->regs[EF_T11] = pt->r25; | ||
365 | dump->regs[EF_RA] = pt->r26; | ||
366 | dump->regs[EF_T12] = pt->r27; | ||
367 | dump->regs[EF_AT] = pt->r28; | ||
368 | dump->regs[EF_SP] = rdusp(); | ||
369 | dump->regs[EF_PS] = pt->ps; | ||
370 | dump->regs[EF_PC] = pt->pc; | ||
371 | dump->regs[EF_GP] = pt->gp; | ||
372 | dump->regs[EF_A0] = pt->r16; | ||
373 | dump->regs[EF_A1] = pt->r17; | ||
374 | dump->regs[EF_A2] = pt->r18; | ||
375 | memcpy((char *)dump->regs + EF_SIZE, sw->fp, 32 * 8); | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * Fill in the user structure for a ELF core dump. | ||
380 | */ | ||
381 | void | ||
382 | dump_elf_thread(elf_greg_t *dest, struct pt_regs *pt, struct thread_info *ti) | ||
383 | { | ||
384 | /* switch stack follows right below pt_regs: */ | ||
385 | struct switch_stack * sw = ((struct switch_stack *) pt) - 1; | ||
386 | |||
387 | dest[ 0] = pt->r0; | ||
388 | dest[ 1] = pt->r1; | ||
389 | dest[ 2] = pt->r2; | ||
390 | dest[ 3] = pt->r3; | ||
391 | dest[ 4] = pt->r4; | ||
392 | dest[ 5] = pt->r5; | ||
393 | dest[ 6] = pt->r6; | ||
394 | dest[ 7] = pt->r7; | ||
395 | dest[ 8] = pt->r8; | ||
396 | dest[ 9] = sw->r9; | ||
397 | dest[10] = sw->r10; | ||
398 | dest[11] = sw->r11; | ||
399 | dest[12] = sw->r12; | ||
400 | dest[13] = sw->r13; | ||
401 | dest[14] = sw->r14; | ||
402 | dest[15] = sw->r15; | ||
403 | dest[16] = pt->r16; | ||
404 | dest[17] = pt->r17; | ||
405 | dest[18] = pt->r18; | ||
406 | dest[19] = pt->r19; | ||
407 | dest[20] = pt->r20; | ||
408 | dest[21] = pt->r21; | ||
409 | dest[22] = pt->r22; | ||
410 | dest[23] = pt->r23; | ||
411 | dest[24] = pt->r24; | ||
412 | dest[25] = pt->r25; | ||
413 | dest[26] = pt->r26; | ||
414 | dest[27] = pt->r27; | ||
415 | dest[28] = pt->r28; | ||
416 | dest[29] = pt->gp; | ||
417 | dest[30] = rdusp(); | ||
418 | dest[31] = pt->pc; | ||
419 | |||
420 | /* Once upon a time this was the PS value. Which is stupid | ||
421 | since that is always 8 for usermode. Usurped for the more | ||
422 | useful value of the thread's UNIQUE field. */ | ||
423 | dest[32] = ti->pcb.unique; | ||
424 | } | ||
425 | |||
426 | int | ||
427 | dump_elf_task(elf_greg_t *dest, struct task_struct *task) | ||
428 | { | ||
429 | struct thread_info *ti; | ||
430 | struct pt_regs *pt; | ||
431 | |||
432 | ti = task->thread_info; | ||
433 | pt = (struct pt_regs *)((unsigned long)ti + 2*PAGE_SIZE) - 1; | ||
434 | |||
435 | dump_elf_thread(dest, pt, ti); | ||
436 | |||
437 | return 1; | ||
438 | } | ||
439 | |||
440 | int | ||
441 | dump_elf_task_fp(elf_fpreg_t *dest, struct task_struct *task) | ||
442 | { | ||
443 | struct thread_info *ti; | ||
444 | struct pt_regs *pt; | ||
445 | struct switch_stack *sw; | ||
446 | |||
447 | ti = task->thread_info; | ||
448 | pt = (struct pt_regs *)((unsigned long)ti + 2*PAGE_SIZE) - 1; | ||
449 | sw = (struct switch_stack *)pt - 1; | ||
450 | |||
451 | memcpy(dest, sw->fp, 32 * 8); | ||
452 | |||
453 | return 1; | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * sys_execve() executes a new program. | ||
458 | */ | ||
459 | asmlinkage int | ||
460 | do_sys_execve(char __user *ufilename, char __user * __user *argv, | ||
461 | char __user * __user *envp, struct pt_regs *regs) | ||
462 | { | ||
463 | int error; | ||
464 | char *filename; | ||
465 | |||
466 | filename = getname(ufilename); | ||
467 | error = PTR_ERR(filename); | ||
468 | if (IS_ERR(filename)) | ||
469 | goto out; | ||
470 | error = do_execve(filename, argv, envp, regs); | ||
471 | putname(filename); | ||
472 | out: | ||
473 | return error; | ||
474 | } | ||
475 | |||
476 | /* | ||
477 | * Return saved PC of a blocked thread. This assumes the frame | ||
478 | * pointer is the 6th saved long on the kernel stack and that the | ||
479 | * saved return address is the first long in the frame. This all | ||
480 | * holds provided the thread blocked through a call to schedule() ($15 | ||
481 | * is the frame pointer in schedule() and $15 is saved at offset 48 by | ||
482 | * entry.S:do_switch_stack). | ||
483 | * | ||
484 | * Under heavy swap load I've seen this lose in an ugly way. So do | ||
485 | * some extra sanity checking on the ranges we expect these pointers | ||
486 | * to be in so that we can fail gracefully. This is just for ps after | ||
487 | * all. -- r~ | ||
488 | */ | ||
489 | |||
490 | unsigned long | ||
491 | thread_saved_pc(task_t *t) | ||
492 | { | ||
493 | unsigned long base = (unsigned long)t->thread_info; | ||
494 | unsigned long fp, sp = t->thread_info->pcb.ksp; | ||
495 | |||
496 | if (sp > base && sp+6*8 < base + 16*1024) { | ||
497 | fp = ((unsigned long*)sp)[6]; | ||
498 | if (fp > sp && fp < base + 16*1024) | ||
499 | return *(unsigned long *)fp; | ||
500 | } | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | unsigned long | ||
506 | get_wchan(struct task_struct *p) | ||
507 | { | ||
508 | unsigned long schedule_frame; | ||
509 | unsigned long pc; | ||
510 | if (!p || p == current || p->state == TASK_RUNNING) | ||
511 | return 0; | ||
512 | /* | ||
513 | * This one depends on the frame size of schedule(). Do a | ||
514 | * "disass schedule" in gdb to find the frame size. Also, the | ||
515 | * code assumes that sleep_on() follows immediately after | ||
516 | * interruptible_sleep_on() and that add_timer() follows | ||
517 | * immediately after interruptible_sleep(). Ugly, isn't it? | ||
518 | * Maybe adding a wchan field to task_struct would be better, | ||
519 | * after all... | ||
520 | */ | ||
521 | |||
522 | pc = thread_saved_pc(p); | ||
523 | if (in_sched_functions(pc)) { | ||
524 | schedule_frame = ((unsigned long *)p->thread_info->pcb.ksp)[6]; | ||
525 | return ((unsigned long *)schedule_frame)[12]; | ||
526 | } | ||
527 | return pc; | ||
528 | } | ||
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h new file mode 100644 index 000000000000..e1560fb15610 --- /dev/null +++ b/arch/alpha/kernel/proto.h | |||
@@ -0,0 +1,210 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/interrupt.h> | ||
3 | |||
4 | |||
5 | /* Prototypes of functions used across modules here in this directory. */ | ||
6 | |||
7 | #define vucp volatile unsigned char * | ||
8 | #define vusp volatile unsigned short * | ||
9 | #define vip volatile int * | ||
10 | #define vuip volatile unsigned int * | ||
11 | #define vulp volatile unsigned long * | ||
12 | |||
13 | struct pt_regs; | ||
14 | struct task_struct; | ||
15 | struct pci_dev; | ||
16 | struct pci_controller; | ||
17 | |||
18 | /* core_apecs.c */ | ||
19 | extern struct pci_ops apecs_pci_ops; | ||
20 | extern void apecs_init_arch(void); | ||
21 | extern void apecs_pci_clr_err(void); | ||
22 | extern void apecs_machine_check(u64, u64, struct pt_regs *); | ||
23 | extern void apecs_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
24 | |||
25 | /* core_cia.c */ | ||
26 | extern struct pci_ops cia_pci_ops; | ||
27 | extern void cia_init_pci(void); | ||
28 | extern void cia_init_arch(void); | ||
29 | extern void pyxis_init_arch(void); | ||
30 | extern void cia_kill_arch(int); | ||
31 | extern void cia_machine_check(u64, u64, struct pt_regs *); | ||
32 | extern void cia_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
33 | |||
34 | /* core_irongate.c */ | ||
35 | extern struct pci_ops irongate_pci_ops; | ||
36 | extern int irongate_pci_clr_err(void); | ||
37 | extern void irongate_init_arch(void); | ||
38 | extern void irongate_machine_check(u64, u64, struct pt_regs *); | ||
39 | #define irongate_pci_tbi ((void *)0) | ||
40 | |||
41 | /* core_lca.c */ | ||
42 | extern struct pci_ops lca_pci_ops; | ||
43 | extern void lca_init_arch(void); | ||
44 | extern void lca_machine_check(u64, u64, struct pt_regs *); | ||
45 | extern void lca_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
46 | |||
47 | /* core_marvel.c */ | ||
48 | extern struct pci_ops marvel_pci_ops; | ||
49 | extern void marvel_init_arch(void); | ||
50 | extern void marvel_kill_arch(int); | ||
51 | extern void marvel_machine_check(u64, u64, struct pt_regs *); | ||
52 | extern void marvel_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
53 | extern int marvel_pa_to_nid(unsigned long); | ||
54 | extern int marvel_cpuid_to_nid(int); | ||
55 | extern unsigned long marvel_node_mem_start(int); | ||
56 | extern unsigned long marvel_node_mem_size(int); | ||
57 | extern struct _alpha_agp_info *marvel_agp_info(void); | ||
58 | struct io7 *marvel_find_io7(int pe); | ||
59 | struct io7 *marvel_next_io7(struct io7 *prev); | ||
60 | void io7_clear_errors(struct io7 *io7); | ||
61 | |||
62 | /* core_mcpcia.c */ | ||
63 | extern struct pci_ops mcpcia_pci_ops; | ||
64 | extern void mcpcia_init_arch(void); | ||
65 | extern void mcpcia_init_hoses(void); | ||
66 | extern void mcpcia_machine_check(u64, u64, struct pt_regs *); | ||
67 | extern void mcpcia_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
68 | |||
69 | /* core_polaris.c */ | ||
70 | extern struct pci_ops polaris_pci_ops; | ||
71 | extern int polaris_read_config_dword(struct pci_dev *, int, u32 *); | ||
72 | extern int polaris_write_config_dword(struct pci_dev *, int, u32); | ||
73 | extern void polaris_init_arch(void); | ||
74 | extern void polaris_machine_check(u64, u64, struct pt_regs *); | ||
75 | #define polaris_pci_tbi ((void *)0) | ||
76 | |||
77 | /* core_t2.c */ | ||
78 | extern struct pci_ops t2_pci_ops; | ||
79 | extern void t2_init_arch(void); | ||
80 | extern void t2_kill_arch(int); | ||
81 | extern void t2_machine_check(u64, u64, struct pt_regs *); | ||
82 | extern void t2_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
83 | |||
84 | /* core_titan.c */ | ||
85 | extern struct pci_ops titan_pci_ops; | ||
86 | extern void titan_init_arch(void); | ||
87 | extern void titan_kill_arch(int); | ||
88 | extern void titan_machine_check(u64, u64, struct pt_regs *); | ||
89 | extern void titan_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
90 | extern struct _alpha_agp_info *titan_agp_info(void); | ||
91 | |||
92 | /* core_tsunami.c */ | ||
93 | extern struct pci_ops tsunami_pci_ops; | ||
94 | extern void tsunami_init_arch(void); | ||
95 | extern void tsunami_kill_arch(int); | ||
96 | extern void tsunami_machine_check(u64, u64, struct pt_regs *); | ||
97 | extern void tsunami_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
98 | |||
99 | /* core_wildfire.c */ | ||
100 | extern struct pci_ops wildfire_pci_ops; | ||
101 | extern void wildfire_init_arch(void); | ||
102 | extern void wildfire_kill_arch(int); | ||
103 | extern void wildfire_machine_check(u64, u64, struct pt_regs *); | ||
104 | extern void wildfire_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t); | ||
105 | extern int wildfire_pa_to_nid(unsigned long); | ||
106 | extern int wildfire_cpuid_to_nid(int); | ||
107 | extern unsigned long wildfire_node_mem_start(int); | ||
108 | extern unsigned long wildfire_node_mem_size(int); | ||
109 | |||
110 | /* setup.c */ | ||
111 | extern unsigned long srm_hae; | ||
112 | extern int boot_cpuid; | ||
113 | #ifdef CONFIG_VERBOSE_MCHECK | ||
114 | extern unsigned long alpha_verbose_mcheck; | ||
115 | #endif | ||
116 | |||
117 | /* srmcons.c */ | ||
118 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM) | ||
119 | extern void register_srm_console(void); | ||
120 | extern void unregister_srm_console(void); | ||
121 | #else | ||
122 | #define register_srm_console() | ||
123 | #define unregister_srm_console() | ||
124 | #endif | ||
125 | |||
126 | /* smp.c */ | ||
127 | extern void setup_smp(void); | ||
128 | extern void handle_ipi(struct pt_regs *); | ||
129 | extern void smp_percpu_timer_interrupt(struct pt_regs *); | ||
130 | |||
131 | /* bios32.c */ | ||
132 | /* extern void reset_for_srm(void); */ | ||
133 | |||
134 | /* time.c */ | ||
135 | extern irqreturn_t timer_interrupt(int irq, void *dev, struct pt_regs * regs); | ||
136 | extern void common_init_rtc(void); | ||
137 | extern unsigned long est_cycle_freq; | ||
138 | |||
139 | /* smc37c93x.c */ | ||
140 | extern void SMC93x_Init(void); | ||
141 | |||
142 | /* smc37c669.c */ | ||
143 | extern void SMC669_Init(int); | ||
144 | |||
145 | /* es1888.c */ | ||
146 | extern void es1888_init(void); | ||
147 | |||
148 | /* ns87312.c */ | ||
149 | extern void ns87312_enable_ide(long ide_base); | ||
150 | |||
151 | /* ../lib/fpreg.c */ | ||
152 | extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); | ||
153 | extern unsigned long alpha_read_fp_reg (unsigned long reg); | ||
154 | |||
155 | /* head.S */ | ||
156 | extern void wrmces(unsigned long mces); | ||
157 | extern void cserve_ena(unsigned long); | ||
158 | extern void cserve_dis(unsigned long); | ||
159 | extern void __smp_callin(unsigned long); | ||
160 | |||
161 | /* entry.S */ | ||
162 | extern void entArith(void); | ||
163 | extern void entIF(void); | ||
164 | extern void entInt(void); | ||
165 | extern void entMM(void); | ||
166 | extern void entSys(void); | ||
167 | extern void entUna(void); | ||
168 | extern void entDbg(void); | ||
169 | |||
170 | /* ptrace.c */ | ||
171 | extern int ptrace_set_bpt (struct task_struct *child); | ||
172 | extern int ptrace_cancel_bpt (struct task_struct *child); | ||
173 | |||
174 | /* traps.c */ | ||
175 | extern void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15); | ||
176 | extern void die_if_kernel(char *, struct pt_regs *, long, unsigned long *); | ||
177 | |||
178 | /* sys_titan.c */ | ||
179 | extern void titan_dispatch_irqs(u64, struct pt_regs *); | ||
180 | |||
181 | /* ../mm/init.c */ | ||
182 | extern void switch_to_system_map(void); | ||
183 | extern void srm_paging_stop(void); | ||
184 | |||
185 | /* ../mm/remap.c */ | ||
186 | extern int __alpha_remap_area_pages(unsigned long, unsigned long, | ||
187 | unsigned long, unsigned long); | ||
188 | |||
189 | /* irq.c */ | ||
190 | |||
191 | #ifdef CONFIG_SMP | ||
192 | #define mcheck_expected(cpu) (cpu_data[cpu].mcheck_expected) | ||
193 | #define mcheck_taken(cpu) (cpu_data[cpu].mcheck_taken) | ||
194 | #define mcheck_extra(cpu) (cpu_data[cpu].mcheck_extra) | ||
195 | #else | ||
196 | extern struct mcheck_info | ||
197 | { | ||
198 | unsigned char expected __attribute__((aligned(8))); | ||
199 | unsigned char taken; | ||
200 | unsigned char extra; | ||
201 | } __mcheck_info; | ||
202 | |||
203 | #define mcheck_expected(cpu) (*((void)(cpu), &__mcheck_info.expected)) | ||
204 | #define mcheck_taken(cpu) (*((void)(cpu), &__mcheck_info.taken)) | ||
205 | #define mcheck_extra(cpu) (*((void)(cpu), &__mcheck_info.extra)) | ||
206 | #endif | ||
207 | |||
208 | extern void process_mcheck_info(unsigned long vector, unsigned long la_ptr, | ||
209 | struct pt_regs *regs, const char *machine, | ||
210 | int expected); | ||
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c new file mode 100644 index 000000000000..d00583161574 --- /dev/null +++ b/arch/alpha/kernel/ptrace.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /* ptrace.c */ | ||
2 | /* By Ross Biro 1/23/92 */ | ||
3 | /* edited by Linus Torvalds */ | ||
4 | /* mangled further by Bob Manson (manson@santafe.edu) */ | ||
5 | /* more mutilation by David Mosberger (davidm@azstarnet.com) */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/sched.h> | ||
9 | #include <linux/mm.h> | ||
10 | #include <linux/smp.h> | ||
11 | #include <linux/smp_lock.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/ptrace.h> | ||
14 | #include <linux/user.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/security.h> | ||
17 | |||
18 | #include <asm/uaccess.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/fpu.h> | ||
22 | |||
23 | #include "proto.h" | ||
24 | |||
25 | #define DEBUG DBG_MEM | ||
26 | #undef DEBUG | ||
27 | |||
28 | #ifdef DEBUG | ||
29 | enum { | ||
30 | DBG_MEM = (1<<0), | ||
31 | DBG_BPT = (1<<1), | ||
32 | DBG_MEM_ALL = (1<<2) | ||
33 | }; | ||
34 | #define DBG(fac,args) {if ((fac) & DEBUG) printk args;} | ||
35 | #else | ||
36 | #define DBG(fac,args) | ||
37 | #endif | ||
38 | |||
39 | #define BREAKINST 0x00000080 /* call_pal bpt */ | ||
40 | |||
41 | /* | ||
42 | * does not yet catch signals sent when the child dies. | ||
43 | * in exit.c or in signal.c. | ||
44 | */ | ||
45 | |||
46 | /* | ||
47 | * Processes always block with the following stack-layout: | ||
48 | * | ||
49 | * +================================+ <---- task + 2*PAGE_SIZE | ||
50 | * | PALcode saved frame (ps, pc, | ^ | ||
51 | * | gp, a0, a1, a2) | | | ||
52 | * +================================+ | struct pt_regs | ||
53 | * | | | | ||
54 | * | frame generated by SAVE_ALL | | | ||
55 | * | | v | ||
56 | * +================================+ | ||
57 | * | | ^ | ||
58 | * | frame saved by do_switch_stack | | struct switch_stack | ||
59 | * | | v | ||
60 | * +================================+ | ||
61 | */ | ||
62 | |||
63 | /* | ||
64 | * The following table maps a register index into the stack offset at | ||
65 | * which the register is saved. Register indices are 0-31 for integer | ||
66 | * regs, 32-63 for fp regs, and 64 for the pc. Notice that sp and | ||
67 | * zero have no stack-slot and need to be treated specially (see | ||
68 | * get_reg/put_reg below). | ||
69 | */ | ||
70 | enum { | ||
71 | REG_R0 = 0, REG_F0 = 32, REG_FPCR = 63, REG_PC = 64 | ||
72 | }; | ||
73 | |||
74 | static int regoff[] = { | ||
75 | PT_REG( r0), PT_REG( r1), PT_REG( r2), PT_REG( r3), | ||
76 | PT_REG( r4), PT_REG( r5), PT_REG( r6), PT_REG( r7), | ||
77 | PT_REG( r8), SW_REG( r9), SW_REG( r10), SW_REG( r11), | ||
78 | SW_REG( r12), SW_REG( r13), SW_REG( r14), SW_REG( r15), | ||
79 | PT_REG( r16), PT_REG( r17), PT_REG( r18), PT_REG( r19), | ||
80 | PT_REG( r20), PT_REG( r21), PT_REG( r22), PT_REG( r23), | ||
81 | PT_REG( r24), PT_REG( r25), PT_REG( r26), PT_REG( r27), | ||
82 | PT_REG( r28), PT_REG( gp), -1, -1, | ||
83 | SW_REG(fp[ 0]), SW_REG(fp[ 1]), SW_REG(fp[ 2]), SW_REG(fp[ 3]), | ||
84 | SW_REG(fp[ 4]), SW_REG(fp[ 5]), SW_REG(fp[ 6]), SW_REG(fp[ 7]), | ||
85 | SW_REG(fp[ 8]), SW_REG(fp[ 9]), SW_REG(fp[10]), SW_REG(fp[11]), | ||
86 | SW_REG(fp[12]), SW_REG(fp[13]), SW_REG(fp[14]), SW_REG(fp[15]), | ||
87 | SW_REG(fp[16]), SW_REG(fp[17]), SW_REG(fp[18]), SW_REG(fp[19]), | ||
88 | SW_REG(fp[20]), SW_REG(fp[21]), SW_REG(fp[22]), SW_REG(fp[23]), | ||
89 | SW_REG(fp[24]), SW_REG(fp[25]), SW_REG(fp[26]), SW_REG(fp[27]), | ||
90 | SW_REG(fp[28]), SW_REG(fp[29]), SW_REG(fp[30]), SW_REG(fp[31]), | ||
91 | PT_REG( pc) | ||
92 | }; | ||
93 | |||
94 | static unsigned long zero; | ||
95 | |||
96 | /* | ||
97 | * Get address of register REGNO in task TASK. | ||
98 | */ | ||
99 | static unsigned long * | ||
100 | get_reg_addr(struct task_struct * task, unsigned long regno) | ||
101 | { | ||
102 | unsigned long *addr; | ||
103 | |||
104 | if (regno == 30) { | ||
105 | addr = &task->thread_info->pcb.usp; | ||
106 | } else if (regno == 65) { | ||
107 | addr = &task->thread_info->pcb.unique; | ||
108 | } else if (regno == 31 || regno > 65) { | ||
109 | zero = 0; | ||
110 | addr = &zero; | ||
111 | } else { | ||
112 | addr = (void *)task->thread_info + regoff[regno]; | ||
113 | } | ||
114 | return addr; | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * Get contents of register REGNO in task TASK. | ||
119 | */ | ||
120 | static unsigned long | ||
121 | get_reg(struct task_struct * task, unsigned long regno) | ||
122 | { | ||
123 | /* Special hack for fpcr -- combine hardware and software bits. */ | ||
124 | if (regno == 63) { | ||
125 | unsigned long fpcr = *get_reg_addr(task, regno); | ||
126 | unsigned long swcr | ||
127 | = task->thread_info->ieee_state & IEEE_SW_MASK; | ||
128 | swcr = swcr_update_status(swcr, fpcr); | ||
129 | return fpcr | swcr; | ||
130 | } | ||
131 | return *get_reg_addr(task, regno); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Write contents of register REGNO in task TASK. | ||
136 | */ | ||
137 | static int | ||
138 | put_reg(struct task_struct *task, unsigned long regno, unsigned long data) | ||
139 | { | ||
140 | if (regno == 63) { | ||
141 | task->thread_info->ieee_state | ||
142 | = ((task->thread_info->ieee_state & ~IEEE_SW_MASK) | ||
143 | | (data & IEEE_SW_MASK)); | ||
144 | data = (data & FPCR_DYN_MASK) | ieee_swcr_to_fpcr(data); | ||
145 | } | ||
146 | *get_reg_addr(task, regno) = data; | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static inline int | ||
151 | read_int(struct task_struct *task, unsigned long addr, int * data) | ||
152 | { | ||
153 | int copied = access_process_vm(task, addr, data, sizeof(int), 0); | ||
154 | return (copied == sizeof(int)) ? 0 : -EIO; | ||
155 | } | ||
156 | |||
157 | static inline int | ||
158 | write_int(struct task_struct *task, unsigned long addr, int data) | ||
159 | { | ||
160 | int copied = access_process_vm(task, addr, &data, sizeof(int), 1); | ||
161 | return (copied == sizeof(int)) ? 0 : -EIO; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Set breakpoint. | ||
166 | */ | ||
167 | int | ||
168 | ptrace_set_bpt(struct task_struct * child) | ||
169 | { | ||
170 | int displ, i, res, reg_b, nsaved = 0; | ||
171 | unsigned int insn, op_code; | ||
172 | unsigned long pc; | ||
173 | |||
174 | pc = get_reg(child, REG_PC); | ||
175 | res = read_int(child, pc, (int *) &insn); | ||
176 | if (res < 0) | ||
177 | return res; | ||
178 | |||
179 | op_code = insn >> 26; | ||
180 | if (op_code >= 0x30) { | ||
181 | /* | ||
182 | * It's a branch: instead of trying to figure out | ||
183 | * whether the branch will be taken or not, we'll put | ||
184 | * a breakpoint at either location. This is simpler, | ||
185 | * more reliable, and probably not a whole lot slower | ||
186 | * than the alternative approach of emulating the | ||
187 | * branch (emulation can be tricky for fp branches). | ||
188 | */ | ||
189 | displ = ((s32)(insn << 11)) >> 9; | ||
190 | child->thread_info->bpt_addr[nsaved++] = pc + 4; | ||
191 | if (displ) /* guard against unoptimized code */ | ||
192 | child->thread_info->bpt_addr[nsaved++] | ||
193 | = pc + 4 + displ; | ||
194 | DBG(DBG_BPT, ("execing branch\n")); | ||
195 | } else if (op_code == 0x1a) { | ||
196 | reg_b = (insn >> 16) & 0x1f; | ||
197 | child->thread_info->bpt_addr[nsaved++] = get_reg(child, reg_b); | ||
198 | DBG(DBG_BPT, ("execing jump\n")); | ||
199 | } else { | ||
200 | child->thread_info->bpt_addr[nsaved++] = pc + 4; | ||
201 | DBG(DBG_BPT, ("execing normal insn\n")); | ||
202 | } | ||
203 | |||
204 | /* install breakpoints: */ | ||
205 | for (i = 0; i < nsaved; ++i) { | ||
206 | res = read_int(child, child->thread_info->bpt_addr[i], | ||
207 | (int *) &insn); | ||
208 | if (res < 0) | ||
209 | return res; | ||
210 | child->thread_info->bpt_insn[i] = insn; | ||
211 | DBG(DBG_BPT, (" -> next_pc=%lx\n", | ||
212 | child->thread_info->bpt_addr[i])); | ||
213 | res = write_int(child, child->thread_info->bpt_addr[i], | ||
214 | BREAKINST); | ||
215 | if (res < 0) | ||
216 | return res; | ||
217 | } | ||
218 | child->thread_info->bpt_nsaved = nsaved; | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * Ensure no single-step breakpoint is pending. Returns non-zero | ||
224 | * value if child was being single-stepped. | ||
225 | */ | ||
226 | int | ||
227 | ptrace_cancel_bpt(struct task_struct * child) | ||
228 | { | ||
229 | int i, nsaved = child->thread_info->bpt_nsaved; | ||
230 | |||
231 | child->thread_info->bpt_nsaved = 0; | ||
232 | |||
233 | if (nsaved > 2) { | ||
234 | printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); | ||
235 | nsaved = 2; | ||
236 | } | ||
237 | |||
238 | for (i = 0; i < nsaved; ++i) { | ||
239 | write_int(child, child->thread_info->bpt_addr[i], | ||
240 | child->thread_info->bpt_insn[i]); | ||
241 | } | ||
242 | return (nsaved != 0); | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | * Called by kernel/ptrace.c when detaching.. | ||
247 | * | ||
248 | * Make sure the single step bit is not set. | ||
249 | */ | ||
250 | void ptrace_disable(struct task_struct *child) | ||
251 | { | ||
252 | ptrace_cancel_bpt(child); | ||
253 | } | ||
254 | |||
255 | asmlinkage long | ||
256 | do_sys_ptrace(long request, long pid, long addr, long data, | ||
257 | struct pt_regs *regs) | ||
258 | { | ||
259 | struct task_struct *child; | ||
260 | unsigned long tmp; | ||
261 | size_t copied; | ||
262 | long ret; | ||
263 | |||
264 | lock_kernel(); | ||
265 | DBG(DBG_MEM, ("request=%ld pid=%ld addr=0x%lx data=0x%lx\n", | ||
266 | request, pid, addr, data)); | ||
267 | ret = -EPERM; | ||
268 | if (request == PTRACE_TRACEME) { | ||
269 | /* are we already being traced? */ | ||
270 | if (current->ptrace & PT_PTRACED) | ||
271 | goto out_notsk; | ||
272 | ret = security_ptrace(current->parent, current); | ||
273 | if (ret) | ||
274 | goto out_notsk; | ||
275 | /* set the ptrace bit in the process ptrace flags. */ | ||
276 | current->ptrace |= PT_PTRACED; | ||
277 | ret = 0; | ||
278 | goto out_notsk; | ||
279 | } | ||
280 | if (pid == 1) /* you may not mess with init */ | ||
281 | goto out_notsk; | ||
282 | |||
283 | ret = -ESRCH; | ||
284 | read_lock(&tasklist_lock); | ||
285 | child = find_task_by_pid(pid); | ||
286 | if (child) | ||
287 | get_task_struct(child); | ||
288 | read_unlock(&tasklist_lock); | ||
289 | if (!child) | ||
290 | goto out_notsk; | ||
291 | |||
292 | if (request == PTRACE_ATTACH) { | ||
293 | ret = ptrace_attach(child); | ||
294 | goto out; | ||
295 | } | ||
296 | |||
297 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
298 | if (ret < 0) | ||
299 | goto out; | ||
300 | |||
301 | switch (request) { | ||
302 | /* When I and D space are separate, these will need to be fixed. */ | ||
303 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
304 | case PTRACE_PEEKDATA: | ||
305 | copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); | ||
306 | ret = -EIO; | ||
307 | if (copied != sizeof(tmp)) | ||
308 | break; | ||
309 | |||
310 | regs->r0 = 0; /* special return: no errors */ | ||
311 | ret = tmp; | ||
312 | break; | ||
313 | |||
314 | /* Read register number ADDR. */ | ||
315 | case PTRACE_PEEKUSR: | ||
316 | regs->r0 = 0; /* special return: no errors */ | ||
317 | ret = get_reg(child, addr); | ||
318 | DBG(DBG_MEM, ("peek $%ld->%#lx\n", addr, ret)); | ||
319 | break; | ||
320 | |||
321 | /* When I and D space are separate, this will have to be fixed. */ | ||
322 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
323 | case PTRACE_POKEDATA: | ||
324 | tmp = data; | ||
325 | copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 1); | ||
326 | ret = (copied == sizeof(tmp)) ? 0 : -EIO; | ||
327 | break; | ||
328 | |||
329 | case PTRACE_POKEUSR: /* write the specified register */ | ||
330 | DBG(DBG_MEM, ("poke $%ld<-%#lx\n", addr, data)); | ||
331 | ret = put_reg(child, addr, data); | ||
332 | break; | ||
333 | |||
334 | case PTRACE_SYSCALL: | ||
335 | /* continue and stop at next (return from) syscall */ | ||
336 | case PTRACE_CONT: /* restart after signal. */ | ||
337 | ret = -EIO; | ||
338 | if ((unsigned long) data > _NSIG) | ||
339 | break; | ||
340 | if (request == PTRACE_SYSCALL) | ||
341 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
342 | else | ||
343 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
344 | child->exit_code = data; | ||
345 | /* make sure single-step breakpoint is gone. */ | ||
346 | ptrace_cancel_bpt(child); | ||
347 | wake_up_process(child); | ||
348 | ret = 0; | ||
349 | break; | ||
350 | |||
351 | /* | ||
352 | * Make the child exit. Best I can do is send it a sigkill. | ||
353 | * perhaps it should be put in the status that it wants to | ||
354 | * exit. | ||
355 | */ | ||
356 | case PTRACE_KILL: | ||
357 | ret = 0; | ||
358 | if (child->exit_state == EXIT_ZOMBIE) | ||
359 | break; | ||
360 | child->exit_code = SIGKILL; | ||
361 | /* make sure single-step breakpoint is gone. */ | ||
362 | ptrace_cancel_bpt(child); | ||
363 | wake_up_process(child); | ||
364 | goto out; | ||
365 | |||
366 | case PTRACE_SINGLESTEP: /* execute single instruction. */ | ||
367 | ret = -EIO; | ||
368 | if ((unsigned long) data > _NSIG) | ||
369 | break; | ||
370 | /* Mark single stepping. */ | ||
371 | child->thread_info->bpt_nsaved = -1; | ||
372 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
373 | child->exit_code = data; | ||
374 | wake_up_process(child); | ||
375 | /* give it a chance to run. */ | ||
376 | ret = 0; | ||
377 | goto out; | ||
378 | |||
379 | case PTRACE_DETACH: /* detach a process that was attached. */ | ||
380 | ret = ptrace_detach(child, data); | ||
381 | goto out; | ||
382 | |||
383 | default: | ||
384 | ret = ptrace_request(child, request, addr, data); | ||
385 | goto out; | ||
386 | } | ||
387 | out: | ||
388 | put_task_struct(child); | ||
389 | out_notsk: | ||
390 | unlock_kernel(); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | asmlinkage void | ||
395 | syscall_trace(void) | ||
396 | { | ||
397 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
398 | return; | ||
399 | if (!(current->ptrace & PT_PTRACED)) | ||
400 | return; | ||
401 | /* The 0x80 provides a way for the tracing parent to distinguish | ||
402 | between a syscall stop and SIGTRAP delivery */ | ||
403 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
404 | ? 0x80 : 0)); | ||
405 | |||
406 | /* | ||
407 | * This isn't the same as continuing with a signal, but it will do | ||
408 | * for normal use. strace only continues with a signal if the | ||
409 | * stopping signal is not SIGTRAP. -brl | ||
410 | */ | ||
411 | if (current->exit_code) { | ||
412 | send_sig(current->exit_code, current, 1); | ||
413 | current->exit_code = 0; | ||
414 | } | ||
415 | } | ||
diff --git a/arch/alpha/kernel/semaphore.c b/arch/alpha/kernel/semaphore.c new file mode 100644 index 000000000000..8c8aaa205eae --- /dev/null +++ b/arch/alpha/kernel/semaphore.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * Alpha semaphore implementation. | ||
3 | * | ||
4 | * (C) Copyright 1996 Linus Torvalds | ||
5 | * (C) Copyright 1999, 2000 Richard Henderson | ||
6 | */ | ||
7 | |||
8 | #include <linux/errno.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/init.h> | ||
11 | |||
12 | /* | ||
13 | * This is basically the PPC semaphore scheme ported to use | ||
14 | * the Alpha ll/sc sequences, so see the PPC code for | ||
15 | * credits. | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * Atomically update sem->count. | ||
20 | * This does the equivalent of the following: | ||
21 | * | ||
22 | * old_count = sem->count; | ||
23 | * tmp = MAX(old_count, 0) + incr; | ||
24 | * sem->count = tmp; | ||
25 | * return old_count; | ||
26 | */ | ||
27 | static inline int __sem_update_count(struct semaphore *sem, int incr) | ||
28 | { | ||
29 | long old_count, tmp = 0; | ||
30 | |||
31 | __asm__ __volatile__( | ||
32 | "1: ldl_l %0,%2\n" | ||
33 | " cmovgt %0,%0,%1\n" | ||
34 | " addl %1,%3,%1\n" | ||
35 | " stl_c %1,%2\n" | ||
36 | " beq %1,2f\n" | ||
37 | " mb\n" | ||
38 | ".subsection 2\n" | ||
39 | "2: br 1b\n" | ||
40 | ".previous" | ||
41 | : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) | ||
42 | : "Ir" (incr), "1" (tmp), "m" (sem->count)); | ||
43 | |||
44 | return old_count; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Perform the "down" function. Return zero for semaphore acquired, | ||
49 | * return negative for signalled out of the function. | ||
50 | * | ||
51 | * If called from down, the return is ignored and the wait loop is | ||
52 | * not interruptible. This means that a task waiting on a semaphore | ||
53 | * using "down()" cannot be killed until someone does an "up()" on | ||
54 | * the semaphore. | ||
55 | * | ||
56 | * If called from down_interruptible, the return value gets checked | ||
57 | * upon return. If the return value is negative then the task continues | ||
58 | * with the negative value in the return register (it can be tested by | ||
59 | * the caller). | ||
60 | * | ||
61 | * Either form may be used in conjunction with "up()". | ||
62 | */ | ||
63 | |||
64 | void __sched | ||
65 | __down_failed(struct semaphore *sem) | ||
66 | { | ||
67 | struct task_struct *tsk = current; | ||
68 | DECLARE_WAITQUEUE(wait, tsk); | ||
69 | |||
70 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
71 | printk("%s(%d): down failed(%p)\n", | ||
72 | tsk->comm, tsk->pid, sem); | ||
73 | #endif | ||
74 | |||
75 | tsk->state = TASK_UNINTERRUPTIBLE; | ||
76 | wmb(); | ||
77 | add_wait_queue_exclusive(&sem->wait, &wait); | ||
78 | |||
79 | /* | ||
80 | * Try to get the semaphore. If the count is > 0, then we've | ||
81 | * got the semaphore; we decrement count and exit the loop. | ||
82 | * If the count is 0 or negative, we set it to -1, indicating | ||
83 | * that we are asleep, and then sleep. | ||
84 | */ | ||
85 | while (__sem_update_count(sem, -1) <= 0) { | ||
86 | schedule(); | ||
87 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); | ||
88 | } | ||
89 | remove_wait_queue(&sem->wait, &wait); | ||
90 | tsk->state = TASK_RUNNING; | ||
91 | |||
92 | /* | ||
93 | * If there are any more sleepers, wake one of them up so | ||
94 | * that it can either get the semaphore, or set count to -1 | ||
95 | * indicating that there are still processes sleeping. | ||
96 | */ | ||
97 | wake_up(&sem->wait); | ||
98 | |||
99 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
100 | printk("%s(%d): down acquired(%p)\n", | ||
101 | tsk->comm, tsk->pid, sem); | ||
102 | #endif | ||
103 | } | ||
104 | |||
105 | int __sched | ||
106 | __down_failed_interruptible(struct semaphore *sem) | ||
107 | { | ||
108 | struct task_struct *tsk = current; | ||
109 | DECLARE_WAITQUEUE(wait, tsk); | ||
110 | long ret = 0; | ||
111 | |||
112 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
113 | printk("%s(%d): down failed(%p)\n", | ||
114 | tsk->comm, tsk->pid, sem); | ||
115 | #endif | ||
116 | |||
117 | tsk->state = TASK_INTERRUPTIBLE; | ||
118 | wmb(); | ||
119 | add_wait_queue_exclusive(&sem->wait, &wait); | ||
120 | |||
121 | while (__sem_update_count(sem, -1) <= 0) { | ||
122 | if (signal_pending(current)) { | ||
123 | /* | ||
124 | * A signal is pending - give up trying. | ||
125 | * Set sem->count to 0 if it is negative, | ||
126 | * since we are no longer sleeping. | ||
127 | */ | ||
128 | __sem_update_count(sem, 0); | ||
129 | ret = -EINTR; | ||
130 | break; | ||
131 | } | ||
132 | schedule(); | ||
133 | set_task_state(tsk, TASK_INTERRUPTIBLE); | ||
134 | } | ||
135 | |||
136 | remove_wait_queue(&sem->wait, &wait); | ||
137 | tsk->state = TASK_RUNNING; | ||
138 | wake_up(&sem->wait); | ||
139 | |||
140 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
141 | printk("%s(%d): down %s(%p)\n", | ||
142 | current->comm, current->pid, | ||
143 | (ret < 0 ? "interrupted" : "acquired"), sem); | ||
144 | #endif | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | void | ||
149 | __up_wakeup(struct semaphore *sem) | ||
150 | { | ||
151 | /* | ||
152 | * Note that we incremented count in up() before we came here, | ||
153 | * but that was ineffective since the result was <= 0, and | ||
154 | * any negative value of count is equivalent to 0. | ||
155 | * This ends up setting count to 1, unless count is now > 0 | ||
156 | * (i.e. because some other cpu has called up() in the meantime), | ||
157 | * in which case we just increment count. | ||
158 | */ | ||
159 | __sem_update_count(sem, 1); | ||
160 | wake_up(&sem->wait); | ||
161 | } | ||
162 | |||
163 | void __sched | ||
164 | down(struct semaphore *sem) | ||
165 | { | ||
166 | #ifdef WAITQUEUE_DEBUG | ||
167 | CHECK_MAGIC(sem->__magic); | ||
168 | #endif | ||
169 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
170 | printk("%s(%d): down(%p) <count=%d> from %p\n", | ||
171 | current->comm, current->pid, sem, | ||
172 | atomic_read(&sem->count), __builtin_return_address(0)); | ||
173 | #endif | ||
174 | __down(sem); | ||
175 | } | ||
176 | |||
177 | int __sched | ||
178 | down_interruptible(struct semaphore *sem) | ||
179 | { | ||
180 | #ifdef WAITQUEUE_DEBUG | ||
181 | CHECK_MAGIC(sem->__magic); | ||
182 | #endif | ||
183 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
184 | printk("%s(%d): down(%p) <count=%d> from %p\n", | ||
185 | current->comm, current->pid, sem, | ||
186 | atomic_read(&sem->count), __builtin_return_address(0)); | ||
187 | #endif | ||
188 | return __down_interruptible(sem); | ||
189 | } | ||
190 | |||
191 | int | ||
192 | down_trylock(struct semaphore *sem) | ||
193 | { | ||
194 | int ret; | ||
195 | |||
196 | #ifdef WAITQUEUE_DEBUG | ||
197 | CHECK_MAGIC(sem->__magic); | ||
198 | #endif | ||
199 | |||
200 | ret = __down_trylock(sem); | ||
201 | |||
202 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
203 | printk("%s(%d): down_trylock %s from %p\n", | ||
204 | current->comm, current->pid, | ||
205 | ret ? "failed" : "acquired", | ||
206 | __builtin_return_address(0)); | ||
207 | #endif | ||
208 | |||
209 | return ret; | ||
210 | } | ||
211 | |||
212 | void | ||
213 | up(struct semaphore *sem) | ||
214 | { | ||
215 | #ifdef WAITQUEUE_DEBUG | ||
216 | CHECK_MAGIC(sem->__magic); | ||
217 | #endif | ||
218 | #ifdef CONFIG_DEBUG_SEMAPHORE | ||
219 | printk("%s(%d): up(%p) <count=%d> from %p\n", | ||
220 | current->comm, current->pid, sem, | ||
221 | atomic_read(&sem->count), __builtin_return_address(0)); | ||
222 | #endif | ||
223 | __up(sem); | ||
224 | } | ||
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c new file mode 100644 index 000000000000..b4e5f8ff2b25 --- /dev/null +++ b/arch/alpha/kernel/setup.c | |||
@@ -0,0 +1,1486 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/setup.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* 2.3.x bootmem, 1999 Andrea Arcangeli <andrea@suse.de> */ | ||
8 | |||
9 | /* | ||
10 | * Bootup setup stuff. | ||
11 | */ | ||
12 | |||
13 | #include <linux/sched.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/stddef.h> | ||
17 | #include <linux/unistd.h> | ||
18 | #include <linux/ptrace.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/user.h> | ||
21 | #include <linux/a.out.h> | ||
22 | #include <linux/tty.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/config.h> /* CONFIG_ALPHA_LCA etc */ | ||
25 | #include <linux/mc146818rtc.h> | ||
26 | #include <linux/console.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/string.h> | ||
30 | #include <linux/ioport.h> | ||
31 | #include <linux/bootmem.h> | ||
32 | #include <linux/pci.h> | ||
33 | #include <linux/seq_file.h> | ||
34 | #include <linux/root_dev.h> | ||
35 | #include <linux/initrd.h> | ||
36 | #include <linux/eisa.h> | ||
37 | #ifdef CONFIG_MAGIC_SYSRQ | ||
38 | #include <linux/sysrq.h> | ||
39 | #include <linux/reboot.h> | ||
40 | #endif | ||
41 | #include <linux/notifier.h> | ||
42 | #include <asm/setup.h> | ||
43 | #include <asm/io.h> | ||
44 | |||
45 | extern struct notifier_block *panic_notifier_list; | ||
46 | static int alpha_panic_event(struct notifier_block *, unsigned long, void *); | ||
47 | static struct notifier_block alpha_panic_block = { | ||
48 | alpha_panic_event, | ||
49 | NULL, | ||
50 | INT_MAX /* try to do it first */ | ||
51 | }; | ||
52 | |||
53 | #include <asm/uaccess.h> | ||
54 | #include <asm/pgtable.h> | ||
55 | #include <asm/system.h> | ||
56 | #include <asm/hwrpb.h> | ||
57 | #include <asm/dma.h> | ||
58 | #include <asm/io.h> | ||
59 | #include <asm/mmu_context.h> | ||
60 | #include <asm/console.h> | ||
61 | |||
62 | #include "proto.h" | ||
63 | #include "pci_impl.h" | ||
64 | |||
65 | |||
66 | struct hwrpb_struct *hwrpb; | ||
67 | unsigned long srm_hae; | ||
68 | |||
69 | int alpha_l1i_cacheshape; | ||
70 | int alpha_l1d_cacheshape; | ||
71 | int alpha_l2_cacheshape; | ||
72 | int alpha_l3_cacheshape; | ||
73 | |||
74 | #ifdef CONFIG_VERBOSE_MCHECK | ||
75 | /* 0=minimum, 1=verbose, 2=all */ | ||
76 | /* These can be overridden via the command line, ie "verbose_mcheck=2") */ | ||
77 | unsigned long alpha_verbose_mcheck = CONFIG_VERBOSE_MCHECK_ON; | ||
78 | #endif | ||
79 | |||
80 | /* Which processor we booted from. */ | ||
81 | int boot_cpuid; | ||
82 | |||
83 | /* | ||
84 | * Using SRM callbacks for initial console output. This works from | ||
85 | * setup_arch() time through the end of time_init(), as those places | ||
86 | * are under our (Alpha) control. | ||
87 | |||
88 | * "srmcons" specified in the boot command arguments allows us to | ||
89 | * see kernel messages during the period of time before the true | ||
90 | * console device is "registered" during console_init(). | ||
91 | * As of this version (2.5.59), console_init() will call | ||
92 | * disable_early_printk() as the last action before initializing | ||
93 | * the console drivers. That's the last possible time srmcons can be | ||
94 | * unregistered without interfering with console behavior. | ||
95 | * | ||
96 | * By default, OFF; set it with a bootcommand arg of "srmcons" or | ||
97 | * "console=srm". The meaning of these two args is: | ||
98 | * "srmcons" - early callback prints | ||
99 | * "console=srm" - full callback based console, including early prints | ||
100 | */ | ||
101 | int srmcons_output = 0; | ||
102 | |||
103 | /* Enforce a memory size limit; useful for testing. By default, none. */ | ||
104 | unsigned long mem_size_limit = 0; | ||
105 | |||
106 | /* Set AGP GART window size (0 means disabled). */ | ||
107 | unsigned long alpha_agpgart_size = DEFAULT_AGP_APER_SIZE; | ||
108 | |||
109 | #ifdef CONFIG_ALPHA_GENERIC | ||
110 | struct alpha_machine_vector alpha_mv; | ||
111 | int alpha_using_srm; | ||
112 | #endif | ||
113 | |||
114 | #define N(a) (sizeof(a)/sizeof(a[0])) | ||
115 | |||
116 | static struct alpha_machine_vector *get_sysvec(unsigned long, unsigned long, | ||
117 | unsigned long); | ||
118 | static struct alpha_machine_vector *get_sysvec_byname(const char *); | ||
119 | static void get_sysnames(unsigned long, unsigned long, unsigned long, | ||
120 | char **, char **); | ||
121 | static void determine_cpu_caches (unsigned int); | ||
122 | |||
123 | static char command_line[COMMAND_LINE_SIZE]; | ||
124 | |||
125 | /* | ||
126 | * The format of "screen_info" is strange, and due to early | ||
127 | * i386-setup code. This is just enough to make the console | ||
128 | * code think we're on a VGA color display. | ||
129 | */ | ||
130 | |||
131 | struct screen_info screen_info = { | ||
132 | .orig_x = 0, | ||
133 | .orig_y = 25, | ||
134 | .orig_video_cols = 80, | ||
135 | .orig_video_lines = 25, | ||
136 | .orig_video_isVGA = 1, | ||
137 | .orig_video_points = 16 | ||
138 | }; | ||
139 | |||
140 | /* | ||
141 | * The direct map I/O window, if any. This should be the same | ||
142 | * for all busses, since it's used by virt_to_bus. | ||
143 | */ | ||
144 | |||
145 | unsigned long __direct_map_base; | ||
146 | unsigned long __direct_map_size; | ||
147 | |||
148 | /* | ||
149 | * Declare all of the machine vectors. | ||
150 | */ | ||
151 | |||
152 | /* GCC 2.7.2 (on alpha at least) is lame. It does not support either | ||
153 | __attribute__((weak)) or #pragma weak. Bypass it and talk directly | ||
154 | to the assembler. */ | ||
155 | |||
156 | #define WEAK(X) \ | ||
157 | extern struct alpha_machine_vector X; \ | ||
158 | asm(".weak "#X) | ||
159 | |||
160 | WEAK(alcor_mv); | ||
161 | WEAK(alphabook1_mv); | ||
162 | WEAK(avanti_mv); | ||
163 | WEAK(cabriolet_mv); | ||
164 | WEAK(clipper_mv); | ||
165 | WEAK(dp264_mv); | ||
166 | WEAK(eb164_mv); | ||
167 | WEAK(eb64p_mv); | ||
168 | WEAK(eb66_mv); | ||
169 | WEAK(eb66p_mv); | ||
170 | WEAK(eiger_mv); | ||
171 | WEAK(jensen_mv); | ||
172 | WEAK(lx164_mv); | ||
173 | WEAK(lynx_mv); | ||
174 | WEAK(marvel_ev7_mv); | ||
175 | WEAK(miata_mv); | ||
176 | WEAK(mikasa_mv); | ||
177 | WEAK(mikasa_primo_mv); | ||
178 | WEAK(monet_mv); | ||
179 | WEAK(nautilus_mv); | ||
180 | WEAK(noname_mv); | ||
181 | WEAK(noritake_mv); | ||
182 | WEAK(noritake_primo_mv); | ||
183 | WEAK(p2k_mv); | ||
184 | WEAK(pc164_mv); | ||
185 | WEAK(privateer_mv); | ||
186 | WEAK(rawhide_mv); | ||
187 | WEAK(ruffian_mv); | ||
188 | WEAK(rx164_mv); | ||
189 | WEAK(sable_mv); | ||
190 | WEAK(sable_gamma_mv); | ||
191 | WEAK(shark_mv); | ||
192 | WEAK(sx164_mv); | ||
193 | WEAK(takara_mv); | ||
194 | WEAK(titan_mv); | ||
195 | WEAK(webbrick_mv); | ||
196 | WEAK(wildfire_mv); | ||
197 | WEAK(xl_mv); | ||
198 | WEAK(xlt_mv); | ||
199 | |||
200 | #undef WEAK | ||
201 | |||
202 | /* | ||
203 | * I/O resources inherited from PeeCees. Except for perhaps the | ||
204 | * turbochannel alphas, everyone has these on some sort of SuperIO chip. | ||
205 | * | ||
206 | * ??? If this becomes less standard, move the struct out into the | ||
207 | * machine vector. | ||
208 | */ | ||
209 | |||
210 | static void __init | ||
211 | reserve_std_resources(void) | ||
212 | { | ||
213 | static struct resource standard_io_resources[] = { | ||
214 | { .name = "rtc", .start = -1, .end = -1 }, | ||
215 | { .name = "dma1", .start = 0x00, .end = 0x1f }, | ||
216 | { .name = "pic1", .start = 0x20, .end = 0x3f }, | ||
217 | { .name = "timer", .start = 0x40, .end = 0x5f }, | ||
218 | { .name = "keyboard", .start = 0x60, .end = 0x6f }, | ||
219 | { .name = "dma page reg", .start = 0x80, .end = 0x8f }, | ||
220 | { .name = "pic2", .start = 0xa0, .end = 0xbf }, | ||
221 | { .name = "dma2", .start = 0xc0, .end = 0xdf }, | ||
222 | }; | ||
223 | |||
224 | struct resource *io = &ioport_resource; | ||
225 | size_t i; | ||
226 | |||
227 | if (hose_head) { | ||
228 | struct pci_controller *hose; | ||
229 | for (hose = hose_head; hose; hose = hose->next) | ||
230 | if (hose->index == 0) { | ||
231 | io = hose->io_space; | ||
232 | break; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | /* Fix up for the Jensen's queer RTC placement. */ | ||
237 | standard_io_resources[0].start = RTC_PORT(0); | ||
238 | standard_io_resources[0].end = RTC_PORT(0) + 0x10; | ||
239 | |||
240 | for (i = 0; i < N(standard_io_resources); ++i) | ||
241 | request_resource(io, standard_io_resources+i); | ||
242 | } | ||
243 | |||
244 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
245 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
246 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
247 | #define PFN_MAX PFN_DOWN(0x80000000) | ||
248 | #define for_each_mem_cluster(memdesc, cluster, i) \ | ||
249 | for ((cluster) = (memdesc)->cluster, (i) = 0; \ | ||
250 | (i) < (memdesc)->numclusters; (i)++, (cluster)++) | ||
251 | |||
252 | static unsigned long __init | ||
253 | get_mem_size_limit(char *s) | ||
254 | { | ||
255 | unsigned long end = 0; | ||
256 | char *from = s; | ||
257 | |||
258 | end = simple_strtoul(from, &from, 0); | ||
259 | if ( *from == 'K' || *from == 'k' ) { | ||
260 | end = end << 10; | ||
261 | from++; | ||
262 | } else if ( *from == 'M' || *from == 'm' ) { | ||
263 | end = end << 20; | ||
264 | from++; | ||
265 | } else if ( *from == 'G' || *from == 'g' ) { | ||
266 | end = end << 30; | ||
267 | from++; | ||
268 | } | ||
269 | return end >> PAGE_SHIFT; /* Return the PFN of the limit. */ | ||
270 | } | ||
271 | |||
272 | #ifdef CONFIG_BLK_DEV_INITRD | ||
273 | void * __init | ||
274 | move_initrd(unsigned long mem_limit) | ||
275 | { | ||
276 | void *start; | ||
277 | unsigned long size; | ||
278 | |||
279 | size = initrd_end - initrd_start; | ||
280 | start = __alloc_bootmem(PAGE_ALIGN(size), PAGE_SIZE, 0); | ||
281 | if (!start || __pa(start) + size > mem_limit) { | ||
282 | initrd_start = initrd_end = 0; | ||
283 | return NULL; | ||
284 | } | ||
285 | memmove(start, (void *)initrd_start, size); | ||
286 | initrd_start = (unsigned long)start; | ||
287 | initrd_end = initrd_start + size; | ||
288 | printk("initrd moved to %p\n", start); | ||
289 | return start; | ||
290 | } | ||
291 | #endif | ||
292 | |||
293 | #ifndef CONFIG_DISCONTIGMEM | ||
294 | static void __init | ||
295 | setup_memory(void *kernel_end) | ||
296 | { | ||
297 | struct memclust_struct * cluster; | ||
298 | struct memdesc_struct * memdesc; | ||
299 | unsigned long start_kernel_pfn, end_kernel_pfn; | ||
300 | unsigned long bootmap_size, bootmap_pages, bootmap_start; | ||
301 | unsigned long start, end; | ||
302 | unsigned long i; | ||
303 | |||
304 | /* Find free clusters, and init and free the bootmem accordingly. */ | ||
305 | memdesc = (struct memdesc_struct *) | ||
306 | (hwrpb->mddt_offset + (unsigned long) hwrpb); | ||
307 | |||
308 | for_each_mem_cluster(memdesc, cluster, i) { | ||
309 | printk("memcluster %lu, usage %01lx, start %8lu, end %8lu\n", | ||
310 | i, cluster->usage, cluster->start_pfn, | ||
311 | cluster->start_pfn + cluster->numpages); | ||
312 | |||
313 | /* Bit 0 is console/PALcode reserved. Bit 1 is | ||
314 | non-volatile memory -- we might want to mark | ||
315 | this for later. */ | ||
316 | if (cluster->usage & 3) | ||
317 | continue; | ||
318 | |||
319 | end = cluster->start_pfn + cluster->numpages; | ||
320 | if (end > max_low_pfn) | ||
321 | max_low_pfn = end; | ||
322 | } | ||
323 | |||
324 | /* | ||
325 | * Except for the NUMA systems (wildfire, marvel) all of the | ||
326 | * Alpha systems we run on support 32GB of memory or less. | ||
327 | * Since the NUMA systems introduce large holes in memory addressing, | ||
328 | * we can get into a situation where there is not enough contiguous | ||
329 | * memory for the memory map. | ||
330 | * | ||
331 | * Limit memory to the first 32GB to limit the NUMA systems to | ||
332 | * memory on their first node (wildfire) or 2 (marvel) to avoid | ||
333 | * not being able to produce the memory map. In order to access | ||
334 | * all of the memory on the NUMA systems, build with discontiguous | ||
335 | * memory support. | ||
336 | * | ||
337 | * If the user specified a memory limit, let that memory limit stand. | ||
338 | */ | ||
339 | if (!mem_size_limit) | ||
340 | mem_size_limit = (32ul * 1024 * 1024 * 1024) >> PAGE_SHIFT; | ||
341 | |||
342 | if (mem_size_limit && max_low_pfn >= mem_size_limit) | ||
343 | { | ||
344 | printk("setup: forcing memory size to %ldK (from %ldK).\n", | ||
345 | mem_size_limit << (PAGE_SHIFT - 10), | ||
346 | max_low_pfn << (PAGE_SHIFT - 10)); | ||
347 | max_low_pfn = mem_size_limit; | ||
348 | } | ||
349 | |||
350 | /* Find the bounds of kernel memory. */ | ||
351 | start_kernel_pfn = PFN_DOWN(KERNEL_START_PHYS); | ||
352 | end_kernel_pfn = PFN_UP(virt_to_phys(kernel_end)); | ||
353 | bootmap_start = -1; | ||
354 | |||
355 | try_again: | ||
356 | if (max_low_pfn <= end_kernel_pfn) | ||
357 | panic("not enough memory to boot"); | ||
358 | |||
359 | /* We need to know how many physically contiguous pages | ||
360 | we'll need for the bootmap. */ | ||
361 | bootmap_pages = bootmem_bootmap_pages(max_low_pfn); | ||
362 | |||
363 | /* Now find a good region where to allocate the bootmap. */ | ||
364 | for_each_mem_cluster(memdesc, cluster, i) { | ||
365 | if (cluster->usage & 3) | ||
366 | continue; | ||
367 | |||
368 | start = cluster->start_pfn; | ||
369 | end = start + cluster->numpages; | ||
370 | if (start >= max_low_pfn) | ||
371 | continue; | ||
372 | if (end > max_low_pfn) | ||
373 | end = max_low_pfn; | ||
374 | if (start < start_kernel_pfn) { | ||
375 | if (end > end_kernel_pfn | ||
376 | && end - end_kernel_pfn >= bootmap_pages) { | ||
377 | bootmap_start = end_kernel_pfn; | ||
378 | break; | ||
379 | } else if (end > start_kernel_pfn) | ||
380 | end = start_kernel_pfn; | ||
381 | } else if (start < end_kernel_pfn) | ||
382 | start = end_kernel_pfn; | ||
383 | if (end - start >= bootmap_pages) { | ||
384 | bootmap_start = start; | ||
385 | break; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | if (bootmap_start == ~0UL) { | ||
390 | max_low_pfn >>= 1; | ||
391 | goto try_again; | ||
392 | } | ||
393 | |||
394 | /* Allocate the bootmap and mark the whole MM as reserved. */ | ||
395 | bootmap_size = init_bootmem(bootmap_start, max_low_pfn); | ||
396 | |||
397 | /* Mark the free regions. */ | ||
398 | for_each_mem_cluster(memdesc, cluster, i) { | ||
399 | if (cluster->usage & 3) | ||
400 | continue; | ||
401 | |||
402 | start = cluster->start_pfn; | ||
403 | end = cluster->start_pfn + cluster->numpages; | ||
404 | if (start >= max_low_pfn) | ||
405 | continue; | ||
406 | if (end > max_low_pfn) | ||
407 | end = max_low_pfn; | ||
408 | if (start < start_kernel_pfn) { | ||
409 | if (end > end_kernel_pfn) { | ||
410 | free_bootmem(PFN_PHYS(start), | ||
411 | (PFN_PHYS(start_kernel_pfn) | ||
412 | - PFN_PHYS(start))); | ||
413 | printk("freeing pages %ld:%ld\n", | ||
414 | start, start_kernel_pfn); | ||
415 | start = end_kernel_pfn; | ||
416 | } else if (end > start_kernel_pfn) | ||
417 | end = start_kernel_pfn; | ||
418 | } else if (start < end_kernel_pfn) | ||
419 | start = end_kernel_pfn; | ||
420 | if (start >= end) | ||
421 | continue; | ||
422 | |||
423 | free_bootmem(PFN_PHYS(start), PFN_PHYS(end) - PFN_PHYS(start)); | ||
424 | printk("freeing pages %ld:%ld\n", start, end); | ||
425 | } | ||
426 | |||
427 | /* Reserve the bootmap memory. */ | ||
428 | reserve_bootmem(PFN_PHYS(bootmap_start), bootmap_size); | ||
429 | printk("reserving pages %ld:%ld\n", bootmap_start, bootmap_start+PFN_UP(bootmap_size)); | ||
430 | |||
431 | #ifdef CONFIG_BLK_DEV_INITRD | ||
432 | initrd_start = INITRD_START; | ||
433 | if (initrd_start) { | ||
434 | initrd_end = initrd_start+INITRD_SIZE; | ||
435 | printk("Initial ramdisk at: 0x%p (%lu bytes)\n", | ||
436 | (void *) initrd_start, INITRD_SIZE); | ||
437 | |||
438 | if ((void *)initrd_end > phys_to_virt(PFN_PHYS(max_low_pfn))) { | ||
439 | if (!move_initrd(PFN_PHYS(max_low_pfn))) | ||
440 | printk("initrd extends beyond end of memory " | ||
441 | "(0x%08lx > 0x%p)\ndisabling initrd\n", | ||
442 | initrd_end, | ||
443 | phys_to_virt(PFN_PHYS(max_low_pfn))); | ||
444 | } else { | ||
445 | reserve_bootmem(virt_to_phys((void *)initrd_start), | ||
446 | INITRD_SIZE); | ||
447 | } | ||
448 | } | ||
449 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
450 | } | ||
451 | #else | ||
452 | extern void setup_memory(void *); | ||
453 | #endif /* !CONFIG_DISCONTIGMEM */ | ||
454 | |||
455 | int __init | ||
456 | page_is_ram(unsigned long pfn) | ||
457 | { | ||
458 | struct memclust_struct * cluster; | ||
459 | struct memdesc_struct * memdesc; | ||
460 | unsigned long i; | ||
461 | |||
462 | memdesc = (struct memdesc_struct *) | ||
463 | (hwrpb->mddt_offset + (unsigned long) hwrpb); | ||
464 | for_each_mem_cluster(memdesc, cluster, i) | ||
465 | { | ||
466 | if (pfn >= cluster->start_pfn && | ||
467 | pfn < cluster->start_pfn + cluster->numpages) { | ||
468 | return (cluster->usage & 3) ? 0 : 1; | ||
469 | } | ||
470 | } | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | #undef PFN_UP | ||
476 | #undef PFN_DOWN | ||
477 | #undef PFN_PHYS | ||
478 | #undef PFN_MAX | ||
479 | |||
480 | void __init | ||
481 | setup_arch(char **cmdline_p) | ||
482 | { | ||
483 | extern char _end[]; | ||
484 | |||
485 | struct alpha_machine_vector *vec = NULL; | ||
486 | struct percpu_struct *cpu; | ||
487 | char *type_name, *var_name, *p; | ||
488 | void *kernel_end = _end; /* end of kernel */ | ||
489 | char *args = command_line; | ||
490 | |||
491 | hwrpb = (struct hwrpb_struct*) __va(INIT_HWRPB->phys_addr); | ||
492 | boot_cpuid = hard_smp_processor_id(); | ||
493 | |||
494 | /* | ||
495 | * Pre-process the system type to make sure it will be valid. | ||
496 | * | ||
497 | * This may restore real CABRIO and EB66+ family names, ie | ||
498 | * EB64+ and EB66. | ||
499 | * | ||
500 | * Oh, and "white box" AS800 (aka DIGITAL Server 3000 series) | ||
501 | * and AS1200 (DIGITAL Server 5000 series) have the type as | ||
502 | * the negative of the real one. | ||
503 | */ | ||
504 | if ((long)hwrpb->sys_type < 0) { | ||
505 | hwrpb->sys_type = -((long)hwrpb->sys_type); | ||
506 | hwrpb_update_checksum(hwrpb); | ||
507 | } | ||
508 | |||
509 | /* Register a call for panic conditions. */ | ||
510 | notifier_chain_register(&panic_notifier_list, &alpha_panic_block); | ||
511 | |||
512 | #ifdef CONFIG_ALPHA_GENERIC | ||
513 | /* Assume that we've booted from SRM if we haven't booted from MILO. | ||
514 | Detect the later by looking for "MILO" in the system serial nr. */ | ||
515 | alpha_using_srm = strncmp((const char *)hwrpb->ssn, "MILO", 4) != 0; | ||
516 | #endif | ||
517 | |||
518 | /* If we are using SRM, we want to allow callbacks | ||
519 | as early as possible, so do this NOW, and then | ||
520 | they should work immediately thereafter. | ||
521 | */ | ||
522 | kernel_end = callback_init(kernel_end); | ||
523 | |||
524 | /* | ||
525 | * Locate the command line. | ||
526 | */ | ||
527 | /* Hack for Jensen... since we're restricted to 8 or 16 chars for | ||
528 | boot flags depending on the boot mode, we need some shorthand. | ||
529 | This should do for installation. */ | ||
530 | if (strcmp(COMMAND_LINE, "INSTALL") == 0) { | ||
531 | strlcpy(command_line, "root=/dev/fd0 load_ramdisk=1", sizeof command_line); | ||
532 | } else { | ||
533 | strlcpy(command_line, COMMAND_LINE, sizeof command_line); | ||
534 | } | ||
535 | strcpy(saved_command_line, command_line); | ||
536 | *cmdline_p = command_line; | ||
537 | |||
538 | /* | ||
539 | * Process command-line arguments. | ||
540 | */ | ||
541 | while ((p = strsep(&args, " \t")) != NULL) { | ||
542 | if (!*p) continue; | ||
543 | if (strncmp(p, "alpha_mv=", 9) == 0) { | ||
544 | vec = get_sysvec_byname(p+9); | ||
545 | continue; | ||
546 | } | ||
547 | if (strncmp(p, "cycle=", 6) == 0) { | ||
548 | est_cycle_freq = simple_strtol(p+6, NULL, 0); | ||
549 | continue; | ||
550 | } | ||
551 | if (strncmp(p, "mem=", 4) == 0) { | ||
552 | mem_size_limit = get_mem_size_limit(p+4); | ||
553 | continue; | ||
554 | } | ||
555 | if (strncmp(p, "srmcons", 7) == 0) { | ||
556 | srmcons_output |= 1; | ||
557 | continue; | ||
558 | } | ||
559 | if (strncmp(p, "console=srm", 11) == 0) { | ||
560 | srmcons_output |= 2; | ||
561 | continue; | ||
562 | } | ||
563 | if (strncmp(p, "gartsize=", 9) == 0) { | ||
564 | alpha_agpgart_size = | ||
565 | get_mem_size_limit(p+9) << PAGE_SHIFT; | ||
566 | continue; | ||
567 | } | ||
568 | #ifdef CONFIG_VERBOSE_MCHECK | ||
569 | if (strncmp(p, "verbose_mcheck=", 15) == 0) { | ||
570 | alpha_verbose_mcheck = simple_strtol(p+15, NULL, 0); | ||
571 | continue; | ||
572 | } | ||
573 | #endif | ||
574 | } | ||
575 | |||
576 | /* Replace the command line, now that we've killed it with strsep. */ | ||
577 | strcpy(command_line, saved_command_line); | ||
578 | |||
579 | /* If we want SRM console printk echoing early, do it now. */ | ||
580 | if (alpha_using_srm && srmcons_output) { | ||
581 | register_srm_console(); | ||
582 | |||
583 | /* | ||
584 | * If "console=srm" was specified, clear the srmcons_output | ||
585 | * flag now so that time.c won't unregister_srm_console | ||
586 | */ | ||
587 | if (srmcons_output & 2) | ||
588 | srmcons_output = 0; | ||
589 | } | ||
590 | |||
591 | #ifdef CONFIG_MAGIC_SYSRQ | ||
592 | /* If we're using SRM, make sysrq-b halt back to the prom, | ||
593 | not auto-reboot. */ | ||
594 | if (alpha_using_srm) { | ||
595 | struct sysrq_key_op *op = __sysrq_get_key_op('b'); | ||
596 | op->handler = (void *) machine_halt; | ||
597 | } | ||
598 | #endif | ||
599 | |||
600 | /* | ||
601 | * Identify and reconfigure for the current system. | ||
602 | */ | ||
603 | cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); | ||
604 | |||
605 | get_sysnames(hwrpb->sys_type, hwrpb->sys_variation, | ||
606 | cpu->type, &type_name, &var_name); | ||
607 | if (*var_name == '0') | ||
608 | var_name = ""; | ||
609 | |||
610 | if (!vec) { | ||
611 | vec = get_sysvec(hwrpb->sys_type, hwrpb->sys_variation, | ||
612 | cpu->type); | ||
613 | } | ||
614 | |||
615 | if (!vec) { | ||
616 | panic("Unsupported system type: %s%s%s (%ld %ld)\n", | ||
617 | type_name, (*var_name ? " variation " : ""), var_name, | ||
618 | hwrpb->sys_type, hwrpb->sys_variation); | ||
619 | } | ||
620 | if (vec != &alpha_mv) { | ||
621 | alpha_mv = *vec; | ||
622 | } | ||
623 | |||
624 | printk("Booting " | ||
625 | #ifdef CONFIG_ALPHA_GENERIC | ||
626 | "GENERIC " | ||
627 | #endif | ||
628 | "on %s%s%s using machine vector %s from %s\n", | ||
629 | type_name, (*var_name ? " variation " : ""), | ||
630 | var_name, alpha_mv.vector_name, | ||
631 | (alpha_using_srm ? "SRM" : "MILO")); | ||
632 | |||
633 | printk("Major Options: " | ||
634 | #ifdef CONFIG_SMP | ||
635 | "SMP " | ||
636 | #endif | ||
637 | #ifdef CONFIG_ALPHA_EV56 | ||
638 | "EV56 " | ||
639 | #endif | ||
640 | #ifdef CONFIG_ALPHA_EV67 | ||
641 | "EV67 " | ||
642 | #endif | ||
643 | #ifdef CONFIG_ALPHA_LEGACY_START_ADDRESS | ||
644 | "LEGACY_START " | ||
645 | #endif | ||
646 | #ifdef CONFIG_VERBOSE_MCHECK | ||
647 | "VERBOSE_MCHECK " | ||
648 | #endif | ||
649 | |||
650 | #ifdef CONFIG_DISCONTIGMEM | ||
651 | "DISCONTIGMEM " | ||
652 | #ifdef CONFIG_NUMA | ||
653 | "NUMA " | ||
654 | #endif | ||
655 | #endif | ||
656 | |||
657 | #ifdef CONFIG_DEBUG_SPINLOCK | ||
658 | "DEBUG_SPINLOCK " | ||
659 | #endif | ||
660 | #ifdef CONFIG_MAGIC_SYSRQ | ||
661 | "MAGIC_SYSRQ " | ||
662 | #endif | ||
663 | "\n"); | ||
664 | |||
665 | printk("Command line: %s\n", command_line); | ||
666 | |||
667 | /* | ||
668 | * Sync up the HAE. | ||
669 | * Save the SRM's current value for restoration. | ||
670 | */ | ||
671 | srm_hae = *alpha_mv.hae_register; | ||
672 | __set_hae(alpha_mv.hae_cache); | ||
673 | |||
674 | /* Reset enable correctable error reports. */ | ||
675 | wrmces(0x7); | ||
676 | |||
677 | /* Find our memory. */ | ||
678 | setup_memory(kernel_end); | ||
679 | |||
680 | /* First guess at cpu cache sizes. Do this before init_arch. */ | ||
681 | determine_cpu_caches(cpu->type); | ||
682 | |||
683 | /* Initialize the machine. Usually has to do with setting up | ||
684 | DMA windows and the like. */ | ||
685 | if (alpha_mv.init_arch) | ||
686 | alpha_mv.init_arch(); | ||
687 | |||
688 | /* Reserve standard resources. */ | ||
689 | reserve_std_resources(); | ||
690 | |||
691 | /* | ||
692 | * Give us a default console. TGA users will see nothing until | ||
693 | * chr_dev_init is called, rather late in the boot sequence. | ||
694 | */ | ||
695 | |||
696 | #ifdef CONFIG_VT | ||
697 | #if defined(CONFIG_VGA_CONSOLE) | ||
698 | conswitchp = &vga_con; | ||
699 | #elif defined(CONFIG_DUMMY_CONSOLE) | ||
700 | conswitchp = &dummy_con; | ||
701 | #endif | ||
702 | #endif | ||
703 | |||
704 | /* Default root filesystem to sda2. */ | ||
705 | ROOT_DEV = Root_SDA2; | ||
706 | |||
707 | #ifdef CONFIG_EISA | ||
708 | /* FIXME: only set this when we actually have EISA in this box? */ | ||
709 | EISA_bus = 1; | ||
710 | #endif | ||
711 | |||
712 | /* | ||
713 | * Check ASN in HWRPB for validity, report if bad. | ||
714 | * FIXME: how was this failing? Should we trust it instead, | ||
715 | * and copy the value into alpha_mv.max_asn? | ||
716 | */ | ||
717 | |||
718 | if (hwrpb->max_asn != MAX_ASN) { | ||
719 | printk("Max ASN from HWRPB is bad (0x%lx)\n", hwrpb->max_asn); | ||
720 | } | ||
721 | |||
722 | /* | ||
723 | * Identify the flock of penguins. | ||
724 | */ | ||
725 | |||
726 | #ifdef CONFIG_SMP | ||
727 | setup_smp(); | ||
728 | #endif | ||
729 | paging_init(); | ||
730 | } | ||
731 | |||
732 | void __init | ||
733 | disable_early_printk(void) | ||
734 | { | ||
735 | if (alpha_using_srm && srmcons_output) { | ||
736 | unregister_srm_console(); | ||
737 | srmcons_output = 0; | ||
738 | } | ||
739 | } | ||
740 | |||
741 | static char sys_unknown[] = "Unknown"; | ||
742 | static char systype_names[][16] = { | ||
743 | "0", | ||
744 | "ADU", "Cobra", "Ruby", "Flamingo", "Mannequin", "Jensen", | ||
745 | "Pelican", "Morgan", "Sable", "Medulla", "Noname", | ||
746 | "Turbolaser", "Avanti", "Mustang", "Alcor", "Tradewind", | ||
747 | "Mikasa", "EB64", "EB66", "EB64+", "AlphaBook1", | ||
748 | "Rawhide", "K2", "Lynx", "XL", "EB164", "Noritake", | ||
749 | "Cortex", "29", "Miata", "XXM", "Takara", "Yukon", | ||
750 | "Tsunami", "Wildfire", "CUSCO", "Eiger", "Titan", "Marvel" | ||
751 | }; | ||
752 | |||
753 | static char unofficial_names[][8] = {"100", "Ruffian"}; | ||
754 | |||
755 | static char api_names[][16] = {"200", "Nautilus"}; | ||
756 | |||
757 | static char eb164_names[][8] = {"EB164", "PC164", "LX164", "SX164", "RX164"}; | ||
758 | static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3,4}; | ||
759 | |||
760 | static char alcor_names[][16] = {"Alcor", "Maverick", "Bret"}; | ||
761 | static int alcor_indices[] = {0,0,0,1,1,1,0,0,0,0,0,0,2,2,2,2,2,2}; | ||
762 | |||
763 | static char eb64p_names[][16] = {"EB64+", "Cabriolet", "AlphaPCI64"}; | ||
764 | static int eb64p_indices[] = {0,0,1,2}; | ||
765 | |||
766 | static char eb66_names[][8] = {"EB66", "EB66+"}; | ||
767 | static int eb66_indices[] = {0,0,1}; | ||
768 | |||
769 | static char marvel_names[][16] = { | ||
770 | "Marvel/EV7" | ||
771 | }; | ||
772 | static int marvel_indices[] = { 0 }; | ||
773 | |||
774 | static char rawhide_names[][16] = { | ||
775 | "Dodge", "Wrangler", "Durango", "Tincup", "DaVinci" | ||
776 | }; | ||
777 | static int rawhide_indices[] = {0,0,0,1,1,2,2,3,3,4,4}; | ||
778 | |||
779 | static char titan_names[][16] = { | ||
780 | "DEFAULT", "Privateer", "Falcon", "Granite" | ||
781 | }; | ||
782 | static int titan_indices[] = {0,1,2,2,3}; | ||
783 | |||
784 | static char tsunami_names[][16] = { | ||
785 | "0", "DP264", "Warhol", "Windjammer", "Monet", "Clipper", | ||
786 | "Goldrush", "Webbrick", "Catamaran", "Brisbane", "Melbourne", | ||
787 | "Flying Clipper", "Shark" | ||
788 | }; | ||
789 | static int tsunami_indices[] = {0,1,2,3,4,5,6,7,8,9,10,11,12}; | ||
790 | |||
791 | static struct alpha_machine_vector * __init | ||
792 | get_sysvec(unsigned long type, unsigned long variation, unsigned long cpu) | ||
793 | { | ||
794 | static struct alpha_machine_vector *systype_vecs[] __initdata = | ||
795 | { | ||
796 | NULL, /* 0 */ | ||
797 | NULL, /* ADU */ | ||
798 | NULL, /* Cobra */ | ||
799 | NULL, /* Ruby */ | ||
800 | NULL, /* Flamingo */ | ||
801 | NULL, /* Mannequin */ | ||
802 | &jensen_mv, | ||
803 | NULL, /* Pelican */ | ||
804 | NULL, /* Morgan */ | ||
805 | NULL, /* Sable -- see below. */ | ||
806 | NULL, /* Medulla */ | ||
807 | &noname_mv, | ||
808 | NULL, /* Turbolaser */ | ||
809 | &avanti_mv, | ||
810 | NULL, /* Mustang */ | ||
811 | NULL, /* Alcor, Bret, Maverick. HWRPB inaccurate? */ | ||
812 | NULL, /* Tradewind */ | ||
813 | NULL, /* Mikasa -- see below. */ | ||
814 | NULL, /* EB64 */ | ||
815 | NULL, /* EB66 -- see variation. */ | ||
816 | NULL, /* EB64+ -- see variation. */ | ||
817 | &alphabook1_mv, | ||
818 | &rawhide_mv, | ||
819 | NULL, /* K2 */ | ||
820 | &lynx_mv, /* Lynx */ | ||
821 | &xl_mv, | ||
822 | NULL, /* EB164 -- see variation. */ | ||
823 | NULL, /* Noritake -- see below. */ | ||
824 | NULL, /* Cortex */ | ||
825 | NULL, /* 29 */ | ||
826 | &miata_mv, | ||
827 | NULL, /* XXM */ | ||
828 | &takara_mv, | ||
829 | NULL, /* Yukon */ | ||
830 | NULL, /* Tsunami -- see variation. */ | ||
831 | &wildfire_mv, /* Wildfire */ | ||
832 | NULL, /* CUSCO */ | ||
833 | &eiger_mv, /* Eiger */ | ||
834 | NULL, /* Titan */ | ||
835 | NULL, /* Marvel */ | ||
836 | }; | ||
837 | |||
838 | static struct alpha_machine_vector *unofficial_vecs[] __initdata = | ||
839 | { | ||
840 | NULL, /* 100 */ | ||
841 | &ruffian_mv, | ||
842 | }; | ||
843 | |||
844 | static struct alpha_machine_vector *api_vecs[] __initdata = | ||
845 | { | ||
846 | NULL, /* 200 */ | ||
847 | &nautilus_mv, | ||
848 | }; | ||
849 | |||
850 | static struct alpha_machine_vector *alcor_vecs[] __initdata = | ||
851 | { | ||
852 | &alcor_mv, &xlt_mv, &xlt_mv | ||
853 | }; | ||
854 | |||
855 | static struct alpha_machine_vector *eb164_vecs[] __initdata = | ||
856 | { | ||
857 | &eb164_mv, &pc164_mv, &lx164_mv, &sx164_mv, &rx164_mv | ||
858 | }; | ||
859 | |||
860 | static struct alpha_machine_vector *eb64p_vecs[] __initdata = | ||
861 | { | ||
862 | &eb64p_mv, | ||
863 | &cabriolet_mv, | ||
864 | &cabriolet_mv /* AlphaPCI64 */ | ||
865 | }; | ||
866 | |||
867 | static struct alpha_machine_vector *eb66_vecs[] __initdata = | ||
868 | { | ||
869 | &eb66_mv, | ||
870 | &eb66p_mv | ||
871 | }; | ||
872 | |||
873 | static struct alpha_machine_vector *marvel_vecs[] __initdata = | ||
874 | { | ||
875 | &marvel_ev7_mv, | ||
876 | }; | ||
877 | |||
878 | static struct alpha_machine_vector *titan_vecs[] __initdata = | ||
879 | { | ||
880 | &titan_mv, /* default */ | ||
881 | &privateer_mv, /* privateer */ | ||
882 | &titan_mv, /* falcon */ | ||
883 | &privateer_mv, /* granite */ | ||
884 | }; | ||
885 | |||
886 | static struct alpha_machine_vector *tsunami_vecs[] __initdata = | ||
887 | { | ||
888 | NULL, | ||
889 | &dp264_mv, /* dp264 */ | ||
890 | &dp264_mv, /* warhol */ | ||
891 | &dp264_mv, /* windjammer */ | ||
892 | &monet_mv, /* monet */ | ||
893 | &clipper_mv, /* clipper */ | ||
894 | &dp264_mv, /* goldrush */ | ||
895 | &webbrick_mv, /* webbrick */ | ||
896 | &dp264_mv, /* catamaran */ | ||
897 | NULL, /* brisbane? */ | ||
898 | NULL, /* melbourne? */ | ||
899 | NULL, /* flying clipper? */ | ||
900 | &shark_mv, /* shark */ | ||
901 | }; | ||
902 | |||
903 | /* ??? Do we need to distinguish between Rawhides? */ | ||
904 | |||
905 | struct alpha_machine_vector *vec; | ||
906 | |||
907 | /* Search the system tables first... */ | ||
908 | vec = NULL; | ||
909 | if (type < N(systype_vecs)) { | ||
910 | vec = systype_vecs[type]; | ||
911 | } else if ((type > ST_API_BIAS) && | ||
912 | (type - ST_API_BIAS) < N(api_vecs)) { | ||
913 | vec = api_vecs[type - ST_API_BIAS]; | ||
914 | } else if ((type > ST_UNOFFICIAL_BIAS) && | ||
915 | (type - ST_UNOFFICIAL_BIAS) < N(unofficial_vecs)) { | ||
916 | vec = unofficial_vecs[type - ST_UNOFFICIAL_BIAS]; | ||
917 | } | ||
918 | |||
919 | /* If we've not found one, try for a variation. */ | ||
920 | |||
921 | if (!vec) { | ||
922 | /* Member ID is a bit-field. */ | ||
923 | unsigned long member = (variation >> 10) & 0x3f; | ||
924 | |||
925 | cpu &= 0xffffffff; /* make it usable */ | ||
926 | |||
927 | switch (type) { | ||
928 | case ST_DEC_ALCOR: | ||
929 | if (member < N(alcor_indices)) | ||
930 | vec = alcor_vecs[alcor_indices[member]]; | ||
931 | break; | ||
932 | case ST_DEC_EB164: | ||
933 | if (member < N(eb164_indices)) | ||
934 | vec = eb164_vecs[eb164_indices[member]]; | ||
935 | /* PC164 may show as EB164 variation with EV56 CPU, | ||
936 | but, since no true EB164 had anything but EV5... */ | ||
937 | if (vec == &eb164_mv && cpu == EV56_CPU) | ||
938 | vec = &pc164_mv; | ||
939 | break; | ||
940 | case ST_DEC_EB64P: | ||
941 | if (member < N(eb64p_indices)) | ||
942 | vec = eb64p_vecs[eb64p_indices[member]]; | ||
943 | break; | ||
944 | case ST_DEC_EB66: | ||
945 | if (member < N(eb66_indices)) | ||
946 | vec = eb66_vecs[eb66_indices[member]]; | ||
947 | break; | ||
948 | case ST_DEC_MARVEL: | ||
949 | if (member < N(marvel_indices)) | ||
950 | vec = marvel_vecs[marvel_indices[member]]; | ||
951 | break; | ||
952 | case ST_DEC_TITAN: | ||
953 | vec = titan_vecs[0]; /* default */ | ||
954 | if (member < N(titan_indices)) | ||
955 | vec = titan_vecs[titan_indices[member]]; | ||
956 | break; | ||
957 | case ST_DEC_TSUNAMI: | ||
958 | if (member < N(tsunami_indices)) | ||
959 | vec = tsunami_vecs[tsunami_indices[member]]; | ||
960 | break; | ||
961 | case ST_DEC_1000: | ||
962 | if (cpu == EV5_CPU || cpu == EV56_CPU) | ||
963 | vec = &mikasa_primo_mv; | ||
964 | else | ||
965 | vec = &mikasa_mv; | ||
966 | break; | ||
967 | case ST_DEC_NORITAKE: | ||
968 | if (cpu == EV5_CPU || cpu == EV56_CPU) | ||
969 | vec = &noritake_primo_mv; | ||
970 | else | ||
971 | vec = &noritake_mv; | ||
972 | break; | ||
973 | case ST_DEC_2100_A500: | ||
974 | if (cpu == EV5_CPU || cpu == EV56_CPU) | ||
975 | vec = &sable_gamma_mv; | ||
976 | else | ||
977 | vec = &sable_mv; | ||
978 | break; | ||
979 | } | ||
980 | } | ||
981 | return vec; | ||
982 | } | ||
983 | |||
984 | static struct alpha_machine_vector * __init | ||
985 | get_sysvec_byname(const char *name) | ||
986 | { | ||
987 | static struct alpha_machine_vector *all_vecs[] __initdata = | ||
988 | { | ||
989 | &alcor_mv, | ||
990 | &alphabook1_mv, | ||
991 | &avanti_mv, | ||
992 | &cabriolet_mv, | ||
993 | &clipper_mv, | ||
994 | &dp264_mv, | ||
995 | &eb164_mv, | ||
996 | &eb64p_mv, | ||
997 | &eb66_mv, | ||
998 | &eb66p_mv, | ||
999 | &eiger_mv, | ||
1000 | &jensen_mv, | ||
1001 | &lx164_mv, | ||
1002 | &lynx_mv, | ||
1003 | &miata_mv, | ||
1004 | &mikasa_mv, | ||
1005 | &mikasa_primo_mv, | ||
1006 | &monet_mv, | ||
1007 | &nautilus_mv, | ||
1008 | &noname_mv, | ||
1009 | &noritake_mv, | ||
1010 | &noritake_primo_mv, | ||
1011 | &p2k_mv, | ||
1012 | &pc164_mv, | ||
1013 | &privateer_mv, | ||
1014 | &rawhide_mv, | ||
1015 | &ruffian_mv, | ||
1016 | &rx164_mv, | ||
1017 | &sable_mv, | ||
1018 | &sable_gamma_mv, | ||
1019 | &shark_mv, | ||
1020 | &sx164_mv, | ||
1021 | &takara_mv, | ||
1022 | &webbrick_mv, | ||
1023 | &wildfire_mv, | ||
1024 | &xl_mv, | ||
1025 | &xlt_mv | ||
1026 | }; | ||
1027 | |||
1028 | size_t i; | ||
1029 | |||
1030 | for (i = 0; i < N(all_vecs); ++i) { | ||
1031 | struct alpha_machine_vector *mv = all_vecs[i]; | ||
1032 | if (strcasecmp(mv->vector_name, name) == 0) | ||
1033 | return mv; | ||
1034 | } | ||
1035 | return NULL; | ||
1036 | } | ||
1037 | |||
1038 | static void | ||
1039 | get_sysnames(unsigned long type, unsigned long variation, unsigned long cpu, | ||
1040 | char **type_name, char **variation_name) | ||
1041 | { | ||
1042 | unsigned long member; | ||
1043 | |||
1044 | /* If not in the tables, make it UNKNOWN, | ||
1045 | else set type name to family */ | ||
1046 | if (type < N(systype_names)) { | ||
1047 | *type_name = systype_names[type]; | ||
1048 | } else if ((type > ST_API_BIAS) && | ||
1049 | (type - ST_API_BIAS) < N(api_names)) { | ||
1050 | *type_name = api_names[type - ST_API_BIAS]; | ||
1051 | } else if ((type > ST_UNOFFICIAL_BIAS) && | ||
1052 | (type - ST_UNOFFICIAL_BIAS) < N(unofficial_names)) { | ||
1053 | *type_name = unofficial_names[type - ST_UNOFFICIAL_BIAS]; | ||
1054 | } else { | ||
1055 | *type_name = sys_unknown; | ||
1056 | *variation_name = sys_unknown; | ||
1057 | return; | ||
1058 | } | ||
1059 | |||
1060 | /* Set variation to "0"; if variation is zero, done. */ | ||
1061 | *variation_name = systype_names[0]; | ||
1062 | if (variation == 0) { | ||
1063 | return; | ||
1064 | } | ||
1065 | |||
1066 | member = (variation >> 10) & 0x3f; /* member ID is a bit-field */ | ||
1067 | |||
1068 | cpu &= 0xffffffff; /* make it usable */ | ||
1069 | |||
1070 | switch (type) { /* select by family */ | ||
1071 | default: /* default to variation "0" for now */ | ||
1072 | break; | ||
1073 | case ST_DEC_EB164: | ||
1074 | if (member < N(eb164_indices)) | ||
1075 | *variation_name = eb164_names[eb164_indices[member]]; | ||
1076 | /* PC164 may show as EB164 variation, but with EV56 CPU, | ||
1077 | so, since no true EB164 had anything but EV5... */ | ||
1078 | if (eb164_indices[member] == 0 && cpu == EV56_CPU) | ||
1079 | *variation_name = eb164_names[1]; /* make it PC164 */ | ||
1080 | break; | ||
1081 | case ST_DEC_ALCOR: | ||
1082 | if (member < N(alcor_indices)) | ||
1083 | *variation_name = alcor_names[alcor_indices[member]]; | ||
1084 | break; | ||
1085 | case ST_DEC_EB64P: | ||
1086 | if (member < N(eb64p_indices)) | ||
1087 | *variation_name = eb64p_names[eb64p_indices[member]]; | ||
1088 | break; | ||
1089 | case ST_DEC_EB66: | ||
1090 | if (member < N(eb66_indices)) | ||
1091 | *variation_name = eb66_names[eb66_indices[member]]; | ||
1092 | break; | ||
1093 | case ST_DEC_MARVEL: | ||
1094 | if (member < N(marvel_indices)) | ||
1095 | *variation_name = marvel_names[marvel_indices[member]]; | ||
1096 | break; | ||
1097 | case ST_DEC_RAWHIDE: | ||
1098 | if (member < N(rawhide_indices)) | ||
1099 | *variation_name = rawhide_names[rawhide_indices[member]]; | ||
1100 | break; | ||
1101 | case ST_DEC_TITAN: | ||
1102 | *variation_name = titan_names[0]; /* default */ | ||
1103 | if (member < N(titan_indices)) | ||
1104 | *variation_name = titan_names[titan_indices[member]]; | ||
1105 | break; | ||
1106 | case ST_DEC_TSUNAMI: | ||
1107 | if (member < N(tsunami_indices)) | ||
1108 | *variation_name = tsunami_names[tsunami_indices[member]]; | ||
1109 | break; | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | /* | ||
1114 | * A change was made to the HWRPB via an ECO and the following code | ||
1115 | * tracks a part of the ECO. In HWRPB versions less than 5, the ECO | ||
1116 | * was not implemented in the console firmware. If it's revision 5 or | ||
1117 | * greater we can get the name of the platform as an ASCII string from | ||
1118 | * the HWRPB. That's what this function does. It checks the revision | ||
1119 | * level and if the string is in the HWRPB it returns the address of | ||
1120 | * the string--a pointer to the name of the platform. | ||
1121 | * | ||
1122 | * Returns: | ||
1123 | * - Pointer to a ASCII string if it's in the HWRPB | ||
1124 | * - Pointer to a blank string if the data is not in the HWRPB. | ||
1125 | */ | ||
1126 | |||
1127 | static char * | ||
1128 | platform_string(void) | ||
1129 | { | ||
1130 | struct dsr_struct *dsr; | ||
1131 | static char unk_system_string[] = "N/A"; | ||
1132 | |||
1133 | /* Go to the console for the string pointer. | ||
1134 | * If the rpb_vers is not 5 or greater the rpb | ||
1135 | * is old and does not have this data in it. | ||
1136 | */ | ||
1137 | if (hwrpb->revision < 5) | ||
1138 | return (unk_system_string); | ||
1139 | else { | ||
1140 | /* The Dynamic System Recognition struct | ||
1141 | * has the system platform name starting | ||
1142 | * after the character count of the string. | ||
1143 | */ | ||
1144 | dsr = ((struct dsr_struct *) | ||
1145 | ((char *)hwrpb + hwrpb->dsr_offset)); | ||
1146 | return ((char *)dsr + (dsr->sysname_off + | ||
1147 | sizeof(long))); | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | static int | ||
1152 | get_nr_processors(struct percpu_struct *cpubase, unsigned long num) | ||
1153 | { | ||
1154 | struct percpu_struct *cpu; | ||
1155 | unsigned long i; | ||
1156 | int count = 0; | ||
1157 | |||
1158 | for (i = 0; i < num; i++) { | ||
1159 | cpu = (struct percpu_struct *) | ||
1160 | ((char *)cpubase + i*hwrpb->processor_size); | ||
1161 | if ((cpu->flags & 0x1cc) == 0x1cc) | ||
1162 | count++; | ||
1163 | } | ||
1164 | return count; | ||
1165 | } | ||
1166 | |||
1167 | static void | ||
1168 | show_cache_size (struct seq_file *f, const char *which, int shape) | ||
1169 | { | ||
1170 | if (shape == -1) | ||
1171 | seq_printf (f, "%s\t\t: n/a\n", which); | ||
1172 | else if (shape == 0) | ||
1173 | seq_printf (f, "%s\t\t: unknown\n", which); | ||
1174 | else | ||
1175 | seq_printf (f, "%s\t\t: %dK, %d-way, %db line\n", | ||
1176 | which, shape >> 10, shape & 15, | ||
1177 | 1 << ((shape >> 4) & 15)); | ||
1178 | } | ||
1179 | |||
1180 | static int | ||
1181 | show_cpuinfo(struct seq_file *f, void *slot) | ||
1182 | { | ||
1183 | extern struct unaligned_stat { | ||
1184 | unsigned long count, va, pc; | ||
1185 | } unaligned[2]; | ||
1186 | |||
1187 | static char cpu_names[][8] = { | ||
1188 | "EV3", "EV4", "Simulate", "LCA4", "EV5", "EV45", "EV56", | ||
1189 | "EV6", "PCA56", "PCA57", "EV67", "EV68CB", "EV68AL", | ||
1190 | "EV68CX", "EV7", "EV79", "EV69" | ||
1191 | }; | ||
1192 | |||
1193 | struct percpu_struct *cpu = slot; | ||
1194 | unsigned int cpu_index; | ||
1195 | char *cpu_name; | ||
1196 | char *systype_name; | ||
1197 | char *sysvariation_name; | ||
1198 | int nr_processors; | ||
1199 | |||
1200 | cpu_index = (unsigned) (cpu->type - 1); | ||
1201 | cpu_name = "Unknown"; | ||
1202 | if (cpu_index < N(cpu_names)) | ||
1203 | cpu_name = cpu_names[cpu_index]; | ||
1204 | |||
1205 | get_sysnames(hwrpb->sys_type, hwrpb->sys_variation, | ||
1206 | cpu->type, &systype_name, &sysvariation_name); | ||
1207 | |||
1208 | nr_processors = get_nr_processors(cpu, hwrpb->nr_processors); | ||
1209 | |||
1210 | seq_printf(f, "cpu\t\t\t: Alpha\n" | ||
1211 | "cpu model\t\t: %s\n" | ||
1212 | "cpu variation\t\t: %ld\n" | ||
1213 | "cpu revision\t\t: %ld\n" | ||
1214 | "cpu serial number\t: %s\n" | ||
1215 | "system type\t\t: %s\n" | ||
1216 | "system variation\t: %s\n" | ||
1217 | "system revision\t\t: %ld\n" | ||
1218 | "system serial number\t: %s\n" | ||
1219 | "cycle frequency [Hz]\t: %lu %s\n" | ||
1220 | "timer frequency [Hz]\t: %lu.%02lu\n" | ||
1221 | "page size [bytes]\t: %ld\n" | ||
1222 | "phys. address bits\t: %ld\n" | ||
1223 | "max. addr. space #\t: %ld\n" | ||
1224 | "BogoMIPS\t\t: %lu.%02lu\n" | ||
1225 | "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n" | ||
1226 | "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n" | ||
1227 | "platform string\t\t: %s\n" | ||
1228 | "cpus detected\t\t: %d\n", | ||
1229 | cpu_name, cpu->variation, cpu->revision, | ||
1230 | (char*)cpu->serial_no, | ||
1231 | systype_name, sysvariation_name, hwrpb->sys_revision, | ||
1232 | (char*)hwrpb->ssn, | ||
1233 | est_cycle_freq ? : hwrpb->cycle_freq, | ||
1234 | est_cycle_freq ? "est." : "", | ||
1235 | hwrpb->intr_freq / 4096, | ||
1236 | (100 * hwrpb->intr_freq / 4096) % 100, | ||
1237 | hwrpb->pagesize, | ||
1238 | hwrpb->pa_bits, | ||
1239 | hwrpb->max_asn, | ||
1240 | loops_per_jiffy / (500000/HZ), | ||
1241 | (loops_per_jiffy / (5000/HZ)) % 100, | ||
1242 | unaligned[0].count, unaligned[0].pc, unaligned[0].va, | ||
1243 | unaligned[1].count, unaligned[1].pc, unaligned[1].va, | ||
1244 | platform_string(), nr_processors); | ||
1245 | |||
1246 | #ifdef CONFIG_SMP | ||
1247 | seq_printf(f, "cpus active\t\t: %d\n" | ||
1248 | "cpu active mask\t\t: %016lx\n", | ||
1249 | num_online_cpus(), cpus_addr(cpu_possible_map)[0]); | ||
1250 | #endif | ||
1251 | |||
1252 | show_cache_size (f, "L1 Icache", alpha_l1i_cacheshape); | ||
1253 | show_cache_size (f, "L1 Dcache", alpha_l1d_cacheshape); | ||
1254 | show_cache_size (f, "L2 cache", alpha_l2_cacheshape); | ||
1255 | show_cache_size (f, "L3 cache", alpha_l3_cacheshape); | ||
1256 | |||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | static int __init | ||
1261 | read_mem_block(int *addr, int stride, int size) | ||
1262 | { | ||
1263 | long nloads = size / stride, cnt, tmp; | ||
1264 | |||
1265 | __asm__ __volatile__( | ||
1266 | " rpcc %0\n" | ||
1267 | "1: ldl %3,0(%2)\n" | ||
1268 | " subq %1,1,%1\n" | ||
1269 | /* Next two XORs introduce an explicit data dependency between | ||
1270 | consecutive loads in the loop, which will give us true load | ||
1271 | latency. */ | ||
1272 | " xor %3,%2,%2\n" | ||
1273 | " xor %3,%2,%2\n" | ||
1274 | " addq %2,%4,%2\n" | ||
1275 | " bne %1,1b\n" | ||
1276 | " rpcc %3\n" | ||
1277 | " subl %3,%0,%0\n" | ||
1278 | : "=&r" (cnt), "=&r" (nloads), "=&r" (addr), "=&r" (tmp) | ||
1279 | : "r" (stride), "1" (nloads), "2" (addr)); | ||
1280 | |||
1281 | return cnt / (size / stride); | ||
1282 | } | ||
1283 | |||
1284 | #define CSHAPE(totalsize, linesize, assoc) \ | ||
1285 | ((totalsize & ~0xff) | (linesize << 4) | assoc) | ||
1286 | |||
1287 | /* ??? EV5 supports up to 64M, but did the systems with more than | ||
1288 | 16M of BCACHE ever exist? */ | ||
1289 | #define MAX_BCACHE_SIZE 16*1024*1024 | ||
1290 | |||
1291 | /* Note that the offchip caches are direct mapped on all Alphas. */ | ||
1292 | static int __init | ||
1293 | external_cache_probe(int minsize, int width) | ||
1294 | { | ||
1295 | int cycles, prev_cycles = 1000000; | ||
1296 | int stride = 1 << width; | ||
1297 | long size = minsize, maxsize = MAX_BCACHE_SIZE * 2; | ||
1298 | |||
1299 | if (maxsize > (max_low_pfn + 1) << PAGE_SHIFT) | ||
1300 | maxsize = 1 << (floor_log2(max_low_pfn + 1) + PAGE_SHIFT); | ||
1301 | |||
1302 | /* Get the first block cached. */ | ||
1303 | read_mem_block(__va(0), stride, size); | ||
1304 | |||
1305 | while (size < maxsize) { | ||
1306 | /* Get an average load latency in cycles. */ | ||
1307 | cycles = read_mem_block(__va(0), stride, size); | ||
1308 | if (cycles > prev_cycles * 2) { | ||
1309 | /* Fine, we exceed the cache. */ | ||
1310 | printk("%ldK Bcache detected; load hit latency %d " | ||
1311 | "cycles, load miss latency %d cycles\n", | ||
1312 | size >> 11, prev_cycles, cycles); | ||
1313 | return CSHAPE(size >> 1, width, 1); | ||
1314 | } | ||
1315 | /* Try to get the next block cached. */ | ||
1316 | read_mem_block(__va(size), stride, size); | ||
1317 | prev_cycles = cycles; | ||
1318 | size <<= 1; | ||
1319 | } | ||
1320 | return -1; /* No BCACHE found. */ | ||
1321 | } | ||
1322 | |||
1323 | static void __init | ||
1324 | determine_cpu_caches (unsigned int cpu_type) | ||
1325 | { | ||
1326 | int L1I, L1D, L2, L3; | ||
1327 | |||
1328 | switch (cpu_type) { | ||
1329 | case EV4_CPU: | ||
1330 | case EV45_CPU: | ||
1331 | { | ||
1332 | if (cpu_type == EV4_CPU) | ||
1333 | L1I = CSHAPE(8*1024, 5, 1); | ||
1334 | else | ||
1335 | L1I = CSHAPE(16*1024, 5, 1); | ||
1336 | L1D = L1I; | ||
1337 | L3 = -1; | ||
1338 | |||
1339 | /* BIU_CTL is a write-only Abox register. PALcode has a | ||
1340 | shadow copy, and may be available from some versions | ||
1341 | of the CSERVE PALcall. If we can get it, then | ||
1342 | |||
1343 | unsigned long biu_ctl, size; | ||
1344 | size = 128*1024 * (1 << ((biu_ctl >> 28) & 7)); | ||
1345 | L2 = CSHAPE (size, 5, 1); | ||
1346 | |||
1347 | Unfortunately, we can't rely on that. | ||
1348 | */ | ||
1349 | L2 = external_cache_probe(128*1024, 5); | ||
1350 | break; | ||
1351 | } | ||
1352 | |||
1353 | case LCA4_CPU: | ||
1354 | { | ||
1355 | unsigned long car, size; | ||
1356 | |||
1357 | L1I = L1D = CSHAPE(8*1024, 5, 1); | ||
1358 | L3 = -1; | ||
1359 | |||
1360 | car = *(vuip) phys_to_virt (0x120000078UL); | ||
1361 | size = 64*1024 * (1 << ((car >> 5) & 7)); | ||
1362 | /* No typo -- 8 byte cacheline size. Whodathunk. */ | ||
1363 | L2 = (car & 1 ? CSHAPE (size, 3, 1) : -1); | ||
1364 | break; | ||
1365 | } | ||
1366 | |||
1367 | case EV5_CPU: | ||
1368 | case EV56_CPU: | ||
1369 | { | ||
1370 | unsigned long sc_ctl, width; | ||
1371 | |||
1372 | L1I = L1D = CSHAPE(8*1024, 5, 1); | ||
1373 | |||
1374 | /* Check the line size of the Scache. */ | ||
1375 | sc_ctl = *(vulp) phys_to_virt (0xfffff000a8UL); | ||
1376 | width = sc_ctl & 0x1000 ? 6 : 5; | ||
1377 | L2 = CSHAPE (96*1024, width, 3); | ||
1378 | |||
1379 | /* BC_CONTROL and BC_CONFIG are write-only IPRs. PALcode | ||
1380 | has a shadow copy, and may be available from some versions | ||
1381 | of the CSERVE PALcall. If we can get it, then | ||
1382 | |||
1383 | unsigned long bc_control, bc_config, size; | ||
1384 | size = 1024*1024 * (1 << ((bc_config & 7) - 1)); | ||
1385 | L3 = (bc_control & 1 ? CSHAPE (size, width, 1) : -1); | ||
1386 | |||
1387 | Unfortunately, we can't rely on that. | ||
1388 | */ | ||
1389 | L3 = external_cache_probe(1024*1024, width); | ||
1390 | break; | ||
1391 | } | ||
1392 | |||
1393 | case PCA56_CPU: | ||
1394 | case PCA57_CPU: | ||
1395 | { | ||
1396 | unsigned long cbox_config, size; | ||
1397 | |||
1398 | if (cpu_type == PCA56_CPU) { | ||
1399 | L1I = CSHAPE(16*1024, 6, 1); | ||
1400 | L1D = CSHAPE(8*1024, 5, 1); | ||
1401 | } else { | ||
1402 | L1I = CSHAPE(32*1024, 6, 2); | ||
1403 | L1D = CSHAPE(16*1024, 5, 1); | ||
1404 | } | ||
1405 | L3 = -1; | ||
1406 | |||
1407 | cbox_config = *(vulp) phys_to_virt (0xfffff00008UL); | ||
1408 | size = 512*1024 * (1 << ((cbox_config >> 12) & 3)); | ||
1409 | |||
1410 | #if 0 | ||
1411 | L2 = ((cbox_config >> 31) & 1 ? CSHAPE (size, 6, 1) : -1); | ||
1412 | #else | ||
1413 | L2 = external_cache_probe(512*1024, 6); | ||
1414 | #endif | ||
1415 | break; | ||
1416 | } | ||
1417 | |||
1418 | case EV6_CPU: | ||
1419 | case EV67_CPU: | ||
1420 | case EV68CB_CPU: | ||
1421 | case EV68AL_CPU: | ||
1422 | case EV68CX_CPU: | ||
1423 | case EV69_CPU: | ||
1424 | L1I = L1D = CSHAPE(64*1024, 6, 2); | ||
1425 | L2 = external_cache_probe(1024*1024, 6); | ||
1426 | L3 = -1; | ||
1427 | break; | ||
1428 | |||
1429 | case EV7_CPU: | ||
1430 | case EV79_CPU: | ||
1431 | L1I = L1D = CSHAPE(64*1024, 6, 2); | ||
1432 | L2 = CSHAPE(7*1024*1024/4, 6, 7); | ||
1433 | L3 = -1; | ||
1434 | break; | ||
1435 | |||
1436 | default: | ||
1437 | /* Nothing known about this cpu type. */ | ||
1438 | L1I = L1D = L2 = L3 = 0; | ||
1439 | break; | ||
1440 | } | ||
1441 | |||
1442 | alpha_l1i_cacheshape = L1I; | ||
1443 | alpha_l1d_cacheshape = L1D; | ||
1444 | alpha_l2_cacheshape = L2; | ||
1445 | alpha_l3_cacheshape = L3; | ||
1446 | } | ||
1447 | |||
1448 | /* | ||
1449 | * We show only CPU #0 info. | ||
1450 | */ | ||
1451 | static void * | ||
1452 | c_start(struct seq_file *f, loff_t *pos) | ||
1453 | { | ||
1454 | return *pos ? NULL : (char *)hwrpb + hwrpb->processor_offset; | ||
1455 | } | ||
1456 | |||
1457 | static void * | ||
1458 | c_next(struct seq_file *f, void *v, loff_t *pos) | ||
1459 | { | ||
1460 | return NULL; | ||
1461 | } | ||
1462 | |||
1463 | static void | ||
1464 | c_stop(struct seq_file *f, void *v) | ||
1465 | { | ||
1466 | } | ||
1467 | |||
1468 | struct seq_operations cpuinfo_op = { | ||
1469 | .start = c_start, | ||
1470 | .next = c_next, | ||
1471 | .stop = c_stop, | ||
1472 | .show = show_cpuinfo, | ||
1473 | }; | ||
1474 | |||
1475 | |||
1476 | static int | ||
1477 | alpha_panic_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
1478 | { | ||
1479 | #if 1 | ||
1480 | /* FIXME FIXME FIXME */ | ||
1481 | /* If we are using SRM and serial console, just hard halt here. */ | ||
1482 | if (alpha_using_srm && srmcons_output) | ||
1483 | __halt(); | ||
1484 | #endif | ||
1485 | return NOTIFY_DONE; | ||
1486 | } | ||
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c new file mode 100644 index 000000000000..08fe8071a7f8 --- /dev/null +++ b/arch/alpha/kernel/signal.c | |||
@@ -0,0 +1,672 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/signal.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * | ||
6 | * 1997-11-02 Modified for POSIX.1b signals by Richard Henderson | ||
7 | */ | ||
8 | |||
9 | #include <linux/sched.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/signal.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/wait.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/unistd.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/smp.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/stddef.h> | ||
20 | #include <linux/tty.h> | ||
21 | #include <linux/binfmts.h> | ||
22 | #include <linux/bitops.h> | ||
23 | |||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/sigcontext.h> | ||
26 | #include <asm/ucontext.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | |||
30 | |||
31 | #define DEBUG_SIG 0 | ||
32 | |||
33 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
34 | |||
35 | asmlinkage void ret_from_sys_call(void); | ||
36 | static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, | ||
37 | unsigned long, unsigned long); | ||
38 | |||
39 | |||
40 | /* | ||
41 | * The OSF/1 sigprocmask calling sequence is different from the | ||
42 | * C sigprocmask() sequence.. | ||
43 | * | ||
44 | * how: | ||
45 | * 1 - SIG_BLOCK | ||
46 | * 2 - SIG_UNBLOCK | ||
47 | * 3 - SIG_SETMASK | ||
48 | * | ||
49 | * We change the range to -1 .. 1 in order to let gcc easily | ||
50 | * use the conditional move instructions. | ||
51 | * | ||
52 | * Note that we don't need to acquire the kernel lock for SMP | ||
53 | * operation, as all of this is local to this thread. | ||
54 | */ | ||
55 | asmlinkage unsigned long | ||
56 | do_osf_sigprocmask(int how, unsigned long newmask, struct pt_regs *regs) | ||
57 | { | ||
58 | unsigned long oldmask = -EINVAL; | ||
59 | |||
60 | if ((unsigned long)how-1 <= 2) { | ||
61 | long sign = how-2; /* -1 .. 1 */ | ||
62 | unsigned long block, unblock; | ||
63 | |||
64 | newmask &= _BLOCKABLE; | ||
65 | spin_lock_irq(¤t->sighand->siglock); | ||
66 | oldmask = current->blocked.sig[0]; | ||
67 | |||
68 | unblock = oldmask & ~newmask; | ||
69 | block = oldmask | newmask; | ||
70 | if (!sign) | ||
71 | block = unblock; | ||
72 | if (sign <= 0) | ||
73 | newmask = block; | ||
74 | if (_NSIG_WORDS > 1 && sign > 0) | ||
75 | sigemptyset(¤t->blocked); | ||
76 | current->blocked.sig[0] = newmask; | ||
77 | recalc_sigpending(); | ||
78 | spin_unlock_irq(¤t->sighand->siglock); | ||
79 | |||
80 | regs->r0 = 0; /* special no error return */ | ||
81 | } | ||
82 | return oldmask; | ||
83 | } | ||
84 | |||
85 | asmlinkage int | ||
86 | osf_sigaction(int sig, const struct osf_sigaction __user *act, | ||
87 | struct osf_sigaction __user *oact) | ||
88 | { | ||
89 | struct k_sigaction new_ka, old_ka; | ||
90 | int ret; | ||
91 | |||
92 | if (act) { | ||
93 | old_sigset_t mask; | ||
94 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
95 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
96 | __get_user(new_ka.sa.sa_flags, &act->sa_flags)) | ||
97 | return -EFAULT; | ||
98 | __get_user(mask, &act->sa_mask); | ||
99 | siginitset(&new_ka.sa.sa_mask, mask); | ||
100 | new_ka.ka_restorer = NULL; | ||
101 | } | ||
102 | |||
103 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
104 | |||
105 | if (!ret && oact) { | ||
106 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
107 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
108 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags)) | ||
109 | return -EFAULT; | ||
110 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); | ||
111 | } | ||
112 | |||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | asmlinkage long | ||
117 | sys_rt_sigaction(int sig, const struct sigaction __user *act, | ||
118 | struct sigaction __user *oact, | ||
119 | size_t sigsetsize, void __user *restorer) | ||
120 | { | ||
121 | struct k_sigaction new_ka, old_ka; | ||
122 | int ret; | ||
123 | |||
124 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
125 | if (sigsetsize != sizeof(sigset_t)) | ||
126 | return -EINVAL; | ||
127 | |||
128 | if (act) { | ||
129 | new_ka.ka_restorer = restorer; | ||
130 | if (copy_from_user(&new_ka.sa, act, sizeof(*act))) | ||
131 | return -EFAULT; | ||
132 | } | ||
133 | |||
134 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
135 | |||
136 | if (!ret && oact) { | ||
137 | if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) | ||
138 | return -EFAULT; | ||
139 | } | ||
140 | |||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Atomically swap in the new signal mask, and wait for a signal. | ||
146 | */ | ||
147 | asmlinkage int | ||
148 | do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw) | ||
149 | { | ||
150 | sigset_t oldset; | ||
151 | |||
152 | mask &= _BLOCKABLE; | ||
153 | spin_lock_irq(¤t->sighand->siglock); | ||
154 | oldset = current->blocked; | ||
155 | siginitset(¤t->blocked, mask); | ||
156 | recalc_sigpending(); | ||
157 | spin_unlock_irq(¤t->sighand->siglock); | ||
158 | |||
159 | /* Indicate EINTR on return from any possible signal handler, | ||
160 | which will not come back through here, but via sigreturn. */ | ||
161 | regs->r0 = EINTR; | ||
162 | regs->r19 = 1; | ||
163 | |||
164 | while (1) { | ||
165 | current->state = TASK_INTERRUPTIBLE; | ||
166 | schedule(); | ||
167 | if (do_signal(&oldset, regs, sw, 0, 0)) | ||
168 | return -EINTR; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | asmlinkage int | ||
173 | do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize, | ||
174 | struct pt_regs *regs, struct switch_stack *sw) | ||
175 | { | ||
176 | sigset_t oldset, set; | ||
177 | |||
178 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
179 | if (sigsetsize != sizeof(sigset_t)) | ||
180 | return -EINVAL; | ||
181 | if (copy_from_user(&set, uset, sizeof(set))) | ||
182 | return -EFAULT; | ||
183 | |||
184 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
185 | spin_lock_irq(¤t->sighand->siglock); | ||
186 | oldset = current->blocked; | ||
187 | current->blocked = set; | ||
188 | recalc_sigpending(); | ||
189 | spin_unlock_irq(¤t->sighand->siglock); | ||
190 | |||
191 | /* Indicate EINTR on return from any possible signal handler, | ||
192 | which will not come back through here, but via sigreturn. */ | ||
193 | regs->r0 = EINTR; | ||
194 | regs->r19 = 1; | ||
195 | |||
196 | while (1) { | ||
197 | current->state = TASK_INTERRUPTIBLE; | ||
198 | schedule(); | ||
199 | if (do_signal(&oldset, regs, sw, 0, 0)) | ||
200 | return -EINTR; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | asmlinkage int | ||
205 | sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) | ||
206 | { | ||
207 | return do_sigaltstack(uss, uoss, rdusp()); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Do a signal return; undo the signal stack. | ||
212 | */ | ||
213 | |||
214 | #if _NSIG_WORDS > 1 | ||
215 | # error "Non SA_SIGINFO frame needs rearranging" | ||
216 | #endif | ||
217 | |||
218 | struct sigframe | ||
219 | { | ||
220 | struct sigcontext sc; | ||
221 | unsigned int retcode[3]; | ||
222 | }; | ||
223 | |||
224 | struct rt_sigframe | ||
225 | { | ||
226 | struct siginfo info; | ||
227 | struct ucontext uc; | ||
228 | unsigned int retcode[3]; | ||
229 | }; | ||
230 | |||
231 | /* If this changes, userland unwinders that Know Things about our signal | ||
232 | frame will break. Do not undertake lightly. It also implies an ABI | ||
233 | change wrt the size of siginfo_t, which may cause some pain. */ | ||
234 | extern char compile_time_assert | ||
235 | [offsetof(struct rt_sigframe, uc.uc_mcontext) == 176 ? 1 : -1]; | ||
236 | |||
237 | #define INSN_MOV_R30_R16 0x47fe0410 | ||
238 | #define INSN_LDI_R0 0x201f0000 | ||
239 | #define INSN_CALLSYS 0x00000083 | ||
240 | |||
241 | static long | ||
242 | restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
243 | struct switch_stack *sw) | ||
244 | { | ||
245 | unsigned long usp; | ||
246 | long i, err = __get_user(regs->pc, &sc->sc_pc); | ||
247 | |||
248 | sw->r26 = (unsigned long) ret_from_sys_call; | ||
249 | |||
250 | err |= __get_user(regs->r0, sc->sc_regs+0); | ||
251 | err |= __get_user(regs->r1, sc->sc_regs+1); | ||
252 | err |= __get_user(regs->r2, sc->sc_regs+2); | ||
253 | err |= __get_user(regs->r3, sc->sc_regs+3); | ||
254 | err |= __get_user(regs->r4, sc->sc_regs+4); | ||
255 | err |= __get_user(regs->r5, sc->sc_regs+5); | ||
256 | err |= __get_user(regs->r6, sc->sc_regs+6); | ||
257 | err |= __get_user(regs->r7, sc->sc_regs+7); | ||
258 | err |= __get_user(regs->r8, sc->sc_regs+8); | ||
259 | err |= __get_user(sw->r9, sc->sc_regs+9); | ||
260 | err |= __get_user(sw->r10, sc->sc_regs+10); | ||
261 | err |= __get_user(sw->r11, sc->sc_regs+11); | ||
262 | err |= __get_user(sw->r12, sc->sc_regs+12); | ||
263 | err |= __get_user(sw->r13, sc->sc_regs+13); | ||
264 | err |= __get_user(sw->r14, sc->sc_regs+14); | ||
265 | err |= __get_user(sw->r15, sc->sc_regs+15); | ||
266 | err |= __get_user(regs->r16, sc->sc_regs+16); | ||
267 | err |= __get_user(regs->r17, sc->sc_regs+17); | ||
268 | err |= __get_user(regs->r18, sc->sc_regs+18); | ||
269 | err |= __get_user(regs->r19, sc->sc_regs+19); | ||
270 | err |= __get_user(regs->r20, sc->sc_regs+20); | ||
271 | err |= __get_user(regs->r21, sc->sc_regs+21); | ||
272 | err |= __get_user(regs->r22, sc->sc_regs+22); | ||
273 | err |= __get_user(regs->r23, sc->sc_regs+23); | ||
274 | err |= __get_user(regs->r24, sc->sc_regs+24); | ||
275 | err |= __get_user(regs->r25, sc->sc_regs+25); | ||
276 | err |= __get_user(regs->r26, sc->sc_regs+26); | ||
277 | err |= __get_user(regs->r27, sc->sc_regs+27); | ||
278 | err |= __get_user(regs->r28, sc->sc_regs+28); | ||
279 | err |= __get_user(regs->gp, sc->sc_regs+29); | ||
280 | err |= __get_user(usp, sc->sc_regs+30); | ||
281 | wrusp(usp); | ||
282 | |||
283 | for (i = 0; i < 31; i++) | ||
284 | err |= __get_user(sw->fp[i], sc->sc_fpregs+i); | ||
285 | err |= __get_user(sw->fp[31], &sc->sc_fpcr); | ||
286 | |||
287 | return err; | ||
288 | } | ||
289 | |||
290 | /* Note that this syscall is also used by setcontext(3) to install | ||
291 | a given sigcontext. This because it's impossible to set *all* | ||
292 | registers and transfer control from userland. */ | ||
293 | |||
294 | asmlinkage void | ||
295 | do_sigreturn(struct sigcontext __user *sc, struct pt_regs *regs, | ||
296 | struct switch_stack *sw) | ||
297 | { | ||
298 | sigset_t set; | ||
299 | |||
300 | /* Verify that it's a good sigcontext before using it */ | ||
301 | if (!access_ok(VERIFY_READ, sc, sizeof(*sc))) | ||
302 | goto give_sigsegv; | ||
303 | if (__get_user(set.sig[0], &sc->sc_mask)) | ||
304 | goto give_sigsegv; | ||
305 | |||
306 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
307 | spin_lock_irq(¤t->sighand->siglock); | ||
308 | current->blocked = set; | ||
309 | recalc_sigpending(); | ||
310 | spin_unlock_irq(¤t->sighand->siglock); | ||
311 | |||
312 | if (restore_sigcontext(sc, regs, sw)) | ||
313 | goto give_sigsegv; | ||
314 | |||
315 | /* Send SIGTRAP if we're single-stepping: */ | ||
316 | if (ptrace_cancel_bpt (current)) { | ||
317 | siginfo_t info; | ||
318 | |||
319 | info.si_signo = SIGTRAP; | ||
320 | info.si_errno = 0; | ||
321 | info.si_code = TRAP_BRKPT; | ||
322 | info.si_addr = (void __user *) regs->pc; | ||
323 | info.si_trapno = 0; | ||
324 | send_sig_info(SIGTRAP, &info, current); | ||
325 | } | ||
326 | return; | ||
327 | |||
328 | give_sigsegv: | ||
329 | force_sig(SIGSEGV, current); | ||
330 | } | ||
331 | |||
332 | asmlinkage void | ||
333 | do_rt_sigreturn(struct rt_sigframe __user *frame, struct pt_regs *regs, | ||
334 | struct switch_stack *sw) | ||
335 | { | ||
336 | sigset_t set; | ||
337 | |||
338 | /* Verify that it's a good ucontext_t before using it */ | ||
339 | if (!access_ok(VERIFY_READ, &frame->uc, sizeof(frame->uc))) | ||
340 | goto give_sigsegv; | ||
341 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
342 | goto give_sigsegv; | ||
343 | |||
344 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
345 | spin_lock_irq(¤t->sighand->siglock); | ||
346 | current->blocked = set; | ||
347 | recalc_sigpending(); | ||
348 | spin_unlock_irq(¤t->sighand->siglock); | ||
349 | |||
350 | if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw)) | ||
351 | goto give_sigsegv; | ||
352 | |||
353 | /* Send SIGTRAP if we're single-stepping: */ | ||
354 | if (ptrace_cancel_bpt (current)) { | ||
355 | siginfo_t info; | ||
356 | |||
357 | info.si_signo = SIGTRAP; | ||
358 | info.si_errno = 0; | ||
359 | info.si_code = TRAP_BRKPT; | ||
360 | info.si_addr = (void __user *) regs->pc; | ||
361 | info.si_trapno = 0; | ||
362 | send_sig_info(SIGTRAP, &info, current); | ||
363 | } | ||
364 | return; | ||
365 | |||
366 | give_sigsegv: | ||
367 | force_sig(SIGSEGV, current); | ||
368 | } | ||
369 | |||
370 | |||
371 | /* | ||
372 | * Set up a signal frame. | ||
373 | */ | ||
374 | |||
375 | static inline void __user * | ||
376 | get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) | ||
377 | { | ||
378 | if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) | ||
379 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
380 | |||
381 | return (void __user *)((sp - frame_size) & -32ul); | ||
382 | } | ||
383 | |||
384 | static long | ||
385 | setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
386 | struct switch_stack *sw, unsigned long mask, unsigned long sp) | ||
387 | { | ||
388 | long i, err = 0; | ||
389 | |||
390 | err |= __put_user(on_sig_stack((unsigned long)sc), &sc->sc_onstack); | ||
391 | err |= __put_user(mask, &sc->sc_mask); | ||
392 | err |= __put_user(regs->pc, &sc->sc_pc); | ||
393 | err |= __put_user(8, &sc->sc_ps); | ||
394 | |||
395 | err |= __put_user(regs->r0 , sc->sc_regs+0); | ||
396 | err |= __put_user(regs->r1 , sc->sc_regs+1); | ||
397 | err |= __put_user(regs->r2 , sc->sc_regs+2); | ||
398 | err |= __put_user(regs->r3 , sc->sc_regs+3); | ||
399 | err |= __put_user(regs->r4 , sc->sc_regs+4); | ||
400 | err |= __put_user(regs->r5 , sc->sc_regs+5); | ||
401 | err |= __put_user(regs->r6 , sc->sc_regs+6); | ||
402 | err |= __put_user(regs->r7 , sc->sc_regs+7); | ||
403 | err |= __put_user(regs->r8 , sc->sc_regs+8); | ||
404 | err |= __put_user(sw->r9 , sc->sc_regs+9); | ||
405 | err |= __put_user(sw->r10 , sc->sc_regs+10); | ||
406 | err |= __put_user(sw->r11 , sc->sc_regs+11); | ||
407 | err |= __put_user(sw->r12 , sc->sc_regs+12); | ||
408 | err |= __put_user(sw->r13 , sc->sc_regs+13); | ||
409 | err |= __put_user(sw->r14 , sc->sc_regs+14); | ||
410 | err |= __put_user(sw->r15 , sc->sc_regs+15); | ||
411 | err |= __put_user(regs->r16, sc->sc_regs+16); | ||
412 | err |= __put_user(regs->r17, sc->sc_regs+17); | ||
413 | err |= __put_user(regs->r18, sc->sc_regs+18); | ||
414 | err |= __put_user(regs->r19, sc->sc_regs+19); | ||
415 | err |= __put_user(regs->r20, sc->sc_regs+20); | ||
416 | err |= __put_user(regs->r21, sc->sc_regs+21); | ||
417 | err |= __put_user(regs->r22, sc->sc_regs+22); | ||
418 | err |= __put_user(regs->r23, sc->sc_regs+23); | ||
419 | err |= __put_user(regs->r24, sc->sc_regs+24); | ||
420 | err |= __put_user(regs->r25, sc->sc_regs+25); | ||
421 | err |= __put_user(regs->r26, sc->sc_regs+26); | ||
422 | err |= __put_user(regs->r27, sc->sc_regs+27); | ||
423 | err |= __put_user(regs->r28, sc->sc_regs+28); | ||
424 | err |= __put_user(regs->gp , sc->sc_regs+29); | ||
425 | err |= __put_user(sp, sc->sc_regs+30); | ||
426 | err |= __put_user(0, sc->sc_regs+31); | ||
427 | |||
428 | for (i = 0; i < 31; i++) | ||
429 | err |= __put_user(sw->fp[i], sc->sc_fpregs+i); | ||
430 | err |= __put_user(0, sc->sc_fpregs+31); | ||
431 | err |= __put_user(sw->fp[31], &sc->sc_fpcr); | ||
432 | |||
433 | err |= __put_user(regs->trap_a0, &sc->sc_traparg_a0); | ||
434 | err |= __put_user(regs->trap_a1, &sc->sc_traparg_a1); | ||
435 | err |= __put_user(regs->trap_a2, &sc->sc_traparg_a2); | ||
436 | |||
437 | return err; | ||
438 | } | ||
439 | |||
440 | static void | ||
441 | setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, | ||
442 | struct pt_regs *regs, struct switch_stack * sw) | ||
443 | { | ||
444 | unsigned long oldsp, r26, err = 0; | ||
445 | struct sigframe __user *frame; | ||
446 | |||
447 | oldsp = rdusp(); | ||
448 | frame = get_sigframe(ka, oldsp, sizeof(*frame)); | ||
449 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
450 | goto give_sigsegv; | ||
451 | |||
452 | err |= setup_sigcontext(&frame->sc, regs, sw, set->sig[0], oldsp); | ||
453 | if (err) | ||
454 | goto give_sigsegv; | ||
455 | |||
456 | /* Set up to return from userspace. If provided, use a stub | ||
457 | already in userspace. */ | ||
458 | if (ka->ka_restorer) { | ||
459 | r26 = (unsigned long) ka->ka_restorer; | ||
460 | } else { | ||
461 | err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); | ||
462 | err |= __put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1); | ||
463 | err |= __put_user(INSN_CALLSYS, frame->retcode+2); | ||
464 | imb(); | ||
465 | r26 = (unsigned long) frame->retcode; | ||
466 | } | ||
467 | |||
468 | /* Check that everything was written properly. */ | ||
469 | if (err) | ||
470 | goto give_sigsegv; | ||
471 | |||
472 | /* "Return" to the handler */ | ||
473 | regs->r26 = r26; | ||
474 | regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; | ||
475 | regs->r16 = sig; /* a0: signal number */ | ||
476 | regs->r17 = 0; /* a1: exception code */ | ||
477 | regs->r18 = (unsigned long) &frame->sc; /* a2: sigcontext pointer */ | ||
478 | wrusp((unsigned long) frame); | ||
479 | |||
480 | #if DEBUG_SIG | ||
481 | printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", | ||
482 | current->comm, current->pid, frame, regs->pc, regs->r26); | ||
483 | #endif | ||
484 | |||
485 | return; | ||
486 | |||
487 | give_sigsegv: | ||
488 | force_sigsegv(sig, current); | ||
489 | } | ||
490 | |||
491 | static void | ||
492 | setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
493 | sigset_t *set, struct pt_regs *regs, struct switch_stack * sw) | ||
494 | { | ||
495 | unsigned long oldsp, r26, err = 0; | ||
496 | struct rt_sigframe __user *frame; | ||
497 | |||
498 | oldsp = rdusp(); | ||
499 | frame = get_sigframe(ka, oldsp, sizeof(*frame)); | ||
500 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
501 | goto give_sigsegv; | ||
502 | |||
503 | err |= copy_siginfo_to_user(&frame->info, info); | ||
504 | |||
505 | /* Create the ucontext. */ | ||
506 | err |= __put_user(0, &frame->uc.uc_flags); | ||
507 | err |= __put_user(0, &frame->uc.uc_link); | ||
508 | err |= __put_user(set->sig[0], &frame->uc.uc_osf_sigmask); | ||
509 | err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
510 | err |= __put_user(sas_ss_flags(oldsp), &frame->uc.uc_stack.ss_flags); | ||
511 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
512 | err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, | ||
513 | set->sig[0], oldsp); | ||
514 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
515 | if (err) | ||
516 | goto give_sigsegv; | ||
517 | |||
518 | /* Set up to return from userspace. If provided, use a stub | ||
519 | already in userspace. */ | ||
520 | if (ka->ka_restorer) { | ||
521 | r26 = (unsigned long) ka->ka_restorer; | ||
522 | } else { | ||
523 | err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); | ||
524 | err |= __put_user(INSN_LDI_R0+__NR_rt_sigreturn, | ||
525 | frame->retcode+1); | ||
526 | err |= __put_user(INSN_CALLSYS, frame->retcode+2); | ||
527 | imb(); | ||
528 | r26 = (unsigned long) frame->retcode; | ||
529 | } | ||
530 | |||
531 | if (err) | ||
532 | goto give_sigsegv; | ||
533 | |||
534 | /* "Return" to the handler */ | ||
535 | regs->r26 = r26; | ||
536 | regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; | ||
537 | regs->r16 = sig; /* a0: signal number */ | ||
538 | regs->r17 = (unsigned long) &frame->info; /* a1: siginfo pointer */ | ||
539 | regs->r18 = (unsigned long) &frame->uc; /* a2: ucontext pointer */ | ||
540 | wrusp((unsigned long) frame); | ||
541 | |||
542 | #if DEBUG_SIG | ||
543 | printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", | ||
544 | current->comm, current->pid, frame, regs->pc, regs->r26); | ||
545 | #endif | ||
546 | |||
547 | return; | ||
548 | |||
549 | give_sigsegv: | ||
550 | force_sigsegv(sig, current); | ||
551 | } | ||
552 | |||
553 | |||
554 | /* | ||
555 | * OK, we're invoking a handler. | ||
556 | */ | ||
557 | static inline void | ||
558 | handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
559 | sigset_t *oldset, struct pt_regs * regs, struct switch_stack *sw) | ||
560 | { | ||
561 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
562 | setup_rt_frame(sig, ka, info, oldset, regs, sw); | ||
563 | else | ||
564 | setup_frame(sig, ka, oldset, regs, sw); | ||
565 | |||
566 | if (ka->sa.sa_flags & SA_RESETHAND) | ||
567 | ka->sa.sa_handler = SIG_DFL; | ||
568 | |||
569 | if (!(ka->sa.sa_flags & SA_NODEFER)) { | ||
570 | spin_lock_irq(¤t->sighand->siglock); | ||
571 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | ||
572 | sigaddset(¤t->blocked,sig); | ||
573 | recalc_sigpending(); | ||
574 | spin_unlock_irq(¤t->sighand->siglock); | ||
575 | } | ||
576 | } | ||
577 | |||
578 | static inline void | ||
579 | syscall_restart(unsigned long r0, unsigned long r19, | ||
580 | struct pt_regs *regs, struct k_sigaction *ka) | ||
581 | { | ||
582 | switch (regs->r0) { | ||
583 | case ERESTARTSYS: | ||
584 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
585 | case ERESTARTNOHAND: | ||
586 | regs->r0 = EINTR; | ||
587 | break; | ||
588 | } | ||
589 | /* fallthrough */ | ||
590 | case ERESTARTNOINTR: | ||
591 | regs->r0 = r0; /* reset v0 and a3 and replay syscall */ | ||
592 | regs->r19 = r19; | ||
593 | regs->pc -= 4; | ||
594 | break; | ||
595 | case ERESTART_RESTARTBLOCK: | ||
596 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
597 | regs->r0 = EINTR; | ||
598 | break; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | |||
603 | /* | ||
604 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
605 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
606 | * mistake. | ||
607 | * | ||
608 | * Note that we go through the signals twice: once to check the signals that | ||
609 | * the kernel can handle, and then we build all the user-level signal handling | ||
610 | * stack-frames in one go after that. | ||
611 | * | ||
612 | * "r0" and "r19" are the registers we need to restore for system call | ||
613 | * restart. "r0" is also used as an indicator whether we can restart at | ||
614 | * all (if we get here from anything but a syscall return, it will be 0) | ||
615 | */ | ||
616 | static int | ||
617 | do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw, | ||
618 | unsigned long r0, unsigned long r19) | ||
619 | { | ||
620 | siginfo_t info; | ||
621 | int signr; | ||
622 | unsigned long single_stepping = ptrace_cancel_bpt(current); | ||
623 | struct k_sigaction ka; | ||
624 | |||
625 | if (!oldset) | ||
626 | oldset = ¤t->blocked; | ||
627 | |||
628 | /* This lets the debugger run, ... */ | ||
629 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
630 | /* ... so re-check the single stepping. */ | ||
631 | single_stepping |= ptrace_cancel_bpt(current); | ||
632 | |||
633 | if (signr > 0) { | ||
634 | /* Whee! Actually deliver the signal. */ | ||
635 | if (r0) syscall_restart(r0, r19, regs, &ka); | ||
636 | handle_signal(signr, &ka, &info, oldset, regs, sw); | ||
637 | if (single_stepping) | ||
638 | ptrace_set_bpt(current); /* re-set bpt */ | ||
639 | return 1; | ||
640 | } | ||
641 | |||
642 | if (r0) { | ||
643 | switch (regs->r0) { | ||
644 | case ERESTARTNOHAND: | ||
645 | case ERESTARTSYS: | ||
646 | case ERESTARTNOINTR: | ||
647 | /* Reset v0 and a3 and replay syscall. */ | ||
648 | regs->r0 = r0; | ||
649 | regs->r19 = r19; | ||
650 | regs->pc -= 4; | ||
651 | break; | ||
652 | case ERESTART_RESTARTBLOCK: | ||
653 | /* Force v0 to the restart syscall and reply. */ | ||
654 | regs->r0 = __NR_restart_syscall; | ||
655 | regs->pc -= 4; | ||
656 | break; | ||
657 | } | ||
658 | } | ||
659 | if (single_stepping) | ||
660 | ptrace_set_bpt(current); /* re-set breakpoint */ | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | void | ||
666 | do_notify_resume(sigset_t *oldset, struct pt_regs *regs, | ||
667 | struct switch_stack *sw, unsigned long r0, | ||
668 | unsigned long r19, unsigned long thread_info_flags) | ||
669 | { | ||
670 | if (thread_info_flags & _TIF_SIGPENDING) | ||
671 | do_signal(oldset, regs, sw, r0, r19); | ||
672 | } | ||
diff --git a/arch/alpha/kernel/smc37c669.c b/arch/alpha/kernel/smc37c669.c new file mode 100644 index 000000000000..fd467b207f0f --- /dev/null +++ b/arch/alpha/kernel/smc37c669.c | |||
@@ -0,0 +1,2554 @@ | |||
1 | /* | ||
2 | * SMC 37C669 initialization code | ||
3 | */ | ||
4 | #include <linux/kernel.h> | ||
5 | |||
6 | #include <linux/slab.h> | ||
7 | #include <linux/mm.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/spinlock.h> | ||
11 | |||
12 | #include <asm/hwrpb.h> | ||
13 | #include <asm/io.h> | ||
14 | #include <asm/segment.h> | ||
15 | |||
16 | #if 0 | ||
17 | # define DBG_DEVS(args) printk args | ||
18 | #else | ||
19 | # define DBG_DEVS(args) | ||
20 | #endif | ||
21 | |||
22 | #define KB 1024 | ||
23 | #define MB (1024*KB) | ||
24 | #define GB (1024*MB) | ||
25 | |||
26 | #define SMC_DEBUG 0 | ||
27 | |||
28 | /* File: smcc669_def.h | ||
29 | * | ||
30 | * Copyright (C) 1997 by | ||
31 | * Digital Equipment Corporation, Maynard, Massachusetts. | ||
32 | * All rights reserved. | ||
33 | * | ||
34 | * This software is furnished under a license and may be used and copied | ||
35 | * only in accordance of the terms of such license and with the | ||
36 | * inclusion of the above copyright notice. This software or any other | ||
37 | * copies thereof may not be provided or otherwise made available to any | ||
38 | * other person. No title to and ownership of the software is hereby | ||
39 | * transferred. | ||
40 | * | ||
41 | * The information in this software is subject to change without notice | ||
42 | * and should not be construed as a commitment by Digital Equipment | ||
43 | * Corporation. | ||
44 | * | ||
45 | * Digital assumes no responsibility for the use or reliability of its | ||
46 | * software on equipment which is not supplied by Digital. | ||
47 | * | ||
48 | * | ||
49 | * Abstract: | ||
50 | * | ||
51 | * This file contains header definitions for the SMC37c669 | ||
52 | * Super I/O controller. | ||
53 | * | ||
54 | * Author: | ||
55 | * | ||
56 | * Eric Rasmussen | ||
57 | * | ||
58 | * Modification History: | ||
59 | * | ||
60 | * er 28-Jan-1997 Initial Entry | ||
61 | */ | ||
62 | |||
63 | #ifndef __SMC37c669_H | ||
64 | #define __SMC37c669_H | ||
65 | |||
66 | /* | ||
67 | ** Macros for handling device IRQs | ||
68 | ** | ||
69 | ** The mask acts as a flag used in mapping actual ISA IRQs (0 - 15) | ||
70 | ** to device IRQs (A - H). | ||
71 | */ | ||
72 | #define SMC37c669_DEVICE_IRQ_MASK 0x80000000 | ||
73 | #define SMC37c669_DEVICE_IRQ( __i ) \ | ||
74 | ((SMC37c669_DEVICE_IRQ_MASK) | (__i)) | ||
75 | #define SMC37c669_IS_DEVICE_IRQ(__i) \ | ||
76 | (((__i) & (SMC37c669_DEVICE_IRQ_MASK)) == (SMC37c669_DEVICE_IRQ_MASK)) | ||
77 | #define SMC37c669_RAW_DEVICE_IRQ(__i) \ | ||
78 | ((__i) & ~(SMC37c669_DEVICE_IRQ_MASK)) | ||
79 | |||
80 | /* | ||
81 | ** Macros for handling device DRQs | ||
82 | ** | ||
83 | ** The mask acts as a flag used in mapping actual ISA DMA | ||
84 | ** channels to device DMA channels (A - C). | ||
85 | */ | ||
86 | #define SMC37c669_DEVICE_DRQ_MASK 0x80000000 | ||
87 | #define SMC37c669_DEVICE_DRQ(__d) \ | ||
88 | ((SMC37c669_DEVICE_DRQ_MASK) | (__d)) | ||
89 | #define SMC37c669_IS_DEVICE_DRQ(__d) \ | ||
90 | (((__d) & (SMC37c669_DEVICE_DRQ_MASK)) == (SMC37c669_DEVICE_DRQ_MASK)) | ||
91 | #define SMC37c669_RAW_DEVICE_DRQ(__d) \ | ||
92 | ((__d) & ~(SMC37c669_DEVICE_DRQ_MASK)) | ||
93 | |||
94 | #define SMC37c669_DEVICE_ID 0x3 | ||
95 | |||
96 | /* | ||
97 | ** SMC37c669 Device Function Definitions | ||
98 | */ | ||
99 | #define SERIAL_0 0 | ||
100 | #define SERIAL_1 1 | ||
101 | #define PARALLEL_0 2 | ||
102 | #define FLOPPY_0 3 | ||
103 | #define IDE_0 4 | ||
104 | #define NUM_FUNCS 5 | ||
105 | |||
106 | /* | ||
107 | ** Default Device Function Mappings | ||
108 | */ | ||
109 | #define COM1_BASE 0x3F8 | ||
110 | #define COM1_IRQ 4 | ||
111 | #define COM2_BASE 0x2F8 | ||
112 | #define COM2_IRQ 3 | ||
113 | #define PARP_BASE 0x3BC | ||
114 | #define PARP_IRQ 7 | ||
115 | #define PARP_DRQ 3 | ||
116 | #define FDC_BASE 0x3F0 | ||
117 | #define FDC_IRQ 6 | ||
118 | #define FDC_DRQ 2 | ||
119 | |||
120 | /* | ||
121 | ** Configuration On/Off Key Definitions | ||
122 | */ | ||
123 | #define SMC37c669_CONFIG_ON_KEY 0x55 | ||
124 | #define SMC37c669_CONFIG_OFF_KEY 0xAA | ||
125 | |||
126 | /* | ||
127 | ** SMC 37c669 Device IRQs | ||
128 | */ | ||
129 | #define SMC37c669_DEVICE_IRQ_A ( SMC37c669_DEVICE_IRQ( 0x01 ) ) | ||
130 | #define SMC37c669_DEVICE_IRQ_B ( SMC37c669_DEVICE_IRQ( 0x02 ) ) | ||
131 | #define SMC37c669_DEVICE_IRQ_C ( SMC37c669_DEVICE_IRQ( 0x03 ) ) | ||
132 | #define SMC37c669_DEVICE_IRQ_D ( SMC37c669_DEVICE_IRQ( 0x04 ) ) | ||
133 | #define SMC37c669_DEVICE_IRQ_E ( SMC37c669_DEVICE_IRQ( 0x05 ) ) | ||
134 | #define SMC37c669_DEVICE_IRQ_F ( SMC37c669_DEVICE_IRQ( 0x06 ) ) | ||
135 | /* SMC37c669_DEVICE_IRQ_G *** RESERVED ***/ | ||
136 | #define SMC37c669_DEVICE_IRQ_H ( SMC37c669_DEVICE_IRQ( 0x08 ) ) | ||
137 | |||
138 | /* | ||
139 | ** SMC 37c669 Device DMA Channel Definitions | ||
140 | */ | ||
141 | #define SMC37c669_DEVICE_DRQ_A ( SMC37c669_DEVICE_DRQ( 0x01 ) ) | ||
142 | #define SMC37c669_DEVICE_DRQ_B ( SMC37c669_DEVICE_DRQ( 0x02 ) ) | ||
143 | #define SMC37c669_DEVICE_DRQ_C ( SMC37c669_DEVICE_DRQ( 0x03 ) ) | ||
144 | |||
145 | /* | ||
146 | ** Configuration Register Index Definitions | ||
147 | */ | ||
148 | #define SMC37c669_CR00_INDEX 0x00 | ||
149 | #define SMC37c669_CR01_INDEX 0x01 | ||
150 | #define SMC37c669_CR02_INDEX 0x02 | ||
151 | #define SMC37c669_CR03_INDEX 0x03 | ||
152 | #define SMC37c669_CR04_INDEX 0x04 | ||
153 | #define SMC37c669_CR05_INDEX 0x05 | ||
154 | #define SMC37c669_CR06_INDEX 0x06 | ||
155 | #define SMC37c669_CR07_INDEX 0x07 | ||
156 | #define SMC37c669_CR08_INDEX 0x08 | ||
157 | #define SMC37c669_CR09_INDEX 0x09 | ||
158 | #define SMC37c669_CR0A_INDEX 0x0A | ||
159 | #define SMC37c669_CR0B_INDEX 0x0B | ||
160 | #define SMC37c669_CR0C_INDEX 0x0C | ||
161 | #define SMC37c669_CR0D_INDEX 0x0D | ||
162 | #define SMC37c669_CR0E_INDEX 0x0E | ||
163 | #define SMC37c669_CR0F_INDEX 0x0F | ||
164 | #define SMC37c669_CR10_INDEX 0x10 | ||
165 | #define SMC37c669_CR11_INDEX 0x11 | ||
166 | #define SMC37c669_CR12_INDEX 0x12 | ||
167 | #define SMC37c669_CR13_INDEX 0x13 | ||
168 | #define SMC37c669_CR14_INDEX 0x14 | ||
169 | #define SMC37c669_CR15_INDEX 0x15 | ||
170 | #define SMC37c669_CR16_INDEX 0x16 | ||
171 | #define SMC37c669_CR17_INDEX 0x17 | ||
172 | #define SMC37c669_CR18_INDEX 0x18 | ||
173 | #define SMC37c669_CR19_INDEX 0x19 | ||
174 | #define SMC37c669_CR1A_INDEX 0x1A | ||
175 | #define SMC37c669_CR1B_INDEX 0x1B | ||
176 | #define SMC37c669_CR1C_INDEX 0x1C | ||
177 | #define SMC37c669_CR1D_INDEX 0x1D | ||
178 | #define SMC37c669_CR1E_INDEX 0x1E | ||
179 | #define SMC37c669_CR1F_INDEX 0x1F | ||
180 | #define SMC37c669_CR20_INDEX 0x20 | ||
181 | #define SMC37c669_CR21_INDEX 0x21 | ||
182 | #define SMC37c669_CR22_INDEX 0x22 | ||
183 | #define SMC37c669_CR23_INDEX 0x23 | ||
184 | #define SMC37c669_CR24_INDEX 0x24 | ||
185 | #define SMC37c669_CR25_INDEX 0x25 | ||
186 | #define SMC37c669_CR26_INDEX 0x26 | ||
187 | #define SMC37c669_CR27_INDEX 0x27 | ||
188 | #define SMC37c669_CR28_INDEX 0x28 | ||
189 | #define SMC37c669_CR29_INDEX 0x29 | ||
190 | |||
191 | /* | ||
192 | ** Configuration Register Alias Definitions | ||
193 | */ | ||
194 | #define SMC37c669_DEVICE_ID_INDEX SMC37c669_CR0D_INDEX | ||
195 | #define SMC37c669_DEVICE_REVISION_INDEX SMC37c669_CR0E_INDEX | ||
196 | #define SMC37c669_FDC_BASE_ADDRESS_INDEX SMC37c669_CR20_INDEX | ||
197 | #define SMC37c669_IDE_BASE_ADDRESS_INDEX SMC37c669_CR21_INDEX | ||
198 | #define SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX SMC37c669_CR22_INDEX | ||
199 | #define SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX SMC37c669_CR23_INDEX | ||
200 | #define SMC37c669_SERIAL0_BASE_ADDRESS_INDEX SMC37c669_CR24_INDEX | ||
201 | #define SMC37c669_SERIAL1_BASE_ADDRESS_INDEX SMC37c669_CR25_INDEX | ||
202 | #define SMC37c669_PARALLEL_FDC_DRQ_INDEX SMC37c669_CR26_INDEX | ||
203 | #define SMC37c669_PARALLEL_FDC_IRQ_INDEX SMC37c669_CR27_INDEX | ||
204 | #define SMC37c669_SERIAL_IRQ_INDEX SMC37c669_CR28_INDEX | ||
205 | |||
206 | /* | ||
207 | ** Configuration Register Definitions | ||
208 | ** | ||
209 | ** The INDEX (write only) and DATA (read/write) ports are effective | ||
210 | ** only when the chip is in the Configuration State. | ||
211 | */ | ||
212 | typedef struct _SMC37c669_CONFIG_REGS { | ||
213 | unsigned char index_port; | ||
214 | unsigned char data_port; | ||
215 | } SMC37c669_CONFIG_REGS; | ||
216 | |||
217 | /* | ||
218 | ** CR00 - default value 0x28 | ||
219 | ** | ||
220 | ** IDE_EN (CR00<1:0>): | ||
221 | ** 0x - 30ua pull-ups on nIDEEN, nHDCS0, NHDCS1 | ||
222 | ** 11 - IRQ_H available as IRQ output, | ||
223 | ** IRRX2, IRTX2 available as alternate IR pins | ||
224 | ** 10 - nIDEEN, nHDCS0, nHDCS1 used to control IDE | ||
225 | ** | ||
226 | ** VALID (CR00<7>): | ||
227 | ** A high level on this software controlled bit can | ||
228 | ** be used to indicate that a valid configuration | ||
229 | ** cycle has occurred. The control software must | ||
230 | ** take care to set this bit at the appropriate times. | ||
231 | ** Set to zero after power up. This bit has no | ||
232 | ** effect on any other hardware in the chip. | ||
233 | ** | ||
234 | */ | ||
235 | typedef union _SMC37c669_CR00 { | ||
236 | unsigned char as_uchar; | ||
237 | struct { | ||
238 | unsigned ide_en : 2; /* See note above */ | ||
239 | unsigned reserved1 : 1; /* RAZ */ | ||
240 | unsigned fdc_pwr : 1; /* 1 = supply power to FDC */ | ||
241 | unsigned reserved2 : 3; /* Read as 010b */ | ||
242 | unsigned valid : 1; /* See note above */ | ||
243 | } by_field; | ||
244 | } SMC37c669_CR00; | ||
245 | |||
246 | /* | ||
247 | ** CR01 - default value 0x9C | ||
248 | */ | ||
249 | typedef union _SMC37c669_CR01 { | ||
250 | unsigned char as_uchar; | ||
251 | struct { | ||
252 | unsigned reserved1 : 2; /* RAZ */ | ||
253 | unsigned ppt_pwr : 1; /* 1 = supply power to PPT */ | ||
254 | unsigned ppt_mode : 1; /* 1 = Printer mode, 0 = EPP */ | ||
255 | unsigned reserved2 : 1; /* Read as 1 */ | ||
256 | unsigned reserved3 : 2; /* RAZ */ | ||
257 | unsigned lock_crx: 1; /* Lock CR00 - CR18 */ | ||
258 | } by_field; | ||
259 | } SMC37c669_CR01; | ||
260 | |||
261 | /* | ||
262 | ** CR02 - default value 0x88 | ||
263 | */ | ||
264 | typedef union _SMC37c669_CR02 { | ||
265 | unsigned char as_uchar; | ||
266 | struct { | ||
267 | unsigned reserved1 : 3; /* RAZ */ | ||
268 | unsigned uart1_pwr : 1; /* 1 = supply power to UART1 */ | ||
269 | unsigned reserved2 : 3; /* RAZ */ | ||
270 | unsigned uart2_pwr : 1; /* 1 = supply power to UART2 */ | ||
271 | } by_field; | ||
272 | } SMC37c669_CR02; | ||
273 | |||
274 | /* | ||
275 | ** CR03 - default value 0x78 | ||
276 | ** | ||
277 | ** CR03<7> CR03<2> Pin 94 | ||
278 | ** ------- ------- ------ | ||
279 | ** 0 X DRV2 (input) | ||
280 | ** 1 0 ADRX | ||
281 | ** 1 1 IRQ_B | ||
282 | ** | ||
283 | ** CR03<6> CR03<5> Op Mode | ||
284 | ** ------- ------- ------- | ||
285 | ** 0 0 Model 30 | ||
286 | ** 0 1 PS/2 | ||
287 | ** 1 0 Reserved | ||
288 | ** 1 1 AT Mode | ||
289 | */ | ||
290 | typedef union _SMC37c669_CR03 { | ||
291 | unsigned char as_uchar; | ||
292 | struct { | ||
293 | unsigned pwrgd_gamecs : 1; /* 1 = PWRGD, 0 = GAMECS */ | ||
294 | unsigned fdc_mode2 : 1; /* 1 = Enhanced Mode 2 */ | ||
295 | unsigned pin94_0 : 1; /* See note above */ | ||
296 | unsigned reserved1 : 1; /* RAZ */ | ||
297 | unsigned drvden : 1; /* 1 = high, 0 - output */ | ||
298 | unsigned op_mode : 2; /* See note above */ | ||
299 | unsigned pin94_1 : 1; /* See note above */ | ||
300 | } by_field; | ||
301 | } SMC37c669_CR03; | ||
302 | |||
303 | /* | ||
304 | ** CR04 - default value 0x00 | ||
305 | ** | ||
306 | ** PP_EXT_MODE: | ||
307 | ** If CR01<PP_MODE> = 0 and PP_EXT_MODE = | ||
308 | ** 00 - Standard and Bidirectional | ||
309 | ** 01 - EPP mode and SPP | ||
310 | ** 10 - ECP mode | ||
311 | ** In this mode, 2 drives can be supported | ||
312 | ** directly, 3 or 4 drives must use external | ||
313 | ** 4 drive support. SPP can be selected | ||
314 | ** through the ECR register of ECP as mode 000. | ||
315 | ** 11 - ECP mode and EPP mode | ||
316 | ** In this mode, 2 drives can be supported | ||
317 | ** directly, 3 or 4 drives must use external | ||
318 | ** 4 drive support. SPP can be selected | ||
319 | ** through the ECR register of ECP as mode 000. | ||
320 | ** In this mode, EPP can be selected through | ||
321 | ** the ECR register of ECP as mode 100. | ||
322 | ** | ||
323 | ** PP_FDC: | ||
324 | ** 00 - Normal | ||
325 | ** 01 - PPFD1 | ||
326 | ** 10 - PPFD2 | ||
327 | ** 11 - Reserved | ||
328 | ** | ||
329 | ** MIDI1: | ||
330 | ** Serial Clock Select: | ||
331 | ** A low level on this bit disables MIDI support, | ||
332 | ** clock = divide by 13. A high level on this | ||
333 | ** bit enables MIDI support, clock = divide by 12. | ||
334 | ** | ||
335 | ** MIDI operates at 31.25 Kbps which can be derived | ||
336 | ** from 125 KHz (24 MHz / 12 = 2 MHz, 2 MHz / 16 = 125 KHz) | ||
337 | ** | ||
338 | ** ALT_IO: | ||
339 | ** 0 - Use pins IRRX, IRTX | ||
340 | ** 1 - Use pins IRRX2, IRTX2 | ||
341 | ** | ||
342 | ** If this bit is set, the IR receive and transmit | ||
343 | ** functions will not be available on pins 25 and 26 | ||
344 | ** unless CR00<IDE_EN> = 11. | ||
345 | */ | ||
346 | typedef union _SMC37c669_CR04 { | ||
347 | unsigned char as_uchar; | ||
348 | struct { | ||
349 | unsigned ppt_ext_mode : 2; /* See note above */ | ||
350 | unsigned ppt_fdc : 2; /* See note above */ | ||
351 | unsigned midi1 : 1; /* See note above */ | ||
352 | unsigned midi2 : 1; /* See note above */ | ||
353 | unsigned epp_type : 1; /* 0 = EPP 1.9, 1 = EPP 1.7 */ | ||
354 | unsigned alt_io : 1; /* See note above */ | ||
355 | } by_field; | ||
356 | } SMC37c669_CR04; | ||
357 | |||
358 | /* | ||
359 | ** CR05 - default value 0x00 | ||
360 | ** | ||
361 | ** DEN_SEL: | ||
362 | ** 00 - Densel output normal | ||
363 | ** 01 - Reserved | ||
364 | ** 10 - Densel output 1 | ||
365 | ** 11 - Densel output 0 | ||
366 | ** | ||
367 | */ | ||
368 | typedef union _SMC37c669_CR05 { | ||
369 | unsigned char as_uchar; | ||
370 | struct { | ||
371 | unsigned reserved1 : 2; /* RAZ */ | ||
372 | unsigned fdc_dma_mode : 1; /* 0 = burst, 1 = non-burst */ | ||
373 | unsigned den_sel : 2; /* See note above */ | ||
374 | unsigned swap_drv : 1; /* Swap the FDC motor selects */ | ||
375 | unsigned extx4 : 1; /* 0 = 2 drive, 1 = external 4 drive decode */ | ||
376 | unsigned reserved2 : 1; /* RAZ */ | ||
377 | } by_field; | ||
378 | } SMC37c669_CR05; | ||
379 | |||
380 | /* | ||
381 | ** CR06 - default value 0xFF | ||
382 | */ | ||
383 | typedef union _SMC37c669_CR06 { | ||
384 | unsigned char as_uchar; | ||
385 | struct { | ||
386 | unsigned floppy_a : 2; /* Type of floppy drive A */ | ||
387 | unsigned floppy_b : 2; /* Type of floppy drive B */ | ||
388 | unsigned floppy_c : 2; /* Type of floppy drive C */ | ||
389 | unsigned floppy_d : 2; /* Type of floppy drive D */ | ||
390 | } by_field; | ||
391 | } SMC37c669_CR06; | ||
392 | |||
393 | /* | ||
394 | ** CR07 - default value 0x00 | ||
395 | ** | ||
396 | ** Auto Power Management CR07<7:4>: | ||
397 | ** 0 - Auto Powerdown disabled (default) | ||
398 | ** 1 - Auto Powerdown enabled | ||
399 | ** | ||
400 | ** This bit is reset to the default state by POR or | ||
401 | ** a hardware reset. | ||
402 | ** | ||
403 | */ | ||
404 | typedef union _SMC37c669_CR07 { | ||
405 | unsigned char as_uchar; | ||
406 | struct { | ||
407 | unsigned floppy_boot : 2; /* 0 = A:, 1 = B: */ | ||
408 | unsigned reserved1 : 2; /* RAZ */ | ||
409 | unsigned ppt_en : 1; /* See note above */ | ||
410 | unsigned uart1_en : 1; /* See note above */ | ||
411 | unsigned uart2_en : 1; /* See note above */ | ||
412 | unsigned fdc_en : 1; /* See note above */ | ||
413 | } by_field; | ||
414 | } SMC37c669_CR07; | ||
415 | |||
416 | /* | ||
417 | ** CR08 - default value 0x00 | ||
418 | */ | ||
419 | typedef union _SMC37c669_CR08 { | ||
420 | unsigned char as_uchar; | ||
421 | struct { | ||
422 | unsigned zero : 4; /* 0 */ | ||
423 | unsigned addrx7_4 : 4; /* ADR<7:3> for ADRx decode */ | ||
424 | } by_field; | ||
425 | } SMC37c669_CR08; | ||
426 | |||
427 | /* | ||
428 | ** CR09 - default value 0x00 | ||
429 | ** | ||
430 | ** ADRx_CONFIG: | ||
431 | ** 00 - ADRx disabled | ||
432 | ** 01 - 1 byte decode A<3:0> = 0000b | ||
433 | ** 10 - 8 byte block decode A<3:0> = 0XXXb | ||
434 | ** 11 - 16 byte block decode A<3:0> = XXXXb | ||
435 | ** | ||
436 | */ | ||
437 | typedef union _SMC37c669_CR09 { | ||
438 | unsigned char as_uchar; | ||
439 | struct { | ||
440 | unsigned adra8 : 3; /* ADR<10:8> for ADRx decode */ | ||
441 | unsigned reserved1 : 3; | ||
442 | unsigned adrx_config : 2; /* See note above */ | ||
443 | } by_field; | ||
444 | } SMC37c669_CR09; | ||
445 | |||
446 | /* | ||
447 | ** CR0A - default value 0x00 | ||
448 | */ | ||
449 | typedef union _SMC37c669_CR0A { | ||
450 | unsigned char as_uchar; | ||
451 | struct { | ||
452 | unsigned ecp_fifo_threshold : 4; | ||
453 | unsigned reserved1 : 4; | ||
454 | } by_field; | ||
455 | } SMC37c669_CR0A; | ||
456 | |||
457 | /* | ||
458 | ** CR0B - default value 0x00 | ||
459 | */ | ||
460 | typedef union _SMC37c669_CR0B { | ||
461 | unsigned char as_uchar; | ||
462 | struct { | ||
463 | unsigned fdd0_drtx : 2; /* FDD0 Data Rate Table */ | ||
464 | unsigned fdd1_drtx : 2; /* FDD1 Data Rate Table */ | ||
465 | unsigned fdd2_drtx : 2; /* FDD2 Data Rate Table */ | ||
466 | unsigned fdd3_drtx : 2; /* FDD3 Data Rate Table */ | ||
467 | } by_field; | ||
468 | } SMC37c669_CR0B; | ||
469 | |||
470 | /* | ||
471 | ** CR0C - default value 0x00 | ||
472 | ** | ||
473 | ** UART2_MODE: | ||
474 | ** 000 - Standard (default) | ||
475 | ** 001 - IrDA (HPSIR) | ||
476 | ** 010 - Amplitude Shift Keyed IR @500 KHz | ||
477 | ** 011 - Reserved | ||
478 | ** 1xx - Reserved | ||
479 | ** | ||
480 | */ | ||
481 | typedef union _SMC37c669_CR0C { | ||
482 | unsigned char as_uchar; | ||
483 | struct { | ||
484 | unsigned uart2_rcv_polarity : 1; /* 1 = invert RX */ | ||
485 | unsigned uart2_xmit_polarity : 1; /* 1 = invert TX */ | ||
486 | unsigned uart2_duplex : 1; /* 1 = full, 0 = half */ | ||
487 | unsigned uart2_mode : 3; /* See note above */ | ||
488 | unsigned uart1_speed : 1; /* 1 = high speed enabled */ | ||
489 | unsigned uart2_speed : 1; /* 1 = high speed enabled */ | ||
490 | } by_field; | ||
491 | } SMC37c669_CR0C; | ||
492 | |||
493 | /* | ||
494 | ** CR0D - default value 0x03 | ||
495 | ** | ||
496 | ** Device ID Register - read only | ||
497 | */ | ||
498 | typedef union _SMC37c669_CR0D { | ||
499 | unsigned char as_uchar; | ||
500 | struct { | ||
501 | unsigned device_id : 8; /* Returns 0x3 in this field */ | ||
502 | } by_field; | ||
503 | } SMC37c669_CR0D; | ||
504 | |||
505 | /* | ||
506 | ** CR0E - default value 0x02 | ||
507 | ** | ||
508 | ** Device Revision Register - read only | ||
509 | */ | ||
510 | typedef union _SMC37c669_CR0E { | ||
511 | unsigned char as_uchar; | ||
512 | struct { | ||
513 | unsigned device_rev : 8; /* Returns 0x2 in this field */ | ||
514 | } by_field; | ||
515 | } SMC37c669_CR0E; | ||
516 | |||
517 | /* | ||
518 | ** CR0F - default value 0x00 | ||
519 | */ | ||
520 | typedef union _SMC37c669_CR0F { | ||
521 | unsigned char as_uchar; | ||
522 | struct { | ||
523 | unsigned test0 : 1; /* Reserved - set to 0 */ | ||
524 | unsigned test1 : 1; /* Reserved - set to 0 */ | ||
525 | unsigned test2 : 1; /* Reserved - set to 0 */ | ||
526 | unsigned test3 : 1; /* Reserved - set t0 0 */ | ||
527 | unsigned test4 : 1; /* Reserved - set to 0 */ | ||
528 | unsigned test5 : 1; /* Reserved - set t0 0 */ | ||
529 | unsigned test6 : 1; /* Reserved - set t0 0 */ | ||
530 | unsigned test7 : 1; /* Reserved - set to 0 */ | ||
531 | } by_field; | ||
532 | } SMC37c669_CR0F; | ||
533 | |||
534 | /* | ||
535 | ** CR10 - default value 0x00 | ||
536 | */ | ||
537 | typedef union _SMC37c669_CR10 { | ||
538 | unsigned char as_uchar; | ||
539 | struct { | ||
540 | unsigned reserved1 : 3; /* RAZ */ | ||
541 | unsigned pll_gain : 1; /* 1 = 3V, 2 = 5V operation */ | ||
542 | unsigned pll_stop : 1; /* 1 = stop PLLs */ | ||
543 | unsigned ace_stop : 1; /* 1 = stop UART clocks */ | ||
544 | unsigned pll_clock_ctrl : 1; /* 0 = 14.318 MHz, 1 = 24 MHz */ | ||
545 | unsigned ir_test : 1; /* Enable IR test mode */ | ||
546 | } by_field; | ||
547 | } SMC37c669_CR10; | ||
548 | |||
549 | /* | ||
550 | ** CR11 - default value 0x00 | ||
551 | */ | ||
552 | typedef union _SMC37c669_CR11 { | ||
553 | unsigned char as_uchar; | ||
554 | struct { | ||
555 | unsigned ir_loopback : 1; /* Internal IR loop back */ | ||
556 | unsigned test_10ms : 1; /* Test 10ms autopowerdown FDC timeout */ | ||
557 | unsigned reserved1 : 6; /* RAZ */ | ||
558 | } by_field; | ||
559 | } SMC37c669_CR11; | ||
560 | |||
561 | /* | ||
562 | ** CR12 - CR1D are reserved registers | ||
563 | */ | ||
564 | |||
565 | /* | ||
566 | ** CR1E - default value 0x80 | ||
567 | ** | ||
568 | ** GAMECS: | ||
569 | ** 00 - GAMECS disabled | ||
570 | ** 01 - 1 byte decode ADR<3:0> = 0001b | ||
571 | ** 10 - 8 byte block decode ADR<3:0> = 0XXXb | ||
572 | ** 11 - 16 byte block decode ADR<3:0> = XXXXb | ||
573 | ** | ||
574 | */ | ||
575 | typedef union _SMC37c66_CR1E { | ||
576 | unsigned char as_uchar; | ||
577 | struct { | ||
578 | unsigned gamecs_config: 2; /* See note above */ | ||
579 | unsigned gamecs_addr9_4 : 6; /* GAMECS Addr<9:4> */ | ||
580 | } by_field; | ||
581 | } SMC37c669_CR1E; | ||
582 | |||
583 | /* | ||
584 | ** CR1F - default value 0x00 | ||
585 | ** | ||
586 | ** DT0 DT1 DRVDEN0 DRVDEN1 Drive Type | ||
587 | ** --- --- ------- ------- ---------- | ||
588 | ** 0 0 DENSEL DRATE0 4/2/1 MB 3.5" | ||
589 | ** 2/1 MB 5.25" | ||
590 | ** 2/1.6/1 MB 3.5" (3-mode) | ||
591 | ** 0 1 DRATE1 DRATE0 | ||
592 | ** 1 0 nDENSEL DRATE0 PS/2 | ||
593 | ** 1 1 DRATE0 DRATE1 | ||
594 | ** | ||
595 | ** Note: DENSEL, DRATE1, and DRATE0 map onto two output | ||
596 | ** pins - DRVDEN0 and DRVDEN1. | ||
597 | ** | ||
598 | */ | ||
599 | typedef union _SMC37c669_CR1F { | ||
600 | unsigned char as_uchar; | ||
601 | struct { | ||
602 | unsigned fdd0_drive_type : 2; /* FDD0 drive type */ | ||
603 | unsigned fdd1_drive_type : 2; /* FDD1 drive type */ | ||
604 | unsigned fdd2_drive_type : 2; /* FDD2 drive type */ | ||
605 | unsigned fdd3_drive_type : 2; /* FDD3 drive type */ | ||
606 | } by_field; | ||
607 | } SMC37c669_CR1F; | ||
608 | |||
609 | /* | ||
610 | ** CR20 - default value 0x3C | ||
611 | ** | ||
612 | ** FDC Base Address Register | ||
613 | ** - To disable this decode set Addr<9:8> = 0 | ||
614 | ** - A<10> = 0, A<3:0> = 0XXXb to access. | ||
615 | ** | ||
616 | */ | ||
617 | typedef union _SMC37c669_CR20 { | ||
618 | unsigned char as_uchar; | ||
619 | struct { | ||
620 | unsigned zero : 2; /* 0 */ | ||
621 | unsigned addr9_4 : 6; /* FDC Addr<9:4> */ | ||
622 | } by_field; | ||
623 | } SMC37c669_CR20; | ||
624 | |||
625 | /* | ||
626 | ** CR21 - default value 0x3C | ||
627 | ** | ||
628 | ** IDE Base Address Register | ||
629 | ** - To disable this decode set Addr<9:8> = 0 | ||
630 | ** - A<10> = 0, A<3:0> = 0XXXb to access. | ||
631 | ** | ||
632 | */ | ||
633 | typedef union _SMC37c669_CR21 { | ||
634 | unsigned char as_uchar; | ||
635 | struct { | ||
636 | unsigned zero : 2; /* 0 */ | ||
637 | unsigned addr9_4 : 6; /* IDE Addr<9:4> */ | ||
638 | } by_field; | ||
639 | } SMC37c669_CR21; | ||
640 | |||
641 | /* | ||
642 | ** CR22 - default value 0x3D | ||
643 | ** | ||
644 | ** IDE Alternate Status Base Address Register | ||
645 | ** - To disable this decode set Addr<9:8> = 0 | ||
646 | ** - A<10> = 0, A<3:0> = 0110b to access. | ||
647 | ** | ||
648 | */ | ||
649 | typedef union _SMC37c669_CR22 { | ||
650 | unsigned char as_uchar; | ||
651 | struct { | ||
652 | unsigned zero : 2; /* 0 */ | ||
653 | unsigned addr9_4 : 6; /* IDE Alt Status Addr<9:4> */ | ||
654 | } by_field; | ||
655 | } SMC37c669_CR22; | ||
656 | |||
657 | /* | ||
658 | ** CR23 - default value 0x00 | ||
659 | ** | ||
660 | ** Parallel Port Base Address Register | ||
661 | ** - To disable this decode set Addr<9:8> = 0 | ||
662 | ** - A<10> = 0 to access. | ||
663 | ** - If EPP is enabled, A<2:0> = XXXb to access. | ||
664 | ** If EPP is NOT enabled, A<1:0> = XXb to access | ||
665 | ** | ||
666 | */ | ||
667 | typedef union _SMC37c669_CR23 { | ||
668 | unsigned char as_uchar; | ||
669 | struct { | ||
670 | unsigned addr9_2 : 8; /* Parallel Port Addr<9:2> */ | ||
671 | } by_field; | ||
672 | } SMC37c669_CR23; | ||
673 | |||
674 | /* | ||
675 | ** CR24 - default value 0x00 | ||
676 | ** | ||
677 | ** UART1 Base Address Register | ||
678 | ** - To disable this decode set Addr<9:8> = 0 | ||
679 | ** - A<10> = 0, A<2:0> = XXXb to access. | ||
680 | ** | ||
681 | */ | ||
682 | typedef union _SMC37c669_CR24 { | ||
683 | unsigned char as_uchar; | ||
684 | struct { | ||
685 | unsigned zero : 1; /* 0 */ | ||
686 | unsigned addr9_3 : 7; /* UART1 Addr<9:3> */ | ||
687 | } by_field; | ||
688 | } SMC37c669_CR24; | ||
689 | |||
690 | /* | ||
691 | ** CR25 - default value 0x00 | ||
692 | ** | ||
693 | ** UART2 Base Address Register | ||
694 | ** - To disable this decode set Addr<9:8> = 0 | ||
695 | ** - A<10> = 0, A<2:0> = XXXb to access. | ||
696 | ** | ||
697 | */ | ||
698 | typedef union _SMC37c669_CR25 { | ||
699 | unsigned char as_uchar; | ||
700 | struct { | ||
701 | unsigned zero : 1; /* 0 */ | ||
702 | unsigned addr9_3 : 7; /* UART2 Addr<9:3> */ | ||
703 | } by_field; | ||
704 | } SMC37c669_CR25; | ||
705 | |||
706 | /* | ||
707 | ** CR26 - default value 0x00 | ||
708 | ** | ||
709 | ** Parallel Port / FDC DMA Select Register | ||
710 | ** | ||
711 | ** D3 - D0 DMA | ||
712 | ** D7 - D4 Selected | ||
713 | ** ------- -------- | ||
714 | ** 0000 None | ||
715 | ** 0001 DMA_A | ||
716 | ** 0010 DMA_B | ||
717 | ** 0011 DMA_C | ||
718 | ** | ||
719 | */ | ||
720 | typedef union _SMC37c669_CR26 { | ||
721 | unsigned char as_uchar; | ||
722 | struct { | ||
723 | unsigned ppt_drq : 4; /* See note above */ | ||
724 | unsigned fdc_drq : 4; /* See note above */ | ||
725 | } by_field; | ||
726 | } SMC37c669_CR26; | ||
727 | |||
728 | /* | ||
729 | ** CR27 - default value 0x00 | ||
730 | ** | ||
731 | ** Parallel Port / FDC IRQ Select Register | ||
732 | ** | ||
733 | ** D3 - D0 IRQ | ||
734 | ** D7 - D4 Selected | ||
735 | ** ------- -------- | ||
736 | ** 0000 None | ||
737 | ** 0001 IRQ_A | ||
738 | ** 0010 IRQ_B | ||
739 | ** 0011 IRQ_C | ||
740 | ** 0100 IRQ_D | ||
741 | ** 0101 IRQ_E | ||
742 | ** 0110 IRQ_F | ||
743 | ** 0111 Reserved | ||
744 | ** 1000 IRQ_H | ||
745 | ** | ||
746 | ** Any unselected IRQ REQ is in tristate | ||
747 | ** | ||
748 | */ | ||
749 | typedef union _SMC37c669_CR27 { | ||
750 | unsigned char as_uchar; | ||
751 | struct { | ||
752 | unsigned ppt_irq : 4; /* See note above */ | ||
753 | unsigned fdc_irq : 4; /* See note above */ | ||
754 | } by_field; | ||
755 | } SMC37c669_CR27; | ||
756 | |||
757 | /* | ||
758 | ** CR28 - default value 0x00 | ||
759 | ** | ||
760 | ** UART IRQ Select Register | ||
761 | ** | ||
762 | ** D3 - D0 IRQ | ||
763 | ** D7 - D4 Selected | ||
764 | ** ------- -------- | ||
765 | ** 0000 None | ||
766 | ** 0001 IRQ_A | ||
767 | ** 0010 IRQ_B | ||
768 | ** 0011 IRQ_C | ||
769 | ** 0100 IRQ_D | ||
770 | ** 0101 IRQ_E | ||
771 | ** 0110 IRQ_F | ||
772 | ** 0111 Reserved | ||
773 | ** 1000 IRQ_H | ||
774 | ** 1111 share with UART1 (only for UART2) | ||
775 | ** | ||
776 | ** Any unselected IRQ REQ is in tristate | ||
777 | ** | ||
778 | ** To share an IRQ between UART1 and UART2, set | ||
779 | ** UART1 to use the desired IRQ and set UART2 to | ||
780 | ** 0xF to enable sharing mechanism. | ||
781 | ** | ||
782 | */ | ||
783 | typedef union _SMC37c669_CR28 { | ||
784 | unsigned char as_uchar; | ||
785 | struct { | ||
786 | unsigned uart2_irq : 4; /* See note above */ | ||
787 | unsigned uart1_irq : 4; /* See note above */ | ||
788 | } by_field; | ||
789 | } SMC37c669_CR28; | ||
790 | |||
791 | /* | ||
792 | ** CR29 - default value 0x00 | ||
793 | ** | ||
794 | ** IRQIN IRQ Select Register | ||
795 | ** | ||
796 | ** D3 - D0 IRQ | ||
797 | ** D7 - D4 Selected | ||
798 | ** ------- -------- | ||
799 | ** 0000 None | ||
800 | ** 0001 IRQ_A | ||
801 | ** 0010 IRQ_B | ||
802 | ** 0011 IRQ_C | ||
803 | ** 0100 IRQ_D | ||
804 | ** 0101 IRQ_E | ||
805 | ** 0110 IRQ_F | ||
806 | ** 0111 Reserved | ||
807 | ** 1000 IRQ_H | ||
808 | ** | ||
809 | ** Any unselected IRQ REQ is in tristate | ||
810 | ** | ||
811 | */ | ||
812 | typedef union _SMC37c669_CR29 { | ||
813 | unsigned char as_uchar; | ||
814 | struct { | ||
815 | unsigned irqin_irq : 4; /* See note above */ | ||
816 | unsigned reserved1 : 4; /* RAZ */ | ||
817 | } by_field; | ||
818 | } SMC37c669_CR29; | ||
819 | |||
820 | /* | ||
821 | ** Aliases of Configuration Register formats (should match | ||
822 | ** the set of index aliases). | ||
823 | ** | ||
824 | ** Note that CR24 and CR25 have the same format and are the | ||
825 | ** base address registers for UART1 and UART2. Because of | ||
826 | ** this we only define 1 alias here - for CR24 - as the serial | ||
827 | ** base address register. | ||
828 | ** | ||
829 | ** Note that CR21 and CR22 have the same format and are the | ||
830 | ** base address and alternate status address registers for | ||
831 | ** the IDE controller. Because of this we only define 1 alias | ||
832 | ** here - for CR21 - as the IDE address register. | ||
833 | ** | ||
834 | */ | ||
835 | typedef SMC37c669_CR0D SMC37c669_DEVICE_ID_REGISTER; | ||
836 | typedef SMC37c669_CR0E SMC37c669_DEVICE_REVISION_REGISTER; | ||
837 | typedef SMC37c669_CR20 SMC37c669_FDC_BASE_ADDRESS_REGISTER; | ||
838 | typedef SMC37c669_CR21 SMC37c669_IDE_ADDRESS_REGISTER; | ||
839 | typedef SMC37c669_CR23 SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER; | ||
840 | typedef SMC37c669_CR24 SMC37c669_SERIAL_BASE_ADDRESS_REGISTER; | ||
841 | typedef SMC37c669_CR26 SMC37c669_PARALLEL_FDC_DRQ_REGISTER; | ||
842 | typedef SMC37c669_CR27 SMC37c669_PARALLEL_FDC_IRQ_REGISTER; | ||
843 | typedef SMC37c669_CR28 SMC37c669_SERIAL_IRQ_REGISTER; | ||
844 | |||
845 | /* | ||
846 | ** ISA/Device IRQ Translation Table Entry Definition | ||
847 | */ | ||
848 | typedef struct _SMC37c669_IRQ_TRANSLATION_ENTRY { | ||
849 | int device_irq; | ||
850 | int isa_irq; | ||
851 | } SMC37c669_IRQ_TRANSLATION_ENTRY; | ||
852 | |||
853 | /* | ||
854 | ** ISA/Device DMA Translation Table Entry Definition | ||
855 | */ | ||
856 | typedef struct _SMC37c669_DRQ_TRANSLATION_ENTRY { | ||
857 | int device_drq; | ||
858 | int isa_drq; | ||
859 | } SMC37c669_DRQ_TRANSLATION_ENTRY; | ||
860 | |||
861 | /* | ||
862 | ** External Interface Function Prototype Declarations | ||
863 | */ | ||
864 | |||
865 | SMC37c669_CONFIG_REGS *SMC37c669_detect( | ||
866 | int | ||
867 | ); | ||
868 | |||
869 | unsigned int SMC37c669_enable_device( | ||
870 | unsigned int func | ||
871 | ); | ||
872 | |||
873 | unsigned int SMC37c669_disable_device( | ||
874 | unsigned int func | ||
875 | ); | ||
876 | |||
877 | unsigned int SMC37c669_configure_device( | ||
878 | unsigned int func, | ||
879 | int port, | ||
880 | int irq, | ||
881 | int drq | ||
882 | ); | ||
883 | |||
884 | void SMC37c669_display_device_info( | ||
885 | void | ||
886 | ); | ||
887 | |||
888 | #endif /* __SMC37c669_H */ | ||
889 | |||
890 | /* file: smcc669.c | ||
891 | * | ||
892 | * Copyright (C) 1997 by | ||
893 | * Digital Equipment Corporation, Maynard, Massachusetts. | ||
894 | * All rights reserved. | ||
895 | * | ||
896 | * This software is furnished under a license and may be used and copied | ||
897 | * only in accordance of the terms of such license and with the | ||
898 | * inclusion of the above copyright notice. This software or any other | ||
899 | * copies thereof may not be provided or otherwise made available to any | ||
900 | * other person. No title to and ownership of the software is hereby | ||
901 | * transferred. | ||
902 | * | ||
903 | * The information in this software is subject to change without notice | ||
904 | * and should not be construed as a commitment by digital equipment | ||
905 | * corporation. | ||
906 | * | ||
907 | * Digital assumes no responsibility for the use or reliability of its | ||
908 | * software on equipment which is not supplied by digital. | ||
909 | */ | ||
910 | |||
911 | /* | ||
912 | *++ | ||
913 | * FACILITY: | ||
914 | * | ||
915 | * Alpha SRM Console Firmware | ||
916 | * | ||
917 | * MODULE DESCRIPTION: | ||
918 | * | ||
919 | * SMC37c669 Super I/O controller configuration routines. | ||
920 | * | ||
921 | * AUTHORS: | ||
922 | * | ||
923 | * Eric Rasmussen | ||
924 | * | ||
925 | * CREATION DATE: | ||
926 | * | ||
927 | * 28-Jan-1997 | ||
928 | * | ||
929 | * MODIFICATION HISTORY: | ||
930 | * | ||
931 | * er 01-May-1997 Fixed pointer conversion errors in | ||
932 | * SMC37c669_get_device_config(). | ||
933 | * er 28-Jan-1997 Initial version. | ||
934 | * | ||
935 | *-- | ||
936 | */ | ||
937 | #if 0 | ||
938 | /* $INCLUDE_OPTIONS$ */ | ||
939 | #include "cp$inc:platform_io.h" | ||
940 | /* $INCLUDE_OPTIONS_END$ */ | ||
941 | #include "cp$src:common.h" | ||
942 | #include "cp$inc:prototypes.h" | ||
943 | #include "cp$src:kernel_def.h" | ||
944 | #include "cp$src:msg_def.h" | ||
945 | #include "cp$src:smcc669_def.h" | ||
946 | /* Platform-specific includes */ | ||
947 | #include "cp$src:platform.h" | ||
948 | #endif | ||
949 | |||
950 | #ifndef TRUE | ||
951 | #define TRUE 1 | ||
952 | #endif | ||
953 | #ifndef FALSE | ||
954 | #define FALSE 0 | ||
955 | #endif | ||
956 | |||
957 | #define wb( _x_, _y_ ) outb( _y_, (unsigned int)((unsigned long)_x_) ) | ||
958 | #define rb( _x_ ) inb( (unsigned int)((unsigned long)_x_) ) | ||
959 | |||
960 | /* | ||
961 | ** Local storage for device configuration information. | ||
962 | ** | ||
963 | ** Since the SMC37c669 does not provide an explicit | ||
964 | ** mechanism for enabling/disabling individual device | ||
965 | ** functions, other than unmapping the device, local | ||
966 | ** storage for device configuration information is | ||
967 | ** allocated here for use in implementing our own | ||
968 | ** function enable/disable scheme. | ||
969 | */ | ||
970 | static struct DEVICE_CONFIG { | ||
971 | unsigned int port1; | ||
972 | unsigned int port2; | ||
973 | int irq; | ||
974 | int drq; | ||
975 | } local_config [NUM_FUNCS]; | ||
976 | |||
977 | /* | ||
978 | ** List of all possible addresses for the Super I/O chip | ||
979 | */ | ||
980 | static unsigned long SMC37c669_Addresses[] __initdata = | ||
981 | { | ||
982 | 0x3F0UL, /* Primary address */ | ||
983 | 0x370UL, /* Secondary address */ | ||
984 | 0UL /* End of list */ | ||
985 | }; | ||
986 | |||
987 | /* | ||
988 | ** Global Pointer to the Super I/O device | ||
989 | */ | ||
990 | static SMC37c669_CONFIG_REGS *SMC37c669 __initdata = NULL; | ||
991 | |||
992 | /* | ||
993 | ** IRQ Translation Table | ||
994 | ** | ||
995 | ** The IRQ translation table is a list of SMC37c669 device | ||
996 | ** and standard ISA IRQs. | ||
997 | ** | ||
998 | */ | ||
999 | static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata; | ||
1000 | |||
1001 | /* | ||
1002 | ** The following definition is for the default IRQ | ||
1003 | ** translation table. | ||
1004 | */ | ||
1005 | static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_default_irq_table[] | ||
1006 | __initdata = | ||
1007 | { | ||
1008 | { SMC37c669_DEVICE_IRQ_A, -1 }, | ||
1009 | { SMC37c669_DEVICE_IRQ_B, -1 }, | ||
1010 | { SMC37c669_DEVICE_IRQ_C, 7 }, | ||
1011 | { SMC37c669_DEVICE_IRQ_D, 6 }, | ||
1012 | { SMC37c669_DEVICE_IRQ_E, 4 }, | ||
1013 | { SMC37c669_DEVICE_IRQ_F, 3 }, | ||
1014 | { SMC37c669_DEVICE_IRQ_H, -1 }, | ||
1015 | { -1, -1 } /* End of table */ | ||
1016 | }; | ||
1017 | |||
1018 | /* | ||
1019 | ** The following definition is for the MONET (XP1000) IRQ | ||
1020 | ** translation table. | ||
1021 | */ | ||
1022 | static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_monet_irq_table[] | ||
1023 | __initdata = | ||
1024 | { | ||
1025 | { SMC37c669_DEVICE_IRQ_A, -1 }, | ||
1026 | { SMC37c669_DEVICE_IRQ_B, -1 }, | ||
1027 | { SMC37c669_DEVICE_IRQ_C, 6 }, | ||
1028 | { SMC37c669_DEVICE_IRQ_D, 7 }, | ||
1029 | { SMC37c669_DEVICE_IRQ_E, 4 }, | ||
1030 | { SMC37c669_DEVICE_IRQ_F, 3 }, | ||
1031 | { SMC37c669_DEVICE_IRQ_H, -1 }, | ||
1032 | { -1, -1 } /* End of table */ | ||
1033 | }; | ||
1034 | |||
1035 | static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_tables[] __initdata = | ||
1036 | { | ||
1037 | SMC37c669_default_irq_table, | ||
1038 | SMC37c669_monet_irq_table | ||
1039 | }; | ||
1040 | |||
1041 | /* | ||
1042 | ** DRQ Translation Table | ||
1043 | ** | ||
1044 | ** The DRQ translation table is a list of SMC37c669 device and | ||
1045 | ** ISA DMA channels. | ||
1046 | ** | ||
1047 | */ | ||
1048 | static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata; | ||
1049 | |||
1050 | /* | ||
1051 | ** The following definition is the default DRQ | ||
1052 | ** translation table. | ||
1053 | */ | ||
1054 | static SMC37c669_DRQ_TRANSLATION_ENTRY SMC37c669_default_drq_table[] | ||
1055 | __initdata = | ||
1056 | { | ||
1057 | { SMC37c669_DEVICE_DRQ_A, 2 }, | ||
1058 | { SMC37c669_DEVICE_DRQ_B, 3 }, | ||
1059 | { SMC37c669_DEVICE_DRQ_C, -1 }, | ||
1060 | { -1, -1 } /* End of table */ | ||
1061 | }; | ||
1062 | |||
1063 | /* | ||
1064 | ** Local Function Prototype Declarations | ||
1065 | */ | ||
1066 | |||
1067 | static unsigned int SMC37c669_is_device_enabled( | ||
1068 | unsigned int func | ||
1069 | ); | ||
1070 | |||
1071 | #if 0 | ||
1072 | static unsigned int SMC37c669_get_device_config( | ||
1073 | unsigned int func, | ||
1074 | int *port, | ||
1075 | int *irq, | ||
1076 | int *drq | ||
1077 | ); | ||
1078 | #endif | ||
1079 | |||
1080 | static void SMC37c669_config_mode( | ||
1081 | unsigned int enable | ||
1082 | ); | ||
1083 | |||
1084 | static unsigned char SMC37c669_read_config( | ||
1085 | unsigned char index | ||
1086 | ); | ||
1087 | |||
1088 | static void SMC37c669_write_config( | ||
1089 | unsigned char index, | ||
1090 | unsigned char data | ||
1091 | ); | ||
1092 | |||
1093 | static void SMC37c669_init_local_config( void ); | ||
1094 | |||
1095 | static struct DEVICE_CONFIG *SMC37c669_get_config( | ||
1096 | unsigned int func | ||
1097 | ); | ||
1098 | |||
1099 | static int SMC37c669_xlate_irq( | ||
1100 | int irq | ||
1101 | ); | ||
1102 | |||
1103 | static int SMC37c669_xlate_drq( | ||
1104 | int drq | ||
1105 | ); | ||
1106 | |||
1107 | static __cacheline_aligned DEFINE_SPINLOCK(smc_lock); | ||
1108 | |||
1109 | /* | ||
1110 | **++ | ||
1111 | ** FUNCTIONAL DESCRIPTION: | ||
1112 | ** | ||
1113 | ** This function detects the presence of an SMC37c669 Super I/O | ||
1114 | ** controller. | ||
1115 | ** | ||
1116 | ** FORMAL PARAMETERS: | ||
1117 | ** | ||
1118 | ** None | ||
1119 | ** | ||
1120 | ** RETURN VALUE: | ||
1121 | ** | ||
1122 | ** Returns a pointer to the device if found, otherwise, | ||
1123 | ** the NULL pointer is returned. | ||
1124 | ** | ||
1125 | ** SIDE EFFECTS: | ||
1126 | ** | ||
1127 | ** None | ||
1128 | ** | ||
1129 | **-- | ||
1130 | */ | ||
1131 | SMC37c669_CONFIG_REGS * __init SMC37c669_detect( int index ) | ||
1132 | { | ||
1133 | int i; | ||
1134 | SMC37c669_DEVICE_ID_REGISTER id; | ||
1135 | |||
1136 | for ( i = 0; SMC37c669_Addresses[i] != 0; i++ ) { | ||
1137 | /* | ||
1138 | ** Initialize the device pointer even though we don't yet know if | ||
1139 | ** the controller is at this address. The support functions access | ||
1140 | ** the controller through this device pointer so we need to set it | ||
1141 | ** even when we are looking ... | ||
1142 | */ | ||
1143 | SMC37c669 = ( SMC37c669_CONFIG_REGS * )SMC37c669_Addresses[i]; | ||
1144 | /* | ||
1145 | ** Enter configuration mode | ||
1146 | */ | ||
1147 | SMC37c669_config_mode( TRUE ); | ||
1148 | /* | ||
1149 | ** Read the device id | ||
1150 | */ | ||
1151 | id.as_uchar = SMC37c669_read_config( SMC37c669_DEVICE_ID_INDEX ); | ||
1152 | /* | ||
1153 | ** Exit configuration mode | ||
1154 | */ | ||
1155 | SMC37c669_config_mode( FALSE ); | ||
1156 | /* | ||
1157 | ** Does the device id match? If so, assume we have found an | ||
1158 | ** SMC37c669 controller at this address. | ||
1159 | */ | ||
1160 | if ( id.by_field.device_id == SMC37c669_DEVICE_ID ) { | ||
1161 | /* | ||
1162 | ** Initialize the IRQ and DRQ translation tables. | ||
1163 | */ | ||
1164 | SMC37c669_irq_table = SMC37c669_irq_tables[ index ]; | ||
1165 | SMC37c669_drq_table = SMC37c669_default_drq_table; | ||
1166 | /* | ||
1167 | ** erfix | ||
1168 | ** | ||
1169 | ** If the platform can't use the IRQ and DRQ defaults set up in this | ||
1170 | ** file, it should call a platform-specific external routine at this | ||
1171 | ** point to reset the IRQ and DRQ translation table pointers to point | ||
1172 | ** at the appropriate tables for the platform. If the defaults are | ||
1173 | ** acceptable, then the external routine should do nothing. | ||
1174 | */ | ||
1175 | |||
1176 | /* | ||
1177 | ** Put the chip back into configuration mode | ||
1178 | */ | ||
1179 | SMC37c669_config_mode( TRUE ); | ||
1180 | /* | ||
1181 | ** Initialize local storage for configuration information | ||
1182 | */ | ||
1183 | SMC37c669_init_local_config( ); | ||
1184 | /* | ||
1185 | ** Exit configuration mode | ||
1186 | */ | ||
1187 | SMC37c669_config_mode( FALSE ); | ||
1188 | /* | ||
1189 | ** SMC37c669 controller found, break out of search loop | ||
1190 | */ | ||
1191 | break; | ||
1192 | } | ||
1193 | else { | ||
1194 | /* | ||
1195 | ** Otherwise, we did not find an SMC37c669 controller at this | ||
1196 | ** address so set the device pointer to NULL. | ||
1197 | */ | ||
1198 | SMC37c669 = NULL; | ||
1199 | } | ||
1200 | } | ||
1201 | return SMC37c669; | ||
1202 | } | ||
1203 | |||
1204 | |||
1205 | /* | ||
1206 | **++ | ||
1207 | ** FUNCTIONAL DESCRIPTION: | ||
1208 | ** | ||
1209 | ** This function enables an SMC37c669 device function. | ||
1210 | ** | ||
1211 | ** FORMAL PARAMETERS: | ||
1212 | ** | ||
1213 | ** func: | ||
1214 | ** Which device function to enable | ||
1215 | ** | ||
1216 | ** RETURN VALUE: | ||
1217 | ** | ||
1218 | ** Returns TRUE is the device function was enabled, otherwise, FALSE | ||
1219 | ** | ||
1220 | ** SIDE EFFECTS: | ||
1221 | ** | ||
1222 | ** {@description or none@} | ||
1223 | ** | ||
1224 | ** DESIGN: | ||
1225 | ** | ||
1226 | ** Enabling a device function in the SMC37c669 controller involves | ||
1227 | ** setting all of its mappings (port, irq, drq ...). A local | ||
1228 | ** "shadow" copy of the device configuration is kept so we can | ||
1229 | ** just set each mapping to what the local copy says. | ||
1230 | ** | ||
1231 | ** This function ALWAYS updates the local shadow configuration of | ||
1232 | ** the device function being enabled, even if the device is always | ||
1233 | ** enabled. To avoid replication of code, functions such as | ||
1234 | ** configure_device set up the local copy and then call this | ||
1235 | ** function to the update the real device. | ||
1236 | ** | ||
1237 | **-- | ||
1238 | */ | ||
1239 | unsigned int __init SMC37c669_enable_device ( unsigned int func ) | ||
1240 | { | ||
1241 | unsigned int ret_val = FALSE; | ||
1242 | /* | ||
1243 | ** Put the device into configuration mode | ||
1244 | */ | ||
1245 | SMC37c669_config_mode( TRUE ); | ||
1246 | switch ( func ) { | ||
1247 | case SERIAL_0: | ||
1248 | { | ||
1249 | SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; | ||
1250 | SMC37c669_SERIAL_IRQ_REGISTER irq; | ||
1251 | /* | ||
1252 | ** Enable the serial 1 IRQ mapping | ||
1253 | */ | ||
1254 | irq.as_uchar = | ||
1255 | SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); | ||
1256 | |||
1257 | irq.by_field.uart1_irq = | ||
1258 | SMC37c669_RAW_DEVICE_IRQ( | ||
1259 | SMC37c669_xlate_irq( local_config[ func ].irq ) | ||
1260 | ); | ||
1261 | |||
1262 | SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); | ||
1263 | /* | ||
1264 | ** Enable the serial 1 port base address mapping | ||
1265 | */ | ||
1266 | base_addr.as_uchar = 0; | ||
1267 | base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3; | ||
1268 | |||
1269 | SMC37c669_write_config( | ||
1270 | SMC37c669_SERIAL0_BASE_ADDRESS_INDEX, | ||
1271 | base_addr.as_uchar | ||
1272 | ); | ||
1273 | ret_val = TRUE; | ||
1274 | break; | ||
1275 | } | ||
1276 | case SERIAL_1: | ||
1277 | { | ||
1278 | SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; | ||
1279 | SMC37c669_SERIAL_IRQ_REGISTER irq; | ||
1280 | /* | ||
1281 | ** Enable the serial 2 IRQ mapping | ||
1282 | */ | ||
1283 | irq.as_uchar = | ||
1284 | SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); | ||
1285 | |||
1286 | irq.by_field.uart2_irq = | ||
1287 | SMC37c669_RAW_DEVICE_IRQ( | ||
1288 | SMC37c669_xlate_irq( local_config[ func ].irq ) | ||
1289 | ); | ||
1290 | |||
1291 | SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); | ||
1292 | /* | ||
1293 | ** Enable the serial 2 port base address mapping | ||
1294 | */ | ||
1295 | base_addr.as_uchar = 0; | ||
1296 | base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3; | ||
1297 | |||
1298 | SMC37c669_write_config( | ||
1299 | SMC37c669_SERIAL1_BASE_ADDRESS_INDEX, | ||
1300 | base_addr.as_uchar | ||
1301 | ); | ||
1302 | ret_val = TRUE; | ||
1303 | break; | ||
1304 | } | ||
1305 | case PARALLEL_0: | ||
1306 | { | ||
1307 | SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr; | ||
1308 | SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; | ||
1309 | SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; | ||
1310 | /* | ||
1311 | ** Enable the parallel port DMA channel mapping | ||
1312 | */ | ||
1313 | drq.as_uchar = | ||
1314 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); | ||
1315 | |||
1316 | drq.by_field.ppt_drq = | ||
1317 | SMC37c669_RAW_DEVICE_DRQ( | ||
1318 | SMC37c669_xlate_drq( local_config[ func ].drq ) | ||
1319 | ); | ||
1320 | |||
1321 | SMC37c669_write_config( | ||
1322 | SMC37c669_PARALLEL_FDC_DRQ_INDEX, | ||
1323 | drq.as_uchar | ||
1324 | ); | ||
1325 | /* | ||
1326 | ** Enable the parallel port IRQ mapping | ||
1327 | */ | ||
1328 | irq.as_uchar = | ||
1329 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); | ||
1330 | |||
1331 | irq.by_field.ppt_irq = | ||
1332 | SMC37c669_RAW_DEVICE_IRQ( | ||
1333 | SMC37c669_xlate_irq( local_config[ func ].irq ) | ||
1334 | ); | ||
1335 | |||
1336 | SMC37c669_write_config( | ||
1337 | SMC37c669_PARALLEL_FDC_IRQ_INDEX, | ||
1338 | irq.as_uchar | ||
1339 | ); | ||
1340 | /* | ||
1341 | ** Enable the parallel port base address mapping | ||
1342 | */ | ||
1343 | base_addr.as_uchar = 0; | ||
1344 | base_addr.by_field.addr9_2 = local_config[ func ].port1 >> 2; | ||
1345 | |||
1346 | SMC37c669_write_config( | ||
1347 | SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX, | ||
1348 | base_addr.as_uchar | ||
1349 | ); | ||
1350 | ret_val = TRUE; | ||
1351 | break; | ||
1352 | } | ||
1353 | case FLOPPY_0: | ||
1354 | { | ||
1355 | SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr; | ||
1356 | SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; | ||
1357 | SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; | ||
1358 | /* | ||
1359 | ** Enable the floppy controller DMA channel mapping | ||
1360 | */ | ||
1361 | drq.as_uchar = | ||
1362 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); | ||
1363 | |||
1364 | drq.by_field.fdc_drq = | ||
1365 | SMC37c669_RAW_DEVICE_DRQ( | ||
1366 | SMC37c669_xlate_drq( local_config[ func ].drq ) | ||
1367 | ); | ||
1368 | |||
1369 | SMC37c669_write_config( | ||
1370 | SMC37c669_PARALLEL_FDC_DRQ_INDEX, | ||
1371 | drq.as_uchar | ||
1372 | ); | ||
1373 | /* | ||
1374 | ** Enable the floppy controller IRQ mapping | ||
1375 | */ | ||
1376 | irq.as_uchar = | ||
1377 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); | ||
1378 | |||
1379 | irq.by_field.fdc_irq = | ||
1380 | SMC37c669_RAW_DEVICE_IRQ( | ||
1381 | SMC37c669_xlate_irq( local_config[ func ].irq ) | ||
1382 | ); | ||
1383 | |||
1384 | SMC37c669_write_config( | ||
1385 | SMC37c669_PARALLEL_FDC_IRQ_INDEX, | ||
1386 | irq.as_uchar | ||
1387 | ); | ||
1388 | /* | ||
1389 | ** Enable the floppy controller base address mapping | ||
1390 | */ | ||
1391 | base_addr.as_uchar = 0; | ||
1392 | base_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4; | ||
1393 | |||
1394 | SMC37c669_write_config( | ||
1395 | SMC37c669_FDC_BASE_ADDRESS_INDEX, | ||
1396 | base_addr.as_uchar | ||
1397 | ); | ||
1398 | ret_val = TRUE; | ||
1399 | break; | ||
1400 | } | ||
1401 | case IDE_0: | ||
1402 | { | ||
1403 | SMC37c669_IDE_ADDRESS_REGISTER ide_addr; | ||
1404 | /* | ||
1405 | ** Enable the IDE alternate status base address mapping | ||
1406 | */ | ||
1407 | ide_addr.as_uchar = 0; | ||
1408 | ide_addr.by_field.addr9_4 = local_config[ func ].port2 >> 4; | ||
1409 | |||
1410 | SMC37c669_write_config( | ||
1411 | SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX, | ||
1412 | ide_addr.as_uchar | ||
1413 | ); | ||
1414 | /* | ||
1415 | ** Enable the IDE controller base address mapping | ||
1416 | */ | ||
1417 | ide_addr.as_uchar = 0; | ||
1418 | ide_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4; | ||
1419 | |||
1420 | SMC37c669_write_config( | ||
1421 | SMC37c669_IDE_BASE_ADDRESS_INDEX, | ||
1422 | ide_addr.as_uchar | ||
1423 | ); | ||
1424 | ret_val = TRUE; | ||
1425 | break; | ||
1426 | } | ||
1427 | } | ||
1428 | /* | ||
1429 | ** Exit configuration mode and return | ||
1430 | */ | ||
1431 | SMC37c669_config_mode( FALSE ); | ||
1432 | |||
1433 | return ret_val; | ||
1434 | } | ||
1435 | |||
1436 | |||
1437 | /* | ||
1438 | **++ | ||
1439 | ** FUNCTIONAL DESCRIPTION: | ||
1440 | ** | ||
1441 | ** This function disables a device function within the | ||
1442 | ** SMC37c669 Super I/O controller. | ||
1443 | ** | ||
1444 | ** FORMAL PARAMETERS: | ||
1445 | ** | ||
1446 | ** func: | ||
1447 | ** Which function to disable | ||
1448 | ** | ||
1449 | ** RETURN VALUE: | ||
1450 | ** | ||
1451 | ** Return TRUE if the device function was disabled, otherwise, FALSE | ||
1452 | ** | ||
1453 | ** SIDE EFFECTS: | ||
1454 | ** | ||
1455 | ** {@description or none@} | ||
1456 | ** | ||
1457 | ** DESIGN: | ||
1458 | ** | ||
1459 | ** Disabling a function in the SMC37c669 device involves | ||
1460 | ** disabling all the function's mappings (port, irq, drq ...). | ||
1461 | ** A shadow copy of the device configuration is maintained | ||
1462 | ** in local storage so we won't worry aboving saving the | ||
1463 | ** current configuration information. | ||
1464 | ** | ||
1465 | **-- | ||
1466 | */ | ||
1467 | unsigned int __init SMC37c669_disable_device ( unsigned int func ) | ||
1468 | { | ||
1469 | unsigned int ret_val = FALSE; | ||
1470 | |||
1471 | /* | ||
1472 | ** Put the device into configuration mode | ||
1473 | */ | ||
1474 | SMC37c669_config_mode( TRUE ); | ||
1475 | switch ( func ) { | ||
1476 | case SERIAL_0: | ||
1477 | { | ||
1478 | SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; | ||
1479 | SMC37c669_SERIAL_IRQ_REGISTER irq; | ||
1480 | /* | ||
1481 | ** Disable the serial 1 IRQ mapping | ||
1482 | */ | ||
1483 | irq.as_uchar = | ||
1484 | SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); | ||
1485 | |||
1486 | irq.by_field.uart1_irq = 0; | ||
1487 | |||
1488 | SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); | ||
1489 | /* | ||
1490 | ** Disable the serial 1 port base address mapping | ||
1491 | */ | ||
1492 | base_addr.as_uchar = 0; | ||
1493 | SMC37c669_write_config( | ||
1494 | SMC37c669_SERIAL0_BASE_ADDRESS_INDEX, | ||
1495 | base_addr.as_uchar | ||
1496 | ); | ||
1497 | ret_val = TRUE; | ||
1498 | break; | ||
1499 | } | ||
1500 | case SERIAL_1: | ||
1501 | { | ||
1502 | SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; | ||
1503 | SMC37c669_SERIAL_IRQ_REGISTER irq; | ||
1504 | /* | ||
1505 | ** Disable the serial 2 IRQ mapping | ||
1506 | */ | ||
1507 | irq.as_uchar = | ||
1508 | SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); | ||
1509 | |||
1510 | irq.by_field.uart2_irq = 0; | ||
1511 | |||
1512 | SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); | ||
1513 | /* | ||
1514 | ** Disable the serial 2 port base address mapping | ||
1515 | */ | ||
1516 | base_addr.as_uchar = 0; | ||
1517 | |||
1518 | SMC37c669_write_config( | ||
1519 | SMC37c669_SERIAL1_BASE_ADDRESS_INDEX, | ||
1520 | base_addr.as_uchar | ||
1521 | ); | ||
1522 | ret_val = TRUE; | ||
1523 | break; | ||
1524 | } | ||
1525 | case PARALLEL_0: | ||
1526 | { | ||
1527 | SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr; | ||
1528 | SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; | ||
1529 | SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; | ||
1530 | /* | ||
1531 | ** Disable the parallel port DMA channel mapping | ||
1532 | */ | ||
1533 | drq.as_uchar = | ||
1534 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); | ||
1535 | |||
1536 | drq.by_field.ppt_drq = 0; | ||
1537 | |||
1538 | SMC37c669_write_config( | ||
1539 | SMC37c669_PARALLEL_FDC_DRQ_INDEX, | ||
1540 | drq.as_uchar | ||
1541 | ); | ||
1542 | /* | ||
1543 | ** Disable the parallel port IRQ mapping | ||
1544 | */ | ||
1545 | irq.as_uchar = | ||
1546 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); | ||
1547 | |||
1548 | irq.by_field.ppt_irq = 0; | ||
1549 | |||
1550 | SMC37c669_write_config( | ||
1551 | SMC37c669_PARALLEL_FDC_IRQ_INDEX, | ||
1552 | irq.as_uchar | ||
1553 | ); | ||
1554 | /* | ||
1555 | ** Disable the parallel port base address mapping | ||
1556 | */ | ||
1557 | base_addr.as_uchar = 0; | ||
1558 | |||
1559 | SMC37c669_write_config( | ||
1560 | SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX, | ||
1561 | base_addr.as_uchar | ||
1562 | ); | ||
1563 | ret_val = TRUE; | ||
1564 | break; | ||
1565 | } | ||
1566 | case FLOPPY_0: | ||
1567 | { | ||
1568 | SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr; | ||
1569 | SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; | ||
1570 | SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; | ||
1571 | /* | ||
1572 | ** Disable the floppy controller DMA channel mapping | ||
1573 | */ | ||
1574 | drq.as_uchar = | ||
1575 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); | ||
1576 | |||
1577 | drq.by_field.fdc_drq = 0; | ||
1578 | |||
1579 | SMC37c669_write_config( | ||
1580 | SMC37c669_PARALLEL_FDC_DRQ_INDEX, | ||
1581 | drq.as_uchar | ||
1582 | ); | ||
1583 | /* | ||
1584 | ** Disable the floppy controller IRQ mapping | ||
1585 | */ | ||
1586 | irq.as_uchar = | ||
1587 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); | ||
1588 | |||
1589 | irq.by_field.fdc_irq = 0; | ||
1590 | |||
1591 | SMC37c669_write_config( | ||
1592 | SMC37c669_PARALLEL_FDC_IRQ_INDEX, | ||
1593 | irq.as_uchar | ||
1594 | ); | ||
1595 | /* | ||
1596 | ** Disable the floppy controller base address mapping | ||
1597 | */ | ||
1598 | base_addr.as_uchar = 0; | ||
1599 | |||
1600 | SMC37c669_write_config( | ||
1601 | SMC37c669_FDC_BASE_ADDRESS_INDEX, | ||
1602 | base_addr.as_uchar | ||
1603 | ); | ||
1604 | ret_val = TRUE; | ||
1605 | break; | ||
1606 | } | ||
1607 | case IDE_0: | ||
1608 | { | ||
1609 | SMC37c669_IDE_ADDRESS_REGISTER ide_addr; | ||
1610 | /* | ||
1611 | ** Disable the IDE alternate status base address mapping | ||
1612 | */ | ||
1613 | ide_addr.as_uchar = 0; | ||
1614 | |||
1615 | SMC37c669_write_config( | ||
1616 | SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX, | ||
1617 | ide_addr.as_uchar | ||
1618 | ); | ||
1619 | /* | ||
1620 | ** Disable the IDE controller base address mapping | ||
1621 | */ | ||
1622 | ide_addr.as_uchar = 0; | ||
1623 | |||
1624 | SMC37c669_write_config( | ||
1625 | SMC37c669_IDE_BASE_ADDRESS_INDEX, | ||
1626 | ide_addr.as_uchar | ||
1627 | ); | ||
1628 | ret_val = TRUE; | ||
1629 | break; | ||
1630 | } | ||
1631 | } | ||
1632 | /* | ||
1633 | ** Exit configuration mode and return | ||
1634 | */ | ||
1635 | SMC37c669_config_mode( FALSE ); | ||
1636 | |||
1637 | return ret_val; | ||
1638 | } | ||
1639 | |||
1640 | |||
1641 | /* | ||
1642 | **++ | ||
1643 | ** FUNCTIONAL DESCRIPTION: | ||
1644 | ** | ||
1645 | ** This function configures a device function within the | ||
1646 | ** SMC37c669 Super I/O controller. | ||
1647 | ** | ||
1648 | ** FORMAL PARAMETERS: | ||
1649 | ** | ||
1650 | ** func: | ||
1651 | ** Which device function | ||
1652 | ** | ||
1653 | ** port: | ||
1654 | ** I/O port for the function to use | ||
1655 | ** | ||
1656 | ** irq: | ||
1657 | ** IRQ for the device function to use | ||
1658 | ** | ||
1659 | ** drq: | ||
1660 | ** DMA channel for the device function to use | ||
1661 | ** | ||
1662 | ** RETURN VALUE: | ||
1663 | ** | ||
1664 | ** Returns TRUE if the device function was configured, | ||
1665 | ** otherwise, FALSE. | ||
1666 | ** | ||
1667 | ** SIDE EFFECTS: | ||
1668 | ** | ||
1669 | ** {@description or none@} | ||
1670 | ** | ||
1671 | ** DESIGN: | ||
1672 | ** | ||
1673 | ** If this function returns TRUE, the local shadow copy of | ||
1674 | ** the configuration is also updated. If the device function | ||
1675 | ** is currently disabled, only the local shadow copy is | ||
1676 | ** updated and the actual device function will be updated | ||
1677 | ** if/when it is enabled. | ||
1678 | ** | ||
1679 | **-- | ||
1680 | */ | ||
1681 | unsigned int __init SMC37c669_configure_device ( | ||
1682 | unsigned int func, | ||
1683 | int port, | ||
1684 | int irq, | ||
1685 | int drq ) | ||
1686 | { | ||
1687 | struct DEVICE_CONFIG *cp; | ||
1688 | |||
1689 | /* | ||
1690 | ** Check for a valid configuration | ||
1691 | */ | ||
1692 | if ( ( cp = SMC37c669_get_config ( func ) ) != NULL ) { | ||
1693 | /* | ||
1694 | ** Configuration is valid, update the local shadow copy | ||
1695 | */ | ||
1696 | if ( ( drq & ~0xFF ) == 0 ) { | ||
1697 | cp->drq = drq; | ||
1698 | } | ||
1699 | if ( ( irq & ~0xFF ) == 0 ) { | ||
1700 | cp->irq = irq; | ||
1701 | } | ||
1702 | if ( ( port & ~0xFFFF ) == 0 ) { | ||
1703 | cp->port1 = port; | ||
1704 | } | ||
1705 | /* | ||
1706 | ** If the device function is enabled, update the actual | ||
1707 | ** device configuration. | ||
1708 | */ | ||
1709 | if ( SMC37c669_is_device_enabled( func ) ) { | ||
1710 | SMC37c669_enable_device( func ); | ||
1711 | } | ||
1712 | return TRUE; | ||
1713 | } | ||
1714 | return FALSE; | ||
1715 | } | ||
1716 | |||
1717 | |||
1718 | /* | ||
1719 | **++ | ||
1720 | ** FUNCTIONAL DESCRIPTION: | ||
1721 | ** | ||
1722 | ** This function determines whether a device function | ||
1723 | ** within the SMC37c669 controller is enabled. | ||
1724 | ** | ||
1725 | ** FORMAL PARAMETERS: | ||
1726 | ** | ||
1727 | ** func: | ||
1728 | ** Which device function | ||
1729 | ** | ||
1730 | ** RETURN VALUE: | ||
1731 | ** | ||
1732 | ** Returns TRUE if the device function is enabled, otherwise, FALSE | ||
1733 | ** | ||
1734 | ** SIDE EFFECTS: | ||
1735 | ** | ||
1736 | ** {@description or none@} | ||
1737 | ** | ||
1738 | ** DESIGN: | ||
1739 | ** | ||
1740 | ** To check whether a device is enabled we will only look at | ||
1741 | ** the port base address mapping. According to the SMC37c669 | ||
1742 | ** specification, all of the port base address mappings are | ||
1743 | ** disabled if the addr<9:8> (bits <7:6> of the register) are | ||
1744 | ** zero. | ||
1745 | ** | ||
1746 | **-- | ||
1747 | */ | ||
1748 | static unsigned int __init SMC37c669_is_device_enabled ( unsigned int func ) | ||
1749 | { | ||
1750 | unsigned char base_addr = 0; | ||
1751 | unsigned int dev_ok = FALSE; | ||
1752 | unsigned int ret_val = FALSE; | ||
1753 | /* | ||
1754 | ** Enter configuration mode | ||
1755 | */ | ||
1756 | SMC37c669_config_mode( TRUE ); | ||
1757 | |||
1758 | switch ( func ) { | ||
1759 | case SERIAL_0: | ||
1760 | base_addr = | ||
1761 | SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX ); | ||
1762 | dev_ok = TRUE; | ||
1763 | break; | ||
1764 | case SERIAL_1: | ||
1765 | base_addr = | ||
1766 | SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX ); | ||
1767 | dev_ok = TRUE; | ||
1768 | break; | ||
1769 | case PARALLEL_0: | ||
1770 | base_addr = | ||
1771 | SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX ); | ||
1772 | dev_ok = TRUE; | ||
1773 | break; | ||
1774 | case FLOPPY_0: | ||
1775 | base_addr = | ||
1776 | SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX ); | ||
1777 | dev_ok = TRUE; | ||
1778 | break; | ||
1779 | case IDE_0: | ||
1780 | base_addr = | ||
1781 | SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX ); | ||
1782 | dev_ok = TRUE; | ||
1783 | break; | ||
1784 | } | ||
1785 | /* | ||
1786 | ** If we have a valid device, check base_addr<7:6> to see if the | ||
1787 | ** device is enabled (mapped). | ||
1788 | */ | ||
1789 | if ( ( dev_ok ) && ( ( base_addr & 0xC0 ) != 0 ) ) { | ||
1790 | /* | ||
1791 | ** The mapping is not disabled, so assume that the function is | ||
1792 | ** enabled. | ||
1793 | */ | ||
1794 | ret_val = TRUE; | ||
1795 | } | ||
1796 | /* | ||
1797 | ** Exit configuration mode | ||
1798 | */ | ||
1799 | SMC37c669_config_mode( FALSE ); | ||
1800 | |||
1801 | return ret_val; | ||
1802 | } | ||
1803 | |||
1804 | |||
1805 | #if 0 | ||
1806 | /* | ||
1807 | **++ | ||
1808 | ** FUNCTIONAL DESCRIPTION: | ||
1809 | ** | ||
1810 | ** This function retrieves the configuration information of a | ||
1811 | ** device function within the SMC37c699 Super I/O controller. | ||
1812 | ** | ||
1813 | ** FORMAL PARAMETERS: | ||
1814 | ** | ||
1815 | ** func: | ||
1816 | ** Which device function | ||
1817 | ** | ||
1818 | ** port: | ||
1819 | ** I/O port returned | ||
1820 | ** | ||
1821 | ** irq: | ||
1822 | ** IRQ returned | ||
1823 | ** | ||
1824 | ** drq: | ||
1825 | ** DMA channel returned | ||
1826 | ** | ||
1827 | ** RETURN VALUE: | ||
1828 | ** | ||
1829 | ** Returns TRUE if the device configuration was successfully | ||
1830 | ** retrieved, otherwise, FALSE. | ||
1831 | ** | ||
1832 | ** SIDE EFFECTS: | ||
1833 | ** | ||
1834 | ** The data pointed to by the port, irq, and drq parameters | ||
1835 | ** my be modified even if the configuration is not successfully | ||
1836 | ** retrieved. | ||
1837 | ** | ||
1838 | ** DESIGN: | ||
1839 | ** | ||
1840 | ** The device configuration is fetched from the local shadow | ||
1841 | ** copy. Any unused parameters will be set to -1. Any | ||
1842 | ** parameter which is not desired can specify the NULL | ||
1843 | ** pointer. | ||
1844 | ** | ||
1845 | **-- | ||
1846 | */ | ||
1847 | static unsigned int __init SMC37c669_get_device_config ( | ||
1848 | unsigned int func, | ||
1849 | int *port, | ||
1850 | int *irq, | ||
1851 | int *drq ) | ||
1852 | { | ||
1853 | struct DEVICE_CONFIG *cp; | ||
1854 | unsigned int ret_val = FALSE; | ||
1855 | /* | ||
1856 | ** Check for a valid device configuration | ||
1857 | */ | ||
1858 | if ( ( cp = SMC37c669_get_config( func ) ) != NULL ) { | ||
1859 | if ( drq != NULL ) { | ||
1860 | *drq = cp->drq; | ||
1861 | ret_val = TRUE; | ||
1862 | } | ||
1863 | if ( irq != NULL ) { | ||
1864 | *irq = cp->irq; | ||
1865 | ret_val = TRUE; | ||
1866 | } | ||
1867 | if ( port != NULL ) { | ||
1868 | *port = cp->port1; | ||
1869 | ret_val = TRUE; | ||
1870 | } | ||
1871 | } | ||
1872 | return ret_val; | ||
1873 | } | ||
1874 | #endif | ||
1875 | |||
1876 | |||
1877 | /* | ||
1878 | **++ | ||
1879 | ** FUNCTIONAL DESCRIPTION: | ||
1880 | ** | ||
1881 | ** This function displays the current state of the SMC37c699 | ||
1882 | ** Super I/O controller's device functions. | ||
1883 | ** | ||
1884 | ** FORMAL PARAMETERS: | ||
1885 | ** | ||
1886 | ** None | ||
1887 | ** | ||
1888 | ** RETURN VALUE: | ||
1889 | ** | ||
1890 | ** None | ||
1891 | ** | ||
1892 | ** SIDE EFFECTS: | ||
1893 | ** | ||
1894 | ** None | ||
1895 | ** | ||
1896 | **-- | ||
1897 | */ | ||
1898 | void __init SMC37c669_display_device_info ( void ) | ||
1899 | { | ||
1900 | if ( SMC37c669_is_device_enabled( SERIAL_0 ) ) { | ||
1901 | printk( " Serial 0: Enabled [ Port 0x%x, IRQ %d ]\n", | ||
1902 | local_config[ SERIAL_0 ].port1, | ||
1903 | local_config[ SERIAL_0 ].irq | ||
1904 | ); | ||
1905 | } | ||
1906 | else { | ||
1907 | printk( " Serial 0: Disabled\n" ); | ||
1908 | } | ||
1909 | |||
1910 | if ( SMC37c669_is_device_enabled( SERIAL_1 ) ) { | ||
1911 | printk( " Serial 1: Enabled [ Port 0x%x, IRQ %d ]\n", | ||
1912 | local_config[ SERIAL_1 ].port1, | ||
1913 | local_config[ SERIAL_1 ].irq | ||
1914 | ); | ||
1915 | } | ||
1916 | else { | ||
1917 | printk( " Serial 1: Disabled\n" ); | ||
1918 | } | ||
1919 | |||
1920 | if ( SMC37c669_is_device_enabled( PARALLEL_0 ) ) { | ||
1921 | printk( " Parallel: Enabled [ Port 0x%x, IRQ %d/%d ]\n", | ||
1922 | local_config[ PARALLEL_0 ].port1, | ||
1923 | local_config[ PARALLEL_0 ].irq, | ||
1924 | local_config[ PARALLEL_0 ].drq | ||
1925 | ); | ||
1926 | } | ||
1927 | else { | ||
1928 | printk( " Parallel: Disabled\n" ); | ||
1929 | } | ||
1930 | |||
1931 | if ( SMC37c669_is_device_enabled( FLOPPY_0 ) ) { | ||
1932 | printk( " Floppy Ctrl: Enabled [ Port 0x%x, IRQ %d/%d ]\n", | ||
1933 | local_config[ FLOPPY_0 ].port1, | ||
1934 | local_config[ FLOPPY_0 ].irq, | ||
1935 | local_config[ FLOPPY_0 ].drq | ||
1936 | ); | ||
1937 | } | ||
1938 | else { | ||
1939 | printk( " Floppy Ctrl: Disabled\n" ); | ||
1940 | } | ||
1941 | |||
1942 | if ( SMC37c669_is_device_enabled( IDE_0 ) ) { | ||
1943 | printk( " IDE 0: Enabled [ Port 0x%x, IRQ %d ]\n", | ||
1944 | local_config[ IDE_0 ].port1, | ||
1945 | local_config[ IDE_0 ].irq | ||
1946 | ); | ||
1947 | } | ||
1948 | else { | ||
1949 | printk( " IDE 0: Disabled\n" ); | ||
1950 | } | ||
1951 | } | ||
1952 | |||
1953 | |||
1954 | /* | ||
1955 | **++ | ||
1956 | ** FUNCTIONAL DESCRIPTION: | ||
1957 | ** | ||
1958 | ** This function puts the SMC37c669 Super I/O controller into, | ||
1959 | ** and takes it out of, configuration mode. | ||
1960 | ** | ||
1961 | ** FORMAL PARAMETERS: | ||
1962 | ** | ||
1963 | ** enable: | ||
1964 | ** TRUE to enter configuration mode, FALSE to exit. | ||
1965 | ** | ||
1966 | ** RETURN VALUE: | ||
1967 | ** | ||
1968 | ** None | ||
1969 | ** | ||
1970 | ** SIDE EFFECTS: | ||
1971 | ** | ||
1972 | ** The SMC37c669 controller may be left in configuration mode. | ||
1973 | ** | ||
1974 | **-- | ||
1975 | */ | ||
1976 | static void __init SMC37c669_config_mode( | ||
1977 | unsigned int enable ) | ||
1978 | { | ||
1979 | if ( enable ) { | ||
1980 | /* | ||
1981 | ** To enter configuration mode, two writes in succession to the index | ||
1982 | ** port are required. If a write to another address or port occurs | ||
1983 | ** between these two writes, the chip does not enter configuration | ||
1984 | ** mode. Therefore, a spinlock is placed around the two writes to | ||
1985 | ** guarantee that they complete uninterrupted. | ||
1986 | */ | ||
1987 | spin_lock(&smc_lock); | ||
1988 | wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY ); | ||
1989 | wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY ); | ||
1990 | spin_unlock(&smc_lock); | ||
1991 | } | ||
1992 | else { | ||
1993 | wb( &SMC37c669->index_port, SMC37c669_CONFIG_OFF_KEY ); | ||
1994 | } | ||
1995 | } | ||
1996 | |||
1997 | /* | ||
1998 | **++ | ||
1999 | ** FUNCTIONAL DESCRIPTION: | ||
2000 | ** | ||
2001 | ** This function reads an SMC37c669 Super I/O controller | ||
2002 | ** configuration register. This function assumes that the | ||
2003 | ** device is already in configuration mode. | ||
2004 | ** | ||
2005 | ** FORMAL PARAMETERS: | ||
2006 | ** | ||
2007 | ** index: | ||
2008 | ** Index value of configuration register to read | ||
2009 | ** | ||
2010 | ** RETURN VALUE: | ||
2011 | ** | ||
2012 | ** Data read from configuration register | ||
2013 | ** | ||
2014 | ** SIDE EFFECTS: | ||
2015 | ** | ||
2016 | ** None | ||
2017 | ** | ||
2018 | **-- | ||
2019 | */ | ||
2020 | static unsigned char __init SMC37c669_read_config( | ||
2021 | unsigned char index ) | ||
2022 | { | ||
2023 | unsigned char data; | ||
2024 | |||
2025 | wb( &SMC37c669->index_port, index ); | ||
2026 | data = rb( &SMC37c669->data_port ); | ||
2027 | return data; | ||
2028 | } | ||
2029 | |||
2030 | /* | ||
2031 | **++ | ||
2032 | ** FUNCTIONAL DESCRIPTION: | ||
2033 | ** | ||
2034 | ** This function writes an SMC37c669 Super I/O controller | ||
2035 | ** configuration register. This function assumes that the | ||
2036 | ** device is already in configuration mode. | ||
2037 | ** | ||
2038 | ** FORMAL PARAMETERS: | ||
2039 | ** | ||
2040 | ** index: | ||
2041 | ** Index of configuration register to write | ||
2042 | ** | ||
2043 | ** data: | ||
2044 | ** Data to be written | ||
2045 | ** | ||
2046 | ** RETURN VALUE: | ||
2047 | ** | ||
2048 | ** None | ||
2049 | ** | ||
2050 | ** SIDE EFFECTS: | ||
2051 | ** | ||
2052 | ** None | ||
2053 | ** | ||
2054 | **-- | ||
2055 | */ | ||
2056 | static void __init SMC37c669_write_config( | ||
2057 | unsigned char index, | ||
2058 | unsigned char data ) | ||
2059 | { | ||
2060 | wb( &SMC37c669->index_port, index ); | ||
2061 | wb( &SMC37c669->data_port, data ); | ||
2062 | } | ||
2063 | |||
2064 | |||
2065 | /* | ||
2066 | **++ | ||
2067 | ** FUNCTIONAL DESCRIPTION: | ||
2068 | ** | ||
2069 | ** This function initializes the local device | ||
2070 | ** configuration storage. This function assumes | ||
2071 | ** that the device is already in configuration | ||
2072 | ** mode. | ||
2073 | ** | ||
2074 | ** FORMAL PARAMETERS: | ||
2075 | ** | ||
2076 | ** None | ||
2077 | ** | ||
2078 | ** RETURN VALUE: | ||
2079 | ** | ||
2080 | ** None | ||
2081 | ** | ||
2082 | ** SIDE EFFECTS: | ||
2083 | ** | ||
2084 | ** Local storage for device configuration information | ||
2085 | ** is initialized. | ||
2086 | ** | ||
2087 | **-- | ||
2088 | */ | ||
2089 | static void __init SMC37c669_init_local_config ( void ) | ||
2090 | { | ||
2091 | SMC37c669_SERIAL_BASE_ADDRESS_REGISTER uart_base; | ||
2092 | SMC37c669_SERIAL_IRQ_REGISTER uart_irqs; | ||
2093 | SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER ppt_base; | ||
2094 | SMC37c669_PARALLEL_FDC_IRQ_REGISTER ppt_fdc_irqs; | ||
2095 | SMC37c669_PARALLEL_FDC_DRQ_REGISTER ppt_fdc_drqs; | ||
2096 | SMC37c669_FDC_BASE_ADDRESS_REGISTER fdc_base; | ||
2097 | SMC37c669_IDE_ADDRESS_REGISTER ide_base; | ||
2098 | SMC37c669_IDE_ADDRESS_REGISTER ide_alt; | ||
2099 | |||
2100 | /* | ||
2101 | ** Get serial port 1 base address | ||
2102 | */ | ||
2103 | uart_base.as_uchar = | ||
2104 | SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX ); | ||
2105 | /* | ||
2106 | ** Get IRQs for serial ports 1 & 2 | ||
2107 | */ | ||
2108 | uart_irqs.as_uchar = | ||
2109 | SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); | ||
2110 | /* | ||
2111 | ** Store local configuration information for serial port 1 | ||
2112 | */ | ||
2113 | local_config[SERIAL_0].port1 = uart_base.by_field.addr9_3 << 3; | ||
2114 | local_config[SERIAL_0].irq = | ||
2115 | SMC37c669_xlate_irq( | ||
2116 | SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart1_irq ) | ||
2117 | ); | ||
2118 | /* | ||
2119 | ** Get serial port 2 base address | ||
2120 | */ | ||
2121 | uart_base.as_uchar = | ||
2122 | SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX ); | ||
2123 | /* | ||
2124 | ** Store local configuration information for serial port 2 | ||
2125 | */ | ||
2126 | local_config[SERIAL_1].port1 = uart_base.by_field.addr9_3 << 3; | ||
2127 | local_config[SERIAL_1].irq = | ||
2128 | SMC37c669_xlate_irq( | ||
2129 | SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart2_irq ) | ||
2130 | ); | ||
2131 | /* | ||
2132 | ** Get parallel port base address | ||
2133 | */ | ||
2134 | ppt_base.as_uchar = | ||
2135 | SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX ); | ||
2136 | /* | ||
2137 | ** Get IRQs for parallel port and floppy controller | ||
2138 | */ | ||
2139 | ppt_fdc_irqs.as_uchar = | ||
2140 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); | ||
2141 | /* | ||
2142 | ** Get DRQs for parallel port and floppy controller | ||
2143 | */ | ||
2144 | ppt_fdc_drqs.as_uchar = | ||
2145 | SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); | ||
2146 | /* | ||
2147 | ** Store local configuration information for parallel port | ||
2148 | */ | ||
2149 | local_config[PARALLEL_0].port1 = ppt_base.by_field.addr9_2 << 2; | ||
2150 | local_config[PARALLEL_0].irq = | ||
2151 | SMC37c669_xlate_irq( | ||
2152 | SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.ppt_irq ) | ||
2153 | ); | ||
2154 | local_config[PARALLEL_0].drq = | ||
2155 | SMC37c669_xlate_drq( | ||
2156 | SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.ppt_drq ) | ||
2157 | ); | ||
2158 | /* | ||
2159 | ** Get floppy controller base address | ||
2160 | */ | ||
2161 | fdc_base.as_uchar = | ||
2162 | SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX ); | ||
2163 | /* | ||
2164 | ** Store local configuration information for floppy controller | ||
2165 | */ | ||
2166 | local_config[FLOPPY_0].port1 = fdc_base.by_field.addr9_4 << 4; | ||
2167 | local_config[FLOPPY_0].irq = | ||
2168 | SMC37c669_xlate_irq( | ||
2169 | SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.fdc_irq ) | ||
2170 | ); | ||
2171 | local_config[FLOPPY_0].drq = | ||
2172 | SMC37c669_xlate_drq( | ||
2173 | SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.fdc_drq ) | ||
2174 | ); | ||
2175 | /* | ||
2176 | ** Get IDE controller base address | ||
2177 | */ | ||
2178 | ide_base.as_uchar = | ||
2179 | SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX ); | ||
2180 | /* | ||
2181 | ** Get IDE alternate status base address | ||
2182 | */ | ||
2183 | ide_alt.as_uchar = | ||
2184 | SMC37c669_read_config( SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX ); | ||
2185 | /* | ||
2186 | ** Store local configuration information for IDE controller | ||
2187 | */ | ||
2188 | local_config[IDE_0].port1 = ide_base.by_field.addr9_4 << 4; | ||
2189 | local_config[IDE_0].port2 = ide_alt.by_field.addr9_4 << 4; | ||
2190 | local_config[IDE_0].irq = 14; | ||
2191 | } | ||
2192 | |||
2193 | |||
2194 | /* | ||
2195 | **++ | ||
2196 | ** FUNCTIONAL DESCRIPTION: | ||
2197 | ** | ||
2198 | ** This function returns a pointer to the local shadow | ||
2199 | ** configuration of the requested device function. | ||
2200 | ** | ||
2201 | ** FORMAL PARAMETERS: | ||
2202 | ** | ||
2203 | ** func: | ||
2204 | ** Which device function | ||
2205 | ** | ||
2206 | ** RETURN VALUE: | ||
2207 | ** | ||
2208 | ** Returns a pointer to the DEVICE_CONFIG structure for the | ||
2209 | ** requested function, otherwise, NULL. | ||
2210 | ** | ||
2211 | ** SIDE EFFECTS: | ||
2212 | ** | ||
2213 | ** {@description or none@} | ||
2214 | ** | ||
2215 | **-- | ||
2216 | */ | ||
2217 | static struct DEVICE_CONFIG * __init SMC37c669_get_config( unsigned int func ) | ||
2218 | { | ||
2219 | struct DEVICE_CONFIG *cp = NULL; | ||
2220 | |||
2221 | switch ( func ) { | ||
2222 | case SERIAL_0: | ||
2223 | cp = &local_config[ SERIAL_0 ]; | ||
2224 | break; | ||
2225 | case SERIAL_1: | ||
2226 | cp = &local_config[ SERIAL_1 ]; | ||
2227 | break; | ||
2228 | case PARALLEL_0: | ||
2229 | cp = &local_config[ PARALLEL_0 ]; | ||
2230 | break; | ||
2231 | case FLOPPY_0: | ||
2232 | cp = &local_config[ FLOPPY_0 ]; | ||
2233 | break; | ||
2234 | case IDE_0: | ||
2235 | cp = &local_config[ IDE_0 ]; | ||
2236 | break; | ||
2237 | } | ||
2238 | return cp; | ||
2239 | } | ||
2240 | |||
2241 | /* | ||
2242 | **++ | ||
2243 | ** FUNCTIONAL DESCRIPTION: | ||
2244 | ** | ||
2245 | ** This function translates IRQs back and forth between ISA | ||
2246 | ** IRQs and SMC37c669 device IRQs. | ||
2247 | ** | ||
2248 | ** FORMAL PARAMETERS: | ||
2249 | ** | ||
2250 | ** irq: | ||
2251 | ** The IRQ to translate | ||
2252 | ** | ||
2253 | ** RETURN VALUE: | ||
2254 | ** | ||
2255 | ** Returns the translated IRQ, otherwise, returns -1. | ||
2256 | ** | ||
2257 | ** SIDE EFFECTS: | ||
2258 | ** | ||
2259 | ** {@description or none@} | ||
2260 | ** | ||
2261 | **-- | ||
2262 | */ | ||
2263 | static int __init SMC37c669_xlate_irq ( int irq ) | ||
2264 | { | ||
2265 | int i, translated_irq = -1; | ||
2266 | |||
2267 | if ( SMC37c669_IS_DEVICE_IRQ( irq ) ) { | ||
2268 | /* | ||
2269 | ** We are translating a device IRQ to an ISA IRQ | ||
2270 | */ | ||
2271 | for ( i = 0; ( SMC37c669_irq_table[i].device_irq != -1 ) || ( SMC37c669_irq_table[i].isa_irq != -1 ); i++ ) { | ||
2272 | if ( irq == SMC37c669_irq_table[i].device_irq ) { | ||
2273 | translated_irq = SMC37c669_irq_table[i].isa_irq; | ||
2274 | break; | ||
2275 | } | ||
2276 | } | ||
2277 | } | ||
2278 | else { | ||
2279 | /* | ||
2280 | ** We are translating an ISA IRQ to a device IRQ | ||
2281 | */ | ||
2282 | for ( i = 0; ( SMC37c669_irq_table[i].isa_irq != -1 ) || ( SMC37c669_irq_table[i].device_irq != -1 ); i++ ) { | ||
2283 | if ( irq == SMC37c669_irq_table[i].isa_irq ) { | ||
2284 | translated_irq = SMC37c669_irq_table[i].device_irq; | ||
2285 | break; | ||
2286 | } | ||
2287 | } | ||
2288 | } | ||
2289 | return translated_irq; | ||
2290 | } | ||
2291 | |||
2292 | |||
2293 | /* | ||
2294 | **++ | ||
2295 | ** FUNCTIONAL DESCRIPTION: | ||
2296 | ** | ||
2297 | ** This function translates DMA channels back and forth between | ||
2298 | ** ISA DMA channels and SMC37c669 device DMA channels. | ||
2299 | ** | ||
2300 | ** FORMAL PARAMETERS: | ||
2301 | ** | ||
2302 | ** drq: | ||
2303 | ** The DMA channel to translate | ||
2304 | ** | ||
2305 | ** RETURN VALUE: | ||
2306 | ** | ||
2307 | ** Returns the translated DMA channel, otherwise, returns -1 | ||
2308 | ** | ||
2309 | ** SIDE EFFECTS: | ||
2310 | ** | ||
2311 | ** {@description or none@} | ||
2312 | ** | ||
2313 | **-- | ||
2314 | */ | ||
2315 | static int __init SMC37c669_xlate_drq ( int drq ) | ||
2316 | { | ||
2317 | int i, translated_drq = -1; | ||
2318 | |||
2319 | if ( SMC37c669_IS_DEVICE_DRQ( drq ) ) { | ||
2320 | /* | ||
2321 | ** We are translating a device DMA channel to an ISA DMA channel | ||
2322 | */ | ||
2323 | for ( i = 0; ( SMC37c669_drq_table[i].device_drq != -1 ) || ( SMC37c669_drq_table[i].isa_drq != -1 ); i++ ) { | ||
2324 | if ( drq == SMC37c669_drq_table[i].device_drq ) { | ||
2325 | translated_drq = SMC37c669_drq_table[i].isa_drq; | ||
2326 | break; | ||
2327 | } | ||
2328 | } | ||
2329 | } | ||
2330 | else { | ||
2331 | /* | ||
2332 | ** We are translating an ISA DMA channel to a device DMA channel | ||
2333 | */ | ||
2334 | for ( i = 0; ( SMC37c669_drq_table[i].isa_drq != -1 ) || ( SMC37c669_drq_table[i].device_drq != -1 ); i++ ) { | ||
2335 | if ( drq == SMC37c669_drq_table[i].isa_drq ) { | ||
2336 | translated_drq = SMC37c669_drq_table[i].device_drq; | ||
2337 | break; | ||
2338 | } | ||
2339 | } | ||
2340 | } | ||
2341 | return translated_drq; | ||
2342 | } | ||
2343 | |||
2344 | #if 0 | ||
2345 | int __init smcc669_init ( void ) | ||
2346 | { | ||
2347 | struct INODE *ip; | ||
2348 | |||
2349 | allocinode( smc_ddb.name, 1, &ip ); | ||
2350 | ip->dva = &smc_ddb; | ||
2351 | ip->attr = ATTR$M_WRITE | ATTR$M_READ; | ||
2352 | ip->len[0] = 0x30; | ||
2353 | ip->misc = 0; | ||
2354 | INODE_UNLOCK( ip ); | ||
2355 | |||
2356 | return msg_success; | ||
2357 | } | ||
2358 | |||
2359 | int __init smcc669_open( struct FILE *fp, char *info, char *next, char *mode ) | ||
2360 | { | ||
2361 | struct INODE *ip; | ||
2362 | /* | ||
2363 | ** Allow multiple readers but only one writer. ip->misc keeps track | ||
2364 | ** of the number of writers | ||
2365 | */ | ||
2366 | ip = fp->ip; | ||
2367 | INODE_LOCK( ip ); | ||
2368 | if ( fp->mode & ATTR$M_WRITE ) { | ||
2369 | if ( ip->misc ) { | ||
2370 | INODE_UNLOCK( ip ); | ||
2371 | return msg_failure; /* too many writers */ | ||
2372 | } | ||
2373 | ip->misc++; | ||
2374 | } | ||
2375 | /* | ||
2376 | ** Treat the information field as a byte offset | ||
2377 | */ | ||
2378 | *fp->offset = xtoi( info ); | ||
2379 | INODE_UNLOCK( ip ); | ||
2380 | |||
2381 | return msg_success; | ||
2382 | } | ||
2383 | |||
2384 | int __init smcc669_close( struct FILE *fp ) | ||
2385 | { | ||
2386 | struct INODE *ip; | ||
2387 | |||
2388 | ip = fp->ip; | ||
2389 | if ( fp->mode & ATTR$M_WRITE ) { | ||
2390 | INODE_LOCK( ip ); | ||
2391 | ip->misc--; | ||
2392 | INODE_UNLOCK( ip ); | ||
2393 | } | ||
2394 | return msg_success; | ||
2395 | } | ||
2396 | |||
2397 | int __init smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf ) | ||
2398 | { | ||
2399 | int i; | ||
2400 | int length; | ||
2401 | int nbytes; | ||
2402 | struct INODE *ip; | ||
2403 | |||
2404 | /* | ||
2405 | ** Always access a byte at a time | ||
2406 | */ | ||
2407 | ip = fp->ip; | ||
2408 | length = size * number; | ||
2409 | nbytes = 0; | ||
2410 | |||
2411 | SMC37c669_config_mode( TRUE ); | ||
2412 | for ( i = 0; i < length; i++ ) { | ||
2413 | if ( !inrange( *fp->offset, 0, ip->len[0] ) ) | ||
2414 | break; | ||
2415 | *buf++ = SMC37c669_read_config( *fp->offset ); | ||
2416 | *fp->offset += 1; | ||
2417 | nbytes++; | ||
2418 | } | ||
2419 | SMC37c669_config_mode( FALSE ); | ||
2420 | return nbytes; | ||
2421 | } | ||
2422 | |||
2423 | int __init smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf ) | ||
2424 | { | ||
2425 | int i; | ||
2426 | int length; | ||
2427 | int nbytes; | ||
2428 | struct INODE *ip; | ||
2429 | /* | ||
2430 | ** Always access a byte at a time | ||
2431 | */ | ||
2432 | ip = fp->ip; | ||
2433 | length = size * number; | ||
2434 | nbytes = 0; | ||
2435 | |||
2436 | SMC37c669_config_mode( TRUE ); | ||
2437 | for ( i = 0; i < length; i++ ) { | ||
2438 | if ( !inrange( *fp->offset, 0, ip->len[0] ) ) | ||
2439 | break; | ||
2440 | SMC37c669_write_config( *fp->offset, *buf ); | ||
2441 | *fp->offset += 1; | ||
2442 | buf++; | ||
2443 | nbytes++; | ||
2444 | } | ||
2445 | SMC37c669_config_mode( FALSE ); | ||
2446 | return nbytes; | ||
2447 | } | ||
2448 | #endif | ||
2449 | |||
2450 | void __init | ||
2451 | SMC37c669_dump_registers(void) | ||
2452 | { | ||
2453 | int i; | ||
2454 | for (i = 0; i <= 0x29; i++) | ||
2455 | printk("-- CR%02x : %02x\n", i, SMC37c669_read_config(i)); | ||
2456 | } | ||
2457 | /*+ | ||
2458 | * ============================================================================ | ||
2459 | * = SMC_init - SMC37c669 Super I/O controller initialization = | ||
2460 | * ============================================================================ | ||
2461 | * | ||
2462 | * OVERVIEW: | ||
2463 | * | ||
2464 | * This routine configures and enables device functions on the | ||
2465 | * SMC37c669 Super I/O controller. | ||
2466 | * | ||
2467 | * FORM OF CALL: | ||
2468 | * | ||
2469 | * SMC_init( ); | ||
2470 | * | ||
2471 | * RETURNS: | ||
2472 | * | ||
2473 | * Nothing | ||
2474 | * | ||
2475 | * ARGUMENTS: | ||
2476 | * | ||
2477 | * None | ||
2478 | * | ||
2479 | * SIDE EFFECTS: | ||
2480 | * | ||
2481 | * None | ||
2482 | * | ||
2483 | */ | ||
2484 | void __init SMC669_Init ( int index ) | ||
2485 | { | ||
2486 | SMC37c669_CONFIG_REGS *SMC_base; | ||
2487 | unsigned long flags; | ||
2488 | |||
2489 | local_irq_save(flags); | ||
2490 | if ( ( SMC_base = SMC37c669_detect( index ) ) != NULL ) { | ||
2491 | #if SMC_DEBUG | ||
2492 | SMC37c669_config_mode( TRUE ); | ||
2493 | SMC37c669_dump_registers( ); | ||
2494 | SMC37c669_config_mode( FALSE ); | ||
2495 | SMC37c669_display_device_info( ); | ||
2496 | #endif | ||
2497 | SMC37c669_disable_device( SERIAL_0 ); | ||
2498 | SMC37c669_configure_device( | ||
2499 | SERIAL_0, | ||
2500 | COM1_BASE, | ||
2501 | COM1_IRQ, | ||
2502 | -1 | ||
2503 | ); | ||
2504 | SMC37c669_enable_device( SERIAL_0 ); | ||
2505 | |||
2506 | SMC37c669_disable_device( SERIAL_1 ); | ||
2507 | SMC37c669_configure_device( | ||
2508 | SERIAL_1, | ||
2509 | COM2_BASE, | ||
2510 | COM2_IRQ, | ||
2511 | -1 | ||
2512 | ); | ||
2513 | SMC37c669_enable_device( SERIAL_1 ); | ||
2514 | |||
2515 | SMC37c669_disable_device( PARALLEL_0 ); | ||
2516 | SMC37c669_configure_device( | ||
2517 | PARALLEL_0, | ||
2518 | PARP_BASE, | ||
2519 | PARP_IRQ, | ||
2520 | PARP_DRQ | ||
2521 | ); | ||
2522 | SMC37c669_enable_device( PARALLEL_0 ); | ||
2523 | |||
2524 | SMC37c669_disable_device( FLOPPY_0 ); | ||
2525 | SMC37c669_configure_device( | ||
2526 | FLOPPY_0, | ||
2527 | FDC_BASE, | ||
2528 | FDC_IRQ, | ||
2529 | FDC_DRQ | ||
2530 | ); | ||
2531 | SMC37c669_enable_device( FLOPPY_0 ); | ||
2532 | |||
2533 | /* Wake up sometimes forgotten floppy, especially on DP264. */ | ||
2534 | outb(0xc, 0x3f2); | ||
2535 | |||
2536 | SMC37c669_disable_device( IDE_0 ); | ||
2537 | |||
2538 | #if SMC_DEBUG | ||
2539 | SMC37c669_config_mode( TRUE ); | ||
2540 | SMC37c669_dump_registers( ); | ||
2541 | SMC37c669_config_mode( FALSE ); | ||
2542 | SMC37c669_display_device_info( ); | ||
2543 | #endif | ||
2544 | local_irq_restore(flags); | ||
2545 | printk( "SMC37c669 Super I/O Controller found @ 0x%lx\n", | ||
2546 | (unsigned long) SMC_base ); | ||
2547 | } | ||
2548 | else { | ||
2549 | local_irq_restore(flags); | ||
2550 | #if SMC_DEBUG | ||
2551 | printk( "No SMC37c669 Super I/O Controller found\n" ); | ||
2552 | #endif | ||
2553 | } | ||
2554 | } | ||
diff --git a/arch/alpha/kernel/smc37c93x.c b/arch/alpha/kernel/smc37c93x.c new file mode 100644 index 000000000000..421e51ea6bb7 --- /dev/null +++ b/arch/alpha/kernel/smc37c93x.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * SMC 37C93X initialization code | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/kernel.h> | ||
7 | |||
8 | #include <linux/slab.h> | ||
9 | #include <linux/mm.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/delay.h> | ||
12 | |||
13 | #include <asm/hwrpb.h> | ||
14 | #include <asm/io.h> | ||
15 | #include <asm/segment.h> | ||
16 | |||
17 | #define SMC_DEBUG 0 | ||
18 | |||
19 | #if SMC_DEBUG | ||
20 | # define DBG_DEVS(args) printk args | ||
21 | #else | ||
22 | # define DBG_DEVS(args) | ||
23 | #endif | ||
24 | |||
25 | #define KB 1024 | ||
26 | #define MB (1024*KB) | ||
27 | #define GB (1024*MB) | ||
28 | |||
29 | /* device "activate" register contents */ | ||
30 | #define DEVICE_ON 1 | ||
31 | #define DEVICE_OFF 0 | ||
32 | |||
33 | /* configuration on/off keys */ | ||
34 | #define CONFIG_ON_KEY 0x55 | ||
35 | #define CONFIG_OFF_KEY 0xaa | ||
36 | |||
37 | /* configuration space device definitions */ | ||
38 | #define FDC 0 | ||
39 | #define IDE1 1 | ||
40 | #define IDE2 2 | ||
41 | #define PARP 3 | ||
42 | #define SER1 4 | ||
43 | #define SER2 5 | ||
44 | #define RTCL 6 | ||
45 | #define KYBD 7 | ||
46 | #define AUXIO 8 | ||
47 | |||
48 | /* Chip register offsets from base */ | ||
49 | #define CONFIG_CONTROL 0x02 | ||
50 | #define INDEX_ADDRESS 0x03 | ||
51 | #define LOGICAL_DEVICE_NUMBER 0x07 | ||
52 | #define DEVICE_ID 0x20 | ||
53 | #define DEVICE_REV 0x21 | ||
54 | #define POWER_CONTROL 0x22 | ||
55 | #define POWER_MGMT 0x23 | ||
56 | #define OSC 0x24 | ||
57 | |||
58 | #define ACTIVATE 0x30 | ||
59 | #define ADDR_HI 0x60 | ||
60 | #define ADDR_LO 0x61 | ||
61 | #define INTERRUPT_SEL 0x70 | ||
62 | #define INTERRUPT_SEL_2 0x72 /* KYBD/MOUS only */ | ||
63 | #define DMA_CHANNEL_SEL 0x74 /* FDC/PARP only */ | ||
64 | |||
65 | #define FDD_MODE_REGISTER 0x90 | ||
66 | #define FDD_OPTION_REGISTER 0x91 | ||
67 | |||
68 | /* values that we read back that are expected ... */ | ||
69 | #define VALID_DEVICE_ID 2 | ||
70 | |||
71 | /* default device addresses */ | ||
72 | #define KYBD_INTERRUPT 1 | ||
73 | #define MOUS_INTERRUPT 12 | ||
74 | #define COM2_BASE 0x2f8 | ||
75 | #define COM2_INTERRUPT 3 | ||
76 | #define COM1_BASE 0x3f8 | ||
77 | #define COM1_INTERRUPT 4 | ||
78 | #define PARP_BASE 0x3bc | ||
79 | #define PARP_INTERRUPT 7 | ||
80 | |||
81 | static unsigned long __init SMCConfigState(unsigned long baseAddr) | ||
82 | { | ||
83 | unsigned char devId; | ||
84 | unsigned char devRev; | ||
85 | |||
86 | unsigned long configPort; | ||
87 | unsigned long indexPort; | ||
88 | unsigned long dataPort; | ||
89 | |||
90 | int i; | ||
91 | |||
92 | configPort = indexPort = baseAddr; | ||
93 | dataPort = configPort + 1; | ||
94 | |||
95 | #define NUM_RETRIES 5 | ||
96 | |||
97 | for (i = 0; i < NUM_RETRIES; i++) | ||
98 | { | ||
99 | outb(CONFIG_ON_KEY, configPort); | ||
100 | outb(CONFIG_ON_KEY, configPort); | ||
101 | outb(DEVICE_ID, indexPort); | ||
102 | devId = inb(dataPort); | ||
103 | if (devId == VALID_DEVICE_ID) { | ||
104 | outb(DEVICE_REV, indexPort); | ||
105 | devRev = inb(dataPort); | ||
106 | break; | ||
107 | } | ||
108 | else | ||
109 | udelay(100); | ||
110 | } | ||
111 | return (i != NUM_RETRIES) ? baseAddr : 0L; | ||
112 | } | ||
113 | |||
114 | static void __init SMCRunState(unsigned long baseAddr) | ||
115 | { | ||
116 | outb(CONFIG_OFF_KEY, baseAddr); | ||
117 | } | ||
118 | |||
119 | static unsigned long __init SMCDetectUltraIO(void) | ||
120 | { | ||
121 | unsigned long baseAddr; | ||
122 | |||
123 | baseAddr = 0x3F0; | ||
124 | if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) { | ||
125 | return( baseAddr ); | ||
126 | } | ||
127 | baseAddr = 0x370; | ||
128 | if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) { | ||
129 | return( baseAddr ); | ||
130 | } | ||
131 | return( ( unsigned long )0 ); | ||
132 | } | ||
133 | |||
134 | static void __init SMCEnableDevice(unsigned long baseAddr, | ||
135 | unsigned long device, | ||
136 | unsigned long portaddr, | ||
137 | unsigned long interrupt) | ||
138 | { | ||
139 | unsigned long indexPort; | ||
140 | unsigned long dataPort; | ||
141 | |||
142 | indexPort = baseAddr; | ||
143 | dataPort = baseAddr + 1; | ||
144 | |||
145 | outb(LOGICAL_DEVICE_NUMBER, indexPort); | ||
146 | outb(device, dataPort); | ||
147 | |||
148 | outb(ADDR_LO, indexPort); | ||
149 | outb(( portaddr & 0xFF ), dataPort); | ||
150 | |||
151 | outb(ADDR_HI, indexPort); | ||
152 | outb((portaddr >> 8) & 0xFF, dataPort); | ||
153 | |||
154 | outb(INTERRUPT_SEL, indexPort); | ||
155 | outb(interrupt, dataPort); | ||
156 | |||
157 | outb(ACTIVATE, indexPort); | ||
158 | outb(DEVICE_ON, dataPort); | ||
159 | } | ||
160 | |||
161 | static void __init SMCEnableKYBD(unsigned long baseAddr) | ||
162 | { | ||
163 | unsigned long indexPort; | ||
164 | unsigned long dataPort; | ||
165 | |||
166 | indexPort = baseAddr; | ||
167 | dataPort = baseAddr + 1; | ||
168 | |||
169 | outb(LOGICAL_DEVICE_NUMBER, indexPort); | ||
170 | outb(KYBD, dataPort); | ||
171 | |||
172 | outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ | ||
173 | outb(KYBD_INTERRUPT, dataPort); | ||
174 | |||
175 | outb(INTERRUPT_SEL_2, indexPort); /* Secondary interrupt select */ | ||
176 | outb(MOUS_INTERRUPT, dataPort); | ||
177 | |||
178 | outb(ACTIVATE, indexPort); | ||
179 | outb(DEVICE_ON, dataPort); | ||
180 | } | ||
181 | |||
182 | static void __init SMCEnableFDC(unsigned long baseAddr) | ||
183 | { | ||
184 | unsigned long indexPort; | ||
185 | unsigned long dataPort; | ||
186 | |||
187 | unsigned char oldValue; | ||
188 | |||
189 | indexPort = baseAddr; | ||
190 | dataPort = baseAddr + 1; | ||
191 | |||
192 | outb(LOGICAL_DEVICE_NUMBER, indexPort); | ||
193 | outb(FDC, dataPort); | ||
194 | |||
195 | outb(FDD_MODE_REGISTER, indexPort); | ||
196 | oldValue = inb(dataPort); | ||
197 | |||
198 | oldValue |= 0x0E; /* Enable burst mode */ | ||
199 | outb(oldValue, dataPort); | ||
200 | |||
201 | outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ | ||
202 | outb(0x06, dataPort ); | ||
203 | |||
204 | outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */ | ||
205 | outb(0x02, dataPort); | ||
206 | |||
207 | outb(ACTIVATE, indexPort); | ||
208 | outb(DEVICE_ON, dataPort); | ||
209 | } | ||
210 | |||
211 | #if SMC_DEBUG | ||
212 | static void __init SMCReportDeviceStatus(unsigned long baseAddr) | ||
213 | { | ||
214 | unsigned long indexPort; | ||
215 | unsigned long dataPort; | ||
216 | unsigned char currentControl; | ||
217 | |||
218 | indexPort = baseAddr; | ||
219 | dataPort = baseAddr + 1; | ||
220 | |||
221 | outb(POWER_CONTROL, indexPort); | ||
222 | currentControl = inb(dataPort); | ||
223 | |||
224 | printk(currentControl & (1 << FDC) | ||
225 | ? "\t+FDC Enabled\n" : "\t-FDC Disabled\n"); | ||
226 | printk(currentControl & (1 << IDE1) | ||
227 | ? "\t+IDE1 Enabled\n" : "\t-IDE1 Disabled\n"); | ||
228 | printk(currentControl & (1 << IDE2) | ||
229 | ? "\t+IDE2 Enabled\n" : "\t-IDE2 Disabled\n"); | ||
230 | printk(currentControl & (1 << PARP) | ||
231 | ? "\t+PARP Enabled\n" : "\t-PARP Disabled\n"); | ||
232 | printk(currentControl & (1 << SER1) | ||
233 | ? "\t+SER1 Enabled\n" : "\t-SER1 Disabled\n"); | ||
234 | printk(currentControl & (1 << SER2) | ||
235 | ? "\t+SER2 Enabled\n" : "\t-SER2 Disabled\n"); | ||
236 | |||
237 | printk( "\n" ); | ||
238 | } | ||
239 | #endif | ||
240 | |||
241 | int __init SMC93x_Init(void) | ||
242 | { | ||
243 | unsigned long SMCUltraBase; | ||
244 | unsigned long flags; | ||
245 | |||
246 | local_irq_save(flags); | ||
247 | if ((SMCUltraBase = SMCDetectUltraIO()) != 0UL) { | ||
248 | #if SMC_DEBUG | ||
249 | SMCReportDeviceStatus(SMCUltraBase); | ||
250 | #endif | ||
251 | SMCEnableDevice(SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT); | ||
252 | DBG_DEVS(("SMC FDC37C93X: SER1 done\n")); | ||
253 | SMCEnableDevice(SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT); | ||
254 | DBG_DEVS(("SMC FDC37C93X: SER2 done\n")); | ||
255 | SMCEnableDevice(SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT); | ||
256 | DBG_DEVS(("SMC FDC37C93X: PARP done\n")); | ||
257 | /* On PC164, IDE on the SMC is not enabled; | ||
258 | CMD646 (PCI) on MB */ | ||
259 | SMCEnableKYBD(SMCUltraBase); | ||
260 | DBG_DEVS(("SMC FDC37C93X: KYB done\n")); | ||
261 | SMCEnableFDC(SMCUltraBase); | ||
262 | DBG_DEVS(("SMC FDC37C93X: FDC done\n")); | ||
263 | #if SMC_DEBUG | ||
264 | SMCReportDeviceStatus(SMCUltraBase); | ||
265 | #endif | ||
266 | SMCRunState(SMCUltraBase); | ||
267 | local_irq_restore(flags); | ||
268 | printk("SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n", | ||
269 | SMCUltraBase); | ||
270 | return 1; | ||
271 | } | ||
272 | else { | ||
273 | local_irq_restore(flags); | ||
274 | DBG_DEVS(("No SMC FDC37C93X Ultra I/O Controller found\n")); | ||
275 | return 0; | ||
276 | } | ||
277 | } | ||
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c new file mode 100644 index 000000000000..8f1e78551b1e --- /dev/null +++ b/arch/alpha/kernel/smp.c | |||
@@ -0,0 +1,1163 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/smp.c | ||
3 | * | ||
4 | * 2001-07-09 Phil Ezolt (Phillip.Ezolt@compaq.com) | ||
5 | * Renamed modified smp_call_function to smp_call_function_on_cpu() | ||
6 | * Created an function that conforms to the old calling convention | ||
7 | * of smp_call_function(). | ||
8 | * | ||
9 | * This is helpful for DCPI. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/kernel_stat.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/threads.h> | ||
20 | #include <linux/smp.h> | ||
21 | #include <linux/smp_lock.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/cache.h> | ||
28 | #include <linux/profile.h> | ||
29 | #include <linux/bitops.h> | ||
30 | |||
31 | #include <asm/hwrpb.h> | ||
32 | #include <asm/ptrace.h> | ||
33 | #include <asm/atomic.h> | ||
34 | |||
35 | #include <asm/io.h> | ||
36 | #include <asm/irq.h> | ||
37 | #include <asm/pgtable.h> | ||
38 | #include <asm/pgalloc.h> | ||
39 | #include <asm/mmu_context.h> | ||
40 | #include <asm/tlbflush.h> | ||
41 | |||
42 | #include "proto.h" | ||
43 | #include "irq_impl.h" | ||
44 | |||
45 | |||
46 | #define DEBUG_SMP 0 | ||
47 | #if DEBUG_SMP | ||
48 | #define DBGS(args) printk args | ||
49 | #else | ||
50 | #define DBGS(args) | ||
51 | #endif | ||
52 | |||
53 | /* A collection of per-processor data. */ | ||
54 | struct cpuinfo_alpha cpu_data[NR_CPUS]; | ||
55 | |||
56 | /* A collection of single bit ipi messages. */ | ||
57 | static struct { | ||
58 | unsigned long bits ____cacheline_aligned; | ||
59 | } ipi_data[NR_CPUS] __cacheline_aligned; | ||
60 | |||
61 | enum ipi_message_type { | ||
62 | IPI_RESCHEDULE, | ||
63 | IPI_CALL_FUNC, | ||
64 | IPI_CPU_STOP, | ||
65 | }; | ||
66 | |||
67 | /* Set to a secondary's cpuid when it comes online. */ | ||
68 | static int smp_secondary_alive __initdata = 0; | ||
69 | |||
70 | /* Which cpus ids came online. */ | ||
71 | cpumask_t cpu_present_mask; | ||
72 | cpumask_t cpu_online_map; | ||
73 | |||
74 | EXPORT_SYMBOL(cpu_online_map); | ||
75 | |||
76 | /* cpus reported in the hwrpb */ | ||
77 | static unsigned long hwrpb_cpu_present_mask __initdata = 0; | ||
78 | |||
79 | int smp_num_probed; /* Internal processor count */ | ||
80 | int smp_num_cpus = 1; /* Number that came online. */ | ||
81 | |||
82 | extern void calibrate_delay(void); | ||
83 | |||
84 | |||
85 | |||
86 | /* | ||
87 | * Called by both boot and secondaries to move global data into | ||
88 | * per-processor storage. | ||
89 | */ | ||
90 | static inline void __init | ||
91 | smp_store_cpu_info(int cpuid) | ||
92 | { | ||
93 | cpu_data[cpuid].loops_per_jiffy = loops_per_jiffy; | ||
94 | cpu_data[cpuid].last_asn = ASN_FIRST_VERSION; | ||
95 | cpu_data[cpuid].need_new_asn = 0; | ||
96 | cpu_data[cpuid].asn_lock = 0; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Ideally sets up per-cpu profiling hooks. Doesn't do much now... | ||
101 | */ | ||
102 | static inline void __init | ||
103 | smp_setup_percpu_timer(int cpuid) | ||
104 | { | ||
105 | cpu_data[cpuid].prof_counter = 1; | ||
106 | cpu_data[cpuid].prof_multiplier = 1; | ||
107 | } | ||
108 | |||
109 | static void __init | ||
110 | wait_boot_cpu_to_stop(int cpuid) | ||
111 | { | ||
112 | unsigned long stop = jiffies + 10*HZ; | ||
113 | |||
114 | while (time_before(jiffies, stop)) { | ||
115 | if (!smp_secondary_alive) | ||
116 | return; | ||
117 | barrier(); | ||
118 | } | ||
119 | |||
120 | printk("wait_boot_cpu_to_stop: FAILED on CPU %d, hanging now\n", cpuid); | ||
121 | for (;;) | ||
122 | barrier(); | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * Where secondaries begin a life of C. | ||
127 | */ | ||
128 | void __init | ||
129 | smp_callin(void) | ||
130 | { | ||
131 | int cpuid = hard_smp_processor_id(); | ||
132 | |||
133 | if (cpu_test_and_set(cpuid, cpu_online_map)) { | ||
134 | printk("??, cpu 0x%x already present??\n", cpuid); | ||
135 | BUG(); | ||
136 | } | ||
137 | |||
138 | /* Turn on machine checks. */ | ||
139 | wrmces(7); | ||
140 | |||
141 | /* Set trap vectors. */ | ||
142 | trap_init(); | ||
143 | |||
144 | /* Set interrupt vector. */ | ||
145 | wrent(entInt, 0); | ||
146 | |||
147 | /* Get our local ticker going. */ | ||
148 | smp_setup_percpu_timer(cpuid); | ||
149 | |||
150 | /* Call platform-specific callin, if specified */ | ||
151 | if (alpha_mv.smp_callin) alpha_mv.smp_callin(); | ||
152 | |||
153 | /* All kernel threads share the same mm context. */ | ||
154 | atomic_inc(&init_mm.mm_count); | ||
155 | current->active_mm = &init_mm; | ||
156 | |||
157 | /* Must have completely accurate bogos. */ | ||
158 | local_irq_enable(); | ||
159 | |||
160 | /* Wait boot CPU to stop with irq enabled before running | ||
161 | calibrate_delay. */ | ||
162 | wait_boot_cpu_to_stop(cpuid); | ||
163 | mb(); | ||
164 | calibrate_delay(); | ||
165 | |||
166 | smp_store_cpu_info(cpuid); | ||
167 | /* Allow master to continue only after we written loops_per_jiffy. */ | ||
168 | wmb(); | ||
169 | smp_secondary_alive = 1; | ||
170 | |||
171 | DBGS(("smp_callin: commencing CPU %d current %p active_mm %p\n", | ||
172 | cpuid, current, current->active_mm)); | ||
173 | |||
174 | /* Do nothing. */ | ||
175 | cpu_idle(); | ||
176 | } | ||
177 | |||
178 | /* Wait until hwrpb->txrdy is clear for cpu. Return -1 on timeout. */ | ||
179 | static int __init | ||
180 | wait_for_txrdy (unsigned long cpumask) | ||
181 | { | ||
182 | unsigned long timeout; | ||
183 | |||
184 | if (!(hwrpb->txrdy & cpumask)) | ||
185 | return 0; | ||
186 | |||
187 | timeout = jiffies + 10*HZ; | ||
188 | while (time_before(jiffies, timeout)) { | ||
189 | if (!(hwrpb->txrdy & cpumask)) | ||
190 | return 0; | ||
191 | udelay(10); | ||
192 | barrier(); | ||
193 | } | ||
194 | |||
195 | return -1; | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * Send a message to a secondary's console. "START" is one such | ||
200 | * interesting message. ;-) | ||
201 | */ | ||
202 | static void __init | ||
203 | send_secondary_console_msg(char *str, int cpuid) | ||
204 | { | ||
205 | struct percpu_struct *cpu; | ||
206 | register char *cp1, *cp2; | ||
207 | unsigned long cpumask; | ||
208 | size_t len; | ||
209 | |||
210 | cpu = (struct percpu_struct *) | ||
211 | ((char*)hwrpb | ||
212 | + hwrpb->processor_offset | ||
213 | + cpuid * hwrpb->processor_size); | ||
214 | |||
215 | cpumask = (1UL << cpuid); | ||
216 | if (wait_for_txrdy(cpumask)) | ||
217 | goto timeout; | ||
218 | |||
219 | cp2 = str; | ||
220 | len = strlen(cp2); | ||
221 | *(unsigned int *)&cpu->ipc_buffer[0] = len; | ||
222 | cp1 = (char *) &cpu->ipc_buffer[1]; | ||
223 | memcpy(cp1, cp2, len); | ||
224 | |||
225 | /* atomic test and set */ | ||
226 | wmb(); | ||
227 | set_bit(cpuid, &hwrpb->rxrdy); | ||
228 | |||
229 | if (wait_for_txrdy(cpumask)) | ||
230 | goto timeout; | ||
231 | return; | ||
232 | |||
233 | timeout: | ||
234 | printk("Processor %x not ready\n", cpuid); | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * A secondary console wants to send a message. Receive it. | ||
239 | */ | ||
240 | static void | ||
241 | recv_secondary_console_msg(void) | ||
242 | { | ||
243 | int mycpu, i, cnt; | ||
244 | unsigned long txrdy = hwrpb->txrdy; | ||
245 | char *cp1, *cp2, buf[80]; | ||
246 | struct percpu_struct *cpu; | ||
247 | |||
248 | DBGS(("recv_secondary_console_msg: TXRDY 0x%lx.\n", txrdy)); | ||
249 | |||
250 | mycpu = hard_smp_processor_id(); | ||
251 | |||
252 | for (i = 0; i < NR_CPUS; i++) { | ||
253 | if (!(txrdy & (1UL << i))) | ||
254 | continue; | ||
255 | |||
256 | DBGS(("recv_secondary_console_msg: " | ||
257 | "TXRDY contains CPU %d.\n", i)); | ||
258 | |||
259 | cpu = (struct percpu_struct *) | ||
260 | ((char*)hwrpb | ||
261 | + hwrpb->processor_offset | ||
262 | + i * hwrpb->processor_size); | ||
263 | |||
264 | DBGS(("recv_secondary_console_msg: on %d from %d" | ||
265 | " HALT_REASON 0x%lx FLAGS 0x%lx\n", | ||
266 | mycpu, i, cpu->halt_reason, cpu->flags)); | ||
267 | |||
268 | cnt = cpu->ipc_buffer[0] >> 32; | ||
269 | if (cnt <= 0 || cnt >= 80) | ||
270 | strcpy(buf, "<<< BOGUS MSG >>>"); | ||
271 | else { | ||
272 | cp1 = (char *) &cpu->ipc_buffer[11]; | ||
273 | cp2 = buf; | ||
274 | strcpy(cp2, cp1); | ||
275 | |||
276 | while ((cp2 = strchr(cp2, '\r')) != 0) { | ||
277 | *cp2 = ' '; | ||
278 | if (cp2[1] == '\n') | ||
279 | cp2[1] = ' '; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | DBGS((KERN_INFO "recv_secondary_console_msg: on %d " | ||
284 | "message is '%s'\n", mycpu, buf)); | ||
285 | } | ||
286 | |||
287 | hwrpb->txrdy = 0; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Convince the console to have a secondary cpu begin execution. | ||
292 | */ | ||
293 | static int __init | ||
294 | secondary_cpu_start(int cpuid, struct task_struct *idle) | ||
295 | { | ||
296 | struct percpu_struct *cpu; | ||
297 | struct pcb_struct *hwpcb, *ipcb; | ||
298 | unsigned long timeout; | ||
299 | |||
300 | cpu = (struct percpu_struct *) | ||
301 | ((char*)hwrpb | ||
302 | + hwrpb->processor_offset | ||
303 | + cpuid * hwrpb->processor_size); | ||
304 | hwpcb = (struct pcb_struct *) cpu->hwpcb; | ||
305 | ipcb = &idle->thread_info->pcb; | ||
306 | |||
307 | /* Initialize the CPU's HWPCB to something just good enough for | ||
308 | us to get started. Immediately after starting, we'll swpctx | ||
309 | to the target idle task's pcb. Reuse the stack in the mean | ||
310 | time. Precalculate the target PCBB. */ | ||
311 | hwpcb->ksp = (unsigned long)ipcb + sizeof(union thread_union) - 16; | ||
312 | hwpcb->usp = 0; | ||
313 | hwpcb->ptbr = ipcb->ptbr; | ||
314 | hwpcb->pcc = 0; | ||
315 | hwpcb->asn = 0; | ||
316 | hwpcb->unique = virt_to_phys(ipcb); | ||
317 | hwpcb->flags = ipcb->flags; | ||
318 | hwpcb->res1 = hwpcb->res2 = 0; | ||
319 | |||
320 | #if 0 | ||
321 | DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx UNIQUE 0x%lx\n", | ||
322 | hwpcb->ksp, hwpcb->ptbr, hwrpb->vptb, hwpcb->unique)); | ||
323 | #endif | ||
324 | DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", | ||
325 | cpuid, idle->state, ipcb->flags)); | ||
326 | |||
327 | /* Setup HWRPB fields that SRM uses to activate secondary CPU */ | ||
328 | hwrpb->CPU_restart = __smp_callin; | ||
329 | hwrpb->CPU_restart_data = (unsigned long) __smp_callin; | ||
330 | |||
331 | /* Recalculate and update the HWRPB checksum */ | ||
332 | hwrpb_update_checksum(hwrpb); | ||
333 | |||
334 | /* | ||
335 | * Send a "start" command to the specified processor. | ||
336 | */ | ||
337 | |||
338 | /* SRM III 3.4.1.3 */ | ||
339 | cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */ | ||
340 | cpu->flags &= ~1; /* turn off Bootstrap In Progress */ | ||
341 | wmb(); | ||
342 | |||
343 | send_secondary_console_msg("START\r\n", cpuid); | ||
344 | |||
345 | /* Wait 10 seconds for an ACK from the console. */ | ||
346 | timeout = jiffies + 10*HZ; | ||
347 | while (time_before(jiffies, timeout)) { | ||
348 | if (cpu->flags & 1) | ||
349 | goto started; | ||
350 | udelay(10); | ||
351 | barrier(); | ||
352 | } | ||
353 | printk(KERN_ERR "SMP: Processor %d failed to start.\n", cpuid); | ||
354 | return -1; | ||
355 | |||
356 | started: | ||
357 | DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * Bring one cpu online. | ||
363 | */ | ||
364 | static int __init | ||
365 | smp_boot_one_cpu(int cpuid) | ||
366 | { | ||
367 | struct task_struct *idle; | ||
368 | unsigned long timeout; | ||
369 | |||
370 | /* Cook up an idler for this guy. Note that the address we | ||
371 | give to kernel_thread is irrelevant -- it's going to start | ||
372 | where HWRPB.CPU_restart says to start. But this gets all | ||
373 | the other task-y sort of data structures set up like we | ||
374 | wish. We can't use kernel_thread since we must avoid | ||
375 | rescheduling the child. */ | ||
376 | idle = fork_idle(cpuid); | ||
377 | if (IS_ERR(idle)) | ||
378 | panic("failed fork for CPU %d", cpuid); | ||
379 | |||
380 | DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n", | ||
381 | cpuid, idle->state, idle->flags)); | ||
382 | |||
383 | /* Signal the secondary to wait a moment. */ | ||
384 | smp_secondary_alive = -1; | ||
385 | |||
386 | /* Whirrr, whirrr, whirrrrrrrrr... */ | ||
387 | if (secondary_cpu_start(cpuid, idle)) | ||
388 | return -1; | ||
389 | |||
390 | /* Notify the secondary CPU it can run calibrate_delay. */ | ||
391 | mb(); | ||
392 | smp_secondary_alive = 0; | ||
393 | |||
394 | /* We've been acked by the console; wait one second for | ||
395 | the task to start up for real. */ | ||
396 | timeout = jiffies + 1*HZ; | ||
397 | while (time_before(jiffies, timeout)) { | ||
398 | if (smp_secondary_alive == 1) | ||
399 | goto alive; | ||
400 | udelay(10); | ||
401 | barrier(); | ||
402 | } | ||
403 | |||
404 | /* We failed to boot the CPU. */ | ||
405 | |||
406 | printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid); | ||
407 | return -1; | ||
408 | |||
409 | alive: | ||
410 | /* Another "Red Snapper". */ | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * Called from setup_arch. Detect an SMP system and which processors | ||
416 | * are present. | ||
417 | */ | ||
418 | void __init | ||
419 | setup_smp(void) | ||
420 | { | ||
421 | struct percpu_struct *cpubase, *cpu; | ||
422 | unsigned long i; | ||
423 | |||
424 | if (boot_cpuid != 0) { | ||
425 | printk(KERN_WARNING "SMP: Booting off cpu %d instead of 0?\n", | ||
426 | boot_cpuid); | ||
427 | } | ||
428 | |||
429 | if (hwrpb->nr_processors > 1) { | ||
430 | int boot_cpu_palrev; | ||
431 | |||
432 | DBGS(("setup_smp: nr_processors %ld\n", | ||
433 | hwrpb->nr_processors)); | ||
434 | |||
435 | cpubase = (struct percpu_struct *) | ||
436 | ((char*)hwrpb + hwrpb->processor_offset); | ||
437 | boot_cpu_palrev = cpubase->pal_revision; | ||
438 | |||
439 | for (i = 0; i < hwrpb->nr_processors; i++) { | ||
440 | cpu = (struct percpu_struct *) | ||
441 | ((char *)cpubase + i*hwrpb->processor_size); | ||
442 | if ((cpu->flags & 0x1cc) == 0x1cc) { | ||
443 | smp_num_probed++; | ||
444 | /* Assume here that "whami" == index */ | ||
445 | hwrpb_cpu_present_mask |= (1UL << i); | ||
446 | cpu->pal_revision = boot_cpu_palrev; | ||
447 | } | ||
448 | |||
449 | DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", | ||
450 | i, cpu->flags, cpu->type)); | ||
451 | DBGS(("setup_smp: CPU %d: PAL rev 0x%lx\n", | ||
452 | i, cpu->pal_revision)); | ||
453 | } | ||
454 | } else { | ||
455 | smp_num_probed = 1; | ||
456 | hwrpb_cpu_present_mask = (1UL << boot_cpuid); | ||
457 | } | ||
458 | cpu_present_mask = cpumask_of_cpu(boot_cpuid); | ||
459 | |||
460 | printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_mask = %lx\n", | ||
461 | smp_num_probed, hwrpb_cpu_present_mask); | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * Called by smp_init prepare the secondaries | ||
466 | */ | ||
467 | void __init | ||
468 | smp_prepare_cpus(unsigned int max_cpus) | ||
469 | { | ||
470 | int cpu_count, i; | ||
471 | |||
472 | /* Take care of some initial bookkeeping. */ | ||
473 | memset(ipi_data, 0, sizeof(ipi_data)); | ||
474 | |||
475 | current_thread_info()->cpu = boot_cpuid; | ||
476 | |||
477 | smp_store_cpu_info(boot_cpuid); | ||
478 | smp_setup_percpu_timer(boot_cpuid); | ||
479 | |||
480 | /* Nothing to do on a UP box, or when told not to. */ | ||
481 | if (smp_num_probed == 1 || max_cpus == 0) { | ||
482 | cpu_present_mask = cpumask_of_cpu(boot_cpuid); | ||
483 | printk(KERN_INFO "SMP mode deactivated.\n"); | ||
484 | return; | ||
485 | } | ||
486 | |||
487 | printk(KERN_INFO "SMP starting up secondaries.\n"); | ||
488 | |||
489 | cpu_count = 1; | ||
490 | for (i = 0; (i < NR_CPUS) && (cpu_count < max_cpus); i++) { | ||
491 | if (i == boot_cpuid) | ||
492 | continue; | ||
493 | |||
494 | if (((hwrpb_cpu_present_mask >> i) & 1) == 0) | ||
495 | continue; | ||
496 | |||
497 | cpu_set(i, cpu_possible_map); | ||
498 | cpu_count++; | ||
499 | } | ||
500 | |||
501 | smp_num_cpus = cpu_count; | ||
502 | } | ||
503 | |||
504 | void __devinit | ||
505 | smp_prepare_boot_cpu(void) | ||
506 | { | ||
507 | /* | ||
508 | * Mark the boot cpu (current cpu) as both present and online | ||
509 | */ | ||
510 | cpu_set(smp_processor_id(), cpu_present_mask); | ||
511 | cpu_set(smp_processor_id(), cpu_online_map); | ||
512 | } | ||
513 | |||
514 | int __devinit | ||
515 | __cpu_up(unsigned int cpu) | ||
516 | { | ||
517 | smp_boot_one_cpu(cpu); | ||
518 | |||
519 | return cpu_online(cpu) ? 0 : -ENOSYS; | ||
520 | } | ||
521 | |||
522 | void __init | ||
523 | smp_cpus_done(unsigned int max_cpus) | ||
524 | { | ||
525 | int cpu; | ||
526 | unsigned long bogosum = 0; | ||
527 | |||
528 | for(cpu = 0; cpu < NR_CPUS; cpu++) | ||
529 | if (cpu_online(cpu)) | ||
530 | bogosum += cpu_data[cpu].loops_per_jiffy; | ||
531 | |||
532 | printk(KERN_INFO "SMP: Total of %d processors activated " | ||
533 | "(%lu.%02lu BogoMIPS).\n", | ||
534 | num_online_cpus(), | ||
535 | (bogosum + 2500) / (500000/HZ), | ||
536 | ((bogosum + 2500) / (5000/HZ)) % 100); | ||
537 | } | ||
538 | |||
539 | |||
540 | void | ||
541 | smp_percpu_timer_interrupt(struct pt_regs *regs) | ||
542 | { | ||
543 | int cpu = smp_processor_id(); | ||
544 | unsigned long user = user_mode(regs); | ||
545 | struct cpuinfo_alpha *data = &cpu_data[cpu]; | ||
546 | |||
547 | /* Record kernel PC. */ | ||
548 | profile_tick(CPU_PROFILING, regs); | ||
549 | |||
550 | if (!--data->prof_counter) { | ||
551 | /* We need to make like a normal interrupt -- otherwise | ||
552 | timer interrupts ignore the global interrupt lock, | ||
553 | which would be a Bad Thing. */ | ||
554 | irq_enter(); | ||
555 | |||
556 | update_process_times(user); | ||
557 | |||
558 | data->prof_counter = data->prof_multiplier; | ||
559 | |||
560 | irq_exit(); | ||
561 | } | ||
562 | } | ||
563 | |||
564 | int __init | ||
565 | setup_profiling_timer(unsigned int multiplier) | ||
566 | { | ||
567 | return -EINVAL; | ||
568 | } | ||
569 | |||
570 | |||
571 | static void | ||
572 | send_ipi_message(cpumask_t to_whom, enum ipi_message_type operation) | ||
573 | { | ||
574 | int i; | ||
575 | |||
576 | mb(); | ||
577 | for_each_cpu_mask(i, to_whom) | ||
578 | set_bit(operation, &ipi_data[i].bits); | ||
579 | |||
580 | mb(); | ||
581 | for_each_cpu_mask(i, to_whom) | ||
582 | wripir(i); | ||
583 | } | ||
584 | |||
585 | /* Structure and data for smp_call_function. This is designed to | ||
586 | minimize static memory requirements. Plus it looks cleaner. */ | ||
587 | |||
588 | struct smp_call_struct { | ||
589 | void (*func) (void *info); | ||
590 | void *info; | ||
591 | long wait; | ||
592 | atomic_t unstarted_count; | ||
593 | atomic_t unfinished_count; | ||
594 | }; | ||
595 | |||
596 | static struct smp_call_struct *smp_call_function_data; | ||
597 | |||
598 | /* Atomicly drop data into a shared pointer. The pointer is free if | ||
599 | it is initially locked. If retry, spin until free. */ | ||
600 | |||
601 | static int | ||
602 | pointer_lock (void *lock, void *data, int retry) | ||
603 | { | ||
604 | void *old, *tmp; | ||
605 | |||
606 | mb(); | ||
607 | again: | ||
608 | /* Compare and swap with zero. */ | ||
609 | asm volatile ( | ||
610 | "1: ldq_l %0,%1\n" | ||
611 | " mov %3,%2\n" | ||
612 | " bne %0,2f\n" | ||
613 | " stq_c %2,%1\n" | ||
614 | " beq %2,1b\n" | ||
615 | "2:" | ||
616 | : "=&r"(old), "=m"(*(void **)lock), "=&r"(tmp) | ||
617 | : "r"(data) | ||
618 | : "memory"); | ||
619 | |||
620 | if (old == 0) | ||
621 | return 0; | ||
622 | if (! retry) | ||
623 | return -EBUSY; | ||
624 | |||
625 | while (*(void **)lock) | ||
626 | barrier(); | ||
627 | goto again; | ||
628 | } | ||
629 | |||
630 | void | ||
631 | handle_ipi(struct pt_regs *regs) | ||
632 | { | ||
633 | int this_cpu = smp_processor_id(); | ||
634 | unsigned long *pending_ipis = &ipi_data[this_cpu].bits; | ||
635 | unsigned long ops; | ||
636 | |||
637 | #if 0 | ||
638 | DBGS(("handle_ipi: on CPU %d ops 0x%lx PC 0x%lx\n", | ||
639 | this_cpu, *pending_ipis, regs->pc)); | ||
640 | #endif | ||
641 | |||
642 | mb(); /* Order interrupt and bit testing. */ | ||
643 | while ((ops = xchg(pending_ipis, 0)) != 0) { | ||
644 | mb(); /* Order bit clearing and data access. */ | ||
645 | do { | ||
646 | unsigned long which; | ||
647 | |||
648 | which = ops & -ops; | ||
649 | ops &= ~which; | ||
650 | which = __ffs(which); | ||
651 | |||
652 | switch (which) { | ||
653 | case IPI_RESCHEDULE: | ||
654 | /* Reschedule callback. Everything to be done | ||
655 | is done by the interrupt return path. */ | ||
656 | break; | ||
657 | |||
658 | case IPI_CALL_FUNC: | ||
659 | { | ||
660 | struct smp_call_struct *data; | ||
661 | void (*func)(void *info); | ||
662 | void *info; | ||
663 | int wait; | ||
664 | |||
665 | data = smp_call_function_data; | ||
666 | func = data->func; | ||
667 | info = data->info; | ||
668 | wait = data->wait; | ||
669 | |||
670 | /* Notify the sending CPU that the data has been | ||
671 | received, and execution is about to begin. */ | ||
672 | mb(); | ||
673 | atomic_dec (&data->unstarted_count); | ||
674 | |||
675 | /* At this point the structure may be gone unless | ||
676 | wait is true. */ | ||
677 | (*func)(info); | ||
678 | |||
679 | /* Notify the sending CPU that the task is done. */ | ||
680 | mb(); | ||
681 | if (wait) atomic_dec (&data->unfinished_count); | ||
682 | break; | ||
683 | } | ||
684 | |||
685 | case IPI_CPU_STOP: | ||
686 | halt(); | ||
687 | |||
688 | default: | ||
689 | printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", | ||
690 | this_cpu, which); | ||
691 | break; | ||
692 | } | ||
693 | } while (ops); | ||
694 | |||
695 | mb(); /* Order data access and bit testing. */ | ||
696 | } | ||
697 | |||
698 | cpu_data[this_cpu].ipi_count++; | ||
699 | |||
700 | if (hwrpb->txrdy) | ||
701 | recv_secondary_console_msg(); | ||
702 | } | ||
703 | |||
704 | void | ||
705 | smp_send_reschedule(int cpu) | ||
706 | { | ||
707 | #ifdef DEBUG_IPI_MSG | ||
708 | if (cpu == hard_smp_processor_id()) | ||
709 | printk(KERN_WARNING | ||
710 | "smp_send_reschedule: Sending IPI to self.\n"); | ||
711 | #endif | ||
712 | send_ipi_message(cpumask_of_cpu(cpu), IPI_RESCHEDULE); | ||
713 | } | ||
714 | |||
715 | void | ||
716 | smp_send_stop(void) | ||
717 | { | ||
718 | cpumask_t to_whom = cpu_possible_map; | ||
719 | cpu_clear(smp_processor_id(), to_whom); | ||
720 | #ifdef DEBUG_IPI_MSG | ||
721 | if (hard_smp_processor_id() != boot_cpu_id) | ||
722 | printk(KERN_WARNING "smp_send_stop: Not on boot cpu.\n"); | ||
723 | #endif | ||
724 | send_ipi_message(to_whom, IPI_CPU_STOP); | ||
725 | } | ||
726 | |||
727 | /* | ||
728 | * Run a function on all other CPUs. | ||
729 | * <func> The function to run. This must be fast and non-blocking. | ||
730 | * <info> An arbitrary pointer to pass to the function. | ||
731 | * <retry> If true, keep retrying until ready. | ||
732 | * <wait> If true, wait until function has completed on other CPUs. | ||
733 | * [RETURNS] 0 on success, else a negative status code. | ||
734 | * | ||
735 | * Does not return until remote CPUs are nearly ready to execute <func> | ||
736 | * or are or have executed. | ||
737 | * You must not call this function with disabled interrupts or from a | ||
738 | * hardware interrupt handler or from a bottom half handler. | ||
739 | */ | ||
740 | |||
741 | int | ||
742 | smp_call_function_on_cpu (void (*func) (void *info), void *info, int retry, | ||
743 | int wait, cpumask_t to_whom) | ||
744 | { | ||
745 | struct smp_call_struct data; | ||
746 | unsigned long timeout; | ||
747 | int num_cpus_to_call; | ||
748 | |||
749 | /* Can deadlock when called with interrupts disabled */ | ||
750 | WARN_ON(irqs_disabled()); | ||
751 | |||
752 | data.func = func; | ||
753 | data.info = info; | ||
754 | data.wait = wait; | ||
755 | |||
756 | cpu_clear(smp_processor_id(), to_whom); | ||
757 | num_cpus_to_call = cpus_weight(to_whom); | ||
758 | |||
759 | atomic_set(&data.unstarted_count, num_cpus_to_call); | ||
760 | atomic_set(&data.unfinished_count, num_cpus_to_call); | ||
761 | |||
762 | /* Acquire the smp_call_function_data mutex. */ | ||
763 | if (pointer_lock(&smp_call_function_data, &data, retry)) | ||
764 | return -EBUSY; | ||
765 | |||
766 | /* Send a message to the requested CPUs. */ | ||
767 | send_ipi_message(to_whom, IPI_CALL_FUNC); | ||
768 | |||
769 | /* Wait for a minimal response. */ | ||
770 | timeout = jiffies + HZ; | ||
771 | while (atomic_read (&data.unstarted_count) > 0 | ||
772 | && time_before (jiffies, timeout)) | ||
773 | barrier(); | ||
774 | |||
775 | /* If there's no response yet, log a message but allow a longer | ||
776 | * timeout period -- if we get a response this time, log | ||
777 | * a message saying when we got it.. | ||
778 | */ | ||
779 | if (atomic_read(&data.unstarted_count) > 0) { | ||
780 | long start_time = jiffies; | ||
781 | printk(KERN_ERR "%s: initial timeout -- trying long wait\n", | ||
782 | __FUNCTION__); | ||
783 | timeout = jiffies + 30 * HZ; | ||
784 | while (atomic_read(&data.unstarted_count) > 0 | ||
785 | && time_before(jiffies, timeout)) | ||
786 | barrier(); | ||
787 | if (atomic_read(&data.unstarted_count) <= 0) { | ||
788 | long delta = jiffies - start_time; | ||
789 | printk(KERN_ERR | ||
790 | "%s: response %ld.%ld seconds into long wait\n", | ||
791 | __FUNCTION__, delta / HZ, | ||
792 | (100 * (delta - ((delta / HZ) * HZ))) / HZ); | ||
793 | } | ||
794 | } | ||
795 | |||
796 | /* We either got one or timed out -- clear the lock. */ | ||
797 | mb(); | ||
798 | smp_call_function_data = NULL; | ||
799 | |||
800 | /* | ||
801 | * If after both the initial and long timeout periods we still don't | ||
802 | * have a response, something is very wrong... | ||
803 | */ | ||
804 | BUG_ON(atomic_read (&data.unstarted_count) > 0); | ||
805 | |||
806 | /* Wait for a complete response, if needed. */ | ||
807 | if (wait) { | ||
808 | while (atomic_read (&data.unfinished_count) > 0) | ||
809 | barrier(); | ||
810 | } | ||
811 | |||
812 | return 0; | ||
813 | } | ||
814 | |||
815 | int | ||
816 | smp_call_function (void (*func) (void *info), void *info, int retry, int wait) | ||
817 | { | ||
818 | return smp_call_function_on_cpu (func, info, retry, wait, | ||
819 | cpu_online_map); | ||
820 | } | ||
821 | |||
822 | static void | ||
823 | ipi_imb(void *ignored) | ||
824 | { | ||
825 | imb(); | ||
826 | } | ||
827 | |||
828 | void | ||
829 | smp_imb(void) | ||
830 | { | ||
831 | /* Must wait other processors to flush their icache before continue. */ | ||
832 | if (on_each_cpu(ipi_imb, NULL, 1, 1)) | ||
833 | printk(KERN_CRIT "smp_imb: timed out\n"); | ||
834 | } | ||
835 | |||
836 | static void | ||
837 | ipi_flush_tlb_all(void *ignored) | ||
838 | { | ||
839 | tbia(); | ||
840 | } | ||
841 | |||
842 | void | ||
843 | flush_tlb_all(void) | ||
844 | { | ||
845 | /* Although we don't have any data to pass, we do want to | ||
846 | synchronize with the other processors. */ | ||
847 | if (on_each_cpu(ipi_flush_tlb_all, NULL, 1, 1)) { | ||
848 | printk(KERN_CRIT "flush_tlb_all: timed out\n"); | ||
849 | } | ||
850 | } | ||
851 | |||
852 | #define asn_locked() (cpu_data[smp_processor_id()].asn_lock) | ||
853 | |||
854 | static void | ||
855 | ipi_flush_tlb_mm(void *x) | ||
856 | { | ||
857 | struct mm_struct *mm = (struct mm_struct *) x; | ||
858 | if (mm == current->active_mm && !asn_locked()) | ||
859 | flush_tlb_current(mm); | ||
860 | else | ||
861 | flush_tlb_other(mm); | ||
862 | } | ||
863 | |||
864 | void | ||
865 | flush_tlb_mm(struct mm_struct *mm) | ||
866 | { | ||
867 | preempt_disable(); | ||
868 | |||
869 | if (mm == current->active_mm) { | ||
870 | flush_tlb_current(mm); | ||
871 | if (atomic_read(&mm->mm_users) <= 1) { | ||
872 | int cpu, this_cpu = smp_processor_id(); | ||
873 | for (cpu = 0; cpu < NR_CPUS; cpu++) { | ||
874 | if (!cpu_online(cpu) || cpu == this_cpu) | ||
875 | continue; | ||
876 | if (mm->context[cpu]) | ||
877 | mm->context[cpu] = 0; | ||
878 | } | ||
879 | preempt_enable(); | ||
880 | return; | ||
881 | } | ||
882 | } | ||
883 | |||
884 | if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) { | ||
885 | printk(KERN_CRIT "flush_tlb_mm: timed out\n"); | ||
886 | } | ||
887 | |||
888 | preempt_enable(); | ||
889 | } | ||
890 | |||
891 | struct flush_tlb_page_struct { | ||
892 | struct vm_area_struct *vma; | ||
893 | struct mm_struct *mm; | ||
894 | unsigned long addr; | ||
895 | }; | ||
896 | |||
897 | static void | ||
898 | ipi_flush_tlb_page(void *x) | ||
899 | { | ||
900 | struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x; | ||
901 | struct mm_struct * mm = data->mm; | ||
902 | |||
903 | if (mm == current->active_mm && !asn_locked()) | ||
904 | flush_tlb_current_page(mm, data->vma, data->addr); | ||
905 | else | ||
906 | flush_tlb_other(mm); | ||
907 | } | ||
908 | |||
909 | void | ||
910 | flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) | ||
911 | { | ||
912 | struct flush_tlb_page_struct data; | ||
913 | struct mm_struct *mm = vma->vm_mm; | ||
914 | |||
915 | preempt_disable(); | ||
916 | |||
917 | if (mm == current->active_mm) { | ||
918 | flush_tlb_current_page(mm, vma, addr); | ||
919 | if (atomic_read(&mm->mm_users) <= 1) { | ||
920 | int cpu, this_cpu = smp_processor_id(); | ||
921 | for (cpu = 0; cpu < NR_CPUS; cpu++) { | ||
922 | if (!cpu_online(cpu) || cpu == this_cpu) | ||
923 | continue; | ||
924 | if (mm->context[cpu]) | ||
925 | mm->context[cpu] = 0; | ||
926 | } | ||
927 | preempt_enable(); | ||
928 | return; | ||
929 | } | ||
930 | } | ||
931 | |||
932 | data.vma = vma; | ||
933 | data.mm = mm; | ||
934 | data.addr = addr; | ||
935 | |||
936 | if (smp_call_function(ipi_flush_tlb_page, &data, 1, 1)) { | ||
937 | printk(KERN_CRIT "flush_tlb_page: timed out\n"); | ||
938 | } | ||
939 | |||
940 | preempt_enable(); | ||
941 | } | ||
942 | |||
943 | void | ||
944 | flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) | ||
945 | { | ||
946 | /* On the Alpha we always flush the whole user tlb. */ | ||
947 | flush_tlb_mm(vma->vm_mm); | ||
948 | } | ||
949 | |||
950 | static void | ||
951 | ipi_flush_icache_page(void *x) | ||
952 | { | ||
953 | struct mm_struct *mm = (struct mm_struct *) x; | ||
954 | if (mm == current->active_mm && !asn_locked()) | ||
955 | __load_new_mm_context(mm); | ||
956 | else | ||
957 | flush_tlb_other(mm); | ||
958 | } | ||
959 | |||
960 | void | ||
961 | flush_icache_user_range(struct vm_area_struct *vma, struct page *page, | ||
962 | unsigned long addr, int len) | ||
963 | { | ||
964 | struct mm_struct *mm = vma->vm_mm; | ||
965 | |||
966 | if ((vma->vm_flags & VM_EXEC) == 0) | ||
967 | return; | ||
968 | |||
969 | preempt_disable(); | ||
970 | |||
971 | if (mm == current->active_mm) { | ||
972 | __load_new_mm_context(mm); | ||
973 | if (atomic_read(&mm->mm_users) <= 1) { | ||
974 | int cpu, this_cpu = smp_processor_id(); | ||
975 | for (cpu = 0; cpu < NR_CPUS; cpu++) { | ||
976 | if (!cpu_online(cpu) || cpu == this_cpu) | ||
977 | continue; | ||
978 | if (mm->context[cpu]) | ||
979 | mm->context[cpu] = 0; | ||
980 | } | ||
981 | preempt_enable(); | ||
982 | return; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | if (smp_call_function(ipi_flush_icache_page, mm, 1, 1)) { | ||
987 | printk(KERN_CRIT "flush_icache_page: timed out\n"); | ||
988 | } | ||
989 | |||
990 | preempt_enable(); | ||
991 | } | ||
992 | |||
993 | #ifdef CONFIG_DEBUG_SPINLOCK | ||
994 | void | ||
995 | _raw_spin_unlock(spinlock_t * lock) | ||
996 | { | ||
997 | mb(); | ||
998 | lock->lock = 0; | ||
999 | |||
1000 | lock->on_cpu = -1; | ||
1001 | lock->previous = NULL; | ||
1002 | lock->task = NULL; | ||
1003 | lock->base_file = "none"; | ||
1004 | lock->line_no = 0; | ||
1005 | } | ||
1006 | |||
1007 | void | ||
1008 | debug_spin_lock(spinlock_t * lock, const char *base_file, int line_no) | ||
1009 | { | ||
1010 | long tmp; | ||
1011 | long stuck; | ||
1012 | void *inline_pc = __builtin_return_address(0); | ||
1013 | unsigned long started = jiffies; | ||
1014 | int printed = 0; | ||
1015 | int cpu = smp_processor_id(); | ||
1016 | |||
1017 | stuck = 1L << 30; | ||
1018 | try_again: | ||
1019 | |||
1020 | /* Use sub-sections to put the actual loop at the end | ||
1021 | of this object file's text section so as to perfect | ||
1022 | branch prediction. */ | ||
1023 | __asm__ __volatile__( | ||
1024 | "1: ldl_l %0,%1\n" | ||
1025 | " subq %2,1,%2\n" | ||
1026 | " blbs %0,2f\n" | ||
1027 | " or %0,1,%0\n" | ||
1028 | " stl_c %0,%1\n" | ||
1029 | " beq %0,3f\n" | ||
1030 | "4: mb\n" | ||
1031 | ".subsection 2\n" | ||
1032 | "2: ldl %0,%1\n" | ||
1033 | " subq %2,1,%2\n" | ||
1034 | "3: blt %2,4b\n" | ||
1035 | " blbs %0,2b\n" | ||
1036 | " br 1b\n" | ||
1037 | ".previous" | ||
1038 | : "=r" (tmp), "=m" (lock->lock), "=r" (stuck) | ||
1039 | : "1" (lock->lock), "2" (stuck) : "memory"); | ||
1040 | |||
1041 | if (stuck < 0) { | ||
1042 | printk(KERN_WARNING | ||
1043 | "%s:%d spinlock stuck in %s at %p(%d)" | ||
1044 | " owner %s at %p(%d) %s:%d\n", | ||
1045 | base_file, line_no, | ||
1046 | current->comm, inline_pc, cpu, | ||
1047 | lock->task->comm, lock->previous, | ||
1048 | lock->on_cpu, lock->base_file, lock->line_no); | ||
1049 | stuck = 1L << 36; | ||
1050 | printed = 1; | ||
1051 | goto try_again; | ||
1052 | } | ||
1053 | |||
1054 | /* Exiting. Got the lock. */ | ||
1055 | lock->on_cpu = cpu; | ||
1056 | lock->previous = inline_pc; | ||
1057 | lock->task = current; | ||
1058 | lock->base_file = base_file; | ||
1059 | lock->line_no = line_no; | ||
1060 | |||
1061 | if (printed) { | ||
1062 | printk(KERN_WARNING | ||
1063 | "%s:%d spinlock grabbed in %s at %p(%d) %ld ticks\n", | ||
1064 | base_file, line_no, current->comm, inline_pc, | ||
1065 | cpu, jiffies - started); | ||
1066 | } | ||
1067 | } | ||
1068 | |||
1069 | int | ||
1070 | debug_spin_trylock(spinlock_t * lock, const char *base_file, int line_no) | ||
1071 | { | ||
1072 | int ret; | ||
1073 | if ((ret = !test_and_set_bit(0, lock))) { | ||
1074 | lock->on_cpu = smp_processor_id(); | ||
1075 | lock->previous = __builtin_return_address(0); | ||
1076 | lock->task = current; | ||
1077 | } else { | ||
1078 | lock->base_file = base_file; | ||
1079 | lock->line_no = line_no; | ||
1080 | } | ||
1081 | return ret; | ||
1082 | } | ||
1083 | #endif /* CONFIG_DEBUG_SPINLOCK */ | ||
1084 | |||
1085 | #ifdef CONFIG_DEBUG_RWLOCK | ||
1086 | void _raw_write_lock(rwlock_t * lock) | ||
1087 | { | ||
1088 | long regx, regy; | ||
1089 | int stuck_lock, stuck_reader; | ||
1090 | void *inline_pc = __builtin_return_address(0); | ||
1091 | |||
1092 | try_again: | ||
1093 | |||
1094 | stuck_lock = 1<<30; | ||
1095 | stuck_reader = 1<<30; | ||
1096 | |||
1097 | __asm__ __volatile__( | ||
1098 | "1: ldl_l %1,%0\n" | ||
1099 | " blbs %1,6f\n" | ||
1100 | " blt %1,8f\n" | ||
1101 | " mov 1,%1\n" | ||
1102 | " stl_c %1,%0\n" | ||
1103 | " beq %1,6f\n" | ||
1104 | "4: mb\n" | ||
1105 | ".subsection 2\n" | ||
1106 | "6: blt %3,4b # debug\n" | ||
1107 | " subl %3,1,%3 # debug\n" | ||
1108 | " ldl %1,%0\n" | ||
1109 | " blbs %1,6b\n" | ||
1110 | "8: blt %4,4b # debug\n" | ||
1111 | " subl %4,1,%4 # debug\n" | ||
1112 | " ldl %1,%0\n" | ||
1113 | " blt %1,8b\n" | ||
1114 | " br 1b\n" | ||
1115 | ".previous" | ||
1116 | : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (regy), | ||
1117 | "=&r" (stuck_lock), "=&r" (stuck_reader) | ||
1118 | : "0" (*(volatile int *)lock), "3" (stuck_lock), "4" (stuck_reader) : "memory"); | ||
1119 | |||
1120 | if (stuck_lock < 0) { | ||
1121 | printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc); | ||
1122 | goto try_again; | ||
1123 | } | ||
1124 | if (stuck_reader < 0) { | ||
1125 | printk(KERN_WARNING "write_lock stuck on readers at %p\n", | ||
1126 | inline_pc); | ||
1127 | goto try_again; | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | void _raw_read_lock(rwlock_t * lock) | ||
1132 | { | ||
1133 | long regx; | ||
1134 | int stuck_lock; | ||
1135 | void *inline_pc = __builtin_return_address(0); | ||
1136 | |||
1137 | try_again: | ||
1138 | |||
1139 | stuck_lock = 1<<30; | ||
1140 | |||
1141 | __asm__ __volatile__( | ||
1142 | "1: ldl_l %1,%0;" | ||
1143 | " blbs %1,6f;" | ||
1144 | " subl %1,2,%1;" | ||
1145 | " stl_c %1,%0;" | ||
1146 | " beq %1,6f;" | ||
1147 | "4: mb\n" | ||
1148 | ".subsection 2\n" | ||
1149 | "6: ldl %1,%0;" | ||
1150 | " blt %2,4b # debug\n" | ||
1151 | " subl %2,1,%2 # debug\n" | ||
1152 | " blbs %1,6b;" | ||
1153 | " br 1b\n" | ||
1154 | ".previous" | ||
1155 | : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (stuck_lock) | ||
1156 | : "0" (*(volatile int *)lock), "2" (stuck_lock) : "memory"); | ||
1157 | |||
1158 | if (stuck_lock < 0) { | ||
1159 | printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc); | ||
1160 | goto try_again; | ||
1161 | } | ||
1162 | } | ||
1163 | #endif /* CONFIG_DEBUG_RWLOCK */ | ||
diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c new file mode 100644 index 000000000000..5c98fc83e238 --- /dev/null +++ b/arch/alpha/kernel/srm_env.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* | ||
2 | * srm_env.c - Access to SRM environment | ||
3 | * variables through linux' procfs | ||
4 | * | ||
5 | * Copyright (C) 2001-2002 Jan-Benedict Glaw <jbglaw@lug-owl.de> | ||
6 | * | ||
7 | * This driver is at all a modified version of Erik Mouw's | ||
8 | * Documentation/DocBook/procfs_example.c, so: thank | ||
9 | * you, Erik! He can be reached via email at | ||
10 | * <J.A.K.Mouw@its.tudelft.nl>. It is based on an idea | ||
11 | * provided by DEC^WCompaq^WIntel's "Jumpstart" CD. They | ||
12 | * included a patch like this as well. Thanks for idea! | ||
13 | * | ||
14 | * This program is free software; you can redistribute | ||
15 | * it and/or modify it under the terms of the GNU General | ||
16 | * Public License version 2 as published by the Free Software | ||
17 | * Foundation. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be | ||
20 | * useful, but WITHOUT ANY WARRANTY; without even the implied | ||
21 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | ||
22 | * PURPOSE. See the GNU General Public License for more | ||
23 | * details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public | ||
26 | * License along with this program; if not, write to the | ||
27 | * Free Software Foundation, Inc., 59 Temple Place, | ||
28 | * Suite 330, Boston, MA 02111-1307 USA | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * Changelog | ||
34 | * ~~~~~~~~~ | ||
35 | * | ||
36 | * Thu, 22 Aug 2002 15:10:43 +0200 | ||
37 | * - Update Config.help entry. I got a number of emails asking | ||
38 | * me to tell their senders if they could make use of this | ||
39 | * piece of code... So: "SRM is something like BIOS for your | ||
40 | * Alpha" | ||
41 | * - Update code formatting a bit to better conform CodingStyle | ||
42 | * rules. | ||
43 | * - So this is v0.0.5, with no changes (except formatting) | ||
44 | * | ||
45 | * Wed, 22 May 2002 00:11:21 +0200 | ||
46 | * - Fix typo on comment (SRC -> SRM) | ||
47 | * - Call this "Version 0.0.4" | ||
48 | * | ||
49 | * Tue, 9 Apr 2002 18:44:40 +0200 | ||
50 | * - Implement access by variable name and additionally | ||
51 | * by number. This is done by creating two subdirectories | ||
52 | * where one holds all names (like the old directory | ||
53 | * did) and the other holding 256 files named like "0", | ||
54 | * "1" and so on. | ||
55 | * - Call this "Version 0.0.3" | ||
56 | * | ||
57 | */ | ||
58 | |||
59 | #include <linux/kernel.h> | ||
60 | #include <linux/config.h> | ||
61 | #include <linux/module.h> | ||
62 | #include <linux/init.h> | ||
63 | #include <linux/proc_fs.h> | ||
64 | #include <asm/console.h> | ||
65 | #include <asm/uaccess.h> | ||
66 | #include <asm/machvec.h> | ||
67 | |||
68 | #define BASE_DIR "srm_environment" /* Subdir in /proc/ */ | ||
69 | #define NAMED_DIR "named_variables" /* Subdir for known variables */ | ||
70 | #define NUMBERED_DIR "numbered_variables" /* Subdir for all variables */ | ||
71 | #define VERSION "0.0.5" /* Module version */ | ||
72 | #define NAME "srm_env" /* Module name */ | ||
73 | |||
74 | MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); | ||
75 | MODULE_DESCRIPTION("Accessing Alpha SRM environment through procfs interface"); | ||
76 | MODULE_LICENSE("GPL"); | ||
77 | |||
78 | typedef struct _srm_env { | ||
79 | char *name; | ||
80 | unsigned long id; | ||
81 | struct proc_dir_entry *proc_entry; | ||
82 | } srm_env_t; | ||
83 | |||
84 | static struct proc_dir_entry *base_dir; | ||
85 | static struct proc_dir_entry *named_dir; | ||
86 | static struct proc_dir_entry *numbered_dir; | ||
87 | static char number[256][4]; | ||
88 | |||
89 | static srm_env_t srm_named_entries[] = { | ||
90 | { "auto_action", ENV_AUTO_ACTION }, | ||
91 | { "boot_dev", ENV_BOOT_DEV }, | ||
92 | { "bootdef_dev", ENV_BOOTDEF_DEV }, | ||
93 | { "booted_dev", ENV_BOOTED_DEV }, | ||
94 | { "boot_file", ENV_BOOT_FILE }, | ||
95 | { "booted_file", ENV_BOOTED_FILE }, | ||
96 | { "boot_osflags", ENV_BOOT_OSFLAGS }, | ||
97 | { "booted_osflags", ENV_BOOTED_OSFLAGS }, | ||
98 | { "boot_reset", ENV_BOOT_RESET }, | ||
99 | { "dump_dev", ENV_DUMP_DEV }, | ||
100 | { "enable_audit", ENV_ENABLE_AUDIT }, | ||
101 | { "license", ENV_LICENSE }, | ||
102 | { "char_set", ENV_CHAR_SET }, | ||
103 | { "language", ENV_LANGUAGE }, | ||
104 | { "tty_dev", ENV_TTY_DEV }, | ||
105 | { NULL, 0 }, | ||
106 | }; | ||
107 | static srm_env_t srm_numbered_entries[256]; | ||
108 | |||
109 | |||
110 | |||
111 | static int | ||
112 | srm_env_read(char *page, char **start, off_t off, int count, int *eof, | ||
113 | void *data) | ||
114 | { | ||
115 | int nbytes; | ||
116 | unsigned long ret; | ||
117 | srm_env_t *entry; | ||
118 | |||
119 | if(off != 0) | ||
120 | return -EFAULT; | ||
121 | |||
122 | entry = (srm_env_t *) data; | ||
123 | ret = callback_getenv(entry->id, page, count); | ||
124 | |||
125 | if((ret >> 61) == 0) | ||
126 | nbytes = (int) ret; | ||
127 | else | ||
128 | nbytes = -EFAULT; | ||
129 | |||
130 | return nbytes; | ||
131 | } | ||
132 | |||
133 | |||
134 | static int | ||
135 | srm_env_write(struct file *file, const char __user *buffer, unsigned long count, | ||
136 | void *data) | ||
137 | { | ||
138 | int res; | ||
139 | srm_env_t *entry; | ||
140 | char *buf = (char *) __get_free_page(GFP_USER); | ||
141 | unsigned long ret1, ret2; | ||
142 | |||
143 | entry = (srm_env_t *) data; | ||
144 | |||
145 | if (!buf) | ||
146 | return -ENOMEM; | ||
147 | |||
148 | res = -EINVAL; | ||
149 | if (count >= PAGE_SIZE) | ||
150 | goto out; | ||
151 | |||
152 | res = -EFAULT; | ||
153 | if (copy_from_user(buf, buffer, count)) | ||
154 | goto out; | ||
155 | buf[count] = '\0'; | ||
156 | |||
157 | ret1 = callback_setenv(entry->id, buf, count); | ||
158 | if ((ret1 >> 61) == 0) { | ||
159 | do | ||
160 | ret2 = callback_save_env(); | ||
161 | while((ret2 >> 61) == 1); | ||
162 | res = (int) ret1; | ||
163 | } | ||
164 | |||
165 | out: | ||
166 | free_page((unsigned long)buf); | ||
167 | return res; | ||
168 | } | ||
169 | |||
170 | static void | ||
171 | srm_env_cleanup(void) | ||
172 | { | ||
173 | srm_env_t *entry; | ||
174 | unsigned long var_num; | ||
175 | |||
176 | if(base_dir) { | ||
177 | /* | ||
178 | * Remove named entries | ||
179 | */ | ||
180 | if(named_dir) { | ||
181 | entry = srm_named_entries; | ||
182 | while(entry->name != NULL && entry->id != 0) { | ||
183 | if(entry->proc_entry) { | ||
184 | remove_proc_entry(entry->name, | ||
185 | named_dir); | ||
186 | entry->proc_entry = NULL; | ||
187 | } | ||
188 | entry++; | ||
189 | } | ||
190 | remove_proc_entry(NAMED_DIR, base_dir); | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * Remove numbered entries | ||
195 | */ | ||
196 | if(numbered_dir) { | ||
197 | for(var_num = 0; var_num <= 255; var_num++) { | ||
198 | entry = &srm_numbered_entries[var_num]; | ||
199 | |||
200 | if(entry->proc_entry) { | ||
201 | remove_proc_entry(entry->name, | ||
202 | numbered_dir); | ||
203 | entry->proc_entry = NULL; | ||
204 | entry->name = NULL; | ||
205 | } | ||
206 | } | ||
207 | remove_proc_entry(NUMBERED_DIR, base_dir); | ||
208 | } | ||
209 | |||
210 | remove_proc_entry(BASE_DIR, NULL); | ||
211 | } | ||
212 | |||
213 | return; | ||
214 | } | ||
215 | |||
216 | |||
217 | static int __init | ||
218 | srm_env_init(void) | ||
219 | { | ||
220 | srm_env_t *entry; | ||
221 | unsigned long var_num; | ||
222 | |||
223 | /* | ||
224 | * Check system | ||
225 | */ | ||
226 | if(!alpha_using_srm) { | ||
227 | printk(KERN_INFO "%s: This Alpha system doesn't " | ||
228 | "know about SRM (or you've booted " | ||
229 | "SRM->MILO->Linux, which gets " | ||
230 | "misdetected)...\n", __FUNCTION__); | ||
231 | return -ENODEV; | ||
232 | } | ||
233 | |||
234 | /* | ||
235 | * Init numbers | ||
236 | */ | ||
237 | for(var_num = 0; var_num <= 255; var_num++) | ||
238 | sprintf(number[var_num], "%ld", var_num); | ||
239 | |||
240 | /* | ||
241 | * Create base directory | ||
242 | */ | ||
243 | base_dir = proc_mkdir(BASE_DIR, NULL); | ||
244 | if(base_dir == NULL) { | ||
245 | printk(KERN_ERR "Couldn't create base dir /proc/%s\n", | ||
246 | BASE_DIR); | ||
247 | goto cleanup; | ||
248 | } | ||
249 | base_dir->owner = THIS_MODULE; | ||
250 | |||
251 | /* | ||
252 | * Create per-name subdirectory | ||
253 | */ | ||
254 | named_dir = proc_mkdir(NAMED_DIR, base_dir); | ||
255 | if(named_dir == NULL) { | ||
256 | printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n", | ||
257 | BASE_DIR, NAMED_DIR); | ||
258 | goto cleanup; | ||
259 | } | ||
260 | named_dir->owner = THIS_MODULE; | ||
261 | |||
262 | /* | ||
263 | * Create per-number subdirectory | ||
264 | */ | ||
265 | numbered_dir = proc_mkdir(NUMBERED_DIR, base_dir); | ||
266 | if(numbered_dir == NULL) { | ||
267 | printk(KERN_ERR "Couldn't create dir /proc/%s/%s\n", | ||
268 | BASE_DIR, NUMBERED_DIR); | ||
269 | goto cleanup; | ||
270 | |||
271 | } | ||
272 | numbered_dir->owner = THIS_MODULE; | ||
273 | |||
274 | /* | ||
275 | * Create all named nodes | ||
276 | */ | ||
277 | entry = srm_named_entries; | ||
278 | while(entry->name != NULL && entry->id != 0) { | ||
279 | entry->proc_entry = create_proc_entry(entry->name, | ||
280 | 0644, named_dir); | ||
281 | if(entry->proc_entry == NULL) | ||
282 | goto cleanup; | ||
283 | |||
284 | entry->proc_entry->data = (void *) entry; | ||
285 | entry->proc_entry->owner = THIS_MODULE; | ||
286 | entry->proc_entry->read_proc = srm_env_read; | ||
287 | entry->proc_entry->write_proc = srm_env_write; | ||
288 | |||
289 | entry++; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * Create all numbered nodes | ||
294 | */ | ||
295 | for(var_num = 0; var_num <= 255; var_num++) { | ||
296 | entry = &srm_numbered_entries[var_num]; | ||
297 | entry->name = number[var_num]; | ||
298 | |||
299 | entry->proc_entry = create_proc_entry(entry->name, | ||
300 | 0644, numbered_dir); | ||
301 | if(entry->proc_entry == NULL) | ||
302 | goto cleanup; | ||
303 | |||
304 | entry->id = var_num; | ||
305 | entry->proc_entry->data = (void *) entry; | ||
306 | entry->proc_entry->owner = THIS_MODULE; | ||
307 | entry->proc_entry->read_proc = srm_env_read; | ||
308 | entry->proc_entry->write_proc = srm_env_write; | ||
309 | } | ||
310 | |||
311 | printk(KERN_INFO "%s: version %s loaded successfully\n", NAME, | ||
312 | VERSION); | ||
313 | |||
314 | return 0; | ||
315 | |||
316 | cleanup: | ||
317 | srm_env_cleanup(); | ||
318 | |||
319 | return -ENOMEM; | ||
320 | } | ||
321 | |||
322 | |||
323 | static void __exit | ||
324 | srm_env_exit(void) | ||
325 | { | ||
326 | srm_env_cleanup(); | ||
327 | printk(KERN_INFO "%s: unloaded successfully\n", NAME); | ||
328 | |||
329 | return; | ||
330 | } | ||
331 | |||
332 | |||
333 | module_init(srm_env_init); | ||
334 | module_exit(srm_env_exit); | ||
335 | |||
diff --git a/arch/alpha/kernel/srmcons.c b/arch/alpha/kernel/srmcons.c new file mode 100644 index 000000000000..3b30d4f1fc42 --- /dev/null +++ b/arch/alpha/kernel/srmcons.c | |||
@@ -0,0 +1,326 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/srmcons.c | ||
3 | * | ||
4 | * Callback based driver for SRM Console console device. | ||
5 | * (TTY driver and console driver) | ||
6 | */ | ||
7 | |||
8 | #include <linux/config.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/console.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/timer.h> | ||
17 | #include <linux/tty.h> | ||
18 | #include <linux/tty_driver.h> | ||
19 | #include <linux/tty_flip.h> | ||
20 | |||
21 | #include <asm/console.h> | ||
22 | #include <asm/uaccess.h> | ||
23 | |||
24 | |||
25 | static DEFINE_SPINLOCK(srmcons_callback_lock); | ||
26 | static int srm_is_registered_console = 0; | ||
27 | |||
28 | /* | ||
29 | * The TTY driver | ||
30 | */ | ||
31 | #define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */ | ||
32 | |||
33 | struct srmcons_private { | ||
34 | struct tty_struct *tty; | ||
35 | struct timer_list timer; | ||
36 | spinlock_t lock; | ||
37 | }; | ||
38 | |||
39 | typedef union _srmcons_result { | ||
40 | struct { | ||
41 | unsigned long c :61; | ||
42 | unsigned long status :3; | ||
43 | } bits; | ||
44 | long as_long; | ||
45 | } srmcons_result; | ||
46 | |||
47 | /* called with callback_lock held */ | ||
48 | static int | ||
49 | srmcons_do_receive_chars(struct tty_struct *tty) | ||
50 | { | ||
51 | srmcons_result result; | ||
52 | int count = 0, loops = 0; | ||
53 | |||
54 | do { | ||
55 | result.as_long = callback_getc(0); | ||
56 | if (result.bits.status < 2) { | ||
57 | tty_insert_flip_char(tty, (char)result.bits.c, 0); | ||
58 | count++; | ||
59 | } | ||
60 | } while((result.bits.status & 1) && (++loops < 10)); | ||
61 | |||
62 | if (count) | ||
63 | tty_schedule_flip(tty); | ||
64 | |||
65 | return count; | ||
66 | } | ||
67 | |||
68 | static void | ||
69 | srmcons_receive_chars(unsigned long data) | ||
70 | { | ||
71 | struct srmcons_private *srmconsp = (struct srmcons_private *)data; | ||
72 | unsigned long flags; | ||
73 | int incr = 10; | ||
74 | |||
75 | local_irq_save(flags); | ||
76 | if (spin_trylock(&srmcons_callback_lock)) { | ||
77 | if (!srmcons_do_receive_chars(srmconsp->tty)) | ||
78 | incr = 100; | ||
79 | spin_unlock(&srmcons_callback_lock); | ||
80 | } | ||
81 | |||
82 | spin_lock(&srmconsp->lock); | ||
83 | if (srmconsp->tty) { | ||
84 | srmconsp->timer.expires = jiffies + incr; | ||
85 | add_timer(&srmconsp->timer); | ||
86 | } | ||
87 | spin_unlock(&srmconsp->lock); | ||
88 | |||
89 | local_irq_restore(flags); | ||
90 | } | ||
91 | |||
92 | /* called with callback_lock held */ | ||
93 | static int | ||
94 | srmcons_do_write(struct tty_struct *tty, const char *buf, int count) | ||
95 | { | ||
96 | static char str_cr[1] = "\r"; | ||
97 | long c, remaining = count; | ||
98 | srmcons_result result; | ||
99 | char *cur; | ||
100 | int need_cr; | ||
101 | |||
102 | for (cur = (char *)buf; remaining > 0; ) { | ||
103 | need_cr = 0; | ||
104 | /* | ||
105 | * Break it up into reasonable size chunks to allow a chance | ||
106 | * for input to get in | ||
107 | */ | ||
108 | for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++) | ||
109 | if (cur[c] == '\n') | ||
110 | need_cr = 1; | ||
111 | |||
112 | while (c > 0) { | ||
113 | result.as_long = callback_puts(0, cur, c); | ||
114 | c -= result.bits.c; | ||
115 | remaining -= result.bits.c; | ||
116 | cur += result.bits.c; | ||
117 | |||
118 | /* | ||
119 | * Check for pending input iff a tty was provided | ||
120 | */ | ||
121 | if (tty) | ||
122 | srmcons_do_receive_chars(tty); | ||
123 | } | ||
124 | |||
125 | while (need_cr) { | ||
126 | result.as_long = callback_puts(0, str_cr, 1); | ||
127 | if (result.bits.c > 0) | ||
128 | need_cr = 0; | ||
129 | } | ||
130 | } | ||
131 | return count; | ||
132 | } | ||
133 | |||
134 | static int | ||
135 | srmcons_write(struct tty_struct *tty, | ||
136 | const unsigned char *buf, int count) | ||
137 | { | ||
138 | unsigned long flags; | ||
139 | |||
140 | spin_lock_irqsave(&srmcons_callback_lock, flags); | ||
141 | srmcons_do_write(tty, (const char *) buf, count); | ||
142 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | ||
143 | |||
144 | return count; | ||
145 | } | ||
146 | |||
147 | static int | ||
148 | srmcons_write_room(struct tty_struct *tty) | ||
149 | { | ||
150 | return 512; | ||
151 | } | ||
152 | |||
153 | static int | ||
154 | srmcons_chars_in_buffer(struct tty_struct *tty) | ||
155 | { | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int | ||
160 | srmcons_get_private_struct(struct srmcons_private **ps) | ||
161 | { | ||
162 | static struct srmcons_private *srmconsp = NULL; | ||
163 | static DEFINE_SPINLOCK(srmconsp_lock); | ||
164 | unsigned long flags; | ||
165 | int retval = 0; | ||
166 | |||
167 | if (srmconsp == NULL) { | ||
168 | spin_lock_irqsave(&srmconsp_lock, flags); | ||
169 | |||
170 | srmconsp = kmalloc(sizeof(*srmconsp), GFP_KERNEL); | ||
171 | if (srmconsp == NULL) | ||
172 | retval = -ENOMEM; | ||
173 | else { | ||
174 | srmconsp->tty = NULL; | ||
175 | spin_lock_init(&srmconsp->lock); | ||
176 | init_timer(&srmconsp->timer); | ||
177 | } | ||
178 | |||
179 | spin_unlock_irqrestore(&srmconsp_lock, flags); | ||
180 | } | ||
181 | |||
182 | *ps = srmconsp; | ||
183 | return retval; | ||
184 | } | ||
185 | |||
186 | static int | ||
187 | srmcons_open(struct tty_struct *tty, struct file *filp) | ||
188 | { | ||
189 | struct srmcons_private *srmconsp; | ||
190 | unsigned long flags; | ||
191 | int retval; | ||
192 | |||
193 | retval = srmcons_get_private_struct(&srmconsp); | ||
194 | if (retval) | ||
195 | return retval; | ||
196 | |||
197 | spin_lock_irqsave(&srmconsp->lock, flags); | ||
198 | |||
199 | if (!srmconsp->tty) { | ||
200 | tty->driver_data = srmconsp; | ||
201 | |||
202 | srmconsp->tty = tty; | ||
203 | srmconsp->timer.function = srmcons_receive_chars; | ||
204 | srmconsp->timer.data = (unsigned long)srmconsp; | ||
205 | srmconsp->timer.expires = jiffies + 10; | ||
206 | add_timer(&srmconsp->timer); | ||
207 | } | ||
208 | |||
209 | spin_unlock_irqrestore(&srmconsp->lock, flags); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void | ||
215 | srmcons_close(struct tty_struct *tty, struct file *filp) | ||
216 | { | ||
217 | struct srmcons_private *srmconsp = tty->driver_data; | ||
218 | unsigned long flags; | ||
219 | |||
220 | spin_lock_irqsave(&srmconsp->lock, flags); | ||
221 | |||
222 | if (tty->count == 1) { | ||
223 | srmconsp->tty = NULL; | ||
224 | del_timer(&srmconsp->timer); | ||
225 | } | ||
226 | |||
227 | spin_unlock_irqrestore(&srmconsp->lock, flags); | ||
228 | } | ||
229 | |||
230 | |||
231 | static struct tty_driver *srmcons_driver; | ||
232 | |||
233 | static struct tty_operations srmcons_ops = { | ||
234 | .open = srmcons_open, | ||
235 | .close = srmcons_close, | ||
236 | .write = srmcons_write, | ||
237 | .write_room = srmcons_write_room, | ||
238 | .chars_in_buffer= srmcons_chars_in_buffer, | ||
239 | }; | ||
240 | |||
241 | static int __init | ||
242 | srmcons_init(void) | ||
243 | { | ||
244 | if (srm_is_registered_console) { | ||
245 | struct tty_driver *driver; | ||
246 | int err; | ||
247 | |||
248 | driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES); | ||
249 | if (!driver) | ||
250 | return -ENOMEM; | ||
251 | driver->driver_name = "srm"; | ||
252 | driver->name = "srm"; | ||
253 | driver->major = 0; /* dynamic */ | ||
254 | driver->minor_start = 0; | ||
255 | driver->type = TTY_DRIVER_TYPE_SYSTEM; | ||
256 | driver->subtype = SYSTEM_TYPE_SYSCONS; | ||
257 | driver->init_termios = tty_std_termios; | ||
258 | tty_set_operations(driver, &srmcons_ops); | ||
259 | err = tty_register_driver(driver); | ||
260 | if (err) { | ||
261 | put_tty_driver(driver); | ||
262 | return err; | ||
263 | } | ||
264 | srmcons_driver = driver; | ||
265 | } | ||
266 | |||
267 | return -ENODEV; | ||
268 | } | ||
269 | |||
270 | module_init(srmcons_init); | ||
271 | |||
272 | |||
273 | /* | ||
274 | * The console driver | ||
275 | */ | ||
276 | static void | ||
277 | srm_console_write(struct console *co, const char *s, unsigned count) | ||
278 | { | ||
279 | unsigned long flags; | ||
280 | |||
281 | spin_lock_irqsave(&srmcons_callback_lock, flags); | ||
282 | srmcons_do_write(NULL, s, count); | ||
283 | spin_unlock_irqrestore(&srmcons_callback_lock, flags); | ||
284 | } | ||
285 | |||
286 | static struct tty_driver * | ||
287 | srm_console_device(struct console *co, int *index) | ||
288 | { | ||
289 | *index = co->index; | ||
290 | return srmcons_driver; | ||
291 | } | ||
292 | |||
293 | static int __init | ||
294 | srm_console_setup(struct console *co, char *options) | ||
295 | { | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static struct console srmcons = { | ||
300 | .name = "srm", | ||
301 | .write = srm_console_write, | ||
302 | .device = srm_console_device, | ||
303 | .setup = srm_console_setup, | ||
304 | .flags = CON_PRINTBUFFER, | ||
305 | .index = -1, | ||
306 | }; | ||
307 | |||
308 | void __init | ||
309 | register_srm_console(void) | ||
310 | { | ||
311 | if (!srm_is_registered_console) { | ||
312 | callback_open_console(); | ||
313 | register_console(&srmcons); | ||
314 | srm_is_registered_console = 1; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | void __init | ||
319 | unregister_srm_console(void) | ||
320 | { | ||
321 | if (srm_is_registered_console) { | ||
322 | callback_close_console(); | ||
323 | unregister_console(&srmcons); | ||
324 | srm_is_registered_console = 0; | ||
325 | } | ||
326 | } | ||
diff --git a/arch/alpha/kernel/sys_alcor.c b/arch/alpha/kernel/sys_alcor.c new file mode 100644 index 000000000000..145dcde143ae --- /dev/null +++ b/arch/alpha/kernel/sys_alcor.c | |||
@@ -0,0 +1,326 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_alcor.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the ALCOR and XLT (XL-300/366/433). | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/reboot.h> | ||
19 | #include <linux/bitops.h> | ||
20 | |||
21 | #include <asm/ptrace.h> | ||
22 | #include <asm/system.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/dma.h> | ||
25 | #include <asm/mmu_context.h> | ||
26 | #include <asm/irq.h> | ||
27 | #include <asm/pgtable.h> | ||
28 | #include <asm/core_cia.h> | ||
29 | #include <asm/tlbflush.h> | ||
30 | |||
31 | #include "proto.h" | ||
32 | #include "irq_impl.h" | ||
33 | #include "pci_impl.h" | ||
34 | #include "machvec_impl.h" | ||
35 | |||
36 | |||
37 | /* Note mask bit is true for ENABLED irqs. */ | ||
38 | static unsigned long cached_irq_mask; | ||
39 | |||
40 | static inline void | ||
41 | alcor_update_irq_hw(unsigned long mask) | ||
42 | { | ||
43 | *(vuip)GRU_INT_MASK = mask; | ||
44 | mb(); | ||
45 | } | ||
46 | |||
47 | static inline void | ||
48 | alcor_enable_irq(unsigned int irq) | ||
49 | { | ||
50 | alcor_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); | ||
51 | } | ||
52 | |||
53 | static void | ||
54 | alcor_disable_irq(unsigned int irq) | ||
55 | { | ||
56 | alcor_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); | ||
57 | } | ||
58 | |||
59 | static void | ||
60 | alcor_mask_and_ack_irq(unsigned int irq) | ||
61 | { | ||
62 | alcor_disable_irq(irq); | ||
63 | |||
64 | /* On ALCOR/XLT, need to dismiss interrupt via GRU. */ | ||
65 | *(vuip)GRU_INT_CLEAR = 1 << (irq - 16); mb(); | ||
66 | *(vuip)GRU_INT_CLEAR = 0; mb(); | ||
67 | } | ||
68 | |||
69 | static unsigned int | ||
70 | alcor_startup_irq(unsigned int irq) | ||
71 | { | ||
72 | alcor_enable_irq(irq); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static void | ||
77 | alcor_isa_mask_and_ack_irq(unsigned int irq) | ||
78 | { | ||
79 | i8259a_mask_and_ack_irq(irq); | ||
80 | |||
81 | /* On ALCOR/XLT, need to dismiss interrupt via GRU. */ | ||
82 | *(vuip)GRU_INT_CLEAR = 0x80000000; mb(); | ||
83 | *(vuip)GRU_INT_CLEAR = 0; mb(); | ||
84 | } | ||
85 | |||
86 | static void | ||
87 | alcor_end_irq(unsigned int irq) | ||
88 | { | ||
89 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
90 | alcor_enable_irq(irq); | ||
91 | } | ||
92 | |||
93 | static struct hw_interrupt_type alcor_irq_type = { | ||
94 | .typename = "ALCOR", | ||
95 | .startup = alcor_startup_irq, | ||
96 | .shutdown = alcor_disable_irq, | ||
97 | .enable = alcor_enable_irq, | ||
98 | .disable = alcor_disable_irq, | ||
99 | .ack = alcor_mask_and_ack_irq, | ||
100 | .end = alcor_end_irq, | ||
101 | }; | ||
102 | |||
103 | static void | ||
104 | alcor_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
105 | { | ||
106 | unsigned long pld; | ||
107 | unsigned int i; | ||
108 | |||
109 | /* Read the interrupt summary register of the GRU */ | ||
110 | pld = (*(vuip)GRU_INT_REQ) & GRU_INT_REQ_BITS; | ||
111 | |||
112 | /* | ||
113 | * Now for every possible bit set, work through them and call | ||
114 | * the appropriate interrupt handler. | ||
115 | */ | ||
116 | while (pld) { | ||
117 | i = ffz(~pld); | ||
118 | pld &= pld - 1; /* clear least bit set */ | ||
119 | if (i == 31) { | ||
120 | isa_device_interrupt(vector, regs); | ||
121 | } else { | ||
122 | handle_irq(16 + i, regs); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | static void __init | ||
128 | alcor_init_irq(void) | ||
129 | { | ||
130 | long i; | ||
131 | |||
132 | if (alpha_using_srm) | ||
133 | alpha_mv.device_interrupt = srm_device_interrupt; | ||
134 | |||
135 | *(vuip)GRU_INT_MASK = 0; mb(); /* all disabled */ | ||
136 | *(vuip)GRU_INT_EDGE = 0; mb(); /* all are level */ | ||
137 | *(vuip)GRU_INT_HILO = 0x80000000U; mb(); /* ISA only HI */ | ||
138 | *(vuip)GRU_INT_CLEAR = 0; mb(); /* all clear */ | ||
139 | |||
140 | for (i = 16; i < 48; ++i) { | ||
141 | /* On Alcor, at least, lines 20..30 are not connected | ||
142 | and can generate spurrious interrupts if we turn them | ||
143 | on while IRQ probing. */ | ||
144 | if (i >= 16+20 && i <= 16+30) | ||
145 | continue; | ||
146 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
147 | irq_desc[i].handler = &alcor_irq_type; | ||
148 | } | ||
149 | i8259a_irq_type.ack = alcor_isa_mask_and_ack_irq; | ||
150 | |||
151 | init_i8259a_irqs(); | ||
152 | common_init_isa_dma(); | ||
153 | |||
154 | setup_irq(16+31, &isa_cascade_irqaction); | ||
155 | } | ||
156 | |||
157 | |||
158 | /* | ||
159 | * PCI Fixup configuration. | ||
160 | * | ||
161 | * Summary @ GRU_INT_REQ: | ||
162 | * Bit Meaning | ||
163 | * 0 Interrupt Line A from slot 2 | ||
164 | * 1 Interrupt Line B from slot 2 | ||
165 | * 2 Interrupt Line C from slot 2 | ||
166 | * 3 Interrupt Line D from slot 2 | ||
167 | * 4 Interrupt Line A from slot 1 | ||
168 | * 5 Interrupt line B from slot 1 | ||
169 | * 6 Interrupt Line C from slot 1 | ||
170 | * 7 Interrupt Line D from slot 1 | ||
171 | * 8 Interrupt Line A from slot 0 | ||
172 | * 9 Interrupt Line B from slot 0 | ||
173 | *10 Interrupt Line C from slot 0 | ||
174 | *11 Interrupt Line D from slot 0 | ||
175 | *12 Interrupt Line A from slot 4 | ||
176 | *13 Interrupt Line B from slot 4 | ||
177 | *14 Interrupt Line C from slot 4 | ||
178 | *15 Interrupt Line D from slot 4 | ||
179 | *16 Interrupt Line D from slot 3 | ||
180 | *17 Interrupt Line D from slot 3 | ||
181 | *18 Interrupt Line D from slot 3 | ||
182 | *19 Interrupt Line D from slot 3 | ||
183 | *20-30 Reserved | ||
184 | *31 EISA interrupt | ||
185 | * | ||
186 | * The device to slot mapping looks like: | ||
187 | * | ||
188 | * Slot Device | ||
189 | * 6 built-in TULIP (XLT only) | ||
190 | * 7 PCI on board slot 0 | ||
191 | * 8 PCI on board slot 3 | ||
192 | * 9 PCI on board slot 4 | ||
193 | * 10 PCEB (PCI-EISA bridge) | ||
194 | * 11 PCI on board slot 2 | ||
195 | * 12 PCI on board slot 1 | ||
196 | * | ||
197 | * | ||
198 | * This two layered interrupt approach means that we allocate IRQ 16 and | ||
199 | * above for PCI interrupts. The IRQ relates to which bit the interrupt | ||
200 | * comes in on. This makes interrupt processing much easier. | ||
201 | */ | ||
202 | |||
203 | static int __init | ||
204 | alcor_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
205 | { | ||
206 | static char irq_tab[7][5] __initdata = { | ||
207 | /*INT INTA INTB INTC INTD */ | ||
208 | /* note: IDSEL 17 is XLT only */ | ||
209 | {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ | ||
210 | { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ | ||
211 | {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */ | ||
212 | {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */ | ||
213 | { -1, -1, -1, -1, -1}, /* IdSel 21, PCEB */ | ||
214 | { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ | ||
215 | { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ | ||
216 | }; | ||
217 | const long min_idsel = 6, max_idsel = 12, irqs_per_slot = 5; | ||
218 | return COMMON_TABLE_LOOKUP; | ||
219 | } | ||
220 | |||
221 | static void | ||
222 | alcor_kill_arch(int mode) | ||
223 | { | ||
224 | cia_kill_arch(mode); | ||
225 | |||
226 | #ifndef ALPHA_RESTORE_SRM_SETUP | ||
227 | switch(mode) { | ||
228 | case LINUX_REBOOT_CMD_RESTART: | ||
229 | /* Who said DEC engineer's have no sense of humor? ;-) */ | ||
230 | if (alpha_using_srm) { | ||
231 | *(vuip) GRU_RESET = 0x0000dead; | ||
232 | mb(); | ||
233 | } | ||
234 | break; | ||
235 | case LINUX_REBOOT_CMD_HALT: | ||
236 | break; | ||
237 | case LINUX_REBOOT_CMD_POWER_OFF: | ||
238 | break; | ||
239 | } | ||
240 | |||
241 | halt(); | ||
242 | #endif | ||
243 | } | ||
244 | |||
245 | static void __init | ||
246 | alcor_init_pci(void) | ||
247 | { | ||
248 | struct pci_dev *dev; | ||
249 | |||
250 | cia_init_pci(); | ||
251 | |||
252 | /* | ||
253 | * Now we can look to see if we are really running on an XLT-type | ||
254 | * motherboard, by looking for a 21040 TULIP in slot 6, which is | ||
255 | * built into XLT and BRET/MAVERICK, but not available on ALCOR. | ||
256 | */ | ||
257 | dev = pci_find_device(PCI_VENDOR_ID_DEC, | ||
258 | PCI_DEVICE_ID_DEC_TULIP, | ||
259 | NULL); | ||
260 | if (dev && dev->devfn == PCI_DEVFN(6,0)) { | ||
261 | alpha_mv.sys.cia.gru_int_req_bits = XLT_GRU_INT_REQ_BITS; | ||
262 | printk(KERN_INFO "%s: Detected AS500 or XLT motherboard.\n", | ||
263 | __FUNCTION__); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | |||
268 | /* | ||
269 | * The System Vectors | ||
270 | */ | ||
271 | |||
272 | struct alpha_machine_vector alcor_mv __initmv = { | ||
273 | .vector_name = "Alcor", | ||
274 | DO_EV5_MMU, | ||
275 | DO_DEFAULT_RTC, | ||
276 | DO_CIA_IO, | ||
277 | .machine_check = cia_machine_check, | ||
278 | .max_isa_dma_address = ALPHA_ALCOR_MAX_ISA_DMA_ADDRESS, | ||
279 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
280 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
281 | |||
282 | .nr_irqs = 48, | ||
283 | .device_interrupt = alcor_device_interrupt, | ||
284 | |||
285 | .init_arch = cia_init_arch, | ||
286 | .init_irq = alcor_init_irq, | ||
287 | .init_rtc = common_init_rtc, | ||
288 | .init_pci = alcor_init_pci, | ||
289 | .kill_arch = alcor_kill_arch, | ||
290 | .pci_map_irq = alcor_map_irq, | ||
291 | .pci_swizzle = common_swizzle, | ||
292 | |||
293 | .sys = { .cia = { | ||
294 | .gru_int_req_bits = ALCOR_GRU_INT_REQ_BITS | ||
295 | }} | ||
296 | }; | ||
297 | ALIAS_MV(alcor) | ||
298 | |||
299 | struct alpha_machine_vector xlt_mv __initmv = { | ||
300 | .vector_name = "XLT", | ||
301 | DO_EV5_MMU, | ||
302 | DO_DEFAULT_RTC, | ||
303 | DO_CIA_IO, | ||
304 | .machine_check = cia_machine_check, | ||
305 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
306 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
307 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
308 | |||
309 | .nr_irqs = 48, | ||
310 | .device_interrupt = alcor_device_interrupt, | ||
311 | |||
312 | .init_arch = cia_init_arch, | ||
313 | .init_irq = alcor_init_irq, | ||
314 | .init_rtc = common_init_rtc, | ||
315 | .init_pci = alcor_init_pci, | ||
316 | .kill_arch = alcor_kill_arch, | ||
317 | .pci_map_irq = alcor_map_irq, | ||
318 | .pci_swizzle = common_swizzle, | ||
319 | |||
320 | .sys = { .cia = { | ||
321 | .gru_int_req_bits = XLT_GRU_INT_REQ_BITS | ||
322 | }} | ||
323 | }; | ||
324 | |||
325 | /* No alpha_mv alias for XLT, since we compile it in unconditionally | ||
326 | with ALCOR; setup_arch knows how to cope. */ | ||
diff --git a/arch/alpha/kernel/sys_cabriolet.c b/arch/alpha/kernel/sys_cabriolet.c new file mode 100644 index 000000000000..8e3374d34c95 --- /dev/null +++ b/arch/alpha/kernel/sys_cabriolet.c | |||
@@ -0,0 +1,448 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_cabriolet.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999, 2000 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the Cabriolet (AlphaPC64), EB66+, and EB164, | ||
9 | * PC164 and LX164. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/bitops.h> | ||
20 | |||
21 | #include <asm/ptrace.h> | ||
22 | #include <asm/system.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <asm/irq.h> | ||
25 | #include <asm/mmu_context.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/pgtable.h> | ||
28 | #include <asm/core_apecs.h> | ||
29 | #include <asm/core_cia.h> | ||
30 | #include <asm/core_lca.h> | ||
31 | #include <asm/tlbflush.h> | ||
32 | |||
33 | #include "proto.h" | ||
34 | #include "irq_impl.h" | ||
35 | #include "pci_impl.h" | ||
36 | #include "machvec_impl.h" | ||
37 | |||
38 | |||
39 | /* Note mask bit is true for DISABLED irqs. */ | ||
40 | static unsigned long cached_irq_mask = ~0UL; | ||
41 | |||
42 | static inline void | ||
43 | cabriolet_update_irq_hw(unsigned int irq, unsigned long mask) | ||
44 | { | ||
45 | int ofs = (irq - 16) / 8; | ||
46 | outb(mask >> (16 + ofs * 8), 0x804 + ofs); | ||
47 | } | ||
48 | |||
49 | static inline void | ||
50 | cabriolet_enable_irq(unsigned int irq) | ||
51 | { | ||
52 | cabriolet_update_irq_hw(irq, cached_irq_mask &= ~(1UL << irq)); | ||
53 | } | ||
54 | |||
55 | static void | ||
56 | cabriolet_disable_irq(unsigned int irq) | ||
57 | { | ||
58 | cabriolet_update_irq_hw(irq, cached_irq_mask |= 1UL << irq); | ||
59 | } | ||
60 | |||
61 | static unsigned int | ||
62 | cabriolet_startup_irq(unsigned int irq) | ||
63 | { | ||
64 | cabriolet_enable_irq(irq); | ||
65 | return 0; /* never anything pending */ | ||
66 | } | ||
67 | |||
68 | static void | ||
69 | cabriolet_end_irq(unsigned int irq) | ||
70 | { | ||
71 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
72 | cabriolet_enable_irq(irq); | ||
73 | } | ||
74 | |||
75 | static struct hw_interrupt_type cabriolet_irq_type = { | ||
76 | .typename = "CABRIOLET", | ||
77 | .startup = cabriolet_startup_irq, | ||
78 | .shutdown = cabriolet_disable_irq, | ||
79 | .enable = cabriolet_enable_irq, | ||
80 | .disable = cabriolet_disable_irq, | ||
81 | .ack = cabriolet_disable_irq, | ||
82 | .end = cabriolet_end_irq, | ||
83 | }; | ||
84 | |||
85 | static void | ||
86 | cabriolet_device_interrupt(unsigned long v, struct pt_regs *r) | ||
87 | { | ||
88 | unsigned long pld; | ||
89 | unsigned int i; | ||
90 | |||
91 | /* Read the interrupt summary registers */ | ||
92 | pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16); | ||
93 | |||
94 | /* | ||
95 | * Now for every possible bit set, work through them and call | ||
96 | * the appropriate interrupt handler. | ||
97 | */ | ||
98 | while (pld) { | ||
99 | i = ffz(~pld); | ||
100 | pld &= pld - 1; /* clear least bit set */ | ||
101 | if (i == 4) { | ||
102 | isa_device_interrupt(v, r); | ||
103 | } else { | ||
104 | handle_irq(16 + i, r); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static void __init | ||
110 | common_init_irq(void (*srm_dev_int)(unsigned long v, struct pt_regs *r)) | ||
111 | { | ||
112 | init_i8259a_irqs(); | ||
113 | |||
114 | if (alpha_using_srm) { | ||
115 | alpha_mv.device_interrupt = srm_dev_int; | ||
116 | init_srm_irqs(35, 0); | ||
117 | } | ||
118 | else { | ||
119 | long i; | ||
120 | |||
121 | outb(0xff, 0x804); | ||
122 | outb(0xff, 0x805); | ||
123 | outb(0xff, 0x806); | ||
124 | |||
125 | for (i = 16; i < 35; ++i) { | ||
126 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
127 | irq_desc[i].handler = &cabriolet_irq_type; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | common_init_isa_dma(); | ||
132 | setup_irq(16+4, &isa_cascade_irqaction); | ||
133 | } | ||
134 | |||
135 | #ifndef CONFIG_ALPHA_PC164 | ||
136 | static void __init | ||
137 | cabriolet_init_irq(void) | ||
138 | { | ||
139 | common_init_irq(srm_device_interrupt); | ||
140 | } | ||
141 | #endif | ||
142 | |||
143 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_PC164) | ||
144 | /* In theory, the PC164 has the same interrupt hardware as the other | ||
145 | Cabriolet based systems. However, something got screwed up late | ||
146 | in the development cycle which broke the interrupt masking hardware. | ||
147 | Repeat, it is not possible to mask and ack interrupts. At all. | ||
148 | |||
149 | In an attempt to work around this, while processing interrupts, | ||
150 | we do not allow the IPL to drop below what it is currently. This | ||
151 | prevents the possibility of recursion. | ||
152 | |||
153 | ??? Another option might be to force all PCI devices to use edge | ||
154 | triggered rather than level triggered interrupts. That might be | ||
155 | too invasive though. */ | ||
156 | |||
157 | static void | ||
158 | pc164_srm_device_interrupt(unsigned long v, struct pt_regs *r) | ||
159 | { | ||
160 | __min_ipl = getipl(); | ||
161 | srm_device_interrupt(v, r); | ||
162 | __min_ipl = 0; | ||
163 | } | ||
164 | |||
165 | static void | ||
166 | pc164_device_interrupt(unsigned long v, struct pt_regs *r) | ||
167 | { | ||
168 | __min_ipl = getipl(); | ||
169 | cabriolet_device_interrupt(v, r); | ||
170 | __min_ipl = 0; | ||
171 | } | ||
172 | |||
173 | static void __init | ||
174 | pc164_init_irq(void) | ||
175 | { | ||
176 | common_init_irq(pc164_srm_device_interrupt); | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | /* | ||
181 | * The EB66+ is very similar to the EB66 except that it does not have | ||
182 | * the on-board NCR and Tulip chips. In the code below, I have used | ||
183 | * slot number to refer to the id select line and *not* the slot | ||
184 | * number used in the EB66+ documentation. However, in the table, | ||
185 | * I've given the slot number, the id select line and the Jxx number | ||
186 | * that's printed on the board. The interrupt pins from the PCI slots | ||
187 | * are wired into 3 interrupt summary registers at 0x804, 0x805 and | ||
188 | * 0x806 ISA. | ||
189 | * | ||
190 | * In the table, -1 means don't assign an IRQ number. This is usually | ||
191 | * because it is the Saturn IO (SIO) PCI/ISA Bridge Chip. | ||
192 | */ | ||
193 | |||
194 | static inline int __init | ||
195 | eb66p_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
196 | { | ||
197 | static char irq_tab[5][5] __initdata = { | ||
198 | /*INT INTA INTB INTC INTD */ | ||
199 | {16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */ | ||
200 | {16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */ | ||
201 | { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ | ||
202 | {16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 9, slot 2, J27 */ | ||
203 | {16+3, 16+3, 16+8, 16+12, 16+6} /* IdSel 10, slot 3, J28 */ | ||
204 | }; | ||
205 | const long min_idsel = 6, max_idsel = 10, irqs_per_slot = 5; | ||
206 | return COMMON_TABLE_LOOKUP; | ||
207 | } | ||
208 | |||
209 | |||
210 | /* | ||
211 | * The AlphaPC64 is very similar to the EB66+ except that its slots | ||
212 | * are numbered differently. In the code below, I have used slot | ||
213 | * number to refer to the id select line and *not* the slot number | ||
214 | * used in the AlphaPC64 documentation. However, in the table, I've | ||
215 | * given the slot number, the id select line and the Jxx number that's | ||
216 | * printed on the board. The interrupt pins from the PCI slots are | ||
217 | * wired into 3 interrupt summary registers at 0x804, 0x805 and 0x806 | ||
218 | * ISA. | ||
219 | * | ||
220 | * In the table, -1 means don't assign an IRQ number. This is usually | ||
221 | * because it is the Saturn IO (SIO) PCI/ISA Bridge Chip. | ||
222 | */ | ||
223 | |||
224 | static inline int __init | ||
225 | cabriolet_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
226 | { | ||
227 | static char irq_tab[5][5] __initdata = { | ||
228 | /*INT INTA INTB INTC INTD */ | ||
229 | { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */ | ||
230 | { 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */ | ||
231 | { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */ | ||
232 | { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ | ||
233 | { 16+3, 16+3, 16+8, 16+12, 16+16} /* IdSel 9, slot 3, J22 */ | ||
234 | }; | ||
235 | const long min_idsel = 5, max_idsel = 9, irqs_per_slot = 5; | ||
236 | return COMMON_TABLE_LOOKUP; | ||
237 | } | ||
238 | |||
239 | static inline void __init | ||
240 | cabriolet_init_pci(void) | ||
241 | { | ||
242 | common_init_pci(); | ||
243 | ns87312_enable_ide(0x398); | ||
244 | } | ||
245 | |||
246 | static inline void __init | ||
247 | cia_cab_init_pci(void) | ||
248 | { | ||
249 | cia_init_pci(); | ||
250 | ns87312_enable_ide(0x398); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * The PC164 and LX164 have 19 PCI interrupts, four from each of the four | ||
255 | * PCI slots, the SIO, PCI/IDE, and USB. | ||
256 | * | ||
257 | * Each of the interrupts can be individually masked. This is | ||
258 | * accomplished by setting the appropriate bit in the mask register. | ||
259 | * A bit is set by writing a "1" to the desired position in the mask | ||
260 | * register and cleared by writing a "0". There are 3 mask registers | ||
261 | * located at ISA address 804h, 805h and 806h. | ||
262 | * | ||
263 | * An I/O read at ISA address 804h, 805h, 806h will return the | ||
264 | * state of the 11 PCI interrupts and not the state of the MASKED | ||
265 | * interrupts. | ||
266 | * | ||
267 | * Note: A write to I/O 804h, 805h, and 806h the mask register will be | ||
268 | * updated. | ||
269 | * | ||
270 | * | ||
271 | * ISA DATA<7:0> | ||
272 | * ISA +--------------------------------------------------------------+ | ||
273 | * ADDRESS | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | ||
274 | * +==============================================================+ | ||
275 | * 0x804 | INTB0 | USB | IDE | SIO | INTA3 |INTA2 | INTA1 | INTA0 | | ||
276 | * +--------------------------------------------------------------+ | ||
277 | * 0x805 | INTD0 | INTC3 | INTC2 | INTC1 | INTC0 |INTB3 | INTB2 | INTB1 | | ||
278 | * +--------------------------------------------------------------+ | ||
279 | * 0x806 | Rsrv | Rsrv | Rsrv | Rsrv | Rsrv |INTD3 | INTD2 | INTD1 | | ||
280 | * +--------------------------------------------------------------+ | ||
281 | * * Rsrv = reserved bits | ||
282 | * Note: The mask register is write-only. | ||
283 | * | ||
284 | * IdSel | ||
285 | * 5 32 bit PCI option slot 2 | ||
286 | * 6 64 bit PCI option slot 0 | ||
287 | * 7 64 bit PCI option slot 1 | ||
288 | * 8 Saturn I/O | ||
289 | * 9 32 bit PCI option slot 3 | ||
290 | * 10 USB | ||
291 | * 11 IDE | ||
292 | * | ||
293 | */ | ||
294 | |||
295 | static inline int __init | ||
296 | alphapc164_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
297 | { | ||
298 | static char irq_tab[7][5] __initdata = { | ||
299 | /*INT INTA INTB INTC INTD */ | ||
300 | { 16+2, 16+2, 16+9, 16+13, 16+17}, /* IdSel 5, slot 2, J20 */ | ||
301 | { 16+0, 16+0, 16+7, 16+11, 16+15}, /* IdSel 6, slot 0, J29 */ | ||
302 | { 16+1, 16+1, 16+8, 16+12, 16+16}, /* IdSel 7, slot 1, J26 */ | ||
303 | { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ | ||
304 | { 16+3, 16+3, 16+10, 16+14, 16+18}, /* IdSel 9, slot 3, J19 */ | ||
305 | { 16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 10, USB */ | ||
306 | { 16+5, 16+5, 16+5, 16+5, 16+5} /* IdSel 11, IDE */ | ||
307 | }; | ||
308 | const long min_idsel = 5, max_idsel = 11, irqs_per_slot = 5; | ||
309 | return COMMON_TABLE_LOOKUP; | ||
310 | } | ||
311 | |||
312 | static inline void __init | ||
313 | alphapc164_init_pci(void) | ||
314 | { | ||
315 | cia_init_pci(); | ||
316 | SMC93x_Init(); | ||
317 | } | ||
318 | |||
319 | |||
320 | /* | ||
321 | * The System Vector | ||
322 | */ | ||
323 | |||
324 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_CABRIOLET) | ||
325 | struct alpha_machine_vector cabriolet_mv __initmv = { | ||
326 | .vector_name = "Cabriolet", | ||
327 | DO_EV4_MMU, | ||
328 | DO_DEFAULT_RTC, | ||
329 | DO_APECS_IO, | ||
330 | .machine_check = apecs_machine_check, | ||
331 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
332 | .min_io_address = DEFAULT_IO_BASE, | ||
333 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
334 | |||
335 | .nr_irqs = 35, | ||
336 | .device_interrupt = cabriolet_device_interrupt, | ||
337 | |||
338 | .init_arch = apecs_init_arch, | ||
339 | .init_irq = cabriolet_init_irq, | ||
340 | .init_rtc = common_init_rtc, | ||
341 | .init_pci = cabriolet_init_pci, | ||
342 | .pci_map_irq = cabriolet_map_irq, | ||
343 | .pci_swizzle = common_swizzle, | ||
344 | }; | ||
345 | #ifndef CONFIG_ALPHA_EB64P | ||
346 | ALIAS_MV(cabriolet) | ||
347 | #endif | ||
348 | #endif | ||
349 | |||
350 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EB164) | ||
351 | struct alpha_machine_vector eb164_mv __initmv = { | ||
352 | .vector_name = "EB164", | ||
353 | DO_EV5_MMU, | ||
354 | DO_DEFAULT_RTC, | ||
355 | DO_CIA_IO, | ||
356 | .machine_check = cia_machine_check, | ||
357 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
358 | .min_io_address = DEFAULT_IO_BASE, | ||
359 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
360 | |||
361 | .nr_irqs = 35, | ||
362 | .device_interrupt = cabriolet_device_interrupt, | ||
363 | |||
364 | .init_arch = cia_init_arch, | ||
365 | .init_irq = cabriolet_init_irq, | ||
366 | .init_rtc = common_init_rtc, | ||
367 | .init_pci = cia_cab_init_pci, | ||
368 | .kill_arch = cia_kill_arch, | ||
369 | .pci_map_irq = cabriolet_map_irq, | ||
370 | .pci_swizzle = common_swizzle, | ||
371 | }; | ||
372 | ALIAS_MV(eb164) | ||
373 | #endif | ||
374 | |||
375 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EB66P) | ||
376 | struct alpha_machine_vector eb66p_mv __initmv = { | ||
377 | .vector_name = "EB66+", | ||
378 | DO_EV4_MMU, | ||
379 | DO_DEFAULT_RTC, | ||
380 | DO_LCA_IO, | ||
381 | .machine_check = lca_machine_check, | ||
382 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
383 | .min_io_address = DEFAULT_IO_BASE, | ||
384 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
385 | |||
386 | .nr_irqs = 35, | ||
387 | .device_interrupt = cabriolet_device_interrupt, | ||
388 | |||
389 | .init_arch = lca_init_arch, | ||
390 | .init_irq = cabriolet_init_irq, | ||
391 | .init_rtc = common_init_rtc, | ||
392 | .init_pci = cabriolet_init_pci, | ||
393 | .pci_map_irq = eb66p_map_irq, | ||
394 | .pci_swizzle = common_swizzle, | ||
395 | }; | ||
396 | ALIAS_MV(eb66p) | ||
397 | #endif | ||
398 | |||
399 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_LX164) | ||
400 | struct alpha_machine_vector lx164_mv __initmv = { | ||
401 | .vector_name = "LX164", | ||
402 | DO_EV5_MMU, | ||
403 | DO_DEFAULT_RTC, | ||
404 | DO_PYXIS_IO, | ||
405 | .machine_check = cia_machine_check, | ||
406 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
407 | .min_io_address = DEFAULT_IO_BASE, | ||
408 | .min_mem_address = DEFAULT_MEM_BASE, | ||
409 | .pci_dac_offset = PYXIS_DAC_OFFSET, | ||
410 | |||
411 | .nr_irqs = 35, | ||
412 | .device_interrupt = cabriolet_device_interrupt, | ||
413 | |||
414 | .init_arch = pyxis_init_arch, | ||
415 | .init_irq = cabriolet_init_irq, | ||
416 | .init_rtc = common_init_rtc, | ||
417 | .init_pci = alphapc164_init_pci, | ||
418 | .kill_arch = cia_kill_arch, | ||
419 | .pci_map_irq = alphapc164_map_irq, | ||
420 | .pci_swizzle = common_swizzle, | ||
421 | }; | ||
422 | ALIAS_MV(lx164) | ||
423 | #endif | ||
424 | |||
425 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_PC164) | ||
426 | struct alpha_machine_vector pc164_mv __initmv = { | ||
427 | .vector_name = "PC164", | ||
428 | DO_EV5_MMU, | ||
429 | DO_DEFAULT_RTC, | ||
430 | DO_CIA_IO, | ||
431 | .machine_check = cia_machine_check, | ||
432 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
433 | .min_io_address = DEFAULT_IO_BASE, | ||
434 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
435 | |||
436 | .nr_irqs = 35, | ||
437 | .device_interrupt = pc164_device_interrupt, | ||
438 | |||
439 | .init_arch = cia_init_arch, | ||
440 | .init_irq = pc164_init_irq, | ||
441 | .init_rtc = common_init_rtc, | ||
442 | .init_pci = alphapc164_init_pci, | ||
443 | .kill_arch = cia_kill_arch, | ||
444 | .pci_map_irq = alphapc164_map_irq, | ||
445 | .pci_swizzle = common_swizzle, | ||
446 | }; | ||
447 | ALIAS_MV(pc164) | ||
448 | #endif | ||
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c new file mode 100644 index 000000000000..9e36b07fa940 --- /dev/null +++ b/arch/alpha/kernel/sys_dp264.c | |||
@@ -0,0 +1,689 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_dp264.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996, 1999 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Modified by Christopher C. Chimelis, 2001 to | ||
9 | * add support for the addition of Shark to the | ||
10 | * Tsunami family. | ||
11 | * | ||
12 | * Code supporting the DP264 (EV6+TSUNAMI). | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/bitops.h> | ||
23 | |||
24 | #include <asm/ptrace.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/dma.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/mmu_context.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/pgtable.h> | ||
31 | #include <asm/core_tsunami.h> | ||
32 | #include <asm/hwrpb.h> | ||
33 | #include <asm/tlbflush.h> | ||
34 | |||
35 | #include "proto.h" | ||
36 | #include "irq_impl.h" | ||
37 | #include "pci_impl.h" | ||
38 | #include "machvec_impl.h" | ||
39 | |||
40 | |||
41 | /* Note mask bit is true for ENABLED irqs. */ | ||
42 | static unsigned long cached_irq_mask; | ||
43 | /* dp264 boards handle at max four CPUs */ | ||
44 | static unsigned long cpu_irq_affinity[4] = { 0UL, 0UL, 0UL, 0UL }; | ||
45 | |||
46 | DEFINE_SPINLOCK(dp264_irq_lock); | ||
47 | |||
48 | static void | ||
49 | tsunami_update_irq_hw(unsigned long mask) | ||
50 | { | ||
51 | register tsunami_cchip *cchip = TSUNAMI_cchip; | ||
52 | unsigned long isa_enable = 1UL << 55; | ||
53 | register int bcpu = boot_cpuid; | ||
54 | |||
55 | #ifdef CONFIG_SMP | ||
56 | volatile unsigned long *dim0, *dim1, *dim2, *dim3; | ||
57 | unsigned long mask0, mask1, mask2, mask3, dummy; | ||
58 | |||
59 | mask &= ~isa_enable; | ||
60 | mask0 = mask & cpu_irq_affinity[0]; | ||
61 | mask1 = mask & cpu_irq_affinity[1]; | ||
62 | mask2 = mask & cpu_irq_affinity[2]; | ||
63 | mask3 = mask & cpu_irq_affinity[3]; | ||
64 | |||
65 | if (bcpu == 0) mask0 |= isa_enable; | ||
66 | else if (bcpu == 1) mask1 |= isa_enable; | ||
67 | else if (bcpu == 2) mask2 |= isa_enable; | ||
68 | else mask3 |= isa_enable; | ||
69 | |||
70 | dim0 = &cchip->dim0.csr; | ||
71 | dim1 = &cchip->dim1.csr; | ||
72 | dim2 = &cchip->dim2.csr; | ||
73 | dim3 = &cchip->dim3.csr; | ||
74 | if (!cpu_possible(0)) dim0 = &dummy; | ||
75 | if (!cpu_possible(1)) dim1 = &dummy; | ||
76 | if (!cpu_possible(2)) dim2 = &dummy; | ||
77 | if (!cpu_possible(3)) dim3 = &dummy; | ||
78 | |||
79 | *dim0 = mask0; | ||
80 | *dim1 = mask1; | ||
81 | *dim2 = mask2; | ||
82 | *dim3 = mask3; | ||
83 | mb(); | ||
84 | *dim0; | ||
85 | *dim1; | ||
86 | *dim2; | ||
87 | *dim3; | ||
88 | #else | ||
89 | volatile unsigned long *dimB; | ||
90 | if (bcpu == 0) dimB = &cchip->dim0.csr; | ||
91 | else if (bcpu == 1) dimB = &cchip->dim1.csr; | ||
92 | else if (bcpu == 2) dimB = &cchip->dim2.csr; | ||
93 | else dimB = &cchip->dim3.csr; | ||
94 | |||
95 | *dimB = mask | isa_enable; | ||
96 | mb(); | ||
97 | *dimB; | ||
98 | #endif | ||
99 | } | ||
100 | |||
101 | static void | ||
102 | dp264_enable_irq(unsigned int irq) | ||
103 | { | ||
104 | spin_lock(&dp264_irq_lock); | ||
105 | cached_irq_mask |= 1UL << irq; | ||
106 | tsunami_update_irq_hw(cached_irq_mask); | ||
107 | spin_unlock(&dp264_irq_lock); | ||
108 | } | ||
109 | |||
110 | static void | ||
111 | dp264_disable_irq(unsigned int irq) | ||
112 | { | ||
113 | spin_lock(&dp264_irq_lock); | ||
114 | cached_irq_mask &= ~(1UL << irq); | ||
115 | tsunami_update_irq_hw(cached_irq_mask); | ||
116 | spin_unlock(&dp264_irq_lock); | ||
117 | } | ||
118 | |||
119 | static unsigned int | ||
120 | dp264_startup_irq(unsigned int irq) | ||
121 | { | ||
122 | dp264_enable_irq(irq); | ||
123 | return 0; /* never anything pending */ | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | dp264_end_irq(unsigned int irq) | ||
128 | { | ||
129 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
130 | dp264_enable_irq(irq); | ||
131 | } | ||
132 | |||
133 | static void | ||
134 | clipper_enable_irq(unsigned int irq) | ||
135 | { | ||
136 | spin_lock(&dp264_irq_lock); | ||
137 | cached_irq_mask |= 1UL << (irq - 16); | ||
138 | tsunami_update_irq_hw(cached_irq_mask); | ||
139 | spin_unlock(&dp264_irq_lock); | ||
140 | } | ||
141 | |||
142 | static void | ||
143 | clipper_disable_irq(unsigned int irq) | ||
144 | { | ||
145 | spin_lock(&dp264_irq_lock); | ||
146 | cached_irq_mask &= ~(1UL << (irq - 16)); | ||
147 | tsunami_update_irq_hw(cached_irq_mask); | ||
148 | spin_unlock(&dp264_irq_lock); | ||
149 | } | ||
150 | |||
151 | static unsigned int | ||
152 | clipper_startup_irq(unsigned int irq) | ||
153 | { | ||
154 | clipper_enable_irq(irq); | ||
155 | return 0; /* never anything pending */ | ||
156 | } | ||
157 | |||
158 | static void | ||
159 | clipper_end_irq(unsigned int irq) | ||
160 | { | ||
161 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
162 | clipper_enable_irq(irq); | ||
163 | } | ||
164 | |||
165 | static void | ||
166 | cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) | ||
167 | { | ||
168 | int cpu; | ||
169 | |||
170 | for (cpu = 0; cpu < 4; cpu++) { | ||
171 | unsigned long aff = cpu_irq_affinity[cpu]; | ||
172 | if (cpu_isset(cpu, affinity)) | ||
173 | aff |= 1UL << irq; | ||
174 | else | ||
175 | aff &= ~(1UL << irq); | ||
176 | cpu_irq_affinity[cpu] = aff; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static void | ||
181 | dp264_set_affinity(unsigned int irq, cpumask_t affinity) | ||
182 | { | ||
183 | spin_lock(&dp264_irq_lock); | ||
184 | cpu_set_irq_affinity(irq, affinity); | ||
185 | tsunami_update_irq_hw(cached_irq_mask); | ||
186 | spin_unlock(&dp264_irq_lock); | ||
187 | } | ||
188 | |||
189 | static void | ||
190 | clipper_set_affinity(unsigned int irq, cpumask_t affinity) | ||
191 | { | ||
192 | spin_lock(&dp264_irq_lock); | ||
193 | cpu_set_irq_affinity(irq - 16, affinity); | ||
194 | tsunami_update_irq_hw(cached_irq_mask); | ||
195 | spin_unlock(&dp264_irq_lock); | ||
196 | } | ||
197 | |||
198 | static struct hw_interrupt_type dp264_irq_type = { | ||
199 | .typename = "DP264", | ||
200 | .startup = dp264_startup_irq, | ||
201 | .shutdown = dp264_disable_irq, | ||
202 | .enable = dp264_enable_irq, | ||
203 | .disable = dp264_disable_irq, | ||
204 | .ack = dp264_disable_irq, | ||
205 | .end = dp264_end_irq, | ||
206 | .set_affinity = dp264_set_affinity, | ||
207 | }; | ||
208 | |||
209 | static struct hw_interrupt_type clipper_irq_type = { | ||
210 | .typename = "CLIPPER", | ||
211 | .startup = clipper_startup_irq, | ||
212 | .shutdown = clipper_disable_irq, | ||
213 | .enable = clipper_enable_irq, | ||
214 | .disable = clipper_disable_irq, | ||
215 | .ack = clipper_disable_irq, | ||
216 | .end = clipper_end_irq, | ||
217 | .set_affinity = clipper_set_affinity, | ||
218 | }; | ||
219 | |||
220 | static void | ||
221 | dp264_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
222 | { | ||
223 | #if 1 | ||
224 | printk("dp264_device_interrupt: NOT IMPLEMENTED YET!! \n"); | ||
225 | #else | ||
226 | unsigned long pld; | ||
227 | unsigned int i; | ||
228 | |||
229 | /* Read the interrupt summary register of TSUNAMI */ | ||
230 | pld = TSUNAMI_cchip->dir0.csr; | ||
231 | |||
232 | /* | ||
233 | * Now for every possible bit set, work through them and call | ||
234 | * the appropriate interrupt handler. | ||
235 | */ | ||
236 | while (pld) { | ||
237 | i = ffz(~pld); | ||
238 | pld &= pld - 1; /* clear least bit set */ | ||
239 | if (i == 55) | ||
240 | isa_device_interrupt(vector, regs); | ||
241 | else | ||
242 | handle_irq(16 + i, 16 + i, regs); | ||
243 | #if 0 | ||
244 | TSUNAMI_cchip->dir0.csr = 1UL << i; mb(); | ||
245 | tmp = TSUNAMI_cchip->dir0.csr; | ||
246 | #endif | ||
247 | } | ||
248 | #endif | ||
249 | } | ||
250 | |||
251 | static void | ||
252 | dp264_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
253 | { | ||
254 | int irq; | ||
255 | |||
256 | irq = (vector - 0x800) >> 4; | ||
257 | |||
258 | /* | ||
259 | * The SRM console reports PCI interrupts with a vector calculated by: | ||
260 | * | ||
261 | * 0x900 + (0x10 * DRIR-bit) | ||
262 | * | ||
263 | * So bit 16 shows up as IRQ 32, etc. | ||
264 | * | ||
265 | * On DP264/BRICK/MONET, we adjust it down by 16 because at least | ||
266 | * that many of the low order bits of the DRIR are not used, and | ||
267 | * so we don't count them. | ||
268 | */ | ||
269 | if (irq >= 32) | ||
270 | irq -= 16; | ||
271 | |||
272 | handle_irq(irq, regs); | ||
273 | } | ||
274 | |||
275 | static void | ||
276 | clipper_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
277 | { | ||
278 | int irq; | ||
279 | |||
280 | irq = (vector - 0x800) >> 4; | ||
281 | |||
282 | /* | ||
283 | * The SRM console reports PCI interrupts with a vector calculated by: | ||
284 | * | ||
285 | * 0x900 + (0x10 * DRIR-bit) | ||
286 | * | ||
287 | * So bit 16 shows up as IRQ 32, etc. | ||
288 | * | ||
289 | * CLIPPER uses bits 8-47 for PCI interrupts, so we do not need | ||
290 | * to scale down the vector reported, we just use it. | ||
291 | * | ||
292 | * Eg IRQ 24 is DRIR bit 8, etc, etc | ||
293 | */ | ||
294 | handle_irq(irq, regs); | ||
295 | } | ||
296 | |||
297 | static void __init | ||
298 | init_tsunami_irqs(struct hw_interrupt_type * ops, int imin, int imax) | ||
299 | { | ||
300 | long i; | ||
301 | for (i = imin; i <= imax; ++i) { | ||
302 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
303 | irq_desc[i].handler = ops; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | static void __init | ||
308 | dp264_init_irq(void) | ||
309 | { | ||
310 | outb(0, DMA1_RESET_REG); | ||
311 | outb(0, DMA2_RESET_REG); | ||
312 | outb(DMA_MODE_CASCADE, DMA2_MODE_REG); | ||
313 | outb(0, DMA2_MASK_REG); | ||
314 | |||
315 | if (alpha_using_srm) | ||
316 | alpha_mv.device_interrupt = dp264_srm_device_interrupt; | ||
317 | |||
318 | tsunami_update_irq_hw(0); | ||
319 | |||
320 | init_i8259a_irqs(); | ||
321 | init_tsunami_irqs(&dp264_irq_type, 16, 47); | ||
322 | } | ||
323 | |||
324 | static void __init | ||
325 | clipper_init_irq(void) | ||
326 | { | ||
327 | outb(0, DMA1_RESET_REG); | ||
328 | outb(0, DMA2_RESET_REG); | ||
329 | outb(DMA_MODE_CASCADE, DMA2_MODE_REG); | ||
330 | outb(0, DMA2_MASK_REG); | ||
331 | |||
332 | if (alpha_using_srm) | ||
333 | alpha_mv.device_interrupt = clipper_srm_device_interrupt; | ||
334 | |||
335 | tsunami_update_irq_hw(0); | ||
336 | |||
337 | init_i8259a_irqs(); | ||
338 | init_tsunami_irqs(&clipper_irq_type, 24, 63); | ||
339 | } | ||
340 | |||
341 | |||
342 | /* | ||
343 | * PCI Fixup configuration. | ||
344 | * | ||
345 | * Summary @ TSUNAMI_CSR_DIM0: | ||
346 | * Bit Meaning | ||
347 | * 0-17 Unused | ||
348 | *18 Interrupt SCSI B (Adaptec 7895 builtin) | ||
349 | *19 Interrupt SCSI A (Adaptec 7895 builtin) | ||
350 | *20 Interrupt Line D from slot 2 PCI0 | ||
351 | *21 Interrupt Line C from slot 2 PCI0 | ||
352 | *22 Interrupt Line B from slot 2 PCI0 | ||
353 | *23 Interrupt Line A from slot 2 PCI0 | ||
354 | *24 Interrupt Line D from slot 1 PCI0 | ||
355 | *25 Interrupt Line C from slot 1 PCI0 | ||
356 | *26 Interrupt Line B from slot 1 PCI0 | ||
357 | *27 Interrupt Line A from slot 1 PCI0 | ||
358 | *28 Interrupt Line D from slot 0 PCI0 | ||
359 | *29 Interrupt Line C from slot 0 PCI0 | ||
360 | *30 Interrupt Line B from slot 0 PCI0 | ||
361 | *31 Interrupt Line A from slot 0 PCI0 | ||
362 | * | ||
363 | *32 Interrupt Line D from slot 3 PCI1 | ||
364 | *33 Interrupt Line C from slot 3 PCI1 | ||
365 | *34 Interrupt Line B from slot 3 PCI1 | ||
366 | *35 Interrupt Line A from slot 3 PCI1 | ||
367 | *36 Interrupt Line D from slot 2 PCI1 | ||
368 | *37 Interrupt Line C from slot 2 PCI1 | ||
369 | *38 Interrupt Line B from slot 2 PCI1 | ||
370 | *39 Interrupt Line A from slot 2 PCI1 | ||
371 | *40 Interrupt Line D from slot 1 PCI1 | ||
372 | *41 Interrupt Line C from slot 1 PCI1 | ||
373 | *42 Interrupt Line B from slot 1 PCI1 | ||
374 | *43 Interrupt Line A from slot 1 PCI1 | ||
375 | *44 Interrupt Line D from slot 0 PCI1 | ||
376 | *45 Interrupt Line C from slot 0 PCI1 | ||
377 | *46 Interrupt Line B from slot 0 PCI1 | ||
378 | *47 Interrupt Line A from slot 0 PCI1 | ||
379 | *48-52 Unused | ||
380 | *53 PCI0 NMI (from Cypress) | ||
381 | *54 PCI0 SMI INT (from Cypress) | ||
382 | *55 PCI0 ISA Interrupt (from Cypress) | ||
383 | *56-60 Unused | ||
384 | *61 PCI1 Bus Error | ||
385 | *62 PCI0 Bus Error | ||
386 | *63 Reserved | ||
387 | * | ||
388 | * IdSel | ||
389 | * 5 Cypress Bridge I/O | ||
390 | * 6 SCSI Adaptec builtin | ||
391 | * 7 64 bit PCI option slot 0 (all busses) | ||
392 | * 8 64 bit PCI option slot 1 (all busses) | ||
393 | * 9 64 bit PCI option slot 2 (all busses) | ||
394 | * 10 64 bit PCI option slot 3 (not bus 0) | ||
395 | */ | ||
396 | |||
397 | static int __init | ||
398 | dp264_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
399 | { | ||
400 | static char irq_tab[6][5] __initdata = { | ||
401 | /*INT INTA INTB INTC INTD */ | ||
402 | { -1, -1, -1, -1, -1}, /* IdSel 5 ISA Bridge */ | ||
403 | { 16+ 3, 16+ 3, 16+ 2, 16+ 2, 16+ 2}, /* IdSel 6 SCSI builtin*/ | ||
404 | { 16+15, 16+15, 16+14, 16+13, 16+12}, /* IdSel 7 slot 0 */ | ||
405 | { 16+11, 16+11, 16+10, 16+ 9, 16+ 8}, /* IdSel 8 slot 1 */ | ||
406 | { 16+ 7, 16+ 7, 16+ 6, 16+ 5, 16+ 4}, /* IdSel 9 slot 2 */ | ||
407 | { 16+ 3, 16+ 3, 16+ 2, 16+ 1, 16+ 0} /* IdSel 10 slot 3 */ | ||
408 | }; | ||
409 | const long min_idsel = 5, max_idsel = 10, irqs_per_slot = 5; | ||
410 | |||
411 | struct pci_controller *hose = dev->sysdata; | ||
412 | int irq = COMMON_TABLE_LOOKUP; | ||
413 | |||
414 | if (irq > 0) { | ||
415 | irq += 16 * hose->index; | ||
416 | } else { | ||
417 | /* ??? The Contaq IDE controller on the ISA bridge uses | ||
418 | "legacy" interrupts 14 and 15. I don't know if anything | ||
419 | can wind up at the same slot+pin on hose1, so we'll | ||
420 | just have to trust whatever value the console might | ||
421 | have assigned. */ | ||
422 | |||
423 | u8 irq8; | ||
424 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq8); | ||
425 | irq = irq8; | ||
426 | } | ||
427 | |||
428 | return irq; | ||
429 | } | ||
430 | |||
431 | static int __init | ||
432 | monet_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
433 | { | ||
434 | static char irq_tab[13][5] __initdata = { | ||
435 | /*INT INTA INTB INTC INTD */ | ||
436 | { 45, 45, 45, 45, 45}, /* IdSel 3 21143 PCI1 */ | ||
437 | { -1, -1, -1, -1, -1}, /* IdSel 4 unused */ | ||
438 | { -1, -1, -1, -1, -1}, /* IdSel 5 unused */ | ||
439 | { 47, 47, 47, 47, 47}, /* IdSel 6 SCSI PCI1 */ | ||
440 | { -1, -1, -1, -1, -1}, /* IdSel 7 ISA Bridge */ | ||
441 | { -1, -1, -1, -1, -1}, /* IdSel 8 P2P PCI1 */ | ||
442 | #if 1 | ||
443 | { 28, 28, 29, 30, 31}, /* IdSel 14 slot 4 PCI2*/ | ||
444 | { 24, 24, 25, 26, 27}, /* IdSel 15 slot 5 PCI2*/ | ||
445 | #else | ||
446 | { -1, -1, -1, -1, -1}, /* IdSel 9 unused */ | ||
447 | { -1, -1, -1, -1, -1}, /* IdSel 10 unused */ | ||
448 | #endif | ||
449 | { 40, 40, 41, 42, 43}, /* IdSel 11 slot 1 PCI0*/ | ||
450 | { 36, 36, 37, 38, 39}, /* IdSel 12 slot 2 PCI0*/ | ||
451 | { 32, 32, 33, 34, 35}, /* IdSel 13 slot 3 PCI0*/ | ||
452 | { 28, 28, 29, 30, 31}, /* IdSel 14 slot 4 PCI2*/ | ||
453 | { 24, 24, 25, 26, 27} /* IdSel 15 slot 5 PCI2*/ | ||
454 | }; | ||
455 | const long min_idsel = 3, max_idsel = 15, irqs_per_slot = 5; | ||
456 | return COMMON_TABLE_LOOKUP; | ||
457 | } | ||
458 | |||
459 | static u8 __init | ||
460 | monet_swizzle(struct pci_dev *dev, u8 *pinp) | ||
461 | { | ||
462 | struct pci_controller *hose = dev->sysdata; | ||
463 | int slot, pin = *pinp; | ||
464 | |||
465 | if (!dev->bus->parent) { | ||
466 | slot = PCI_SLOT(dev->devfn); | ||
467 | } | ||
468 | /* Check for the built-in bridge on hose 1. */ | ||
469 | else if (hose->index == 1 && PCI_SLOT(dev->bus->self->devfn) == 8) { | ||
470 | slot = PCI_SLOT(dev->devfn); | ||
471 | } else { | ||
472 | /* Must be a card-based bridge. */ | ||
473 | do { | ||
474 | /* Check for built-in bridge on hose 1. */ | ||
475 | if (hose->index == 1 && | ||
476 | PCI_SLOT(dev->bus->self->devfn) == 8) { | ||
477 | slot = PCI_SLOT(dev->devfn); | ||
478 | break; | ||
479 | } | ||
480 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; | ||
481 | |||
482 | /* Move up the chain of bridges. */ | ||
483 | dev = dev->bus->self; | ||
484 | /* Slot of the next bridge. */ | ||
485 | slot = PCI_SLOT(dev->devfn); | ||
486 | } while (dev->bus->self); | ||
487 | } | ||
488 | *pinp = pin; | ||
489 | return slot; | ||
490 | } | ||
491 | |||
492 | static int __init | ||
493 | webbrick_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
494 | { | ||
495 | static char irq_tab[13][5] __initdata = { | ||
496 | /*INT INTA INTB INTC INTD */ | ||
497 | { -1, -1, -1, -1, -1}, /* IdSel 7 ISA Bridge */ | ||
498 | { -1, -1, -1, -1, -1}, /* IdSel 8 unused */ | ||
499 | { 29, 29, 29, 29, 29}, /* IdSel 9 21143 #1 */ | ||
500 | { -1, -1, -1, -1, -1}, /* IdSel 10 unused */ | ||
501 | { 30, 30, 30, 30, 30}, /* IdSel 11 21143 #2 */ | ||
502 | { -1, -1, -1, -1, -1}, /* IdSel 12 unused */ | ||
503 | { -1, -1, -1, -1, -1}, /* IdSel 13 unused */ | ||
504 | { 35, 35, 34, 33, 32}, /* IdSel 14 slot 0 */ | ||
505 | { 39, 39, 38, 37, 36}, /* IdSel 15 slot 1 */ | ||
506 | { 43, 43, 42, 41, 40}, /* IdSel 16 slot 2 */ | ||
507 | { 47, 47, 46, 45, 44}, /* IdSel 17 slot 3 */ | ||
508 | }; | ||
509 | const long min_idsel = 7, max_idsel = 17, irqs_per_slot = 5; | ||
510 | return COMMON_TABLE_LOOKUP; | ||
511 | } | ||
512 | |||
513 | static int __init | ||
514 | clipper_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
515 | { | ||
516 | static char irq_tab[7][5] __initdata = { | ||
517 | /*INT INTA INTB INTC INTD */ | ||
518 | { 16+ 8, 16+ 8, 16+ 9, 16+10, 16+11}, /* IdSel 1 slot 1 */ | ||
519 | { 16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 2 slot 2 */ | ||
520 | { 16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 3 slot 3 */ | ||
521 | { 16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 4 slot 4 */ | ||
522 | { 16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 5 slot 5 */ | ||
523 | { 16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 6 slot 6 */ | ||
524 | { -1, -1, -1, -1, -1} /* IdSel 7 ISA Bridge */ | ||
525 | }; | ||
526 | const long min_idsel = 1, max_idsel = 7, irqs_per_slot = 5; | ||
527 | |||
528 | struct pci_controller *hose = dev->sysdata; | ||
529 | int irq = COMMON_TABLE_LOOKUP; | ||
530 | |||
531 | if (irq > 0) | ||
532 | irq += 16 * hose->index; | ||
533 | |||
534 | return irq; | ||
535 | } | ||
536 | |||
537 | static void __init | ||
538 | dp264_init_pci(void) | ||
539 | { | ||
540 | common_init_pci(); | ||
541 | SMC669_Init(0); | ||
542 | } | ||
543 | |||
544 | static void __init | ||
545 | monet_init_pci(void) | ||
546 | { | ||
547 | common_init_pci(); | ||
548 | SMC669_Init(1); | ||
549 | es1888_init(); | ||
550 | } | ||
551 | |||
552 | static void __init | ||
553 | webbrick_init_arch(void) | ||
554 | { | ||
555 | tsunami_init_arch(); | ||
556 | |||
557 | /* Tsunami caches 4 PTEs at a time; DS10 has only 1 hose. */ | ||
558 | hose_head->sg_isa->align_entry = 4; | ||
559 | hose_head->sg_pci->align_entry = 4; | ||
560 | } | ||
561 | |||
562 | |||
563 | /* | ||
564 | * The System Vectors | ||
565 | */ | ||
566 | |||
567 | struct alpha_machine_vector dp264_mv __initmv = { | ||
568 | .vector_name = "DP264", | ||
569 | DO_EV6_MMU, | ||
570 | DO_DEFAULT_RTC, | ||
571 | DO_TSUNAMI_IO, | ||
572 | .machine_check = tsunami_machine_check, | ||
573 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
574 | .min_io_address = DEFAULT_IO_BASE, | ||
575 | .min_mem_address = DEFAULT_MEM_BASE, | ||
576 | .pci_dac_offset = TSUNAMI_DAC_OFFSET, | ||
577 | |||
578 | .nr_irqs = 64, | ||
579 | .device_interrupt = dp264_device_interrupt, | ||
580 | |||
581 | .init_arch = tsunami_init_arch, | ||
582 | .init_irq = dp264_init_irq, | ||
583 | .init_rtc = common_init_rtc, | ||
584 | .init_pci = dp264_init_pci, | ||
585 | .kill_arch = tsunami_kill_arch, | ||
586 | .pci_map_irq = dp264_map_irq, | ||
587 | .pci_swizzle = common_swizzle, | ||
588 | }; | ||
589 | ALIAS_MV(dp264) | ||
590 | |||
591 | struct alpha_machine_vector monet_mv __initmv = { | ||
592 | .vector_name = "Monet", | ||
593 | DO_EV6_MMU, | ||
594 | DO_DEFAULT_RTC, | ||
595 | DO_TSUNAMI_IO, | ||
596 | .machine_check = tsunami_machine_check, | ||
597 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
598 | .min_io_address = DEFAULT_IO_BASE, | ||
599 | .min_mem_address = DEFAULT_MEM_BASE, | ||
600 | .pci_dac_offset = TSUNAMI_DAC_OFFSET, | ||
601 | |||
602 | .nr_irqs = 64, | ||
603 | .device_interrupt = dp264_device_interrupt, | ||
604 | |||
605 | .init_arch = tsunami_init_arch, | ||
606 | .init_irq = dp264_init_irq, | ||
607 | .init_rtc = common_init_rtc, | ||
608 | .init_pci = monet_init_pci, | ||
609 | .kill_arch = tsunami_kill_arch, | ||
610 | .pci_map_irq = monet_map_irq, | ||
611 | .pci_swizzle = monet_swizzle, | ||
612 | }; | ||
613 | |||
614 | struct alpha_machine_vector webbrick_mv __initmv = { | ||
615 | .vector_name = "Webbrick", | ||
616 | DO_EV6_MMU, | ||
617 | DO_DEFAULT_RTC, | ||
618 | DO_TSUNAMI_IO, | ||
619 | .machine_check = tsunami_machine_check, | ||
620 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
621 | .min_io_address = DEFAULT_IO_BASE, | ||
622 | .min_mem_address = DEFAULT_MEM_BASE, | ||
623 | .pci_dac_offset = TSUNAMI_DAC_OFFSET, | ||
624 | |||
625 | .nr_irqs = 64, | ||
626 | .device_interrupt = dp264_device_interrupt, | ||
627 | |||
628 | .init_arch = webbrick_init_arch, | ||
629 | .init_irq = dp264_init_irq, | ||
630 | .init_rtc = common_init_rtc, | ||
631 | .init_pci = common_init_pci, | ||
632 | .kill_arch = tsunami_kill_arch, | ||
633 | .pci_map_irq = webbrick_map_irq, | ||
634 | .pci_swizzle = common_swizzle, | ||
635 | }; | ||
636 | |||
637 | struct alpha_machine_vector clipper_mv __initmv = { | ||
638 | .vector_name = "Clipper", | ||
639 | DO_EV6_MMU, | ||
640 | DO_DEFAULT_RTC, | ||
641 | DO_TSUNAMI_IO, | ||
642 | .machine_check = tsunami_machine_check, | ||
643 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
644 | .min_io_address = DEFAULT_IO_BASE, | ||
645 | .min_mem_address = DEFAULT_MEM_BASE, | ||
646 | .pci_dac_offset = TSUNAMI_DAC_OFFSET, | ||
647 | |||
648 | .nr_irqs = 64, | ||
649 | .device_interrupt = dp264_device_interrupt, | ||
650 | |||
651 | .init_arch = tsunami_init_arch, | ||
652 | .init_irq = clipper_init_irq, | ||
653 | .init_rtc = common_init_rtc, | ||
654 | .init_pci = common_init_pci, | ||
655 | .kill_arch = tsunami_kill_arch, | ||
656 | .pci_map_irq = clipper_map_irq, | ||
657 | .pci_swizzle = common_swizzle, | ||
658 | }; | ||
659 | |||
660 | /* Sharks strongly resemble Clipper, at least as far | ||
661 | * as interrupt routing, etc, so we're using the | ||
662 | * same functions as Clipper does | ||
663 | */ | ||
664 | |||
665 | struct alpha_machine_vector shark_mv __initmv = { | ||
666 | .vector_name = "Shark", | ||
667 | DO_EV6_MMU, | ||
668 | DO_DEFAULT_RTC, | ||
669 | DO_TSUNAMI_IO, | ||
670 | .machine_check = tsunami_machine_check, | ||
671 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
672 | .min_io_address = DEFAULT_IO_BASE, | ||
673 | .min_mem_address = DEFAULT_MEM_BASE, | ||
674 | .pci_dac_offset = TSUNAMI_DAC_OFFSET, | ||
675 | |||
676 | .nr_irqs = 64, | ||
677 | .device_interrupt = dp264_device_interrupt, | ||
678 | |||
679 | .init_arch = tsunami_init_arch, | ||
680 | .init_irq = clipper_init_irq, | ||
681 | .init_rtc = common_init_rtc, | ||
682 | .init_pci = common_init_pci, | ||
683 | .kill_arch = tsunami_kill_arch, | ||
684 | .pci_map_irq = clipper_map_irq, | ||
685 | .pci_swizzle = common_swizzle, | ||
686 | }; | ||
687 | |||
688 | /* No alpha_mv alias for webbrick/monet/clipper, since we compile them | ||
689 | in unconditionally with DP264; setup_arch knows how to cope. */ | ||
diff --git a/arch/alpha/kernel/sys_eb64p.c b/arch/alpha/kernel/sys_eb64p.c new file mode 100644 index 000000000000..61a79c354f0b --- /dev/null +++ b/arch/alpha/kernel/sys_eb64p.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_eb64p.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the EB64+ and EB66. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/bitops.h> | ||
19 | |||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/dma.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <asm/mmu_context.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/pgtable.h> | ||
27 | #include <asm/core_apecs.h> | ||
28 | #include <asm/core_lca.h> | ||
29 | #include <asm/hwrpb.h> | ||
30 | #include <asm/tlbflush.h> | ||
31 | |||
32 | #include "proto.h" | ||
33 | #include "irq_impl.h" | ||
34 | #include "pci_impl.h" | ||
35 | #include "machvec_impl.h" | ||
36 | |||
37 | |||
38 | /* Note mask bit is true for DISABLED irqs. */ | ||
39 | static unsigned int cached_irq_mask = -1; | ||
40 | |||
41 | static inline void | ||
42 | eb64p_update_irq_hw(unsigned int irq, unsigned long mask) | ||
43 | { | ||
44 | outb(mask >> (irq >= 24 ? 24 : 16), (irq >= 24 ? 0x27 : 0x26)); | ||
45 | } | ||
46 | |||
47 | static inline void | ||
48 | eb64p_enable_irq(unsigned int irq) | ||
49 | { | ||
50 | eb64p_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); | ||
51 | } | ||
52 | |||
53 | static void | ||
54 | eb64p_disable_irq(unsigned int irq) | ||
55 | { | ||
56 | eb64p_update_irq_hw(irq, cached_irq_mask |= 1 << irq); | ||
57 | } | ||
58 | |||
59 | static unsigned int | ||
60 | eb64p_startup_irq(unsigned int irq) | ||
61 | { | ||
62 | eb64p_enable_irq(irq); | ||
63 | return 0; /* never anything pending */ | ||
64 | } | ||
65 | |||
66 | static void | ||
67 | eb64p_end_irq(unsigned int irq) | ||
68 | { | ||
69 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
70 | eb64p_enable_irq(irq); | ||
71 | } | ||
72 | |||
73 | static struct hw_interrupt_type eb64p_irq_type = { | ||
74 | .typename = "EB64P", | ||
75 | .startup = eb64p_startup_irq, | ||
76 | .shutdown = eb64p_disable_irq, | ||
77 | .enable = eb64p_enable_irq, | ||
78 | .disable = eb64p_disable_irq, | ||
79 | .ack = eb64p_disable_irq, | ||
80 | .end = eb64p_end_irq, | ||
81 | }; | ||
82 | |||
83 | static void | ||
84 | eb64p_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
85 | { | ||
86 | unsigned long pld; | ||
87 | unsigned int i; | ||
88 | |||
89 | /* Read the interrupt summary registers */ | ||
90 | pld = inb(0x26) | (inb(0x27) << 8); | ||
91 | |||
92 | /* | ||
93 | * Now, for every possible bit set, work through | ||
94 | * them and call the appropriate interrupt handler. | ||
95 | */ | ||
96 | while (pld) { | ||
97 | i = ffz(~pld); | ||
98 | pld &= pld - 1; /* clear least bit set */ | ||
99 | |||
100 | if (i == 5) { | ||
101 | isa_device_interrupt(vector, regs); | ||
102 | } else { | ||
103 | handle_irq(16 + i, regs); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static void __init | ||
109 | eb64p_init_irq(void) | ||
110 | { | ||
111 | long i; | ||
112 | |||
113 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_CABRIOLET) | ||
114 | /* | ||
115 | * CABRIO SRM may not set variation correctly, so here we test | ||
116 | * the high word of the interrupt summary register for the RAZ | ||
117 | * bits, and hope that a true EB64+ would read all ones... | ||
118 | */ | ||
119 | if (inw(0x806) != 0xffff) { | ||
120 | extern struct alpha_machine_vector cabriolet_mv; | ||
121 | |||
122 | printk("Detected Cabriolet: correcting HWRPB.\n"); | ||
123 | |||
124 | hwrpb->sys_variation |= 2L << 10; | ||
125 | hwrpb_update_checksum(hwrpb); | ||
126 | |||
127 | alpha_mv = cabriolet_mv; | ||
128 | alpha_mv.init_irq(); | ||
129 | return; | ||
130 | } | ||
131 | #endif /* GENERIC */ | ||
132 | |||
133 | outb(0xff, 0x26); | ||
134 | outb(0xff, 0x27); | ||
135 | |||
136 | init_i8259a_irqs(); | ||
137 | |||
138 | for (i = 16; i < 32; ++i) { | ||
139 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
140 | irq_desc[i].handler = &eb64p_irq_type; | ||
141 | } | ||
142 | |||
143 | common_init_isa_dma(); | ||
144 | setup_irq(16+5, &isa_cascade_irqaction); | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * PCI Fixup configuration. | ||
149 | * | ||
150 | * There are two 8 bit external summary registers as follows: | ||
151 | * | ||
152 | * Summary @ 0x26: | ||
153 | * Bit Meaning | ||
154 | * 0 Interrupt Line A from slot 0 | ||
155 | * 1 Interrupt Line A from slot 1 | ||
156 | * 2 Interrupt Line B from slot 0 | ||
157 | * 3 Interrupt Line B from slot 1 | ||
158 | * 4 Interrupt Line C from slot 0 | ||
159 | * 5 Interrupt line from the two ISA PICs | ||
160 | * 6 Tulip | ||
161 | * 7 NCR SCSI | ||
162 | * | ||
163 | * Summary @ 0x27 | ||
164 | * Bit Meaning | ||
165 | * 0 Interrupt Line C from slot 1 | ||
166 | * 1 Interrupt Line D from slot 0 | ||
167 | * 2 Interrupt Line D from slot 1 | ||
168 | * 3 RAZ | ||
169 | * 4 RAZ | ||
170 | * 5 RAZ | ||
171 | * 6 RAZ | ||
172 | * 7 RAZ | ||
173 | * | ||
174 | * The device to slot mapping looks like: | ||
175 | * | ||
176 | * Slot Device | ||
177 | * 5 NCR SCSI controller | ||
178 | * 6 PCI on board slot 0 | ||
179 | * 7 PCI on board slot 1 | ||
180 | * 8 Intel SIO PCI-ISA bridge chip | ||
181 | * 9 Tulip - DECchip 21040 Ethernet controller | ||
182 | * | ||
183 | * | ||
184 | * This two layered interrupt approach means that we allocate IRQ 16 and | ||
185 | * above for PCI interrupts. The IRQ relates to which bit the interrupt | ||
186 | * comes in on. This makes interrupt processing much easier. | ||
187 | */ | ||
188 | |||
189 | static int __init | ||
190 | eb64p_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
191 | { | ||
192 | static char irq_tab[5][5] __initdata = { | ||
193 | /*INT INTA INTB INTC INTD */ | ||
194 | {16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */ | ||
195 | {16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */ | ||
196 | {16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */ | ||
197 | { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ | ||
198 | {16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 9, TULIP */ | ||
199 | }; | ||
200 | const long min_idsel = 5, max_idsel = 9, irqs_per_slot = 5; | ||
201 | return COMMON_TABLE_LOOKUP; | ||
202 | } | ||
203 | |||
204 | |||
205 | /* | ||
206 | * The System Vector | ||
207 | */ | ||
208 | |||
209 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EB64P) | ||
210 | struct alpha_machine_vector eb64p_mv __initmv = { | ||
211 | .vector_name = "EB64+", | ||
212 | DO_EV4_MMU, | ||
213 | DO_DEFAULT_RTC, | ||
214 | DO_APECS_IO, | ||
215 | .machine_check = apecs_machine_check, | ||
216 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
217 | .min_io_address = DEFAULT_IO_BASE, | ||
218 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
219 | |||
220 | .nr_irqs = 32, | ||
221 | .device_interrupt = eb64p_device_interrupt, | ||
222 | |||
223 | .init_arch = apecs_init_arch, | ||
224 | .init_irq = eb64p_init_irq, | ||
225 | .init_rtc = common_init_rtc, | ||
226 | .init_pci = common_init_pci, | ||
227 | .kill_arch = NULL, | ||
228 | .pci_map_irq = eb64p_map_irq, | ||
229 | .pci_swizzle = common_swizzle, | ||
230 | }; | ||
231 | ALIAS_MV(eb64p) | ||
232 | #endif | ||
233 | |||
234 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EB66) | ||
235 | struct alpha_machine_vector eb66_mv __initmv = { | ||
236 | .vector_name = "EB66", | ||
237 | DO_EV4_MMU, | ||
238 | DO_DEFAULT_RTC, | ||
239 | DO_LCA_IO, | ||
240 | .machine_check = lca_machine_check, | ||
241 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
242 | .min_io_address = DEFAULT_IO_BASE, | ||
243 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
244 | |||
245 | .nr_irqs = 32, | ||
246 | .device_interrupt = eb64p_device_interrupt, | ||
247 | |||
248 | .init_arch = lca_init_arch, | ||
249 | .init_irq = eb64p_init_irq, | ||
250 | .init_rtc = common_init_rtc, | ||
251 | .init_pci = common_init_pci, | ||
252 | .pci_map_irq = eb64p_map_irq, | ||
253 | .pci_swizzle = common_swizzle, | ||
254 | }; | ||
255 | ALIAS_MV(eb66) | ||
256 | #endif | ||
diff --git a/arch/alpha/kernel/sys_eiger.c b/arch/alpha/kernel/sys_eiger.c new file mode 100644 index 000000000000..bd6e5f0e43c7 --- /dev/null +++ b/arch/alpha/kernel/sys_eiger.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_eiger.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996, 1999 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * Copyright (C) 1999 Iain Grant | ||
8 | * | ||
9 | * Code supporting the EIGER (EV6+TSUNAMI). | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/bitops.h> | ||
19 | |||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/dma.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <asm/mmu_context.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/pci.h> | ||
27 | #include <asm/pgtable.h> | ||
28 | #include <asm/core_tsunami.h> | ||
29 | #include <asm/hwrpb.h> | ||
30 | #include <asm/tlbflush.h> | ||
31 | |||
32 | #include "proto.h" | ||
33 | #include "irq_impl.h" | ||
34 | #include "pci_impl.h" | ||
35 | #include "machvec_impl.h" | ||
36 | |||
37 | |||
38 | /* Note that this interrupt code is identical to TAKARA. */ | ||
39 | |||
40 | /* Note mask bit is true for DISABLED irqs. */ | ||
41 | static unsigned long cached_irq_mask[2] = { -1, -1 }; | ||
42 | |||
43 | static inline void | ||
44 | eiger_update_irq_hw(unsigned long irq, unsigned long mask) | ||
45 | { | ||
46 | int regaddr; | ||
47 | |||
48 | mask = (irq >= 64 ? mask << 16 : mask >> ((irq - 16) & 0x30)); | ||
49 | regaddr = 0x510 + (((irq - 16) >> 2) & 0x0c); | ||
50 | outl(mask & 0xffff0000UL, regaddr); | ||
51 | } | ||
52 | |||
53 | static inline void | ||
54 | eiger_enable_irq(unsigned int irq) | ||
55 | { | ||
56 | unsigned long mask; | ||
57 | mask = (cached_irq_mask[irq >= 64] &= ~(1UL << (irq & 63))); | ||
58 | eiger_update_irq_hw(irq, mask); | ||
59 | } | ||
60 | |||
61 | static void | ||
62 | eiger_disable_irq(unsigned int irq) | ||
63 | { | ||
64 | unsigned long mask; | ||
65 | mask = (cached_irq_mask[irq >= 64] |= 1UL << (irq & 63)); | ||
66 | eiger_update_irq_hw(irq, mask); | ||
67 | } | ||
68 | |||
69 | static unsigned int | ||
70 | eiger_startup_irq(unsigned int irq) | ||
71 | { | ||
72 | eiger_enable_irq(irq); | ||
73 | return 0; /* never anything pending */ | ||
74 | } | ||
75 | |||
76 | static void | ||
77 | eiger_end_irq(unsigned int irq) | ||
78 | { | ||
79 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
80 | eiger_enable_irq(irq); | ||
81 | } | ||
82 | |||
83 | static struct hw_interrupt_type eiger_irq_type = { | ||
84 | .typename = "EIGER", | ||
85 | .startup = eiger_startup_irq, | ||
86 | .shutdown = eiger_disable_irq, | ||
87 | .enable = eiger_enable_irq, | ||
88 | .disable = eiger_disable_irq, | ||
89 | .ack = eiger_disable_irq, | ||
90 | .end = eiger_end_irq, | ||
91 | }; | ||
92 | |||
93 | static void | ||
94 | eiger_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
95 | { | ||
96 | unsigned intstatus; | ||
97 | |||
98 | /* | ||
99 | * The PALcode will have passed us vectors 0x800 or 0x810, | ||
100 | * which are fairly arbitrary values and serve only to tell | ||
101 | * us whether an interrupt has come in on IRQ0 or IRQ1. If | ||
102 | * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's | ||
103 | * probably ISA, but PCI interrupts can come through IRQ0 | ||
104 | * as well if the interrupt controller isn't in accelerated | ||
105 | * mode. | ||
106 | * | ||
107 | * OTOH, the accelerator thing doesn't seem to be working | ||
108 | * overly well, so what we'll do instead is try directly | ||
109 | * examining the Master Interrupt Register to see if it's a | ||
110 | * PCI interrupt, and if _not_ then we'll pass it on to the | ||
111 | * ISA handler. | ||
112 | */ | ||
113 | |||
114 | intstatus = inw(0x500) & 15; | ||
115 | if (intstatus) { | ||
116 | /* | ||
117 | * This is a PCI interrupt. Check each bit and | ||
118 | * despatch an interrupt if it's set. | ||
119 | */ | ||
120 | |||
121 | if (intstatus & 8) handle_irq(16+3, regs); | ||
122 | if (intstatus & 4) handle_irq(16+2, regs); | ||
123 | if (intstatus & 2) handle_irq(16+1, regs); | ||
124 | if (intstatus & 1) handle_irq(16+0, regs); | ||
125 | } else { | ||
126 | isa_device_interrupt(vector, regs); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | static void | ||
131 | eiger_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
132 | { | ||
133 | int irq = (vector - 0x800) >> 4; | ||
134 | handle_irq(irq, regs); | ||
135 | } | ||
136 | |||
137 | static void __init | ||
138 | eiger_init_irq(void) | ||
139 | { | ||
140 | long i; | ||
141 | |||
142 | outb(0, DMA1_RESET_REG); | ||
143 | outb(0, DMA2_RESET_REG); | ||
144 | outb(DMA_MODE_CASCADE, DMA2_MODE_REG); | ||
145 | outb(0, DMA2_MASK_REG); | ||
146 | |||
147 | if (alpha_using_srm) | ||
148 | alpha_mv.device_interrupt = eiger_srm_device_interrupt; | ||
149 | |||
150 | for (i = 16; i < 128; i += 16) | ||
151 | eiger_update_irq_hw(i, -1); | ||
152 | |||
153 | init_i8259a_irqs(); | ||
154 | |||
155 | for (i = 16; i < 128; ++i) { | ||
156 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
157 | irq_desc[i].handler = &eiger_irq_type; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | static int __init | ||
162 | eiger_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
163 | { | ||
164 | u8 irq_orig; | ||
165 | |||
166 | /* The SRM console has already calculated out the IRQ value's for | ||
167 | option cards. As this works lets just read in the value already | ||
168 | set and change it to a useable value by Linux. | ||
169 | |||
170 | All the IRQ values generated by the console are greater than 90, | ||
171 | so we subtract 80 because it is (90 - allocated ISA IRQ's). */ | ||
172 | |||
173 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq_orig); | ||
174 | |||
175 | return irq_orig - 0x80; | ||
176 | } | ||
177 | |||
178 | static u8 __init | ||
179 | eiger_swizzle(struct pci_dev *dev, u8 *pinp) | ||
180 | { | ||
181 | struct pci_controller *hose = dev->sysdata; | ||
182 | int slot, pin = *pinp; | ||
183 | int bridge_count = 0; | ||
184 | |||
185 | /* Find the number of backplane bridges. */ | ||
186 | int backplane = inw(0x502) & 0x0f; | ||
187 | |||
188 | switch (backplane) | ||
189 | { | ||
190 | case 0x00: bridge_count = 0; break; /* No bridges */ | ||
191 | case 0x01: bridge_count = 1; break; /* 1 */ | ||
192 | case 0x03: bridge_count = 2; break; /* 2 */ | ||
193 | case 0x07: bridge_count = 3; break; /* 3 */ | ||
194 | case 0x0f: bridge_count = 4; break; /* 4 */ | ||
195 | }; | ||
196 | |||
197 | slot = PCI_SLOT(dev->devfn); | ||
198 | while (dev->bus->self) { | ||
199 | /* Check for built-in bridges on hose 0. */ | ||
200 | if (hose->index == 0 | ||
201 | && (PCI_SLOT(dev->bus->self->devfn) | ||
202 | > 20 - bridge_count)) { | ||
203 | slot = PCI_SLOT(dev->devfn); | ||
204 | break; | ||
205 | } | ||
206 | /* Must be a card-based bridge. */ | ||
207 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); | ||
208 | |||
209 | /* Move up the chain of bridges. */ | ||
210 | dev = dev->bus->self; | ||
211 | } | ||
212 | *pinp = pin; | ||
213 | return slot; | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * The System Vectors | ||
218 | */ | ||
219 | |||
220 | struct alpha_machine_vector eiger_mv __initmv = { | ||
221 | .vector_name = "Eiger", | ||
222 | DO_EV6_MMU, | ||
223 | DO_DEFAULT_RTC, | ||
224 | DO_TSUNAMI_IO, | ||
225 | .machine_check = tsunami_machine_check, | ||
226 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
227 | .min_io_address = DEFAULT_IO_BASE, | ||
228 | .min_mem_address = DEFAULT_MEM_BASE, | ||
229 | .pci_dac_offset = TSUNAMI_DAC_OFFSET, | ||
230 | |||
231 | .nr_irqs = 128, | ||
232 | .device_interrupt = eiger_device_interrupt, | ||
233 | |||
234 | .init_arch = tsunami_init_arch, | ||
235 | .init_irq = eiger_init_irq, | ||
236 | .init_rtc = common_init_rtc, | ||
237 | .init_pci = common_init_pci, | ||
238 | .kill_arch = tsunami_kill_arch, | ||
239 | .pci_map_irq = eiger_map_irq, | ||
240 | .pci_swizzle = eiger_swizzle, | ||
241 | }; | ||
242 | ALIAS_MV(eiger) | ||
diff --git a/arch/alpha/kernel/sys_jensen.c b/arch/alpha/kernel/sys_jensen.c new file mode 100644 index 000000000000..fcabb7c96a16 --- /dev/null +++ b/arch/alpha/kernel/sys_jensen.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_jensen.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Copyright (C) 1998, 1999 Richard Henderson | ||
6 | * | ||
7 | * Code supporting the Jensen. | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/pci.h> | ||
15 | #include <linux/init.h> | ||
16 | |||
17 | #include <asm/ptrace.h> | ||
18 | #include <asm/system.h> | ||
19 | |||
20 | #define __EXTERN_INLINE inline | ||
21 | #include <asm/io.h> | ||
22 | #include <asm/jensen.h> | ||
23 | #undef __EXTERN_INLINE | ||
24 | |||
25 | #include <asm/dma.h> | ||
26 | #include <asm/irq.h> | ||
27 | #include <asm/mmu_context.h> | ||
28 | #include <asm/pgtable.h> | ||
29 | #include <asm/tlbflush.h> | ||
30 | |||
31 | #include "proto.h" | ||
32 | #include "irq_impl.h" | ||
33 | #include "pci_impl.h" | ||
34 | #include "machvec_impl.h" | ||
35 | |||
36 | |||
37 | /* | ||
38 | * Jensen is special: the vector is 0x8X0 for EISA interrupt X, and | ||
39 | * 0x9X0 for the local motherboard interrupts. | ||
40 | * | ||
41 | * Note especially that those local interrupts CANNOT be masked, | ||
42 | * which causes much of the pain below... | ||
43 | * | ||
44 | * 0x660 - NMI | ||
45 | * | ||
46 | * 0x800 - IRQ0 interval timer (not used, as we use the RTC timer) | ||
47 | * 0x810 - IRQ1 line printer (duh..) | ||
48 | * 0x860 - IRQ6 floppy disk | ||
49 | * | ||
50 | * 0x900 - COM1 | ||
51 | * 0x920 - COM2 | ||
52 | * 0x980 - keyboard | ||
53 | * 0x990 - mouse | ||
54 | * | ||
55 | * PCI-based systems are more sane: they don't have the local | ||
56 | * interrupts at all, and have only normal PCI interrupts from | ||
57 | * devices. Happily it's easy enough to do a sane mapping from the | ||
58 | * Jensen. | ||
59 | * | ||
60 | * Note that this means that we may have to do a hardware | ||
61 | * "local_op" to a different interrupt than we report to the rest of the | ||
62 | * world. | ||
63 | */ | ||
64 | |||
65 | static unsigned int | ||
66 | jensen_local_startup(unsigned int irq) | ||
67 | { | ||
68 | /* the parport is really hw IRQ 1, silly Jensen. */ | ||
69 | if (irq == 7) | ||
70 | i8259a_startup_irq(1); | ||
71 | else | ||
72 | /* | ||
73 | * For all true local interrupts, set the flag that prevents | ||
74 | * the IPL from being dropped during handler processing. | ||
75 | */ | ||
76 | if (irq_desc[irq].action) | ||
77 | irq_desc[irq].action->flags |= SA_INTERRUPT; | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | jensen_local_shutdown(unsigned int irq) | ||
83 | { | ||
84 | /* the parport is really hw IRQ 1, silly Jensen. */ | ||
85 | if (irq == 7) | ||
86 | i8259a_disable_irq(1); | ||
87 | } | ||
88 | |||
89 | static void | ||
90 | jensen_local_enable(unsigned int irq) | ||
91 | { | ||
92 | /* the parport is really hw IRQ 1, silly Jensen. */ | ||
93 | if (irq == 7) | ||
94 | i8259a_enable_irq(1); | ||
95 | } | ||
96 | |||
97 | static void | ||
98 | jensen_local_disable(unsigned int irq) | ||
99 | { | ||
100 | /* the parport is really hw IRQ 1, silly Jensen. */ | ||
101 | if (irq == 7) | ||
102 | i8259a_disable_irq(1); | ||
103 | } | ||
104 | |||
105 | static void | ||
106 | jensen_local_ack(unsigned int irq) | ||
107 | { | ||
108 | /* the parport is really hw IRQ 1, silly Jensen. */ | ||
109 | if (irq == 7) | ||
110 | i8259a_mask_and_ack_irq(1); | ||
111 | } | ||
112 | |||
113 | static void | ||
114 | jensen_local_end(unsigned int irq) | ||
115 | { | ||
116 | /* the parport is really hw IRQ 1, silly Jensen. */ | ||
117 | if (irq == 7) | ||
118 | i8259a_end_irq(1); | ||
119 | } | ||
120 | |||
121 | static struct hw_interrupt_type jensen_local_irq_type = { | ||
122 | .typename = "LOCAL", | ||
123 | .startup = jensen_local_startup, | ||
124 | .shutdown = jensen_local_shutdown, | ||
125 | .enable = jensen_local_enable, | ||
126 | .disable = jensen_local_disable, | ||
127 | .ack = jensen_local_ack, | ||
128 | .end = jensen_local_end, | ||
129 | }; | ||
130 | |||
131 | static void | ||
132 | jensen_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
133 | { | ||
134 | int irq; | ||
135 | |||
136 | switch (vector) { | ||
137 | case 0x660: | ||
138 | printk("Whee.. NMI received. Probable hardware error\n"); | ||
139 | printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461)); | ||
140 | return; | ||
141 | |||
142 | /* local device interrupts: */ | ||
143 | case 0x900: irq = 4; break; /* com1 -> irq 4 */ | ||
144 | case 0x920: irq = 3; break; /* com2 -> irq 3 */ | ||
145 | case 0x980: irq = 1; break; /* kbd -> irq 1 */ | ||
146 | case 0x990: irq = 9; break; /* mouse -> irq 9 */ | ||
147 | |||
148 | default: | ||
149 | if (vector > 0x900) { | ||
150 | printk("Unknown local interrupt %lx\n", vector); | ||
151 | return; | ||
152 | } | ||
153 | |||
154 | irq = (vector - 0x800) >> 4; | ||
155 | if (irq == 1) | ||
156 | irq = 7; | ||
157 | break; | ||
158 | } | ||
159 | |||
160 | /* If there is no handler yet... */ | ||
161 | if (irq_desc[irq].action == NULL) { | ||
162 | /* If it is a local interrupt that cannot be masked... */ | ||
163 | if (vector >= 0x900) | ||
164 | { | ||
165 | /* Clear keyboard/mouse state */ | ||
166 | inb(0x64); | ||
167 | inb(0x60); | ||
168 | /* Reset serial ports */ | ||
169 | inb(0x3fa); | ||
170 | inb(0x2fa); | ||
171 | outb(0x0c, 0x3fc); | ||
172 | outb(0x0c, 0x2fc); | ||
173 | /* Clear NMI */ | ||
174 | outb(0,0x61); | ||
175 | outb(0,0x461); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | #if 0 | ||
180 | /* A useful bit of code to find out if an interrupt is going wild. */ | ||
181 | { | ||
182 | static unsigned int last_msg = 0, last_cc = 0; | ||
183 | static int last_irq = -1, count = 0; | ||
184 | unsigned int cc; | ||
185 | |||
186 | __asm __volatile("rpcc %0" : "=r"(cc)); | ||
187 | ++count; | ||
188 | #define JENSEN_CYCLES_PER_SEC (150000000) | ||
189 | if (cc - last_msg > ((JENSEN_CYCLES_PER_SEC) * 3) || | ||
190 | irq != last_irq) { | ||
191 | printk(KERN_CRIT " irq %d count %d cc %u @ %lx\n", | ||
192 | irq, count, cc-last_cc, regs->pc); | ||
193 | count = 0; | ||
194 | last_msg = cc; | ||
195 | last_irq = irq; | ||
196 | } | ||
197 | last_cc = cc; | ||
198 | } | ||
199 | #endif | ||
200 | |||
201 | handle_irq(irq, regs); | ||
202 | } | ||
203 | |||
204 | static void __init | ||
205 | jensen_init_irq(void) | ||
206 | { | ||
207 | init_i8259a_irqs(); | ||
208 | |||
209 | irq_desc[1].handler = &jensen_local_irq_type; | ||
210 | irq_desc[4].handler = &jensen_local_irq_type; | ||
211 | irq_desc[3].handler = &jensen_local_irq_type; | ||
212 | irq_desc[7].handler = &jensen_local_irq_type; | ||
213 | irq_desc[9].handler = &jensen_local_irq_type; | ||
214 | |||
215 | common_init_isa_dma(); | ||
216 | } | ||
217 | |||
218 | static void __init | ||
219 | jensen_init_arch(void) | ||
220 | { | ||
221 | struct pci_controller *hose; | ||
222 | #ifdef CONFIG_PCI | ||
223 | static struct pci_dev fake_isa_bridge = { .dma_mask = 0xffffffffUL, }; | ||
224 | |||
225 | isa_bridge = &fake_isa_bridge; | ||
226 | #endif | ||
227 | |||
228 | /* Create a hose so that we can report i/o base addresses to | ||
229 | userland. */ | ||
230 | |||
231 | pci_isa_hose = hose = alloc_pci_controller(); | ||
232 | hose->io_space = &ioport_resource; | ||
233 | hose->mem_space = &iomem_resource; | ||
234 | hose->index = 0; | ||
235 | |||
236 | hose->sparse_mem_base = EISA_MEM - IDENT_ADDR; | ||
237 | hose->dense_mem_base = 0; | ||
238 | hose->sparse_io_base = EISA_IO - IDENT_ADDR; | ||
239 | hose->dense_io_base = 0; | ||
240 | |||
241 | hose->sg_isa = hose->sg_pci = NULL; | ||
242 | __direct_map_base = 0; | ||
243 | __direct_map_size = 0xffffffff; | ||
244 | } | ||
245 | |||
246 | static void | ||
247 | jensen_machine_check (u64 vector, u64 la, struct pt_regs *regs) | ||
248 | { | ||
249 | printk(KERN_CRIT "Machine check\n"); | ||
250 | } | ||
251 | |||
252 | |||
253 | /* | ||
254 | * The System Vector | ||
255 | */ | ||
256 | |||
257 | struct alpha_machine_vector jensen_mv __initmv = { | ||
258 | .vector_name = "Jensen", | ||
259 | DO_EV4_MMU, | ||
260 | IO_LITE(JENSEN,jensen), | ||
261 | .machine_check = jensen_machine_check, | ||
262 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
263 | .rtc_port = 0x170, | ||
264 | |||
265 | .nr_irqs = 16, | ||
266 | .device_interrupt = jensen_device_interrupt, | ||
267 | |||
268 | .init_arch = jensen_init_arch, | ||
269 | .init_irq = jensen_init_irq, | ||
270 | .init_rtc = common_init_rtc, | ||
271 | .init_pci = NULL, | ||
272 | .kill_arch = NULL, | ||
273 | }; | ||
274 | ALIAS_MV(jensen) | ||
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c new file mode 100644 index 000000000000..804727853d25 --- /dev/null +++ b/arch/alpha/kernel/sys_marvel.c | |||
@@ -0,0 +1,499 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_marvel.c | ||
3 | * | ||
4 | * Marvel / IO7 support | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/mm.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/pci.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/bitops.h> | ||
14 | |||
15 | #include <asm/ptrace.h> | ||
16 | #include <asm/system.h> | ||
17 | #include <asm/dma.h> | ||
18 | #include <asm/irq.h> | ||
19 | #include <asm/mmu_context.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/core_marvel.h> | ||
23 | #include <asm/hwrpb.h> | ||
24 | #include <asm/tlbflush.h> | ||
25 | |||
26 | #include "proto.h" | ||
27 | #include "err_impl.h" | ||
28 | #include "irq_impl.h" | ||
29 | #include "pci_impl.h" | ||
30 | #include "machvec_impl.h" | ||
31 | |||
32 | #if NR_IRQS < MARVEL_NR_IRQS | ||
33 | # error NR_IRQS < MARVEL_NR_IRQS !!! | ||
34 | #endif | ||
35 | |||
36 | |||
37 | /* | ||
38 | * Interrupt handling. | ||
39 | */ | ||
40 | static void | ||
41 | io7_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
42 | { | ||
43 | unsigned int pid; | ||
44 | unsigned int irq; | ||
45 | |||
46 | /* | ||
47 | * Vector is 0x800 + (interrupt) | ||
48 | * | ||
49 | * where (interrupt) is: | ||
50 | * | ||
51 | * ...16|15 14|13 4|3 0 | ||
52 | * -----+-----+--------+--- | ||
53 | * PE | 0 | irq | 0 | ||
54 | * | ||
55 | * where (irq) is | ||
56 | * | ||
57 | * 0x0800 - 0x0ff0 - 0x0800 + (LSI id << 4) | ||
58 | * 0x1000 - 0x2ff0 - 0x1000 + (MSI_DAT<8:0> << 4) | ||
59 | */ | ||
60 | pid = vector >> 16; | ||
61 | irq = ((vector & 0xffff) - 0x800) >> 4; | ||
62 | |||
63 | irq += 16; /* offset for legacy */ | ||
64 | irq &= MARVEL_IRQ_VEC_IRQ_MASK; /* not too many bits */ | ||
65 | irq |= pid << MARVEL_IRQ_VEC_PE_SHIFT; /* merge the pid */ | ||
66 | |||
67 | handle_irq(irq, regs); | ||
68 | } | ||
69 | |||
70 | static volatile unsigned long * | ||
71 | io7_get_irq_ctl(unsigned int irq, struct io7 **pio7) | ||
72 | { | ||
73 | volatile unsigned long *ctl; | ||
74 | unsigned int pid; | ||
75 | struct io7 *io7; | ||
76 | |||
77 | pid = irq >> MARVEL_IRQ_VEC_PE_SHIFT; | ||
78 | |||
79 | if (!(io7 = marvel_find_io7(pid))) { | ||
80 | printk(KERN_ERR | ||
81 | "%s for nonexistent io7 -- vec %x, pid %d\n", | ||
82 | __FUNCTION__, irq, pid); | ||
83 | return NULL; | ||
84 | } | ||
85 | |||
86 | irq &= MARVEL_IRQ_VEC_IRQ_MASK; /* isolate the vector */ | ||
87 | irq -= 16; /* subtract legacy bias */ | ||
88 | |||
89 | if (irq >= 0x180) { | ||
90 | printk(KERN_ERR | ||
91 | "%s for invalid irq -- pid %d adjusted irq %x\n", | ||
92 | __FUNCTION__, pid, irq); | ||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | ctl = &io7->csrs->PO7_LSI_CTL[irq & 0xff].csr; /* assume LSI */ | ||
97 | if (irq >= 0x80) /* MSI */ | ||
98 | ctl = &io7->csrs->PO7_MSI_CTL[((irq - 0x80) >> 5) & 0x0f].csr; | ||
99 | |||
100 | if (pio7) *pio7 = io7; | ||
101 | return ctl; | ||
102 | } | ||
103 | |||
104 | static void | ||
105 | io7_enable_irq(unsigned int irq) | ||
106 | { | ||
107 | volatile unsigned long *ctl; | ||
108 | struct io7 *io7; | ||
109 | |||
110 | ctl = io7_get_irq_ctl(irq, &io7); | ||
111 | if (!ctl || !io7) { | ||
112 | printk(KERN_ERR "%s: get_ctl failed for irq %x\n", | ||
113 | __FUNCTION__, irq); | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | spin_lock(&io7->irq_lock); | ||
118 | *ctl |= 1UL << 24; | ||
119 | mb(); | ||
120 | *ctl; | ||
121 | spin_unlock(&io7->irq_lock); | ||
122 | } | ||
123 | |||
124 | static void | ||
125 | io7_disable_irq(unsigned int irq) | ||
126 | { | ||
127 | volatile unsigned long *ctl; | ||
128 | struct io7 *io7; | ||
129 | |||
130 | ctl = io7_get_irq_ctl(irq, &io7); | ||
131 | if (!ctl || !io7) { | ||
132 | printk(KERN_ERR "%s: get_ctl failed for irq %x\n", | ||
133 | __FUNCTION__, irq); | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | spin_lock(&io7->irq_lock); | ||
138 | *ctl &= ~(1UL << 24); | ||
139 | mb(); | ||
140 | *ctl; | ||
141 | spin_unlock(&io7->irq_lock); | ||
142 | } | ||
143 | |||
144 | static unsigned int | ||
145 | io7_startup_irq(unsigned int irq) | ||
146 | { | ||
147 | io7_enable_irq(irq); | ||
148 | return 0; /* never anything pending */ | ||
149 | } | ||
150 | |||
151 | static void | ||
152 | io7_end_irq(unsigned int irq) | ||
153 | { | ||
154 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
155 | io7_enable_irq(irq); | ||
156 | } | ||
157 | |||
158 | static void | ||
159 | marvel_irq_noop(unsigned int irq) | ||
160 | { | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | static unsigned int | ||
165 | marvel_irq_noop_return(unsigned int irq) | ||
166 | { | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static struct hw_interrupt_type marvel_legacy_irq_type = { | ||
171 | .typename = "LEGACY", | ||
172 | .startup = marvel_irq_noop_return, | ||
173 | .shutdown = marvel_irq_noop, | ||
174 | .enable = marvel_irq_noop, | ||
175 | .disable = marvel_irq_noop, | ||
176 | .ack = marvel_irq_noop, | ||
177 | .end = marvel_irq_noop, | ||
178 | }; | ||
179 | |||
180 | static struct hw_interrupt_type io7_lsi_irq_type = { | ||
181 | .typename = "LSI", | ||
182 | .startup = io7_startup_irq, | ||
183 | .shutdown = io7_disable_irq, | ||
184 | .enable = io7_enable_irq, | ||
185 | .disable = io7_disable_irq, | ||
186 | .ack = io7_disable_irq, | ||
187 | .end = io7_end_irq, | ||
188 | }; | ||
189 | |||
190 | static struct hw_interrupt_type io7_msi_irq_type = { | ||
191 | .typename = "MSI", | ||
192 | .startup = io7_startup_irq, | ||
193 | .shutdown = io7_disable_irq, | ||
194 | .enable = io7_enable_irq, | ||
195 | .disable = io7_disable_irq, | ||
196 | .ack = marvel_irq_noop, | ||
197 | .end = io7_end_irq, | ||
198 | }; | ||
199 | |||
200 | static void | ||
201 | io7_redirect_irq(struct io7 *io7, | ||
202 | volatile unsigned long *csr, | ||
203 | unsigned int where) | ||
204 | { | ||
205 | unsigned long val; | ||
206 | |||
207 | val = *csr; | ||
208 | val &= ~(0x1ffUL << 24); /* clear the target pid */ | ||
209 | val |= ((unsigned long)where << 24); /* set the new target pid */ | ||
210 | |||
211 | *csr = val; | ||
212 | mb(); | ||
213 | *csr; | ||
214 | } | ||
215 | |||
216 | static void | ||
217 | io7_redirect_one_lsi(struct io7 *io7, unsigned int which, unsigned int where) | ||
218 | { | ||
219 | unsigned long val; | ||
220 | |||
221 | /* | ||
222 | * LSI_CTL has target PID @ 14 | ||
223 | */ | ||
224 | val = io7->csrs->PO7_LSI_CTL[which].csr; | ||
225 | val &= ~(0x1ffUL << 14); /* clear the target pid */ | ||
226 | val |= ((unsigned long)where << 14); /* set the new target pid */ | ||
227 | |||
228 | io7->csrs->PO7_LSI_CTL[which].csr = val; | ||
229 | mb(); | ||
230 | io7->csrs->PO7_LSI_CTL[which].csr; | ||
231 | } | ||
232 | |||
233 | static void | ||
234 | io7_redirect_one_msi(struct io7 *io7, unsigned int which, unsigned int where) | ||
235 | { | ||
236 | unsigned long val; | ||
237 | |||
238 | /* | ||
239 | * MSI_CTL has target PID @ 14 | ||
240 | */ | ||
241 | val = io7->csrs->PO7_MSI_CTL[which].csr; | ||
242 | val &= ~(0x1ffUL << 14); /* clear the target pid */ | ||
243 | val |= ((unsigned long)where << 14); /* set the new target pid */ | ||
244 | |||
245 | io7->csrs->PO7_MSI_CTL[which].csr = val; | ||
246 | mb(); | ||
247 | io7->csrs->PO7_MSI_CTL[which].csr; | ||
248 | } | ||
249 | |||
250 | static void __init | ||
251 | init_one_io7_lsi(struct io7 *io7, unsigned int which, unsigned int where) | ||
252 | { | ||
253 | /* | ||
254 | * LSI_CTL has target PID @ 14 | ||
255 | */ | ||
256 | io7->csrs->PO7_LSI_CTL[which].csr = ((unsigned long)where << 14); | ||
257 | mb(); | ||
258 | io7->csrs->PO7_LSI_CTL[which].csr; | ||
259 | } | ||
260 | |||
261 | static void __init | ||
262 | init_one_io7_msi(struct io7 *io7, unsigned int which, unsigned int where) | ||
263 | { | ||
264 | /* | ||
265 | * MSI_CTL has target PID @ 14 | ||
266 | */ | ||
267 | io7->csrs->PO7_MSI_CTL[which].csr = ((unsigned long)where << 14); | ||
268 | mb(); | ||
269 | io7->csrs->PO7_MSI_CTL[which].csr; | ||
270 | } | ||
271 | |||
272 | static void __init | ||
273 | init_io7_irqs(struct io7 *io7, | ||
274 | struct hw_interrupt_type *lsi_ops, | ||
275 | struct hw_interrupt_type *msi_ops) | ||
276 | { | ||
277 | long base = (io7->pe << MARVEL_IRQ_VEC_PE_SHIFT) + 16; | ||
278 | long i; | ||
279 | |||
280 | printk("Initializing interrupts for IO7 at PE %u - base %lx\n", | ||
281 | io7->pe, base); | ||
282 | |||
283 | /* | ||
284 | * Where should interrupts from this IO7 go? | ||
285 | * | ||
286 | * They really should be sent to the local CPU to avoid having to | ||
287 | * traverse the mesh, but if it's not an SMP kernel, they have to | ||
288 | * go to the boot CPU. Send them all to the boot CPU for now, | ||
289 | * as each secondary starts, it can redirect it's local device | ||
290 | * interrupts. | ||
291 | */ | ||
292 | printk(" Interrupts reported to CPU at PE %u\n", boot_cpuid); | ||
293 | |||
294 | spin_lock(&io7->irq_lock); | ||
295 | |||
296 | /* set up the error irqs */ | ||
297 | io7_redirect_irq(io7, &io7->csrs->HLT_CTL.csr, boot_cpuid); | ||
298 | io7_redirect_irq(io7, &io7->csrs->HPI_CTL.csr, boot_cpuid); | ||
299 | io7_redirect_irq(io7, &io7->csrs->CRD_CTL.csr, boot_cpuid); | ||
300 | io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid); | ||
301 | io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid); | ||
302 | |||
303 | /* Set up the lsi irqs. */ | ||
304 | for (i = 0; i < 128; ++i) { | ||
305 | irq_desc[base + i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
306 | irq_desc[base + i].handler = lsi_ops; | ||
307 | } | ||
308 | |||
309 | /* Disable the implemented irqs in hardware. */ | ||
310 | for (i = 0; i < 0x60; ++i) | ||
311 | init_one_io7_lsi(io7, i, boot_cpuid); | ||
312 | |||
313 | init_one_io7_lsi(io7, 0x74, boot_cpuid); | ||
314 | init_one_io7_lsi(io7, 0x75, boot_cpuid); | ||
315 | |||
316 | |||
317 | /* Set up the msi irqs. */ | ||
318 | for (i = 128; i < (128 + 512); ++i) { | ||
319 | irq_desc[base + i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
320 | irq_desc[base + i].handler = msi_ops; | ||
321 | } | ||
322 | |||
323 | for (i = 0; i < 16; ++i) | ||
324 | init_one_io7_msi(io7, i, boot_cpuid); | ||
325 | |||
326 | spin_unlock(&io7->irq_lock); | ||
327 | } | ||
328 | |||
329 | static void __init | ||
330 | marvel_init_irq(void) | ||
331 | { | ||
332 | int i; | ||
333 | struct io7 *io7 = NULL; | ||
334 | |||
335 | /* Reserve the legacy irqs. */ | ||
336 | for (i = 0; i < 16; ++i) { | ||
337 | irq_desc[i].status = IRQ_DISABLED; | ||
338 | irq_desc[i].handler = &marvel_legacy_irq_type; | ||
339 | } | ||
340 | |||
341 | /* Init the io7 irqs. */ | ||
342 | for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; ) | ||
343 | init_io7_irqs(io7, &io7_lsi_irq_type, &io7_msi_irq_type); | ||
344 | } | ||
345 | |||
346 | static int | ||
347 | marvel_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
348 | { | ||
349 | struct pci_controller *hose = dev->sysdata; | ||
350 | struct io7_port *io7_port = hose->sysdata; | ||
351 | struct io7 *io7 = io7_port->io7; | ||
352 | int msi_loc, msi_data_off; | ||
353 | u16 msg_ctl; | ||
354 | u16 msg_dat; | ||
355 | u8 intline; | ||
356 | int irq; | ||
357 | |||
358 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &intline); | ||
359 | irq = intline; | ||
360 | |||
361 | msi_loc = pci_find_capability(dev, PCI_CAP_ID_MSI); | ||
362 | msg_ctl = 0; | ||
363 | if (msi_loc) | ||
364 | pci_read_config_word(dev, msi_loc + PCI_MSI_FLAGS, &msg_ctl); | ||
365 | |||
366 | if (msg_ctl & PCI_MSI_FLAGS_ENABLE) { | ||
367 | msi_data_off = PCI_MSI_DATA_32; | ||
368 | if (msg_ctl & PCI_MSI_FLAGS_64BIT) | ||
369 | msi_data_off = PCI_MSI_DATA_64; | ||
370 | pci_read_config_word(dev, msi_loc + msi_data_off, &msg_dat); | ||
371 | |||
372 | irq = msg_dat & 0x1ff; /* we use msg_data<8:0> */ | ||
373 | irq += 0x80; /* offset for lsi */ | ||
374 | |||
375 | #if 1 | ||
376 | printk("PCI:%d:%d:%d (hose %d) [%s] is using MSI\n", | ||
377 | dev->bus->number, | ||
378 | PCI_SLOT(dev->devfn), | ||
379 | PCI_FUNC(dev->devfn), | ||
380 | hose->index, | ||
381 | pci_pretty_name (dev)); | ||
382 | printk(" %d message(s) from 0x%04x\n", | ||
383 | 1 << ((msg_ctl & PCI_MSI_FLAGS_QSIZE) >> 4), | ||
384 | msg_dat); | ||
385 | printk(" reporting on %d IRQ(s) from %d (0x%x)\n", | ||
386 | 1 << ((msg_ctl & PCI_MSI_FLAGS_QSIZE) >> 4), | ||
387 | (irq + 16) | (io7->pe << MARVEL_IRQ_VEC_PE_SHIFT), | ||
388 | (irq + 16) | (io7->pe << MARVEL_IRQ_VEC_PE_SHIFT)); | ||
389 | #endif | ||
390 | |||
391 | #if 0 | ||
392 | pci_write_config_word(dev, msi_loc + PCI_MSI_FLAGS, | ||
393 | msg_ctl & ~PCI_MSI_FLAGS_ENABLE); | ||
394 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &intline); | ||
395 | irq = intline; | ||
396 | |||
397 | printk(" forcing LSI interrupt on irq %d [0x%x]\n", irq, irq); | ||
398 | #endif | ||
399 | } | ||
400 | |||
401 | irq += 16; /* offset for legacy */ | ||
402 | irq |= io7->pe << MARVEL_IRQ_VEC_PE_SHIFT; /* merge the pid */ | ||
403 | |||
404 | return irq; | ||
405 | } | ||
406 | |||
407 | static void __init | ||
408 | marvel_init_pci(void) | ||
409 | { | ||
410 | struct io7 *io7; | ||
411 | |||
412 | marvel_register_error_handlers(); | ||
413 | |||
414 | pci_probe_only = 1; | ||
415 | common_init_pci(); | ||
416 | |||
417 | #ifdef CONFIG_VGA_HOSE | ||
418 | locate_and_init_vga(NULL); | ||
419 | #endif | ||
420 | |||
421 | /* Clear any io7 errors. */ | ||
422 | for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; ) | ||
423 | io7_clear_errors(io7); | ||
424 | } | ||
425 | |||
426 | static void | ||
427 | marvel_init_rtc(void) | ||
428 | { | ||
429 | init_rtc_irq(); | ||
430 | } | ||
431 | |||
432 | static void | ||
433 | marvel_smp_callin(void) | ||
434 | { | ||
435 | int cpuid = hard_smp_processor_id(); | ||
436 | struct io7 *io7 = marvel_find_io7(cpuid); | ||
437 | unsigned int i; | ||
438 | |||
439 | if (!io7) | ||
440 | return; | ||
441 | |||
442 | /* | ||
443 | * There is a local IO7 - redirect all of its interrupts here. | ||
444 | */ | ||
445 | printk("Redirecting IO7 interrupts to local CPU at PE %u\n", cpuid); | ||
446 | |||
447 | /* Redirect the error IRQS here. */ | ||
448 | io7_redirect_irq(io7, &io7->csrs->HLT_CTL.csr, cpuid); | ||
449 | io7_redirect_irq(io7, &io7->csrs->HPI_CTL.csr, cpuid); | ||
450 | io7_redirect_irq(io7, &io7->csrs->CRD_CTL.csr, cpuid); | ||
451 | io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, cpuid); | ||
452 | io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, cpuid); | ||
453 | |||
454 | /* Redirect the implemented LSIs here. */ | ||
455 | for (i = 0; i < 0x60; ++i) | ||
456 | io7_redirect_one_lsi(io7, i, cpuid); | ||
457 | |||
458 | io7_redirect_one_lsi(io7, 0x74, cpuid); | ||
459 | io7_redirect_one_lsi(io7, 0x75, cpuid); | ||
460 | |||
461 | /* Redirect the MSIs here. */ | ||
462 | for (i = 0; i < 16; ++i) | ||
463 | io7_redirect_one_msi(io7, i, cpuid); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * System Vectors | ||
468 | */ | ||
469 | struct alpha_machine_vector marvel_ev7_mv __initmv = { | ||
470 | .vector_name = "MARVEL/EV7", | ||
471 | DO_EV7_MMU, | ||
472 | DO_DEFAULT_RTC, | ||
473 | DO_MARVEL_IO, | ||
474 | .machine_check = marvel_machine_check, | ||
475 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
476 | .min_io_address = DEFAULT_IO_BASE, | ||
477 | .min_mem_address = DEFAULT_MEM_BASE, | ||
478 | .pci_dac_offset = IO7_DAC_OFFSET, | ||
479 | |||
480 | .nr_irqs = MARVEL_NR_IRQS, | ||
481 | .device_interrupt = io7_device_interrupt, | ||
482 | |||
483 | .agp_info = marvel_agp_info, | ||
484 | |||
485 | .smp_callin = marvel_smp_callin, | ||
486 | .init_arch = marvel_init_arch, | ||
487 | .init_irq = marvel_init_irq, | ||
488 | .init_rtc = marvel_init_rtc, | ||
489 | .init_pci = marvel_init_pci, | ||
490 | .kill_arch = marvel_kill_arch, | ||
491 | .pci_map_irq = marvel_map_irq, | ||
492 | .pci_swizzle = common_swizzle, | ||
493 | |||
494 | .pa_to_nid = marvel_pa_to_nid, | ||
495 | .cpuid_to_nid = marvel_cpuid_to_nid, | ||
496 | .node_mem_start = marvel_node_mem_start, | ||
497 | .node_mem_size = marvel_node_mem_size, | ||
498 | }; | ||
499 | ALIAS_MV(marvel_ev7) | ||
diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c new file mode 100644 index 000000000000..61ac56f8eeea --- /dev/null +++ b/arch/alpha/kernel/sys_miata.c | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_miata.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999, 2000 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the MIATA (EV56+PYXIS). | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/reboot.h> | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/dma.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/mmu_context.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/core_cia.h> | ||
27 | #include <asm/tlbflush.h> | ||
28 | |||
29 | #include "proto.h" | ||
30 | #include "irq_impl.h" | ||
31 | #include "pci_impl.h" | ||
32 | #include "machvec_impl.h" | ||
33 | |||
34 | |||
35 | static void | ||
36 | miata_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
37 | { | ||
38 | int irq; | ||
39 | |||
40 | irq = (vector - 0x800) >> 4; | ||
41 | |||
42 | /* | ||
43 | * I really hate to do this, but the MIATA SRM console ignores the | ||
44 | * low 8 bits in the interrupt summary register, and reports the | ||
45 | * vector 0x80 *lower* than I expected from the bit numbering in | ||
46 | * the documentation. | ||
47 | * This was done because the low 8 summary bits really aren't used | ||
48 | * for reporting any interrupts (the PCI-ISA bridge, bit 7, isn't | ||
49 | * used for this purpose, as PIC interrupts are delivered as the | ||
50 | * vectors 0x800-0x8f0). | ||
51 | * But I really don't want to change the fixup code for allocation | ||
52 | * of IRQs, nor the alpha_irq_mask maintenance stuff, both of which | ||
53 | * look nice and clean now. | ||
54 | * So, here's this grotty hack... :-( | ||
55 | */ | ||
56 | if (irq >= 16) | ||
57 | irq = irq + 8; | ||
58 | |||
59 | handle_irq(irq, regs); | ||
60 | } | ||
61 | |||
62 | static void __init | ||
63 | miata_init_irq(void) | ||
64 | { | ||
65 | if (alpha_using_srm) | ||
66 | alpha_mv.device_interrupt = miata_srm_device_interrupt; | ||
67 | |||
68 | #if 0 | ||
69 | /* These break on MiataGL so we'll try not to do it at all. */ | ||
70 | *(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb(); /* ISA/NMI HI */ | ||
71 | *(vulp)PYXIS_RT_COUNT = 0UL; mb(); /* clear count */ | ||
72 | #endif | ||
73 | |||
74 | init_i8259a_irqs(); | ||
75 | |||
76 | /* Not interested in the bogus interrupts (3,10), Fan Fault (0), | ||
77 | NMI (1), or EIDE (9). | ||
78 | |||
79 | We also disable the risers (4,5), since we don't know how to | ||
80 | route the interrupts behind the bridge. */ | ||
81 | init_pyxis_irqs(0x63b0000); | ||
82 | |||
83 | common_init_isa_dma(); | ||
84 | setup_irq(16+2, &halt_switch_irqaction); /* SRM only? */ | ||
85 | setup_irq(16+6, &timer_cascade_irqaction); | ||
86 | } | ||
87 | |||
88 | |||
89 | /* | ||
90 | * PCI Fixup configuration. | ||
91 | * | ||
92 | * Summary @ PYXIS_INT_REQ: | ||
93 | * Bit Meaning | ||
94 | * 0 Fan Fault | ||
95 | * 1 NMI | ||
96 | * 2 Halt/Reset switch | ||
97 | * 3 none | ||
98 | * 4 CID0 (Riser ID) | ||
99 | * 5 CID1 (Riser ID) | ||
100 | * 6 Interval timer | ||
101 | * 7 PCI-ISA Bridge | ||
102 | * 8 Ethernet | ||
103 | * 9 EIDE (deprecated, ISA 14/15 used) | ||
104 | *10 none | ||
105 | *11 USB | ||
106 | *12 Interrupt Line A from slot 4 | ||
107 | *13 Interrupt Line B from slot 4 | ||
108 | *14 Interrupt Line C from slot 4 | ||
109 | *15 Interrupt Line D from slot 4 | ||
110 | *16 Interrupt Line A from slot 5 | ||
111 | *17 Interrupt line B from slot 5 | ||
112 | *18 Interrupt Line C from slot 5 | ||
113 | *19 Interrupt Line D from slot 5 | ||
114 | *20 Interrupt Line A from slot 1 | ||
115 | *21 Interrupt Line B from slot 1 | ||
116 | *22 Interrupt Line C from slot 1 | ||
117 | *23 Interrupt Line D from slot 1 | ||
118 | *24 Interrupt Line A from slot 2 | ||
119 | *25 Interrupt Line B from slot 2 | ||
120 | *26 Interrupt Line C from slot 2 | ||
121 | *27 Interrupt Line D from slot 2 | ||
122 | *27 Interrupt Line A from slot 3 | ||
123 | *29 Interrupt Line B from slot 3 | ||
124 | *30 Interrupt Line C from slot 3 | ||
125 | *31 Interrupt Line D from slot 3 | ||
126 | * | ||
127 | * The device to slot mapping looks like: | ||
128 | * | ||
129 | * Slot Device | ||
130 | * 3 DC21142 Ethernet | ||
131 | * 4 EIDE CMD646 | ||
132 | * 5 none | ||
133 | * 6 USB | ||
134 | * 7 PCI-ISA bridge | ||
135 | * 8 PCI-PCI Bridge (SBU Riser) | ||
136 | * 9 none | ||
137 | * 10 none | ||
138 | * 11 PCI on board slot 4 (SBU Riser) | ||
139 | * 12 PCI on board slot 5 (SBU Riser) | ||
140 | * | ||
141 | * These are behind the bridge, so I'm not sure what to do... | ||
142 | * | ||
143 | * 13 PCI on board slot 1 (SBU Riser) | ||
144 | * 14 PCI on board slot 2 (SBU Riser) | ||
145 | * 15 PCI on board slot 3 (SBU Riser) | ||
146 | * | ||
147 | * | ||
148 | * This two layered interrupt approach means that we allocate IRQ 16 and | ||
149 | * above for PCI interrupts. The IRQ relates to which bit the interrupt | ||
150 | * comes in on. This makes interrupt processing much easier. | ||
151 | */ | ||
152 | |||
153 | static int __init | ||
154 | miata_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
155 | { | ||
156 | static char irq_tab[18][5] __initdata = { | ||
157 | /*INT INTA INTB INTC INTD */ | ||
158 | {16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8}, /* IdSel 14, DC21142 */ | ||
159 | { -1, -1, -1, -1, -1}, /* IdSel 15, EIDE */ | ||
160 | { -1, -1, -1, -1, -1}, /* IdSel 16, none */ | ||
161 | { -1, -1, -1, -1, -1}, /* IdSel 17, none */ | ||
162 | { -1, -1, -1, -1, -1}, /* IdSel 18, PCI-ISA */ | ||
163 | { -1, -1, -1, -1, -1}, /* IdSel 19, PCI-PCI */ | ||
164 | { -1, -1, -1, -1, -1}, /* IdSel 20, none */ | ||
165 | { -1, -1, -1, -1, -1}, /* IdSel 21, none */ | ||
166 | {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 22, slot 4 */ | ||
167 | {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 23, slot 5 */ | ||
168 | /* the next 7 are actually on PCI bus 1, across the bridge */ | ||
169 | {16+11, 16+11, 16+11, 16+11, 16+11}, /* IdSel 24, QLISP/GL*/ | ||
170 | { -1, -1, -1, -1, -1}, /* IdSel 25, none */ | ||
171 | { -1, -1, -1, -1, -1}, /* IdSel 26, none */ | ||
172 | { -1, -1, -1, -1, -1}, /* IdSel 27, none */ | ||
173 | {16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 28, slot 1 */ | ||
174 | {16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 29, slot 2 */ | ||
175 | {16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 30, slot 3 */ | ||
176 | /* This bridge is on the main bus of the later orig MIATA */ | ||
177 | { -1, -1, -1, -1, -1}, /* IdSel 31, PCI-PCI */ | ||
178 | }; | ||
179 | const long min_idsel = 3, max_idsel = 20, irqs_per_slot = 5; | ||
180 | |||
181 | /* the USB function of the 82c693 has it's interrupt connected to | ||
182 | the 2nd 8259 controller. So we have to check for it first. */ | ||
183 | |||
184 | if((slot == 7) && (PCI_FUNC(dev->devfn) == 3)) { | ||
185 | u8 irq=0; | ||
186 | |||
187 | if(pci_read_config_byte(pci_find_slot(dev->bus->number, dev->devfn & ~(7)), 0x40,&irq)!=PCIBIOS_SUCCESSFUL) | ||
188 | return -1; | ||
189 | else | ||
190 | return irq; | ||
191 | } | ||
192 | |||
193 | return COMMON_TABLE_LOOKUP; | ||
194 | } | ||
195 | |||
196 | static u8 __init | ||
197 | miata_swizzle(struct pci_dev *dev, u8 *pinp) | ||
198 | { | ||
199 | int slot, pin = *pinp; | ||
200 | |||
201 | if (dev->bus->number == 0) { | ||
202 | slot = PCI_SLOT(dev->devfn); | ||
203 | } | ||
204 | /* Check for the built-in bridge. */ | ||
205 | else if ((PCI_SLOT(dev->bus->self->devfn) == 8) || | ||
206 | (PCI_SLOT(dev->bus->self->devfn) == 20)) { | ||
207 | slot = PCI_SLOT(dev->devfn) + 9; | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | /* Must be a card-based bridge. */ | ||
212 | do { | ||
213 | if ((PCI_SLOT(dev->bus->self->devfn) == 8) || | ||
214 | (PCI_SLOT(dev->bus->self->devfn) == 20)) { | ||
215 | slot = PCI_SLOT(dev->devfn) + 9; | ||
216 | break; | ||
217 | } | ||
218 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); | ||
219 | |||
220 | /* Move up the chain of bridges. */ | ||
221 | dev = dev->bus->self; | ||
222 | /* Slot of the next bridge. */ | ||
223 | slot = PCI_SLOT(dev->devfn); | ||
224 | } while (dev->bus->self); | ||
225 | } | ||
226 | *pinp = pin; | ||
227 | return slot; | ||
228 | } | ||
229 | |||
230 | static void __init | ||
231 | miata_init_pci(void) | ||
232 | { | ||
233 | cia_init_pci(); | ||
234 | SMC669_Init(0); /* it might be a GL (fails harmlessly if not) */ | ||
235 | es1888_init(); | ||
236 | } | ||
237 | |||
238 | static void | ||
239 | miata_kill_arch(int mode) | ||
240 | { | ||
241 | cia_kill_arch(mode); | ||
242 | |||
243 | #ifndef ALPHA_RESTORE_SRM_SETUP | ||
244 | switch(mode) { | ||
245 | case LINUX_REBOOT_CMD_RESTART: | ||
246 | /* Who said DEC engineers have no sense of humor? ;-) */ | ||
247 | if (alpha_using_srm) { | ||
248 | *(vuip) PYXIS_RESET = 0x0000dead; | ||
249 | mb(); | ||
250 | } | ||
251 | break; | ||
252 | case LINUX_REBOOT_CMD_HALT: | ||
253 | break; | ||
254 | case LINUX_REBOOT_CMD_POWER_OFF: | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | halt(); | ||
259 | #endif | ||
260 | } | ||
261 | |||
262 | |||
263 | /* | ||
264 | * The System Vector | ||
265 | */ | ||
266 | |||
267 | struct alpha_machine_vector miata_mv __initmv = { | ||
268 | .vector_name = "Miata", | ||
269 | DO_EV5_MMU, | ||
270 | DO_DEFAULT_RTC, | ||
271 | DO_PYXIS_IO, | ||
272 | .machine_check = cia_machine_check, | ||
273 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
274 | .min_io_address = DEFAULT_IO_BASE, | ||
275 | .min_mem_address = DEFAULT_MEM_BASE, | ||
276 | .pci_dac_offset = PYXIS_DAC_OFFSET, | ||
277 | |||
278 | .nr_irqs = 48, | ||
279 | .device_interrupt = pyxis_device_interrupt, | ||
280 | |||
281 | .init_arch = pyxis_init_arch, | ||
282 | .init_irq = miata_init_irq, | ||
283 | .init_rtc = common_init_rtc, | ||
284 | .init_pci = miata_init_pci, | ||
285 | .kill_arch = miata_kill_arch, | ||
286 | .pci_map_irq = miata_map_irq, | ||
287 | .pci_swizzle = miata_swizzle, | ||
288 | }; | ||
289 | ALIAS_MV(miata) | ||
diff --git a/arch/alpha/kernel/sys_mikasa.c b/arch/alpha/kernel/sys_mikasa.c new file mode 100644 index 000000000000..d78a0daa6168 --- /dev/null +++ b/arch/alpha/kernel/sys_mikasa.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_mikasa.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the MIKASA (AlphaServer 1000). | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/bitops.h> | ||
19 | |||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/dma.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <asm/mmu_context.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/pgtable.h> | ||
27 | #include <asm/core_apecs.h> | ||
28 | #include <asm/core_cia.h> | ||
29 | #include <asm/tlbflush.h> | ||
30 | |||
31 | #include "proto.h" | ||
32 | #include "irq_impl.h" | ||
33 | #include "pci_impl.h" | ||
34 | #include "machvec_impl.h" | ||
35 | |||
36 | |||
37 | /* Note mask bit is true for ENABLED irqs. */ | ||
38 | static int cached_irq_mask; | ||
39 | |||
40 | static inline void | ||
41 | mikasa_update_irq_hw(int mask) | ||
42 | { | ||
43 | outw(mask, 0x536); | ||
44 | } | ||
45 | |||
46 | static inline void | ||
47 | mikasa_enable_irq(unsigned int irq) | ||
48 | { | ||
49 | mikasa_update_irq_hw(cached_irq_mask |= 1 << (irq - 16)); | ||
50 | } | ||
51 | |||
52 | static void | ||
53 | mikasa_disable_irq(unsigned int irq) | ||
54 | { | ||
55 | mikasa_update_irq_hw(cached_irq_mask &= ~(1 << (irq - 16))); | ||
56 | } | ||
57 | |||
58 | static unsigned int | ||
59 | mikasa_startup_irq(unsigned int irq) | ||
60 | { | ||
61 | mikasa_enable_irq(irq); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static void | ||
66 | mikasa_end_irq(unsigned int irq) | ||
67 | { | ||
68 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
69 | mikasa_enable_irq(irq); | ||
70 | } | ||
71 | |||
72 | static struct hw_interrupt_type mikasa_irq_type = { | ||
73 | .typename = "MIKASA", | ||
74 | .startup = mikasa_startup_irq, | ||
75 | .shutdown = mikasa_disable_irq, | ||
76 | .enable = mikasa_enable_irq, | ||
77 | .disable = mikasa_disable_irq, | ||
78 | .ack = mikasa_disable_irq, | ||
79 | .end = mikasa_end_irq, | ||
80 | }; | ||
81 | |||
82 | static void | ||
83 | mikasa_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
84 | { | ||
85 | unsigned long pld; | ||
86 | unsigned int i; | ||
87 | |||
88 | /* Read the interrupt summary registers */ | ||
89 | pld = (((~inw(0x534) & 0x0000ffffUL) << 16) | ||
90 | | (((unsigned long) inb(0xa0)) << 8) | ||
91 | | inb(0x20)); | ||
92 | |||
93 | /* | ||
94 | * Now for every possible bit set, work through them and call | ||
95 | * the appropriate interrupt handler. | ||
96 | */ | ||
97 | while (pld) { | ||
98 | i = ffz(~pld); | ||
99 | pld &= pld - 1; /* clear least bit set */ | ||
100 | if (i < 16) { | ||
101 | isa_device_interrupt(vector, regs); | ||
102 | } else { | ||
103 | handle_irq(i, regs); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static void __init | ||
109 | mikasa_init_irq(void) | ||
110 | { | ||
111 | long i; | ||
112 | |||
113 | if (alpha_using_srm) | ||
114 | alpha_mv.device_interrupt = srm_device_interrupt; | ||
115 | |||
116 | mikasa_update_irq_hw(0); | ||
117 | |||
118 | for (i = 16; i < 32; ++i) { | ||
119 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
120 | irq_desc[i].handler = &mikasa_irq_type; | ||
121 | } | ||
122 | |||
123 | init_i8259a_irqs(); | ||
124 | common_init_isa_dma(); | ||
125 | } | ||
126 | |||
127 | |||
128 | /* | ||
129 | * PCI Fixup configuration. | ||
130 | * | ||
131 | * Summary @ 0x536: | ||
132 | * Bit Meaning | ||
133 | * 0 Interrupt Line A from slot 0 | ||
134 | * 1 Interrupt Line B from slot 0 | ||
135 | * 2 Interrupt Line C from slot 0 | ||
136 | * 3 Interrupt Line D from slot 0 | ||
137 | * 4 Interrupt Line A from slot 1 | ||
138 | * 5 Interrupt line B from slot 1 | ||
139 | * 6 Interrupt Line C from slot 1 | ||
140 | * 7 Interrupt Line D from slot 1 | ||
141 | * 8 Interrupt Line A from slot 2 | ||
142 | * 9 Interrupt Line B from slot 2 | ||
143 | *10 Interrupt Line C from slot 2 | ||
144 | *11 Interrupt Line D from slot 2 | ||
145 | *12 NCR 810 SCSI | ||
146 | *13 Power Supply Fail | ||
147 | *14 Temperature Warn | ||
148 | *15 Reserved | ||
149 | * | ||
150 | * The device to slot mapping looks like: | ||
151 | * | ||
152 | * Slot Device | ||
153 | * 6 NCR SCSI controller | ||
154 | * 7 Intel PCI-EISA bridge chip | ||
155 | * 11 PCI on board slot 0 | ||
156 | * 12 PCI on board slot 1 | ||
157 | * 13 PCI on board slot 2 | ||
158 | * | ||
159 | * | ||
160 | * This two layered interrupt approach means that we allocate IRQ 16 and | ||
161 | * above for PCI interrupts. The IRQ relates to which bit the interrupt | ||
162 | * comes in on. This makes interrupt processing much easier. | ||
163 | */ | ||
164 | |||
165 | static int __init | ||
166 | mikasa_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
167 | { | ||
168 | static char irq_tab[8][5] __initdata = { | ||
169 | /*INT INTA INTB INTC INTD */ | ||
170 | {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 17, SCSI */ | ||
171 | { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ | ||
172 | { -1, -1, -1, -1, -1}, /* IdSel 19, ???? */ | ||
173 | { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ | ||
174 | { -1, -1, -1, -1, -1}, /* IdSel 21, ???? */ | ||
175 | { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 0 */ | ||
176 | { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ | ||
177 | { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 24, slot 2 */ | ||
178 | }; | ||
179 | const long min_idsel = 6, max_idsel = 13, irqs_per_slot = 5; | ||
180 | return COMMON_TABLE_LOOKUP; | ||
181 | } | ||
182 | |||
183 | |||
184 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(CONFIG_ALPHA_PRIMO) | ||
185 | static void | ||
186 | mikasa_apecs_machine_check(unsigned long vector, unsigned long la_ptr, | ||
187 | struct pt_regs * regs) | ||
188 | { | ||
189 | #define MCHK_NO_DEVSEL 0x205U | ||
190 | #define MCHK_NO_TABT 0x204U | ||
191 | |||
192 | struct el_common *mchk_header; | ||
193 | unsigned int code; | ||
194 | |||
195 | mchk_header = (struct el_common *)la_ptr; | ||
196 | |||
197 | /* Clear the error before any reporting. */ | ||
198 | mb(); | ||
199 | mb(); /* magic */ | ||
200 | draina(); | ||
201 | apecs_pci_clr_err(); | ||
202 | wrmces(0x7); | ||
203 | mb(); | ||
204 | |||
205 | code = mchk_header->code; | ||
206 | process_mcheck_info(vector, la_ptr, regs, "MIKASA APECS", | ||
207 | (mcheck_expected(0) | ||
208 | && (code == MCHK_NO_DEVSEL | ||
209 | || code == MCHK_NO_TABT))); | ||
210 | } | ||
211 | #endif | ||
212 | |||
213 | |||
214 | /* | ||
215 | * The System Vector | ||
216 | */ | ||
217 | |||
218 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(CONFIG_ALPHA_PRIMO) | ||
219 | struct alpha_machine_vector mikasa_mv __initmv = { | ||
220 | .vector_name = "Mikasa", | ||
221 | DO_EV4_MMU, | ||
222 | DO_DEFAULT_RTC, | ||
223 | DO_APECS_IO, | ||
224 | .machine_check = mikasa_apecs_machine_check, | ||
225 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
226 | .min_io_address = DEFAULT_IO_BASE, | ||
227 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
228 | |||
229 | .nr_irqs = 32, | ||
230 | .device_interrupt = mikasa_device_interrupt, | ||
231 | |||
232 | .init_arch = apecs_init_arch, | ||
233 | .init_irq = mikasa_init_irq, | ||
234 | .init_rtc = common_init_rtc, | ||
235 | .init_pci = common_init_pci, | ||
236 | .pci_map_irq = mikasa_map_irq, | ||
237 | .pci_swizzle = common_swizzle, | ||
238 | }; | ||
239 | ALIAS_MV(mikasa) | ||
240 | #endif | ||
241 | |||
242 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_PRIMO) | ||
243 | struct alpha_machine_vector mikasa_primo_mv __initmv = { | ||
244 | .vector_name = "Mikasa-Primo", | ||
245 | DO_EV5_MMU, | ||
246 | DO_DEFAULT_RTC, | ||
247 | DO_CIA_IO, | ||
248 | .machine_check = cia_machine_check, | ||
249 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
250 | .min_io_address = DEFAULT_IO_BASE, | ||
251 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
252 | |||
253 | .nr_irqs = 32, | ||
254 | .device_interrupt = mikasa_device_interrupt, | ||
255 | |||
256 | .init_arch = cia_init_arch, | ||
257 | .init_irq = mikasa_init_irq, | ||
258 | .init_rtc = common_init_rtc, | ||
259 | .init_pci = cia_init_pci, | ||
260 | .kill_arch = cia_kill_arch, | ||
261 | .pci_map_irq = mikasa_map_irq, | ||
262 | .pci_swizzle = common_swizzle, | ||
263 | }; | ||
264 | ALIAS_MV(mikasa_primo) | ||
265 | #endif | ||
diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c new file mode 100644 index 000000000000..c0d696efec5b --- /dev/null +++ b/arch/alpha/kernel/sys_nautilus.c | |||
@@ -0,0 +1,269 @@ | |||
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) | ||
diff --git a/arch/alpha/kernel/sys_noritake.c b/arch/alpha/kernel/sys_noritake.c new file mode 100644 index 000000000000..65061f5d7410 --- /dev/null +++ b/arch/alpha/kernel/sys_noritake.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_noritake.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the NORITAKE (AlphaServer 1000A), | ||
9 | * CORELLE (AlphaServer 800), and ALCOR Primo (AlphaStation 600A). | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/bitops.h> | ||
20 | |||
21 | #include <asm/ptrace.h> | ||
22 | #include <asm/system.h> | ||
23 | #include <asm/dma.h> | ||
24 | #include <asm/irq.h> | ||
25 | #include <asm/mmu_context.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/pgtable.h> | ||
28 | #include <asm/core_apecs.h> | ||
29 | #include <asm/core_cia.h> | ||
30 | #include <asm/tlbflush.h> | ||
31 | |||
32 | #include "proto.h" | ||
33 | #include "irq_impl.h" | ||
34 | #include "pci_impl.h" | ||
35 | #include "machvec_impl.h" | ||
36 | |||
37 | /* Note mask bit is true for ENABLED irqs. */ | ||
38 | static int cached_irq_mask; | ||
39 | |||
40 | static inline void | ||
41 | noritake_update_irq_hw(int irq, int mask) | ||
42 | { | ||
43 | int port = 0x54a; | ||
44 | if (irq >= 32) { | ||
45 | mask >>= 16; | ||
46 | port = 0x54c; | ||
47 | } | ||
48 | outw(mask, port); | ||
49 | } | ||
50 | |||
51 | static void | ||
52 | noritake_enable_irq(unsigned int irq) | ||
53 | { | ||
54 | noritake_update_irq_hw(irq, cached_irq_mask |= 1 << (irq - 16)); | ||
55 | } | ||
56 | |||
57 | static void | ||
58 | noritake_disable_irq(unsigned int irq) | ||
59 | { | ||
60 | noritake_update_irq_hw(irq, cached_irq_mask &= ~(1 << (irq - 16))); | ||
61 | } | ||
62 | |||
63 | static unsigned int | ||
64 | noritake_startup_irq(unsigned int irq) | ||
65 | { | ||
66 | noritake_enable_irq(irq); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static struct hw_interrupt_type noritake_irq_type = { | ||
71 | .typename = "NORITAKE", | ||
72 | .startup = noritake_startup_irq, | ||
73 | .shutdown = noritake_disable_irq, | ||
74 | .enable = noritake_enable_irq, | ||
75 | .disable = noritake_disable_irq, | ||
76 | .ack = noritake_disable_irq, | ||
77 | .end = noritake_enable_irq, | ||
78 | }; | ||
79 | |||
80 | static void | ||
81 | noritake_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
82 | { | ||
83 | unsigned long pld; | ||
84 | unsigned int i; | ||
85 | |||
86 | /* Read the interrupt summary registers of NORITAKE */ | ||
87 | pld = (((unsigned long) inw(0x54c) << 32) | ||
88 | | ((unsigned long) inw(0x54a) << 16) | ||
89 | | ((unsigned long) inb(0xa0) << 8) | ||
90 | | inb(0x20)); | ||
91 | |||
92 | /* | ||
93 | * Now for every possible bit set, work through them and call | ||
94 | * the appropriate interrupt handler. | ||
95 | */ | ||
96 | while (pld) { | ||
97 | i = ffz(~pld); | ||
98 | pld &= pld - 1; /* clear least bit set */ | ||
99 | if (i < 16) { | ||
100 | isa_device_interrupt(vector, regs); | ||
101 | } else { | ||
102 | handle_irq(i, regs); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static void | ||
108 | noritake_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
109 | { | ||
110 | int irq; | ||
111 | |||
112 | irq = (vector - 0x800) >> 4; | ||
113 | |||
114 | /* | ||
115 | * I really hate to do this, too, but the NORITAKE SRM console also | ||
116 | * reports PCI vectors *lower* than I expected from the bit numbers | ||
117 | * in the documentation. | ||
118 | * But I really don't want to change the fixup code for allocation | ||
119 | * of IRQs, nor the alpha_irq_mask maintenance stuff, both of which | ||
120 | * look nice and clean now. | ||
121 | * So, here's this additional grotty hack... :-( | ||
122 | */ | ||
123 | if (irq >= 16) | ||
124 | irq = irq + 1; | ||
125 | |||
126 | handle_irq(irq, regs); | ||
127 | } | ||
128 | |||
129 | static void __init | ||
130 | noritake_init_irq(void) | ||
131 | { | ||
132 | long i; | ||
133 | |||
134 | if (alpha_using_srm) | ||
135 | alpha_mv.device_interrupt = noritake_srm_device_interrupt; | ||
136 | |||
137 | outw(0, 0x54a); | ||
138 | outw(0, 0x54c); | ||
139 | |||
140 | for (i = 16; i < 48; ++i) { | ||
141 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
142 | irq_desc[i].handler = &noritake_irq_type; | ||
143 | } | ||
144 | |||
145 | init_i8259a_irqs(); | ||
146 | common_init_isa_dma(); | ||
147 | } | ||
148 | |||
149 | |||
150 | /* | ||
151 | * PCI Fixup configuration. | ||
152 | * | ||
153 | * Summary @ 0x542, summary register #1: | ||
154 | * Bit Meaning | ||
155 | * 0 All valid ints from summary regs 2 & 3 | ||
156 | * 1 QLOGIC ISP1020A SCSI | ||
157 | * 2 Interrupt Line A from slot 0 | ||
158 | * 3 Interrupt Line B from slot 0 | ||
159 | * 4 Interrupt Line A from slot 1 | ||
160 | * 5 Interrupt line B from slot 1 | ||
161 | * 6 Interrupt Line A from slot 2 | ||
162 | * 7 Interrupt Line B from slot 2 | ||
163 | * 8 Interrupt Line A from slot 3 | ||
164 | * 9 Interrupt Line B from slot 3 | ||
165 | *10 Interrupt Line A from slot 4 | ||
166 | *11 Interrupt Line B from slot 4 | ||
167 | *12 Interrupt Line A from slot 5 | ||
168 | *13 Interrupt Line B from slot 5 | ||
169 | *14 Interrupt Line A from slot 6 | ||
170 | *15 Interrupt Line B from slot 6 | ||
171 | * | ||
172 | * Summary @ 0x544, summary register #2: | ||
173 | * Bit Meaning | ||
174 | * 0 OR of all unmasked ints in SR #2 | ||
175 | * 1 OR of secondary bus ints | ||
176 | * 2 Interrupt Line C from slot 0 | ||
177 | * 3 Interrupt Line D from slot 0 | ||
178 | * 4 Interrupt Line C from slot 1 | ||
179 | * 5 Interrupt line D from slot 1 | ||
180 | * 6 Interrupt Line C from slot 2 | ||
181 | * 7 Interrupt Line D from slot 2 | ||
182 | * 8 Interrupt Line C from slot 3 | ||
183 | * 9 Interrupt Line D from slot 3 | ||
184 | *10 Interrupt Line C from slot 4 | ||
185 | *11 Interrupt Line D from slot 4 | ||
186 | *12 Interrupt Line C from slot 5 | ||
187 | *13 Interrupt Line D from slot 5 | ||
188 | *14 Interrupt Line C from slot 6 | ||
189 | *15 Interrupt Line D from slot 6 | ||
190 | * | ||
191 | * The device to slot mapping looks like: | ||
192 | * | ||
193 | * Slot Device | ||
194 | * 7 Intel PCI-EISA bridge chip | ||
195 | * 8 DEC PCI-PCI bridge chip | ||
196 | * 11 PCI on board slot 0 | ||
197 | * 12 PCI on board slot 1 | ||
198 | * 13 PCI on board slot 2 | ||
199 | * | ||
200 | * | ||
201 | * This two layered interrupt approach means that we allocate IRQ 16 and | ||
202 | * above for PCI interrupts. The IRQ relates to which bit the interrupt | ||
203 | * comes in on. This makes interrupt processing much easier. | ||
204 | */ | ||
205 | |||
206 | static int __init | ||
207 | noritake_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
208 | { | ||
209 | static char irq_tab[15][5] __initdata = { | ||
210 | /*INT INTA INTB INTC INTD */ | ||
211 | /* note: IDSELs 16, 17, and 25 are CORELLE only */ | ||
212 | { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ | ||
213 | { -1, -1, -1, -1, -1}, /* IdSel 17, S3 Trio64 */ | ||
214 | { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ | ||
215 | { -1, -1, -1, -1, -1}, /* IdSel 19, PPB */ | ||
216 | { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ | ||
217 | { -1, -1, -1, -1, -1}, /* IdSel 21, ???? */ | ||
218 | { 16+2, 16+2, 16+3, 32+2, 32+3}, /* IdSel 22, slot 0 */ | ||
219 | { 16+4, 16+4, 16+5, 32+4, 32+5}, /* IdSel 23, slot 1 */ | ||
220 | { 16+6, 16+6, 16+7, 32+6, 32+7}, /* IdSel 24, slot 2 */ | ||
221 | { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 25, slot 3 */ | ||
222 | /* The following 5 are actually on PCI bus 1, which is | ||
223 | across the built-in bridge of the NORITAKE only. */ | ||
224 | { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ | ||
225 | { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 17, slot 3 */ | ||
226 | {16+10, 16+10, 16+11, 32+10, 32+11}, /* IdSel 18, slot 4 */ | ||
227 | {16+12, 16+12, 16+13, 32+12, 32+13}, /* IdSel 19, slot 5 */ | ||
228 | {16+14, 16+14, 16+15, 32+14, 32+15}, /* IdSel 20, slot 6 */ | ||
229 | }; | ||
230 | const long min_idsel = 5, max_idsel = 19, irqs_per_slot = 5; | ||
231 | return COMMON_TABLE_LOOKUP; | ||
232 | } | ||
233 | |||
234 | static u8 __init | ||
235 | noritake_swizzle(struct pci_dev *dev, u8 *pinp) | ||
236 | { | ||
237 | int slot, pin = *pinp; | ||
238 | |||
239 | if (dev->bus->number == 0) { | ||
240 | slot = PCI_SLOT(dev->devfn); | ||
241 | } | ||
242 | /* Check for the built-in bridge */ | ||
243 | else if (PCI_SLOT(dev->bus->self->devfn) == 8) { | ||
244 | slot = PCI_SLOT(dev->devfn) + 15; /* WAG! */ | ||
245 | } | ||
246 | else | ||
247 | { | ||
248 | /* Must be a card-based bridge. */ | ||
249 | do { | ||
250 | if (PCI_SLOT(dev->bus->self->devfn) == 8) { | ||
251 | slot = PCI_SLOT(dev->devfn) + 15; | ||
252 | break; | ||
253 | } | ||
254 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; | ||
255 | |||
256 | /* Move up the chain of bridges. */ | ||
257 | dev = dev->bus->self; | ||
258 | /* Slot of the next bridge. */ | ||
259 | slot = PCI_SLOT(dev->devfn); | ||
260 | } while (dev->bus->self); | ||
261 | } | ||
262 | *pinp = pin; | ||
263 | return slot; | ||
264 | } | ||
265 | |||
266 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(CONFIG_ALPHA_PRIMO) | ||
267 | static void | ||
268 | noritake_apecs_machine_check(unsigned long vector, unsigned long la_ptr, | ||
269 | struct pt_regs * regs) | ||
270 | { | ||
271 | #define MCHK_NO_DEVSEL 0x205U | ||
272 | #define MCHK_NO_TABT 0x204U | ||
273 | |||
274 | struct el_common *mchk_header; | ||
275 | unsigned int code; | ||
276 | |||
277 | mchk_header = (struct el_common *)la_ptr; | ||
278 | |||
279 | /* Clear the error before any reporting. */ | ||
280 | mb(); | ||
281 | mb(); /* magic */ | ||
282 | draina(); | ||
283 | apecs_pci_clr_err(); | ||
284 | wrmces(0x7); | ||
285 | mb(); | ||
286 | |||
287 | code = mchk_header->code; | ||
288 | process_mcheck_info(vector, la_ptr, regs, "NORITAKE APECS", | ||
289 | (mcheck_expected(0) | ||
290 | && (code == MCHK_NO_DEVSEL | ||
291 | || code == MCHK_NO_TABT))); | ||
292 | } | ||
293 | #endif | ||
294 | |||
295 | |||
296 | /* | ||
297 | * The System Vectors | ||
298 | */ | ||
299 | |||
300 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(CONFIG_ALPHA_PRIMO) | ||
301 | struct alpha_machine_vector noritake_mv __initmv = { | ||
302 | .vector_name = "Noritake", | ||
303 | DO_EV4_MMU, | ||
304 | DO_DEFAULT_RTC, | ||
305 | DO_APECS_IO, | ||
306 | .machine_check = noritake_apecs_machine_check, | ||
307 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
308 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
309 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
310 | |||
311 | .nr_irqs = 48, | ||
312 | .device_interrupt = noritake_device_interrupt, | ||
313 | |||
314 | .init_arch = apecs_init_arch, | ||
315 | .init_irq = noritake_init_irq, | ||
316 | .init_rtc = common_init_rtc, | ||
317 | .init_pci = common_init_pci, | ||
318 | .pci_map_irq = noritake_map_irq, | ||
319 | .pci_swizzle = noritake_swizzle, | ||
320 | }; | ||
321 | ALIAS_MV(noritake) | ||
322 | #endif | ||
323 | |||
324 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_PRIMO) | ||
325 | struct alpha_machine_vector noritake_primo_mv __initmv = { | ||
326 | .vector_name = "Noritake-Primo", | ||
327 | DO_EV5_MMU, | ||
328 | DO_DEFAULT_RTC, | ||
329 | DO_CIA_IO, | ||
330 | .machine_check = cia_machine_check, | ||
331 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
332 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
333 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
334 | |||
335 | .nr_irqs = 48, | ||
336 | .device_interrupt = noritake_device_interrupt, | ||
337 | |||
338 | .init_arch = cia_init_arch, | ||
339 | .init_irq = noritake_init_irq, | ||
340 | .init_rtc = common_init_rtc, | ||
341 | .init_pci = cia_init_pci, | ||
342 | .kill_arch = cia_kill_arch, | ||
343 | .pci_map_irq = noritake_map_irq, | ||
344 | .pci_swizzle = noritake_swizzle, | ||
345 | }; | ||
346 | ALIAS_MV(noritake_primo) | ||
347 | #endif | ||
diff --git a/arch/alpha/kernel/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c new file mode 100644 index 000000000000..05888a02a604 --- /dev/null +++ b/arch/alpha/kernel/sys_rawhide.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_rawhide.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the RAWHIDE. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/init.h> | ||
17 | |||
18 | #include <asm/ptrace.h> | ||
19 | #include <asm/system.h> | ||
20 | #include <asm/dma.h> | ||
21 | #include <asm/irq.h> | ||
22 | #include <asm/mmu_context.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/core_mcpcia.h> | ||
26 | #include <asm/tlbflush.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | #include "irq_impl.h" | ||
30 | #include "pci_impl.h" | ||
31 | #include "machvec_impl.h" | ||
32 | |||
33 | |||
34 | /* | ||
35 | * HACK ALERT! only the boot cpu is used for interrupts. | ||
36 | */ | ||
37 | |||
38 | |||
39 | /* Note mask bit is true for ENABLED irqs. */ | ||
40 | |||
41 | static unsigned int hose_irq_masks[4] = { | ||
42 | 0xff0000, 0xfe0000, 0xff0000, 0xff0000 | ||
43 | }; | ||
44 | static unsigned int cached_irq_masks[4]; | ||
45 | DEFINE_SPINLOCK(rawhide_irq_lock); | ||
46 | |||
47 | static inline void | ||
48 | rawhide_update_irq_hw(int hose, int mask) | ||
49 | { | ||
50 | *(vuip)MCPCIA_INT_MASK0(MCPCIA_HOSE2MID(hose)) = mask; | ||
51 | mb(); | ||
52 | *(vuip)MCPCIA_INT_MASK0(MCPCIA_HOSE2MID(hose)); | ||
53 | } | ||
54 | |||
55 | static inline void | ||
56 | rawhide_enable_irq(unsigned int irq) | ||
57 | { | ||
58 | unsigned int mask, hose; | ||
59 | |||
60 | irq -= 16; | ||
61 | hose = irq / 24; | ||
62 | irq -= hose * 24; | ||
63 | mask = 1 << irq; | ||
64 | |||
65 | spin_lock(&rawhide_irq_lock); | ||
66 | mask |= cached_irq_masks[hose]; | ||
67 | cached_irq_masks[hose] = mask; | ||
68 | rawhide_update_irq_hw(hose, mask); | ||
69 | spin_unlock(&rawhide_irq_lock); | ||
70 | } | ||
71 | |||
72 | static void | ||
73 | rawhide_disable_irq(unsigned int irq) | ||
74 | { | ||
75 | unsigned int mask, hose; | ||
76 | |||
77 | irq -= 16; | ||
78 | hose = irq / 24; | ||
79 | irq -= hose * 24; | ||
80 | mask = ~(1 << irq) | hose_irq_masks[hose]; | ||
81 | |||
82 | spin_lock(&rawhide_irq_lock); | ||
83 | mask &= cached_irq_masks[hose]; | ||
84 | cached_irq_masks[hose] = mask; | ||
85 | rawhide_update_irq_hw(hose, mask); | ||
86 | spin_unlock(&rawhide_irq_lock); | ||
87 | } | ||
88 | |||
89 | static void | ||
90 | rawhide_mask_and_ack_irq(unsigned int irq) | ||
91 | { | ||
92 | unsigned int mask, mask1, hose; | ||
93 | |||
94 | irq -= 16; | ||
95 | hose = irq / 24; | ||
96 | irq -= hose * 24; | ||
97 | mask1 = 1 << irq; | ||
98 | mask = ~mask1 | hose_irq_masks[hose]; | ||
99 | |||
100 | spin_lock(&rawhide_irq_lock); | ||
101 | |||
102 | mask &= cached_irq_masks[hose]; | ||
103 | cached_irq_masks[hose] = mask; | ||
104 | rawhide_update_irq_hw(hose, mask); | ||
105 | |||
106 | /* Clear the interrupt. */ | ||
107 | *(vuip)MCPCIA_INT_REQ(MCPCIA_HOSE2MID(hose)) = mask1; | ||
108 | |||
109 | spin_unlock(&rawhide_irq_lock); | ||
110 | } | ||
111 | |||
112 | static unsigned int | ||
113 | rawhide_startup_irq(unsigned int irq) | ||
114 | { | ||
115 | rawhide_enable_irq(irq); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static void | ||
120 | rawhide_end_irq(unsigned int irq) | ||
121 | { | ||
122 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
123 | rawhide_enable_irq(irq); | ||
124 | } | ||
125 | |||
126 | static struct hw_interrupt_type rawhide_irq_type = { | ||
127 | .typename = "RAWHIDE", | ||
128 | .startup = rawhide_startup_irq, | ||
129 | .shutdown = rawhide_disable_irq, | ||
130 | .enable = rawhide_enable_irq, | ||
131 | .disable = rawhide_disable_irq, | ||
132 | .ack = rawhide_mask_and_ack_irq, | ||
133 | .end = rawhide_end_irq, | ||
134 | }; | ||
135 | |||
136 | static void | ||
137 | rawhide_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
138 | { | ||
139 | int irq; | ||
140 | |||
141 | irq = (vector - 0x800) >> 4; | ||
142 | |||
143 | /* | ||
144 | * The RAWHIDE SRM console reports PCI interrupts with a vector | ||
145 | * 0x80 *higher* than one might expect, as PCI IRQ 0 (ie bit 0) | ||
146 | * shows up as IRQ 24, etc, etc. We adjust it down by 8 to have | ||
147 | * it line up with the actual bit numbers from the REQ registers, | ||
148 | * which is how we manage the interrupts/mask. Sigh... | ||
149 | * | ||
150 | * Also, PCI #1 interrupts are offset some more... :-( | ||
151 | */ | ||
152 | |||
153 | if (irq == 52) { | ||
154 | /* SCSI on PCI1 is special. */ | ||
155 | irq = 72; | ||
156 | } | ||
157 | |||
158 | /* Adjust by which hose it is from. */ | ||
159 | irq -= ((irq + 16) >> 2) & 0x38; | ||
160 | |||
161 | handle_irq(irq, regs); | ||
162 | } | ||
163 | |||
164 | static void __init | ||
165 | rawhide_init_irq(void) | ||
166 | { | ||
167 | struct pci_controller *hose; | ||
168 | long i; | ||
169 | |||
170 | mcpcia_init_hoses(); | ||
171 | |||
172 | for (hose = hose_head; hose; hose = hose->next) { | ||
173 | unsigned int h = hose->index; | ||
174 | unsigned int mask = hose_irq_masks[h]; | ||
175 | |||
176 | cached_irq_masks[h] = mask; | ||
177 | *(vuip)MCPCIA_INT_MASK0(MCPCIA_HOSE2MID(h)) = mask; | ||
178 | *(vuip)MCPCIA_INT_MASK1(MCPCIA_HOSE2MID(h)) = 0; | ||
179 | } | ||
180 | |||
181 | for (i = 16; i < 128; ++i) { | ||
182 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
183 | irq_desc[i].handler = &rawhide_irq_type; | ||
184 | } | ||
185 | |||
186 | init_i8259a_irqs(); | ||
187 | common_init_isa_dma(); | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * PCI Fixup configuration. | ||
192 | * | ||
193 | * Summary @ MCPCIA_PCI0_INT_REQ: | ||
194 | * Bit Meaning | ||
195 | * 0 Interrupt Line A from slot 2 PCI0 | ||
196 | * 1 Interrupt Line B from slot 2 PCI0 | ||
197 | * 2 Interrupt Line C from slot 2 PCI0 | ||
198 | * 3 Interrupt Line D from slot 2 PCI0 | ||
199 | * 4 Interrupt Line A from slot 3 PCI0 | ||
200 | * 5 Interrupt Line B from slot 3 PCI0 | ||
201 | * 6 Interrupt Line C from slot 3 PCI0 | ||
202 | * 7 Interrupt Line D from slot 3 PCI0 | ||
203 | * 8 Interrupt Line A from slot 4 PCI0 | ||
204 | * 9 Interrupt Line B from slot 4 PCI0 | ||
205 | * 10 Interrupt Line C from slot 4 PCI0 | ||
206 | * 11 Interrupt Line D from slot 4 PCI0 | ||
207 | * 12 Interrupt Line A from slot 5 PCI0 | ||
208 | * 13 Interrupt Line B from slot 5 PCI0 | ||
209 | * 14 Interrupt Line C from slot 5 PCI0 | ||
210 | * 15 Interrupt Line D from slot 5 PCI0 | ||
211 | * 16 EISA interrupt (PCI 0) or SCSI interrupt (PCI 1) | ||
212 | * 17-23 NA | ||
213 | * | ||
214 | * IdSel | ||
215 | * 1 EISA bridge (PCI bus 0 only) | ||
216 | * 2 PCI option slot 2 | ||
217 | * 3 PCI option slot 3 | ||
218 | * 4 PCI option slot 4 | ||
219 | * 5 PCI option slot 5 | ||
220 | * | ||
221 | */ | ||
222 | |||
223 | static int __init | ||
224 | rawhide_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
225 | { | ||
226 | static char irq_tab[5][5] __initdata = { | ||
227 | /*INT INTA INTB INTC INTD */ | ||
228 | { 16+16, 16+16, 16+16, 16+16, 16+16}, /* IdSel 1 SCSI PCI 1 */ | ||
229 | { 16+ 0, 16+ 0, 16+ 1, 16+ 2, 16+ 3}, /* IdSel 2 slot 2 */ | ||
230 | { 16+ 4, 16+ 4, 16+ 5, 16+ 6, 16+ 7}, /* IdSel 3 slot 3 */ | ||
231 | { 16+ 8, 16+ 8, 16+ 9, 16+10, 16+11}, /* IdSel 4 slot 4 */ | ||
232 | { 16+12, 16+12, 16+13, 16+14, 16+15} /* IdSel 5 slot 5 */ | ||
233 | }; | ||
234 | const long min_idsel = 1, max_idsel = 5, irqs_per_slot = 5; | ||
235 | |||
236 | struct pci_controller *hose = dev->sysdata; | ||
237 | int irq = COMMON_TABLE_LOOKUP; | ||
238 | if (irq >= 0) | ||
239 | irq += 24 * hose->index; | ||
240 | return irq; | ||
241 | } | ||
242 | |||
243 | |||
244 | /* | ||
245 | * The System Vector | ||
246 | */ | ||
247 | |||
248 | struct alpha_machine_vector rawhide_mv __initmv = { | ||
249 | .vector_name = "Rawhide", | ||
250 | DO_EV5_MMU, | ||
251 | DO_DEFAULT_RTC, | ||
252 | DO_MCPCIA_IO, | ||
253 | .machine_check = mcpcia_machine_check, | ||
254 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
255 | .min_io_address = DEFAULT_IO_BASE, | ||
256 | .min_mem_address = MCPCIA_DEFAULT_MEM_BASE, | ||
257 | .pci_dac_offset = MCPCIA_DAC_OFFSET, | ||
258 | |||
259 | .nr_irqs = 128, | ||
260 | .device_interrupt = rawhide_srm_device_interrupt, | ||
261 | |||
262 | .init_arch = mcpcia_init_arch, | ||
263 | .init_irq = rawhide_init_irq, | ||
264 | .init_rtc = common_init_rtc, | ||
265 | .init_pci = common_init_pci, | ||
266 | .kill_arch = NULL, | ||
267 | .pci_map_irq = rawhide_map_irq, | ||
268 | .pci_swizzle = common_swizzle, | ||
269 | }; | ||
270 | ALIAS_MV(rawhide) | ||
diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c new file mode 100644 index 000000000000..78c30decf3ff --- /dev/null +++ b/arch/alpha/kernel/sys_ruffian.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_ruffian.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999, 2000 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the RUFFIAN. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/ioport.h> | ||
17 | #include <linux/init.h> | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/dma.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/mmu_context.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/core_cia.h> | ||
27 | #include <asm/tlbflush.h> | ||
28 | #include <asm/8253pit.h> | ||
29 | |||
30 | #include "proto.h" | ||
31 | #include "irq_impl.h" | ||
32 | #include "pci_impl.h" | ||
33 | #include "machvec_impl.h" | ||
34 | |||
35 | |||
36 | static void __init | ||
37 | ruffian_init_irq(void) | ||
38 | { | ||
39 | /* Invert 6&7 for i82371 */ | ||
40 | *(vulp)PYXIS_INT_HILO = 0x000000c0UL; mb(); | ||
41 | *(vulp)PYXIS_INT_CNFG = 0x00002064UL; mb(); /* all clear */ | ||
42 | |||
43 | outb(0x11,0xA0); | ||
44 | outb(0x08,0xA1); | ||
45 | outb(0x02,0xA1); | ||
46 | outb(0x01,0xA1); | ||
47 | outb(0xFF,0xA1); | ||
48 | |||
49 | outb(0x11,0x20); | ||
50 | outb(0x00,0x21); | ||
51 | outb(0x04,0x21); | ||
52 | outb(0x01,0x21); | ||
53 | outb(0xFF,0x21); | ||
54 | |||
55 | /* Finish writing the 82C59A PIC Operation Control Words */ | ||
56 | outb(0x20,0xA0); | ||
57 | outb(0x20,0x20); | ||
58 | |||
59 | init_i8259a_irqs(); | ||
60 | |||
61 | /* Not interested in the bogus interrupts (0,3,6), | ||
62 | NMI (1), HALT (2), flash (5), or 21142 (8). */ | ||
63 | init_pyxis_irqs(0x16f0000); | ||
64 | |||
65 | common_init_isa_dma(); | ||
66 | } | ||
67 | |||
68 | #define RUFFIAN_LATCH ((PIT_TICK_RATE + HZ / 2) / HZ) | ||
69 | |||
70 | static void __init | ||
71 | ruffian_init_rtc(void) | ||
72 | { | ||
73 | /* Ruffian does not have the RTC connected to the CPU timer | ||
74 | interrupt. Instead, it uses the PIT connected to IRQ 0. */ | ||
75 | |||
76 | /* Setup interval timer. */ | ||
77 | outb(0x34, 0x43); /* binary, mode 2, LSB/MSB, ch 0 */ | ||
78 | outb(RUFFIAN_LATCH & 0xff, 0x40); /* LSB */ | ||
79 | outb(RUFFIAN_LATCH >> 8, 0x40); /* MSB */ | ||
80 | |||
81 | outb(0xb6, 0x43); /* pit counter 2: speaker */ | ||
82 | outb(0x31, 0x42); | ||
83 | outb(0x13, 0x42); | ||
84 | |||
85 | setup_irq(0, &timer_irqaction); | ||
86 | } | ||
87 | |||
88 | static void | ||
89 | ruffian_kill_arch (int mode) | ||
90 | { | ||
91 | cia_kill_arch(mode); | ||
92 | #if 0 | ||
93 | /* This only causes re-entry to ARCSBIOS */ | ||
94 | /* Perhaps this works for other PYXIS as well? */ | ||
95 | *(vuip) PYXIS_RESET = 0x0000dead; | ||
96 | mb(); | ||
97 | #endif | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Interrupt routing: | ||
102 | * | ||
103 | * Primary bus | ||
104 | * IdSel INTA INTB INTC INTD | ||
105 | * 21052 13 - - - - | ||
106 | * SIO 14 23 - - - | ||
107 | * 21143 15 44 - - - | ||
108 | * Slot 0 17 43 42 41 40 | ||
109 | * | ||
110 | * Secondary bus | ||
111 | * IdSel INTA INTB INTC INTD | ||
112 | * Slot 0 8 (18) 19 18 17 16 | ||
113 | * Slot 1 9 (19) 31 30 29 28 | ||
114 | * Slot 2 10 (20) 27 26 25 24 | ||
115 | * Slot 3 11 (21) 39 38 37 36 | ||
116 | * Slot 4 12 (22) 35 34 33 32 | ||
117 | * 53c875 13 (23) 20 - - - | ||
118 | * | ||
119 | */ | ||
120 | |||
121 | static int __init | ||
122 | ruffian_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
123 | { | ||
124 | static char irq_tab[11][5] __initdata = { | ||
125 | /*INT INTA INTB INTC INTD */ | ||
126 | {-1, -1, -1, -1, -1}, /* IdSel 13, 21052 */ | ||
127 | {-1, -1, -1, -1, -1}, /* IdSel 14, SIO */ | ||
128 | {44, 44, 44, 44, 44}, /* IdSel 15, 21143 */ | ||
129 | {-1, -1, -1, -1, -1}, /* IdSel 16, none */ | ||
130 | {43, 43, 42, 41, 40}, /* IdSel 17, 64-bit slot */ | ||
131 | /* the next 6 are actually on PCI bus 1, across the bridge */ | ||
132 | {19, 19, 18, 17, 16}, /* IdSel 8, slot 0 */ | ||
133 | {31, 31, 30, 29, 28}, /* IdSel 9, slot 1 */ | ||
134 | {27, 27, 26, 25, 24}, /* IdSel 10, slot 2 */ | ||
135 | {39, 39, 38, 37, 36}, /* IdSel 11, slot 3 */ | ||
136 | {35, 35, 34, 33, 32}, /* IdSel 12, slot 4 */ | ||
137 | {20, 20, 20, 20, 20}, /* IdSel 13, 53c875 */ | ||
138 | }; | ||
139 | const long min_idsel = 13, max_idsel = 23, irqs_per_slot = 5; | ||
140 | return COMMON_TABLE_LOOKUP; | ||
141 | } | ||
142 | |||
143 | static u8 __init | ||
144 | ruffian_swizzle(struct pci_dev *dev, u8 *pinp) | ||
145 | { | ||
146 | int slot, pin = *pinp; | ||
147 | |||
148 | if (dev->bus->number == 0) { | ||
149 | slot = PCI_SLOT(dev->devfn); | ||
150 | } | ||
151 | /* Check for the built-in bridge. */ | ||
152 | else if (PCI_SLOT(dev->bus->self->devfn) == 13) { | ||
153 | slot = PCI_SLOT(dev->devfn) + 10; | ||
154 | } | ||
155 | else | ||
156 | { | ||
157 | /* Must be a card-based bridge. */ | ||
158 | do { | ||
159 | if (PCI_SLOT(dev->bus->self->devfn) == 13) { | ||
160 | slot = PCI_SLOT(dev->devfn) + 10; | ||
161 | break; | ||
162 | } | ||
163 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); | ||
164 | |||
165 | /* Move up the chain of bridges. */ | ||
166 | dev = dev->bus->self; | ||
167 | /* Slot of the next bridge. */ | ||
168 | slot = PCI_SLOT(dev->devfn); | ||
169 | } while (dev->bus->self); | ||
170 | } | ||
171 | *pinp = pin; | ||
172 | return slot; | ||
173 | } | ||
174 | |||
175 | #ifdef BUILDING_FOR_MILO | ||
176 | /* | ||
177 | * The DeskStation Ruffian motherboard firmware does not place | ||
178 | * the memory size in the PALimpure area. Therefore, we use | ||
179 | * the Bank Configuration Registers in PYXIS to obtain the size. | ||
180 | */ | ||
181 | static unsigned long __init | ||
182 | ruffian_get_bank_size(unsigned long offset) | ||
183 | { | ||
184 | unsigned long bank_addr, bank, ret = 0; | ||
185 | |||
186 | /* Valid offsets are: 0x800, 0x840 and 0x880 | ||
187 | since Ruffian only uses three banks. */ | ||
188 | bank_addr = (unsigned long)PYXIS_MCR + offset; | ||
189 | bank = *(vulp)bank_addr; | ||
190 | |||
191 | /* Check BANK_ENABLE */ | ||
192 | if (bank & 0x01) { | ||
193 | static unsigned long size[] __initdata = { | ||
194 | 0x40000000UL, /* 0x00, 1G */ | ||
195 | 0x20000000UL, /* 0x02, 512M */ | ||
196 | 0x10000000UL, /* 0x04, 256M */ | ||
197 | 0x08000000UL, /* 0x06, 128M */ | ||
198 | 0x04000000UL, /* 0x08, 64M */ | ||
199 | 0x02000000UL, /* 0x0a, 32M */ | ||
200 | 0x01000000UL, /* 0x0c, 16M */ | ||
201 | 0x00800000UL, /* 0x0e, 8M */ | ||
202 | 0x80000000UL, /* 0x10, 2G */ | ||
203 | }; | ||
204 | |||
205 | bank = (bank & 0x1e) >> 1; | ||
206 | if (bank < sizeof(size)/sizeof(*size)) | ||
207 | ret = size[bank]; | ||
208 | } | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | #endif /* BUILDING_FOR_MILO */ | ||
213 | |||
214 | /* | ||
215 | * The System Vector | ||
216 | */ | ||
217 | |||
218 | struct alpha_machine_vector ruffian_mv __initmv = { | ||
219 | .vector_name = "Ruffian", | ||
220 | DO_EV5_MMU, | ||
221 | DO_DEFAULT_RTC, | ||
222 | DO_PYXIS_IO, | ||
223 | .machine_check = cia_machine_check, | ||
224 | .max_isa_dma_address = ALPHA_RUFFIAN_MAX_ISA_DMA_ADDRESS, | ||
225 | .min_io_address = DEFAULT_IO_BASE, | ||
226 | .min_mem_address = DEFAULT_MEM_BASE, | ||
227 | .pci_dac_offset = PYXIS_DAC_OFFSET, | ||
228 | |||
229 | .nr_irqs = 48, | ||
230 | .device_interrupt = pyxis_device_interrupt, | ||
231 | |||
232 | .init_arch = pyxis_init_arch, | ||
233 | .init_irq = ruffian_init_irq, | ||
234 | .init_rtc = ruffian_init_rtc, | ||
235 | .init_pci = cia_init_pci, | ||
236 | .kill_arch = ruffian_kill_arch, | ||
237 | .pci_map_irq = ruffian_map_irq, | ||
238 | .pci_swizzle = ruffian_swizzle, | ||
239 | }; | ||
240 | ALIAS_MV(ruffian) | ||
diff --git a/arch/alpha/kernel/sys_rx164.c b/arch/alpha/kernel/sys_rx164.c new file mode 100644 index 000000000000..58404243057b --- /dev/null +++ b/arch/alpha/kernel/sys_rx164.c | |||
@@ -0,0 +1,220 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_rx164.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the RX164 (PCA56+POLARIS). | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/bitops.h> | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/dma.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/mmu_context.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/core_polaris.h> | ||
27 | #include <asm/tlbflush.h> | ||
28 | |||
29 | #include "proto.h" | ||
30 | #include "irq_impl.h" | ||
31 | #include "pci_impl.h" | ||
32 | #include "machvec_impl.h" | ||
33 | |||
34 | |||
35 | /* Note mask bit is true for ENABLED irqs. */ | ||
36 | static unsigned long cached_irq_mask; | ||
37 | |||
38 | static inline void | ||
39 | rx164_update_irq_hw(unsigned long mask) | ||
40 | { | ||
41 | volatile unsigned int *irq_mask; | ||
42 | |||
43 | irq_mask = (void *)(POLARIS_DENSE_CONFIG_BASE + 0x74); | ||
44 | *irq_mask = mask; | ||
45 | mb(); | ||
46 | *irq_mask; | ||
47 | } | ||
48 | |||
49 | static inline void | ||
50 | rx164_enable_irq(unsigned int irq) | ||
51 | { | ||
52 | rx164_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); | ||
53 | } | ||
54 | |||
55 | static void | ||
56 | rx164_disable_irq(unsigned int irq) | ||
57 | { | ||
58 | rx164_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); | ||
59 | } | ||
60 | |||
61 | static unsigned int | ||
62 | rx164_startup_irq(unsigned int irq) | ||
63 | { | ||
64 | rx164_enable_irq(irq); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static void | ||
69 | rx164_end_irq(unsigned int irq) | ||
70 | { | ||
71 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
72 | rx164_enable_irq(irq); | ||
73 | } | ||
74 | |||
75 | static struct hw_interrupt_type rx164_irq_type = { | ||
76 | .typename = "RX164", | ||
77 | .startup = rx164_startup_irq, | ||
78 | .shutdown = rx164_disable_irq, | ||
79 | .enable = rx164_enable_irq, | ||
80 | .disable = rx164_disable_irq, | ||
81 | .ack = rx164_disable_irq, | ||
82 | .end = rx164_end_irq, | ||
83 | }; | ||
84 | |||
85 | static void | ||
86 | rx164_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
87 | { | ||
88 | unsigned long pld; | ||
89 | volatile unsigned int *dirr; | ||
90 | long i; | ||
91 | |||
92 | /* Read the interrupt summary register. On Polaris, this is | ||
93 | the DIRR register in PCI config space (offset 0x84). */ | ||
94 | dirr = (void *)(POLARIS_DENSE_CONFIG_BASE + 0x84); | ||
95 | pld = *dirr; | ||
96 | |||
97 | /* | ||
98 | * Now for every possible bit set, work through them and call | ||
99 | * the appropriate interrupt handler. | ||
100 | */ | ||
101 | while (pld) { | ||
102 | i = ffz(~pld); | ||
103 | pld &= pld - 1; /* clear least bit set */ | ||
104 | if (i == 20) { | ||
105 | isa_no_iack_sc_device_interrupt(vector, regs); | ||
106 | } else { | ||
107 | handle_irq(16+i, regs); | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | static void __init | ||
113 | rx164_init_irq(void) | ||
114 | { | ||
115 | long i; | ||
116 | |||
117 | rx164_update_irq_hw(0); | ||
118 | for (i = 16; i < 40; ++i) { | ||
119 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
120 | irq_desc[i].handler = &rx164_irq_type; | ||
121 | } | ||
122 | |||
123 | init_i8259a_irqs(); | ||
124 | common_init_isa_dma(); | ||
125 | |||
126 | setup_irq(16+20, &isa_cascade_irqaction); | ||
127 | } | ||
128 | |||
129 | |||
130 | /* | ||
131 | * The RX164 changed its interrupt routing between pass1 and pass2... | ||
132 | * | ||
133 | * PASS1: | ||
134 | * | ||
135 | * Slot IDSEL INTA INTB INTC INTD | ||
136 | * 0 6 5 10 15 20 | ||
137 | * 1 7 4 9 14 19 | ||
138 | * 2 5 3 8 13 18 | ||
139 | * 3 9 2 7 12 17 | ||
140 | * 4 10 1 6 11 16 | ||
141 | * | ||
142 | * PASS2: | ||
143 | * Slot IDSEL INTA INTB INTC INTD | ||
144 | * 0 5 1 7 12 17 | ||
145 | * 1 6 2 8 13 18 | ||
146 | * 2 8 3 9 14 19 | ||
147 | * 3 9 4 10 15 20 | ||
148 | * 4 10 5 11 16 6 | ||
149 | * | ||
150 | */ | ||
151 | |||
152 | /* | ||
153 | * IdSel | ||
154 | * 5 32 bit PCI option slot 0 | ||
155 | * 6 64 bit PCI option slot 1 | ||
156 | * 7 PCI-ISA bridge | ||
157 | * 7 64 bit PCI option slot 2 | ||
158 | * 9 32 bit PCI option slot 3 | ||
159 | * 10 PCI-PCI bridge | ||
160 | * | ||
161 | */ | ||
162 | |||
163 | static int __init | ||
164 | rx164_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
165 | { | ||
166 | #if 0 | ||
167 | static char irq_tab_pass1[6][5] __initdata = { | ||
168 | /*INT INTA INTB INTC INTD */ | ||
169 | { 16+3, 16+3, 16+8, 16+13, 16+18}, /* IdSel 5, slot 2 */ | ||
170 | { 16+5, 16+5, 16+10, 16+15, 16+20}, /* IdSel 6, slot 0 */ | ||
171 | { 16+4, 16+4, 16+9, 16+14, 16+19}, /* IdSel 7, slot 1 */ | ||
172 | { -1, -1, -1, -1, -1}, /* IdSel 8, PCI/ISA bridge */ | ||
173 | { 16+2, 16+2, 16+7, 16+12, 16+17}, /* IdSel 9, slot 3 */ | ||
174 | { 16+1, 16+1, 16+6, 16+11, 16+16}, /* IdSel 10, slot 4 */ | ||
175 | }; | ||
176 | #else | ||
177 | static char irq_tab[6][5] __initdata = { | ||
178 | /*INT INTA INTB INTC INTD */ | ||
179 | { 16+0, 16+0, 16+6, 16+11, 16+16}, /* IdSel 5, slot 0 */ | ||
180 | { 16+1, 16+1, 16+7, 16+12, 16+17}, /* IdSel 6, slot 1 */ | ||
181 | { -1, -1, -1, -1, -1}, /* IdSel 7, PCI/ISA bridge */ | ||
182 | { 16+2, 16+2, 16+8, 16+13, 16+18}, /* IdSel 8, slot 2 */ | ||
183 | { 16+3, 16+3, 16+9, 16+14, 16+19}, /* IdSel 9, slot 3 */ | ||
184 | { 16+4, 16+4, 16+10, 16+15, 16+5}, /* IdSel 10, PCI-PCI */ | ||
185 | }; | ||
186 | #endif | ||
187 | const long min_idsel = 5, max_idsel = 10, irqs_per_slot = 5; | ||
188 | |||
189 | /* JRP - Need to figure out how to distinguish pass1 from pass2, | ||
190 | and use the correct table. */ | ||
191 | return COMMON_TABLE_LOOKUP; | ||
192 | } | ||
193 | |||
194 | |||
195 | /* | ||
196 | * The System Vector | ||
197 | */ | ||
198 | |||
199 | struct alpha_machine_vector rx164_mv __initmv = { | ||
200 | .vector_name = "RX164", | ||
201 | DO_EV5_MMU, | ||
202 | DO_DEFAULT_RTC, | ||
203 | DO_POLARIS_IO, | ||
204 | .machine_check = polaris_machine_check, | ||
205 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
206 | .min_io_address = DEFAULT_IO_BASE, | ||
207 | .min_mem_address = DEFAULT_MEM_BASE, | ||
208 | |||
209 | .nr_irqs = 40, | ||
210 | .device_interrupt = rx164_device_interrupt, | ||
211 | |||
212 | .init_arch = polaris_init_arch, | ||
213 | .init_irq = rx164_init_irq, | ||
214 | .init_rtc = common_init_rtc, | ||
215 | .init_pci = common_init_pci, | ||
216 | .kill_arch = NULL, | ||
217 | .pci_map_irq = rx164_map_irq, | ||
218 | .pci_swizzle = common_swizzle, | ||
219 | }; | ||
220 | ALIAS_MV(rx164) | ||
diff --git a/arch/alpha/kernel/sys_sable.c b/arch/alpha/kernel/sys_sable.c new file mode 100644 index 000000000000..a7ff84474ace --- /dev/null +++ b/arch/alpha/kernel/sys_sable.c | |||
@@ -0,0 +1,653 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_sable.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the Sable, Sable-Gamma, and Lynx systems. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/init.h> | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/dma.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/mmu_context.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/core_t2.h> | ||
27 | #include <asm/tlbflush.h> | ||
28 | |||
29 | #include "proto.h" | ||
30 | #include "irq_impl.h" | ||
31 | #include "pci_impl.h" | ||
32 | #include "machvec_impl.h" | ||
33 | |||
34 | DEFINE_SPINLOCK(sable_lynx_irq_lock); | ||
35 | |||
36 | typedef struct irq_swizzle_struct | ||
37 | { | ||
38 | char irq_to_mask[64]; | ||
39 | char mask_to_irq[64]; | ||
40 | |||
41 | /* Note mask bit is true for DISABLED irqs. */ | ||
42 | unsigned long shadow_mask; | ||
43 | |||
44 | void (*update_irq_hw)(unsigned long bit, unsigned long mask); | ||
45 | void (*ack_irq_hw)(unsigned long bit); | ||
46 | |||
47 | } irq_swizzle_t; | ||
48 | |||
49 | static irq_swizzle_t *sable_lynx_irq_swizzle; | ||
50 | |||
51 | static void sable_lynx_init_irq(int nr_irqs); | ||
52 | |||
53 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SABLE) | ||
54 | |||
55 | /***********************************************************************/ | ||
56 | /* | ||
57 | * For SABLE, which is really baroque, we manage 40 IRQ's, but the | ||
58 | * hardware really only supports 24, not via normal ISA PIC, | ||
59 | * but cascaded custom 8259's, etc. | ||
60 | * 0-7 (char at 536) | ||
61 | * 8-15 (char at 53a) | ||
62 | * 16-23 (char at 53c) | ||
63 | * | ||
64 | * Summary Registers (536/53a/53c): | ||
65 | * | ||
66 | * Bit Meaning Kernel IRQ | ||
67 | *------------------------------------------ | ||
68 | * 0 PCI slot 0 34 | ||
69 | * 1 NCR810 (builtin) 33 | ||
70 | * 2 TULIP (builtin) 32 | ||
71 | * 3 mouse 12 | ||
72 | * 4 PCI slot 1 35 | ||
73 | * 5 PCI slot 2 36 | ||
74 | * 6 keyboard 1 | ||
75 | * 7 floppy 6 | ||
76 | * 8 COM2 3 | ||
77 | * 9 parallel port 7 | ||
78 | *10 EISA irq 3 - | ||
79 | *11 EISA irq 4 - | ||
80 | *12 EISA irq 5 5 | ||
81 | *13 EISA irq 6 - | ||
82 | *14 EISA irq 7 - | ||
83 | *15 COM1 4 | ||
84 | *16 EISA irq 9 9 | ||
85 | *17 EISA irq 10 10 | ||
86 | *18 EISA irq 11 11 | ||
87 | *19 EISA irq 12 - | ||
88 | *20 EISA irq 13 - | ||
89 | *21 EISA irq 14 14 | ||
90 | *22 NC 15 | ||
91 | *23 IIC - | ||
92 | */ | ||
93 | |||
94 | static void | ||
95 | sable_update_irq_hw(unsigned long bit, unsigned long mask) | ||
96 | { | ||
97 | int port = 0x537; | ||
98 | |||
99 | if (bit >= 16) { | ||
100 | port = 0x53d; | ||
101 | mask >>= 16; | ||
102 | } else if (bit >= 8) { | ||
103 | port = 0x53b; | ||
104 | mask >>= 8; | ||
105 | } | ||
106 | |||
107 | outb(mask, port); | ||
108 | } | ||
109 | |||
110 | static void | ||
111 | sable_ack_irq_hw(unsigned long bit) | ||
112 | { | ||
113 | int port, val1, val2; | ||
114 | |||
115 | if (bit >= 16) { | ||
116 | port = 0x53c; | ||
117 | val1 = 0xE0 | (bit - 16); | ||
118 | val2 = 0xE0 | 4; | ||
119 | } else if (bit >= 8) { | ||
120 | port = 0x53a; | ||
121 | val1 = 0xE0 | (bit - 8); | ||
122 | val2 = 0xE0 | 3; | ||
123 | } else { | ||
124 | port = 0x536; | ||
125 | val1 = 0xE0 | (bit - 0); | ||
126 | val2 = 0xE0 | 1; | ||
127 | } | ||
128 | |||
129 | outb(val1, port); /* ack the slave */ | ||
130 | outb(val2, 0x534); /* ack the master */ | ||
131 | } | ||
132 | |||
133 | static irq_swizzle_t sable_irq_swizzle = { | ||
134 | { | ||
135 | -1, 6, -1, 8, 15, 12, 7, 9, /* pseudo PIC 0-7 */ | ||
136 | -1, 16, 17, 18, 3, -1, 21, 22, /* pseudo PIC 8-15 */ | ||
137 | -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 0-7 */ | ||
138 | -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 8-15 */ | ||
139 | 2, 1, 0, 4, 5, -1, -1, -1, /* pseudo PCI */ | ||
140 | -1, -1, -1, -1, -1, -1, -1, -1, /* */ | ||
141 | -1, -1, -1, -1, -1, -1, -1, -1, /* */ | ||
142 | -1, -1, -1, -1, -1, -1, -1, -1 /* */ | ||
143 | }, | ||
144 | { | ||
145 | 34, 33, 32, 12, 35, 36, 1, 6, /* mask 0-7 */ | ||
146 | 3, 7, -1, -1, 5, -1, -1, 4, /* mask 8-15 */ | ||
147 | 9, 10, 11, -1, -1, 14, 15, -1, /* mask 16-23 */ | ||
148 | -1, -1, -1, -1, -1, -1, -1, -1, /* */ | ||
149 | -1, -1, -1, -1, -1, -1, -1, -1, /* */ | ||
150 | -1, -1, -1, -1, -1, -1, -1, -1, /* */ | ||
151 | -1, -1, -1, -1, -1, -1, -1, -1, /* */ | ||
152 | -1, -1, -1, -1, -1, -1, -1, -1 /* */ | ||
153 | }, | ||
154 | -1, | ||
155 | sable_update_irq_hw, | ||
156 | sable_ack_irq_hw | ||
157 | }; | ||
158 | |||
159 | static void __init | ||
160 | sable_init_irq(void) | ||
161 | { | ||
162 | outb(-1, 0x537); /* slave 0 */ | ||
163 | outb(-1, 0x53b); /* slave 1 */ | ||
164 | outb(-1, 0x53d); /* slave 2 */ | ||
165 | outb(0x44, 0x535); /* enable cascades in master */ | ||
166 | |||
167 | sable_lynx_irq_swizzle = &sable_irq_swizzle; | ||
168 | sable_lynx_init_irq(40); | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * PCI Fixup configuration for ALPHA SABLE (2100). | ||
173 | * | ||
174 | * The device to slot mapping looks like: | ||
175 | * | ||
176 | * Slot Device | ||
177 | * 0 TULIP | ||
178 | * 1 SCSI | ||
179 | * 2 PCI-EISA bridge | ||
180 | * 3 none | ||
181 | * 4 none | ||
182 | * 5 none | ||
183 | * 6 PCI on board slot 0 | ||
184 | * 7 PCI on board slot 1 | ||
185 | * 8 PCI on board slot 2 | ||
186 | * | ||
187 | * | ||
188 | * This two layered interrupt approach means that we allocate IRQ 16 and | ||
189 | * above for PCI interrupts. The IRQ relates to which bit the interrupt | ||
190 | * comes in on. This makes interrupt processing much easier. | ||
191 | */ | ||
192 | /* | ||
193 | * NOTE: the IRQ assignments below are arbitrary, but need to be consistent | ||
194 | * with the values in the irq swizzling tables above. | ||
195 | */ | ||
196 | |||
197 | static int __init | ||
198 | sable_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
199 | { | ||
200 | static char irq_tab[9][5] __initdata = { | ||
201 | /*INT INTA INTB INTC INTD */ | ||
202 | { 32+0, 32+0, 32+0, 32+0, 32+0}, /* IdSel 0, TULIP */ | ||
203 | { 32+1, 32+1, 32+1, 32+1, 32+1}, /* IdSel 1, SCSI */ | ||
204 | { -1, -1, -1, -1, -1}, /* IdSel 2, SIO */ | ||
205 | { -1, -1, -1, -1, -1}, /* IdSel 3, none */ | ||
206 | { -1, -1, -1, -1, -1}, /* IdSel 4, none */ | ||
207 | { -1, -1, -1, -1, -1}, /* IdSel 5, none */ | ||
208 | { 32+2, 32+2, 32+2, 32+2, 32+2}, /* IdSel 6, slot 0 */ | ||
209 | { 32+3, 32+3, 32+3, 32+3, 32+3}, /* IdSel 7, slot 1 */ | ||
210 | { 32+4, 32+4, 32+4, 32+4, 32+4} /* IdSel 8, slot 2 */ | ||
211 | }; | ||
212 | long min_idsel = 0, max_idsel = 8, irqs_per_slot = 5; | ||
213 | return COMMON_TABLE_LOOKUP; | ||
214 | } | ||
215 | #endif /* defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SABLE) */ | ||
216 | |||
217 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_LYNX) | ||
218 | |||
219 | /***********************************************************************/ | ||
220 | /* LYNX hardware specifics | ||
221 | */ | ||
222 | /* | ||
223 | * For LYNX, which is also baroque, we manage 64 IRQs, via a custom IC. | ||
224 | * | ||
225 | * Bit Meaning Kernel IRQ | ||
226 | *------------------------------------------ | ||
227 | * 0 | ||
228 | * 1 | ||
229 | * 2 | ||
230 | * 3 mouse 12 | ||
231 | * 4 | ||
232 | * 5 | ||
233 | * 6 keyboard 1 | ||
234 | * 7 floppy 6 | ||
235 | * 8 COM2 3 | ||
236 | * 9 parallel port 7 | ||
237 | *10 EISA irq 3 - | ||
238 | *11 EISA irq 4 - | ||
239 | *12 EISA irq 5 5 | ||
240 | *13 EISA irq 6 - | ||
241 | *14 EISA irq 7 - | ||
242 | *15 COM1 4 | ||
243 | *16 EISA irq 9 9 | ||
244 | *17 EISA irq 10 10 | ||
245 | *18 EISA irq 11 11 | ||
246 | *19 EISA irq 12 - | ||
247 | *20 | ||
248 | *21 EISA irq 14 14 | ||
249 | *22 EISA irq 15 15 | ||
250 | *23 IIC - | ||
251 | *24 VGA (builtin) - | ||
252 | *25 | ||
253 | *26 | ||
254 | *27 | ||
255 | *28 NCR810 (builtin) 28 | ||
256 | *29 | ||
257 | *30 | ||
258 | *31 | ||
259 | *32 PCI 0 slot 4 A primary bus 32 | ||
260 | *33 PCI 0 slot 4 B primary bus 33 | ||
261 | *34 PCI 0 slot 4 C primary bus 34 | ||
262 | *35 PCI 0 slot 4 D primary bus | ||
263 | *36 PCI 0 slot 5 A primary bus | ||
264 | *37 PCI 0 slot 5 B primary bus | ||
265 | *38 PCI 0 slot 5 C primary bus | ||
266 | *39 PCI 0 slot 5 D primary bus | ||
267 | *40 PCI 0 slot 6 A primary bus | ||
268 | *41 PCI 0 slot 6 B primary bus | ||
269 | *42 PCI 0 slot 6 C primary bus | ||
270 | *43 PCI 0 slot 6 D primary bus | ||
271 | *44 PCI 0 slot 7 A primary bus | ||
272 | *45 PCI 0 slot 7 B primary bus | ||
273 | *46 PCI 0 slot 7 C primary bus | ||
274 | *47 PCI 0 slot 7 D primary bus | ||
275 | *48 PCI 0 slot 0 A secondary bus | ||
276 | *49 PCI 0 slot 0 B secondary bus | ||
277 | *50 PCI 0 slot 0 C secondary bus | ||
278 | *51 PCI 0 slot 0 D secondary bus | ||
279 | *52 PCI 0 slot 1 A secondary bus | ||
280 | *53 PCI 0 slot 1 B secondary bus | ||
281 | *54 PCI 0 slot 1 C secondary bus | ||
282 | *55 PCI 0 slot 1 D secondary bus | ||
283 | *56 PCI 0 slot 2 A secondary bus | ||
284 | *57 PCI 0 slot 2 B secondary bus | ||
285 | *58 PCI 0 slot 2 C secondary bus | ||
286 | *59 PCI 0 slot 2 D secondary bus | ||
287 | *60 PCI 0 slot 3 A secondary bus | ||
288 | *61 PCI 0 slot 3 B secondary bus | ||
289 | *62 PCI 0 slot 3 C secondary bus | ||
290 | *63 PCI 0 slot 3 D secondary bus | ||
291 | */ | ||
292 | |||
293 | static void | ||
294 | lynx_update_irq_hw(unsigned long bit, unsigned long mask) | ||
295 | { | ||
296 | /* | ||
297 | * Write the AIR register on the T3/T4 with the | ||
298 | * address of the IC mask register (offset 0x40) | ||
299 | */ | ||
300 | *(vulp)T2_AIR = 0x40; | ||
301 | mb(); | ||
302 | *(vulp)T2_AIR; /* re-read to force write */ | ||
303 | mb(); | ||
304 | *(vulp)T2_DIR = mask; | ||
305 | mb(); | ||
306 | mb(); | ||
307 | } | ||
308 | |||
309 | static void | ||
310 | lynx_ack_irq_hw(unsigned long bit) | ||
311 | { | ||
312 | *(vulp)T2_VAR = (u_long) bit; | ||
313 | mb(); | ||
314 | mb(); | ||
315 | } | ||
316 | |||
317 | static irq_swizzle_t lynx_irq_swizzle = { | ||
318 | { /* irq_to_mask */ | ||
319 | -1, 6, -1, 8, 15, 12, 7, 9, /* pseudo PIC 0-7 */ | ||
320 | -1, 16, 17, 18, 3, -1, 21, 22, /* pseudo PIC 8-15 */ | ||
321 | -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo */ | ||
322 | -1, -1, -1, -1, 28, -1, -1, -1, /* pseudo */ | ||
323 | 32, 33, 34, 35, 36, 37, 38, 39, /* mask 32-39 */ | ||
324 | 40, 41, 42, 43, 44, 45, 46, 47, /* mask 40-47 */ | ||
325 | 48, 49, 50, 51, 52, 53, 54, 55, /* mask 48-55 */ | ||
326 | 56, 57, 58, 59, 60, 61, 62, 63 /* mask 56-63 */ | ||
327 | }, | ||
328 | { /* mask_to_irq */ | ||
329 | -1, -1, -1, 12, -1, -1, 1, 6, /* mask 0-7 */ | ||
330 | 3, 7, -1, -1, 5, -1, -1, 4, /* mask 8-15 */ | ||
331 | 9, 10, 11, -1, -1, 14, 15, -1, /* mask 16-23 */ | ||
332 | -1, -1, -1, -1, 28, -1, -1, -1, /* mask 24-31 */ | ||
333 | 32, 33, 34, 35, 36, 37, 38, 39, /* mask 32-39 */ | ||
334 | 40, 41, 42, 43, 44, 45, 46, 47, /* mask 40-47 */ | ||
335 | 48, 49, 50, 51, 52, 53, 54, 55, /* mask 48-55 */ | ||
336 | 56, 57, 58, 59, 60, 61, 62, 63 /* mask 56-63 */ | ||
337 | }, | ||
338 | -1, | ||
339 | lynx_update_irq_hw, | ||
340 | lynx_ack_irq_hw | ||
341 | }; | ||
342 | |||
343 | static void __init | ||
344 | lynx_init_irq(void) | ||
345 | { | ||
346 | sable_lynx_irq_swizzle = &lynx_irq_swizzle; | ||
347 | sable_lynx_init_irq(64); | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * PCI Fixup configuration for ALPHA LYNX (2100A) | ||
352 | * | ||
353 | * The device to slot mapping looks like: | ||
354 | * | ||
355 | * Slot Device | ||
356 | * 0 none | ||
357 | * 1 none | ||
358 | * 2 PCI-EISA bridge | ||
359 | * 3 PCI-PCI bridge | ||
360 | * 4 NCR 810 (Demi-Lynx only) | ||
361 | * 5 none | ||
362 | * 6 PCI on board slot 4 | ||
363 | * 7 PCI on board slot 5 | ||
364 | * 8 PCI on board slot 6 | ||
365 | * 9 PCI on board slot 7 | ||
366 | * | ||
367 | * And behind the PPB we have: | ||
368 | * | ||
369 | * 11 PCI on board slot 0 | ||
370 | * 12 PCI on board slot 1 | ||
371 | * 13 PCI on board slot 2 | ||
372 | * 14 PCI on board slot 3 | ||
373 | */ | ||
374 | /* | ||
375 | * NOTE: the IRQ assignments below are arbitrary, but need to be consistent | ||
376 | * with the values in the irq swizzling tables above. | ||
377 | */ | ||
378 | |||
379 | static int __init | ||
380 | lynx_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
381 | { | ||
382 | static char irq_tab[19][5] __initdata = { | ||
383 | /*INT INTA INTB INTC INTD */ | ||
384 | { -1, -1, -1, -1, -1}, /* IdSel 13, PCEB */ | ||
385 | { -1, -1, -1, -1, -1}, /* IdSel 14, PPB */ | ||
386 | { 28, 28, 28, 28, 28}, /* IdSel 15, NCR demi */ | ||
387 | { -1, -1, -1, -1, -1}, /* IdSel 16, none */ | ||
388 | { 32, 32, 33, 34, 35}, /* IdSel 17, slot 4 */ | ||
389 | { 36, 36, 37, 38, 39}, /* IdSel 18, slot 5 */ | ||
390 | { 40, 40, 41, 42, 43}, /* IdSel 19, slot 6 */ | ||
391 | { 44, 44, 45, 46, 47}, /* IdSel 20, slot 7 */ | ||
392 | { -1, -1, -1, -1, -1}, /* IdSel 22, none */ | ||
393 | /* The following are actually behind the PPB. */ | ||
394 | { -1, -1, -1, -1, -1}, /* IdSel 16 none */ | ||
395 | { 28, 28, 28, 28, 28}, /* IdSel 17 NCR lynx */ | ||
396 | { -1, -1, -1, -1, -1}, /* IdSel 18 none */ | ||
397 | { -1, -1, -1, -1, -1}, /* IdSel 19 none */ | ||
398 | { -1, -1, -1, -1, -1}, /* IdSel 20 none */ | ||
399 | { -1, -1, -1, -1, -1}, /* IdSel 21 none */ | ||
400 | { 48, 48, 49, 50, 51}, /* IdSel 22 slot 0 */ | ||
401 | { 52, 52, 53, 54, 55}, /* IdSel 23 slot 1 */ | ||
402 | { 56, 56, 57, 58, 59}, /* IdSel 24 slot 2 */ | ||
403 | { 60, 60, 61, 62, 63} /* IdSel 25 slot 3 */ | ||
404 | }; | ||
405 | const long min_idsel = 2, max_idsel = 20, irqs_per_slot = 5; | ||
406 | return COMMON_TABLE_LOOKUP; | ||
407 | } | ||
408 | |||
409 | static u8 __init | ||
410 | lynx_swizzle(struct pci_dev *dev, u8 *pinp) | ||
411 | { | ||
412 | int slot, pin = *pinp; | ||
413 | |||
414 | if (dev->bus->number == 0) { | ||
415 | slot = PCI_SLOT(dev->devfn); | ||
416 | } | ||
417 | /* Check for the built-in bridge */ | ||
418 | else if (PCI_SLOT(dev->bus->self->devfn) == 3) { | ||
419 | slot = PCI_SLOT(dev->devfn) + 11; | ||
420 | } | ||
421 | else | ||
422 | { | ||
423 | /* Must be a card-based bridge. */ | ||
424 | do { | ||
425 | if (PCI_SLOT(dev->bus->self->devfn) == 3) { | ||
426 | slot = PCI_SLOT(dev->devfn) + 11; | ||
427 | break; | ||
428 | } | ||
429 | pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; | ||
430 | |||
431 | /* Move up the chain of bridges. */ | ||
432 | dev = dev->bus->self; | ||
433 | /* Slot of the next bridge. */ | ||
434 | slot = PCI_SLOT(dev->devfn); | ||
435 | } while (dev->bus->self); | ||
436 | } | ||
437 | *pinp = pin; | ||
438 | return slot; | ||
439 | } | ||
440 | |||
441 | #endif /* defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_LYNX) */ | ||
442 | |||
443 | /***********************************************************************/ | ||
444 | /* GENERIC irq routines */ | ||
445 | |||
446 | static inline void | ||
447 | sable_lynx_enable_irq(unsigned int irq) | ||
448 | { | ||
449 | unsigned long bit, mask; | ||
450 | |||
451 | bit = sable_lynx_irq_swizzle->irq_to_mask[irq]; | ||
452 | spin_lock(&sable_lynx_irq_lock); | ||
453 | mask = sable_lynx_irq_swizzle->shadow_mask &= ~(1UL << bit); | ||
454 | sable_lynx_irq_swizzle->update_irq_hw(bit, mask); | ||
455 | spin_unlock(&sable_lynx_irq_lock); | ||
456 | #if 0 | ||
457 | printk("%s: mask 0x%lx bit 0x%x irq 0x%x\n", | ||
458 | __FUNCTION__, mask, bit, irq); | ||
459 | #endif | ||
460 | } | ||
461 | |||
462 | static void | ||
463 | sable_lynx_disable_irq(unsigned int irq) | ||
464 | { | ||
465 | unsigned long bit, mask; | ||
466 | |||
467 | bit = sable_lynx_irq_swizzle->irq_to_mask[irq]; | ||
468 | spin_lock(&sable_lynx_irq_lock); | ||
469 | mask = sable_lynx_irq_swizzle->shadow_mask |= 1UL << bit; | ||
470 | sable_lynx_irq_swizzle->update_irq_hw(bit, mask); | ||
471 | spin_unlock(&sable_lynx_irq_lock); | ||
472 | #if 0 | ||
473 | printk("%s: mask 0x%lx bit 0x%x irq 0x%x\n", | ||
474 | __FUNCTION__, mask, bit, irq); | ||
475 | #endif | ||
476 | } | ||
477 | |||
478 | static unsigned int | ||
479 | sable_lynx_startup_irq(unsigned int irq) | ||
480 | { | ||
481 | sable_lynx_enable_irq(irq); | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static void | ||
486 | sable_lynx_end_irq(unsigned int irq) | ||
487 | { | ||
488 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
489 | sable_lynx_enable_irq(irq); | ||
490 | } | ||
491 | |||
492 | static void | ||
493 | sable_lynx_mask_and_ack_irq(unsigned int irq) | ||
494 | { | ||
495 | unsigned long bit, mask; | ||
496 | |||
497 | bit = sable_lynx_irq_swizzle->irq_to_mask[irq]; | ||
498 | spin_lock(&sable_lynx_irq_lock); | ||
499 | mask = sable_lynx_irq_swizzle->shadow_mask |= 1UL << bit; | ||
500 | sable_lynx_irq_swizzle->update_irq_hw(bit, mask); | ||
501 | sable_lynx_irq_swizzle->ack_irq_hw(bit); | ||
502 | spin_unlock(&sable_lynx_irq_lock); | ||
503 | } | ||
504 | |||
505 | static struct hw_interrupt_type sable_lynx_irq_type = { | ||
506 | .typename = "SABLE/LYNX", | ||
507 | .startup = sable_lynx_startup_irq, | ||
508 | .shutdown = sable_lynx_disable_irq, | ||
509 | .enable = sable_lynx_enable_irq, | ||
510 | .disable = sable_lynx_disable_irq, | ||
511 | .ack = sable_lynx_mask_and_ack_irq, | ||
512 | .end = sable_lynx_end_irq, | ||
513 | }; | ||
514 | |||
515 | static void | ||
516 | sable_lynx_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
517 | { | ||
518 | /* Note that the vector reported by the SRM PALcode corresponds | ||
519 | to the interrupt mask bits, but we have to manage via the | ||
520 | so-called legacy IRQs for many common devices. */ | ||
521 | |||
522 | int bit, irq; | ||
523 | |||
524 | bit = (vector - 0x800) >> 4; | ||
525 | irq = sable_lynx_irq_swizzle->mask_to_irq[bit]; | ||
526 | #if 0 | ||
527 | printk("%s: vector 0x%lx bit 0x%x irq 0x%x\n", | ||
528 | __FUNCTION__, vector, bit, irq); | ||
529 | #endif | ||
530 | handle_irq(irq, regs); | ||
531 | } | ||
532 | |||
533 | static void __init | ||
534 | sable_lynx_init_irq(int nr_irqs) | ||
535 | { | ||
536 | long i; | ||
537 | |||
538 | for (i = 0; i < nr_irqs; ++i) { | ||
539 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
540 | irq_desc[i].handler = &sable_lynx_irq_type; | ||
541 | } | ||
542 | |||
543 | common_init_isa_dma(); | ||
544 | } | ||
545 | |||
546 | static void __init | ||
547 | sable_lynx_init_pci(void) | ||
548 | { | ||
549 | common_init_pci(); | ||
550 | } | ||
551 | |||
552 | /*****************************************************************/ | ||
553 | /* | ||
554 | * The System Vectors | ||
555 | * | ||
556 | * In order that T2_HAE_ADDRESS should be a constant, we play | ||
557 | * these games with GAMMA_BIAS. | ||
558 | */ | ||
559 | |||
560 | #if defined(CONFIG_ALPHA_GENERIC) || \ | ||
561 | (defined(CONFIG_ALPHA_SABLE) && !defined(CONFIG_ALPHA_GAMMA)) | ||
562 | #undef GAMMA_BIAS | ||
563 | #define GAMMA_BIAS 0 | ||
564 | struct alpha_machine_vector sable_mv __initmv = { | ||
565 | .vector_name = "Sable", | ||
566 | DO_EV4_MMU, | ||
567 | DO_DEFAULT_RTC, | ||
568 | DO_T2_IO, | ||
569 | .machine_check = t2_machine_check, | ||
570 | .max_isa_dma_address = ALPHA_SABLE_MAX_ISA_DMA_ADDRESS, | ||
571 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
572 | .min_mem_address = T2_DEFAULT_MEM_BASE, | ||
573 | |||
574 | .nr_irqs = 40, | ||
575 | .device_interrupt = sable_lynx_srm_device_interrupt, | ||
576 | |||
577 | .init_arch = t2_init_arch, | ||
578 | .init_irq = sable_init_irq, | ||
579 | .init_rtc = common_init_rtc, | ||
580 | .init_pci = sable_lynx_init_pci, | ||
581 | .kill_arch = t2_kill_arch, | ||
582 | .pci_map_irq = sable_map_irq, | ||
583 | .pci_swizzle = common_swizzle, | ||
584 | |||
585 | .sys = { .t2 = { | ||
586 | .gamma_bias = 0 | ||
587 | } } | ||
588 | }; | ||
589 | ALIAS_MV(sable) | ||
590 | #endif /* GENERIC || (SABLE && !GAMMA) */ | ||
591 | |||
592 | #if defined(CONFIG_ALPHA_GENERIC) || \ | ||
593 | (defined(CONFIG_ALPHA_SABLE) && defined(CONFIG_ALPHA_GAMMA)) | ||
594 | #undef GAMMA_BIAS | ||
595 | #define GAMMA_BIAS _GAMMA_BIAS | ||
596 | struct alpha_machine_vector sable_gamma_mv __initmv = { | ||
597 | .vector_name = "Sable-Gamma", | ||
598 | DO_EV5_MMU, | ||
599 | DO_DEFAULT_RTC, | ||
600 | DO_T2_IO, | ||
601 | .machine_check = t2_machine_check, | ||
602 | .max_isa_dma_address = ALPHA_SABLE_MAX_ISA_DMA_ADDRESS, | ||
603 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
604 | .min_mem_address = T2_DEFAULT_MEM_BASE, | ||
605 | |||
606 | .nr_irqs = 40, | ||
607 | .device_interrupt = sable_lynx_srm_device_interrupt, | ||
608 | |||
609 | .init_arch = t2_init_arch, | ||
610 | .init_irq = sable_init_irq, | ||
611 | .init_rtc = common_init_rtc, | ||
612 | .init_pci = sable_lynx_init_pci, | ||
613 | .kill_arch = t2_kill_arch, | ||
614 | .pci_map_irq = sable_map_irq, | ||
615 | .pci_swizzle = common_swizzle, | ||
616 | |||
617 | .sys = { .t2 = { | ||
618 | .gamma_bias = _GAMMA_BIAS | ||
619 | } } | ||
620 | }; | ||
621 | ALIAS_MV(sable_gamma) | ||
622 | #endif /* GENERIC || (SABLE && GAMMA) */ | ||
623 | |||
624 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_LYNX) | ||
625 | #undef GAMMA_BIAS | ||
626 | #define GAMMA_BIAS _GAMMA_BIAS | ||
627 | struct alpha_machine_vector lynx_mv __initmv = { | ||
628 | .vector_name = "Lynx", | ||
629 | DO_EV4_MMU, | ||
630 | DO_DEFAULT_RTC, | ||
631 | DO_T2_IO, | ||
632 | .machine_check = t2_machine_check, | ||
633 | .max_isa_dma_address = ALPHA_SABLE_MAX_ISA_DMA_ADDRESS, | ||
634 | .min_io_address = EISA_DEFAULT_IO_BASE, | ||
635 | .min_mem_address = T2_DEFAULT_MEM_BASE, | ||
636 | |||
637 | .nr_irqs = 64, | ||
638 | .device_interrupt = sable_lynx_srm_device_interrupt, | ||
639 | |||
640 | .init_arch = t2_init_arch, | ||
641 | .init_irq = lynx_init_irq, | ||
642 | .init_rtc = common_init_rtc, | ||
643 | .init_pci = sable_lynx_init_pci, | ||
644 | .kill_arch = t2_kill_arch, | ||
645 | .pci_map_irq = lynx_map_irq, | ||
646 | .pci_swizzle = lynx_swizzle, | ||
647 | |||
648 | .sys = { .t2 = { | ||
649 | .gamma_bias = _GAMMA_BIAS | ||
650 | } } | ||
651 | }; | ||
652 | ALIAS_MV(lynx) | ||
653 | #endif /* GENERIC || LYNX */ | ||
diff --git a/arch/alpha/kernel/sys_sio.c b/arch/alpha/kernel/sys_sio.c new file mode 100644 index 000000000000..47df48a6ddb7 --- /dev/null +++ b/arch/alpha/kernel/sys_sio.c | |||
@@ -0,0 +1,438 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_sio.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code for all boards that route the PCI interrupts through the SIO | ||
9 | * PCI/ISA bridge. This includes Noname (AXPpci33), Multia (UDB), | ||
10 | * Kenetics's Platform 2000, Avanti (AlphaStation), XL, and AlphaBook1. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/tty.h> | ||
21 | |||
22 | #include <asm/compiler.h> | ||
23 | #include <asm/ptrace.h> | ||
24 | #include <asm/system.h> | ||
25 | #include <asm/dma.h> | ||
26 | #include <asm/irq.h> | ||
27 | #include <asm/mmu_context.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/pgtable.h> | ||
30 | #include <asm/core_apecs.h> | ||
31 | #include <asm/core_lca.h> | ||
32 | #include <asm/tlbflush.h> | ||
33 | |||
34 | #include "proto.h" | ||
35 | #include "irq_impl.h" | ||
36 | #include "pci_impl.h" | ||
37 | #include "machvec_impl.h" | ||
38 | |||
39 | #if defined(ALPHA_RESTORE_SRM_SETUP) | ||
40 | /* Save LCA configuration data as the console had it set up. */ | ||
41 | struct | ||
42 | { | ||
43 | unsigned int orig_route_tab; /* for SAVE/RESTORE */ | ||
44 | } saved_config __attribute((common)); | ||
45 | #endif | ||
46 | |||
47 | |||
48 | static void __init | ||
49 | sio_init_irq(void) | ||
50 | { | ||
51 | if (alpha_using_srm) | ||
52 | alpha_mv.device_interrupt = srm_device_interrupt; | ||
53 | |||
54 | init_i8259a_irqs(); | ||
55 | common_init_isa_dma(); | ||
56 | } | ||
57 | |||
58 | static inline void __init | ||
59 | alphabook1_init_arch(void) | ||
60 | { | ||
61 | /* The AlphaBook1 has LCD video fixed at 800x600, | ||
62 | 37 rows and 100 cols. */ | ||
63 | screen_info.orig_y = 37; | ||
64 | screen_info.orig_video_cols = 100; | ||
65 | screen_info.orig_video_lines = 37; | ||
66 | |||
67 | lca_init_arch(); | ||
68 | } | ||
69 | |||
70 | |||
71 | /* | ||
72 | * sio_route_tab selects irq routing in PCI/ISA bridge so that: | ||
73 | * PIRQ0 -> irq 15 | ||
74 | * PIRQ1 -> irq 9 | ||
75 | * PIRQ2 -> irq 10 | ||
76 | * PIRQ3 -> irq 11 | ||
77 | * | ||
78 | * This probably ought to be configurable via MILO. For | ||
79 | * example, sound boards seem to like using IRQ 9. | ||
80 | * | ||
81 | * This is NOT how we should do it. PIRQ0-X should have | ||
82 | * their own IRQ's, the way intel uses the IO-APIC irq's. | ||
83 | */ | ||
84 | |||
85 | static void __init | ||
86 | sio_pci_route(void) | ||
87 | { | ||
88 | #if defined(ALPHA_RESTORE_SRM_SETUP) | ||
89 | /* First, read and save the original setting. */ | ||
90 | pci_bus_read_config_dword(pci_isa_hose->bus, PCI_DEVFN(7, 0), 0x60, | ||
91 | &saved_config.orig_route_tab); | ||
92 | printk("%s: PIRQ original 0x%x new 0x%x\n", __FUNCTION__, | ||
93 | saved_config.orig_route_tab, alpha_mv.sys.sio.route_tab); | ||
94 | #endif | ||
95 | |||
96 | /* Now override with desired setting. */ | ||
97 | pci_bus_write_config_dword(pci_isa_hose->bus, PCI_DEVFN(7, 0), 0x60, | ||
98 | alpha_mv.sys.sio.route_tab); | ||
99 | } | ||
100 | |||
101 | static unsigned int __init | ||
102 | sio_collect_irq_levels(void) | ||
103 | { | ||
104 | unsigned int level_bits = 0; | ||
105 | struct pci_dev *dev = NULL; | ||
106 | |||
107 | /* Iterate through the devices, collecting IRQ levels. */ | ||
108 | while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | ||
109 | if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) && | ||
110 | (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA)) | ||
111 | continue; | ||
112 | |||
113 | if (dev->irq) | ||
114 | level_bits |= (1 << dev->irq); | ||
115 | } | ||
116 | return level_bits; | ||
117 | } | ||
118 | |||
119 | static void __init | ||
120 | sio_fixup_irq_levels(unsigned int level_bits) | ||
121 | { | ||
122 | unsigned int old_level_bits; | ||
123 | |||
124 | /* | ||
125 | * Now, make all PCI interrupts level sensitive. Notice: | ||
126 | * these registers must be accessed byte-wise. inw()/outw() | ||
127 | * don't work. | ||
128 | * | ||
129 | * Make sure to turn off any level bits set for IRQs 9,10,11,15, | ||
130 | * so that the only bits getting set are for devices actually found. | ||
131 | * Note that we do preserve the remainder of the bits, which we hope | ||
132 | * will be set correctly by ARC/SRM. | ||
133 | * | ||
134 | * Note: we at least preserve any level-set bits on AlphaBook1 | ||
135 | */ | ||
136 | old_level_bits = inb(0x4d0) | (inb(0x4d1) << 8); | ||
137 | |||
138 | level_bits |= (old_level_bits & 0x71ff); | ||
139 | |||
140 | outb((level_bits >> 0) & 0xff, 0x4d0); | ||
141 | outb((level_bits >> 8) & 0xff, 0x4d1); | ||
142 | } | ||
143 | |||
144 | static inline int __init | ||
145 | noname_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
146 | { | ||
147 | /* | ||
148 | * The Noname board has 5 PCI slots with each of the 4 | ||
149 | * interrupt pins routed to different pins on the PCI/ISA | ||
150 | * bridge (PIRQ0-PIRQ3). The table below is based on | ||
151 | * information available at: | ||
152 | * | ||
153 | * http://ftp.digital.com/pub/DEC/axppci/ref_interrupts.txt | ||
154 | * | ||
155 | * I have no information on the Avanti interrupt routing, but | ||
156 | * the routing seems to be identical to the Noname except | ||
157 | * that the Avanti has an additional slot whose routing I'm | ||
158 | * unsure of. | ||
159 | * | ||
160 | * pirq_tab[0] is a fake entry to deal with old PCI boards | ||
161 | * that have the interrupt pin number hardwired to 0 (meaning | ||
162 | * that they use the default INTA line, if they are interrupt | ||
163 | * driven at all). | ||
164 | */ | ||
165 | static char irq_tab[][5] __initdata = { | ||
166 | /*INT A B C D */ | ||
167 | { 3, 3, 3, 3, 3}, /* idsel 6 (53c810) */ | ||
168 | {-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */ | ||
169 | { 2, 2, -1, -1, -1}, /* idsel 8 (Hack: slot closest ISA) */ | ||
170 | {-1, -1, -1, -1, -1}, /* idsel 9 (unused) */ | ||
171 | {-1, -1, -1, -1, -1}, /* idsel 10 (unused) */ | ||
172 | { 0, 0, 2, 1, 0}, /* idsel 11 KN25_PCI_SLOT0 */ | ||
173 | { 1, 1, 0, 2, 1}, /* idsel 12 KN25_PCI_SLOT1 */ | ||
174 | { 2, 2, 1, 0, 2}, /* idsel 13 KN25_PCI_SLOT2 */ | ||
175 | { 0, 0, 0, 0, 0}, /* idsel 14 AS255 TULIP */ | ||
176 | }; | ||
177 | const long min_idsel = 6, max_idsel = 14, irqs_per_slot = 5; | ||
178 | int irq = COMMON_TABLE_LOOKUP, tmp; | ||
179 | tmp = __kernel_extbl(alpha_mv.sys.sio.route_tab, irq); | ||
180 | return irq >= 0 ? tmp : -1; | ||
181 | } | ||
182 | |||
183 | static inline int __init | ||
184 | p2k_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
185 | { | ||
186 | static char irq_tab[][5] __initdata = { | ||
187 | /*INT A B C D */ | ||
188 | { 0, 0, -1, -1, -1}, /* idsel 6 (53c810) */ | ||
189 | {-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */ | ||
190 | { 1, 1, 2, 3, 0}, /* idsel 8 (slot A) */ | ||
191 | { 2, 2, 3, 0, 1}, /* idsel 9 (slot B) */ | ||
192 | {-1, -1, -1, -1, -1}, /* idsel 10 (unused) */ | ||
193 | {-1, -1, -1, -1, -1}, /* idsel 11 (unused) */ | ||
194 | { 3, 3, -1, -1, -1}, /* idsel 12 (CMD0646) */ | ||
195 | }; | ||
196 | const long min_idsel = 6, max_idsel = 12, irqs_per_slot = 5; | ||
197 | int irq = COMMON_TABLE_LOOKUP, tmp; | ||
198 | tmp = __kernel_extbl(alpha_mv.sys.sio.route_tab, irq); | ||
199 | return irq >= 0 ? tmp : -1; | ||
200 | } | ||
201 | |||
202 | static inline void __init | ||
203 | noname_init_pci(void) | ||
204 | { | ||
205 | common_init_pci(); | ||
206 | sio_pci_route(); | ||
207 | sio_fixup_irq_levels(sio_collect_irq_levels()); | ||
208 | ns87312_enable_ide(0x26e); | ||
209 | } | ||
210 | |||
211 | static inline void __init | ||
212 | alphabook1_init_pci(void) | ||
213 | { | ||
214 | struct pci_dev *dev; | ||
215 | unsigned char orig, config; | ||
216 | |||
217 | common_init_pci(); | ||
218 | sio_pci_route(); | ||
219 | |||
220 | /* | ||
221 | * On the AlphaBook1, the PCMCIA chip (Cirrus 6729) | ||
222 | * is sensitive to PCI bus bursts, so we must DISABLE | ||
223 | * burst mode for the NCR 8xx SCSI... :-( | ||
224 | * | ||
225 | * Note that the NCR810 SCSI driver must preserve the | ||
226 | * setting of the bit in order for this to work. At the | ||
227 | * moment (2.0.29), ncr53c8xx.c does NOT do this, but | ||
228 | * 53c7,8xx.c DOES. | ||
229 | */ | ||
230 | |||
231 | dev = NULL; | ||
232 | while ((dev = pci_find_device(PCI_VENDOR_ID_NCR, PCI_ANY_ID, dev))) { | ||
233 | if (dev->device == PCI_DEVICE_ID_NCR_53C810 | ||
234 | || dev->device == PCI_DEVICE_ID_NCR_53C815 | ||
235 | || dev->device == PCI_DEVICE_ID_NCR_53C820 | ||
236 | || dev->device == PCI_DEVICE_ID_NCR_53C825) { | ||
237 | unsigned long io_port; | ||
238 | unsigned char ctest4; | ||
239 | |||
240 | io_port = dev->resource[0].start; | ||
241 | ctest4 = inb(io_port+0x21); | ||
242 | if (!(ctest4 & 0x80)) { | ||
243 | printk("AlphaBook1 NCR init: setting" | ||
244 | " burst disable\n"); | ||
245 | outb(ctest4 | 0x80, io_port+0x21); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | /* Do not set *ANY* level triggers for AlphaBook1. */ | ||
251 | sio_fixup_irq_levels(0); | ||
252 | |||
253 | /* Make sure that register PR1 indicates 1Mb mem */ | ||
254 | outb(0x0f, 0x3ce); orig = inb(0x3cf); /* read PR5 */ | ||
255 | outb(0x0f, 0x3ce); outb(0x05, 0x3cf); /* unlock PR0-4 */ | ||
256 | outb(0x0b, 0x3ce); config = inb(0x3cf); /* read PR1 */ | ||
257 | if ((config & 0xc0) != 0xc0) { | ||
258 | printk("AlphaBook1 VGA init: setting 1Mb memory\n"); | ||
259 | config |= 0xc0; | ||
260 | outb(0x0b, 0x3ce); outb(config, 0x3cf); /* write PR1 */ | ||
261 | } | ||
262 | outb(0x0f, 0x3ce); outb(orig, 0x3cf); /* (re)lock PR0-4 */ | ||
263 | } | ||
264 | |||
265 | void | ||
266 | sio_kill_arch(int mode) | ||
267 | { | ||
268 | #if defined(ALPHA_RESTORE_SRM_SETUP) | ||
269 | /* Since we cannot read the PCI DMA Window CSRs, we | ||
270 | * cannot restore them here. | ||
271 | * | ||
272 | * However, we CAN read the PIRQ route register, so restore it | ||
273 | * now... | ||
274 | */ | ||
275 | pci_bus_write_config_dword(pci_isa_hose->bus, PCI_DEVFN(7, 0), 0x60, | ||
276 | saved_config.orig_route_tab); | ||
277 | #endif | ||
278 | } | ||
279 | |||
280 | |||
281 | /* | ||
282 | * The System Vectors | ||
283 | */ | ||
284 | |||
285 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_BOOK1) | ||
286 | struct alpha_machine_vector alphabook1_mv __initmv = { | ||
287 | .vector_name = "AlphaBook1", | ||
288 | DO_EV4_MMU, | ||
289 | DO_DEFAULT_RTC, | ||
290 | DO_LCA_IO, | ||
291 | .machine_check = lca_machine_check, | ||
292 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
293 | .min_io_address = DEFAULT_IO_BASE, | ||
294 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
295 | |||
296 | .nr_irqs = 16, | ||
297 | .device_interrupt = isa_device_interrupt, | ||
298 | |||
299 | .init_arch = alphabook1_init_arch, | ||
300 | .init_irq = sio_init_irq, | ||
301 | .init_rtc = common_init_rtc, | ||
302 | .init_pci = alphabook1_init_pci, | ||
303 | .kill_arch = sio_kill_arch, | ||
304 | .pci_map_irq = noname_map_irq, | ||
305 | .pci_swizzle = common_swizzle, | ||
306 | |||
307 | .sys = { .sio = { | ||
308 | /* NCR810 SCSI is 14, PCMCIA controller is 15. */ | ||
309 | .route_tab = 0x0e0f0a0a, | ||
310 | }} | ||
311 | }; | ||
312 | ALIAS_MV(alphabook1) | ||
313 | #endif | ||
314 | |||
315 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_AVANTI) | ||
316 | struct alpha_machine_vector avanti_mv __initmv = { | ||
317 | .vector_name = "Avanti", | ||
318 | DO_EV4_MMU, | ||
319 | DO_DEFAULT_RTC, | ||
320 | DO_APECS_IO, | ||
321 | .machine_check = apecs_machine_check, | ||
322 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
323 | .min_io_address = DEFAULT_IO_BASE, | ||
324 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
325 | |||
326 | .nr_irqs = 16, | ||
327 | .device_interrupt = isa_device_interrupt, | ||
328 | |||
329 | .init_arch = apecs_init_arch, | ||
330 | .init_irq = sio_init_irq, | ||
331 | .init_rtc = common_init_rtc, | ||
332 | .init_pci = noname_init_pci, | ||
333 | .kill_arch = sio_kill_arch, | ||
334 | .pci_map_irq = noname_map_irq, | ||
335 | .pci_swizzle = common_swizzle, | ||
336 | |||
337 | .sys = { .sio = { | ||
338 | .route_tab = 0x0b0a0e0f, | ||
339 | }} | ||
340 | }; | ||
341 | ALIAS_MV(avanti) | ||
342 | #endif | ||
343 | |||
344 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_NONAME) | ||
345 | struct alpha_machine_vector noname_mv __initmv = { | ||
346 | .vector_name = "Noname", | ||
347 | DO_EV4_MMU, | ||
348 | DO_DEFAULT_RTC, | ||
349 | DO_LCA_IO, | ||
350 | .machine_check = lca_machine_check, | ||
351 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
352 | .min_io_address = DEFAULT_IO_BASE, | ||
353 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
354 | |||
355 | .nr_irqs = 16, | ||
356 | .device_interrupt = srm_device_interrupt, | ||
357 | |||
358 | .init_arch = lca_init_arch, | ||
359 | .init_irq = sio_init_irq, | ||
360 | .init_rtc = common_init_rtc, | ||
361 | .init_pci = noname_init_pci, | ||
362 | .kill_arch = sio_kill_arch, | ||
363 | .pci_map_irq = noname_map_irq, | ||
364 | .pci_swizzle = common_swizzle, | ||
365 | |||
366 | .sys = { .sio = { | ||
367 | /* For UDB, the only available PCI slot must not map to IRQ 9, | ||
368 | since that's the builtin MSS sound chip. That PCI slot | ||
369 | will map to PIRQ1 (for INTA at least), so we give it IRQ 15 | ||
370 | instead. | ||
371 | |||
372 | Unfortunately we have to do this for NONAME as well, since | ||
373 | they are co-indicated when the platform type "Noname" is | ||
374 | selected... :-( */ | ||
375 | |||
376 | .route_tab = 0x0b0a0f0d, | ||
377 | }} | ||
378 | }; | ||
379 | ALIAS_MV(noname) | ||
380 | #endif | ||
381 | |||
382 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_P2K) | ||
383 | struct alpha_machine_vector p2k_mv __initmv = { | ||
384 | .vector_name = "Platform2000", | ||
385 | DO_EV4_MMU, | ||
386 | DO_DEFAULT_RTC, | ||
387 | DO_LCA_IO, | ||
388 | .machine_check = lca_machine_check, | ||
389 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
390 | .min_io_address = DEFAULT_IO_BASE, | ||
391 | .min_mem_address = APECS_AND_LCA_DEFAULT_MEM_BASE, | ||
392 | |||
393 | .nr_irqs = 16, | ||
394 | .device_interrupt = srm_device_interrupt, | ||
395 | |||
396 | .init_arch = lca_init_arch, | ||
397 | .init_irq = sio_init_irq, | ||
398 | .init_rtc = common_init_rtc, | ||
399 | .init_pci = noname_init_pci, | ||
400 | .kill_arch = sio_kill_arch, | ||
401 | .pci_map_irq = p2k_map_irq, | ||
402 | .pci_swizzle = common_swizzle, | ||
403 | |||
404 | .sys = { .sio = { | ||
405 | .route_tab = 0x0b0a090f, | ||
406 | }} | ||
407 | }; | ||
408 | ALIAS_MV(p2k) | ||
409 | #endif | ||
410 | |||
411 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_XL) | ||
412 | struct alpha_machine_vector xl_mv __initmv = { | ||
413 | .vector_name = "XL", | ||
414 | DO_EV4_MMU, | ||
415 | DO_DEFAULT_RTC, | ||
416 | DO_APECS_IO, | ||
417 | .machine_check = apecs_machine_check, | ||
418 | .max_isa_dma_address = ALPHA_XL_MAX_ISA_DMA_ADDRESS, | ||
419 | .min_io_address = DEFAULT_IO_BASE, | ||
420 | .min_mem_address = XL_DEFAULT_MEM_BASE, | ||
421 | |||
422 | .nr_irqs = 16, | ||
423 | .device_interrupt = isa_device_interrupt, | ||
424 | |||
425 | .init_arch = apecs_init_arch, | ||
426 | .init_irq = sio_init_irq, | ||
427 | .init_rtc = common_init_rtc, | ||
428 | .init_pci = noname_init_pci, | ||
429 | .kill_arch = sio_kill_arch, | ||
430 | .pci_map_irq = noname_map_irq, | ||
431 | .pci_swizzle = common_swizzle, | ||
432 | |||
433 | .sys = { .sio = { | ||
434 | .route_tab = 0x0b0a090f, | ||
435 | }} | ||
436 | }; | ||
437 | ALIAS_MV(xl) | ||
438 | #endif | ||
diff --git a/arch/alpha/kernel/sys_sx164.c b/arch/alpha/kernel/sys_sx164.c new file mode 100644 index 000000000000..94ad68b7c0ae --- /dev/null +++ b/arch/alpha/kernel/sys_sx164.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_sx164.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999, 2000 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the SX164 (PCA56+PYXIS). | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/bitops.h> | ||
18 | |||
19 | #include <asm/ptrace.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/dma.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/mmu_context.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/core_cia.h> | ||
27 | #include <asm/hwrpb.h> | ||
28 | #include <asm/tlbflush.h> | ||
29 | |||
30 | #include "proto.h" | ||
31 | #include "irq_impl.h" | ||
32 | #include "pci_impl.h" | ||
33 | #include "machvec_impl.h" | ||
34 | |||
35 | |||
36 | static void __init | ||
37 | sx164_init_irq(void) | ||
38 | { | ||
39 | outb(0, DMA1_RESET_REG); | ||
40 | outb(0, DMA2_RESET_REG); | ||
41 | outb(DMA_MODE_CASCADE, DMA2_MODE_REG); | ||
42 | outb(0, DMA2_MASK_REG); | ||
43 | |||
44 | if (alpha_using_srm) | ||
45 | alpha_mv.device_interrupt = srm_device_interrupt; | ||
46 | |||
47 | init_i8259a_irqs(); | ||
48 | |||
49 | /* Not interested in the bogus interrupts (0,3,4,5,40-47), | ||
50 | NMI (1), or HALT (2). */ | ||
51 | if (alpha_using_srm) | ||
52 | init_srm_irqs(40, 0x3f0000); | ||
53 | else | ||
54 | init_pyxis_irqs(0xff00003f0000UL); | ||
55 | |||
56 | setup_irq(16+6, &timer_cascade_irqaction); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * PCI Fixup configuration. | ||
61 | * | ||
62 | * Summary @ PYXIS_INT_REQ: | ||
63 | * Bit Meaning | ||
64 | * 0 RSVD | ||
65 | * 1 NMI | ||
66 | * 2 Halt/Reset switch | ||
67 | * 3 MBZ | ||
68 | * 4 RAZ | ||
69 | * 5 RAZ | ||
70 | * 6 Interval timer (RTC) | ||
71 | * 7 PCI-ISA Bridge | ||
72 | * 8 Interrupt Line A from slot 3 | ||
73 | * 9 Interrupt Line A from slot 2 | ||
74 | *10 Interrupt Line A from slot 1 | ||
75 | *11 Interrupt Line A from slot 0 | ||
76 | *12 Interrupt Line B from slot 3 | ||
77 | *13 Interrupt Line B from slot 2 | ||
78 | *14 Interrupt Line B from slot 1 | ||
79 | *15 Interrupt line B from slot 0 | ||
80 | *16 Interrupt Line C from slot 3 | ||
81 | *17 Interrupt Line C from slot 2 | ||
82 | *18 Interrupt Line C from slot 1 | ||
83 | *19 Interrupt Line C from slot 0 | ||
84 | *20 Interrupt Line D from slot 3 | ||
85 | *21 Interrupt Line D from slot 2 | ||
86 | *22 Interrupt Line D from slot 1 | ||
87 | *23 Interrupt Line D from slot 0 | ||
88 | * | ||
89 | * IdSel | ||
90 | * 5 32 bit PCI option slot 2 | ||
91 | * 6 64 bit PCI option slot 0 | ||
92 | * 7 64 bit PCI option slot 1 | ||
93 | * 8 Cypress I/O | ||
94 | * 9 32 bit PCI option slot 3 | ||
95 | */ | ||
96 | |||
97 | static int __init | ||
98 | sx164_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
99 | { | ||
100 | static char irq_tab[5][5] __initdata = { | ||
101 | /*INT INTA INTB INTC INTD */ | ||
102 | { 16+ 9, 16+ 9, 16+13, 16+17, 16+21}, /* IdSel 5 slot 2 J17 */ | ||
103 | { 16+11, 16+11, 16+15, 16+19, 16+23}, /* IdSel 6 slot 0 J19 */ | ||
104 | { 16+10, 16+10, 16+14, 16+18, 16+22}, /* IdSel 7 slot 1 J18 */ | ||
105 | { -1, -1, -1, -1, -1}, /* IdSel 8 SIO */ | ||
106 | { 16+ 8, 16+ 8, 16+12, 16+16, 16+20} /* IdSel 9 slot 3 J15 */ | ||
107 | }; | ||
108 | const long min_idsel = 5, max_idsel = 9, irqs_per_slot = 5; | ||
109 | return COMMON_TABLE_LOOKUP; | ||
110 | } | ||
111 | |||
112 | static void __init | ||
113 | sx164_init_pci(void) | ||
114 | { | ||
115 | cia_init_pci(); | ||
116 | SMC669_Init(0); | ||
117 | } | ||
118 | |||
119 | static void __init | ||
120 | sx164_init_arch(void) | ||
121 | { | ||
122 | /* | ||
123 | * OSF palcode v1.23 forgets to enable PCA56 Motion Video | ||
124 | * Instructions. Let's enable it. | ||
125 | * We have to check palcode revision because CSERVE interface | ||
126 | * is subject to change without notice. For example, it | ||
127 | * has been changed completely since v1.16 (found in MILO | ||
128 | * distribution). -ink | ||
129 | */ | ||
130 | struct percpu_struct *cpu = (struct percpu_struct*) | ||
131 | ((char*)hwrpb + hwrpb->processor_offset); | ||
132 | |||
133 | if (amask(AMASK_MAX) != 0 | ||
134 | && alpha_using_srm | ||
135 | && (cpu->pal_revision & 0xffff) == 0x117) { | ||
136 | __asm__ __volatile__( | ||
137 | "lda $16,8($31)\n" | ||
138 | "call_pal 9\n" /* Allow PALRES insns in kernel mode */ | ||
139 | ".long 0x64000118\n\n" /* hw_mfpr $0,icsr */ | ||
140 | "ldah $16,(1<<(19-16))($31)\n" | ||
141 | "or $0,$16,$0\n" /* set MVE bit */ | ||
142 | ".long 0x74000118\n" /* hw_mtpr $0,icsr */ | ||
143 | "lda $16,9($31)\n" | ||
144 | "call_pal 9" /* Disable PALRES insns */ | ||
145 | : : : "$0", "$16"); | ||
146 | printk("PCA56 MVI set enabled\n"); | ||
147 | } | ||
148 | |||
149 | pyxis_init_arch(); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * The System Vector | ||
154 | */ | ||
155 | |||
156 | struct alpha_machine_vector sx164_mv __initmv = { | ||
157 | .vector_name = "SX164", | ||
158 | DO_EV5_MMU, | ||
159 | DO_DEFAULT_RTC, | ||
160 | DO_PYXIS_IO, | ||
161 | .machine_check = cia_machine_check, | ||
162 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
163 | .min_io_address = DEFAULT_IO_BASE, | ||
164 | .min_mem_address = DEFAULT_MEM_BASE, | ||
165 | .pci_dac_offset = PYXIS_DAC_OFFSET, | ||
166 | |||
167 | .nr_irqs = 48, | ||
168 | .device_interrupt = pyxis_device_interrupt, | ||
169 | |||
170 | .init_arch = sx164_init_arch, | ||
171 | .init_irq = sx164_init_irq, | ||
172 | .init_rtc = common_init_rtc, | ||
173 | .init_pci = sx164_init_pci, | ||
174 | .kill_arch = cia_kill_arch, | ||
175 | .pci_map_irq = sx164_map_irq, | ||
176 | .pci_swizzle = common_swizzle, | ||
177 | }; | ||
178 | ALIAS_MV(sx164) | ||
diff --git a/arch/alpha/kernel/sys_takara.c b/arch/alpha/kernel/sys_takara.c new file mode 100644 index 000000000000..7955bdfc2db0 --- /dev/null +++ b/arch/alpha/kernel/sys_takara.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_takara.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * | ||
8 | * Code supporting the TAKARA. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/init.h> | ||
17 | |||
18 | #include <asm/ptrace.h> | ||
19 | #include <asm/system.h> | ||
20 | #include <asm/dma.h> | ||
21 | #include <asm/irq.h> | ||
22 | #include <asm/mmu_context.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/core_cia.h> | ||
26 | #include <asm/tlbflush.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | #include "irq_impl.h" | ||
30 | #include "pci_impl.h" | ||
31 | #include "machvec_impl.h" | ||
32 | |||
33 | |||
34 | /* Note mask bit is true for DISABLED irqs. */ | ||
35 | static unsigned long cached_irq_mask[2] = { -1, -1 }; | ||
36 | |||
37 | static inline void | ||
38 | takara_update_irq_hw(unsigned long irq, unsigned long mask) | ||
39 | { | ||
40 | int regaddr; | ||
41 | |||
42 | mask = (irq >= 64 ? mask << 16 : mask >> ((irq - 16) & 0x30)); | ||
43 | regaddr = 0x510 + (((irq - 16) >> 2) & 0x0c); | ||
44 | outl(mask & 0xffff0000UL, regaddr); | ||
45 | } | ||
46 | |||
47 | static inline void | ||
48 | takara_enable_irq(unsigned int irq) | ||
49 | { | ||
50 | unsigned long mask; | ||
51 | mask = (cached_irq_mask[irq >= 64] &= ~(1UL << (irq & 63))); | ||
52 | takara_update_irq_hw(irq, mask); | ||
53 | } | ||
54 | |||
55 | static void | ||
56 | takara_disable_irq(unsigned int irq) | ||
57 | { | ||
58 | unsigned long mask; | ||
59 | mask = (cached_irq_mask[irq >= 64] |= 1UL << (irq & 63)); | ||
60 | takara_update_irq_hw(irq, mask); | ||
61 | } | ||
62 | |||
63 | static unsigned int | ||
64 | takara_startup_irq(unsigned int irq) | ||
65 | { | ||
66 | takara_enable_irq(irq); | ||
67 | return 0; /* never anything pending */ | ||
68 | } | ||
69 | |||
70 | static void | ||
71 | takara_end_irq(unsigned int irq) | ||
72 | { | ||
73 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
74 | takara_enable_irq(irq); | ||
75 | } | ||
76 | |||
77 | static struct hw_interrupt_type takara_irq_type = { | ||
78 | .typename = "TAKARA", | ||
79 | .startup = takara_startup_irq, | ||
80 | .shutdown = takara_disable_irq, | ||
81 | .enable = takara_enable_irq, | ||
82 | .disable = takara_disable_irq, | ||
83 | .ack = takara_disable_irq, | ||
84 | .end = takara_end_irq, | ||
85 | }; | ||
86 | |||
87 | static void | ||
88 | takara_device_interrupt(unsigned long vector, struct pt_regs *regs) | ||
89 | { | ||
90 | unsigned intstatus; | ||
91 | |||
92 | /* | ||
93 | * The PALcode will have passed us vectors 0x800 or 0x810, | ||
94 | * which are fairly arbitrary values and serve only to tell | ||
95 | * us whether an interrupt has come in on IRQ0 or IRQ1. If | ||
96 | * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's | ||
97 | * probably ISA, but PCI interrupts can come through IRQ0 | ||
98 | * as well if the interrupt controller isn't in accelerated | ||
99 | * mode. | ||
100 | * | ||
101 | * OTOH, the accelerator thing doesn't seem to be working | ||
102 | * overly well, so what we'll do instead is try directly | ||
103 | * examining the Master Interrupt Register to see if it's a | ||
104 | * PCI interrupt, and if _not_ then we'll pass it on to the | ||
105 | * ISA handler. | ||
106 | */ | ||
107 | |||
108 | intstatus = inw(0x500) & 15; | ||
109 | if (intstatus) { | ||
110 | /* | ||
111 | * This is a PCI interrupt. Check each bit and | ||
112 | * despatch an interrupt if it's set. | ||
113 | */ | ||
114 | |||
115 | if (intstatus & 8) handle_irq(16+3, regs); | ||
116 | if (intstatus & 4) handle_irq(16+2, regs); | ||
117 | if (intstatus & 2) handle_irq(16+1, regs); | ||
118 | if (intstatus & 1) handle_irq(16+0, regs); | ||
119 | } else { | ||
120 | isa_device_interrupt (vector, regs); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static void | ||
125 | takara_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
126 | { | ||
127 | int irq = (vector - 0x800) >> 4; | ||
128 | handle_irq(irq, regs); | ||
129 | } | ||
130 | |||
131 | static void __init | ||
132 | takara_init_irq(void) | ||
133 | { | ||
134 | long i; | ||
135 | |||
136 | init_i8259a_irqs(); | ||
137 | |||
138 | if (alpha_using_srm) { | ||
139 | alpha_mv.device_interrupt = takara_srm_device_interrupt; | ||
140 | } else { | ||
141 | unsigned int ctlreg = inl(0x500); | ||
142 | |||
143 | /* Return to non-accelerated mode. */ | ||
144 | ctlreg &= ~0x8000; | ||
145 | outl(ctlreg, 0x500); | ||
146 | |||
147 | /* Enable the PCI interrupt register. */ | ||
148 | ctlreg = 0x05107c00; | ||
149 | outl(ctlreg, 0x500); | ||
150 | } | ||
151 | |||
152 | for (i = 16; i < 128; i += 16) | ||
153 | takara_update_irq_hw(i, -1); | ||
154 | |||
155 | for (i = 16; i < 128; ++i) { | ||
156 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
157 | irq_desc[i].handler = &takara_irq_type; | ||
158 | } | ||
159 | |||
160 | common_init_isa_dma(); | ||
161 | } | ||
162 | |||
163 | |||
164 | /* | ||
165 | * The Takara has PCI devices 1, 2, and 3 configured to slots 20, | ||
166 | * 19, and 18 respectively, in the default configuration. They can | ||
167 | * also be jumpered to slots 8, 7, and 6 respectively, which is fun | ||
168 | * because the SIO ISA bridge can also be slot 7. However, the SIO | ||
169 | * doesn't explicitly generate PCI-type interrupts, so we can | ||
170 | * assign it whatever the hell IRQ we like and it doesn't matter. | ||
171 | */ | ||
172 | |||
173 | static int __init | ||
174 | takara_map_irq_srm(struct pci_dev *dev, u8 slot, u8 pin) | ||
175 | { | ||
176 | static char irq_tab[15][5] __initdata = { | ||
177 | { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */ | ||
178 | { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */ | ||
179 | { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */ | ||
180 | { -1, -1, -1, -1, -1}, /* slot 9 == nothing */ | ||
181 | { -1, -1, -1, -1, -1}, /* slot 10 == nothing */ | ||
182 | { -1, -1, -1, -1, -1}, /* slot 11 == nothing */ | ||
183 | /* These are behind the bridges. */ | ||
184 | { 12, 12, 13, 14, 15}, /* slot 12 == nothing */ | ||
185 | { 8, 8, 9, 19, 11}, /* slot 13 == nothing */ | ||
186 | { 4, 4, 5, 6, 7}, /* slot 14 == nothing */ | ||
187 | { 0, 0, 1, 2, 3}, /* slot 15 == nothing */ | ||
188 | { -1, -1, -1, -1, -1}, /* slot 16 == nothing */ | ||
189 | {64+ 0, 64+0, 64+1, 64+2, 64+3}, /* slot 17= device 4 */ | ||
190 | {48+ 0, 48+0, 48+1, 48+2, 48+3}, /* slot 18= device 3 */ | ||
191 | {32+ 0, 32+0, 32+1, 32+2, 32+3}, /* slot 19= device 2 */ | ||
192 | {16+ 0, 16+0, 16+1, 16+2, 16+3}, /* slot 20= device 1 */ | ||
193 | }; | ||
194 | const long min_idsel = 6, max_idsel = 20, irqs_per_slot = 5; | ||
195 | int irq = COMMON_TABLE_LOOKUP; | ||
196 | if (irq >= 0 && irq < 16) { | ||
197 | /* Guess that we are behind a bridge. */ | ||
198 | unsigned int busslot = PCI_SLOT(dev->bus->self->devfn); | ||
199 | irq += irq_tab[busslot-min_idsel][0]; | ||
200 | } | ||
201 | return irq; | ||
202 | } | ||
203 | |||
204 | static int __init | ||
205 | takara_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
206 | { | ||
207 | static char irq_tab[15][5] __initdata = { | ||
208 | { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */ | ||
209 | { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */ | ||
210 | { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */ | ||
211 | { -1, -1, -1, -1, -1}, /* slot 9 == nothing */ | ||
212 | { -1, -1, -1, -1, -1}, /* slot 10 == nothing */ | ||
213 | { -1, -1, -1, -1, -1}, /* slot 11 == nothing */ | ||
214 | { -1, -1, -1, -1, -1}, /* slot 12 == nothing */ | ||
215 | { -1, -1, -1, -1, -1}, /* slot 13 == nothing */ | ||
216 | { -1, -1, -1, -1, -1}, /* slot 14 == nothing */ | ||
217 | { -1, -1, -1, -1, -1}, /* slot 15 == nothing */ | ||
218 | { -1, -1, -1, -1, -1}, /* slot 16 == nothing */ | ||
219 | { -1, -1, -1, -1, -1}, /* slot 17 == nothing */ | ||
220 | { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 18 == device 3 */ | ||
221 | { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 19 == device 2 */ | ||
222 | { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 20 == device 1 */ | ||
223 | }; | ||
224 | const long min_idsel = 6, max_idsel = 20, irqs_per_slot = 5; | ||
225 | return COMMON_TABLE_LOOKUP; | ||
226 | } | ||
227 | |||
228 | static u8 __init | ||
229 | takara_swizzle(struct pci_dev *dev, u8 *pinp) | ||
230 | { | ||
231 | int slot = PCI_SLOT(dev->devfn); | ||
232 | int pin = *pinp; | ||
233 | unsigned int ctlreg = inl(0x500); | ||
234 | unsigned int busslot; | ||
235 | |||
236 | if (!dev->bus->self) | ||
237 | return slot; | ||
238 | |||
239 | busslot = PCI_SLOT(dev->bus->self->devfn); | ||
240 | /* Check for built-in bridges. */ | ||
241 | if (dev->bus->number != 0 | ||
242 | && busslot > 16 | ||
243 | && ((1<<(36-busslot)) & ctlreg)) { | ||
244 | if (pin == 1) | ||
245 | pin += (20 - busslot); | ||
246 | else { | ||
247 | printk(KERN_WARNING "takara_swizzle: can only " | ||
248 | "handle cards with INTA IRQ pin.\n"); | ||
249 | } | ||
250 | } else { | ||
251 | /* Must be a card-based bridge. */ | ||
252 | printk(KERN_WARNING "takara_swizzle: cannot handle " | ||
253 | "card-bridge behind builtin bridge yet.\n"); | ||
254 | } | ||
255 | |||
256 | *pinp = pin; | ||
257 | return slot; | ||
258 | } | ||
259 | |||
260 | static void __init | ||
261 | takara_init_pci(void) | ||
262 | { | ||
263 | if (alpha_using_srm) | ||
264 | alpha_mv.pci_map_irq = takara_map_irq_srm; | ||
265 | |||
266 | cia_init_pci(); | ||
267 | ns87312_enable_ide(0x26e); | ||
268 | } | ||
269 | |||
270 | |||
271 | /* | ||
272 | * The System Vector | ||
273 | */ | ||
274 | |||
275 | struct alpha_machine_vector takara_mv __initmv = { | ||
276 | .vector_name = "Takara", | ||
277 | DO_EV5_MMU, | ||
278 | DO_DEFAULT_RTC, | ||
279 | DO_CIA_IO, | ||
280 | .machine_check = cia_machine_check, | ||
281 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
282 | .min_io_address = DEFAULT_IO_BASE, | ||
283 | .min_mem_address = CIA_DEFAULT_MEM_BASE, | ||
284 | |||
285 | .nr_irqs = 128, | ||
286 | .device_interrupt = takara_device_interrupt, | ||
287 | |||
288 | .init_arch = cia_init_arch, | ||
289 | .init_irq = takara_init_irq, | ||
290 | .init_rtc = common_init_rtc, | ||
291 | .init_pci = takara_init_pci, | ||
292 | .kill_arch = cia_kill_arch, | ||
293 | .pci_map_irq = takara_map_irq, | ||
294 | .pci_swizzle = takara_swizzle, | ||
295 | }; | ||
296 | ALIAS_MV(takara) | ||
diff --git a/arch/alpha/kernel/sys_titan.c b/arch/alpha/kernel/sys_titan.c new file mode 100644 index 000000000000..5f84417eeb7b --- /dev/null +++ b/arch/alpha/kernel/sys_titan.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_titan.c | ||
3 | * | ||
4 | * Copyright (C) 1995 David A Rusling | ||
5 | * Copyright (C) 1996, 1999 Jay A Estabrook | ||
6 | * Copyright (C) 1998, 1999 Richard Henderson | ||
7 | * Copyright (C) 1999, 2000 Jeff Wiedemeier | ||
8 | * | ||
9 | * Code supporting TITAN systems (EV6+TITAN), currently: | ||
10 | * Privateer | ||
11 | * Falcon | ||
12 | * Granite | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/bitops.h> | ||
23 | |||
24 | #include <asm/ptrace.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/dma.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/mmu_context.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/pgtable.h> | ||
31 | #include <asm/core_titan.h> | ||
32 | #include <asm/hwrpb.h> | ||
33 | #include <asm/tlbflush.h> | ||
34 | |||
35 | #include "proto.h" | ||
36 | #include "irq_impl.h" | ||
37 | #include "pci_impl.h" | ||
38 | #include "machvec_impl.h" | ||
39 | #include "err_impl.h" | ||
40 | |||
41 | |||
42 | /* | ||
43 | * Titan generic | ||
44 | */ | ||
45 | |||
46 | /* | ||
47 | * Titan supports up to 4 CPUs | ||
48 | */ | ||
49 | static unsigned long titan_cpu_irq_affinity[4] = { ~0UL, ~0UL, ~0UL, ~0UL }; | ||
50 | |||
51 | /* | ||
52 | * Mask is set (1) if enabled | ||
53 | */ | ||
54 | static unsigned long titan_cached_irq_mask; | ||
55 | |||
56 | /* | ||
57 | * Need SMP-safe access to interrupt CSRs | ||
58 | */ | ||
59 | DEFINE_SPINLOCK(titan_irq_lock); | ||
60 | |||
61 | static void | ||
62 | titan_update_irq_hw(unsigned long mask) | ||
63 | { | ||
64 | register titan_cchip *cchip = TITAN_cchip; | ||
65 | unsigned long isa_enable = 1UL << 55; | ||
66 | register int bcpu = boot_cpuid; | ||
67 | |||
68 | #ifdef CONFIG_SMP | ||
69 | cpumask_t cpm = cpu_present_mask; | ||
70 | volatile unsigned long *dim0, *dim1, *dim2, *dim3; | ||
71 | unsigned long mask0, mask1, mask2, mask3, dummy; | ||
72 | |||
73 | mask &= ~isa_enable; | ||
74 | mask0 = mask & titan_cpu_irq_affinity[0]; | ||
75 | mask1 = mask & titan_cpu_irq_affinity[1]; | ||
76 | mask2 = mask & titan_cpu_irq_affinity[2]; | ||
77 | mask3 = mask & titan_cpu_irq_affinity[3]; | ||
78 | |||
79 | if (bcpu == 0) mask0 |= isa_enable; | ||
80 | else if (bcpu == 1) mask1 |= isa_enable; | ||
81 | else if (bcpu == 2) mask2 |= isa_enable; | ||
82 | else mask3 |= isa_enable; | ||
83 | |||
84 | dim0 = &cchip->dim0.csr; | ||
85 | dim1 = &cchip->dim1.csr; | ||
86 | dim2 = &cchip->dim2.csr; | ||
87 | dim3 = &cchip->dim3.csr; | ||
88 | if (!cpu_isset(0, cpm)) dim0 = &dummy; | ||
89 | if (!cpu_isset(1, cpm)) dim1 = &dummy; | ||
90 | if (!cpu_isset(2, cpm)) dim2 = &dummy; | ||
91 | if (!cpu_isset(3, cpm)) dim3 = &dummy; | ||
92 | |||
93 | *dim0 = mask0; | ||
94 | *dim1 = mask1; | ||
95 | *dim2 = mask2; | ||
96 | *dim3 = mask3; | ||
97 | mb(); | ||
98 | *dim0; | ||
99 | *dim1; | ||
100 | *dim2; | ||
101 | *dim3; | ||
102 | #else | ||
103 | volatile unsigned long *dimB; | ||
104 | dimB = &cchip->dim0.csr; | ||
105 | if (bcpu == 1) dimB = &cchip->dim1.csr; | ||
106 | else if (bcpu == 2) dimB = &cchip->dim2.csr; | ||
107 | else if (bcpu == 3) dimB = &cchip->dim3.csr; | ||
108 | |||
109 | *dimB = mask | isa_enable; | ||
110 | mb(); | ||
111 | *dimB; | ||
112 | #endif | ||
113 | } | ||
114 | |||
115 | static inline void | ||
116 | titan_enable_irq(unsigned int irq) | ||
117 | { | ||
118 | spin_lock(&titan_irq_lock); | ||
119 | titan_cached_irq_mask |= 1UL << (irq - 16); | ||
120 | titan_update_irq_hw(titan_cached_irq_mask); | ||
121 | spin_unlock(&titan_irq_lock); | ||
122 | } | ||
123 | |||
124 | static inline void | ||
125 | titan_disable_irq(unsigned int irq) | ||
126 | { | ||
127 | spin_lock(&titan_irq_lock); | ||
128 | titan_cached_irq_mask &= ~(1UL << (irq - 16)); | ||
129 | titan_update_irq_hw(titan_cached_irq_mask); | ||
130 | spin_unlock(&titan_irq_lock); | ||
131 | } | ||
132 | |||
133 | static unsigned int | ||
134 | titan_startup_irq(unsigned int irq) | ||
135 | { | ||
136 | titan_enable_irq(irq); | ||
137 | return 0; /* never anything pending */ | ||
138 | } | ||
139 | |||
140 | static void | ||
141 | titan_end_irq(unsigned int irq) | ||
142 | { | ||
143 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
144 | titan_enable_irq(irq); | ||
145 | } | ||
146 | |||
147 | static void | ||
148 | titan_cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) | ||
149 | { | ||
150 | int cpu; | ||
151 | |||
152 | for (cpu = 0; cpu < 4; cpu++) { | ||
153 | if (cpu_isset(cpu, affinity)) | ||
154 | titan_cpu_irq_affinity[cpu] |= 1UL << irq; | ||
155 | else | ||
156 | titan_cpu_irq_affinity[cpu] &= ~(1UL << irq); | ||
157 | } | ||
158 | |||
159 | } | ||
160 | |||
161 | static void | ||
162 | titan_set_irq_affinity(unsigned int irq, cpumask_t affinity) | ||
163 | { | ||
164 | spin_lock(&titan_irq_lock); | ||
165 | titan_cpu_set_irq_affinity(irq - 16, affinity); | ||
166 | titan_update_irq_hw(titan_cached_irq_mask); | ||
167 | spin_unlock(&titan_irq_lock); | ||
168 | } | ||
169 | |||
170 | static void | ||
171 | titan_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
172 | { | ||
173 | printk("titan_device_interrupt: NOT IMPLEMENTED YET!! \n"); | ||
174 | } | ||
175 | |||
176 | static void | ||
177 | titan_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
178 | { | ||
179 | int irq; | ||
180 | |||
181 | irq = (vector - 0x800) >> 4; | ||
182 | handle_irq(irq, regs); | ||
183 | } | ||
184 | |||
185 | |||
186 | static void __init | ||
187 | init_titan_irqs(struct hw_interrupt_type * ops, int imin, int imax) | ||
188 | { | ||
189 | long i; | ||
190 | for (i = imin; i <= imax; ++i) { | ||
191 | irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; | ||
192 | irq_desc[i].handler = ops; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | static struct hw_interrupt_type titan_irq_type = { | ||
197 | .typename = "TITAN", | ||
198 | .startup = titan_startup_irq, | ||
199 | .shutdown = titan_disable_irq, | ||
200 | .enable = titan_enable_irq, | ||
201 | .disable = titan_disable_irq, | ||
202 | .ack = titan_disable_irq, | ||
203 | .end = titan_end_irq, | ||
204 | .set_affinity = titan_set_irq_affinity, | ||
205 | }; | ||
206 | |||
207 | static irqreturn_t | ||
208 | titan_intr_nop(int irq, void *dev_id, struct pt_regs *regs) | ||
209 | { | ||
210 | /* | ||
211 | * This is a NOP interrupt handler for the purposes of | ||
212 | * event counting -- just return. | ||
213 | */ | ||
214 | return IRQ_HANDLED; | ||
215 | } | ||
216 | |||
217 | static void __init | ||
218 | titan_init_irq(void) | ||
219 | { | ||
220 | if (alpha_using_srm && !alpha_mv.device_interrupt) | ||
221 | alpha_mv.device_interrupt = titan_srm_device_interrupt; | ||
222 | if (!alpha_mv.device_interrupt) | ||
223 | alpha_mv.device_interrupt = titan_device_interrupt; | ||
224 | |||
225 | titan_update_irq_hw(0); | ||
226 | |||
227 | init_titan_irqs(&titan_irq_type, 16, 63 + 16); | ||
228 | } | ||
229 | |||
230 | static void __init | ||
231 | titan_legacy_init_irq(void) | ||
232 | { | ||
233 | /* init the legacy dma controller */ | ||
234 | outb(0, DMA1_RESET_REG); | ||
235 | outb(0, DMA2_RESET_REG); | ||
236 | outb(DMA_MODE_CASCADE, DMA2_MODE_REG); | ||
237 | outb(0, DMA2_MASK_REG); | ||
238 | |||
239 | /* init the legacy irq controller */ | ||
240 | init_i8259a_irqs(); | ||
241 | |||
242 | /* init the titan irqs */ | ||
243 | titan_init_irq(); | ||
244 | } | ||
245 | |||
246 | void | ||
247 | titan_dispatch_irqs(u64 mask, struct pt_regs *regs) | ||
248 | { | ||
249 | unsigned long vector; | ||
250 | |||
251 | /* | ||
252 | * Mask down to those interrupts which are enable on this processor | ||
253 | */ | ||
254 | mask &= titan_cpu_irq_affinity[smp_processor_id()]; | ||
255 | |||
256 | /* | ||
257 | * Dispatch all requested interrupts | ||
258 | */ | ||
259 | while (mask) { | ||
260 | /* convert to SRM vector... priority is <63> -> <0> */ | ||
261 | __asm__("ctlz %1, %0" : "=r"(vector) : "r"(mask)); | ||
262 | vector = 63 - vector; | ||
263 | mask &= ~(1UL << vector); /* clear it out */ | ||
264 | vector = 0x900 + (vector << 4); /* convert to SRM vector */ | ||
265 | |||
266 | /* dispatch it */ | ||
267 | alpha_mv.device_interrupt(vector, regs); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | |||
272 | /* | ||
273 | * Titan Family | ||
274 | */ | ||
275 | static void __init | ||
276 | titan_late_init(void) | ||
277 | { | ||
278 | /* | ||
279 | * Enable the system error interrupts. These interrupts are | ||
280 | * all reported to the kernel as machine checks, so the handler | ||
281 | * is a nop so it can be called to count the individual events. | ||
282 | */ | ||
283 | request_irq(63+16, titan_intr_nop, SA_INTERRUPT, | ||
284 | "CChip Error", NULL); | ||
285 | request_irq(62+16, titan_intr_nop, SA_INTERRUPT, | ||
286 | "PChip 0 H_Error", NULL); | ||
287 | request_irq(61+16, titan_intr_nop, SA_INTERRUPT, | ||
288 | "PChip 1 H_Error", NULL); | ||
289 | request_irq(60+16, titan_intr_nop, SA_INTERRUPT, | ||
290 | "PChip 0 C_Error", NULL); | ||
291 | request_irq(59+16, titan_intr_nop, SA_INTERRUPT, | ||
292 | "PChip 1 C_Error", NULL); | ||
293 | |||
294 | /* | ||
295 | * Register our error handlers. | ||
296 | */ | ||
297 | titan_register_error_handlers(); | ||
298 | |||
299 | /* | ||
300 | * Check if the console left us any error logs. | ||
301 | */ | ||
302 | cdl_check_console_data_log(); | ||
303 | |||
304 | } | ||
305 | |||
306 | static int __devinit | ||
307 | titan_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
308 | { | ||
309 | u8 intline; | ||
310 | int irq; | ||
311 | |||
312 | /* Get the current intline. */ | ||
313 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &intline); | ||
314 | irq = intline; | ||
315 | |||
316 | /* Is it explicitly routed through ISA? */ | ||
317 | if ((irq & 0xF0) == 0xE0) | ||
318 | return irq; | ||
319 | |||
320 | /* Offset by 16 to make room for ISA interrupts 0 - 15. */ | ||
321 | return irq + 16; | ||
322 | } | ||
323 | |||
324 | static void __init | ||
325 | titan_init_pci(void) | ||
326 | { | ||
327 | /* | ||
328 | * This isn't really the right place, but there's some init | ||
329 | * that needs to be done after everything is basically up. | ||
330 | */ | ||
331 | titan_late_init(); | ||
332 | |||
333 | pci_probe_only = 1; | ||
334 | common_init_pci(); | ||
335 | SMC669_Init(0); | ||
336 | #ifdef CONFIG_VGA_HOSE | ||
337 | locate_and_init_vga(NULL); | ||
338 | #endif | ||
339 | } | ||
340 | |||
341 | |||
342 | /* | ||
343 | * Privateer | ||
344 | */ | ||
345 | static void __init | ||
346 | privateer_init_pci(void) | ||
347 | { | ||
348 | /* | ||
349 | * Hook a couple of extra err interrupts that the | ||
350 | * common titan code won't. | ||
351 | */ | ||
352 | request_irq(53+16, titan_intr_nop, SA_INTERRUPT, | ||
353 | "NMI", NULL); | ||
354 | request_irq(50+16, titan_intr_nop, SA_INTERRUPT, | ||
355 | "Temperature Warning", NULL); | ||
356 | |||
357 | /* | ||
358 | * Finish with the common version. | ||
359 | */ | ||
360 | return titan_init_pci(); | ||
361 | } | ||
362 | |||
363 | |||
364 | /* | ||
365 | * The System Vectors. | ||
366 | */ | ||
367 | struct alpha_machine_vector titan_mv __initmv = { | ||
368 | .vector_name = "TITAN", | ||
369 | DO_EV6_MMU, | ||
370 | DO_DEFAULT_RTC, | ||
371 | DO_TITAN_IO, | ||
372 | .machine_check = titan_machine_check, | ||
373 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
374 | .min_io_address = DEFAULT_IO_BASE, | ||
375 | .min_mem_address = DEFAULT_MEM_BASE, | ||
376 | .pci_dac_offset = TITAN_DAC_OFFSET, | ||
377 | |||
378 | .nr_irqs = 80, /* 64 + 16 */ | ||
379 | /* device_interrupt will be filled in by titan_init_irq */ | ||
380 | |||
381 | .agp_info = titan_agp_info, | ||
382 | |||
383 | .init_arch = titan_init_arch, | ||
384 | .init_irq = titan_legacy_init_irq, | ||
385 | .init_rtc = common_init_rtc, | ||
386 | .init_pci = titan_init_pci, | ||
387 | |||
388 | .kill_arch = titan_kill_arch, | ||
389 | .pci_map_irq = titan_map_irq, | ||
390 | .pci_swizzle = common_swizzle, | ||
391 | }; | ||
392 | ALIAS_MV(titan) | ||
393 | |||
394 | struct alpha_machine_vector privateer_mv __initmv = { | ||
395 | .vector_name = "PRIVATEER", | ||
396 | DO_EV6_MMU, | ||
397 | DO_DEFAULT_RTC, | ||
398 | DO_TITAN_IO, | ||
399 | .machine_check = privateer_machine_check, | ||
400 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
401 | .min_io_address = DEFAULT_IO_BASE, | ||
402 | .min_mem_address = DEFAULT_MEM_BASE, | ||
403 | .pci_dac_offset = TITAN_DAC_OFFSET, | ||
404 | |||
405 | .nr_irqs = 80, /* 64 + 16 */ | ||
406 | /* device_interrupt will be filled in by titan_init_irq */ | ||
407 | |||
408 | .agp_info = titan_agp_info, | ||
409 | |||
410 | .init_arch = titan_init_arch, | ||
411 | .init_irq = titan_legacy_init_irq, | ||
412 | .init_rtc = common_init_rtc, | ||
413 | .init_pci = privateer_init_pci, | ||
414 | |||
415 | .kill_arch = titan_kill_arch, | ||
416 | .pci_map_irq = titan_map_irq, | ||
417 | .pci_swizzle = common_swizzle, | ||
418 | }; | ||
419 | /* No alpha_mv alias for privateer since we compile it | ||
420 | in unconditionally with titan; setup_arch knows how to cope. */ | ||
diff --git a/arch/alpha/kernel/sys_wildfire.c b/arch/alpha/kernel/sys_wildfire.c new file mode 100644 index 000000000000..1553f470246e --- /dev/null +++ b/arch/alpha/kernel/sys_wildfire.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/sys_wildfire.c | ||
3 | * | ||
4 | * Wildfire support. | ||
5 | * | ||
6 | * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/bitops.h> | ||
16 | |||
17 | #include <asm/ptrace.h> | ||
18 | #include <asm/system.h> | ||
19 | #include <asm/dma.h> | ||
20 | #include <asm/irq.h> | ||
21 | #include <asm/mmu_context.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include <asm/core_wildfire.h> | ||
25 | #include <asm/hwrpb.h> | ||
26 | #include <asm/tlbflush.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | #include "irq_impl.h" | ||
30 | #include "pci_impl.h" | ||
31 | #include "machvec_impl.h" | ||
32 | |||
33 | static unsigned long cached_irq_mask[WILDFIRE_NR_IRQS/(sizeof(long)*8)]; | ||
34 | |||
35 | DEFINE_SPINLOCK(wildfire_irq_lock); | ||
36 | |||
37 | static int doing_init_irq_hw = 0; | ||
38 | |||
39 | static void | ||
40 | wildfire_update_irq_hw(unsigned int irq) | ||
41 | { | ||
42 | int qbbno = (irq >> 8) & (WILDFIRE_MAX_QBB - 1); | ||
43 | int pcano = (irq >> 6) & (WILDFIRE_PCA_PER_QBB - 1); | ||
44 | wildfire_pca *pca; | ||
45 | volatile unsigned long * enable0; | ||
46 | |||
47 | if (!WILDFIRE_PCA_EXISTS(qbbno, pcano)) { | ||
48 | if (!doing_init_irq_hw) { | ||
49 | printk(KERN_ERR "wildfire_update_irq_hw:" | ||
50 | " got irq %d for non-existent PCA %d" | ||
51 | " on QBB %d.\n", | ||
52 | irq, pcano, qbbno); | ||
53 | } | ||
54 | return; | ||
55 | } | ||
56 | |||
57 | pca = WILDFIRE_pca(qbbno, pcano); | ||
58 | enable0 = (unsigned long *) &pca->pca_int[0].enable; /* ??? */ | ||
59 | |||
60 | *enable0 = cached_irq_mask[qbbno * WILDFIRE_PCA_PER_QBB + pcano]; | ||
61 | mb(); | ||
62 | *enable0; | ||
63 | } | ||
64 | |||
65 | static void __init | ||
66 | wildfire_init_irq_hw(void) | ||
67 | { | ||
68 | #if 0 | ||
69 | register wildfire_pca * pca = WILDFIRE_pca(0, 0); | ||
70 | volatile unsigned long * enable0, * enable1, * enable2, *enable3; | ||
71 | volatile unsigned long * target0, * target1, * target2, *target3; | ||
72 | |||
73 | enable0 = (unsigned long *) &pca->pca_int[0].enable; | ||
74 | enable1 = (unsigned long *) &pca->pca_int[1].enable; | ||
75 | enable2 = (unsigned long *) &pca->pca_int[2].enable; | ||
76 | enable3 = (unsigned long *) &pca->pca_int[3].enable; | ||
77 | |||
78 | target0 = (unsigned long *) &pca->pca_int[0].target; | ||
79 | target1 = (unsigned long *) &pca->pca_int[1].target; | ||
80 | target2 = (unsigned long *) &pca->pca_int[2].target; | ||
81 | target3 = (unsigned long *) &pca->pca_int[3].target; | ||
82 | |||
83 | *enable0 = *enable1 = *enable2 = *enable3 = 0; | ||
84 | |||
85 | *target0 = (1UL<<8) | WILDFIRE_QBB(0); | ||
86 | *target1 = *target2 = *target3 = 0; | ||
87 | |||
88 | mb(); | ||
89 | |||
90 | *enable0; *enable1; *enable2; *enable3; | ||
91 | *target0; *target1; *target2; *target3; | ||
92 | |||
93 | #else | ||
94 | int i; | ||
95 | |||
96 | doing_init_irq_hw = 1; | ||
97 | |||
98 | /* Need to update only once for every possible PCA. */ | ||
99 | for (i = 0; i < WILDFIRE_NR_IRQS; i+=WILDFIRE_IRQ_PER_PCA) | ||
100 | wildfire_update_irq_hw(i); | ||
101 | |||
102 | doing_init_irq_hw = 0; | ||
103 | #endif | ||
104 | } | ||
105 | |||
106 | static void | ||
107 | wildfire_enable_irq(unsigned int irq) | ||
108 | { | ||
109 | if (irq < 16) | ||
110 | i8259a_enable_irq(irq); | ||
111 | |||
112 | spin_lock(&wildfire_irq_lock); | ||
113 | set_bit(irq, &cached_irq_mask); | ||
114 | wildfire_update_irq_hw(irq); | ||
115 | spin_unlock(&wildfire_irq_lock); | ||
116 | } | ||
117 | |||
118 | static void | ||
119 | wildfire_disable_irq(unsigned int irq) | ||
120 | { | ||
121 | if (irq < 16) | ||
122 | i8259a_disable_irq(irq); | ||
123 | |||
124 | spin_lock(&wildfire_irq_lock); | ||
125 | clear_bit(irq, &cached_irq_mask); | ||
126 | wildfire_update_irq_hw(irq); | ||
127 | spin_unlock(&wildfire_irq_lock); | ||
128 | } | ||
129 | |||
130 | static void | ||
131 | wildfire_mask_and_ack_irq(unsigned int irq) | ||
132 | { | ||
133 | if (irq < 16) | ||
134 | i8259a_mask_and_ack_irq(irq); | ||
135 | |||
136 | spin_lock(&wildfire_irq_lock); | ||
137 | clear_bit(irq, &cached_irq_mask); | ||
138 | wildfire_update_irq_hw(irq); | ||
139 | spin_unlock(&wildfire_irq_lock); | ||
140 | } | ||
141 | |||
142 | static unsigned int | ||
143 | wildfire_startup_irq(unsigned int irq) | ||
144 | { | ||
145 | wildfire_enable_irq(irq); | ||
146 | return 0; /* never anything pending */ | ||
147 | } | ||
148 | |||
149 | static void | ||
150 | wildfire_end_irq(unsigned int irq) | ||
151 | { | ||
152 | #if 0 | ||
153 | if (!irq_desc[irq].action) | ||
154 | printk("got irq %d\n", irq); | ||
155 | #endif | ||
156 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
157 | wildfire_enable_irq(irq); | ||
158 | } | ||
159 | |||
160 | static struct hw_interrupt_type wildfire_irq_type = { | ||
161 | .typename = "WILDFIRE", | ||
162 | .startup = wildfire_startup_irq, | ||
163 | .shutdown = wildfire_disable_irq, | ||
164 | .enable = wildfire_enable_irq, | ||
165 | .disable = wildfire_disable_irq, | ||
166 | .ack = wildfire_mask_and_ack_irq, | ||
167 | .end = wildfire_end_irq, | ||
168 | }; | ||
169 | |||
170 | static void __init | ||
171 | wildfire_init_irq_per_pca(int qbbno, int pcano) | ||
172 | { | ||
173 | int i, irq_bias; | ||
174 | unsigned long io_bias; | ||
175 | static struct irqaction isa_enable = { | ||
176 | .handler = no_action, | ||
177 | .name = "isa_enable", | ||
178 | }; | ||
179 | |||
180 | irq_bias = qbbno * (WILDFIRE_PCA_PER_QBB * WILDFIRE_IRQ_PER_PCA) | ||
181 | + pcano * WILDFIRE_IRQ_PER_PCA; | ||
182 | |||
183 | /* Only need the following for first PCI bus per PCA. */ | ||
184 | io_bias = WILDFIRE_IO(qbbno, pcano<<1) - WILDFIRE_IO_BIAS; | ||
185 | |||
186 | #if 0 | ||
187 | outb(0, DMA1_RESET_REG + io_bias); | ||
188 | outb(0, DMA2_RESET_REG + io_bias); | ||
189 | outb(DMA_MODE_CASCADE, DMA2_MODE_REG + io_bias); | ||
190 | outb(0, DMA2_MASK_REG + io_bias); | ||
191 | #endif | ||
192 | |||
193 | #if 0 | ||
194 | /* ??? Not sure how to do this, yet... */ | ||
195 | init_i8259a_irqs(); /* ??? */ | ||
196 | #endif | ||
197 | |||
198 | for (i = 0; i < 16; ++i) { | ||
199 | if (i == 2) | ||
200 | continue; | ||
201 | irq_desc[i+irq_bias].status = IRQ_DISABLED | IRQ_LEVEL; | ||
202 | irq_desc[i+irq_bias].handler = &wildfire_irq_type; | ||
203 | } | ||
204 | |||
205 | irq_desc[36+irq_bias].status = IRQ_DISABLED | IRQ_LEVEL; | ||
206 | irq_desc[36+irq_bias].handler = &wildfire_irq_type; | ||
207 | for (i = 40; i < 64; ++i) { | ||
208 | irq_desc[i+irq_bias].status = IRQ_DISABLED | IRQ_LEVEL; | ||
209 | irq_desc[i+irq_bias].handler = &wildfire_irq_type; | ||
210 | } | ||
211 | |||
212 | setup_irq(32+irq_bias, &isa_enable); | ||
213 | } | ||
214 | |||
215 | static void __init | ||
216 | wildfire_init_irq(void) | ||
217 | { | ||
218 | int qbbno, pcano; | ||
219 | |||
220 | #if 1 | ||
221 | wildfire_init_irq_hw(); | ||
222 | init_i8259a_irqs(); | ||
223 | #endif | ||
224 | |||
225 | for (qbbno = 0; qbbno < WILDFIRE_MAX_QBB; qbbno++) { | ||
226 | if (WILDFIRE_QBB_EXISTS(qbbno)) { | ||
227 | for (pcano = 0; pcano < WILDFIRE_PCA_PER_QBB; pcano++) { | ||
228 | if (WILDFIRE_PCA_EXISTS(qbbno, pcano)) { | ||
229 | wildfire_init_irq_per_pca(qbbno, pcano); | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | static void | ||
237 | wildfire_device_interrupt(unsigned long vector, struct pt_regs * regs) | ||
238 | { | ||
239 | int irq; | ||
240 | |||
241 | irq = (vector - 0x800) >> 4; | ||
242 | |||
243 | /* | ||
244 | * bits 10-8: source QBB ID | ||
245 | * bits 7-6: PCA | ||
246 | * bits 5-0: irq in PCA | ||
247 | */ | ||
248 | |||
249 | handle_irq(irq, regs); | ||
250 | return; | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * PCI Fixup configuration. | ||
255 | * | ||
256 | * Summary per PCA (2 PCI or HIPPI buses): | ||
257 | * | ||
258 | * Bit Meaning | ||
259 | * 0-15 ISA | ||
260 | * | ||
261 | *32 ISA summary | ||
262 | *33 SMI | ||
263 | *34 NMI | ||
264 | *36 builtin QLogic SCSI (or slot 0 if no IO module) | ||
265 | *40 Interrupt Line A from slot 2 PCI0 | ||
266 | *41 Interrupt Line B from slot 2 PCI0 | ||
267 | *42 Interrupt Line C from slot 2 PCI0 | ||
268 | *43 Interrupt Line D from slot 2 PCI0 | ||
269 | *44 Interrupt Line A from slot 3 PCI0 | ||
270 | *45 Interrupt Line B from slot 3 PCI0 | ||
271 | *46 Interrupt Line C from slot 3 PCI0 | ||
272 | *47 Interrupt Line D from slot 3 PCI0 | ||
273 | * | ||
274 | *48 Interrupt Line A from slot 4 PCI1 | ||
275 | *49 Interrupt Line B from slot 4 PCI1 | ||
276 | *50 Interrupt Line C from slot 4 PCI1 | ||
277 | *51 Interrupt Line D from slot 4 PCI1 | ||
278 | *52 Interrupt Line A from slot 5 PCI1 | ||
279 | *53 Interrupt Line B from slot 5 PCI1 | ||
280 | *54 Interrupt Line C from slot 5 PCI1 | ||
281 | *55 Interrupt Line D from slot 5 PCI1 | ||
282 | *56 Interrupt Line A from slot 6 PCI1 | ||
283 | *57 Interrupt Line B from slot 6 PCI1 | ||
284 | *58 Interrupt Line C from slot 6 PCI1 | ||
285 | *50 Interrupt Line D from slot 6 PCI1 | ||
286 | *60 Interrupt Line A from slot 7 PCI1 | ||
287 | *61 Interrupt Line B from slot 7 PCI1 | ||
288 | *62 Interrupt Line C from slot 7 PCI1 | ||
289 | *63 Interrupt Line D from slot 7 PCI1 | ||
290 | * | ||
291 | * | ||
292 | * IdSel | ||
293 | * 0 Cypress Bridge I/O (ISA summary interrupt) | ||
294 | * 1 64 bit PCI 0 option slot 1 (SCSI QLogic builtin) | ||
295 | * 2 64 bit PCI 0 option slot 2 | ||
296 | * 3 64 bit PCI 0 option slot 3 | ||
297 | * 4 64 bit PCI 1 option slot 4 | ||
298 | * 5 64 bit PCI 1 option slot 5 | ||
299 | * 6 64 bit PCI 1 option slot 6 | ||
300 | * 7 64 bit PCI 1 option slot 7 | ||
301 | */ | ||
302 | |||
303 | static int __init | ||
304 | wildfire_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
305 | { | ||
306 | static char irq_tab[8][5] __initdata = { | ||
307 | /*INT INTA INTB INTC INTD */ | ||
308 | { -1, -1, -1, -1, -1}, /* IdSel 0 ISA Bridge */ | ||
309 | { 36, 36, 36+1, 36+2, 36+3}, /* IdSel 1 SCSI builtin */ | ||
310 | { 40, 40, 40+1, 40+2, 40+3}, /* IdSel 2 PCI 0 slot 2 */ | ||
311 | { 44, 44, 44+1, 44+2, 44+3}, /* IdSel 3 PCI 0 slot 3 */ | ||
312 | { 48, 48, 48+1, 48+2, 48+3}, /* IdSel 4 PCI 1 slot 4 */ | ||
313 | { 52, 52, 52+1, 52+2, 52+3}, /* IdSel 5 PCI 1 slot 5 */ | ||
314 | { 56, 56, 56+1, 56+2, 56+3}, /* IdSel 6 PCI 1 slot 6 */ | ||
315 | { 60, 60, 60+1, 60+2, 60+3}, /* IdSel 7 PCI 1 slot 7 */ | ||
316 | }; | ||
317 | long min_idsel = 0, max_idsel = 7, irqs_per_slot = 5; | ||
318 | |||
319 | struct pci_controller *hose = dev->sysdata; | ||
320 | int irq = COMMON_TABLE_LOOKUP; | ||
321 | |||
322 | if (irq > 0) { | ||
323 | int qbbno = hose->index >> 3; | ||
324 | int pcano = (hose->index >> 1) & 3; | ||
325 | irq += (qbbno << 8) + (pcano << 6); | ||
326 | } | ||
327 | return irq; | ||
328 | } | ||
329 | |||
330 | |||
331 | /* | ||
332 | * The System Vectors | ||
333 | */ | ||
334 | |||
335 | struct alpha_machine_vector wildfire_mv __initmv = { | ||
336 | .vector_name = "WILDFIRE", | ||
337 | DO_EV6_MMU, | ||
338 | DO_DEFAULT_RTC, | ||
339 | DO_WILDFIRE_IO, | ||
340 | .machine_check = wildfire_machine_check, | ||
341 | .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS, | ||
342 | .min_io_address = DEFAULT_IO_BASE, | ||
343 | .min_mem_address = DEFAULT_MEM_BASE, | ||
344 | |||
345 | .nr_irqs = WILDFIRE_NR_IRQS, | ||
346 | .device_interrupt = wildfire_device_interrupt, | ||
347 | |||
348 | .init_arch = wildfire_init_arch, | ||
349 | .init_irq = wildfire_init_irq, | ||
350 | .init_rtc = common_init_rtc, | ||
351 | .init_pci = common_init_pci, | ||
352 | .kill_arch = wildfire_kill_arch, | ||
353 | .pci_map_irq = wildfire_map_irq, | ||
354 | .pci_swizzle = common_swizzle, | ||
355 | |||
356 | .pa_to_nid = wildfire_pa_to_nid, | ||
357 | .cpuid_to_nid = wildfire_cpuid_to_nid, | ||
358 | .node_mem_start = wildfire_node_mem_start, | ||
359 | .node_mem_size = wildfire_node_mem_size, | ||
360 | }; | ||
361 | ALIAS_MV(wildfire) | ||
diff --git a/arch/alpha/kernel/systbls.S b/arch/alpha/kernel/systbls.S new file mode 100644 index 000000000000..faab8c2a03eb --- /dev/null +++ b/arch/alpha/kernel/systbls.S | |||
@@ -0,0 +1,468 @@ | |||
1 | /* | ||
2 | * arch/alpha/kernel/systbls.S | ||
3 | * | ||
4 | * The system call table. | ||
5 | */ | ||
6 | |||
7 | #include <linux/config.h> /* CONFIG_OSF4_COMPAT */ | ||
8 | #include <asm/unistd.h> | ||
9 | |||
10 | .data | ||
11 | .align 3 | ||
12 | .globl sys_call_table | ||
13 | sys_call_table: | ||
14 | .quad alpha_ni_syscall /* 0 */ | ||
15 | .quad sys_exit | ||
16 | .quad sys_fork | ||
17 | .quad sys_read | ||
18 | .quad sys_write | ||
19 | .quad alpha_ni_syscall /* 5 */ | ||
20 | .quad sys_close | ||
21 | .quad osf_wait4 | ||
22 | .quad alpha_ni_syscall | ||
23 | .quad sys_link | ||
24 | .quad sys_unlink /* 10 */ | ||
25 | .quad alpha_ni_syscall | ||
26 | .quad sys_chdir | ||
27 | .quad sys_fchdir | ||
28 | .quad sys_mknod | ||
29 | .quad sys_chmod /* 15 */ | ||
30 | .quad sys_chown | ||
31 | .quad osf_brk | ||
32 | .quad alpha_ni_syscall | ||
33 | .quad sys_lseek | ||
34 | .quad sys_getxpid /* 20 */ | ||
35 | .quad osf_mount | ||
36 | .quad sys_umount | ||
37 | .quad sys_setuid | ||
38 | .quad sys_getxuid | ||
39 | .quad alpha_ni_syscall /* 25 */ | ||
40 | .quad sys_ptrace | ||
41 | .quad alpha_ni_syscall | ||
42 | .quad alpha_ni_syscall | ||
43 | .quad alpha_ni_syscall | ||
44 | .quad alpha_ni_syscall /* 30 */ | ||
45 | .quad alpha_ni_syscall | ||
46 | .quad alpha_ni_syscall | ||
47 | .quad sys_access | ||
48 | .quad alpha_ni_syscall | ||
49 | .quad alpha_ni_syscall /* 35 */ | ||
50 | .quad sys_sync | ||
51 | .quad sys_kill | ||
52 | .quad alpha_ni_syscall | ||
53 | .quad sys_setpgid | ||
54 | .quad alpha_ni_syscall /* 40 */ | ||
55 | .quad sys_dup | ||
56 | .quad sys_pipe | ||
57 | .quad osf_set_program_attributes | ||
58 | .quad alpha_ni_syscall | ||
59 | .quad sys_open /* 45 */ | ||
60 | .quad alpha_ni_syscall | ||
61 | .quad sys_getxgid | ||
62 | .quad osf_sigprocmask | ||
63 | .quad alpha_ni_syscall | ||
64 | .quad alpha_ni_syscall /* 50 */ | ||
65 | .quad sys_acct | ||
66 | .quad sys_sigpending | ||
67 | .quad alpha_ni_syscall | ||
68 | .quad sys_ioctl | ||
69 | .quad alpha_ni_syscall /* 55 */ | ||
70 | .quad alpha_ni_syscall | ||
71 | .quad sys_symlink | ||
72 | .quad sys_readlink | ||
73 | .quad sys_execve | ||
74 | .quad sys_umask /* 60 */ | ||
75 | .quad sys_chroot | ||
76 | .quad alpha_ni_syscall | ||
77 | .quad sys_getpgrp | ||
78 | .quad sys_getpagesize | ||
79 | .quad alpha_ni_syscall /* 65 */ | ||
80 | .quad sys_vfork | ||
81 | .quad sys_newstat | ||
82 | .quad sys_newlstat | ||
83 | .quad alpha_ni_syscall | ||
84 | .quad alpha_ni_syscall /* 70 */ | ||
85 | .quad osf_mmap | ||
86 | .quad alpha_ni_syscall | ||
87 | .quad sys_munmap | ||
88 | .quad sys_mprotect | ||
89 | .quad sys_madvise /* 75 */ | ||
90 | .quad sys_vhangup | ||
91 | .quad alpha_ni_syscall | ||
92 | .quad alpha_ni_syscall | ||
93 | .quad sys_getgroups | ||
94 | /* map BSD's setpgrp to sys_setpgid for binary compatibility: */ | ||
95 | .quad sys_setgroups /* 80 */ | ||
96 | .quad alpha_ni_syscall | ||
97 | .quad sys_setpgid | ||
98 | .quad osf_setitimer | ||
99 | .quad alpha_ni_syscall | ||
100 | .quad alpha_ni_syscall /* 85 */ | ||
101 | .quad osf_getitimer | ||
102 | .quad sys_gethostname | ||
103 | .quad sys_sethostname | ||
104 | .quad sys_getdtablesize | ||
105 | .quad sys_dup2 /* 90 */ | ||
106 | .quad sys_newfstat | ||
107 | .quad sys_fcntl | ||
108 | .quad osf_select | ||
109 | .quad sys_poll | ||
110 | .quad sys_fsync /* 95 */ | ||
111 | .quad sys_setpriority | ||
112 | .quad sys_socket | ||
113 | .quad sys_connect | ||
114 | .quad sys_accept | ||
115 | .quad osf_getpriority /* 100 */ | ||
116 | .quad sys_send | ||
117 | .quad sys_recv | ||
118 | .quad sys_sigreturn | ||
119 | .quad sys_bind | ||
120 | .quad sys_setsockopt /* 105 */ | ||
121 | .quad sys_listen | ||
122 | .quad alpha_ni_syscall | ||
123 | .quad alpha_ni_syscall | ||
124 | .quad alpha_ni_syscall | ||
125 | .quad alpha_ni_syscall /* 110 */ | ||
126 | .quad sys_sigsuspend | ||
127 | .quad osf_sigstack | ||
128 | .quad sys_recvmsg | ||
129 | .quad sys_sendmsg | ||
130 | .quad alpha_ni_syscall /* 115 */ | ||
131 | .quad osf_gettimeofday | ||
132 | .quad osf_getrusage | ||
133 | .quad sys_getsockopt | ||
134 | .quad alpha_ni_syscall | ||
135 | #ifdef CONFIG_OSF4_COMPAT | ||
136 | .quad osf_readv /* 120 */ | ||
137 | .quad osf_writev | ||
138 | #else | ||
139 | .quad sys_readv /* 120 */ | ||
140 | .quad sys_writev | ||
141 | #endif | ||
142 | .quad osf_settimeofday | ||
143 | .quad sys_fchown | ||
144 | .quad sys_fchmod | ||
145 | .quad sys_recvfrom /* 125 */ | ||
146 | .quad sys_setreuid | ||
147 | .quad sys_setregid | ||
148 | .quad sys_rename | ||
149 | .quad sys_truncate | ||
150 | .quad sys_ftruncate /* 130 */ | ||
151 | .quad sys_flock | ||
152 | .quad sys_setgid | ||
153 | .quad sys_sendto | ||
154 | .quad sys_shutdown | ||
155 | .quad sys_socketpair /* 135 */ | ||
156 | .quad sys_mkdir | ||
157 | .quad sys_rmdir | ||
158 | .quad osf_utimes | ||
159 | .quad alpha_ni_syscall | ||
160 | .quad alpha_ni_syscall /* 140 */ | ||
161 | .quad sys_getpeername | ||
162 | .quad alpha_ni_syscall | ||
163 | .quad alpha_ni_syscall | ||
164 | .quad sys_getrlimit | ||
165 | .quad sys_setrlimit /* 145 */ | ||
166 | .quad alpha_ni_syscall | ||
167 | .quad sys_setsid | ||
168 | .quad sys_quotactl | ||
169 | .quad alpha_ni_syscall | ||
170 | .quad sys_getsockname /* 150 */ | ||
171 | .quad alpha_ni_syscall | ||
172 | .quad alpha_ni_syscall | ||
173 | .quad alpha_ni_syscall | ||
174 | .quad alpha_ni_syscall | ||
175 | .quad alpha_ni_syscall /* 155 */ | ||
176 | .quad osf_sigaction | ||
177 | .quad alpha_ni_syscall | ||
178 | .quad alpha_ni_syscall | ||
179 | .quad osf_getdirentries | ||
180 | .quad osf_statfs /* 160 */ | ||
181 | .quad osf_fstatfs | ||
182 | .quad alpha_ni_syscall | ||
183 | .quad alpha_ni_syscall | ||
184 | .quad alpha_ni_syscall | ||
185 | .quad osf_getdomainname /* 165 */ | ||
186 | .quad sys_setdomainname | ||
187 | .quad alpha_ni_syscall | ||
188 | .quad alpha_ni_syscall | ||
189 | .quad alpha_ni_syscall | ||
190 | .quad alpha_ni_syscall /* 170 */ | ||
191 | .quad alpha_ni_syscall | ||
192 | .quad alpha_ni_syscall | ||
193 | .quad alpha_ni_syscall | ||
194 | .quad alpha_ni_syscall | ||
195 | .quad alpha_ni_syscall /* 175 */ | ||
196 | .quad alpha_ni_syscall | ||
197 | .quad alpha_ni_syscall | ||
198 | .quad alpha_ni_syscall | ||
199 | .quad alpha_ni_syscall | ||
200 | .quad alpha_ni_syscall /* 180 */ | ||
201 | .quad alpha_ni_syscall | ||
202 | .quad alpha_ni_syscall | ||
203 | .quad alpha_ni_syscall | ||
204 | .quad alpha_ni_syscall | ||
205 | .quad alpha_ni_syscall /* 185 */ | ||
206 | .quad alpha_ni_syscall | ||
207 | .quad alpha_ni_syscall | ||
208 | .quad alpha_ni_syscall | ||
209 | .quad alpha_ni_syscall | ||
210 | .quad alpha_ni_syscall /* 190 */ | ||
211 | .quad alpha_ni_syscall | ||
212 | .quad alpha_ni_syscall | ||
213 | .quad alpha_ni_syscall | ||
214 | .quad alpha_ni_syscall | ||
215 | .quad alpha_ni_syscall /* 195 */ | ||
216 | .quad alpha_ni_syscall | ||
217 | .quad alpha_ni_syscall | ||
218 | .quad alpha_ni_syscall | ||
219 | /* The OSF swapon has two extra arguments, but we ignore them. */ | ||
220 | .quad sys_swapon | ||
221 | .quad sys_msgctl /* 200 */ | ||
222 | .quad sys_msgget | ||
223 | .quad sys_msgrcv | ||
224 | .quad sys_msgsnd | ||
225 | .quad sys_semctl | ||
226 | .quad sys_semget /* 205 */ | ||
227 | .quad sys_semop | ||
228 | .quad osf_utsname | ||
229 | .quad sys_lchown | ||
230 | .quad osf_shmat | ||
231 | .quad sys_shmctl /* 210 */ | ||
232 | .quad sys_shmdt | ||
233 | .quad sys_shmget | ||
234 | .quad alpha_ni_syscall | ||
235 | .quad alpha_ni_syscall | ||
236 | .quad alpha_ni_syscall /* 215 */ | ||
237 | .quad alpha_ni_syscall | ||
238 | .quad sys_msync | ||
239 | .quad alpha_ni_syscall | ||
240 | .quad alpha_ni_syscall | ||
241 | .quad alpha_ni_syscall /* 220 */ | ||
242 | .quad alpha_ni_syscall | ||
243 | .quad alpha_ni_syscall | ||
244 | .quad alpha_ni_syscall | ||
245 | .quad alpha_ni_syscall | ||
246 | .quad alpha_ni_syscall /* 225 */ | ||
247 | .quad alpha_ni_syscall | ||
248 | .quad alpha_ni_syscall | ||
249 | .quad alpha_ni_syscall | ||
250 | .quad alpha_ni_syscall | ||
251 | .quad alpha_ni_syscall /* 230 */ | ||
252 | .quad alpha_ni_syscall | ||
253 | .quad alpha_ni_syscall | ||
254 | .quad sys_getpgid | ||
255 | .quad sys_getsid | ||
256 | .quad sys_sigaltstack /* 235 */ | ||
257 | .quad alpha_ni_syscall | ||
258 | .quad alpha_ni_syscall | ||
259 | .quad alpha_ni_syscall | ||
260 | .quad alpha_ni_syscall | ||
261 | .quad alpha_ni_syscall /* 240 */ | ||
262 | .quad osf_sysinfo | ||
263 | .quad alpha_ni_syscall | ||
264 | .quad alpha_ni_syscall | ||
265 | .quad osf_proplist_syscall | ||
266 | .quad alpha_ni_syscall /* 245 */ | ||
267 | .quad alpha_ni_syscall | ||
268 | .quad alpha_ni_syscall | ||
269 | .quad alpha_ni_syscall | ||
270 | .quad alpha_ni_syscall | ||
271 | .quad alpha_ni_syscall /* 250 */ | ||
272 | .quad osf_usleep_thread | ||
273 | .quad alpha_ni_syscall | ||
274 | .quad alpha_ni_syscall | ||
275 | .quad sys_sysfs | ||
276 | .quad alpha_ni_syscall /* 255 */ | ||
277 | .quad osf_getsysinfo | ||
278 | .quad osf_setsysinfo | ||
279 | .quad alpha_ni_syscall | ||
280 | .quad alpha_ni_syscall | ||
281 | .quad alpha_ni_syscall /* 260 */ | ||
282 | .quad alpha_ni_syscall | ||
283 | .quad alpha_ni_syscall | ||
284 | .quad alpha_ni_syscall | ||
285 | .quad alpha_ni_syscall | ||
286 | .quad alpha_ni_syscall /* 265 */ | ||
287 | .quad alpha_ni_syscall | ||
288 | .quad alpha_ni_syscall | ||
289 | .quad alpha_ni_syscall | ||
290 | .quad alpha_ni_syscall | ||
291 | .quad alpha_ni_syscall /* 270 */ | ||
292 | .quad alpha_ni_syscall | ||
293 | .quad alpha_ni_syscall | ||
294 | .quad alpha_ni_syscall | ||
295 | .quad alpha_ni_syscall | ||
296 | .quad alpha_ni_syscall /* 275 */ | ||
297 | .quad alpha_ni_syscall | ||
298 | .quad alpha_ni_syscall | ||
299 | .quad alpha_ni_syscall | ||
300 | .quad alpha_ni_syscall | ||
301 | .quad alpha_ni_syscall /* 280 */ | ||
302 | .quad alpha_ni_syscall | ||
303 | .quad alpha_ni_syscall | ||
304 | .quad alpha_ni_syscall | ||
305 | .quad alpha_ni_syscall | ||
306 | .quad alpha_ni_syscall /* 285 */ | ||
307 | .quad alpha_ni_syscall | ||
308 | .quad alpha_ni_syscall | ||
309 | .quad alpha_ni_syscall | ||
310 | .quad alpha_ni_syscall | ||
311 | .quad alpha_ni_syscall /* 290 */ | ||
312 | .quad alpha_ni_syscall | ||
313 | .quad alpha_ni_syscall | ||
314 | .quad alpha_ni_syscall | ||
315 | .quad alpha_ni_syscall | ||
316 | .quad alpha_ni_syscall /* 295 */ | ||
317 | .quad alpha_ni_syscall | ||
318 | .quad alpha_ni_syscall | ||
319 | .quad alpha_ni_syscall | ||
320 | .quad alpha_ni_syscall | ||
321 | /* linux-specific system calls start at 300 */ | ||
322 | .quad sys_bdflush /* 300 */ | ||
323 | .quad sys_sethae | ||
324 | .quad sys_mount | ||
325 | .quad sys_old_adjtimex | ||
326 | .quad sys_swapoff | ||
327 | .quad sys_getdents /* 305 */ | ||
328 | .quad sys_ni_syscall /* 306: old create_module */ | ||
329 | .quad sys_init_module | ||
330 | .quad sys_delete_module | ||
331 | .quad sys_ni_syscall /* 309: old get_kernel_syms */ | ||
332 | .quad sys_syslog /* 310 */ | ||
333 | .quad sys_reboot | ||
334 | .quad sys_clone | ||
335 | .quad sys_uselib | ||
336 | .quad sys_mlock | ||
337 | .quad sys_munlock /* 315 */ | ||
338 | .quad sys_mlockall | ||
339 | .quad sys_munlockall | ||
340 | .quad sys_sysinfo | ||
341 | .quad sys_sysctl | ||
342 | .quad sys_ni_syscall /* 320 */ | ||
343 | .quad sys_oldumount | ||
344 | .quad sys_swapon | ||
345 | .quad sys_times | ||
346 | .quad sys_personality | ||
347 | .quad sys_setfsuid /* 325 */ | ||
348 | .quad sys_setfsgid | ||
349 | .quad sys_ustat | ||
350 | .quad sys_statfs | ||
351 | .quad sys_fstatfs | ||
352 | .quad sys_sched_setparam /* 330 */ | ||
353 | .quad sys_sched_getparam | ||
354 | .quad sys_sched_setscheduler | ||
355 | .quad sys_sched_getscheduler | ||
356 | .quad sys_sched_yield | ||
357 | .quad sys_sched_get_priority_max /* 335 */ | ||
358 | .quad sys_sched_get_priority_min | ||
359 | .quad sys_sched_rr_get_interval | ||
360 | .quad sys_ni_syscall /* sys_afs_syscall */ | ||
361 | .quad sys_newuname | ||
362 | .quad sys_nanosleep /* 340 */ | ||
363 | .quad sys_mremap | ||
364 | .quad sys_nfsservctl | ||
365 | .quad sys_setresuid | ||
366 | .quad sys_getresuid | ||
367 | .quad sys_pciconfig_read /* 345 */ | ||
368 | .quad sys_pciconfig_write | ||
369 | .quad sys_ni_syscall /* 347: old query_module */ | ||
370 | .quad sys_prctl | ||
371 | .quad sys_pread64 | ||
372 | .quad sys_pwrite64 /* 350 */ | ||
373 | .quad sys_rt_sigreturn | ||
374 | .quad sys_rt_sigaction | ||
375 | .quad sys_rt_sigprocmask | ||
376 | .quad sys_rt_sigpending | ||
377 | .quad sys_rt_sigtimedwait /* 355 */ | ||
378 | .quad sys_rt_sigqueueinfo | ||
379 | .quad sys_rt_sigsuspend | ||
380 | .quad sys_select | ||
381 | .quad sys_gettimeofday | ||
382 | .quad sys_settimeofday /* 360 */ | ||
383 | .quad sys_getitimer | ||
384 | .quad sys_setitimer | ||
385 | .quad sys_utimes | ||
386 | .quad sys_getrusage | ||
387 | .quad sys_wait4 /* 365 */ | ||
388 | .quad sys_adjtimex | ||
389 | .quad sys_getcwd | ||
390 | .quad sys_capget | ||
391 | .quad sys_capset | ||
392 | .quad sys_sendfile64 /* 370 */ | ||
393 | .quad sys_setresgid | ||
394 | .quad sys_getresgid | ||
395 | .quad sys_ni_syscall /* sys_dipc */ | ||
396 | .quad sys_pivot_root | ||
397 | .quad sys_mincore /* 375 */ | ||
398 | .quad sys_pciconfig_iobase | ||
399 | .quad sys_getdents64 | ||
400 | .quad sys_gettid | ||
401 | .quad sys_readahead | ||
402 | .quad sys_ni_syscall /* 380 */ | ||
403 | .quad sys_tkill | ||
404 | .quad sys_setxattr | ||
405 | .quad sys_lsetxattr | ||
406 | .quad sys_fsetxattr | ||
407 | .quad sys_getxattr /* 385 */ | ||
408 | .quad sys_lgetxattr | ||
409 | .quad sys_fgetxattr | ||
410 | .quad sys_listxattr | ||
411 | .quad sys_llistxattr | ||
412 | .quad sys_flistxattr /* 390 */ | ||
413 | .quad sys_removexattr | ||
414 | .quad sys_lremovexattr | ||
415 | .quad sys_fremovexattr | ||
416 | .quad sys_futex | ||
417 | .quad sys_sched_setaffinity /* 395 */ | ||
418 | .quad sys_sched_getaffinity | ||
419 | .quad sys_ni_syscall /* 397, tux */ | ||
420 | .quad sys_io_setup | ||
421 | .quad sys_io_destroy | ||
422 | .quad sys_io_getevents /* 400 */ | ||
423 | .quad sys_io_submit | ||
424 | .quad sys_io_cancel | ||
425 | .quad sys_ni_syscall /* 403, sys_alloc_hugepages */ | ||
426 | .quad sys_ni_syscall /* 404, sys_free_hugepages */ | ||
427 | .quad sys_exit_group /* 405 */ | ||
428 | .quad sys_lookup_dcookie | ||
429 | .quad sys_epoll_create | ||
430 | .quad sys_epoll_ctl | ||
431 | .quad sys_epoll_wait | ||
432 | .quad sys_remap_file_pages /* 410 */ | ||
433 | .quad sys_set_tid_address | ||
434 | .quad sys_restart_syscall | ||
435 | .quad sys_fadvise64 | ||
436 | .quad sys_timer_create | ||
437 | .quad sys_timer_settime /* 415 */ | ||
438 | .quad sys_timer_gettime | ||
439 | .quad sys_timer_getoverrun | ||
440 | .quad sys_timer_delete | ||
441 | .quad sys_clock_settime | ||
442 | .quad sys_clock_gettime /* 420 */ | ||
443 | .quad sys_clock_getres | ||
444 | .quad sys_clock_nanosleep | ||
445 | .quad sys_semtimedop | ||
446 | .quad sys_tgkill | ||
447 | .quad sys_stat64 /* 425 */ | ||
448 | .quad sys_lstat64 | ||
449 | .quad sys_fstat64 | ||
450 | .quad sys_ni_syscall /* sys_vserver */ | ||
451 | .quad sys_ni_syscall /* sys_mbind */ | ||
452 | .quad sys_ni_syscall /* sys_get_mempolicy */ | ||
453 | .quad sys_ni_syscall /* sys_set_mempolicy */ | ||
454 | .quad sys_mq_open | ||
455 | .quad sys_mq_unlink | ||
456 | .quad sys_mq_timedsend | ||
457 | .quad sys_mq_timedreceive /* 435 */ | ||
458 | .quad sys_mq_notify | ||
459 | .quad sys_mq_getsetattr | ||
460 | .quad sys_waitid | ||
461 | |||
462 | .size sys_call_table, . - sys_call_table | ||
463 | .type sys_call_table, @object | ||
464 | |||
465 | /* Remember to update everything, kids. */ | ||
466 | .ifne (. - sys_call_table) - (NR_SYSCALLS * 8) | ||
467 | .err | ||
468 | .endif | ||
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c new file mode 100644 index 000000000000..8226c5cd788c --- /dev/null +++ b/arch/alpha/kernel/time.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/kernel/time.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1995, 1999, 2000 Linus Torvalds | ||
5 | * | ||
6 | * This file contains the PC-specific time handling details: | ||
7 | * reading the RTC at bootup, etc.. | ||
8 | * 1994-07-02 Alan Modra | ||
9 | * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime | ||
10 | * 1995-03-26 Markus Kuhn | ||
11 | * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 | ||
12 | * precision CMOS clock update | ||
13 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 | ||
14 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | ||
15 | * 1997-01-09 Adrian Sun | ||
16 | * use interval timer if CONFIG_RTC=y | ||
17 | * 1997-10-29 John Bowman (bowman@math.ualberta.ca) | ||
18 | * fixed tick loss calculation in timer_interrupt | ||
19 | * (round system clock to nearest tick instead of truncating) | ||
20 | * fixed algorithm in time_init for getting time from CMOS clock | ||
21 | * 1999-04-16 Thorsten Kranzkowski (dl8bcu@gmx.net) | ||
22 | * fixed algorithm in do_gettimeofday() for calculating the precise time | ||
23 | * from processor cycle counter (now taking lost_ticks into account) | ||
24 | * 2000-08-13 Jan-Benedict Glaw <jbglaw@lug-owl.de> | ||
25 | * Fixed time_init to be aware of epoches != 1900. This prevents | ||
26 | * booting up in 2048 for me;) Code is stolen from rtc.c. | ||
27 | * 2003-06-03 R. Scott Bailey <scott.bailey@eds.com> | ||
28 | * Tighten sanity in time_init from 1% (10,000 PPM) to 250 PPM | ||
29 | */ | ||
30 | #include <linux/config.h> | ||
31 | #include <linux/errno.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/param.h> | ||
36 | #include <linux/string.h> | ||
37 | #include <linux/mm.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/ioport.h> | ||
40 | #include <linux/irq.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/bcd.h> | ||
44 | #include <linux/profile.h> | ||
45 | |||
46 | #include <asm/uaccess.h> | ||
47 | #include <asm/io.h> | ||
48 | #include <asm/hwrpb.h> | ||
49 | #include <asm/8253pit.h> | ||
50 | |||
51 | #include <linux/mc146818rtc.h> | ||
52 | #include <linux/time.h> | ||
53 | #include <linux/timex.h> | ||
54 | |||
55 | #include "proto.h" | ||
56 | #include "irq_impl.h" | ||
57 | |||
58 | u64 jiffies_64 = INITIAL_JIFFIES; | ||
59 | |||
60 | EXPORT_SYMBOL(jiffies_64); | ||
61 | |||
62 | extern unsigned long wall_jiffies; /* kernel/timer.c */ | ||
63 | |||
64 | static int set_rtc_mmss(unsigned long); | ||
65 | |||
66 | DEFINE_SPINLOCK(rtc_lock); | ||
67 | |||
68 | #define TICK_SIZE (tick_nsec / 1000) | ||
69 | |||
70 | /* | ||
71 | * Shift amount by which scaled_ticks_per_cycle is scaled. Shifting | ||
72 | * by 48 gives us 16 bits for HZ while keeping the accuracy good even | ||
73 | * for large CPU clock rates. | ||
74 | */ | ||
75 | #define FIX_SHIFT 48 | ||
76 | |||
77 | /* lump static variables together for more efficient access: */ | ||
78 | static struct { | ||
79 | /* cycle counter last time it got invoked */ | ||
80 | __u32 last_time; | ||
81 | /* ticks/cycle * 2^48 */ | ||
82 | unsigned long scaled_ticks_per_cycle; | ||
83 | /* last time the CMOS clock got updated */ | ||
84 | time_t last_rtc_update; | ||
85 | /* partial unused tick */ | ||
86 | unsigned long partial_tick; | ||
87 | } state; | ||
88 | |||
89 | unsigned long est_cycle_freq; | ||
90 | |||
91 | |||
92 | static inline __u32 rpcc(void) | ||
93 | { | ||
94 | __u32 result; | ||
95 | asm volatile ("rpcc %0" : "=r"(result)); | ||
96 | return result; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Scheduler clock - returns current time in nanosec units. | ||
101 | * | ||
102 | * Copied from ARM code for expediency... ;-} | ||
103 | */ | ||
104 | unsigned long long sched_clock(void) | ||
105 | { | ||
106 | return (unsigned long long)jiffies * (1000000000 / HZ); | ||
107 | } | ||
108 | |||
109 | |||
110 | /* | ||
111 | * timer_interrupt() needs to keep up the real-time clock, | ||
112 | * as well as call the "do_timer()" routine every clocktick | ||
113 | */ | ||
114 | irqreturn_t timer_interrupt(int irq, void *dev, struct pt_regs * regs) | ||
115 | { | ||
116 | unsigned long delta; | ||
117 | __u32 now; | ||
118 | long nticks; | ||
119 | |||
120 | #ifndef CONFIG_SMP | ||
121 | /* Not SMP, do kernel PC profiling here. */ | ||
122 | profile_tick(CPU_PROFILING, regs); | ||
123 | #endif | ||
124 | |||
125 | write_seqlock(&xtime_lock); | ||
126 | |||
127 | /* | ||
128 | * Calculate how many ticks have passed since the last update, | ||
129 | * including any previous partial leftover. Save any resulting | ||
130 | * fraction for the next pass. | ||
131 | */ | ||
132 | now = rpcc(); | ||
133 | delta = now - state.last_time; | ||
134 | state.last_time = now; | ||
135 | delta = delta * state.scaled_ticks_per_cycle + state.partial_tick; | ||
136 | state.partial_tick = delta & ((1UL << FIX_SHIFT) - 1); | ||
137 | nticks = delta >> FIX_SHIFT; | ||
138 | |||
139 | while (nticks > 0) { | ||
140 | do_timer(regs); | ||
141 | #ifndef CONFIG_SMP | ||
142 | update_process_times(user_mode(regs)); | ||
143 | #endif | ||
144 | nticks--; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * If we have an externally synchronized Linux clock, then update | ||
149 | * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | ||
150 | * called as close as possible to 500 ms before the new second starts. | ||
151 | */ | ||
152 | if ((time_status & STA_UNSYNC) == 0 | ||
153 | && xtime.tv_sec > state.last_rtc_update + 660 | ||
154 | && xtime.tv_nsec >= 500000 - ((unsigned) TICK_SIZE) / 2 | ||
155 | && xtime.tv_nsec <= 500000 + ((unsigned) TICK_SIZE) / 2) { | ||
156 | int tmp = set_rtc_mmss(xtime.tv_sec); | ||
157 | state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0); | ||
158 | } | ||
159 | |||
160 | write_sequnlock(&xtime_lock); | ||
161 | return IRQ_HANDLED; | ||
162 | } | ||
163 | |||
164 | void | ||
165 | common_init_rtc(void) | ||
166 | { | ||
167 | unsigned char x; | ||
168 | |||
169 | /* Reset periodic interrupt frequency. */ | ||
170 | x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f; | ||
171 | /* Test includes known working values on various platforms | ||
172 | where 0x26 is wrong; we refuse to change those. */ | ||
173 | if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) { | ||
174 | printk("Setting RTC_FREQ to 1024 Hz (%x)\n", x); | ||
175 | CMOS_WRITE(0x26, RTC_FREQ_SELECT); | ||
176 | } | ||
177 | |||
178 | /* Turn on periodic interrupts. */ | ||
179 | x = CMOS_READ(RTC_CONTROL); | ||
180 | if (!(x & RTC_PIE)) { | ||
181 | printk("Turning on RTC interrupts.\n"); | ||
182 | x |= RTC_PIE; | ||
183 | x &= ~(RTC_AIE | RTC_UIE); | ||
184 | CMOS_WRITE(x, RTC_CONTROL); | ||
185 | } | ||
186 | (void) CMOS_READ(RTC_INTR_FLAGS); | ||
187 | |||
188 | outb(0x36, 0x43); /* pit counter 0: system timer */ | ||
189 | outb(0x00, 0x40); | ||
190 | outb(0x00, 0x40); | ||
191 | |||
192 | outb(0xb6, 0x43); /* pit counter 2: speaker */ | ||
193 | outb(0x31, 0x42); | ||
194 | outb(0x13, 0x42); | ||
195 | |||
196 | init_rtc_irq(); | ||
197 | } | ||
198 | |||
199 | |||
200 | /* Validate a computed cycle counter result against the known bounds for | ||
201 | the given processor core. There's too much brokenness in the way of | ||
202 | timing hardware for any one method to work everywhere. :-( | ||
203 | |||
204 | Return 0 if the result cannot be trusted, otherwise return the argument. */ | ||
205 | |||
206 | static unsigned long __init | ||
207 | validate_cc_value(unsigned long cc) | ||
208 | { | ||
209 | static struct bounds { | ||
210 | unsigned int min, max; | ||
211 | } cpu_hz[] __initdata = { | ||
212 | [EV3_CPU] = { 50000000, 200000000 }, /* guess */ | ||
213 | [EV4_CPU] = { 100000000, 300000000 }, | ||
214 | [LCA4_CPU] = { 100000000, 300000000 }, /* guess */ | ||
215 | [EV45_CPU] = { 200000000, 300000000 }, | ||
216 | [EV5_CPU] = { 250000000, 433000000 }, | ||
217 | [EV56_CPU] = { 333000000, 667000000 }, | ||
218 | [PCA56_CPU] = { 400000000, 600000000 }, /* guess */ | ||
219 | [PCA57_CPU] = { 500000000, 600000000 }, /* guess */ | ||
220 | [EV6_CPU] = { 466000000, 600000000 }, | ||
221 | [EV67_CPU] = { 600000000, 750000000 }, | ||
222 | [EV68AL_CPU] = { 750000000, 940000000 }, | ||
223 | [EV68CB_CPU] = { 1000000000, 1333333333 }, | ||
224 | /* None of the following are shipping as of 2001-11-01. */ | ||
225 | [EV68CX_CPU] = { 1000000000, 1700000000 }, /* guess */ | ||
226 | [EV69_CPU] = { 1000000000, 1700000000 }, /* guess */ | ||
227 | [EV7_CPU] = { 800000000, 1400000000 }, /* guess */ | ||
228 | [EV79_CPU] = { 1000000000, 2000000000 }, /* guess */ | ||
229 | }; | ||
230 | |||
231 | /* Allow for some drift in the crystal. 10MHz is more than enough. */ | ||
232 | const unsigned int deviation = 10000000; | ||
233 | |||
234 | struct percpu_struct *cpu; | ||
235 | unsigned int index; | ||
236 | |||
237 | cpu = (struct percpu_struct *)((char*)hwrpb + hwrpb->processor_offset); | ||
238 | index = cpu->type & 0xffffffff; | ||
239 | |||
240 | /* If index out of bounds, no way to validate. */ | ||
241 | if (index >= sizeof(cpu_hz)/sizeof(cpu_hz[0])) | ||
242 | return cc; | ||
243 | |||
244 | /* If index contains no data, no way to validate. */ | ||
245 | if (cpu_hz[index].max == 0) | ||
246 | return cc; | ||
247 | |||
248 | if (cc < cpu_hz[index].min - deviation | ||
249 | || cc > cpu_hz[index].max + deviation) | ||
250 | return 0; | ||
251 | |||
252 | return cc; | ||
253 | } | ||
254 | |||
255 | |||
256 | /* | ||
257 | * Calibrate CPU clock using legacy 8254 timer/counter. Stolen from | ||
258 | * arch/i386/time.c. | ||
259 | */ | ||
260 | |||
261 | #define CALIBRATE_LATCH 0xffff | ||
262 | #define TIMEOUT_COUNT 0x100000 | ||
263 | |||
264 | static unsigned long __init | ||
265 | calibrate_cc_with_pit(void) | ||
266 | { | ||
267 | int cc, count = 0; | ||
268 | |||
269 | /* Set the Gate high, disable speaker */ | ||
270 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
271 | |||
272 | /* | ||
273 | * Now let's take care of CTC channel 2 | ||
274 | * | ||
275 | * Set the Gate high, program CTC channel 2 for mode 0, | ||
276 | * (interrupt on terminal count mode), binary count, | ||
277 | * load 5 * LATCH count, (LSB and MSB) to begin countdown. | ||
278 | */ | ||
279 | outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ | ||
280 | outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ | ||
281 | outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ | ||
282 | |||
283 | cc = rpcc(); | ||
284 | do { | ||
285 | count++; | ||
286 | } while ((inb(0x61) & 0x20) == 0 && count < TIMEOUT_COUNT); | ||
287 | cc = rpcc() - cc; | ||
288 | |||
289 | /* Error: ECTCNEVERSET or ECPUTOOFAST. */ | ||
290 | if (count <= 1 || count == TIMEOUT_COUNT) | ||
291 | return 0; | ||
292 | |||
293 | return ((long)cc * PIT_TICK_RATE) / (CALIBRATE_LATCH + 1); | ||
294 | } | ||
295 | |||
296 | /* The Linux interpretation of the CMOS clock register contents: | ||
297 | When the Update-In-Progress (UIP) flag goes from 1 to 0, the | ||
298 | RTC registers show the second which has precisely just started. | ||
299 | Let's hope other operating systems interpret the RTC the same way. */ | ||
300 | |||
301 | static unsigned long __init | ||
302 | rpcc_after_update_in_progress(void) | ||
303 | { | ||
304 | do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); | ||
305 | do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); | ||
306 | |||
307 | return rpcc(); | ||
308 | } | ||
309 | |||
310 | void __init | ||
311 | time_init(void) | ||
312 | { | ||
313 | unsigned int year, mon, day, hour, min, sec, cc1, cc2, epoch; | ||
314 | unsigned long cycle_freq, tolerance; | ||
315 | long diff; | ||
316 | |||
317 | /* Calibrate CPU clock -- attempt #1. */ | ||
318 | if (!est_cycle_freq) | ||
319 | est_cycle_freq = validate_cc_value(calibrate_cc_with_pit()); | ||
320 | |||
321 | cc1 = rpcc_after_update_in_progress(); | ||
322 | |||
323 | /* Calibrate CPU clock -- attempt #2. */ | ||
324 | if (!est_cycle_freq) { | ||
325 | cc2 = rpcc_after_update_in_progress(); | ||
326 | est_cycle_freq = validate_cc_value(cc2 - cc1); | ||
327 | cc1 = cc2; | ||
328 | } | ||
329 | |||
330 | cycle_freq = hwrpb->cycle_freq; | ||
331 | if (est_cycle_freq) { | ||
332 | /* If the given value is within 250 PPM of what we calculated, | ||
333 | accept it. Otherwise, use what we found. */ | ||
334 | tolerance = cycle_freq / 4000; | ||
335 | diff = cycle_freq - est_cycle_freq; | ||
336 | if (diff < 0) | ||
337 | diff = -diff; | ||
338 | if ((unsigned long)diff > tolerance) { | ||
339 | cycle_freq = est_cycle_freq; | ||
340 | printk("HWRPB cycle frequency bogus. " | ||
341 | "Estimated %lu Hz\n", cycle_freq); | ||
342 | } else { | ||
343 | est_cycle_freq = 0; | ||
344 | } | ||
345 | } else if (! validate_cc_value (cycle_freq)) { | ||
346 | printk("HWRPB cycle frequency bogus, " | ||
347 | "and unable to estimate a proper value!\n"); | ||
348 | } | ||
349 | |||
350 | /* From John Bowman <bowman@math.ualberta.ca>: allow the values | ||
351 | to settle, as the Update-In-Progress bit going low isn't good | ||
352 | enough on some hardware. 2ms is our guess; we haven't found | ||
353 | bogomips yet, but this is close on a 500Mhz box. */ | ||
354 | __delay(1000000); | ||
355 | |||
356 | sec = CMOS_READ(RTC_SECONDS); | ||
357 | min = CMOS_READ(RTC_MINUTES); | ||
358 | hour = CMOS_READ(RTC_HOURS); | ||
359 | day = CMOS_READ(RTC_DAY_OF_MONTH); | ||
360 | mon = CMOS_READ(RTC_MONTH); | ||
361 | year = CMOS_READ(RTC_YEAR); | ||
362 | |||
363 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { | ||
364 | BCD_TO_BIN(sec); | ||
365 | BCD_TO_BIN(min); | ||
366 | BCD_TO_BIN(hour); | ||
367 | BCD_TO_BIN(day); | ||
368 | BCD_TO_BIN(mon); | ||
369 | BCD_TO_BIN(year); | ||
370 | } | ||
371 | |||
372 | /* PC-like is standard; used for year >= 70 */ | ||
373 | epoch = 1900; | ||
374 | if (year < 20) | ||
375 | epoch = 2000; | ||
376 | else if (year >= 20 && year < 48) | ||
377 | /* NT epoch */ | ||
378 | epoch = 1980; | ||
379 | else if (year >= 48 && year < 70) | ||
380 | /* Digital UNIX epoch */ | ||
381 | epoch = 1952; | ||
382 | |||
383 | printk(KERN_INFO "Using epoch = %d\n", epoch); | ||
384 | |||
385 | if ((year += epoch) < 1970) | ||
386 | year += 100; | ||
387 | |||
388 | xtime.tv_sec = mktime(year, mon, day, hour, min, sec); | ||
389 | xtime.tv_nsec = 0; | ||
390 | |||
391 | wall_to_monotonic.tv_sec -= xtime.tv_sec; | ||
392 | wall_to_monotonic.tv_nsec = 0; | ||
393 | |||
394 | if (HZ > (1<<16)) { | ||
395 | extern void __you_loose (void); | ||
396 | __you_loose(); | ||
397 | } | ||
398 | |||
399 | state.last_time = cc1; | ||
400 | state.scaled_ticks_per_cycle | ||
401 | = ((unsigned long) HZ << FIX_SHIFT) / cycle_freq; | ||
402 | state.last_rtc_update = 0; | ||
403 | state.partial_tick = 0L; | ||
404 | |||
405 | /* Startup the timer source. */ | ||
406 | alpha_mv.init_rtc(); | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * Use the cycle counter to estimate an displacement from the last time | ||
411 | * tick. Unfortunately the Alpha designers made only the low 32-bits of | ||
412 | * the cycle counter active, so we overflow on 8.2 seconds on a 500MHz | ||
413 | * part. So we can't do the "find absolute time in terms of cycles" thing | ||
414 | * that the other ports do. | ||
415 | */ | ||
416 | void | ||
417 | do_gettimeofday(struct timeval *tv) | ||
418 | { | ||
419 | unsigned long flags; | ||
420 | unsigned long sec, usec, lost, seq; | ||
421 | unsigned long delta_cycles, delta_usec, partial_tick; | ||
422 | |||
423 | do { | ||
424 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | ||
425 | |||
426 | delta_cycles = rpcc() - state.last_time; | ||
427 | sec = xtime.tv_sec; | ||
428 | usec = (xtime.tv_nsec / 1000); | ||
429 | partial_tick = state.partial_tick; | ||
430 | lost = jiffies - wall_jiffies; | ||
431 | |||
432 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | ||
433 | |||
434 | #ifdef CONFIG_SMP | ||
435 | /* Until and unless we figure out how to get cpu cycle counters | ||
436 | in sync and keep them there, we can't use the rpcc tricks. */ | ||
437 | delta_usec = lost * (1000000 / HZ); | ||
438 | #else | ||
439 | /* | ||
440 | * usec = cycles * ticks_per_cycle * 2**48 * 1e6 / (2**48 * ticks) | ||
441 | * = cycles * (s_t_p_c) * 1e6 / (2**48 * ticks) | ||
442 | * = cycles * (s_t_p_c) * 15625 / (2**42 * ticks) | ||
443 | * | ||
444 | * which, given a 600MHz cycle and a 1024Hz tick, has a | ||
445 | * dynamic range of about 1.7e17, which is less than the | ||
446 | * 1.8e19 in an unsigned long, so we are safe from overflow. | ||
447 | * | ||
448 | * Round, but with .5 up always, since .5 to even is harder | ||
449 | * with no clear gain. | ||
450 | */ | ||
451 | |||
452 | delta_usec = (delta_cycles * state.scaled_ticks_per_cycle | ||
453 | + partial_tick | ||
454 | + (lost << FIX_SHIFT)) * 15625; | ||
455 | delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; | ||
456 | #endif | ||
457 | |||
458 | usec += delta_usec; | ||
459 | if (usec >= 1000000) { | ||
460 | sec += 1; | ||
461 | usec -= 1000000; | ||
462 | } | ||
463 | |||
464 | tv->tv_sec = sec; | ||
465 | tv->tv_usec = usec; | ||
466 | } | ||
467 | |||
468 | EXPORT_SYMBOL(do_gettimeofday); | ||
469 | |||
470 | int | ||
471 | do_settimeofday(struct timespec *tv) | ||
472 | { | ||
473 | time_t wtm_sec, sec = tv->tv_sec; | ||
474 | long wtm_nsec, nsec = tv->tv_nsec; | ||
475 | unsigned long delta_nsec; | ||
476 | |||
477 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | ||
478 | return -EINVAL; | ||
479 | |||
480 | write_seqlock_irq(&xtime_lock); | ||
481 | |||
482 | /* The offset that is added into time in do_gettimeofday above | ||
483 | must be subtracted out here to keep a coherent view of the | ||
484 | time. Without this, a full-tick error is possible. */ | ||
485 | |||
486 | #ifdef CONFIG_SMP | ||
487 | delta_nsec = (jiffies - wall_jiffies) * (NSEC_PER_SEC / HZ); | ||
488 | #else | ||
489 | delta_nsec = rpcc() - state.last_time; | ||
490 | delta_nsec = (delta_nsec * state.scaled_ticks_per_cycle | ||
491 | + state.partial_tick | ||
492 | + ((jiffies - wall_jiffies) << FIX_SHIFT)) * 15625; | ||
493 | delta_nsec = ((delta_nsec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; | ||
494 | delta_nsec *= 1000; | ||
495 | #endif | ||
496 | |||
497 | nsec -= delta_nsec; | ||
498 | |||
499 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | ||
500 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | ||
501 | |||
502 | set_normalized_timespec(&xtime, sec, nsec); | ||
503 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | ||
504 | |||
505 | time_adjust = 0; /* stop active adjtime() */ | ||
506 | time_status |= STA_UNSYNC; | ||
507 | time_maxerror = NTP_PHASE_LIMIT; | ||
508 | time_esterror = NTP_PHASE_LIMIT; | ||
509 | |||
510 | write_sequnlock_irq(&xtime_lock); | ||
511 | clock_was_set(); | ||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | EXPORT_SYMBOL(do_settimeofday); | ||
516 | |||
517 | |||
518 | /* | ||
519 | * In order to set the CMOS clock precisely, set_rtc_mmss has to be | ||
520 | * called 500 ms after the second nowtime has started, because when | ||
521 | * nowtime is written into the registers of the CMOS clock, it will | ||
522 | * jump to the next second precisely 500 ms later. Check the Motorola | ||
523 | * MC146818A or Dallas DS12887 data sheet for details. | ||
524 | * | ||
525 | * BUG: This routine does not handle hour overflow properly; it just | ||
526 | * sets the minutes. Usually you won't notice until after reboot! | ||
527 | */ | ||
528 | |||
529 | |||
530 | static int | ||
531 | set_rtc_mmss(unsigned long nowtime) | ||
532 | { | ||
533 | int retval = 0; | ||
534 | int real_seconds, real_minutes, cmos_minutes; | ||
535 | unsigned char save_control, save_freq_select; | ||
536 | |||
537 | /* irq are locally disabled here */ | ||
538 | spin_lock(&rtc_lock); | ||
539 | /* Tell the clock it's being set */ | ||
540 | save_control = CMOS_READ(RTC_CONTROL); | ||
541 | CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); | ||
542 | |||
543 | /* Stop and reset prescaler */ | ||
544 | save_freq_select = CMOS_READ(RTC_FREQ_SELECT); | ||
545 | CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); | ||
546 | |||
547 | cmos_minutes = CMOS_READ(RTC_MINUTES); | ||
548 | if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) | ||
549 | BCD_TO_BIN(cmos_minutes); | ||
550 | |||
551 | /* | ||
552 | * since we're only adjusting minutes and seconds, | ||
553 | * don't interfere with hour overflow. This avoids | ||
554 | * messing with unknown time zones but requires your | ||
555 | * RTC not to be off by more than 15 minutes | ||
556 | */ | ||
557 | real_seconds = nowtime % 60; | ||
558 | real_minutes = nowtime / 60; | ||
559 | if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) { | ||
560 | /* correct for half hour time zone */ | ||
561 | real_minutes += 30; | ||
562 | } | ||
563 | real_minutes %= 60; | ||
564 | |||
565 | if (abs(real_minutes - cmos_minutes) < 30) { | ||
566 | if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { | ||
567 | BIN_TO_BCD(real_seconds); | ||
568 | BIN_TO_BCD(real_minutes); | ||
569 | } | ||
570 | CMOS_WRITE(real_seconds,RTC_SECONDS); | ||
571 | CMOS_WRITE(real_minutes,RTC_MINUTES); | ||
572 | } else { | ||
573 | printk(KERN_WARNING | ||
574 | "set_rtc_mmss: can't update from %d to %d\n", | ||
575 | cmos_minutes, real_minutes); | ||
576 | retval = -1; | ||
577 | } | ||
578 | |||
579 | /* The following flags have to be released exactly in this order, | ||
580 | * otherwise the DS12887 (popular MC146818A clone with integrated | ||
581 | * battery and quartz) will not reset the oscillator and will not | ||
582 | * update precisely 500 ms later. You won't find this mentioned in | ||
583 | * the Dallas Semiconductor data sheets, but who believes data | ||
584 | * sheets anyway ... -- Markus Kuhn | ||
585 | */ | ||
586 | CMOS_WRITE(save_control, RTC_CONTROL); | ||
587 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); | ||
588 | spin_unlock(&rtc_lock); | ||
589 | |||
590 | return retval; | ||
591 | } | ||
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c new file mode 100644 index 000000000000..fd7bd17cc960 --- /dev/null +++ b/arch/alpha/kernel/traps.c | |||
@@ -0,0 +1,1092 @@ | |||
1 | /* | ||
2 | * arch/alpha/kernel/traps.c | ||
3 | * | ||
4 | * (C) Copyright 1994 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This file initializes the trap entry points | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/tty.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kallsyms.h> | ||
20 | |||
21 | #include <asm/gentrap.h> | ||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/unaligned.h> | ||
24 | #include <asm/sysinfo.h> | ||
25 | #include <asm/hwrpb.h> | ||
26 | #include <asm/mmu_context.h> | ||
27 | |||
28 | #include "proto.h" | ||
29 | |||
30 | /* Work-around for some SRMs which mishandle opDEC faults. */ | ||
31 | |||
32 | static int opDEC_fix; | ||
33 | |||
34 | static void __init | ||
35 | opDEC_check(void) | ||
36 | { | ||
37 | __asm__ __volatile__ ( | ||
38 | /* Load the address of... */ | ||
39 | " br $16, 1f\n" | ||
40 | /* A stub instruction fault handler. Just add 4 to the | ||
41 | pc and continue. */ | ||
42 | " ldq $16, 8($sp)\n" | ||
43 | " addq $16, 4, $16\n" | ||
44 | " stq $16, 8($sp)\n" | ||
45 | " call_pal %[rti]\n" | ||
46 | /* Install the instruction fault handler. */ | ||
47 | "1: lda $17, 3\n" | ||
48 | " call_pal %[wrent]\n" | ||
49 | /* With that in place, the fault from the round-to-minf fp | ||
50 | insn will arrive either at the "lda 4" insn (bad) or one | ||
51 | past that (good). This places the correct fixup in %0. */ | ||
52 | " lda %[fix], 0\n" | ||
53 | " cvttq/svm $f31,$f31\n" | ||
54 | " lda %[fix], 4" | ||
55 | : [fix] "=r" (opDEC_fix) | ||
56 | : [rti] "n" (PAL_rti), [wrent] "n" (PAL_wrent) | ||
57 | : "$0", "$1", "$16", "$17", "$22", "$23", "$24", "$25"); | ||
58 | |||
59 | if (opDEC_fix) | ||
60 | printk("opDEC fixup enabled.\n"); | ||
61 | } | ||
62 | |||
63 | void | ||
64 | dik_show_regs(struct pt_regs *regs, unsigned long *r9_15) | ||
65 | { | ||
66 | printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx %s\n", | ||
67 | regs->pc, regs->r26, regs->ps, print_tainted()); | ||
68 | print_symbol("pc is at %s\n", regs->pc); | ||
69 | print_symbol("ra is at %s\n", regs->r26 ); | ||
70 | printk("v0 = %016lx t0 = %016lx t1 = %016lx\n", | ||
71 | regs->r0, regs->r1, regs->r2); | ||
72 | printk("t2 = %016lx t3 = %016lx t4 = %016lx\n", | ||
73 | regs->r3, regs->r4, regs->r5); | ||
74 | printk("t5 = %016lx t6 = %016lx t7 = %016lx\n", | ||
75 | regs->r6, regs->r7, regs->r8); | ||
76 | |||
77 | if (r9_15) { | ||
78 | printk("s0 = %016lx s1 = %016lx s2 = %016lx\n", | ||
79 | r9_15[9], r9_15[10], r9_15[11]); | ||
80 | printk("s3 = %016lx s4 = %016lx s5 = %016lx\n", | ||
81 | r9_15[12], r9_15[13], r9_15[14]); | ||
82 | printk("s6 = %016lx\n", r9_15[15]); | ||
83 | } | ||
84 | |||
85 | printk("a0 = %016lx a1 = %016lx a2 = %016lx\n", | ||
86 | regs->r16, regs->r17, regs->r18); | ||
87 | printk("a3 = %016lx a4 = %016lx a5 = %016lx\n", | ||
88 | regs->r19, regs->r20, regs->r21); | ||
89 | printk("t8 = %016lx t9 = %016lx t10= %016lx\n", | ||
90 | regs->r22, regs->r23, regs->r24); | ||
91 | printk("t11= %016lx pv = %016lx at = %016lx\n", | ||
92 | regs->r25, regs->r27, regs->r28); | ||
93 | printk("gp = %016lx sp = %p\n", regs->gp, regs+1); | ||
94 | #if 0 | ||
95 | __halt(); | ||
96 | #endif | ||
97 | } | ||
98 | |||
99 | #if 0 | ||
100 | static char * ireg_name[] = {"v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", | ||
101 | "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", | ||
102 | "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", | ||
103 | "t10", "t11", "ra", "pv", "at", "gp", "sp", "zero"}; | ||
104 | #endif | ||
105 | |||
106 | static void | ||
107 | dik_show_code(unsigned int *pc) | ||
108 | { | ||
109 | long i; | ||
110 | |||
111 | printk("Code:"); | ||
112 | for (i = -6; i < 2; i++) { | ||
113 | unsigned int insn; | ||
114 | if (__get_user(insn, (unsigned int __user *)pc + i)) | ||
115 | break; | ||
116 | printk("%c%08x%c", i ? ' ' : '<', insn, i ? ' ' : '>'); | ||
117 | } | ||
118 | printk("\n"); | ||
119 | } | ||
120 | |||
121 | static void | ||
122 | dik_show_trace(unsigned long *sp) | ||
123 | { | ||
124 | long i = 0; | ||
125 | printk("Trace:\n"); | ||
126 | while (0x1ff8 & (unsigned long) sp) { | ||
127 | extern char _stext[], _etext[]; | ||
128 | unsigned long tmp = *sp; | ||
129 | sp++; | ||
130 | if (tmp < (unsigned long) &_stext) | ||
131 | continue; | ||
132 | if (tmp >= (unsigned long) &_etext) | ||
133 | continue; | ||
134 | printk("[<%lx>]", tmp); | ||
135 | print_symbol(" %s", tmp); | ||
136 | printk("\n"); | ||
137 | if (i > 40) { | ||
138 | printk(" ..."); | ||
139 | break; | ||
140 | } | ||
141 | } | ||
142 | printk("\n"); | ||
143 | } | ||
144 | |||
145 | static int kstack_depth_to_print = 24; | ||
146 | |||
147 | void show_stack(struct task_struct *task, unsigned long *sp) | ||
148 | { | ||
149 | unsigned long *stack; | ||
150 | int i; | ||
151 | |||
152 | /* | ||
153 | * debugging aid: "show_stack(NULL);" prints the | ||
154 | * back trace for this cpu. | ||
155 | */ | ||
156 | if(sp==NULL) | ||
157 | sp=(unsigned long*)&sp; | ||
158 | |||
159 | stack = sp; | ||
160 | for(i=0; i < kstack_depth_to_print; i++) { | ||
161 | if (((long) stack & (THREAD_SIZE-1)) == 0) | ||
162 | break; | ||
163 | if (i && ((i % 4) == 0)) | ||
164 | printk("\n "); | ||
165 | printk("%016lx ", *stack++); | ||
166 | } | ||
167 | printk("\n"); | ||
168 | dik_show_trace(sp); | ||
169 | } | ||
170 | |||
171 | void dump_stack(void) | ||
172 | { | ||
173 | show_stack(NULL, NULL); | ||
174 | } | ||
175 | |||
176 | EXPORT_SYMBOL(dump_stack); | ||
177 | |||
178 | void | ||
179 | die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) | ||
180 | { | ||
181 | if (regs->ps & 8) | ||
182 | return; | ||
183 | #ifdef CONFIG_SMP | ||
184 | printk("CPU %d ", hard_smp_processor_id()); | ||
185 | #endif | ||
186 | printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); | ||
187 | dik_show_regs(regs, r9_15); | ||
188 | dik_show_trace((unsigned long *)(regs+1)); | ||
189 | dik_show_code((unsigned int *)regs->pc); | ||
190 | |||
191 | if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { | ||
192 | printk("die_if_kernel recursion detected.\n"); | ||
193 | local_irq_enable(); | ||
194 | while (1); | ||
195 | } | ||
196 | do_exit(SIGSEGV); | ||
197 | } | ||
198 | |||
199 | #ifndef CONFIG_MATHEMU | ||
200 | static long dummy_emul(void) { return 0; } | ||
201 | long (*alpha_fp_emul_imprecise)(struct pt_regs *regs, unsigned long writemask) | ||
202 | = (void *)dummy_emul; | ||
203 | long (*alpha_fp_emul) (unsigned long pc) | ||
204 | = (void *)dummy_emul; | ||
205 | #else | ||
206 | long alpha_fp_emul_imprecise(struct pt_regs *regs, unsigned long writemask); | ||
207 | long alpha_fp_emul (unsigned long pc); | ||
208 | #endif | ||
209 | |||
210 | asmlinkage void | ||
211 | do_entArith(unsigned long summary, unsigned long write_mask, | ||
212 | struct pt_regs *regs) | ||
213 | { | ||
214 | long si_code = FPE_FLTINV; | ||
215 | siginfo_t info; | ||
216 | |||
217 | if (summary & 1) { | ||
218 | /* Software-completion summary bit is set, so try to | ||
219 | emulate the instruction. If the processor supports | ||
220 | precise exceptions, we don't have to search. */ | ||
221 | if (!amask(AMASK_PRECISE_TRAP)) | ||
222 | si_code = alpha_fp_emul(regs->pc - 4); | ||
223 | else | ||
224 | si_code = alpha_fp_emul_imprecise(regs, write_mask); | ||
225 | if (si_code == 0) | ||
226 | return; | ||
227 | } | ||
228 | die_if_kernel("Arithmetic fault", regs, 0, NULL); | ||
229 | |||
230 | info.si_signo = SIGFPE; | ||
231 | info.si_errno = 0; | ||
232 | info.si_code = si_code; | ||
233 | info.si_addr = (void __user *) regs->pc; | ||
234 | send_sig_info(SIGFPE, &info, current); | ||
235 | } | ||
236 | |||
237 | asmlinkage void | ||
238 | do_entIF(unsigned long type, struct pt_regs *regs) | ||
239 | { | ||
240 | siginfo_t info; | ||
241 | int signo, code; | ||
242 | |||
243 | if (regs->ps == 0) { | ||
244 | if (type == 1) { | ||
245 | const unsigned int *data | ||
246 | = (const unsigned int *) regs->pc; | ||
247 | printk("Kernel bug at %s:%d\n", | ||
248 | (const char *)(data[1] | (long)data[2] << 32), | ||
249 | data[0]); | ||
250 | } | ||
251 | die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"), | ||
252 | regs, type, NULL); | ||
253 | } | ||
254 | |||
255 | switch (type) { | ||
256 | case 0: /* breakpoint */ | ||
257 | info.si_signo = SIGTRAP; | ||
258 | info.si_errno = 0; | ||
259 | info.si_code = TRAP_BRKPT; | ||
260 | info.si_trapno = 0; | ||
261 | info.si_addr = (void __user *) regs->pc; | ||
262 | |||
263 | if (ptrace_cancel_bpt(current)) { | ||
264 | regs->pc -= 4; /* make pc point to former bpt */ | ||
265 | } | ||
266 | |||
267 | send_sig_info(SIGTRAP, &info, current); | ||
268 | return; | ||
269 | |||
270 | case 1: /* bugcheck */ | ||
271 | info.si_signo = SIGTRAP; | ||
272 | info.si_errno = 0; | ||
273 | info.si_code = __SI_FAULT; | ||
274 | info.si_addr = (void __user *) regs->pc; | ||
275 | info.si_trapno = 0; | ||
276 | send_sig_info(SIGTRAP, &info, current); | ||
277 | return; | ||
278 | |||
279 | case 2: /* gentrap */ | ||
280 | info.si_addr = (void __user *) regs->pc; | ||
281 | info.si_trapno = regs->r16; | ||
282 | switch ((long) regs->r16) { | ||
283 | case GEN_INTOVF: | ||
284 | signo = SIGFPE; | ||
285 | code = FPE_INTOVF; | ||
286 | break; | ||
287 | case GEN_INTDIV: | ||
288 | signo = SIGFPE; | ||
289 | code = FPE_INTDIV; | ||
290 | break; | ||
291 | case GEN_FLTOVF: | ||
292 | signo = SIGFPE; | ||
293 | code = FPE_FLTOVF; | ||
294 | break; | ||
295 | case GEN_FLTDIV: | ||
296 | signo = SIGFPE; | ||
297 | code = FPE_FLTDIV; | ||
298 | break; | ||
299 | case GEN_FLTUND: | ||
300 | signo = SIGFPE; | ||
301 | code = FPE_FLTUND; | ||
302 | break; | ||
303 | case GEN_FLTINV: | ||
304 | signo = SIGFPE; | ||
305 | code = FPE_FLTINV; | ||
306 | break; | ||
307 | case GEN_FLTINE: | ||
308 | signo = SIGFPE; | ||
309 | code = FPE_FLTRES; | ||
310 | break; | ||
311 | case GEN_ROPRAND: | ||
312 | signo = SIGFPE; | ||
313 | code = __SI_FAULT; | ||
314 | break; | ||
315 | |||
316 | case GEN_DECOVF: | ||
317 | case GEN_DECDIV: | ||
318 | case GEN_DECINV: | ||
319 | case GEN_ASSERTERR: | ||
320 | case GEN_NULPTRERR: | ||
321 | case GEN_STKOVF: | ||
322 | case GEN_STRLENERR: | ||
323 | case GEN_SUBSTRERR: | ||
324 | case GEN_RANGERR: | ||
325 | case GEN_SUBRNG: | ||
326 | case GEN_SUBRNG1: | ||
327 | case GEN_SUBRNG2: | ||
328 | case GEN_SUBRNG3: | ||
329 | case GEN_SUBRNG4: | ||
330 | case GEN_SUBRNG5: | ||
331 | case GEN_SUBRNG6: | ||
332 | case GEN_SUBRNG7: | ||
333 | default: | ||
334 | signo = SIGTRAP; | ||
335 | code = __SI_FAULT; | ||
336 | break; | ||
337 | } | ||
338 | |||
339 | info.si_signo = signo; | ||
340 | info.si_errno = 0; | ||
341 | info.si_code = code; | ||
342 | info.si_addr = (void __user *) regs->pc; | ||
343 | send_sig_info(signo, &info, current); | ||
344 | return; | ||
345 | |||
346 | case 4: /* opDEC */ | ||
347 | if (implver() == IMPLVER_EV4) { | ||
348 | long si_code; | ||
349 | |||
350 | /* The some versions of SRM do not handle | ||
351 | the opDEC properly - they return the PC of the | ||
352 | opDEC fault, not the instruction after as the | ||
353 | Alpha architecture requires. Here we fix it up. | ||
354 | We do this by intentionally causing an opDEC | ||
355 | fault during the boot sequence and testing if | ||
356 | we get the correct PC. If not, we set a flag | ||
357 | to correct it every time through. */ | ||
358 | regs->pc += opDEC_fix; | ||
359 | |||
360 | /* EV4 does not implement anything except normal | ||
361 | rounding. Everything else will come here as | ||
362 | an illegal instruction. Emulate them. */ | ||
363 | si_code = alpha_fp_emul(regs->pc - 4); | ||
364 | if (si_code == 0) | ||
365 | return; | ||
366 | if (si_code > 0) { | ||
367 | info.si_signo = SIGFPE; | ||
368 | info.si_errno = 0; | ||
369 | info.si_code = si_code; | ||
370 | info.si_addr = (void __user *) regs->pc; | ||
371 | send_sig_info(SIGFPE, &info, current); | ||
372 | return; | ||
373 | } | ||
374 | } | ||
375 | break; | ||
376 | |||
377 | case 3: /* FEN fault */ | ||
378 | /* Irritating users can call PAL_clrfen to disable the | ||
379 | FPU for the process. The kernel will then trap in | ||
380 | do_switch_stack and undo_switch_stack when we try | ||
381 | to save and restore the FP registers. | ||
382 | |||
383 | Given that GCC by default generates code that uses the | ||
384 | FP registers, PAL_clrfen is not useful except for DoS | ||
385 | attacks. So turn the bleeding FPU back on and be done | ||
386 | with it. */ | ||
387 | current_thread_info()->pcb.flags |= 1; | ||
388 | __reload_thread(¤t_thread_info()->pcb); | ||
389 | return; | ||
390 | |||
391 | case 5: /* illoc */ | ||
392 | default: /* unexpected instruction-fault type */ | ||
393 | ; | ||
394 | } | ||
395 | |||
396 | info.si_signo = SIGILL; | ||
397 | info.si_errno = 0; | ||
398 | info.si_code = ILL_ILLOPC; | ||
399 | info.si_addr = (void __user *) regs->pc; | ||
400 | send_sig_info(SIGILL, &info, current); | ||
401 | } | ||
402 | |||
403 | /* There is an ifdef in the PALcode in MILO that enables a | ||
404 | "kernel debugging entry point" as an unprivileged call_pal. | ||
405 | |||
406 | We don't want to have anything to do with it, but unfortunately | ||
407 | several versions of MILO included in distributions have it enabled, | ||
408 | and if we don't put something on the entry point we'll oops. */ | ||
409 | |||
410 | asmlinkage void | ||
411 | do_entDbg(struct pt_regs *regs) | ||
412 | { | ||
413 | siginfo_t info; | ||
414 | |||
415 | die_if_kernel("Instruction fault", regs, 0, NULL); | ||
416 | |||
417 | info.si_signo = SIGILL; | ||
418 | info.si_errno = 0; | ||
419 | info.si_code = ILL_ILLOPC; | ||
420 | info.si_addr = (void __user *) regs->pc; | ||
421 | force_sig_info(SIGILL, &info, current); | ||
422 | } | ||
423 | |||
424 | |||
425 | /* | ||
426 | * entUna has a different register layout to be reasonably simple. It | ||
427 | * needs access to all the integer registers (the kernel doesn't use | ||
428 | * fp-regs), and it needs to have them in order for simpler access. | ||
429 | * | ||
430 | * Due to the non-standard register layout (and because we don't want | ||
431 | * to handle floating-point regs), user-mode unaligned accesses are | ||
432 | * handled separately by do_entUnaUser below. | ||
433 | * | ||
434 | * Oh, btw, we don't handle the "gp" register correctly, but if we fault | ||
435 | * on a gp-register unaligned load/store, something is _very_ wrong | ||
436 | * in the kernel anyway.. | ||
437 | */ | ||
438 | struct allregs { | ||
439 | unsigned long regs[32]; | ||
440 | unsigned long ps, pc, gp, a0, a1, a2; | ||
441 | }; | ||
442 | |||
443 | struct unaligned_stat { | ||
444 | unsigned long count, va, pc; | ||
445 | } unaligned[2]; | ||
446 | |||
447 | |||
448 | /* Macro for exception fixup code to access integer registers. */ | ||
449 | #define una_reg(r) (regs.regs[(r) >= 16 && (r) <= 18 ? (r)+19 : (r)]) | ||
450 | |||
451 | |||
452 | asmlinkage void | ||
453 | do_entUna(void * va, unsigned long opcode, unsigned long reg, | ||
454 | unsigned long a3, unsigned long a4, unsigned long a5, | ||
455 | struct allregs regs) | ||
456 | { | ||
457 | long error, tmp1, tmp2, tmp3, tmp4; | ||
458 | unsigned long pc = regs.pc - 4; | ||
459 | const struct exception_table_entry *fixup; | ||
460 | |||
461 | unaligned[0].count++; | ||
462 | unaligned[0].va = (unsigned long) va; | ||
463 | unaligned[0].pc = pc; | ||
464 | |||
465 | /* We don't want to use the generic get/put unaligned macros as | ||
466 | we want to trap exceptions. Only if we actually get an | ||
467 | exception will we decide whether we should have caught it. */ | ||
468 | |||
469 | switch (opcode) { | ||
470 | case 0x0c: /* ldwu */ | ||
471 | __asm__ __volatile__( | ||
472 | "1: ldq_u %1,0(%3)\n" | ||
473 | "2: ldq_u %2,1(%3)\n" | ||
474 | " extwl %1,%3,%1\n" | ||
475 | " extwh %2,%3,%2\n" | ||
476 | "3:\n" | ||
477 | ".section __ex_table,\"a\"\n" | ||
478 | " .long 1b - .\n" | ||
479 | " lda %1,3b-1b(%0)\n" | ||
480 | " .long 2b - .\n" | ||
481 | " lda %2,3b-2b(%0)\n" | ||
482 | ".previous" | ||
483 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
484 | : "r"(va), "0"(0)); | ||
485 | if (error) | ||
486 | goto got_exception; | ||
487 | una_reg(reg) = tmp1|tmp2; | ||
488 | return; | ||
489 | |||
490 | case 0x28: /* ldl */ | ||
491 | __asm__ __volatile__( | ||
492 | "1: ldq_u %1,0(%3)\n" | ||
493 | "2: ldq_u %2,3(%3)\n" | ||
494 | " extll %1,%3,%1\n" | ||
495 | " extlh %2,%3,%2\n" | ||
496 | "3:\n" | ||
497 | ".section __ex_table,\"a\"\n" | ||
498 | " .long 1b - .\n" | ||
499 | " lda %1,3b-1b(%0)\n" | ||
500 | " .long 2b - .\n" | ||
501 | " lda %2,3b-2b(%0)\n" | ||
502 | ".previous" | ||
503 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
504 | : "r"(va), "0"(0)); | ||
505 | if (error) | ||
506 | goto got_exception; | ||
507 | una_reg(reg) = (int)(tmp1|tmp2); | ||
508 | return; | ||
509 | |||
510 | case 0x29: /* ldq */ | ||
511 | __asm__ __volatile__( | ||
512 | "1: ldq_u %1,0(%3)\n" | ||
513 | "2: ldq_u %2,7(%3)\n" | ||
514 | " extql %1,%3,%1\n" | ||
515 | " extqh %2,%3,%2\n" | ||
516 | "3:\n" | ||
517 | ".section __ex_table,\"a\"\n" | ||
518 | " .long 1b - .\n" | ||
519 | " lda %1,3b-1b(%0)\n" | ||
520 | " .long 2b - .\n" | ||
521 | " lda %2,3b-2b(%0)\n" | ||
522 | ".previous" | ||
523 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
524 | : "r"(va), "0"(0)); | ||
525 | if (error) | ||
526 | goto got_exception; | ||
527 | una_reg(reg) = tmp1|tmp2; | ||
528 | return; | ||
529 | |||
530 | /* Note that the store sequences do not indicate that they change | ||
531 | memory because it _should_ be affecting nothing in this context. | ||
532 | (Otherwise we have other, much larger, problems.) */ | ||
533 | case 0x0d: /* stw */ | ||
534 | __asm__ __volatile__( | ||
535 | "1: ldq_u %2,1(%5)\n" | ||
536 | "2: ldq_u %1,0(%5)\n" | ||
537 | " inswh %6,%5,%4\n" | ||
538 | " inswl %6,%5,%3\n" | ||
539 | " mskwh %2,%5,%2\n" | ||
540 | " mskwl %1,%5,%1\n" | ||
541 | " or %2,%4,%2\n" | ||
542 | " or %1,%3,%1\n" | ||
543 | "3: stq_u %2,1(%5)\n" | ||
544 | "4: stq_u %1,0(%5)\n" | ||
545 | "5:\n" | ||
546 | ".section __ex_table,\"a\"\n" | ||
547 | " .long 1b - .\n" | ||
548 | " lda %2,5b-1b(%0)\n" | ||
549 | " .long 2b - .\n" | ||
550 | " lda %1,5b-2b(%0)\n" | ||
551 | " .long 3b - .\n" | ||
552 | " lda $31,5b-3b(%0)\n" | ||
553 | " .long 4b - .\n" | ||
554 | " lda $31,5b-4b(%0)\n" | ||
555 | ".previous" | ||
556 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
557 | "=&r"(tmp3), "=&r"(tmp4) | ||
558 | : "r"(va), "r"(una_reg(reg)), "0"(0)); | ||
559 | if (error) | ||
560 | goto got_exception; | ||
561 | return; | ||
562 | |||
563 | case 0x2c: /* stl */ | ||
564 | __asm__ __volatile__( | ||
565 | "1: ldq_u %2,3(%5)\n" | ||
566 | "2: ldq_u %1,0(%5)\n" | ||
567 | " inslh %6,%5,%4\n" | ||
568 | " insll %6,%5,%3\n" | ||
569 | " msklh %2,%5,%2\n" | ||
570 | " mskll %1,%5,%1\n" | ||
571 | " or %2,%4,%2\n" | ||
572 | " or %1,%3,%1\n" | ||
573 | "3: stq_u %2,3(%5)\n" | ||
574 | "4: stq_u %1,0(%5)\n" | ||
575 | "5:\n" | ||
576 | ".section __ex_table,\"a\"\n" | ||
577 | " .long 1b - .\n" | ||
578 | " lda %2,5b-1b(%0)\n" | ||
579 | " .long 2b - .\n" | ||
580 | " lda %1,5b-2b(%0)\n" | ||
581 | " .long 3b - .\n" | ||
582 | " lda $31,5b-3b(%0)\n" | ||
583 | " .long 4b - .\n" | ||
584 | " lda $31,5b-4b(%0)\n" | ||
585 | ".previous" | ||
586 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
587 | "=&r"(tmp3), "=&r"(tmp4) | ||
588 | : "r"(va), "r"(una_reg(reg)), "0"(0)); | ||
589 | if (error) | ||
590 | goto got_exception; | ||
591 | return; | ||
592 | |||
593 | case 0x2d: /* stq */ | ||
594 | __asm__ __volatile__( | ||
595 | "1: ldq_u %2,7(%5)\n" | ||
596 | "2: ldq_u %1,0(%5)\n" | ||
597 | " insqh %6,%5,%4\n" | ||
598 | " insql %6,%5,%3\n" | ||
599 | " mskqh %2,%5,%2\n" | ||
600 | " mskql %1,%5,%1\n" | ||
601 | " or %2,%4,%2\n" | ||
602 | " or %1,%3,%1\n" | ||
603 | "3: stq_u %2,7(%5)\n" | ||
604 | "4: stq_u %1,0(%5)\n" | ||
605 | "5:\n" | ||
606 | ".section __ex_table,\"a\"\n\t" | ||
607 | " .long 1b - .\n" | ||
608 | " lda %2,5b-1b(%0)\n" | ||
609 | " .long 2b - .\n" | ||
610 | " lda %1,5b-2b(%0)\n" | ||
611 | " .long 3b - .\n" | ||
612 | " lda $31,5b-3b(%0)\n" | ||
613 | " .long 4b - .\n" | ||
614 | " lda $31,5b-4b(%0)\n" | ||
615 | ".previous" | ||
616 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
617 | "=&r"(tmp3), "=&r"(tmp4) | ||
618 | : "r"(va), "r"(una_reg(reg)), "0"(0)); | ||
619 | if (error) | ||
620 | goto got_exception; | ||
621 | return; | ||
622 | } | ||
623 | |||
624 | lock_kernel(); | ||
625 | printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n", | ||
626 | pc, va, opcode, reg); | ||
627 | do_exit(SIGSEGV); | ||
628 | |||
629 | got_exception: | ||
630 | /* Ok, we caught the exception, but we don't want it. Is there | ||
631 | someone to pass it along to? */ | ||
632 | if ((fixup = search_exception_tables(pc)) != 0) { | ||
633 | unsigned long newpc; | ||
634 | newpc = fixup_exception(una_reg, fixup, pc); | ||
635 | |||
636 | printk("Forwarding unaligned exception at %lx (%lx)\n", | ||
637 | pc, newpc); | ||
638 | |||
639 | (®s)->pc = newpc; | ||
640 | return; | ||
641 | } | ||
642 | |||
643 | /* | ||
644 | * Yikes! No one to forward the exception to. | ||
645 | * Since the registers are in a weird format, dump them ourselves. | ||
646 | */ | ||
647 | lock_kernel(); | ||
648 | |||
649 | printk("%s(%d): unhandled unaligned exception\n", | ||
650 | current->comm, current->pid); | ||
651 | |||
652 | printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx\n", | ||
653 | pc, una_reg(26), regs.ps); | ||
654 | printk("r0 = %016lx r1 = %016lx r2 = %016lx\n", | ||
655 | una_reg(0), una_reg(1), una_reg(2)); | ||
656 | printk("r3 = %016lx r4 = %016lx r5 = %016lx\n", | ||
657 | una_reg(3), una_reg(4), una_reg(5)); | ||
658 | printk("r6 = %016lx r7 = %016lx r8 = %016lx\n", | ||
659 | una_reg(6), una_reg(7), una_reg(8)); | ||
660 | printk("r9 = %016lx r10= %016lx r11= %016lx\n", | ||
661 | una_reg(9), una_reg(10), una_reg(11)); | ||
662 | printk("r12= %016lx r13= %016lx r14= %016lx\n", | ||
663 | una_reg(12), una_reg(13), una_reg(14)); | ||
664 | printk("r15= %016lx\n", una_reg(15)); | ||
665 | printk("r16= %016lx r17= %016lx r18= %016lx\n", | ||
666 | una_reg(16), una_reg(17), una_reg(18)); | ||
667 | printk("r19= %016lx r20= %016lx r21= %016lx\n", | ||
668 | una_reg(19), una_reg(20), una_reg(21)); | ||
669 | printk("r22= %016lx r23= %016lx r24= %016lx\n", | ||
670 | una_reg(22), una_reg(23), una_reg(24)); | ||
671 | printk("r25= %016lx r27= %016lx r28= %016lx\n", | ||
672 | una_reg(25), una_reg(27), una_reg(28)); | ||
673 | printk("gp = %016lx sp = %p\n", regs.gp, ®s+1); | ||
674 | |||
675 | dik_show_code((unsigned int *)pc); | ||
676 | dik_show_trace((unsigned long *)(®s+1)); | ||
677 | |||
678 | if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { | ||
679 | printk("die_if_kernel recursion detected.\n"); | ||
680 | local_irq_enable(); | ||
681 | while (1); | ||
682 | } | ||
683 | do_exit(SIGSEGV); | ||
684 | } | ||
685 | |||
686 | /* | ||
687 | * Convert an s-floating point value in memory format to the | ||
688 | * corresponding value in register format. The exponent | ||
689 | * needs to be remapped to preserve non-finite values | ||
690 | * (infinities, not-a-numbers, denormals). | ||
691 | */ | ||
692 | static inline unsigned long | ||
693 | s_mem_to_reg (unsigned long s_mem) | ||
694 | { | ||
695 | unsigned long frac = (s_mem >> 0) & 0x7fffff; | ||
696 | unsigned long sign = (s_mem >> 31) & 0x1; | ||
697 | unsigned long exp_msb = (s_mem >> 30) & 0x1; | ||
698 | unsigned long exp_low = (s_mem >> 23) & 0x7f; | ||
699 | unsigned long exp; | ||
700 | |||
701 | exp = (exp_msb << 10) | exp_low; /* common case */ | ||
702 | if (exp_msb) { | ||
703 | if (exp_low == 0x7f) { | ||
704 | exp = 0x7ff; | ||
705 | } | ||
706 | } else { | ||
707 | if (exp_low == 0x00) { | ||
708 | exp = 0x000; | ||
709 | } else { | ||
710 | exp |= (0x7 << 7); | ||
711 | } | ||
712 | } | ||
713 | return (sign << 63) | (exp << 52) | (frac << 29); | ||
714 | } | ||
715 | |||
716 | /* | ||
717 | * Convert an s-floating point value in register format to the | ||
718 | * corresponding value in memory format. | ||
719 | */ | ||
720 | static inline unsigned long | ||
721 | s_reg_to_mem (unsigned long s_reg) | ||
722 | { | ||
723 | return ((s_reg >> 62) << 30) | ((s_reg << 5) >> 34); | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Handle user-level unaligned fault. Handling user-level unaligned | ||
728 | * faults is *extremely* slow and produces nasty messages. A user | ||
729 | * program *should* fix unaligned faults ASAP. | ||
730 | * | ||
731 | * Notice that we have (almost) the regular kernel stack layout here, | ||
732 | * so finding the appropriate registers is a little more difficult | ||
733 | * than in the kernel case. | ||
734 | * | ||
735 | * Finally, we handle regular integer load/stores only. In | ||
736 | * particular, load-linked/store-conditionally and floating point | ||
737 | * load/stores are not supported. The former make no sense with | ||
738 | * unaligned faults (they are guaranteed to fail) and I don't think | ||
739 | * the latter will occur in any decent program. | ||
740 | * | ||
741 | * Sigh. We *do* have to handle some FP operations, because GCC will | ||
742 | * uses them as temporary storage for integer memory to memory copies. | ||
743 | * However, we need to deal with stt/ldt and sts/lds only. | ||
744 | */ | ||
745 | |||
746 | #define OP_INT_MASK ( 1L << 0x28 | 1L << 0x2c /* ldl stl */ \ | ||
747 | | 1L << 0x29 | 1L << 0x2d /* ldq stq */ \ | ||
748 | | 1L << 0x0c | 1L << 0x0d /* ldwu stw */ \ | ||
749 | | 1L << 0x0a | 1L << 0x0e ) /* ldbu stb */ | ||
750 | |||
751 | #define OP_WRITE_MASK ( 1L << 0x26 | 1L << 0x27 /* sts stt */ \ | ||
752 | | 1L << 0x2c | 1L << 0x2d /* stl stq */ \ | ||
753 | | 1L << 0x0d | 1L << 0x0e ) /* stw stb */ | ||
754 | |||
755 | #define R(x) ((size_t) &((struct pt_regs *)0)->x) | ||
756 | |||
757 | static int unauser_reg_offsets[32] = { | ||
758 | R(r0), R(r1), R(r2), R(r3), R(r4), R(r5), R(r6), R(r7), R(r8), | ||
759 | /* r9 ... r15 are stored in front of regs. */ | ||
760 | -56, -48, -40, -32, -24, -16, -8, | ||
761 | R(r16), R(r17), R(r18), | ||
762 | R(r19), R(r20), R(r21), R(r22), R(r23), R(r24), R(r25), R(r26), | ||
763 | R(r27), R(r28), R(gp), | ||
764 | 0, 0 | ||
765 | }; | ||
766 | |||
767 | #undef R | ||
768 | |||
769 | asmlinkage void | ||
770 | do_entUnaUser(void __user * va, unsigned long opcode, | ||
771 | unsigned long reg, struct pt_regs *regs) | ||
772 | { | ||
773 | static int cnt = 0; | ||
774 | static long last_time = 0; | ||
775 | |||
776 | unsigned long tmp1, tmp2, tmp3, tmp4; | ||
777 | unsigned long fake_reg, *reg_addr = &fake_reg; | ||
778 | siginfo_t info; | ||
779 | long error; | ||
780 | |||
781 | /* Check the UAC bits to decide what the user wants us to do | ||
782 | with the unaliged access. */ | ||
783 | |||
784 | if (!test_thread_flag (TIF_UAC_NOPRINT)) { | ||
785 | if (cnt >= 5 && jiffies - last_time > 5*HZ) { | ||
786 | cnt = 0; | ||
787 | } | ||
788 | if (++cnt < 5) { | ||
789 | printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n", | ||
790 | current->comm, current->pid, | ||
791 | regs->pc - 4, va, opcode, reg); | ||
792 | } | ||
793 | last_time = jiffies; | ||
794 | } | ||
795 | if (test_thread_flag (TIF_UAC_SIGBUS)) | ||
796 | goto give_sigbus; | ||
797 | /* Not sure why you'd want to use this, but... */ | ||
798 | if (test_thread_flag (TIF_UAC_NOFIX)) | ||
799 | return; | ||
800 | |||
801 | /* Don't bother reading ds in the access check since we already | ||
802 | know that this came from the user. Also rely on the fact that | ||
803 | the page at TASK_SIZE is unmapped and so can't be touched anyway. */ | ||
804 | if (!__access_ok((unsigned long)va, 0, USER_DS)) | ||
805 | goto give_sigsegv; | ||
806 | |||
807 | ++unaligned[1].count; | ||
808 | unaligned[1].va = (unsigned long)va; | ||
809 | unaligned[1].pc = regs->pc - 4; | ||
810 | |||
811 | if ((1L << opcode) & OP_INT_MASK) { | ||
812 | /* it's an integer load/store */ | ||
813 | if (reg < 30) { | ||
814 | reg_addr = (unsigned long *) | ||
815 | ((char *)regs + unauser_reg_offsets[reg]); | ||
816 | } else if (reg == 30) { | ||
817 | /* usp in PAL regs */ | ||
818 | fake_reg = rdusp(); | ||
819 | } else { | ||
820 | /* zero "register" */ | ||
821 | fake_reg = 0; | ||
822 | } | ||
823 | } | ||
824 | |||
825 | /* We don't want to use the generic get/put unaligned macros as | ||
826 | we want to trap exceptions. Only if we actually get an | ||
827 | exception will we decide whether we should have caught it. */ | ||
828 | |||
829 | switch (opcode) { | ||
830 | case 0x0c: /* ldwu */ | ||
831 | __asm__ __volatile__( | ||
832 | "1: ldq_u %1,0(%3)\n" | ||
833 | "2: ldq_u %2,1(%3)\n" | ||
834 | " extwl %1,%3,%1\n" | ||
835 | " extwh %2,%3,%2\n" | ||
836 | "3:\n" | ||
837 | ".section __ex_table,\"a\"\n" | ||
838 | " .long 1b - .\n" | ||
839 | " lda %1,3b-1b(%0)\n" | ||
840 | " .long 2b - .\n" | ||
841 | " lda %2,3b-2b(%0)\n" | ||
842 | ".previous" | ||
843 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
844 | : "r"(va), "0"(0)); | ||
845 | if (error) | ||
846 | goto give_sigsegv; | ||
847 | *reg_addr = tmp1|tmp2; | ||
848 | break; | ||
849 | |||
850 | case 0x22: /* lds */ | ||
851 | __asm__ __volatile__( | ||
852 | "1: ldq_u %1,0(%3)\n" | ||
853 | "2: ldq_u %2,3(%3)\n" | ||
854 | " extll %1,%3,%1\n" | ||
855 | " extlh %2,%3,%2\n" | ||
856 | "3:\n" | ||
857 | ".section __ex_table,\"a\"\n" | ||
858 | " .long 1b - .\n" | ||
859 | " lda %1,3b-1b(%0)\n" | ||
860 | " .long 2b - .\n" | ||
861 | " lda %2,3b-2b(%0)\n" | ||
862 | ".previous" | ||
863 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
864 | : "r"(va), "0"(0)); | ||
865 | if (error) | ||
866 | goto give_sigsegv; | ||
867 | alpha_write_fp_reg(reg, s_mem_to_reg((int)(tmp1|tmp2))); | ||
868 | return; | ||
869 | |||
870 | case 0x23: /* ldt */ | ||
871 | __asm__ __volatile__( | ||
872 | "1: ldq_u %1,0(%3)\n" | ||
873 | "2: ldq_u %2,7(%3)\n" | ||
874 | " extql %1,%3,%1\n" | ||
875 | " extqh %2,%3,%2\n" | ||
876 | "3:\n" | ||
877 | ".section __ex_table,\"a\"\n" | ||
878 | " .long 1b - .\n" | ||
879 | " lda %1,3b-1b(%0)\n" | ||
880 | " .long 2b - .\n" | ||
881 | " lda %2,3b-2b(%0)\n" | ||
882 | ".previous" | ||
883 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
884 | : "r"(va), "0"(0)); | ||
885 | if (error) | ||
886 | goto give_sigsegv; | ||
887 | alpha_write_fp_reg(reg, tmp1|tmp2); | ||
888 | return; | ||
889 | |||
890 | case 0x28: /* ldl */ | ||
891 | __asm__ __volatile__( | ||
892 | "1: ldq_u %1,0(%3)\n" | ||
893 | "2: ldq_u %2,3(%3)\n" | ||
894 | " extll %1,%3,%1\n" | ||
895 | " extlh %2,%3,%2\n" | ||
896 | "3:\n" | ||
897 | ".section __ex_table,\"a\"\n" | ||
898 | " .long 1b - .\n" | ||
899 | " lda %1,3b-1b(%0)\n" | ||
900 | " .long 2b - .\n" | ||
901 | " lda %2,3b-2b(%0)\n" | ||
902 | ".previous" | ||
903 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
904 | : "r"(va), "0"(0)); | ||
905 | if (error) | ||
906 | goto give_sigsegv; | ||
907 | *reg_addr = (int)(tmp1|tmp2); | ||
908 | break; | ||
909 | |||
910 | case 0x29: /* ldq */ | ||
911 | __asm__ __volatile__( | ||
912 | "1: ldq_u %1,0(%3)\n" | ||
913 | "2: ldq_u %2,7(%3)\n" | ||
914 | " extql %1,%3,%1\n" | ||
915 | " extqh %2,%3,%2\n" | ||
916 | "3:\n" | ||
917 | ".section __ex_table,\"a\"\n" | ||
918 | " .long 1b - .\n" | ||
919 | " lda %1,3b-1b(%0)\n" | ||
920 | " .long 2b - .\n" | ||
921 | " lda %2,3b-2b(%0)\n" | ||
922 | ".previous" | ||
923 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2) | ||
924 | : "r"(va), "0"(0)); | ||
925 | if (error) | ||
926 | goto give_sigsegv; | ||
927 | *reg_addr = tmp1|tmp2; | ||
928 | break; | ||
929 | |||
930 | /* Note that the store sequences do not indicate that they change | ||
931 | memory because it _should_ be affecting nothing in this context. | ||
932 | (Otherwise we have other, much larger, problems.) */ | ||
933 | case 0x0d: /* stw */ | ||
934 | __asm__ __volatile__( | ||
935 | "1: ldq_u %2,1(%5)\n" | ||
936 | "2: ldq_u %1,0(%5)\n" | ||
937 | " inswh %6,%5,%4\n" | ||
938 | " inswl %6,%5,%3\n" | ||
939 | " mskwh %2,%5,%2\n" | ||
940 | " mskwl %1,%5,%1\n" | ||
941 | " or %2,%4,%2\n" | ||
942 | " or %1,%3,%1\n" | ||
943 | "3: stq_u %2,1(%5)\n" | ||
944 | "4: stq_u %1,0(%5)\n" | ||
945 | "5:\n" | ||
946 | ".section __ex_table,\"a\"\n" | ||
947 | " .long 1b - .\n" | ||
948 | " lda %2,5b-1b(%0)\n" | ||
949 | " .long 2b - .\n" | ||
950 | " lda %1,5b-2b(%0)\n" | ||
951 | " .long 3b - .\n" | ||
952 | " lda $31,5b-3b(%0)\n" | ||
953 | " .long 4b - .\n" | ||
954 | " lda $31,5b-4b(%0)\n" | ||
955 | ".previous" | ||
956 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
957 | "=&r"(tmp3), "=&r"(tmp4) | ||
958 | : "r"(va), "r"(*reg_addr), "0"(0)); | ||
959 | if (error) | ||
960 | goto give_sigsegv; | ||
961 | return; | ||
962 | |||
963 | case 0x26: /* sts */ | ||
964 | fake_reg = s_reg_to_mem(alpha_read_fp_reg(reg)); | ||
965 | /* FALLTHRU */ | ||
966 | |||
967 | case 0x2c: /* stl */ | ||
968 | __asm__ __volatile__( | ||
969 | "1: ldq_u %2,3(%5)\n" | ||
970 | "2: ldq_u %1,0(%5)\n" | ||
971 | " inslh %6,%5,%4\n" | ||
972 | " insll %6,%5,%3\n" | ||
973 | " msklh %2,%5,%2\n" | ||
974 | " mskll %1,%5,%1\n" | ||
975 | " or %2,%4,%2\n" | ||
976 | " or %1,%3,%1\n" | ||
977 | "3: stq_u %2,3(%5)\n" | ||
978 | "4: stq_u %1,0(%5)\n" | ||
979 | "5:\n" | ||
980 | ".section __ex_table,\"a\"\n" | ||
981 | " .long 1b - .\n" | ||
982 | " lda %2,5b-1b(%0)\n" | ||
983 | " .long 2b - .\n" | ||
984 | " lda %1,5b-2b(%0)\n" | ||
985 | " .long 3b - .\n" | ||
986 | " lda $31,5b-3b(%0)\n" | ||
987 | " .long 4b - .\n" | ||
988 | " lda $31,5b-4b(%0)\n" | ||
989 | ".previous" | ||
990 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
991 | "=&r"(tmp3), "=&r"(tmp4) | ||
992 | : "r"(va), "r"(*reg_addr), "0"(0)); | ||
993 | if (error) | ||
994 | goto give_sigsegv; | ||
995 | return; | ||
996 | |||
997 | case 0x27: /* stt */ | ||
998 | fake_reg = alpha_read_fp_reg(reg); | ||
999 | /* FALLTHRU */ | ||
1000 | |||
1001 | case 0x2d: /* stq */ | ||
1002 | __asm__ __volatile__( | ||
1003 | "1: ldq_u %2,7(%5)\n" | ||
1004 | "2: ldq_u %1,0(%5)\n" | ||
1005 | " insqh %6,%5,%4\n" | ||
1006 | " insql %6,%5,%3\n" | ||
1007 | " mskqh %2,%5,%2\n" | ||
1008 | " mskql %1,%5,%1\n" | ||
1009 | " or %2,%4,%2\n" | ||
1010 | " or %1,%3,%1\n" | ||
1011 | "3: stq_u %2,7(%5)\n" | ||
1012 | "4: stq_u %1,0(%5)\n" | ||
1013 | "5:\n" | ||
1014 | ".section __ex_table,\"a\"\n\t" | ||
1015 | " .long 1b - .\n" | ||
1016 | " lda %2,5b-1b(%0)\n" | ||
1017 | " .long 2b - .\n" | ||
1018 | " lda %1,5b-2b(%0)\n" | ||
1019 | " .long 3b - .\n" | ||
1020 | " lda $31,5b-3b(%0)\n" | ||
1021 | " .long 4b - .\n" | ||
1022 | " lda $31,5b-4b(%0)\n" | ||
1023 | ".previous" | ||
1024 | : "=r"(error), "=&r"(tmp1), "=&r"(tmp2), | ||
1025 | "=&r"(tmp3), "=&r"(tmp4) | ||
1026 | : "r"(va), "r"(*reg_addr), "0"(0)); | ||
1027 | if (error) | ||
1028 | goto give_sigsegv; | ||
1029 | return; | ||
1030 | |||
1031 | default: | ||
1032 | /* What instruction were you trying to use, exactly? */ | ||
1033 | goto give_sigbus; | ||
1034 | } | ||
1035 | |||
1036 | /* Only integer loads should get here; everyone else returns early. */ | ||
1037 | if (reg == 30) | ||
1038 | wrusp(fake_reg); | ||
1039 | return; | ||
1040 | |||
1041 | give_sigsegv: | ||
1042 | regs->pc -= 4; /* make pc point to faulting insn */ | ||
1043 | info.si_signo = SIGSEGV; | ||
1044 | info.si_errno = 0; | ||
1045 | |||
1046 | /* We need to replicate some of the logic in mm/fault.c, | ||
1047 | since we don't have access to the fault code in the | ||
1048 | exception handling return path. */ | ||
1049 | if (!__access_ok((unsigned long)va, 0, USER_DS)) | ||
1050 | info.si_code = SEGV_ACCERR; | ||
1051 | else { | ||
1052 | struct mm_struct *mm = current->mm; | ||
1053 | down_read(&mm->mmap_sem); | ||
1054 | if (find_vma(mm, (unsigned long)va)) | ||
1055 | info.si_code = SEGV_ACCERR; | ||
1056 | else | ||
1057 | info.si_code = SEGV_MAPERR; | ||
1058 | up_read(&mm->mmap_sem); | ||
1059 | } | ||
1060 | info.si_addr = va; | ||
1061 | send_sig_info(SIGSEGV, &info, current); | ||
1062 | return; | ||
1063 | |||
1064 | give_sigbus: | ||
1065 | regs->pc -= 4; | ||
1066 | info.si_signo = SIGBUS; | ||
1067 | info.si_errno = 0; | ||
1068 | info.si_code = BUS_ADRALN; | ||
1069 | info.si_addr = va; | ||
1070 | send_sig_info(SIGBUS, &info, current); | ||
1071 | return; | ||
1072 | } | ||
1073 | |||
1074 | void __init | ||
1075 | trap_init(void) | ||
1076 | { | ||
1077 | /* Tell PAL-code what global pointer we want in the kernel. */ | ||
1078 | register unsigned long gptr __asm__("$29"); | ||
1079 | wrkgp(gptr); | ||
1080 | |||
1081 | /* Hack for Multia (UDB) and JENSEN: some of their SRMs have | ||
1082 | a bug in the handling of the opDEC fault. Fix it up if so. */ | ||
1083 | if (implver() == IMPLVER_EV4) | ||
1084 | opDEC_check(); | ||
1085 | |||
1086 | wrent(entArith, 1); | ||
1087 | wrent(entMM, 2); | ||
1088 | wrent(entIF, 3); | ||
1089 | wrent(entUna, 4); | ||
1090 | wrent(entSys, 5); | ||
1091 | wrent(entDbg, 6); | ||
1092 | } | ||
diff --git a/arch/alpha/kernel/vmlinux.lds.S b/arch/alpha/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..0922e0785ddb --- /dev/null +++ b/arch/alpha/kernel/vmlinux.lds.S | |||
@@ -0,0 +1,149 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <asm-generic/vmlinux.lds.h> | ||
3 | |||
4 | OUTPUT_FORMAT("elf64-alpha") | ||
5 | OUTPUT_ARCH(alpha) | ||
6 | ENTRY(__start) | ||
7 | PHDRS { kernel PT_LOAD ; } | ||
8 | jiffies = jiffies_64; | ||
9 | SECTIONS | ||
10 | { | ||
11 | #ifdef CONFIG_ALPHA_LEGACY_START_ADDRESS | ||
12 | . = 0xfffffc0000310000; | ||
13 | #else | ||
14 | . = 0xfffffc0001010000; | ||
15 | #endif | ||
16 | |||
17 | _text = .; /* Text and read-only data */ | ||
18 | .text : { | ||
19 | *(.text) | ||
20 | SCHED_TEXT | ||
21 | LOCK_TEXT | ||
22 | *(.fixup) | ||
23 | *(.gnu.warning) | ||
24 | } :kernel | ||
25 | _etext = .; /* End of text section */ | ||
26 | |||
27 | . = ALIGN(16); | ||
28 | __start___ex_table = .; /* Exception table */ | ||
29 | __ex_table : { *(__ex_table) } | ||
30 | __stop___ex_table = .; | ||
31 | |||
32 | RODATA | ||
33 | |||
34 | /* Will be freed after init */ | ||
35 | . = ALIGN(8192); /* Init code and data */ | ||
36 | __init_begin = .; | ||
37 | .init.text : { | ||
38 | _sinittext = .; | ||
39 | *(.init.text) | ||
40 | _einittext = .; | ||
41 | } | ||
42 | .init.data : { *(.init.data) } | ||
43 | |||
44 | . = ALIGN(16); | ||
45 | __setup_start = .; | ||
46 | .init.setup : { *(.init.setup) } | ||
47 | __setup_end = .; | ||
48 | |||
49 | . = ALIGN(8); | ||
50 | __initcall_start = .; | ||
51 | .initcall.init : { | ||
52 | *(.initcall1.init) | ||
53 | *(.initcall2.init) | ||
54 | *(.initcall3.init) | ||
55 | *(.initcall4.init) | ||
56 | *(.initcall5.init) | ||
57 | *(.initcall6.init) | ||
58 | *(.initcall7.init) | ||
59 | } | ||
60 | __initcall_end = .; | ||
61 | |||
62 | . = ALIGN(8192); | ||
63 | __initramfs_start = .; | ||
64 | .init.ramfs : { *(.init.ramfs) } | ||
65 | __initramfs_end = .; | ||
66 | |||
67 | . = ALIGN(8); | ||
68 | .con_initcall.init : { | ||
69 | __con_initcall_start = .; | ||
70 | *(.con_initcall.init) | ||
71 | __con_initcall_end = .; | ||
72 | } | ||
73 | |||
74 | . = ALIGN(8); | ||
75 | SECURITY_INIT | ||
76 | |||
77 | . = ALIGN(64); | ||
78 | __per_cpu_start = .; | ||
79 | .data.percpu : { *(.data.percpu) } | ||
80 | __per_cpu_end = .; | ||
81 | |||
82 | . = ALIGN(2*8192); | ||
83 | __init_end = .; | ||
84 | /* Freed after init ends here */ | ||
85 | |||
86 | /* Note 2 page alignment above. */ | ||
87 | .data.init_thread : { *(.data.init_thread) } | ||
88 | |||
89 | . = ALIGN(8192); | ||
90 | .data.page_aligned : { *(.data.page_aligned) } | ||
91 | |||
92 | . = ALIGN(64); | ||
93 | .data.cacheline_aligned : { *(.data.cacheline_aligned) } | ||
94 | |||
95 | _data = .; | ||
96 | .data : { /* Data */ | ||
97 | *(.data) | ||
98 | CONSTRUCTORS | ||
99 | } | ||
100 | |||
101 | .got : { *(.got) } | ||
102 | .sdata : { *(.sdata) } | ||
103 | |||
104 | _edata = .; /* End of data section */ | ||
105 | |||
106 | __bss_start = .; | ||
107 | .sbss : { *(.sbss) *(.scommon) } | ||
108 | .bss : { *(.bss) *(COMMON) } | ||
109 | __bss_stop = .; | ||
110 | |||
111 | _end = .; | ||
112 | |||
113 | /* Sections to be discarded */ | ||
114 | /DISCARD/ : { *(.exit.text) *(.exit.data) *(.exitcall.exit) } | ||
115 | |||
116 | .mdebug 0 : { *(.mdebug) } | ||
117 | .note 0 : { *(.note) } | ||
118 | .comment 0 : { *(.comment) } | ||
119 | |||
120 | /* Stabs debugging sections */ | ||
121 | .stab 0 : { *(.stab) } | ||
122 | .stabstr 0 : { *(.stabstr) } | ||
123 | .stab.excl 0 : { *(.stab.excl) } | ||
124 | .stab.exclstr 0 : { *(.stab.exclstr) } | ||
125 | .stab.index 0 : { *(.stab.index) } | ||
126 | .stab.indexstr 0 : { *(.stab.indexstr) } | ||
127 | /* DWARF 1 */ | ||
128 | .debug 0 : { *(.debug) } | ||
129 | .line 0 : { *(.line) } | ||
130 | /* GNU DWARF 1 extensions */ | ||
131 | .debug_srcinfo 0 : { *(.debug_srcinfo) } | ||
132 | .debug_sfnames 0 : { *(.debug_sfnames) } | ||
133 | /* DWARF 1.1 and DWARF 2 */ | ||
134 | .debug_aranges 0 : { *(.debug_aranges) } | ||
135 | .debug_pubnames 0 : { *(.debug_pubnames) } | ||
136 | /* DWARF 2 */ | ||
137 | .debug_info 0 : { *(.debug_info) } | ||
138 | .debug_abbrev 0 : { *(.debug_abbrev) } | ||
139 | .debug_line 0 : { *(.debug_line) } | ||
140 | .debug_frame 0 : { *(.debug_frame) } | ||
141 | .debug_str 0 : { *(.debug_str) } | ||
142 | .debug_loc 0 : { *(.debug_loc) } | ||
143 | .debug_macinfo 0 : { *(.debug_macinfo) } | ||
144 | /* SGI/MIPS DWARF 2 extensions */ | ||
145 | .debug_weaknames 0 : { *(.debug_weaknames) } | ||
146 | .debug_funcnames 0 : { *(.debug_funcnames) } | ||
147 | .debug_typenames 0 : { *(.debug_typenames) } | ||
148 | .debug_varnames 0 : { *(.debug_varnames) } | ||
149 | } | ||
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile new file mode 100644 index 000000000000..21cf624d7329 --- /dev/null +++ b/arch/alpha/lib/Makefile | |||
@@ -0,0 +1,58 @@ | |||
1 | # | ||
2 | # Makefile for alpha-specific library files.. | ||
3 | # | ||
4 | |||
5 | EXTRA_AFLAGS := $(CFLAGS) | ||
6 | EXTRA_CFLAGS := -Werror | ||
7 | |||
8 | # Many of these routines have implementations tuned for ev6. | ||
9 | # Choose them iff we're targeting ev6 specifically. | ||
10 | ev6-$(CONFIG_ALPHA_EV6) := ev6- | ||
11 | |||
12 | # Several make use of the cttz instruction introduced in ev67. | ||
13 | ev67-$(CONFIG_ALPHA_EV67) := ev67- | ||
14 | |||
15 | lib-y = __divqu.o __remqu.o __divlu.o __remlu.o \ | ||
16 | udelay.o \ | ||
17 | $(ev6-y)memset.o \ | ||
18 | $(ev6-y)memcpy.o \ | ||
19 | memmove.o \ | ||
20 | checksum.o \ | ||
21 | csum_partial_copy.o \ | ||
22 | $(ev67-y)strlen.o \ | ||
23 | $(ev67-y)strcat.o \ | ||
24 | strcpy.o \ | ||
25 | $(ev67-y)strncat.o \ | ||
26 | strncpy.o \ | ||
27 | $(ev6-y)stxcpy.o \ | ||
28 | $(ev6-y)stxncpy.o \ | ||
29 | $(ev67-y)strchr.o \ | ||
30 | $(ev67-y)strrchr.o \ | ||
31 | $(ev6-y)memchr.o \ | ||
32 | $(ev6-y)copy_user.o \ | ||
33 | $(ev6-y)clear_user.o \ | ||
34 | $(ev6-y)strncpy_from_user.o \ | ||
35 | $(ev67-y)strlen_user.o \ | ||
36 | $(ev6-y)csum_ipv6_magic.o \ | ||
37 | $(ev6-y)clear_page.o \ | ||
38 | $(ev6-y)copy_page.o \ | ||
39 | strcasecmp.o \ | ||
40 | fpreg.o \ | ||
41 | callback_srm.o srm_puts.o srm_printk.o | ||
42 | |||
43 | lib-$(CONFIG_SMP) += dec_and_lock.o | ||
44 | |||
45 | # The division routines are built from single source, with different defines. | ||
46 | AFLAGS___divqu.o = -DDIV | ||
47 | AFLAGS___remqu.o = -DREM | ||
48 | AFLAGS___divlu.o = -DDIV -DINTSIZE | ||
49 | AFLAGS___remlu.o = -DREM -DINTSIZE | ||
50 | |||
51 | $(obj)/__divqu.o: $(obj)/$(ev6-y)divide.S | ||
52 | $(cmd_as_o_S) | ||
53 | $(obj)/__remqu.o: $(obj)/$(ev6-y)divide.S | ||
54 | $(cmd_as_o_S) | ||
55 | $(obj)/__divlu.o: $(obj)/$(ev6-y)divide.S | ||
56 | $(cmd_as_o_S) | ||
57 | $(obj)/__remlu.o: $(obj)/$(ev6-y)divide.S | ||
58 | $(cmd_as_o_S) | ||
diff --git a/arch/alpha/lib/callback_srm.S b/arch/alpha/lib/callback_srm.S new file mode 100644 index 000000000000..0528acd0d9ad --- /dev/null +++ b/arch/alpha/lib/callback_srm.S | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/callback_srm.S | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <asm/console.h> | ||
7 | |||
8 | .text | ||
9 | #define HWRPB_CRB_OFFSET 0xc0 | ||
10 | |||
11 | #if defined(CONFIG_ALPHA_SRM) || defined(CONFIG_ALPHA_GENERIC) | ||
12 | .align 4 | ||
13 | srm_dispatch: | ||
14 | #if defined(CONFIG_ALPHA_GENERIC) | ||
15 | ldl $4,alpha_using_srm | ||
16 | beq $4,nosrm | ||
17 | #endif | ||
18 | ldq $0,hwrpb # gp is set up by CALLBACK macro. | ||
19 | ldl $25,0($25) # Pick up the wrapper data. | ||
20 | mov $20,$21 # Shift arguments right. | ||
21 | mov $19,$20 | ||
22 | ldq $1,HWRPB_CRB_OFFSET($0) | ||
23 | mov $18,$19 | ||
24 | mov $17,$18 | ||
25 | mov $16,$17 | ||
26 | addq $0,$1,$2 # CRB address | ||
27 | ldq $27,0($2) # DISPATCH procedure descriptor (VMS call std) | ||
28 | extwl $25,0,$16 # SRM callback function code | ||
29 | ldq $3,8($27) # call address | ||
30 | extwl $25,2,$25 # argument information (VMS calling std) | ||
31 | jmp ($3) # Return directly to caller of wrapper. | ||
32 | |||
33 | .align 4 | ||
34 | .globl srm_fixup | ||
35 | .ent srm_fixup | ||
36 | srm_fixup: | ||
37 | ldgp $29,0($27) | ||
38 | #if defined(CONFIG_ALPHA_GENERIC) | ||
39 | ldl $4,alpha_using_srm | ||
40 | beq $4,nosrm | ||
41 | #endif | ||
42 | ldq $0,hwrpb | ||
43 | ldq $1,HWRPB_CRB_OFFSET($0) | ||
44 | addq $0,$1,$2 # CRB address | ||
45 | ldq $27,16($2) # VA of FIXUP procedure descriptor | ||
46 | ldq $3,8($27) # call address | ||
47 | lda $25,2($31) # two integer arguments | ||
48 | jmp ($3) # Return directly to caller of srm_fixup. | ||
49 | .end srm_fixup | ||
50 | |||
51 | #if defined(CONFIG_ALPHA_GENERIC) | ||
52 | .align 3 | ||
53 | nosrm: | ||
54 | lda $0,-1($31) | ||
55 | ret | ||
56 | #endif | ||
57 | |||
58 | #define CALLBACK(NAME, CODE, ARG_CNT) \ | ||
59 | .align 4; .globl callback_##NAME; .ent callback_##NAME; callback_##NAME##: \ | ||
60 | ldgp $29,0($27); br $25,srm_dispatch; .word CODE, ARG_CNT; .end callback_##NAME | ||
61 | |||
62 | #else /* defined(CONFIG_ALPHA_SRM) || defined(CONFIG_ALPHA_GENERIC) */ | ||
63 | |||
64 | #define CALLBACK(NAME, CODE, ARG_CNT) \ | ||
65 | .align 3; .globl callback_##NAME; .ent callback_##NAME; callback_##NAME##: \ | ||
66 | lda $0,-1($31); ret; .end callback_##NAME | ||
67 | |||
68 | .align 3 | ||
69 | .globl srm_fixup | ||
70 | .ent srm_fixup | ||
71 | srm_fixup: | ||
72 | lda $0,-1($31) | ||
73 | ret | ||
74 | .end srm_fixup | ||
75 | #endif /* defined(CONFIG_ALPHA_SRM) || defined(CONFIG_ALPHA_GENERIC) */ | ||
76 | |||
77 | CALLBACK(puts, CCB_PUTS, 4) | ||
78 | CALLBACK(open, CCB_OPEN, 3) | ||
79 | CALLBACK(close, CCB_CLOSE, 2) | ||
80 | CALLBACK(read, CCB_READ, 5) | ||
81 | CALLBACK(open_console, CCB_OPEN_CONSOLE, 1) | ||
82 | CALLBACK(close_console, CCB_CLOSE_CONSOLE, 1) | ||
83 | CALLBACK(getenv, CCB_GET_ENV, 4) | ||
84 | CALLBACK(setenv, CCB_SET_ENV, 4) | ||
85 | CALLBACK(getc, CCB_GETC, 2) | ||
86 | CALLBACK(reset_term, CCB_RESET_TERM, 2) | ||
87 | CALLBACK(term_int, CCB_SET_TERM_INT, 3) | ||
88 | CALLBACK(term_ctl, CCB_SET_TERM_CTL, 3) | ||
89 | CALLBACK(process_keycode, CCB_PROCESS_KEYCODE, 3) | ||
90 | CALLBACK(ioctl, CCB_IOCTL, 6) | ||
91 | CALLBACK(write, CCB_WRITE, 5) | ||
92 | CALLBACK(reset_env, CCB_RESET_ENV, 4) | ||
93 | CALLBACK(save_env, CCB_SAVE_ENV, 1) | ||
94 | CALLBACK(pswitch, CCB_PSWITCH, 3) | ||
95 | CALLBACK(bios_emul, CCB_BIOS_EMUL, 5) | ||
96 | |||
97 | .data | ||
98 | __alpha_using_srm: # For use by bootpheader | ||
99 | .long 7 # value is not 1 for link debugging | ||
100 | .weak alpha_using_srm; alpha_using_srm = __alpha_using_srm | ||
101 | __callback_init_done: # For use by bootpheader | ||
102 | .long 7 # value is not 1 for link debugging | ||
103 | .weak callback_init_done; callback_init_done = __callback_init_done | ||
104 | |||
diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c new file mode 100644 index 000000000000..89044e6385fe --- /dev/null +++ b/arch/alpha/lib/checksum.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/checksum.c | ||
3 | * | ||
4 | * This file contains network checksum routines that are better done | ||
5 | * in an architecture-specific manner due to speed.. | ||
6 | * Comments in other versions indicate that the algorithms are from RFC1071 | ||
7 | * | ||
8 | * accellerated versions (and 21264 assembly versions ) contributed by | ||
9 | * Rick Gorton <rick.gorton@alpha-processor.com> | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/string.h> | ||
14 | |||
15 | #include <asm/byteorder.h> | ||
16 | |||
17 | static inline unsigned short from64to16(unsigned long x) | ||
18 | { | ||
19 | /* Using extract instructions is a bit more efficient | ||
20 | than the original shift/bitmask version. */ | ||
21 | |||
22 | union { | ||
23 | unsigned long ul; | ||
24 | unsigned int ui[2]; | ||
25 | unsigned short us[4]; | ||
26 | } in_v, tmp_v, out_v; | ||
27 | |||
28 | in_v.ul = x; | ||
29 | tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1]; | ||
30 | |||
31 | /* Since the bits of tmp_v.sh[3] are going to always be zero, | ||
32 | we don't have to bother to add that in. */ | ||
33 | out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1] | ||
34 | + (unsigned long) tmp_v.us[2]; | ||
35 | |||
36 | /* Similarly, out_v.us[2] is always zero for the final add. */ | ||
37 | return out_v.us[0] + out_v.us[1]; | ||
38 | } | ||
39 | |||
40 | /* | ||
41 | * computes the checksum of the TCP/UDP pseudo-header | ||
42 | * returns a 16-bit checksum, already complemented. | ||
43 | */ | ||
44 | unsigned short int csum_tcpudp_magic(unsigned long saddr, | ||
45 | unsigned long daddr, | ||
46 | unsigned short len, | ||
47 | unsigned short proto, | ||
48 | unsigned int sum) | ||
49 | { | ||
50 | return ~from64to16(saddr + daddr + sum + | ||
51 | ((unsigned long) ntohs(len) << 16) + | ||
52 | ((unsigned long) proto << 8)); | ||
53 | } | ||
54 | |||
55 | unsigned int csum_tcpudp_nofold(unsigned long saddr, | ||
56 | unsigned long daddr, | ||
57 | unsigned short len, | ||
58 | unsigned short proto, | ||
59 | unsigned int sum) | ||
60 | { | ||
61 | unsigned long result; | ||
62 | |||
63 | result = (saddr + daddr + sum + | ||
64 | ((unsigned long) ntohs(len) << 16) + | ||
65 | ((unsigned long) proto << 8)); | ||
66 | |||
67 | /* Fold down to 32-bits so we don't lose in the typedef-less | ||
68 | network stack. */ | ||
69 | /* 64 to 33 */ | ||
70 | result = (result & 0xffffffff) + (result >> 32); | ||
71 | /* 33 to 32 */ | ||
72 | result = (result & 0xffffffff) + (result >> 32); | ||
73 | return result; | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * Do a 64-bit checksum on an arbitrary memory area.. | ||
78 | * | ||
79 | * This isn't a great routine, but it's not _horrible_ either. The | ||
80 | * inner loop could be unrolled a bit further, and there are better | ||
81 | * ways to do the carry, but this is reasonable. | ||
82 | */ | ||
83 | static inline unsigned long do_csum(const unsigned char * buff, int len) | ||
84 | { | ||
85 | int odd, count; | ||
86 | unsigned long result = 0; | ||
87 | |||
88 | if (len <= 0) | ||
89 | goto out; | ||
90 | odd = 1 & (unsigned long) buff; | ||
91 | if (odd) { | ||
92 | result = *buff << 8; | ||
93 | len--; | ||
94 | buff++; | ||
95 | } | ||
96 | count = len >> 1; /* nr of 16-bit words.. */ | ||
97 | if (count) { | ||
98 | if (2 & (unsigned long) buff) { | ||
99 | result += *(unsigned short *) buff; | ||
100 | count--; | ||
101 | len -= 2; | ||
102 | buff += 2; | ||
103 | } | ||
104 | count >>= 1; /* nr of 32-bit words.. */ | ||
105 | if (count) { | ||
106 | if (4 & (unsigned long) buff) { | ||
107 | result += *(unsigned int *) buff; | ||
108 | count--; | ||
109 | len -= 4; | ||
110 | buff += 4; | ||
111 | } | ||
112 | count >>= 1; /* nr of 64-bit words.. */ | ||
113 | if (count) { | ||
114 | unsigned long carry = 0; | ||
115 | do { | ||
116 | unsigned long w = *(unsigned long *) buff; | ||
117 | count--; | ||
118 | buff += 8; | ||
119 | result += carry; | ||
120 | result += w; | ||
121 | carry = (w > result); | ||
122 | } while (count); | ||
123 | result += carry; | ||
124 | result = (result & 0xffffffff) + (result >> 32); | ||
125 | } | ||
126 | if (len & 4) { | ||
127 | result += *(unsigned int *) buff; | ||
128 | buff += 4; | ||
129 | } | ||
130 | } | ||
131 | if (len & 2) { | ||
132 | result += *(unsigned short *) buff; | ||
133 | buff += 2; | ||
134 | } | ||
135 | } | ||
136 | if (len & 1) | ||
137 | result += *buff; | ||
138 | result = from64to16(result); | ||
139 | if (odd) | ||
140 | result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); | ||
141 | out: | ||
142 | return result; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * This is a version of ip_compute_csum() optimized for IP headers, | ||
147 | * which always checksum on 4 octet boundaries. | ||
148 | */ | ||
149 | unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) | ||
150 | { | ||
151 | return ~do_csum(iph,ihl*4); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * computes the checksum of a memory block at buff, length len, | ||
156 | * and adds in "sum" (32-bit) | ||
157 | * | ||
158 | * returns a 32-bit number suitable for feeding into itself | ||
159 | * or csum_tcpudp_magic | ||
160 | * | ||
161 | * this function must be called with even lengths, except | ||
162 | * for the last fragment, which may be odd | ||
163 | * | ||
164 | * it's best to have buff aligned on a 32-bit boundary | ||
165 | */ | ||
166 | unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) | ||
167 | { | ||
168 | unsigned long result = do_csum(buff, len); | ||
169 | |||
170 | /* add in old sum, and carry.. */ | ||
171 | result += sum; | ||
172 | /* 32+c bits -> 32 bits */ | ||
173 | result = (result & 0xffffffff) + (result >> 32); | ||
174 | return result; | ||
175 | } | ||
176 | |||
177 | EXPORT_SYMBOL(csum_partial); | ||
178 | |||
179 | /* | ||
180 | * this routine is used for miscellaneous IP-like checksums, mainly | ||
181 | * in icmp.c | ||
182 | */ | ||
183 | unsigned short ip_compute_csum(unsigned char * buff, int len) | ||
184 | { | ||
185 | return ~from64to16(do_csum(buff,len)); | ||
186 | } | ||
diff --git a/arch/alpha/lib/clear_page.S b/arch/alpha/lib/clear_page.S new file mode 100644 index 000000000000..a221ae266e29 --- /dev/null +++ b/arch/alpha/lib/clear_page.S | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/clear_page.S | ||
3 | * | ||
4 | * Zero an entire page. | ||
5 | */ | ||
6 | |||
7 | .text | ||
8 | .align 4 | ||
9 | .global clear_page | ||
10 | .ent clear_page | ||
11 | clear_page: | ||
12 | .prologue 0 | ||
13 | |||
14 | lda $0,128 | ||
15 | nop | ||
16 | unop | ||
17 | nop | ||
18 | |||
19 | 1: stq $31,0($16) | ||
20 | stq $31,8($16) | ||
21 | stq $31,16($16) | ||
22 | stq $31,24($16) | ||
23 | |||
24 | stq $31,32($16) | ||
25 | stq $31,40($16) | ||
26 | stq $31,48($16) | ||
27 | subq $0,1,$0 | ||
28 | |||
29 | stq $31,56($16) | ||
30 | addq $16,64,$16 | ||
31 | unop | ||
32 | bne $0,1b | ||
33 | |||
34 | ret | ||
35 | nop | ||
36 | unop | ||
37 | nop | ||
38 | |||
39 | .end clear_page | ||
diff --git a/arch/alpha/lib/clear_user.S b/arch/alpha/lib/clear_user.S new file mode 100644 index 000000000000..8860316c1957 --- /dev/null +++ b/arch/alpha/lib/clear_user.S | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/clear_user.S | ||
3 | * Contributed by Richard Henderson <rth@tamu.edu> | ||
4 | * | ||
5 | * Zero user space, handling exceptions as we go. | ||
6 | * | ||
7 | * We have to make sure that $0 is always up-to-date and contains the | ||
8 | * right "bytes left to zero" value (and that it is updated only _after_ | ||
9 | * a successful copy). There is also some rather minor exception setup | ||
10 | * stuff. | ||
11 | * | ||
12 | * NOTE! This is not directly C-callable, because the calling semantics | ||
13 | * are different: | ||
14 | * | ||
15 | * Inputs: | ||
16 | * length in $0 | ||
17 | * destination address in $6 | ||
18 | * exception pointer in $7 | ||
19 | * return address in $28 (exceptions expect it there) | ||
20 | * | ||
21 | * Outputs: | ||
22 | * bytes left to copy in $0 | ||
23 | * | ||
24 | * Clobbers: | ||
25 | * $1,$2,$3,$4,$5,$6 | ||
26 | */ | ||
27 | |||
28 | /* Allow an exception for an insn; exit if we get one. */ | ||
29 | #define EX(x,y...) \ | ||
30 | 99: x,##y; \ | ||
31 | .section __ex_table,"a"; \ | ||
32 | .long 99b - .; \ | ||
33 | lda $31, $exception-99b($31); \ | ||
34 | .previous | ||
35 | |||
36 | .set noat | ||
37 | .set noreorder | ||
38 | .align 4 | ||
39 | |||
40 | .globl __do_clear_user | ||
41 | .ent __do_clear_user | ||
42 | .frame $30, 0, $28 | ||
43 | .prologue 0 | ||
44 | |||
45 | $loop: | ||
46 | and $1, 3, $4 # e0 : | ||
47 | beq $4, 1f # .. e1 : | ||
48 | |||
49 | 0: EX( stq_u $31, 0($6) ) # e0 : zero one word | ||
50 | subq $0, 8, $0 # .. e1 : | ||
51 | subq $4, 1, $4 # e0 : | ||
52 | addq $6, 8, $6 # .. e1 : | ||
53 | bne $4, 0b # e1 : | ||
54 | unop # : | ||
55 | |||
56 | 1: bic $1, 3, $1 # e0 : | ||
57 | beq $1, $tail # .. e1 : | ||
58 | |||
59 | 2: EX( stq_u $31, 0($6) ) # e0 : zero four words | ||
60 | subq $0, 8, $0 # .. e1 : | ||
61 | EX( stq_u $31, 8($6) ) # e0 : | ||
62 | subq $0, 8, $0 # .. e1 : | ||
63 | EX( stq_u $31, 16($6) ) # e0 : | ||
64 | subq $0, 8, $0 # .. e1 : | ||
65 | EX( stq_u $31, 24($6) ) # e0 : | ||
66 | subq $0, 8, $0 # .. e1 : | ||
67 | subq $1, 4, $1 # e0 : | ||
68 | addq $6, 32, $6 # .. e1 : | ||
69 | bne $1, 2b # e1 : | ||
70 | |||
71 | $tail: | ||
72 | bne $2, 1f # e1 : is there a tail to do? | ||
73 | ret $31, ($28), 1 # .. e1 : | ||
74 | |||
75 | 1: EX( ldq_u $5, 0($6) ) # e0 : | ||
76 | clr $0 # .. e1 : | ||
77 | nop # e1 : | ||
78 | mskqh $5, $0, $5 # e0 : | ||
79 | EX( stq_u $5, 0($6) ) # e0 : | ||
80 | ret $31, ($28), 1 # .. e1 : | ||
81 | |||
82 | __do_clear_user: | ||
83 | and $6, 7, $4 # e0 : find dest misalignment | ||
84 | beq $0, $zerolength # .. e1 : | ||
85 | addq $0, $4, $1 # e0 : bias counter | ||
86 | and $1, 7, $2 # e1 : number of bytes in tail | ||
87 | srl $1, 3, $1 # e0 : | ||
88 | beq $4, $loop # .. e1 : | ||
89 | |||
90 | EX( ldq_u $5, 0($6) ) # e0 : load dst word to mask back in | ||
91 | beq $1, $oneword # .. e1 : sub-word store? | ||
92 | |||
93 | mskql $5, $6, $5 # e0 : take care of misaligned head | ||
94 | addq $6, 8, $6 # .. e1 : | ||
95 | EX( stq_u $5, -8($6) ) # e0 : | ||
96 | addq $0, $4, $0 # .. e1 : bytes left -= 8 - misalignment | ||
97 | subq $1, 1, $1 # e0 : | ||
98 | subq $0, 8, $0 # .. e1 : | ||
99 | br $loop # e1 : | ||
100 | unop # : | ||
101 | |||
102 | $oneword: | ||
103 | mskql $5, $6, $4 # e0 : | ||
104 | mskqh $5, $2, $5 # e0 : | ||
105 | or $5, $4, $5 # e1 : | ||
106 | EX( stq_u $5, 0($6) ) # e0 : | ||
107 | clr $0 # .. e1 : | ||
108 | |||
109 | $zerolength: | ||
110 | $exception: | ||
111 | ret $31, ($28), 1 # .. e1 : | ||
112 | |||
113 | .end __do_clear_user | ||
diff --git a/arch/alpha/lib/copy_page.S b/arch/alpha/lib/copy_page.S new file mode 100644 index 000000000000..9f3b97459cc6 --- /dev/null +++ b/arch/alpha/lib/copy_page.S | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/copy_page.S | ||
3 | * | ||
4 | * Copy an entire page. | ||
5 | */ | ||
6 | |||
7 | .text | ||
8 | .align 4 | ||
9 | .global copy_page | ||
10 | .ent copy_page | ||
11 | copy_page: | ||
12 | .prologue 0 | ||
13 | |||
14 | lda $18,128 | ||
15 | nop | ||
16 | unop | ||
17 | nop | ||
18 | |||
19 | 1: ldq $0,0($17) | ||
20 | ldq $1,8($17) | ||
21 | ldq $2,16($17) | ||
22 | ldq $3,24($17) | ||
23 | |||
24 | ldq $4,32($17) | ||
25 | ldq $5,40($17) | ||
26 | ldq $6,48($17) | ||
27 | ldq $7,56($17) | ||
28 | |||
29 | stq $0,0($16) | ||
30 | subq $18,1,$18 | ||
31 | stq $1,8($16) | ||
32 | addq $17,64,$17 | ||
33 | |||
34 | stq $2,16($16) | ||
35 | stq $3,24($16) | ||
36 | stq $4,32($16) | ||
37 | stq $5,40($16) | ||
38 | |||
39 | stq $6,48($16) | ||
40 | stq $7,56($16) | ||
41 | addq $16,64,$16 | ||
42 | bne $18, 1b | ||
43 | |||
44 | ret | ||
45 | nop | ||
46 | unop | ||
47 | nop | ||
48 | |||
49 | .end copy_page | ||
diff --git a/arch/alpha/lib/copy_user.S b/arch/alpha/lib/copy_user.S new file mode 100644 index 000000000000..6f3fab9eb434 --- /dev/null +++ b/arch/alpha/lib/copy_user.S | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/copy_user.S | ||
3 | * | ||
4 | * Copy to/from user space, handling exceptions as we go.. This | ||
5 | * isn't exactly pretty. | ||
6 | * | ||
7 | * This is essentially the same as "memcpy()", but with a few twists. | ||
8 | * Notably, we have to make sure that $0 is always up-to-date and | ||
9 | * contains the right "bytes left to copy" value (and that it is updated | ||
10 | * only _after_ a successful copy). There is also some rather minor | ||
11 | * exception setup stuff.. | ||
12 | * | ||
13 | * NOTE! This is not directly C-callable, because the calling semantics are | ||
14 | * different: | ||
15 | * | ||
16 | * Inputs: | ||
17 | * length in $0 | ||
18 | * destination address in $6 | ||
19 | * source address in $7 | ||
20 | * return address in $28 | ||
21 | * | ||
22 | * Outputs: | ||
23 | * bytes left to copy in $0 | ||
24 | * | ||
25 | * Clobbers: | ||
26 | * $1,$2,$3,$4,$5,$6,$7 | ||
27 | */ | ||
28 | |||
29 | /* Allow an exception for an insn; exit if we get one. */ | ||
30 | #define EXI(x,y...) \ | ||
31 | 99: x,##y; \ | ||
32 | .section __ex_table,"a"; \ | ||
33 | .long 99b - .; \ | ||
34 | lda $31, $exitin-99b($31); \ | ||
35 | .previous | ||
36 | |||
37 | #define EXO(x,y...) \ | ||
38 | 99: x,##y; \ | ||
39 | .section __ex_table,"a"; \ | ||
40 | .long 99b - .; \ | ||
41 | lda $31, $exitout-99b($31); \ | ||
42 | .previous | ||
43 | |||
44 | .set noat | ||
45 | .align 4 | ||
46 | .globl __copy_user | ||
47 | .ent __copy_user | ||
48 | __copy_user: | ||
49 | .prologue 0 | ||
50 | and $6,7,$3 | ||
51 | beq $0,$35 | ||
52 | beq $3,$36 | ||
53 | subq $3,8,$3 | ||
54 | .align 4 | ||
55 | $37: | ||
56 | EXI( ldq_u $1,0($7) ) | ||
57 | EXO( ldq_u $2,0($6) ) | ||
58 | extbl $1,$7,$1 | ||
59 | mskbl $2,$6,$2 | ||
60 | insbl $1,$6,$1 | ||
61 | addq $3,1,$3 | ||
62 | bis $1,$2,$1 | ||
63 | EXO( stq_u $1,0($6) ) | ||
64 | subq $0,1,$0 | ||
65 | addq $6,1,$6 | ||
66 | addq $7,1,$7 | ||
67 | beq $0,$41 | ||
68 | bne $3,$37 | ||
69 | $36: | ||
70 | and $7,7,$1 | ||
71 | bic $0,7,$4 | ||
72 | beq $1,$43 | ||
73 | beq $4,$48 | ||
74 | EXI( ldq_u $3,0($7) ) | ||
75 | .align 4 | ||
76 | $50: | ||
77 | EXI( ldq_u $2,8($7) ) | ||
78 | subq $4,8,$4 | ||
79 | extql $3,$7,$3 | ||
80 | extqh $2,$7,$1 | ||
81 | bis $3,$1,$1 | ||
82 | EXO( stq $1,0($6) ) | ||
83 | addq $7,8,$7 | ||
84 | subq $0,8,$0 | ||
85 | addq $6,8,$6 | ||
86 | bis $2,$2,$3 | ||
87 | bne $4,$50 | ||
88 | $48: | ||
89 | beq $0,$41 | ||
90 | .align 4 | ||
91 | $57: | ||
92 | EXI( ldq_u $1,0($7) ) | ||
93 | EXO( ldq_u $2,0($6) ) | ||
94 | extbl $1,$7,$1 | ||
95 | mskbl $2,$6,$2 | ||
96 | insbl $1,$6,$1 | ||
97 | bis $1,$2,$1 | ||
98 | EXO( stq_u $1,0($6) ) | ||
99 | subq $0,1,$0 | ||
100 | addq $6,1,$6 | ||
101 | addq $7,1,$7 | ||
102 | bne $0,$57 | ||
103 | br $31,$41 | ||
104 | .align 4 | ||
105 | $43: | ||
106 | beq $4,$65 | ||
107 | .align 4 | ||
108 | $66: | ||
109 | EXI( ldq $1,0($7) ) | ||
110 | subq $4,8,$4 | ||
111 | EXO( stq $1,0($6) ) | ||
112 | addq $7,8,$7 | ||
113 | subq $0,8,$0 | ||
114 | addq $6,8,$6 | ||
115 | bne $4,$66 | ||
116 | $65: | ||
117 | beq $0,$41 | ||
118 | EXI( ldq $2,0($7) ) | ||
119 | EXO( ldq $1,0($6) ) | ||
120 | mskql $2,$0,$2 | ||
121 | mskqh $1,$0,$1 | ||
122 | bis $2,$1,$2 | ||
123 | EXO( stq $2,0($6) ) | ||
124 | bis $31,$31,$0 | ||
125 | $41: | ||
126 | $35: | ||
127 | $exitout: | ||
128 | ret $31,($28),1 | ||
129 | |||
130 | $exitin: | ||
131 | /* A stupid byte-by-byte zeroing of the rest of the output | ||
132 | buffer. This cures security holes by never leaving | ||
133 | random kernel data around to be copied elsewhere. */ | ||
134 | |||
135 | mov $0,$1 | ||
136 | $101: | ||
137 | EXO ( ldq_u $2,0($6) ) | ||
138 | subq $1,1,$1 | ||
139 | mskbl $2,$6,$2 | ||
140 | EXO ( stq_u $2,0($6) ) | ||
141 | addq $6,1,$6 | ||
142 | bgt $1,$101 | ||
143 | ret $31,($28),1 | ||
144 | |||
145 | .end __copy_user | ||
diff --git a/arch/alpha/lib/csum_ipv6_magic.S b/arch/alpha/lib/csum_ipv6_magic.S new file mode 100644 index 000000000000..e09748dbf2ed --- /dev/null +++ b/arch/alpha/lib/csum_ipv6_magic.S | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/csum_ipv6_magic.S | ||
3 | * Contributed by Richard Henderson <rth@tamu.edu> | ||
4 | * | ||
5 | * unsigned short csum_ipv6_magic(struct in6_addr *saddr, | ||
6 | * struct in6_addr *daddr, | ||
7 | * __u32 len, | ||
8 | * unsigned short proto, | ||
9 | * unsigned int csum); | ||
10 | */ | ||
11 | |||
12 | .globl csum_ipv6_magic | ||
13 | .align 4 | ||
14 | .ent csum_ipv6_magic | ||
15 | .frame $30,0,$26,0 | ||
16 | csum_ipv6_magic: | ||
17 | .prologue 0 | ||
18 | |||
19 | ldq $0,0($16) # e0 : load src & dst addr words | ||
20 | zapnot $20,15,$20 # .. e1 : zero extend incoming csum | ||
21 | extqh $18,1,$4 # e0 : byte swap len & proto while we wait | ||
22 | ldq $1,8($16) # .. e1 : | ||
23 | |||
24 | extbl $18,1,$5 # e0 : | ||
25 | ldq $2,0($17) # .. e1 : | ||
26 | extbl $18,2,$6 # e0 : | ||
27 | ldq $3,8($17) # .. e1 : | ||
28 | |||
29 | extbl $18,3,$18 # e0 : | ||
30 | sra $4,32,$4 # e0 : | ||
31 | sll $5,16,$5 # e0 : | ||
32 | addq $20,$0,$20 # .. e1 : begin summing the words | ||
33 | |||
34 | sll $6,8,$6 # e0 : | ||
35 | cmpult $20,$0,$0 # .. e1 : | ||
36 | extwh $19,7,$7 # e0 : | ||
37 | or $4,$18,$18 # .. e1 : | ||
38 | |||
39 | extbl $19,1,$19 # e0 : | ||
40 | or $5,$6,$5 # .. e1 : | ||
41 | or $18,$5,$18 # e0 : len complete | ||
42 | or $19,$7,$19 # .. e1 : | ||
43 | |||
44 | sll $19,48,$19 # e0 : | ||
45 | addq $20,$1,$20 # .. e1 : | ||
46 | sra $19,32,$19 # e0 : proto complete | ||
47 | cmpult $20,$1,$1 # .. e1 : | ||
48 | |||
49 | nop # e0 : | ||
50 | addq $20,$2,$20 # .. e1 : | ||
51 | cmpult $20,$2,$2 # e0 : | ||
52 | addq $20,$3,$20 # .. e1 : | ||
53 | |||
54 | cmpult $20,$3,$3 # e0 : | ||
55 | addq $20,$18,$20 # .. e1 : | ||
56 | cmpult $20,$18,$18 # e0 : | ||
57 | addq $20,$19,$20 # .. e1 : | ||
58 | |||
59 | cmpult $20,$19,$19 # e0 : | ||
60 | addq $0,$1,$0 # .. e1 : merge the carries back into the csum | ||
61 | addq $2,$3,$2 # e0 : | ||
62 | addq $18,$19,$18 # .. e1 : | ||
63 | |||
64 | addq $0,$2,$0 # e0 : | ||
65 | addq $20,$18,$20 # .. e1 : | ||
66 | addq $0,$20,$0 # e0 : | ||
67 | unop # : | ||
68 | |||
69 | extwl $0,2,$2 # e0 : begin folding the 64-bit value | ||
70 | zapnot $0,3,$3 # .. e1 : | ||
71 | extwl $0,4,$1 # e0 : | ||
72 | addq $2,$3,$3 # .. e1 : | ||
73 | |||
74 | extwl $0,6,$0 # e0 : | ||
75 | addq $3,$1,$3 # .. e1 : | ||
76 | addq $0,$3,$0 # e0 : | ||
77 | unop # : | ||
78 | |||
79 | extwl $0,2,$1 # e0 : fold 18-bit value | ||
80 | zapnot $0,3,$0 # .. e1 : | ||
81 | addq $0,$1,$0 # e0 : | ||
82 | unop # : | ||
83 | |||
84 | extwl $0,2,$1 # e0 : fold 17-bit value | ||
85 | zapnot $0,3,$0 # .. e1 : | ||
86 | addq $0,$1,$0 # e0 : | ||
87 | not $0,$0 # e1 : and complement. | ||
88 | |||
89 | zapnot $0,3,$0 # e0 : | ||
90 | ret # .. e1 : | ||
91 | |||
92 | .end csum_ipv6_magic | ||
diff --git a/arch/alpha/lib/csum_partial_copy.c b/arch/alpha/lib/csum_partial_copy.c new file mode 100644 index 000000000000..a37948f3037a --- /dev/null +++ b/arch/alpha/lib/csum_partial_copy.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | * csum_partial_copy - do IP checksumming and copy | ||
3 | * | ||
4 | * (C) Copyright 1996 Linus Torvalds | ||
5 | * accellerated versions (and 21264 assembly versions ) contributed by | ||
6 | * Rick Gorton <rick.gorton@alpha-processor.com> | ||
7 | * | ||
8 | * Don't look at this too closely - you'll go mad. The things | ||
9 | * we do for performance.. | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <asm/uaccess.h> | ||
15 | |||
16 | |||
17 | #define ldq_u(x,y) \ | ||
18 | __asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y))) | ||
19 | |||
20 | #define stq_u(x,y) \ | ||
21 | __asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x)) | ||
22 | |||
23 | #define extql(x,y,z) \ | ||
24 | __asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
25 | |||
26 | #define extqh(x,y,z) \ | ||
27 | __asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
28 | |||
29 | #define mskql(x,y,z) \ | ||
30 | __asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
31 | |||
32 | #define mskqh(x,y,z) \ | ||
33 | __asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
34 | |||
35 | #define insql(x,y,z) \ | ||
36 | __asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
37 | |||
38 | #define insqh(x,y,z) \ | ||
39 | __asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y)) | ||
40 | |||
41 | |||
42 | #define __get_user_u(x,ptr) \ | ||
43 | ({ \ | ||
44 | long __guu_err; \ | ||
45 | __asm__ __volatile__( \ | ||
46 | "1: ldq_u %0,%2\n" \ | ||
47 | "2:\n" \ | ||
48 | ".section __ex_table,\"a\"\n" \ | ||
49 | " .long 1b - .\n" \ | ||
50 | " lda %0,2b-1b(%1)\n" \ | ||
51 | ".previous" \ | ||
52 | : "=r"(x), "=r"(__guu_err) \ | ||
53 | : "m"(__m(ptr)), "1"(0)); \ | ||
54 | __guu_err; \ | ||
55 | }) | ||
56 | |||
57 | #define __put_user_u(x,ptr) \ | ||
58 | ({ \ | ||
59 | long __puu_err; \ | ||
60 | __asm__ __volatile__( \ | ||
61 | "1: stq_u %2,%1\n" \ | ||
62 | "2:\n" \ | ||
63 | ".section __ex_table,\"a\"\n" \ | ||
64 | " .long 1b - ." \ | ||
65 | " lda $31,2b-1b(%0)\n" \ | ||
66 | ".previous" \ | ||
67 | : "=r"(__puu_err) \ | ||
68 | : "m"(__m(addr)), "rJ"(x), "0"(0)); \ | ||
69 | __puu_err; \ | ||
70 | }) | ||
71 | |||
72 | |||
73 | static inline unsigned short from64to16(unsigned long x) | ||
74 | { | ||
75 | /* Using extract instructions is a bit more efficient | ||
76 | than the original shift/bitmask version. */ | ||
77 | |||
78 | union { | ||
79 | unsigned long ul; | ||
80 | unsigned int ui[2]; | ||
81 | unsigned short us[4]; | ||
82 | } in_v, tmp_v, out_v; | ||
83 | |||
84 | in_v.ul = x; | ||
85 | tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1]; | ||
86 | |||
87 | /* Since the bits of tmp_v.sh[3] are going to always be zero, | ||
88 | we don't have to bother to add that in. */ | ||
89 | out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1] | ||
90 | + (unsigned long) tmp_v.us[2]; | ||
91 | |||
92 | /* Similarly, out_v.us[2] is always zero for the final add. */ | ||
93 | return out_v.us[0] + out_v.us[1]; | ||
94 | } | ||
95 | |||
96 | |||
97 | |||
98 | /* | ||
99 | * Ok. This isn't fun, but this is the EASY case. | ||
100 | */ | ||
101 | static inline unsigned long | ||
102 | csum_partial_cfu_aligned(const unsigned long __user *src, unsigned long *dst, | ||
103 | long len, unsigned long checksum, | ||
104 | int *errp) | ||
105 | { | ||
106 | unsigned long carry = 0; | ||
107 | int err = 0; | ||
108 | |||
109 | while (len >= 0) { | ||
110 | unsigned long word; | ||
111 | err |= __get_user(word, src); | ||
112 | checksum += carry; | ||
113 | src++; | ||
114 | checksum += word; | ||
115 | len -= 8; | ||
116 | carry = checksum < word; | ||
117 | *dst = word; | ||
118 | dst++; | ||
119 | } | ||
120 | len += 8; | ||
121 | checksum += carry; | ||
122 | if (len) { | ||
123 | unsigned long word, tmp; | ||
124 | err |= __get_user(word, src); | ||
125 | tmp = *dst; | ||
126 | mskql(word, len, word); | ||
127 | checksum += word; | ||
128 | mskqh(tmp, len, tmp); | ||
129 | carry = checksum < word; | ||
130 | *dst = word | tmp; | ||
131 | checksum += carry; | ||
132 | } | ||
133 | if (err) *errp = err; | ||
134 | return checksum; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * This is even less fun, but this is still reasonably | ||
139 | * easy. | ||
140 | */ | ||
141 | static inline unsigned long | ||
142 | csum_partial_cfu_dest_aligned(const unsigned long __user *src, | ||
143 | unsigned long *dst, | ||
144 | unsigned long soff, | ||
145 | long len, unsigned long checksum, | ||
146 | int *errp) | ||
147 | { | ||
148 | unsigned long first; | ||
149 | unsigned long word, carry; | ||
150 | unsigned long lastsrc = 7+len+(unsigned long)src; | ||
151 | int err = 0; | ||
152 | |||
153 | err |= __get_user_u(first,src); | ||
154 | carry = 0; | ||
155 | while (len >= 0) { | ||
156 | unsigned long second; | ||
157 | |||
158 | err |= __get_user_u(second, src+1); | ||
159 | extql(first, soff, word); | ||
160 | len -= 8; | ||
161 | src++; | ||
162 | extqh(second, soff, first); | ||
163 | checksum += carry; | ||
164 | word |= first; | ||
165 | first = second; | ||
166 | checksum += word; | ||
167 | *dst = word; | ||
168 | dst++; | ||
169 | carry = checksum < word; | ||
170 | } | ||
171 | len += 8; | ||
172 | checksum += carry; | ||
173 | if (len) { | ||
174 | unsigned long tmp; | ||
175 | unsigned long second; | ||
176 | err |= __get_user_u(second, lastsrc); | ||
177 | tmp = *dst; | ||
178 | extql(first, soff, word); | ||
179 | extqh(second, soff, first); | ||
180 | word |= first; | ||
181 | mskql(word, len, word); | ||
182 | checksum += word; | ||
183 | mskqh(tmp, len, tmp); | ||
184 | carry = checksum < word; | ||
185 | *dst = word | tmp; | ||
186 | checksum += carry; | ||
187 | } | ||
188 | if (err) *errp = err; | ||
189 | return checksum; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * This is slightly less fun than the above.. | ||
194 | */ | ||
195 | static inline unsigned long | ||
196 | csum_partial_cfu_src_aligned(const unsigned long __user *src, | ||
197 | unsigned long *dst, | ||
198 | unsigned long doff, | ||
199 | long len, unsigned long checksum, | ||
200 | unsigned long partial_dest, | ||
201 | int *errp) | ||
202 | { | ||
203 | unsigned long carry = 0; | ||
204 | unsigned long word; | ||
205 | unsigned long second_dest; | ||
206 | int err = 0; | ||
207 | |||
208 | mskql(partial_dest, doff, partial_dest); | ||
209 | while (len >= 0) { | ||
210 | err |= __get_user(word, src); | ||
211 | len -= 8; | ||
212 | insql(word, doff, second_dest); | ||
213 | checksum += carry; | ||
214 | stq_u(partial_dest | second_dest, dst); | ||
215 | src++; | ||
216 | checksum += word; | ||
217 | insqh(word, doff, partial_dest); | ||
218 | carry = checksum < word; | ||
219 | dst++; | ||
220 | } | ||
221 | len += 8; | ||
222 | if (len) { | ||
223 | checksum += carry; | ||
224 | err |= __get_user(word, src); | ||
225 | mskql(word, len, word); | ||
226 | len -= 8; | ||
227 | checksum += word; | ||
228 | insql(word, doff, second_dest); | ||
229 | len += doff; | ||
230 | carry = checksum < word; | ||
231 | partial_dest |= second_dest; | ||
232 | if (len >= 0) { | ||
233 | stq_u(partial_dest, dst); | ||
234 | if (!len) goto out; | ||
235 | dst++; | ||
236 | insqh(word, doff, partial_dest); | ||
237 | } | ||
238 | doff = len; | ||
239 | } | ||
240 | ldq_u(second_dest, dst); | ||
241 | mskqh(second_dest, doff, second_dest); | ||
242 | stq_u(partial_dest | second_dest, dst); | ||
243 | out: | ||
244 | checksum += carry; | ||
245 | if (err) *errp = err; | ||
246 | return checksum; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * This is so totally un-fun that it's frightening. Don't | ||
251 | * look at this too closely, you'll go blind. | ||
252 | */ | ||
253 | static inline unsigned long | ||
254 | csum_partial_cfu_unaligned(const unsigned long __user * src, | ||
255 | unsigned long * dst, | ||
256 | unsigned long soff, unsigned long doff, | ||
257 | long len, unsigned long checksum, | ||
258 | unsigned long partial_dest, | ||
259 | int *errp) | ||
260 | { | ||
261 | unsigned long carry = 0; | ||
262 | unsigned long first; | ||
263 | unsigned long lastsrc; | ||
264 | int err = 0; | ||
265 | |||
266 | err |= __get_user_u(first, src); | ||
267 | lastsrc = 7+len+(unsigned long)src; | ||
268 | mskql(partial_dest, doff, partial_dest); | ||
269 | while (len >= 0) { | ||
270 | unsigned long second, word; | ||
271 | unsigned long second_dest; | ||
272 | |||
273 | err |= __get_user_u(second, src+1); | ||
274 | extql(first, soff, word); | ||
275 | checksum += carry; | ||
276 | len -= 8; | ||
277 | extqh(second, soff, first); | ||
278 | src++; | ||
279 | word |= first; | ||
280 | first = second; | ||
281 | insql(word, doff, second_dest); | ||
282 | checksum += word; | ||
283 | stq_u(partial_dest | second_dest, dst); | ||
284 | carry = checksum < word; | ||
285 | insqh(word, doff, partial_dest); | ||
286 | dst++; | ||
287 | } | ||
288 | len += doff; | ||
289 | checksum += carry; | ||
290 | if (len >= 0) { | ||
291 | unsigned long second, word; | ||
292 | unsigned long second_dest; | ||
293 | |||
294 | err |= __get_user_u(second, lastsrc); | ||
295 | extql(first, soff, word); | ||
296 | extqh(second, soff, first); | ||
297 | word |= first; | ||
298 | first = second; | ||
299 | mskql(word, len-doff, word); | ||
300 | checksum += word; | ||
301 | insql(word, doff, second_dest); | ||
302 | carry = checksum < word; | ||
303 | stq_u(partial_dest | second_dest, dst); | ||
304 | if (len) { | ||
305 | ldq_u(second_dest, dst+1); | ||
306 | insqh(word, doff, partial_dest); | ||
307 | mskqh(second_dest, len, second_dest); | ||
308 | stq_u(partial_dest | second_dest, dst+1); | ||
309 | } | ||
310 | checksum += carry; | ||
311 | } else { | ||
312 | unsigned long second, word; | ||
313 | unsigned long second_dest; | ||
314 | |||
315 | err |= __get_user_u(second, lastsrc); | ||
316 | extql(first, soff, word); | ||
317 | extqh(second, soff, first); | ||
318 | word |= first; | ||
319 | ldq_u(second_dest, dst); | ||
320 | mskql(word, len-doff, word); | ||
321 | checksum += word; | ||
322 | mskqh(second_dest, len, second_dest); | ||
323 | carry = checksum < word; | ||
324 | insql(word, doff, word); | ||
325 | stq_u(partial_dest | word | second_dest, dst); | ||
326 | checksum += carry; | ||
327 | } | ||
328 | if (err) *errp = err; | ||
329 | return checksum; | ||
330 | } | ||
331 | |||
332 | static unsigned int | ||
333 | do_csum_partial_copy_from_user(const char __user *src, char *dst, int len, | ||
334 | unsigned int sum, int *errp) | ||
335 | { | ||
336 | unsigned long checksum = (unsigned) sum; | ||
337 | unsigned long soff = 7 & (unsigned long) src; | ||
338 | unsigned long doff = 7 & (unsigned long) dst; | ||
339 | |||
340 | if (len) { | ||
341 | if (!doff) { | ||
342 | if (!soff) | ||
343 | checksum = csum_partial_cfu_aligned( | ||
344 | (const unsigned long __user *) src, | ||
345 | (unsigned long *) dst, | ||
346 | len-8, checksum, errp); | ||
347 | else | ||
348 | checksum = csum_partial_cfu_dest_aligned( | ||
349 | (const unsigned long __user *) src, | ||
350 | (unsigned long *) dst, | ||
351 | soff, len-8, checksum, errp); | ||
352 | } else { | ||
353 | unsigned long partial_dest; | ||
354 | ldq_u(partial_dest, dst); | ||
355 | if (!soff) | ||
356 | checksum = csum_partial_cfu_src_aligned( | ||
357 | (const unsigned long __user *) src, | ||
358 | (unsigned long *) dst, | ||
359 | doff, len-8, checksum, | ||
360 | partial_dest, errp); | ||
361 | else | ||
362 | checksum = csum_partial_cfu_unaligned( | ||
363 | (const unsigned long __user *) src, | ||
364 | (unsigned long *) dst, | ||
365 | soff, doff, len-8, checksum, | ||
366 | partial_dest, errp); | ||
367 | } | ||
368 | checksum = from64to16 (checksum); | ||
369 | } | ||
370 | return checksum; | ||
371 | } | ||
372 | |||
373 | unsigned int | ||
374 | csum_partial_copy_from_user(const char __user *src, char *dst, int len, | ||
375 | unsigned int sum, int *errp) | ||
376 | { | ||
377 | if (!access_ok(VERIFY_READ, src, len)) { | ||
378 | *errp = -EFAULT; | ||
379 | memset(dst, 0, len); | ||
380 | return sum; | ||
381 | } | ||
382 | |||
383 | return do_csum_partial_copy_from_user(src, dst, len, sum, errp); | ||
384 | } | ||
385 | |||
386 | unsigned int | ||
387 | csum_partial_copy_nocheck(const char __user *src, char *dst, int len, | ||
388 | unsigned int sum) | ||
389 | { | ||
390 | return do_csum_partial_copy_from_user(src, dst, len, sum, NULL); | ||
391 | } | ||
diff --git a/arch/alpha/lib/dbg_current.S b/arch/alpha/lib/dbg_current.S new file mode 100644 index 000000000000..e6d071015f9b --- /dev/null +++ b/arch/alpha/lib/dbg_current.S | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/dbg_current.S | ||
3 | * Contributed by Richard Henderson (rth@cygnus.com) | ||
4 | * | ||
5 | * Trap if we find current not correct. | ||
6 | */ | ||
7 | |||
8 | #include <asm/pal.h> | ||
9 | |||
10 | .text | ||
11 | .set noat | ||
12 | |||
13 | .globl _mcount | ||
14 | .ent _mcount | ||
15 | _mcount: | ||
16 | .frame $30, 0, $28, 0 | ||
17 | .prologue 0 | ||
18 | |||
19 | lda $0, -0x4000($30) | ||
20 | cmpult $8, $30, $1 | ||
21 | cmpule $0, $30, $2 | ||
22 | and $1, $2, $3 | ||
23 | bne $3, 1f | ||
24 | |||
25 | call_pal PAL_bugchk | ||
26 | |||
27 | 1: ret $31, ($28), 1 | ||
28 | |||
29 | .end _mcount | ||
diff --git a/arch/alpha/lib/dbg_stackcheck.S b/arch/alpha/lib/dbg_stackcheck.S new file mode 100644 index 000000000000..cc5ce3a5fcad --- /dev/null +++ b/arch/alpha/lib/dbg_stackcheck.S | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/stackcheck.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Verify that we have not overflowed the stack. Oops if we have. | ||
6 | */ | ||
7 | |||
8 | #include <asm/asm_offsets.h> | ||
9 | |||
10 | .text | ||
11 | .set noat | ||
12 | |||
13 | .align 3 | ||
14 | .globl _mcount | ||
15 | .ent _mcount | ||
16 | _mcount: | ||
17 | .frame $30, 0, $28, 0 | ||
18 | .prologue 0 | ||
19 | |||
20 | lda $0, TASK_SIZE($8) | ||
21 | cmpult $30, $0, $0 | ||
22 | bne $0, 1f | ||
23 | ret ($28) | ||
24 | 1: stq $31, -8($31) # oops me, damn it. | ||
25 | br 1b | ||
26 | |||
27 | .end _mcount | ||
diff --git a/arch/alpha/lib/dbg_stackkill.S b/arch/alpha/lib/dbg_stackkill.S new file mode 100644 index 000000000000..e09f2ae1e09e --- /dev/null +++ b/arch/alpha/lib/dbg_stackkill.S | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/killstack.S | ||
3 | * Contributed by Richard Henderson (rth@cygnus.com) | ||
4 | * | ||
5 | * Clobber the balance of the kernel stack, hoping to catch | ||
6 | * uninitialized local variables in the act. | ||
7 | */ | ||
8 | |||
9 | #include <asm/asm_offsets.h> | ||
10 | |||
11 | .text | ||
12 | .set noat | ||
13 | |||
14 | .align 5 | ||
15 | .globl _mcount | ||
16 | .ent _mcount | ||
17 | _mcount: | ||
18 | .frame $30, 0, $28, 0 | ||
19 | .prologue 0 | ||
20 | |||
21 | ldi $0, 0xdeadbeef | ||
22 | lda $2, -STACK_SIZE | ||
23 | sll $0, 32, $1 | ||
24 | and $30, $2, $2 | ||
25 | or $0, $1, $0 | ||
26 | lda $2, TASK_SIZE($2) | ||
27 | cmpult $2, $30, $1 | ||
28 | beq $1, 2f | ||
29 | 1: stq $0, 0($2) | ||
30 | addq $2, 8, $2 | ||
31 | cmpult $2, $30, $1 | ||
32 | bne $1, 1b | ||
33 | 2: ret ($28) | ||
34 | |||
35 | .end _mcount | ||
diff --git a/arch/alpha/lib/dec_and_lock.c b/arch/alpha/lib/dec_and_lock.c new file mode 100644 index 000000000000..6ae2500a9d9e --- /dev/null +++ b/arch/alpha/lib/dec_and_lock.c | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/dec_and_lock.c | ||
3 | * | ||
4 | * ll/sc version of atomic_dec_and_lock() | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/spinlock.h> | ||
9 | #include <asm/atomic.h> | ||
10 | |||
11 | asm (".text \n\ | ||
12 | .global _atomic_dec_and_lock \n\ | ||
13 | .ent _atomic_dec_and_lock \n\ | ||
14 | .align 4 \n\ | ||
15 | _atomic_dec_and_lock: \n\ | ||
16 | .prologue 0 \n\ | ||
17 | 1: ldl_l $1, 0($16) \n\ | ||
18 | subl $1, 1, $1 \n\ | ||
19 | beq $1, 2f \n\ | ||
20 | stl_c $1, 0($16) \n\ | ||
21 | beq $1, 4f \n\ | ||
22 | mb \n\ | ||
23 | clr $0 \n\ | ||
24 | ret \n\ | ||
25 | 2: br $29, 3f \n\ | ||
26 | 3: ldgp $29, 0($29) \n\ | ||
27 | br $atomic_dec_and_lock_1..ng \n\ | ||
28 | .subsection 2 \n\ | ||
29 | 4: br 1b \n\ | ||
30 | .previous \n\ | ||
31 | .end _atomic_dec_and_lock"); | ||
32 | |||
33 | static int __attribute_used__ | ||
34 | atomic_dec_and_lock_1(atomic_t *atomic, spinlock_t *lock) | ||
35 | { | ||
36 | /* Slow path */ | ||
37 | spin_lock(lock); | ||
38 | if (atomic_dec_and_test(atomic)) | ||
39 | return 1; | ||
40 | spin_unlock(lock); | ||
41 | return 0; | ||
42 | } | ||
diff --git a/arch/alpha/lib/divide.S b/arch/alpha/lib/divide.S new file mode 100644 index 000000000000..2d1a0484a99e --- /dev/null +++ b/arch/alpha/lib/divide.S | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/divide.S | ||
3 | * | ||
4 | * (C) 1995 Linus Torvalds | ||
5 | * | ||
6 | * Alpha division.. | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * The alpha chip doesn't provide hardware division, so we have to do it | ||
11 | * by hand. The compiler expects the functions | ||
12 | * | ||
13 | * __divqu: 64-bit unsigned long divide | ||
14 | * __remqu: 64-bit unsigned long remainder | ||
15 | * __divqs/__remqs: signed 64-bit | ||
16 | * __divlu/__remlu: unsigned 32-bit | ||
17 | * __divls/__remls: signed 32-bit | ||
18 | * | ||
19 | * These are not normal C functions: instead of the normal | ||
20 | * calling sequence, these expect their arguments in registers | ||
21 | * $24 and $25, and return the result in $27. Register $28 may | ||
22 | * be clobbered (assembly temporary), anything else must be saved. | ||
23 | * | ||
24 | * In short: painful. | ||
25 | * | ||
26 | * This is a rather simple bit-at-a-time algorithm: it's very good | ||
27 | * at dividing random 64-bit numbers, but the more usual case where | ||
28 | * the divisor is small is handled better by the DEC algorithm | ||
29 | * using lookup tables. This uses much less memory, though, and is | ||
30 | * nicer on the cache.. Besides, I don't know the copyright status | ||
31 | * of the DEC code. | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * My temporaries: | ||
36 | * $0 - current bit | ||
37 | * $1 - shifted divisor | ||
38 | * $2 - modulus/quotient | ||
39 | * | ||
40 | * $23 - return address | ||
41 | * $24 - dividend | ||
42 | * $25 - divisor | ||
43 | * | ||
44 | * $27 - quotient/modulus | ||
45 | * $28 - compare status | ||
46 | */ | ||
47 | |||
48 | #define halt .long 0 | ||
49 | |||
50 | /* | ||
51 | * Select function type and registers | ||
52 | */ | ||
53 | #define mask $0 | ||
54 | #define divisor $1 | ||
55 | #define compare $28 | ||
56 | #define tmp1 $3 | ||
57 | #define tmp2 $4 | ||
58 | |||
59 | #ifdef DIV | ||
60 | #define DIV_ONLY(x,y...) x,##y | ||
61 | #define MOD_ONLY(x,y...) | ||
62 | #define func(x) __div##x | ||
63 | #define modulus $2 | ||
64 | #define quotient $27 | ||
65 | #define GETSIGN(x) xor $24,$25,x | ||
66 | #define STACK 48 | ||
67 | #else | ||
68 | #define DIV_ONLY(x,y...) | ||
69 | #define MOD_ONLY(x,y...) x,##y | ||
70 | #define func(x) __rem##x | ||
71 | #define modulus $27 | ||
72 | #define quotient $2 | ||
73 | #define GETSIGN(x) bis $24,$24,x | ||
74 | #define STACK 32 | ||
75 | #endif | ||
76 | |||
77 | /* | ||
78 | * For 32-bit operations, we need to extend to 64-bit | ||
79 | */ | ||
80 | #ifdef INTSIZE | ||
81 | #define ufunction func(lu) | ||
82 | #define sfunction func(l) | ||
83 | #define LONGIFY(x) zapnot x,15,x | ||
84 | #define SLONGIFY(x) addl x,0,x | ||
85 | #else | ||
86 | #define ufunction func(qu) | ||
87 | #define sfunction func(q) | ||
88 | #define LONGIFY(x) | ||
89 | #define SLONGIFY(x) | ||
90 | #endif | ||
91 | |||
92 | .set noat | ||
93 | .align 3 | ||
94 | .globl ufunction | ||
95 | .ent ufunction | ||
96 | ufunction: | ||
97 | subq $30,STACK,$30 | ||
98 | .frame $30,STACK,$23 | ||
99 | .prologue 0 | ||
100 | |||
101 | 7: stq $1, 0($30) | ||
102 | bis $25,$25,divisor | ||
103 | stq $2, 8($30) | ||
104 | bis $24,$24,modulus | ||
105 | stq $0,16($30) | ||
106 | bis $31,$31,quotient | ||
107 | LONGIFY(divisor) | ||
108 | stq tmp1,24($30) | ||
109 | LONGIFY(modulus) | ||
110 | bis $31,1,mask | ||
111 | DIV_ONLY(stq tmp2,32($30)) | ||
112 | beq divisor, 9f /* div by zero */ | ||
113 | |||
114 | #ifdef INTSIZE | ||
115 | /* | ||
116 | * shift divisor left, using 3-bit shifts for | ||
117 | * 32-bit divides as we can't overflow. Three-bit | ||
118 | * shifts will result in looping three times less | ||
119 | * here, but can result in two loops more later. | ||
120 | * Thus using a large shift isn't worth it (and | ||
121 | * s8add pairs better than a sll..) | ||
122 | */ | ||
123 | 1: cmpult divisor,modulus,compare | ||
124 | s8addq divisor,$31,divisor | ||
125 | s8addq mask,$31,mask | ||
126 | bne compare,1b | ||
127 | #else | ||
128 | 1: cmpult divisor,modulus,compare | ||
129 | blt divisor, 2f | ||
130 | addq divisor,divisor,divisor | ||
131 | addq mask,mask,mask | ||
132 | bne compare,1b | ||
133 | unop | ||
134 | #endif | ||
135 | |||
136 | /* ok, start to go right again.. */ | ||
137 | 2: DIV_ONLY(addq quotient,mask,tmp2) | ||
138 | srl mask,1,mask | ||
139 | cmpule divisor,modulus,compare | ||
140 | subq modulus,divisor,tmp1 | ||
141 | DIV_ONLY(cmovne compare,tmp2,quotient) | ||
142 | srl divisor,1,divisor | ||
143 | cmovne compare,tmp1,modulus | ||
144 | bne mask,2b | ||
145 | |||
146 | 9: ldq $1, 0($30) | ||
147 | ldq $2, 8($30) | ||
148 | ldq $0,16($30) | ||
149 | ldq tmp1,24($30) | ||
150 | DIV_ONLY(ldq tmp2,32($30)) | ||
151 | addq $30,STACK,$30 | ||
152 | ret $31,($23),1 | ||
153 | .end ufunction | ||
154 | |||
155 | /* | ||
156 | * Uhh.. Ugly signed division. I'd rather not have it at all, but | ||
157 | * it's needed in some circumstances. There are different ways to | ||
158 | * handle this, really. This does: | ||
159 | * -a / b = a / -b = -(a / b) | ||
160 | * -a % b = -(a % b) | ||
161 | * a % -b = a % b | ||
162 | * which is probably not the best solution, but at least should | ||
163 | * have the property that (x/y)*y + (x%y) = x. | ||
164 | */ | ||
165 | .align 3 | ||
166 | .globl sfunction | ||
167 | .ent sfunction | ||
168 | sfunction: | ||
169 | subq $30,STACK,$30 | ||
170 | .frame $30,STACK,$23 | ||
171 | .prologue 0 | ||
172 | bis $24,$25,$28 | ||
173 | SLONGIFY($28) | ||
174 | bge $28,7b | ||
175 | stq $24,0($30) | ||
176 | subq $31,$24,$28 | ||
177 | stq $25,8($30) | ||
178 | cmovlt $24,$28,$24 /* abs($24) */ | ||
179 | stq $23,16($30) | ||
180 | subq $31,$25,$28 | ||
181 | stq tmp1,24($30) | ||
182 | cmovlt $25,$28,$25 /* abs($25) */ | ||
183 | unop | ||
184 | bsr $23,ufunction | ||
185 | ldq $24,0($30) | ||
186 | ldq $25,8($30) | ||
187 | GETSIGN($28) | ||
188 | subq $31,$27,tmp1 | ||
189 | SLONGIFY($28) | ||
190 | ldq $23,16($30) | ||
191 | cmovlt $28,tmp1,$27 | ||
192 | ldq tmp1,24($30) | ||
193 | addq $30,STACK,$30 | ||
194 | ret $31,($23),1 | ||
195 | .end sfunction | ||
diff --git a/arch/alpha/lib/ev6-clear_page.S b/arch/alpha/lib/ev6-clear_page.S new file mode 100644 index 000000000000..adf4f7be0e2b --- /dev/null +++ b/arch/alpha/lib/ev6-clear_page.S | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-clear_page.S | ||
3 | * | ||
4 | * Zero an entire page. | ||
5 | */ | ||
6 | |||
7 | .text | ||
8 | .align 4 | ||
9 | .global clear_page | ||
10 | .ent clear_page | ||
11 | clear_page: | ||
12 | .prologue 0 | ||
13 | |||
14 | lda $0,128 | ||
15 | lda $1,125 | ||
16 | addq $16,64,$2 | ||
17 | addq $16,128,$3 | ||
18 | |||
19 | addq $16,192,$17 | ||
20 | wh64 ($16) | ||
21 | wh64 ($2) | ||
22 | wh64 ($3) | ||
23 | |||
24 | 1: wh64 ($17) | ||
25 | stq $31,0($16) | ||
26 | subq $0,1,$0 | ||
27 | subq $1,1,$1 | ||
28 | |||
29 | stq $31,8($16) | ||
30 | stq $31,16($16) | ||
31 | addq $17,64,$2 | ||
32 | nop | ||
33 | |||
34 | stq $31,24($16) | ||
35 | stq $31,32($16) | ||
36 | cmovgt $1,$2,$17 | ||
37 | nop | ||
38 | |||
39 | stq $31,40($16) | ||
40 | stq $31,48($16) | ||
41 | nop | ||
42 | nop | ||
43 | |||
44 | stq $31,56($16) | ||
45 | addq $16,64,$16 | ||
46 | nop | ||
47 | bne $0,1b | ||
48 | |||
49 | ret | ||
50 | nop | ||
51 | nop | ||
52 | nop | ||
53 | |||
54 | .end clear_page | ||
diff --git a/arch/alpha/lib/ev6-clear_user.S b/arch/alpha/lib/ev6-clear_user.S new file mode 100644 index 000000000000..4f42a16b7f53 --- /dev/null +++ b/arch/alpha/lib/ev6-clear_user.S | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-clear_user.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Zero user space, handling exceptions as we go. | ||
6 | * | ||
7 | * We have to make sure that $0 is always up-to-date and contains the | ||
8 | * right "bytes left to zero" value (and that it is updated only _after_ | ||
9 | * a successful copy). There is also some rather minor exception setup | ||
10 | * stuff. | ||
11 | * | ||
12 | * NOTE! This is not directly C-callable, because the calling semantics | ||
13 | * are different: | ||
14 | * | ||
15 | * Inputs: | ||
16 | * length in $0 | ||
17 | * destination address in $6 | ||
18 | * exception pointer in $7 | ||
19 | * return address in $28 (exceptions expect it there) | ||
20 | * | ||
21 | * Outputs: | ||
22 | * bytes left to copy in $0 | ||
23 | * | ||
24 | * Clobbers: | ||
25 | * $1,$2,$3,$4,$5,$6 | ||
26 | * | ||
27 | * Much of the information about 21264 scheduling/coding comes from: | ||
28 | * Compiler Writer's Guide for the Alpha 21264 | ||
29 | * abbreviated as 'CWG' in other comments here | ||
30 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
31 | * Scheduling notation: | ||
32 | * E - either cluster | ||
33 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
34 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
35 | * Try not to change the actual algorithm if possible for consistency. | ||
36 | * Determining actual stalls (other than slotting) doesn't appear to be easy to do. | ||
37 | * From perusing the source code context where this routine is called, it is | ||
38 | * a fair assumption that significant fractions of entire pages are zeroed, so | ||
39 | * it's going to be worth the effort to hand-unroll a big loop, and use wh64. | ||
40 | * ASSUMPTION: | ||
41 | * The believed purpose of only updating $0 after a store is that a signal | ||
42 | * may come along during the execution of this chunk of code, and we don't | ||
43 | * want to leave a hole (and we also want to avoid repeating lots of work) | ||
44 | */ | ||
45 | |||
46 | /* Allow an exception for an insn; exit if we get one. */ | ||
47 | #define EX(x,y...) \ | ||
48 | 99: x,##y; \ | ||
49 | .section __ex_table,"a"; \ | ||
50 | .long 99b - .; \ | ||
51 | lda $31, $exception-99b($31); \ | ||
52 | .previous | ||
53 | |||
54 | .set noat | ||
55 | .set noreorder | ||
56 | .align 4 | ||
57 | |||
58 | .globl __do_clear_user | ||
59 | .ent __do_clear_user | ||
60 | .frame $30, 0, $28 | ||
61 | .prologue 0 | ||
62 | |||
63 | # Pipeline info : Slotting & Comments | ||
64 | __do_clear_user: | ||
65 | and $6, 7, $4 # .. E .. .. : find dest head misalignment | ||
66 | beq $0, $zerolength # U .. .. .. : U L U L | ||
67 | |||
68 | addq $0, $4, $1 # .. .. .. E : bias counter | ||
69 | and $1, 7, $2 # .. .. E .. : number of misaligned bytes in tail | ||
70 | # Note - we never actually use $2, so this is a moot computation | ||
71 | # and we can rewrite this later... | ||
72 | srl $1, 3, $1 # .. E .. .. : number of quadwords to clear | ||
73 | beq $4, $headalign # U .. .. .. : U L U L | ||
74 | |||
75 | /* | ||
76 | * Head is not aligned. Write (8 - $4) bytes to head of destination | ||
77 | * This means $6 is known to be misaligned | ||
78 | */ | ||
79 | EX( ldq_u $5, 0($6) ) # .. .. .. L : load dst word to mask back in | ||
80 | beq $1, $onebyte # .. .. U .. : sub-word store? | ||
81 | mskql $5, $6, $5 # .. U .. .. : take care of misaligned head | ||
82 | addq $6, 8, $6 # E .. .. .. : L U U L | ||
83 | |||
84 | EX( stq_u $5, -8($6) ) # .. .. .. L : | ||
85 | subq $1, 1, $1 # .. .. E .. : | ||
86 | addq $0, $4, $0 # .. E .. .. : bytes left -= 8 - misalignment | ||
87 | subq $0, 8, $0 # E .. .. .. : U L U L | ||
88 | |||
89 | .align 4 | ||
90 | /* | ||
91 | * (The .align directive ought to be a moot point) | ||
92 | * values upon initial entry to the loop | ||
93 | * $1 is number of quadwords to clear (zero is a valid value) | ||
94 | * $2 is number of trailing bytes (0..7) ($2 never used...) | ||
95 | * $6 is known to be aligned 0mod8 | ||
96 | */ | ||
97 | $headalign: | ||
98 | subq $1, 16, $4 # .. .. .. E : If < 16, we can not use the huge loop | ||
99 | and $6, 0x3f, $2 # .. .. E .. : Forward work for huge loop | ||
100 | subq $2, 0x40, $3 # .. E .. .. : bias counter (huge loop) | ||
101 | blt $4, $trailquad # U .. .. .. : U L U L | ||
102 | |||
103 | /* | ||
104 | * We know that we're going to do at least 16 quads, which means we are | ||
105 | * going to be able to use the large block clear loop at least once. | ||
106 | * Figure out how many quads we need to clear before we are 0mod64 aligned | ||
107 | * so we can use the wh64 instruction. | ||
108 | */ | ||
109 | |||
110 | nop # .. .. .. E | ||
111 | nop # .. .. E .. | ||
112 | nop # .. E .. .. | ||
113 | beq $3, $bigalign # U .. .. .. : U L U L : Aligned 0mod64 | ||
114 | |||
115 | $alignmod64: | ||
116 | EX( stq_u $31, 0($6) ) # .. .. .. L | ||
117 | addq $3, 8, $3 # .. .. E .. | ||
118 | subq $0, 8, $0 # .. E .. .. | ||
119 | nop # E .. .. .. : U L U L | ||
120 | |||
121 | nop # .. .. .. E | ||
122 | subq $1, 1, $1 # .. .. E .. | ||
123 | addq $6, 8, $6 # .. E .. .. | ||
124 | blt $3, $alignmod64 # U .. .. .. : U L U L | ||
125 | |||
126 | $bigalign: | ||
127 | /* | ||
128 | * $0 is the number of bytes left | ||
129 | * $1 is the number of quads left | ||
130 | * $6 is aligned 0mod64 | ||
131 | * we know that we'll be taking a minimum of one trip through | ||
132 | * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle | ||
133 | * We are _not_ going to update $0 after every single store. That | ||
134 | * would be silly, because there will be cross-cluster dependencies | ||
135 | * no matter how the code is scheduled. By doing it in slightly | ||
136 | * staggered fashion, we can still do this loop in 5 fetches | ||
137 | * The worse case will be doing two extra quads in some future execution, | ||
138 | * in the event of an interrupted clear. | ||
139 | * Assumes the wh64 needs to be for 2 trips through the loop in the future | ||
140 | * The wh64 is issued on for the starting destination address for trip +2 | ||
141 | * through the loop, and if there are less than two trips left, the target | ||
142 | * address will be for the current trip. | ||
143 | */ | ||
144 | nop # E : | ||
145 | nop # E : | ||
146 | nop # E : | ||
147 | bis $6,$6,$3 # E : U L U L : Initial wh64 address is dest | ||
148 | /* This might actually help for the current trip... */ | ||
149 | |||
150 | $do_wh64: | ||
151 | wh64 ($3) # .. .. .. L1 : memory subsystem hint | ||
152 | subq $1, 16, $4 # .. .. E .. : Forward calculation - repeat the loop? | ||
153 | EX( stq_u $31, 0($6) ) # .. L .. .. | ||
154 | subq $0, 8, $0 # E .. .. .. : U L U L | ||
155 | |||
156 | addq $6, 128, $3 # E : Target address of wh64 | ||
157 | EX( stq_u $31, 8($6) ) # L : | ||
158 | EX( stq_u $31, 16($6) ) # L : | ||
159 | subq $0, 16, $0 # E : U L L U | ||
160 | |||
161 | nop # E : | ||
162 | EX( stq_u $31, 24($6) ) # L : | ||
163 | EX( stq_u $31, 32($6) ) # L : | ||
164 | subq $0, 168, $5 # E : U L L U : two trips through the loop left? | ||
165 | /* 168 = 192 - 24, since we've already completed some stores */ | ||
166 | |||
167 | subq $0, 16, $0 # E : | ||
168 | EX( stq_u $31, 40($6) ) # L : | ||
169 | EX( stq_u $31, 48($6) ) # L : | ||
170 | cmovlt $5, $6, $3 # E : U L L U : Latency 2, extra mapping cycle | ||
171 | |||
172 | subq $1, 8, $1 # E : | ||
173 | subq $0, 16, $0 # E : | ||
174 | EX( stq_u $31, 56($6) ) # L : | ||
175 | nop # E : U L U L | ||
176 | |||
177 | nop # E : | ||
178 | subq $0, 8, $0 # E : | ||
179 | addq $6, 64, $6 # E : | ||
180 | bge $4, $do_wh64 # U : U L U L | ||
181 | |||
182 | $trailquad: | ||
183 | # zero to 16 quadwords left to store, plus any trailing bytes | ||
184 | # $1 is the number of quadwords left to go. | ||
185 | # | ||
186 | nop # .. .. .. E | ||
187 | nop # .. .. E .. | ||
188 | nop # .. E .. .. | ||
189 | beq $1, $trailbytes # U .. .. .. : U L U L : Only 0..7 bytes to go | ||
190 | |||
191 | $onequad: | ||
192 | EX( stq_u $31, 0($6) ) # .. .. .. L | ||
193 | subq $1, 1, $1 # .. .. E .. | ||
194 | subq $0, 8, $0 # .. E .. .. | ||
195 | nop # E .. .. .. : U L U L | ||
196 | |||
197 | nop # .. .. .. E | ||
198 | nop # .. .. E .. | ||
199 | addq $6, 8, $6 # .. E .. .. | ||
200 | bgt $1, $onequad # U .. .. .. : U L U L | ||
201 | |||
202 | # We have an unknown number of bytes left to go. | ||
203 | $trailbytes: | ||
204 | nop # .. .. .. E | ||
205 | nop # .. .. E .. | ||
206 | nop # .. E .. .. | ||
207 | beq $0, $zerolength # U .. .. .. : U L U L | ||
208 | |||
209 | # $0 contains the number of bytes left to copy (0..31) | ||
210 | # so we will use $0 as the loop counter | ||
211 | # We know for a fact that $0 > 0 zero due to previous context | ||
212 | $onebyte: | ||
213 | EX( stb $31, 0($6) ) # .. .. .. L | ||
214 | subq $0, 1, $0 # .. .. E .. : | ||
215 | addq $6, 1, $6 # .. E .. .. : | ||
216 | bgt $0, $onebyte # U .. .. .. : U L U L | ||
217 | |||
218 | $zerolength: | ||
219 | $exception: # Destination for exception recovery(?) | ||
220 | nop # .. .. .. E : | ||
221 | nop # .. .. E .. : | ||
222 | nop # .. E .. .. : | ||
223 | ret $31, ($28), 1 # L0 .. .. .. : L U L U | ||
224 | .end __do_clear_user | ||
225 | |||
diff --git a/arch/alpha/lib/ev6-copy_page.S b/arch/alpha/lib/ev6-copy_page.S new file mode 100644 index 000000000000..b789db192754 --- /dev/null +++ b/arch/alpha/lib/ev6-copy_page.S | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-copy_page.S | ||
3 | * | ||
4 | * Copy an entire page. | ||
5 | */ | ||
6 | |||
7 | /* The following comparison of this routine vs the normal copy_page.S | ||
8 | was written by an unnamed ev6 hardware designer and forwarded to me | ||
9 | via Steven Hobbs <hobbs@steven.zko.dec.com>. | ||
10 | |||
11 | First Problem: STQ overflows. | ||
12 | ----------------------------- | ||
13 | |||
14 | It would be nice if EV6 handled every resource overflow efficiently, | ||
15 | but for some it doesn't. Including store queue overflows. It causes | ||
16 | a trap and a restart of the pipe. | ||
17 | |||
18 | To get around this we sometimes use (to borrow a term from a VSSAD | ||
19 | researcher) "aeration". The idea is to slow the rate at which the | ||
20 | processor receives valid instructions by inserting nops in the fetch | ||
21 | path. In doing so, you can prevent the overflow and actually make | ||
22 | the code run faster. You can, of course, take advantage of the fact | ||
23 | that the processor can fetch at most 4 aligned instructions per cycle. | ||
24 | |||
25 | I inserted enough nops to force it to take 10 cycles to fetch the | ||
26 | loop code. In theory, EV6 should be able to execute this loop in | ||
27 | 9 cycles but I was not able to get it to run that fast -- the initial | ||
28 | conditions were such that I could not reach this optimum rate on | ||
29 | (chaotic) EV6. I wrote the code such that everything would issue | ||
30 | in order. | ||
31 | |||
32 | Second Problem: Dcache index matches. | ||
33 | ------------------------------------- | ||
34 | |||
35 | If you are going to use this routine on random aligned pages, there | ||
36 | is a 25% chance that the pages will be at the same dcache indices. | ||
37 | This results in many nasty memory traps without care. | ||
38 | |||
39 | The solution is to schedule the prefetches to avoid the memory | ||
40 | conflicts. I schedule the wh64 prefetches farther ahead of the | ||
41 | read prefetches to avoid this problem. | ||
42 | |||
43 | Third Problem: Needs more prefetching. | ||
44 | -------------------------------------- | ||
45 | |||
46 | In order to improve the code I added deeper prefetching to take the | ||
47 | most advantage of EV6's bandwidth. | ||
48 | |||
49 | I also prefetched the read stream. Note that adding the read prefetch | ||
50 | forced me to add another cycle to the inner-most kernel - up to 11 | ||
51 | from the original 8 cycles per iteration. We could improve performance | ||
52 | further by unrolling the loop and doing multiple prefetches per cycle. | ||
53 | |||
54 | I think that the code below will be very robust and fast code for the | ||
55 | purposes of copying aligned pages. It is slower when both source and | ||
56 | destination pages are in the dcache, but it is my guess that this is | ||
57 | less important than the dcache miss case. */ | ||
58 | |||
59 | |||
60 | .text | ||
61 | .align 4 | ||
62 | .global copy_page | ||
63 | .ent copy_page | ||
64 | copy_page: | ||
65 | .prologue 0 | ||
66 | |||
67 | /* Prefetch 5 read cachelines; write-hint 10 cache lines. */ | ||
68 | wh64 ($16) | ||
69 | ldl $31,0($17) | ||
70 | ldl $31,64($17) | ||
71 | lda $1,1*64($16) | ||
72 | |||
73 | wh64 ($1) | ||
74 | ldl $31,128($17) | ||
75 | ldl $31,192($17) | ||
76 | lda $1,2*64($16) | ||
77 | |||
78 | wh64 ($1) | ||
79 | ldl $31,256($17) | ||
80 | lda $18,118 | ||
81 | lda $1,3*64($16) | ||
82 | |||
83 | wh64 ($1) | ||
84 | nop | ||
85 | lda $1,4*64($16) | ||
86 | lda $2,5*64($16) | ||
87 | |||
88 | wh64 ($1) | ||
89 | wh64 ($2) | ||
90 | lda $1,6*64($16) | ||
91 | lda $2,7*64($16) | ||
92 | |||
93 | wh64 ($1) | ||
94 | wh64 ($2) | ||
95 | lda $1,8*64($16) | ||
96 | lda $2,9*64($16) | ||
97 | |||
98 | wh64 ($1) | ||
99 | wh64 ($2) | ||
100 | lda $19,10*64($16) | ||
101 | nop | ||
102 | |||
103 | /* Main prefetching/write-hinting loop. */ | ||
104 | 1: ldq $0,0($17) | ||
105 | ldq $1,8($17) | ||
106 | unop | ||
107 | unop | ||
108 | |||
109 | unop | ||
110 | unop | ||
111 | ldq $2,16($17) | ||
112 | ldq $3,24($17) | ||
113 | |||
114 | ldq $4,32($17) | ||
115 | ldq $5,40($17) | ||
116 | unop | ||
117 | unop | ||
118 | |||
119 | unop | ||
120 | unop | ||
121 | ldq $6,48($17) | ||
122 | ldq $7,56($17) | ||
123 | |||
124 | ldl $31,320($17) | ||
125 | unop | ||
126 | unop | ||
127 | unop | ||
128 | |||
129 | /* This gives the extra cycle of aeration above the minimum. */ | ||
130 | unop | ||
131 | unop | ||
132 | unop | ||
133 | unop | ||
134 | |||
135 | wh64 ($19) | ||
136 | unop | ||
137 | unop | ||
138 | unop | ||
139 | |||
140 | stq $0,0($16) | ||
141 | subq $18,1,$18 | ||
142 | stq $1,8($16) | ||
143 | unop | ||
144 | |||
145 | unop | ||
146 | stq $2,16($16) | ||
147 | addq $17,64,$17 | ||
148 | stq $3,24($16) | ||
149 | |||
150 | stq $4,32($16) | ||
151 | stq $5,40($16) | ||
152 | addq $19,64,$19 | ||
153 | unop | ||
154 | |||
155 | stq $6,48($16) | ||
156 | stq $7,56($16) | ||
157 | addq $16,64,$16 | ||
158 | bne $18, 1b | ||
159 | |||
160 | /* Prefetch the final 5 cache lines of the read stream. */ | ||
161 | lda $18,10 | ||
162 | ldl $31,320($17) | ||
163 | ldl $31,384($17) | ||
164 | ldl $31,448($17) | ||
165 | |||
166 | ldl $31,512($17) | ||
167 | ldl $31,576($17) | ||
168 | nop | ||
169 | nop | ||
170 | |||
171 | /* Non-prefetching, non-write-hinting cleanup loop for the | ||
172 | final 10 cache lines. */ | ||
173 | 2: ldq $0,0($17) | ||
174 | ldq $1,8($17) | ||
175 | ldq $2,16($17) | ||
176 | ldq $3,24($17) | ||
177 | |||
178 | ldq $4,32($17) | ||
179 | ldq $5,40($17) | ||
180 | ldq $6,48($17) | ||
181 | ldq $7,56($17) | ||
182 | |||
183 | stq $0,0($16) | ||
184 | subq $18,1,$18 | ||
185 | stq $1,8($16) | ||
186 | addq $17,64,$17 | ||
187 | |||
188 | stq $2,16($16) | ||
189 | stq $3,24($16) | ||
190 | stq $4,32($16) | ||
191 | stq $5,40($16) | ||
192 | |||
193 | stq $6,48($16) | ||
194 | stq $7,56($16) | ||
195 | addq $16,64,$16 | ||
196 | bne $18, 2b | ||
197 | |||
198 | ret | ||
199 | nop | ||
200 | unop | ||
201 | nop | ||
202 | |||
203 | .end copy_page | ||
diff --git a/arch/alpha/lib/ev6-copy_user.S b/arch/alpha/lib/ev6-copy_user.S new file mode 100644 index 000000000000..db42ffe9c350 --- /dev/null +++ b/arch/alpha/lib/ev6-copy_user.S | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-copy_user.S | ||
3 | * | ||
4 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
5 | * | ||
6 | * Copy to/from user space, handling exceptions as we go.. This | ||
7 | * isn't exactly pretty. | ||
8 | * | ||
9 | * This is essentially the same as "memcpy()", but with a few twists. | ||
10 | * Notably, we have to make sure that $0 is always up-to-date and | ||
11 | * contains the right "bytes left to copy" value (and that it is updated | ||
12 | * only _after_ a successful copy). There is also some rather minor | ||
13 | * exception setup stuff.. | ||
14 | * | ||
15 | * NOTE! This is not directly C-callable, because the calling semantics are | ||
16 | * different: | ||
17 | * | ||
18 | * Inputs: | ||
19 | * length in $0 | ||
20 | * destination address in $6 | ||
21 | * source address in $7 | ||
22 | * return address in $28 | ||
23 | * | ||
24 | * Outputs: | ||
25 | * bytes left to copy in $0 | ||
26 | * | ||
27 | * Clobbers: | ||
28 | * $1,$2,$3,$4,$5,$6,$7 | ||
29 | * | ||
30 | * Much of the information about 21264 scheduling/coding comes from: | ||
31 | * Compiler Writer's Guide for the Alpha 21264 | ||
32 | * abbreviated as 'CWG' in other comments here | ||
33 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
34 | * Scheduling notation: | ||
35 | * E - either cluster | ||
36 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
37 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
38 | */ | ||
39 | |||
40 | /* Allow an exception for an insn; exit if we get one. */ | ||
41 | #define EXI(x,y...) \ | ||
42 | 99: x,##y; \ | ||
43 | .section __ex_table,"a"; \ | ||
44 | .long 99b - .; \ | ||
45 | lda $31, $exitin-99b($31); \ | ||
46 | .previous | ||
47 | |||
48 | #define EXO(x,y...) \ | ||
49 | 99: x,##y; \ | ||
50 | .section __ex_table,"a"; \ | ||
51 | .long 99b - .; \ | ||
52 | lda $31, $exitout-99b($31); \ | ||
53 | .previous | ||
54 | |||
55 | .set noat | ||
56 | .align 4 | ||
57 | .globl __copy_user | ||
58 | .ent __copy_user | ||
59 | # Pipeline info: Slotting & Comments | ||
60 | __copy_user: | ||
61 | .prologue 0 | ||
62 | subq $0, 32, $1 # .. E .. .. : Is this going to be a small copy? | ||
63 | beq $0, $zerolength # U .. .. .. : U L U L | ||
64 | |||
65 | and $6,7,$3 # .. .. .. E : is leading dest misalignment | ||
66 | ble $1, $onebyteloop # .. .. U .. : 1st branch : small amount of data | ||
67 | beq $3, $destaligned # .. U .. .. : 2nd (one cycle fetcher stall) | ||
68 | subq $3, 8, $3 # E .. .. .. : L U U L : trip counter | ||
69 | /* | ||
70 | * The fetcher stall also hides the 1 cycle cross-cluster stall for $3 (L --> U) | ||
71 | * This loop aligns the destination a byte at a time | ||
72 | * We know we have at least one trip through this loop | ||
73 | */ | ||
74 | $aligndest: | ||
75 | EXI( ldbu $1,0($7) ) # .. .. .. L : Keep loads separate from stores | ||
76 | addq $6,1,$6 # .. .. E .. : Section 3.8 in the CWG | ||
77 | addq $3,1,$3 # .. E .. .. : | ||
78 | nop # E .. .. .. : U L U L | ||
79 | |||
80 | /* | ||
81 | * the -1 is to compensate for the inc($6) done in a previous quadpack | ||
82 | * which allows us zero dependencies within either quadpack in the loop | ||
83 | */ | ||
84 | EXO( stb $1,-1($6) ) # .. .. .. L : | ||
85 | addq $7,1,$7 # .. .. E .. : Section 3.8 in the CWG | ||
86 | subq $0,1,$0 # .. E .. .. : | ||
87 | bne $3, $aligndest # U .. .. .. : U L U L | ||
88 | |||
89 | /* | ||
90 | * If we fell through into here, we have a minimum of 33 - 7 bytes | ||
91 | * If we arrived via branch, we have a minimum of 32 bytes | ||
92 | */ | ||
93 | $destaligned: | ||
94 | and $7,7,$1 # .. .. .. E : Check _current_ source alignment | ||
95 | bic $0,7,$4 # .. .. E .. : number bytes as a quadword loop | ||
96 | EXI( ldq_u $3,0($7) ) # .. L .. .. : Forward fetch for fallthrough code | ||
97 | beq $1,$quadaligned # U .. .. .. : U L U L | ||
98 | |||
99 | /* | ||
100 | * In the worst case, we've just executed an ldq_u here from 0($7) | ||
101 | * and we'll repeat it once if we take the branch | ||
102 | */ | ||
103 | |||
104 | /* Misaligned quadword loop - not unrolled. Leave it that way. */ | ||
105 | $misquad: | ||
106 | EXI( ldq_u $2,8($7) ) # .. .. .. L : | ||
107 | subq $4,8,$4 # .. .. E .. : | ||
108 | extql $3,$7,$3 # .. U .. .. : | ||
109 | extqh $2,$7,$1 # U .. .. .. : U U L L | ||
110 | |||
111 | bis $3,$1,$1 # .. .. .. E : | ||
112 | EXO( stq $1,0($6) ) # .. .. L .. : | ||
113 | addq $7,8,$7 # .. E .. .. : | ||
114 | subq $0,8,$0 # E .. .. .. : U L L U | ||
115 | |||
116 | addq $6,8,$6 # .. .. .. E : | ||
117 | bis $2,$2,$3 # .. .. E .. : | ||
118 | nop # .. E .. .. : | ||
119 | bne $4,$misquad # U .. .. .. : U L U L | ||
120 | |||
121 | nop # .. .. .. E | ||
122 | nop # .. .. E .. | ||
123 | nop # .. E .. .. | ||
124 | beq $0,$zerolength # U .. .. .. : U L U L | ||
125 | |||
126 | /* We know we have at least one trip through the byte loop */ | ||
127 | EXI ( ldbu $2,0($7) ) # .. .. .. L : No loads in the same quad | ||
128 | addq $6,1,$6 # .. .. E .. : as the store (Section 3.8 in CWG) | ||
129 | nop # .. E .. .. : | ||
130 | br $31, $dirtyentry # L0 .. .. .. : L U U L | ||
131 | /* Do the trailing byte loop load, then hop into the store part of the loop */ | ||
132 | |||
133 | /* | ||
134 | * A minimum of (33 - 7) bytes to do a quad at a time. | ||
135 | * Based upon the usage context, it's worth the effort to unroll this loop | ||
136 | * $0 - number of bytes to be moved | ||
137 | * $4 - number of bytes to move as quadwords | ||
138 | * $6 is current destination address | ||
139 | * $7 is current source address | ||
140 | */ | ||
141 | $quadaligned: | ||
142 | subq $4, 32, $2 # .. .. .. E : do not unroll for small stuff | ||
143 | nop # .. .. E .. | ||
144 | nop # .. E .. .. | ||
145 | blt $2, $onequad # U .. .. .. : U L U L | ||
146 | |||
147 | /* | ||
148 | * There is a significant assumption here that the source and destination | ||
149 | * addresses differ by more than 32 bytes. In this particular case, a | ||
150 | * sparsity of registers further bounds this to be a minimum of 8 bytes. | ||
151 | * But if this isn't met, then the output result will be incorrect. | ||
152 | * Furthermore, due to a lack of available registers, we really can't | ||
153 | * unroll this to be an 8x loop (which would enable us to use the wh64 | ||
154 | * instruction memory hint instruction). | ||
155 | */ | ||
156 | $unroll4: | ||
157 | EXI( ldq $1,0($7) ) # .. .. .. L | ||
158 | EXI( ldq $2,8($7) ) # .. .. L .. | ||
159 | subq $4,32,$4 # .. E .. .. | ||
160 | nop # E .. .. .. : U U L L | ||
161 | |||
162 | addq $7,16,$7 # .. .. .. E | ||
163 | EXO( stq $1,0($6) ) # .. .. L .. | ||
164 | EXO( stq $2,8($6) ) # .. L .. .. | ||
165 | subq $0,16,$0 # E .. .. .. : U L L U | ||
166 | |||
167 | addq $6,16,$6 # .. .. .. E | ||
168 | EXI( ldq $1,0($7) ) # .. .. L .. | ||
169 | EXI( ldq $2,8($7) ) # .. L .. .. | ||
170 | subq $4, 32, $3 # E .. .. .. : U U L L : is there enough for another trip? | ||
171 | |||
172 | EXO( stq $1,0($6) ) # .. .. .. L | ||
173 | EXO( stq $2,8($6) ) # .. .. L .. | ||
174 | subq $0,16,$0 # .. E .. .. | ||
175 | addq $7,16,$7 # E .. .. .. : U L L U | ||
176 | |||
177 | nop # .. .. .. E | ||
178 | nop # .. .. E .. | ||
179 | addq $6,16,$6 # .. E .. .. | ||
180 | bgt $3,$unroll4 # U .. .. .. : U L U L | ||
181 | |||
182 | nop | ||
183 | nop | ||
184 | nop | ||
185 | beq $4, $noquads | ||
186 | |||
187 | $onequad: | ||
188 | EXI( ldq $1,0($7) ) | ||
189 | subq $4,8,$4 | ||
190 | addq $7,8,$7 | ||
191 | nop | ||
192 | |||
193 | EXO( stq $1,0($6) ) | ||
194 | subq $0,8,$0 | ||
195 | addq $6,8,$6 | ||
196 | bne $4,$onequad | ||
197 | |||
198 | $noquads: | ||
199 | nop | ||
200 | nop | ||
201 | nop | ||
202 | beq $0,$zerolength | ||
203 | |||
204 | /* | ||
205 | * For small copies (or the tail of a larger copy), do a very simple byte loop. | ||
206 | * There's no point in doing a lot of complex alignment calculations to try to | ||
207 | * to quadword stuff for a small amount of data. | ||
208 | * $0 - remaining number of bytes left to copy | ||
209 | * $6 - current dest addr | ||
210 | * $7 - current source addr | ||
211 | */ | ||
212 | |||
213 | $onebyteloop: | ||
214 | EXI ( ldbu $2,0($7) ) # .. .. .. L : No loads in the same quad | ||
215 | addq $6,1,$6 # .. .. E .. : as the store (Section 3.8 in CWG) | ||
216 | nop # .. E .. .. : | ||
217 | nop # E .. .. .. : U L U L | ||
218 | |||
219 | $dirtyentry: | ||
220 | /* | ||
221 | * the -1 is to compensate for the inc($6) done in a previous quadpack | ||
222 | * which allows us zero dependencies within either quadpack in the loop | ||
223 | */ | ||
224 | EXO ( stb $2,-1($6) ) # .. .. .. L : | ||
225 | addq $7,1,$7 # .. .. E .. : quadpack as the load | ||
226 | subq $0,1,$0 # .. E .. .. : change count _after_ copy | ||
227 | bgt $0,$onebyteloop # U .. .. .. : U L U L | ||
228 | |||
229 | $zerolength: | ||
230 | $exitout: # Destination for exception recovery(?) | ||
231 | nop # .. .. .. E | ||
232 | nop # .. .. E .. | ||
233 | nop # .. E .. .. | ||
234 | ret $31,($28),1 # L0 .. .. .. : L U L U | ||
235 | |||
236 | $exitin: | ||
237 | |||
238 | /* A stupid byte-by-byte zeroing of the rest of the output | ||
239 | buffer. This cures security holes by never leaving | ||
240 | random kernel data around to be copied elsewhere. */ | ||
241 | |||
242 | nop | ||
243 | nop | ||
244 | nop | ||
245 | mov $0,$1 | ||
246 | |||
247 | $101: | ||
248 | EXO ( stb $31,0($6) ) # L | ||
249 | subq $1,1,$1 # E | ||
250 | addq $6,1,$6 # E | ||
251 | bgt $1,$101 # U | ||
252 | |||
253 | nop | ||
254 | nop | ||
255 | nop | ||
256 | ret $31,($28),1 # L0 | ||
257 | |||
258 | .end __copy_user | ||
259 | |||
diff --git a/arch/alpha/lib/ev6-csum_ipv6_magic.S b/arch/alpha/lib/ev6-csum_ipv6_magic.S new file mode 100644 index 000000000000..de1948a69118 --- /dev/null +++ b/arch/alpha/lib/ev6-csum_ipv6_magic.S | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-csum_ipv6_magic.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * unsigned short csum_ipv6_magic(struct in6_addr *saddr, | ||
6 | * struct in6_addr *daddr, | ||
7 | * __u32 len, | ||
8 | * unsigned short proto, | ||
9 | * unsigned int csum); | ||
10 | * | ||
11 | * Much of the information about 21264 scheduling/coding comes from: | ||
12 | * Compiler Writer's Guide for the Alpha 21264 | ||
13 | * abbreviated as 'CWG' in other comments here | ||
14 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
15 | * Scheduling notation: | ||
16 | * E - either cluster | ||
17 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
18 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
19 | * Try not to change the actual algorithm if possible for consistency. | ||
20 | * Determining actual stalls (other than slotting) doesn't appear to be easy to do. | ||
21 | * | ||
22 | * unsigned short csum_ipv6_magic(struct in6_addr *saddr, | ||
23 | * struct in6_addr *daddr, | ||
24 | * __u32 len, | ||
25 | * unsigned short proto, | ||
26 | * unsigned int csum); | ||
27 | * | ||
28 | * Swap <proto> (takes form 0xaabb) | ||
29 | * Then shift it left by 48, so result is: | ||
30 | * 0xbbaa0000 00000000 | ||
31 | * Then turn it back into a sign extended 32-bit item | ||
32 | * 0xbbaa0000 | ||
33 | * | ||
34 | * Swap <len> (an unsigned int) using Mike Burrows' 7-instruction sequence | ||
35 | * (we can't hide the 3-cycle latency of the unpkbw in the 6-instruction sequence) | ||
36 | * Assume input takes form 0xAABBCCDD | ||
37 | * | ||
38 | * Finally, original 'folding' approach is to split the long into 4 unsigned shorts | ||
39 | * add 4 ushorts, resulting in ushort/carry | ||
40 | * add carry bits + ushort --> ushort | ||
41 | * add carry bits + ushort --> ushort (in case the carry results in an overflow) | ||
42 | * Truncate to a ushort. (took 13 instructions) | ||
43 | * From doing some testing, using the approach in checksum.c:from64to16() | ||
44 | * results in the same outcome: | ||
45 | * split into 2 uints, add those, generating a ulong | ||
46 | * add the 3 low ushorts together, generating a uint | ||
47 | * a final add of the 2 lower ushorts | ||
48 | * truncating the result. | ||
49 | */ | ||
50 | |||
51 | .globl csum_ipv6_magic | ||
52 | .align 4 | ||
53 | .ent csum_ipv6_magic | ||
54 | .frame $30,0,$26,0 | ||
55 | csum_ipv6_magic: | ||
56 | .prologue 0 | ||
57 | |||
58 | ldq $0,0($16) # L : Latency: 3 | ||
59 | inslh $18,7,$4 # U : 0000000000AABBCC | ||
60 | ldq $1,8($16) # L : Latency: 3 | ||
61 | sll $19,8,$7 # U : U L U L : 0x00000000 00aabb00 | ||
62 | |||
63 | zapnot $20,15,$20 # U : zero extend incoming csum | ||
64 | ldq $2,0($17) # L : Latency: 3 | ||
65 | sll $19,24,$19 # U : U L L U : 0x000000aa bb000000 | ||
66 | inswl $18,3,$18 # U : 000000CCDD000000 | ||
67 | |||
68 | ldq $3,8($17) # L : Latency: 3 | ||
69 | bis $18,$4,$18 # E : 000000CCDDAABBCC | ||
70 | addl $19,$7,$19 # E : <sign bits>bbaabb00 | ||
71 | nop # E : U L U L | ||
72 | |||
73 | addq $20,$0,$20 # E : begin summing the words | ||
74 | srl $18,16,$4 # U : 0000000000CCDDAA | ||
75 | zap $19,0x3,$19 # U : <sign bits>bbaa0000 | ||
76 | nop # E : L U U L | ||
77 | |||
78 | cmpult $20,$0,$0 # E : | ||
79 | addq $20,$1,$20 # E : | ||
80 | zapnot $18,0xa,$18 # U : 00000000DD00BB00 | ||
81 | zap $4,0xa,$4 # U : U U L L : 0000000000CC00AA | ||
82 | |||
83 | or $18,$4,$18 # E : 00000000DDCCBBAA | ||
84 | nop # E : | ||
85 | cmpult $20,$1,$1 # E : | ||
86 | addq $20,$2,$20 # E : U L U L | ||
87 | |||
88 | cmpult $20,$2,$2 # E : | ||
89 | addq $20,$3,$20 # E : | ||
90 | cmpult $20,$3,$3 # E : (1 cycle stall on $20) | ||
91 | addq $20,$18,$20 # E : U L U L (1 cycle stall on $20) | ||
92 | |||
93 | cmpult $20,$18,$18 # E : | ||
94 | addq $20,$19,$20 # E : (1 cycle stall on $20) | ||
95 | addq $0,$1,$0 # E : merge the carries back into the csum | ||
96 | addq $2,$3,$2 # E : | ||
97 | |||
98 | cmpult $20,$19,$19 # E : | ||
99 | addq $18,$19,$18 # E : (1 cycle stall on $19) | ||
100 | addq $0,$2,$0 # E : | ||
101 | addq $20,$18,$20 # E : U L U L : | ||
102 | /* (1 cycle stall on $18, 2 cycles on $20) */ | ||
103 | |||
104 | addq $0,$20,$0 # E : | ||
105 | zapnot $0,15,$1 # U : Start folding output (1 cycle stall on $0) | ||
106 | nop # E : | ||
107 | srl $0,32,$0 # U : U L U L : (1 cycle stall on $0) | ||
108 | |||
109 | addq $1,$0,$1 # E : Finished generating ulong | ||
110 | extwl $1,2,$2 # U : ushort[1] (1 cycle stall on $1) | ||
111 | zapnot $1,3,$0 # U : ushort[0] (1 cycle stall on $1) | ||
112 | extwl $1,4,$1 # U : ushort[2] (1 cycle stall on $1) | ||
113 | |||
114 | addq $0,$2,$0 # E | ||
115 | addq $0,$1,$3 # E : Finished generating uint | ||
116 | /* (1 cycle stall on $0) */ | ||
117 | extwl $3,2,$1 # U : ushort[1] (1 cycle stall on $3) | ||
118 | nop # E : L U L U | ||
119 | |||
120 | addq $1,$3,$0 # E : Final carry | ||
121 | not $0,$4 # E : complement (1 cycle stall on $0) | ||
122 | zapnot $4,3,$0 # U : clear upper garbage bits | ||
123 | /* (1 cycle stall on $4) */ | ||
124 | ret # L0 : L U L U | ||
125 | |||
126 | .end csum_ipv6_magic | ||
diff --git a/arch/alpha/lib/ev6-divide.S b/arch/alpha/lib/ev6-divide.S new file mode 100644 index 000000000000..2a82b9be93fa --- /dev/null +++ b/arch/alpha/lib/ev6-divide.S | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-divide.S | ||
3 | * | ||
4 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
5 | * | ||
6 | * Alpha division.. | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * The alpha chip doesn't provide hardware division, so we have to do it | ||
11 | * by hand. The compiler expects the functions | ||
12 | * | ||
13 | * __divqu: 64-bit unsigned long divide | ||
14 | * __remqu: 64-bit unsigned long remainder | ||
15 | * __divqs/__remqs: signed 64-bit | ||
16 | * __divlu/__remlu: unsigned 32-bit | ||
17 | * __divls/__remls: signed 32-bit | ||
18 | * | ||
19 | * These are not normal C functions: instead of the normal | ||
20 | * calling sequence, these expect their arguments in registers | ||
21 | * $24 and $25, and return the result in $27. Register $28 may | ||
22 | * be clobbered (assembly temporary), anything else must be saved. | ||
23 | * | ||
24 | * In short: painful. | ||
25 | * | ||
26 | * This is a rather simple bit-at-a-time algorithm: it's very good | ||
27 | * at dividing random 64-bit numbers, but the more usual case where | ||
28 | * the divisor is small is handled better by the DEC algorithm | ||
29 | * using lookup tables. This uses much less memory, though, and is | ||
30 | * nicer on the cache.. Besides, I don't know the copyright status | ||
31 | * of the DEC code. | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * My temporaries: | ||
36 | * $0 - current bit | ||
37 | * $1 - shifted divisor | ||
38 | * $2 - modulus/quotient | ||
39 | * | ||
40 | * $23 - return address | ||
41 | * $24 - dividend | ||
42 | * $25 - divisor | ||
43 | * | ||
44 | * $27 - quotient/modulus | ||
45 | * $28 - compare status | ||
46 | * | ||
47 | * Much of the information about 21264 scheduling/coding comes from: | ||
48 | * Compiler Writer's Guide for the Alpha 21264 | ||
49 | * abbreviated as 'CWG' in other comments here | ||
50 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
51 | * Scheduling notation: | ||
52 | * E - either cluster | ||
53 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
54 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
55 | * Try not to change the actual algorithm if possible for consistency. | ||
56 | */ | ||
57 | |||
58 | #define halt .long 0 | ||
59 | |||
60 | /* | ||
61 | * Select function type and registers | ||
62 | */ | ||
63 | #define mask $0 | ||
64 | #define divisor $1 | ||
65 | #define compare $28 | ||
66 | #define tmp1 $3 | ||
67 | #define tmp2 $4 | ||
68 | |||
69 | #ifdef DIV | ||
70 | #define DIV_ONLY(x,y...) x,##y | ||
71 | #define MOD_ONLY(x,y...) | ||
72 | #define func(x) __div##x | ||
73 | #define modulus $2 | ||
74 | #define quotient $27 | ||
75 | #define GETSIGN(x) xor $24,$25,x | ||
76 | #define STACK 48 | ||
77 | #else | ||
78 | #define DIV_ONLY(x,y...) | ||
79 | #define MOD_ONLY(x,y...) x,##y | ||
80 | #define func(x) __rem##x | ||
81 | #define modulus $27 | ||
82 | #define quotient $2 | ||
83 | #define GETSIGN(x) bis $24,$24,x | ||
84 | #define STACK 32 | ||
85 | #endif | ||
86 | |||
87 | /* | ||
88 | * For 32-bit operations, we need to extend to 64-bit | ||
89 | */ | ||
90 | #ifdef INTSIZE | ||
91 | #define ufunction func(lu) | ||
92 | #define sfunction func(l) | ||
93 | #define LONGIFY(x) zapnot x,15,x | ||
94 | #define SLONGIFY(x) addl x,0,x | ||
95 | #else | ||
96 | #define ufunction func(qu) | ||
97 | #define sfunction func(q) | ||
98 | #define LONGIFY(x) | ||
99 | #define SLONGIFY(x) | ||
100 | #endif | ||
101 | |||
102 | .set noat | ||
103 | .align 4 | ||
104 | .globl ufunction | ||
105 | .ent ufunction | ||
106 | ufunction: | ||
107 | subq $30,STACK,$30 # E : | ||
108 | .frame $30,STACK,$23 | ||
109 | .prologue 0 | ||
110 | |||
111 | 7: stq $1, 0($30) # L : | ||
112 | bis $25,$25,divisor # E : | ||
113 | stq $2, 8($30) # L : L U L U | ||
114 | |||
115 | bis $24,$24,modulus # E : | ||
116 | stq $0,16($30) # L : | ||
117 | bis $31,$31,quotient # E : | ||
118 | LONGIFY(divisor) # E : U L L U | ||
119 | |||
120 | stq tmp1,24($30) # L : | ||
121 | LONGIFY(modulus) # E : | ||
122 | bis $31,1,mask # E : | ||
123 | DIV_ONLY(stq tmp2,32($30)) # L : L U U L | ||
124 | |||
125 | beq divisor, 9f /* div by zero */ | ||
126 | /* | ||
127 | * In spite of the DIV_ONLY being either a non-instruction | ||
128 | * or an actual stq, the addition of the .align directive | ||
129 | * below ensures that label 1 is going to be nicely aligned | ||
130 | */ | ||
131 | |||
132 | .align 4 | ||
133 | #ifdef INTSIZE | ||
134 | /* | ||
135 | * shift divisor left, using 3-bit shifts for | ||
136 | * 32-bit divides as we can't overflow. Three-bit | ||
137 | * shifts will result in looping three times less | ||
138 | * here, but can result in two loops more later. | ||
139 | * Thus using a large shift isn't worth it (and | ||
140 | * s8add pairs better than a sll..) | ||
141 | */ | ||
142 | 1: cmpult divisor,modulus,compare # E : | ||
143 | s8addq divisor,$31,divisor # E : | ||
144 | s8addq mask,$31,mask # E : | ||
145 | bne compare,1b # U : U L U L | ||
146 | #else | ||
147 | 1: cmpult divisor,modulus,compare # E : | ||
148 | nop # E : | ||
149 | nop # E : | ||
150 | blt divisor, 2f # U : U L U L | ||
151 | |||
152 | addq divisor,divisor,divisor # E : | ||
153 | addq mask,mask,mask # E : | ||
154 | unop # E : | ||
155 | bne compare,1b # U : U L U L | ||
156 | #endif | ||
157 | |||
158 | /* ok, start to go right again.. */ | ||
159 | 2: | ||
160 | /* | ||
161 | * Keep things nicely bundled... use a nop instead of not | ||
162 | * having an instruction for DIV_ONLY | ||
163 | */ | ||
164 | #ifdef DIV | ||
165 | DIV_ONLY(addq quotient,mask,tmp2) # E : | ||
166 | #else | ||
167 | nop # E : | ||
168 | #endif | ||
169 | srl mask,1,mask # U : | ||
170 | cmpule divisor,modulus,compare # E : | ||
171 | subq modulus,divisor,tmp1 # E : | ||
172 | |||
173 | #ifdef DIV | ||
174 | DIV_ONLY(cmovne compare,tmp2,quotient) # E : Latency 2, extra map slot | ||
175 | nop # E : as part of the cmovne | ||
176 | srl divisor,1,divisor # U : | ||
177 | nop # E : L U L U | ||
178 | |||
179 | nop # E : | ||
180 | cmovne compare,tmp1,modulus # E : Latency 2, extra map slot | ||
181 | nop # E : as part of the cmovne | ||
182 | bne mask,2b # U : U L U L | ||
183 | #else | ||
184 | srl divisor,1,divisor # U : | ||
185 | cmovne compare,tmp1,modulus # E : Latency 2, extra map slot | ||
186 | nop # E : as part of the cmovne | ||
187 | bne mask,2b # U : U L L U | ||
188 | #endif | ||
189 | |||
190 | 9: ldq $1, 0($30) # L : | ||
191 | ldq $2, 8($30) # L : | ||
192 | nop # E : | ||
193 | nop # E : U U L L | ||
194 | |||
195 | ldq $0,16($30) # L : | ||
196 | ldq tmp1,24($30) # L : | ||
197 | nop # E : | ||
198 | nop # E : | ||
199 | |||
200 | #ifdef DIV | ||
201 | DIV_ONLY(ldq tmp2,32($30)) # L : | ||
202 | #else | ||
203 | nop # E : | ||
204 | #endif | ||
205 | addq $30,STACK,$30 # E : | ||
206 | ret $31,($23),1 # L0 : L U U L | ||
207 | .end ufunction | ||
208 | |||
209 | /* | ||
210 | * Uhh.. Ugly signed division. I'd rather not have it at all, but | ||
211 | * it's needed in some circumstances. There are different ways to | ||
212 | * handle this, really. This does: | ||
213 | * -a / b = a / -b = -(a / b) | ||
214 | * -a % b = -(a % b) | ||
215 | * a % -b = a % b | ||
216 | * which is probably not the best solution, but at least should | ||
217 | * have the property that (x/y)*y + (x%y) = x. | ||
218 | */ | ||
219 | .align 4 | ||
220 | .globl sfunction | ||
221 | .ent sfunction | ||
222 | sfunction: | ||
223 | subq $30,STACK,$30 # E : | ||
224 | .frame $30,STACK,$23 | ||
225 | .prologue 0 | ||
226 | bis $24,$25,$28 # E : | ||
227 | SLONGIFY($28) # E : | ||
228 | bge $28,7b # U : | ||
229 | |||
230 | stq $24,0($30) # L : | ||
231 | subq $31,$24,$28 # E : | ||
232 | stq $25,8($30) # L : | ||
233 | nop # E : U L U L | ||
234 | |||
235 | cmovlt $24,$28,$24 /* abs($24) */ # E : Latency 2, extra map slot | ||
236 | nop # E : as part of the cmov | ||
237 | stq $23,16($30) # L : | ||
238 | subq $31,$25,$28 # E : U L U L | ||
239 | |||
240 | stq tmp1,24($30) # L : | ||
241 | cmovlt $25,$28,$25 /* abs($25) */ # E : Latency 2, extra map slot | ||
242 | nop # E : | ||
243 | bsr $23,ufunction # L0: L U L U | ||
244 | |||
245 | ldq $24,0($30) # L : | ||
246 | ldq $25,8($30) # L : | ||
247 | GETSIGN($28) # E : | ||
248 | subq $31,$27,tmp1 # E : U U L L | ||
249 | |||
250 | SLONGIFY($28) # E : | ||
251 | ldq $23,16($30) # L : | ||
252 | cmovlt $28,tmp1,$27 # E : Latency 2, extra map slot | ||
253 | nop # E : U L L U : as part of the cmov | ||
254 | |||
255 | ldq tmp1,24($30) # L : | ||
256 | nop # E : as part of the cmov | ||
257 | addq $30,STACK,$30 # E : | ||
258 | ret $31,($23),1 # L0 : L U U L | ||
259 | .end sfunction | ||
diff --git a/arch/alpha/lib/ev6-memchr.S b/arch/alpha/lib/ev6-memchr.S new file mode 100644 index 000000000000..a8e843dbcc23 --- /dev/null +++ b/arch/alpha/lib/ev6-memchr.S | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-memchr.S | ||
3 | * | ||
4 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
5 | * | ||
6 | * Finds characters in a memory area. Optimized for the Alpha: | ||
7 | * | ||
8 | * - memory accessed as aligned quadwords only | ||
9 | * - uses cmpbge to compare 8 bytes in parallel | ||
10 | * - does binary search to find 0 byte in last | ||
11 | * quadword (HAKMEM needed 12 instructions to | ||
12 | * do this instead of the 9 instructions that | ||
13 | * binary search needs). | ||
14 | * | ||
15 | * For correctness consider that: | ||
16 | * | ||
17 | * - only minimum number of quadwords may be accessed | ||
18 | * - the third argument is an unsigned long | ||
19 | * | ||
20 | * Much of the information about 21264 scheduling/coding comes from: | ||
21 | * Compiler Writer's Guide for the Alpha 21264 | ||
22 | * abbreviated as 'CWG' in other comments here | ||
23 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
24 | * Scheduling notation: | ||
25 | * E - either cluster | ||
26 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
27 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
28 | * Try not to change the actual algorithm if possible for consistency. | ||
29 | */ | ||
30 | |||
31 | .set noreorder | ||
32 | .set noat | ||
33 | |||
34 | .align 4 | ||
35 | .globl memchr | ||
36 | .ent memchr | ||
37 | memchr: | ||
38 | .frame $30,0,$26,0 | ||
39 | .prologue 0 | ||
40 | |||
41 | # Hack -- if someone passes in (size_t)-1, hoping to just | ||
42 | # search til the end of the address space, we will overflow | ||
43 | # below when we find the address of the last byte. Given | ||
44 | # that we will never have a 56-bit address space, cropping | ||
45 | # the length is the easiest way to avoid trouble. | ||
46 | zap $18, 0x80, $5 # U : Bound length | ||
47 | beq $18, $not_found # U : | ||
48 | ldq_u $1, 0($16) # L : load first quadword Latency=3 | ||
49 | and $17, 0xff, $17 # E : L L U U : 00000000000000ch | ||
50 | |||
51 | insbl $17, 1, $2 # U : 000000000000ch00 | ||
52 | cmpult $18, 9, $4 # E : small (< 1 quad) string? | ||
53 | or $2, $17, $17 # E : 000000000000chch | ||
54 | lda $3, -1($31) # E : U L L U | ||
55 | |||
56 | sll $17, 16, $2 # U : 00000000chch0000 | ||
57 | addq $16, $5, $5 # E : Max search address | ||
58 | or $2, $17, $17 # E : 00000000chchchch | ||
59 | sll $17, 32, $2 # U : U L L U : chchchch00000000 | ||
60 | |||
61 | or $2, $17, $17 # E : chchchchchchchch | ||
62 | extql $1, $16, $7 # U : $7 is upper bits | ||
63 | beq $4, $first_quad # U : | ||
64 | ldq_u $6, -1($5) # L : L U U L : eight or less bytes to search Latency=3 | ||
65 | |||
66 | extqh $6, $16, $6 # U : 2 cycle stall for $6 | ||
67 | mov $16, $0 # E : | ||
68 | nop # E : | ||
69 | or $7, $6, $1 # E : L U L U $1 = quadword starting at $16 | ||
70 | |||
71 | # Deal with the case where at most 8 bytes remain to be searched | ||
72 | # in $1. E.g.: | ||
73 | # $18 = 6 | ||
74 | # $1 = ????c6c5c4c3c2c1 | ||
75 | $last_quad: | ||
76 | negq $18, $6 # E : | ||
77 | xor $17, $1, $1 # E : | ||
78 | srl $3, $6, $6 # U : $6 = mask of $18 bits set | ||
79 | cmpbge $31, $1, $2 # E : L U L U | ||
80 | |||
81 | nop | ||
82 | nop | ||
83 | and $2, $6, $2 # E : | ||
84 | beq $2, $not_found # U : U L U L | ||
85 | |||
86 | $found_it: | ||
87 | #if defined(__alpha_fix__) && defined(__alpha_cix__) | ||
88 | /* | ||
89 | * Since we are guaranteed to have set one of the bits, we don't | ||
90 | * have to worry about coming back with a 0x40 out of cttz... | ||
91 | */ | ||
92 | cttz $2, $3 # U0 : | ||
93 | addq $0, $3, $0 # E : All done | ||
94 | nop # E : | ||
95 | ret # L0 : L U L U | ||
96 | #else | ||
97 | /* | ||
98 | * Slow and clunky. It can probably be improved. | ||
99 | * An exercise left for others. | ||
100 | */ | ||
101 | negq $2, $3 # E : | ||
102 | and $2, $3, $2 # E : | ||
103 | and $2, 0x0f, $1 # E : | ||
104 | addq $0, 4, $3 # E : | ||
105 | |||
106 | cmoveq $1, $3, $0 # E : Latency 2, extra map cycle | ||
107 | nop # E : keep with cmov | ||
108 | and $2, 0x33, $1 # E : | ||
109 | addq $0, 2, $3 # E : U L U L : 2 cycle stall on $0 | ||
110 | |||
111 | cmoveq $1, $3, $0 # E : Latency 2, extra map cycle | ||
112 | nop # E : keep with cmov | ||
113 | and $2, 0x55, $1 # E : | ||
114 | addq $0, 1, $3 # E : U L U L : 2 cycle stall on $0 | ||
115 | |||
116 | cmoveq $1, $3, $0 # E : Latency 2, extra map cycle | ||
117 | nop | ||
118 | nop | ||
119 | ret # L0 : L U L U | ||
120 | #endif | ||
121 | |||
122 | # Deal with the case where $18 > 8 bytes remain to be | ||
123 | # searched. $16 may not be aligned. | ||
124 | .align 4 | ||
125 | $first_quad: | ||
126 | andnot $16, 0x7, $0 # E : | ||
127 | insqh $3, $16, $2 # U : $2 = 0000ffffffffffff ($16<0:2> ff) | ||
128 | xor $1, $17, $1 # E : | ||
129 | or $1, $2, $1 # E : U L U L $1 = ====ffffffffffff | ||
130 | |||
131 | cmpbge $31, $1, $2 # E : | ||
132 | bne $2, $found_it # U : | ||
133 | # At least one byte left to process. | ||
134 | ldq $1, 8($0) # L : | ||
135 | subq $5, 1, $18 # E : U L U L | ||
136 | |||
137 | addq $0, 8, $0 # E : | ||
138 | # Make $18 point to last quad to be accessed (the | ||
139 | # last quad may or may not be partial). | ||
140 | andnot $18, 0x7, $18 # E : | ||
141 | cmpult $0, $18, $2 # E : | ||
142 | beq $2, $final # U : U L U L | ||
143 | |||
144 | # At least two quads remain to be accessed. | ||
145 | |||
146 | subq $18, $0, $4 # E : $4 <- nr quads to be processed | ||
147 | and $4, 8, $4 # E : odd number of quads? | ||
148 | bne $4, $odd_quad_count # U : | ||
149 | # At least three quads remain to be accessed | ||
150 | mov $1, $4 # E : L U L U : move prefetched value to correct reg | ||
151 | |||
152 | .align 4 | ||
153 | $unrolled_loop: | ||
154 | ldq $1, 8($0) # L : prefetch $1 | ||
155 | xor $17, $4, $2 # E : | ||
156 | cmpbge $31, $2, $2 # E : | ||
157 | bne $2, $found_it # U : U L U L | ||
158 | |||
159 | addq $0, 8, $0 # E : | ||
160 | nop # E : | ||
161 | nop # E : | ||
162 | nop # E : | ||
163 | |||
164 | $odd_quad_count: | ||
165 | xor $17, $1, $2 # E : | ||
166 | ldq $4, 8($0) # L : prefetch $4 | ||
167 | cmpbge $31, $2, $2 # E : | ||
168 | addq $0, 8, $6 # E : | ||
169 | |||
170 | bne $2, $found_it # U : | ||
171 | cmpult $6, $18, $6 # E : | ||
172 | addq $0, 8, $0 # E : | ||
173 | nop # E : | ||
174 | |||
175 | bne $6, $unrolled_loop # U : | ||
176 | mov $4, $1 # E : move prefetched value into $1 | ||
177 | nop # E : | ||
178 | nop # E : | ||
179 | |||
180 | $final: subq $5, $0, $18 # E : $18 <- number of bytes left to do | ||
181 | nop # E : | ||
182 | nop # E : | ||
183 | bne $18, $last_quad # U : | ||
184 | |||
185 | $not_found: | ||
186 | mov $31, $0 # E : | ||
187 | nop # E : | ||
188 | nop # E : | ||
189 | ret # L0 : | ||
190 | |||
191 | .end memchr | ||
diff --git a/arch/alpha/lib/ev6-memcpy.S b/arch/alpha/lib/ev6-memcpy.S new file mode 100644 index 000000000000..52b37b0f2af5 --- /dev/null +++ b/arch/alpha/lib/ev6-memcpy.S | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-memcpy.S | ||
3 | * 21264 version by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Reasonably optimized memcpy() routine for the Alpha 21264 | ||
6 | * | ||
7 | * - memory accessed as aligned quadwords only | ||
8 | * - uses bcmpge to compare 8 bytes in parallel | ||
9 | * | ||
10 | * Much of the information about 21264 scheduling/coding comes from: | ||
11 | * Compiler Writer's Guide for the Alpha 21264 | ||
12 | * abbreviated as 'CWG' in other comments here | ||
13 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
14 | * Scheduling notation: | ||
15 | * E - either cluster | ||
16 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
17 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
18 | * | ||
19 | * Temp usage notes: | ||
20 | * $1,$2, - scratch | ||
21 | */ | ||
22 | |||
23 | .set noreorder | ||
24 | .set noat | ||
25 | |||
26 | .align 4 | ||
27 | .globl memcpy | ||
28 | .ent memcpy | ||
29 | memcpy: | ||
30 | .frame $30,0,$26,0 | ||
31 | .prologue 0 | ||
32 | |||
33 | mov $16, $0 # E : copy dest to return | ||
34 | ble $18, $nomoredata # U : done with the copy? | ||
35 | xor $16, $17, $1 # E : are source and dest alignments the same? | ||
36 | and $1, 7, $1 # E : are they the same mod 8? | ||
37 | |||
38 | bne $1, $misaligned # U : Nope - gotta do this the slow way | ||
39 | /* source and dest are same mod 8 address */ | ||
40 | and $16, 7, $1 # E : Are both 0mod8? | ||
41 | beq $1, $both_0mod8 # U : Yes | ||
42 | nop # E : | ||
43 | |||
44 | /* | ||
45 | * source and dest are same misalignment. move a byte at a time | ||
46 | * until a 0mod8 alignment for both is reached. | ||
47 | * At least one byte more to move | ||
48 | */ | ||
49 | |||
50 | $head_align: | ||
51 | ldbu $1, 0($17) # L : grab a byte | ||
52 | subq $18, 1, $18 # E : count-- | ||
53 | addq $17, 1, $17 # E : src++ | ||
54 | stb $1, 0($16) # L : | ||
55 | addq $16, 1, $16 # E : dest++ | ||
56 | and $16, 7, $1 # E : Are we at 0mod8 yet? | ||
57 | ble $18, $nomoredata # U : done with the copy? | ||
58 | bne $1, $head_align # U : | ||
59 | |||
60 | $both_0mod8: | ||
61 | cmple $18, 127, $1 # E : Can we unroll the loop? | ||
62 | bne $1, $no_unroll # U : | ||
63 | and $16, 63, $1 # E : get mod64 alignment | ||
64 | beq $1, $do_unroll # U : no single quads to fiddle | ||
65 | |||
66 | $single_head_quad: | ||
67 | ldq $1, 0($17) # L : get 8 bytes | ||
68 | subq $18, 8, $18 # E : count -= 8 | ||
69 | addq $17, 8, $17 # E : src += 8 | ||
70 | nop # E : | ||
71 | |||
72 | stq $1, 0($16) # L : store | ||
73 | addq $16, 8, $16 # E : dest += 8 | ||
74 | and $16, 63, $1 # E : get mod64 alignment | ||
75 | bne $1, $single_head_quad # U : still not fully aligned | ||
76 | |||
77 | $do_unroll: | ||
78 | addq $16, 64, $7 # E : Initial (+1 trip) wh64 address | ||
79 | cmple $18, 127, $1 # E : Can we go through the unrolled loop? | ||
80 | bne $1, $tail_quads # U : Nope | ||
81 | nop # E : | ||
82 | |||
83 | $unroll_body: | ||
84 | wh64 ($7) # L1 : memory subsystem hint: 64 bytes at | ||
85 | # ($7) are about to be over-written | ||
86 | ldq $6, 0($17) # L0 : bytes 0..7 | ||
87 | nop # E : | ||
88 | nop # E : | ||
89 | |||
90 | ldq $4, 8($17) # L : bytes 8..15 | ||
91 | ldq $5, 16($17) # L : bytes 16..23 | ||
92 | addq $7, 64, $7 # E : Update next wh64 address | ||
93 | nop # E : | ||
94 | |||
95 | ldq $3, 24($17) # L : bytes 24..31 | ||
96 | addq $16, 64, $1 # E : fallback value for wh64 | ||
97 | nop # E : | ||
98 | nop # E : | ||
99 | |||
100 | addq $17, 32, $17 # E : src += 32 bytes | ||
101 | stq $6, 0($16) # L : bytes 0..7 | ||
102 | nop # E : | ||
103 | nop # E : | ||
104 | |||
105 | stq $4, 8($16) # L : bytes 8..15 | ||
106 | stq $5, 16($16) # L : bytes 16..23 | ||
107 | subq $18, 192, $2 # E : At least two more trips to go? | ||
108 | nop # E : | ||
109 | |||
110 | stq $3, 24($16) # L : bytes 24..31 | ||
111 | addq $16, 32, $16 # E : dest += 32 bytes | ||
112 | nop # E : | ||
113 | nop # E : | ||
114 | |||
115 | ldq $6, 0($17) # L : bytes 0..7 | ||
116 | ldq $4, 8($17) # L : bytes 8..15 | ||
117 | cmovlt $2, $1, $7 # E : Latency 2, extra map slot - Use | ||
118 | # fallback wh64 address if < 2 more trips | ||
119 | nop # E : | ||
120 | |||
121 | ldq $5, 16($17) # L : bytes 16..23 | ||
122 | ldq $3, 24($17) # L : bytes 24..31 | ||
123 | addq $16, 32, $16 # E : dest += 32 | ||
124 | subq $18, 64, $18 # E : count -= 64 | ||
125 | |||
126 | addq $17, 32, $17 # E : src += 32 | ||
127 | stq $6, -32($16) # L : bytes 0..7 | ||
128 | stq $4, -24($16) # L : bytes 8..15 | ||
129 | cmple $18, 63, $1 # E : At least one more trip? | ||
130 | |||
131 | stq $5, -16($16) # L : bytes 16..23 | ||
132 | stq $3, -8($16) # L : bytes 24..31 | ||
133 | nop # E : | ||
134 | beq $1, $unroll_body | ||
135 | |||
136 | $tail_quads: | ||
137 | $no_unroll: | ||
138 | .align 4 | ||
139 | subq $18, 8, $18 # E : At least a quad left? | ||
140 | blt $18, $less_than_8 # U : Nope | ||
141 | nop # E : | ||
142 | nop # E : | ||
143 | |||
144 | $move_a_quad: | ||
145 | ldq $1, 0($17) # L : fetch 8 | ||
146 | subq $18, 8, $18 # E : count -= 8 | ||
147 | addq $17, 8, $17 # E : src += 8 | ||
148 | nop # E : | ||
149 | |||
150 | stq $1, 0($16) # L : store 8 | ||
151 | addq $16, 8, $16 # E : dest += 8 | ||
152 | bge $18, $move_a_quad # U : | ||
153 | nop # E : | ||
154 | |||
155 | $less_than_8: | ||
156 | .align 4 | ||
157 | addq $18, 8, $18 # E : add back for trailing bytes | ||
158 | ble $18, $nomoredata # U : All-done | ||
159 | nop # E : | ||
160 | nop # E : | ||
161 | |||
162 | /* Trailing bytes */ | ||
163 | $tail_bytes: | ||
164 | subq $18, 1, $18 # E : count-- | ||
165 | ldbu $1, 0($17) # L : fetch a byte | ||
166 | addq $17, 1, $17 # E : src++ | ||
167 | nop # E : | ||
168 | |||
169 | stb $1, 0($16) # L : store a byte | ||
170 | addq $16, 1, $16 # E : dest++ | ||
171 | bgt $18, $tail_bytes # U : more to be done? | ||
172 | nop # E : | ||
173 | |||
174 | /* branching to exit takes 3 extra cycles, so replicate exit here */ | ||
175 | ret $31, ($26), 1 # L0 : | ||
176 | nop # E : | ||
177 | nop # E : | ||
178 | nop # E : | ||
179 | |||
180 | $misaligned: | ||
181 | mov $0, $4 # E : dest temp | ||
182 | and $0, 7, $1 # E : dest alignment mod8 | ||
183 | beq $1, $dest_0mod8 # U : life doesnt totally suck | ||
184 | nop | ||
185 | |||
186 | $aligndest: | ||
187 | ble $18, $nomoredata # U : | ||
188 | ldbu $1, 0($17) # L : fetch a byte | ||
189 | subq $18, 1, $18 # E : count-- | ||
190 | addq $17, 1, $17 # E : src++ | ||
191 | |||
192 | stb $1, 0($4) # L : store it | ||
193 | addq $4, 1, $4 # E : dest++ | ||
194 | and $4, 7, $1 # E : dest 0mod8 yet? | ||
195 | bne $1, $aligndest # U : go until we are aligned. | ||
196 | |||
197 | /* Source has unknown alignment, but dest is known to be 0mod8 */ | ||
198 | $dest_0mod8: | ||
199 | subq $18, 8, $18 # E : At least a quad left? | ||
200 | blt $18, $misalign_tail # U : Nope | ||
201 | ldq_u $3, 0($17) # L : seed (rotating load) of 8 bytes | ||
202 | nop # E : | ||
203 | |||
204 | $mis_quad: | ||
205 | ldq_u $16, 8($17) # L : Fetch next 8 | ||
206 | extql $3, $17, $3 # U : masking | ||
207 | extqh $16, $17, $1 # U : masking | ||
208 | bis $3, $1, $1 # E : merged bytes to store | ||
209 | |||
210 | subq $18, 8, $18 # E : count -= 8 | ||
211 | addq $17, 8, $17 # E : src += 8 | ||
212 | stq $1, 0($4) # L : store 8 (aligned) | ||
213 | mov $16, $3 # E : "rotate" source data | ||
214 | |||
215 | addq $4, 8, $4 # E : dest += 8 | ||
216 | bge $18, $mis_quad # U : More quads to move | ||
217 | nop | ||
218 | nop | ||
219 | |||
220 | $misalign_tail: | ||
221 | addq $18, 8, $18 # E : account for tail stuff | ||
222 | ble $18, $nomoredata # U : | ||
223 | nop | ||
224 | nop | ||
225 | |||
226 | $misalign_byte: | ||
227 | ldbu $1, 0($17) # L : fetch 1 | ||
228 | subq $18, 1, $18 # E : count-- | ||
229 | addq $17, 1, $17 # E : src++ | ||
230 | nop # E : | ||
231 | |||
232 | stb $1, 0($4) # L : store | ||
233 | addq $4, 1, $4 # E : dest++ | ||
234 | bgt $18, $misalign_byte # U : more to go? | ||
235 | nop | ||
236 | |||
237 | |||
238 | $nomoredata: | ||
239 | ret $31, ($26), 1 # L0 : | ||
240 | nop # E : | ||
241 | nop # E : | ||
242 | nop # E : | ||
243 | |||
244 | .end memcpy | ||
245 | |||
246 | /* For backwards module compatibility. */ | ||
247 | __memcpy = memcpy | ||
248 | .globl __memcpy | ||
diff --git a/arch/alpha/lib/ev6-memset.S b/arch/alpha/lib/ev6-memset.S new file mode 100644 index 000000000000..d8b94e1c7fca --- /dev/null +++ b/arch/alpha/lib/ev6-memset.S | |||
@@ -0,0 +1,597 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-memset.S | ||
3 | * | ||
4 | * This is an efficient (and relatively small) implementation of the C library | ||
5 | * "memset()" function for the 21264 implementation of Alpha. | ||
6 | * | ||
7 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
8 | * | ||
9 | * Much of the information about 21264 scheduling/coding comes from: | ||
10 | * Compiler Writer's Guide for the Alpha 21264 | ||
11 | * abbreviated as 'CWG' in other comments here | ||
12 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
13 | * Scheduling notation: | ||
14 | * E - either cluster | ||
15 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
16 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
17 | * The algorithm for the leading and trailing quadwords remains the same, | ||
18 | * however the loop has been unrolled to enable better memory throughput, | ||
19 | * and the code has been replicated for each of the entry points: __memset | ||
20 | * and __memsetw to permit better scheduling to eliminate the stalling | ||
21 | * encountered during the mask replication. | ||
22 | * A future enhancement might be to put in a byte store loop for really | ||
23 | * small (say < 32 bytes) memset()s. Whether or not that change would be | ||
24 | * a win in the kernel would depend upon the contextual usage. | ||
25 | * WARNING: Maintaining this is going to be more work than the above version, | ||
26 | * as fixes will need to be made in multiple places. The performance gain | ||
27 | * is worth it. | ||
28 | */ | ||
29 | |||
30 | .set noat | ||
31 | .set noreorder | ||
32 | .text | ||
33 | .globl __memset | ||
34 | .globl __memsetw | ||
35 | .globl __constant_c_memset | ||
36 | .globl memset | ||
37 | |||
38 | .ent __memset | ||
39 | .align 5 | ||
40 | __memset: | ||
41 | .frame $30,0,$26,0 | ||
42 | .prologue 0 | ||
43 | |||
44 | /* | ||
45 | * Serious stalling happens. The only way to mitigate this is to | ||
46 | * undertake a major re-write to interleave the constant materialization | ||
47 | * with other parts of the fall-through code. This is important, even | ||
48 | * though it makes maintenance tougher. | ||
49 | * Do this later. | ||
50 | */ | ||
51 | and $17,255,$1 # E : 00000000000000ch | ||
52 | insbl $17,1,$2 # U : 000000000000ch00 | ||
53 | bis $16,$16,$0 # E : return value | ||
54 | ble $18,end_b # U : zero length requested? | ||
55 | |||
56 | addq $18,$16,$6 # E : max address to write to | ||
57 | bis $1,$2,$17 # E : 000000000000chch | ||
58 | insbl $1,2,$3 # U : 0000000000ch0000 | ||
59 | insbl $1,3,$4 # U : 00000000ch000000 | ||
60 | |||
61 | or $3,$4,$3 # E : 00000000chch0000 | ||
62 | inswl $17,4,$5 # U : 0000chch00000000 | ||
63 | xor $16,$6,$1 # E : will complete write be within one quadword? | ||
64 | inswl $17,6,$2 # U : chch000000000000 | ||
65 | |||
66 | or $17,$3,$17 # E : 00000000chchchch | ||
67 | or $2,$5,$2 # E : chchchch00000000 | ||
68 | bic $1,7,$1 # E : fit within a single quadword? | ||
69 | and $16,7,$3 # E : Target addr misalignment | ||
70 | |||
71 | or $17,$2,$17 # E : chchchchchchchch | ||
72 | beq $1,within_quad_b # U : | ||
73 | nop # E : | ||
74 | beq $3,aligned_b # U : target is 0mod8 | ||
75 | |||
76 | /* | ||
77 | * Target address is misaligned, and won't fit within a quadword | ||
78 | */ | ||
79 | ldq_u $4,0($16) # L : Fetch first partial | ||
80 | bis $16,$16,$5 # E : Save the address | ||
81 | insql $17,$16,$2 # U : Insert new bytes | ||
82 | subq $3,8,$3 # E : Invert (for addressing uses) | ||
83 | |||
84 | addq $18,$3,$18 # E : $18 is new count ($3 is negative) | ||
85 | mskql $4,$16,$4 # U : clear relevant parts of the quad | ||
86 | subq $16,$3,$16 # E : $16 is new aligned destination | ||
87 | bis $2,$4,$1 # E : Final bytes | ||
88 | |||
89 | nop | ||
90 | stq_u $1,0($5) # L : Store result | ||
91 | nop | ||
92 | nop | ||
93 | |||
94 | .align 4 | ||
95 | aligned_b: | ||
96 | /* | ||
97 | * We are now guaranteed to be quad aligned, with at least | ||
98 | * one partial quad to write. | ||
99 | */ | ||
100 | |||
101 | sra $18,3,$3 # U : Number of remaining quads to write | ||
102 | and $18,7,$18 # E : Number of trailing bytes to write | ||
103 | bis $16,$16,$5 # E : Save dest address | ||
104 | beq $3,no_quad_b # U : tail stuff only | ||
105 | |||
106 | /* | ||
107 | * it's worth the effort to unroll this and use wh64 if possible | ||
108 | * Lifted a bunch of code from clear_user.S | ||
109 | * At this point, entry values are: | ||
110 | * $16 Current destination address | ||
111 | * $5 A copy of $16 | ||
112 | * $6 The max quadword address to write to | ||
113 | * $18 Number trailer bytes | ||
114 | * $3 Number quads to write | ||
115 | */ | ||
116 | |||
117 | and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop) | ||
118 | subq $3, 16, $4 # E : Only try to unroll if > 128 bytes | ||
119 | subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64) | ||
120 | blt $4, loop_b # U : | ||
121 | |||
122 | /* | ||
123 | * We know we've got at least 16 quads, minimum of one trip | ||
124 | * through unrolled loop. Do a quad at a time to get us 0mod64 | ||
125 | * aligned. | ||
126 | */ | ||
127 | |||
128 | nop # E : | ||
129 | nop # E : | ||
130 | nop # E : | ||
131 | beq $1, $bigalign_b # U : | ||
132 | |||
133 | $alignmod64_b: | ||
134 | stq $17, 0($5) # L : | ||
135 | subq $3, 1, $3 # E : For consistency later | ||
136 | addq $1, 8, $1 # E : Increment towards zero for alignment | ||
137 | addq $5, 8, $4 # E : Initial wh64 address (filler instruction) | ||
138 | |||
139 | nop | ||
140 | nop | ||
141 | addq $5, 8, $5 # E : Inc address | ||
142 | blt $1, $alignmod64_b # U : | ||
143 | |||
144 | $bigalign_b: | ||
145 | /* | ||
146 | * $3 - number quads left to go | ||
147 | * $5 - target address (aligned 0mod64) | ||
148 | * $17 - mask of stuff to store | ||
149 | * Scratch registers available: $7, $2, $4, $1 | ||
150 | * we know that we'll be taking a minimum of one trip through | ||
151 | * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle | ||
152 | * Assumes the wh64 needs to be for 2 trips through the loop in the future | ||
153 | * The wh64 is issued on for the starting destination address for trip +2 | ||
154 | * through the loop, and if there are less than two trips left, the target | ||
155 | * address will be for the current trip. | ||
156 | */ | ||
157 | |||
158 | $do_wh64_b: | ||
159 | wh64 ($4) # L1 : memory subsystem write hint | ||
160 | subq $3, 24, $2 # E : For determining future wh64 addresses | ||
161 | stq $17, 0($5) # L : | ||
162 | nop # E : | ||
163 | |||
164 | addq $5, 128, $4 # E : speculative target of next wh64 | ||
165 | stq $17, 8($5) # L : | ||
166 | stq $17, 16($5) # L : | ||
167 | addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr) | ||
168 | |||
169 | stq $17, 24($5) # L : | ||
170 | stq $17, 32($5) # L : | ||
171 | cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle | ||
172 | nop | ||
173 | |||
174 | stq $17, 40($5) # L : | ||
175 | stq $17, 48($5) # L : | ||
176 | subq $3, 16, $2 # E : Repeat the loop at least once more? | ||
177 | nop | ||
178 | |||
179 | stq $17, 56($5) # L : | ||
180 | addq $5, 64, $5 # E : | ||
181 | subq $3, 8, $3 # E : | ||
182 | bge $2, $do_wh64_b # U : | ||
183 | |||
184 | nop | ||
185 | nop | ||
186 | nop | ||
187 | beq $3, no_quad_b # U : Might have finished already | ||
188 | |||
189 | .align 4 | ||
190 | /* | ||
191 | * Simple loop for trailing quadwords, or for small amounts | ||
192 | * of data (where we can't use an unrolled loop and wh64) | ||
193 | */ | ||
194 | loop_b: | ||
195 | stq $17,0($5) # L : | ||
196 | subq $3,1,$3 # E : Decrement number quads left | ||
197 | addq $5,8,$5 # E : Inc address | ||
198 | bne $3,loop_b # U : more? | ||
199 | |||
200 | no_quad_b: | ||
201 | /* | ||
202 | * Write 0..7 trailing bytes. | ||
203 | */ | ||
204 | nop # E : | ||
205 | beq $18,end_b # U : All done? | ||
206 | ldq $7,0($5) # L : | ||
207 | mskqh $7,$6,$2 # U : Mask final quad | ||
208 | |||
209 | insqh $17,$6,$4 # U : New bits | ||
210 | bis $2,$4,$1 # E : Put it all together | ||
211 | stq $1,0($5) # L : And back to memory | ||
212 | ret $31,($26),1 # L0 : | ||
213 | |||
214 | within_quad_b: | ||
215 | ldq_u $1,0($16) # L : | ||
216 | insql $17,$16,$2 # U : New bits | ||
217 | mskql $1,$16,$4 # U : Clear old | ||
218 | bis $2,$4,$2 # E : New result | ||
219 | |||
220 | mskql $2,$6,$4 # U : | ||
221 | mskqh $1,$6,$2 # U : | ||
222 | bis $2,$4,$1 # E : | ||
223 | stq_u $1,0($16) # L : | ||
224 | |||
225 | end_b: | ||
226 | nop | ||
227 | nop | ||
228 | nop | ||
229 | ret $31,($26),1 # L0 : | ||
230 | .end __memset | ||
231 | |||
232 | /* | ||
233 | * This is the original body of code, prior to replication and | ||
234 | * rescheduling. Leave it here, as there may be calls to this | ||
235 | * entry point. | ||
236 | */ | ||
237 | .align 4 | ||
238 | .ent __constant_c_memset | ||
239 | __constant_c_memset: | ||
240 | .frame $30,0,$26,0 | ||
241 | .prologue 0 | ||
242 | |||
243 | addq $18,$16,$6 # E : max address to write to | ||
244 | bis $16,$16,$0 # E : return value | ||
245 | xor $16,$6,$1 # E : will complete write be within one quadword? | ||
246 | ble $18,end # U : zero length requested? | ||
247 | |||
248 | bic $1,7,$1 # E : fit within a single quadword | ||
249 | beq $1,within_one_quad # U : | ||
250 | and $16,7,$3 # E : Target addr misalignment | ||
251 | beq $3,aligned # U : target is 0mod8 | ||
252 | |||
253 | /* | ||
254 | * Target address is misaligned, and won't fit within a quadword | ||
255 | */ | ||
256 | ldq_u $4,0($16) # L : Fetch first partial | ||
257 | bis $16,$16,$5 # E : Save the address | ||
258 | insql $17,$16,$2 # U : Insert new bytes | ||
259 | subq $3,8,$3 # E : Invert (for addressing uses) | ||
260 | |||
261 | addq $18,$3,$18 # E : $18 is new count ($3 is negative) | ||
262 | mskql $4,$16,$4 # U : clear relevant parts of the quad | ||
263 | subq $16,$3,$16 # E : $16 is new aligned destination | ||
264 | bis $2,$4,$1 # E : Final bytes | ||
265 | |||
266 | nop | ||
267 | stq_u $1,0($5) # L : Store result | ||
268 | nop | ||
269 | nop | ||
270 | |||
271 | .align 4 | ||
272 | aligned: | ||
273 | /* | ||
274 | * We are now guaranteed to be quad aligned, with at least | ||
275 | * one partial quad to write. | ||
276 | */ | ||
277 | |||
278 | sra $18,3,$3 # U : Number of remaining quads to write | ||
279 | and $18,7,$18 # E : Number of trailing bytes to write | ||
280 | bis $16,$16,$5 # E : Save dest address | ||
281 | beq $3,no_quad # U : tail stuff only | ||
282 | |||
283 | /* | ||
284 | * it's worth the effort to unroll this and use wh64 if possible | ||
285 | * Lifted a bunch of code from clear_user.S | ||
286 | * At this point, entry values are: | ||
287 | * $16 Current destination address | ||
288 | * $5 A copy of $16 | ||
289 | * $6 The max quadword address to write to | ||
290 | * $18 Number trailer bytes | ||
291 | * $3 Number quads to write | ||
292 | */ | ||
293 | |||
294 | and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop) | ||
295 | subq $3, 16, $4 # E : Only try to unroll if > 128 bytes | ||
296 | subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64) | ||
297 | blt $4, loop # U : | ||
298 | |||
299 | /* | ||
300 | * We know we've got at least 16 quads, minimum of one trip | ||
301 | * through unrolled loop. Do a quad at a time to get us 0mod64 | ||
302 | * aligned. | ||
303 | */ | ||
304 | |||
305 | nop # E : | ||
306 | nop # E : | ||
307 | nop # E : | ||
308 | beq $1, $bigalign # U : | ||
309 | |||
310 | $alignmod64: | ||
311 | stq $17, 0($5) # L : | ||
312 | subq $3, 1, $3 # E : For consistency later | ||
313 | addq $1, 8, $1 # E : Increment towards zero for alignment | ||
314 | addq $5, 8, $4 # E : Initial wh64 address (filler instruction) | ||
315 | |||
316 | nop | ||
317 | nop | ||
318 | addq $5, 8, $5 # E : Inc address | ||
319 | blt $1, $alignmod64 # U : | ||
320 | |||
321 | $bigalign: | ||
322 | /* | ||
323 | * $3 - number quads left to go | ||
324 | * $5 - target address (aligned 0mod64) | ||
325 | * $17 - mask of stuff to store | ||
326 | * Scratch registers available: $7, $2, $4, $1 | ||
327 | * we know that we'll be taking a minimum of one trip through | ||
328 | * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle | ||
329 | * Assumes the wh64 needs to be for 2 trips through the loop in the future | ||
330 | * The wh64 is issued on for the starting destination address for trip +2 | ||
331 | * through the loop, and if there are less than two trips left, the target | ||
332 | * address will be for the current trip. | ||
333 | */ | ||
334 | |||
335 | $do_wh64: | ||
336 | wh64 ($4) # L1 : memory subsystem write hint | ||
337 | subq $3, 24, $2 # E : For determining future wh64 addresses | ||
338 | stq $17, 0($5) # L : | ||
339 | nop # E : | ||
340 | |||
341 | addq $5, 128, $4 # E : speculative target of next wh64 | ||
342 | stq $17, 8($5) # L : | ||
343 | stq $17, 16($5) # L : | ||
344 | addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr) | ||
345 | |||
346 | stq $17, 24($5) # L : | ||
347 | stq $17, 32($5) # L : | ||
348 | cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle | ||
349 | nop | ||
350 | |||
351 | stq $17, 40($5) # L : | ||
352 | stq $17, 48($5) # L : | ||
353 | subq $3, 16, $2 # E : Repeat the loop at least once more? | ||
354 | nop | ||
355 | |||
356 | stq $17, 56($5) # L : | ||
357 | addq $5, 64, $5 # E : | ||
358 | subq $3, 8, $3 # E : | ||
359 | bge $2, $do_wh64 # U : | ||
360 | |||
361 | nop | ||
362 | nop | ||
363 | nop | ||
364 | beq $3, no_quad # U : Might have finished already | ||
365 | |||
366 | .align 4 | ||
367 | /* | ||
368 | * Simple loop for trailing quadwords, or for small amounts | ||
369 | * of data (where we can't use an unrolled loop and wh64) | ||
370 | */ | ||
371 | loop: | ||
372 | stq $17,0($5) # L : | ||
373 | subq $3,1,$3 # E : Decrement number quads left | ||
374 | addq $5,8,$5 # E : Inc address | ||
375 | bne $3,loop # U : more? | ||
376 | |||
377 | no_quad: | ||
378 | /* | ||
379 | * Write 0..7 trailing bytes. | ||
380 | */ | ||
381 | nop # E : | ||
382 | beq $18,end # U : All done? | ||
383 | ldq $7,0($5) # L : | ||
384 | mskqh $7,$6,$2 # U : Mask final quad | ||
385 | |||
386 | insqh $17,$6,$4 # U : New bits | ||
387 | bis $2,$4,$1 # E : Put it all together | ||
388 | stq $1,0($5) # L : And back to memory | ||
389 | ret $31,($26),1 # L0 : | ||
390 | |||
391 | within_one_quad: | ||
392 | ldq_u $1,0($16) # L : | ||
393 | insql $17,$16,$2 # U : New bits | ||
394 | mskql $1,$16,$4 # U : Clear old | ||
395 | bis $2,$4,$2 # E : New result | ||
396 | |||
397 | mskql $2,$6,$4 # U : | ||
398 | mskqh $1,$6,$2 # U : | ||
399 | bis $2,$4,$1 # E : | ||
400 | stq_u $1,0($16) # L : | ||
401 | |||
402 | end: | ||
403 | nop | ||
404 | nop | ||
405 | nop | ||
406 | ret $31,($26),1 # L0 : | ||
407 | .end __constant_c_memset | ||
408 | |||
409 | /* | ||
410 | * This is a replicant of the __constant_c_memset code, rescheduled | ||
411 | * to mask stalls. Note that entry point names also had to change | ||
412 | */ | ||
413 | .align 5 | ||
414 | .ent __memsetw | ||
415 | |||
416 | __memsetw: | ||
417 | .frame $30,0,$26,0 | ||
418 | .prologue 0 | ||
419 | |||
420 | inswl $17,0,$5 # U : 000000000000c1c2 | ||
421 | inswl $17,2,$2 # U : 00000000c1c20000 | ||
422 | bis $16,$16,$0 # E : return value | ||
423 | addq $18,$16,$6 # E : max address to write to | ||
424 | |||
425 | ble $18, end_w # U : zero length requested? | ||
426 | inswl $17,4,$3 # U : 0000c1c200000000 | ||
427 | inswl $17,6,$4 # U : c1c2000000000000 | ||
428 | xor $16,$6,$1 # E : will complete write be within one quadword? | ||
429 | |||
430 | or $2,$5,$2 # E : 00000000c1c2c1c2 | ||
431 | or $3,$4,$17 # E : c1c2c1c200000000 | ||
432 | bic $1,7,$1 # E : fit within a single quadword | ||
433 | and $16,7,$3 # E : Target addr misalignment | ||
434 | |||
435 | or $17,$2,$17 # E : c1c2c1c2c1c2c1c2 | ||
436 | beq $1,within_quad_w # U : | ||
437 | nop | ||
438 | beq $3,aligned_w # U : target is 0mod8 | ||
439 | |||
440 | /* | ||
441 | * Target address is misaligned, and won't fit within a quadword | ||
442 | */ | ||
443 | ldq_u $4,0($16) # L : Fetch first partial | ||
444 | bis $16,$16,$5 # E : Save the address | ||
445 | insql $17,$16,$2 # U : Insert new bytes | ||
446 | subq $3,8,$3 # E : Invert (for addressing uses) | ||
447 | |||
448 | addq $18,$3,$18 # E : $18 is new count ($3 is negative) | ||
449 | mskql $4,$16,$4 # U : clear relevant parts of the quad | ||
450 | subq $16,$3,$16 # E : $16 is new aligned destination | ||
451 | bis $2,$4,$1 # E : Final bytes | ||
452 | |||
453 | nop | ||
454 | stq_u $1,0($5) # L : Store result | ||
455 | nop | ||
456 | nop | ||
457 | |||
458 | .align 4 | ||
459 | aligned_w: | ||
460 | /* | ||
461 | * We are now guaranteed to be quad aligned, with at least | ||
462 | * one partial quad to write. | ||
463 | */ | ||
464 | |||
465 | sra $18,3,$3 # U : Number of remaining quads to write | ||
466 | and $18,7,$18 # E : Number of trailing bytes to write | ||
467 | bis $16,$16,$5 # E : Save dest address | ||
468 | beq $3,no_quad_w # U : tail stuff only | ||
469 | |||
470 | /* | ||
471 | * it's worth the effort to unroll this and use wh64 if possible | ||
472 | * Lifted a bunch of code from clear_user.S | ||
473 | * At this point, entry values are: | ||
474 | * $16 Current destination address | ||
475 | * $5 A copy of $16 | ||
476 | * $6 The max quadword address to write to | ||
477 | * $18 Number trailer bytes | ||
478 | * $3 Number quads to write | ||
479 | */ | ||
480 | |||
481 | and $16, 0x3f, $2 # E : Forward work (only useful for unrolled loop) | ||
482 | subq $3, 16, $4 # E : Only try to unroll if > 128 bytes | ||
483 | subq $2, 0x40, $1 # E : bias counter (aligning stuff 0mod64) | ||
484 | blt $4, loop_w # U : | ||
485 | |||
486 | /* | ||
487 | * We know we've got at least 16 quads, minimum of one trip | ||
488 | * through unrolled loop. Do a quad at a time to get us 0mod64 | ||
489 | * aligned. | ||
490 | */ | ||
491 | |||
492 | nop # E : | ||
493 | nop # E : | ||
494 | nop # E : | ||
495 | beq $1, $bigalign_w # U : | ||
496 | |||
497 | $alignmod64_w: | ||
498 | stq $17, 0($5) # L : | ||
499 | subq $3, 1, $3 # E : For consistency later | ||
500 | addq $1, 8, $1 # E : Increment towards zero for alignment | ||
501 | addq $5, 8, $4 # E : Initial wh64 address (filler instruction) | ||
502 | |||
503 | nop | ||
504 | nop | ||
505 | addq $5, 8, $5 # E : Inc address | ||
506 | blt $1, $alignmod64_w # U : | ||
507 | |||
508 | $bigalign_w: | ||
509 | /* | ||
510 | * $3 - number quads left to go | ||
511 | * $5 - target address (aligned 0mod64) | ||
512 | * $17 - mask of stuff to store | ||
513 | * Scratch registers available: $7, $2, $4, $1 | ||
514 | * we know that we'll be taking a minimum of one trip through | ||
515 | * CWG Section 3.7.6: do not expect a sustained store rate of > 1/cycle | ||
516 | * Assumes the wh64 needs to be for 2 trips through the loop in the future | ||
517 | * The wh64 is issued on for the starting destination address for trip +2 | ||
518 | * through the loop, and if there are less than two trips left, the target | ||
519 | * address will be for the current trip. | ||
520 | */ | ||
521 | |||
522 | $do_wh64_w: | ||
523 | wh64 ($4) # L1 : memory subsystem write hint | ||
524 | subq $3, 24, $2 # E : For determining future wh64 addresses | ||
525 | stq $17, 0($5) # L : | ||
526 | nop # E : | ||
527 | |||
528 | addq $5, 128, $4 # E : speculative target of next wh64 | ||
529 | stq $17, 8($5) # L : | ||
530 | stq $17, 16($5) # L : | ||
531 | addq $5, 64, $7 # E : Fallback address for wh64 (== next trip addr) | ||
532 | |||
533 | stq $17, 24($5) # L : | ||
534 | stq $17, 32($5) # L : | ||
535 | cmovlt $2, $7, $4 # E : Latency 2, extra mapping cycle | ||
536 | nop | ||
537 | |||
538 | stq $17, 40($5) # L : | ||
539 | stq $17, 48($5) # L : | ||
540 | subq $3, 16, $2 # E : Repeat the loop at least once more? | ||
541 | nop | ||
542 | |||
543 | stq $17, 56($5) # L : | ||
544 | addq $5, 64, $5 # E : | ||
545 | subq $3, 8, $3 # E : | ||
546 | bge $2, $do_wh64_w # U : | ||
547 | |||
548 | nop | ||
549 | nop | ||
550 | nop | ||
551 | beq $3, no_quad_w # U : Might have finished already | ||
552 | |||
553 | .align 4 | ||
554 | /* | ||
555 | * Simple loop for trailing quadwords, or for small amounts | ||
556 | * of data (where we can't use an unrolled loop and wh64) | ||
557 | */ | ||
558 | loop_w: | ||
559 | stq $17,0($5) # L : | ||
560 | subq $3,1,$3 # E : Decrement number quads left | ||
561 | addq $5,8,$5 # E : Inc address | ||
562 | bne $3,loop_w # U : more? | ||
563 | |||
564 | no_quad_w: | ||
565 | /* | ||
566 | * Write 0..7 trailing bytes. | ||
567 | */ | ||
568 | nop # E : | ||
569 | beq $18,end_w # U : All done? | ||
570 | ldq $7,0($5) # L : | ||
571 | mskqh $7,$6,$2 # U : Mask final quad | ||
572 | |||
573 | insqh $17,$6,$4 # U : New bits | ||
574 | bis $2,$4,$1 # E : Put it all together | ||
575 | stq $1,0($5) # L : And back to memory | ||
576 | ret $31,($26),1 # L0 : | ||
577 | |||
578 | within_quad_w: | ||
579 | ldq_u $1,0($16) # L : | ||
580 | insql $17,$16,$2 # U : New bits | ||
581 | mskql $1,$16,$4 # U : Clear old | ||
582 | bis $2,$4,$2 # E : New result | ||
583 | |||
584 | mskql $2,$6,$4 # U : | ||
585 | mskqh $1,$6,$2 # U : | ||
586 | bis $2,$4,$1 # E : | ||
587 | stq_u $1,0($16) # L : | ||
588 | |||
589 | end_w: | ||
590 | nop | ||
591 | nop | ||
592 | nop | ||
593 | ret $31,($26),1 # L0 : | ||
594 | |||
595 | .end __memsetw | ||
596 | |||
597 | memset = __memset | ||
diff --git a/arch/alpha/lib/ev6-strncpy_from_user.S b/arch/alpha/lib/ev6-strncpy_from_user.S new file mode 100644 index 000000000000..d2e28178cacc --- /dev/null +++ b/arch/alpha/lib/ev6-strncpy_from_user.S | |||
@@ -0,0 +1,424 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-strncpy_from_user.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Just like strncpy except in the return value: | ||
6 | * | ||
7 | * -EFAULT if an exception occurs before the terminator is copied. | ||
8 | * N if the buffer filled. | ||
9 | * | ||
10 | * Otherwise the length of the string is returned. | ||
11 | * | ||
12 | * Much of the information about 21264 scheduling/coding comes from: | ||
13 | * Compiler Writer's Guide for the Alpha 21264 | ||
14 | * abbreviated as 'CWG' in other comments here | ||
15 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
16 | * Scheduling notation: | ||
17 | * E - either cluster | ||
18 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
19 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
20 | * A bunch of instructions got moved and temp registers were changed | ||
21 | * to aid in scheduling. Control flow was also re-arranged to eliminate | ||
22 | * branches, and to provide longer code sequences to enable better scheduling. | ||
23 | * A total rewrite (using byte load/stores for start & tail sequences) | ||
24 | * is desirable, but very difficult to do without a from-scratch rewrite. | ||
25 | * Save that for the future. | ||
26 | */ | ||
27 | |||
28 | |||
29 | #include <asm/errno.h> | ||
30 | #include <asm/regdef.h> | ||
31 | |||
32 | |||
33 | /* Allow an exception for an insn; exit if we get one. */ | ||
34 | #define EX(x,y...) \ | ||
35 | 99: x,##y; \ | ||
36 | .section __ex_table,"a"; \ | ||
37 | .long 99b - .; \ | ||
38 | lda $31, $exception-99b($0); \ | ||
39 | .previous | ||
40 | |||
41 | |||
42 | .set noat | ||
43 | .set noreorder | ||
44 | .text | ||
45 | |||
46 | .globl __strncpy_from_user | ||
47 | .ent __strncpy_from_user | ||
48 | .frame $30, 0, $26 | ||
49 | .prologue 0 | ||
50 | |||
51 | .align 4 | ||
52 | __strncpy_from_user: | ||
53 | and a0, 7, t3 # E : find dest misalignment | ||
54 | beq a2, $zerolength # U : | ||
55 | |||
56 | /* Are source and destination co-aligned? */ | ||
57 | mov a0, v0 # E : save the string start | ||
58 | xor a0, a1, t4 # E : | ||
59 | EX( ldq_u t1, 0(a1) ) # L : Latency=3 load first quadword | ||
60 | ldq_u t0, 0(a0) # L : load first (partial) aligned dest quadword | ||
61 | |||
62 | addq a2, t3, a2 # E : bias count by dest misalignment | ||
63 | subq a2, 1, a3 # E : | ||
64 | addq zero, 1, t10 # E : | ||
65 | and t4, 7, t4 # E : misalignment between the two | ||
66 | |||
67 | and a3, 7, t6 # E : number of tail bytes | ||
68 | sll t10, t6, t10 # E : t10 = bitmask of last count byte | ||
69 | bne t4, $unaligned # U : | ||
70 | lda t2, -1 # E : build a mask against false zero | ||
71 | |||
72 | /* | ||
73 | * We are co-aligned; take care of a partial first word. | ||
74 | * On entry to this basic block: | ||
75 | * t0 == the first destination word for masking back in | ||
76 | * t1 == the first source word. | ||
77 | */ | ||
78 | |||
79 | srl a3, 3, a2 # E : a2 = loop counter = (count - 1)/8 | ||
80 | addq a1, 8, a1 # E : | ||
81 | mskqh t2, a1, t2 # U : detection in the src word | ||
82 | nop | ||
83 | |||
84 | /* Create the 1st output word and detect 0's in the 1st input word. */ | ||
85 | mskqh t1, a1, t3 # U : | ||
86 | mskql t0, a1, t0 # U : assemble the first output word | ||
87 | ornot t1, t2, t2 # E : | ||
88 | nop | ||
89 | |||
90 | cmpbge zero, t2, t8 # E : bits set iff null found | ||
91 | or t0, t3, t0 # E : | ||
92 | beq a2, $a_eoc # U : | ||
93 | bne t8, $a_eos # U : 2nd branch in a quad. Bad. | ||
94 | |||
95 | /* On entry to this basic block: | ||
96 | * t0 == a source quad not containing a null. | ||
97 | * a0 - current aligned destination address | ||
98 | * a1 - current aligned source address | ||
99 | * a2 - count of quadwords to move. | ||
100 | * NOTE: Loop improvement - unrolling this is going to be | ||
101 | * a huge win, since we're going to stall otherwise. | ||
102 | * Fix this later. For _really_ large copies, look | ||
103 | * at using wh64 on a look-ahead basis. See the code | ||
104 | * in clear_user.S and copy_user.S. | ||
105 | * Presumably, since (a0) and (a1) do not overlap (by C definition) | ||
106 | * Lots of nops here: | ||
107 | * - Separate loads from stores | ||
108 | * - Keep it to 1 branch/quadpack so the branch predictor | ||
109 | * can train. | ||
110 | */ | ||
111 | $a_loop: | ||
112 | stq_u t0, 0(a0) # L : | ||
113 | addq a0, 8, a0 # E : | ||
114 | nop | ||
115 | subq a2, 1, a2 # E : | ||
116 | |||
117 | EX( ldq_u t0, 0(a1) ) # L : | ||
118 | addq a1, 8, a1 # E : | ||
119 | cmpbge zero, t0, t8 # E : Stall 2 cycles on t0 | ||
120 | beq a2, $a_eoc # U : | ||
121 | |||
122 | beq t8, $a_loop # U : | ||
123 | nop | ||
124 | nop | ||
125 | nop | ||
126 | |||
127 | /* Take care of the final (partial) word store. At this point | ||
128 | * the end-of-count bit is set in t8 iff it applies. | ||
129 | * | ||
130 | * On entry to this basic block we have: | ||
131 | * t0 == the source word containing the null | ||
132 | * t8 == the cmpbge mask that found it. | ||
133 | */ | ||
134 | $a_eos: | ||
135 | negq t8, t12 # E : find low bit set | ||
136 | and t8, t12, t12 # E : | ||
137 | |||
138 | /* We're doing a partial word store and so need to combine | ||
139 | our source and original destination words. */ | ||
140 | ldq_u t1, 0(a0) # L : | ||
141 | subq t12, 1, t6 # E : | ||
142 | |||
143 | or t12, t6, t8 # E : | ||
144 | zapnot t0, t8, t0 # U : clear src bytes > null | ||
145 | zap t1, t8, t1 # U : clear dst bytes <= null | ||
146 | or t0, t1, t0 # E : | ||
147 | |||
148 | stq_u t0, 0(a0) # L : | ||
149 | br $finish_up # L0 : | ||
150 | nop | ||
151 | nop | ||
152 | |||
153 | /* Add the end-of-count bit to the eos detection bitmask. */ | ||
154 | .align 4 | ||
155 | $a_eoc: | ||
156 | or t10, t8, t8 | ||
157 | br $a_eos | ||
158 | nop | ||
159 | nop | ||
160 | |||
161 | |||
162 | /* The source and destination are not co-aligned. Align the destination | ||
163 | and cope. We have to be very careful about not reading too much and | ||
164 | causing a SEGV. */ | ||
165 | |||
166 | .align 4 | ||
167 | $u_head: | ||
168 | /* We know just enough now to be able to assemble the first | ||
169 | full source word. We can still find a zero at the end of it | ||
170 | that prevents us from outputting the whole thing. | ||
171 | |||
172 | On entry to this basic block: | ||
173 | t0 == the first dest word, unmasked | ||
174 | t1 == the shifted low bits of the first source word | ||
175 | t6 == bytemask that is -1 in dest word bytes */ | ||
176 | |||
177 | EX( ldq_u t2, 8(a1) ) # L : load second src word | ||
178 | addq a1, 8, a1 # E : | ||
179 | mskql t0, a0, t0 # U : mask trailing garbage in dst | ||
180 | extqh t2, a1, t4 # U : | ||
181 | |||
182 | or t1, t4, t1 # E : first aligned src word complete | ||
183 | mskqh t1, a0, t1 # U : mask leading garbage in src | ||
184 | or t0, t1, t0 # E : first output word complete | ||
185 | or t0, t6, t6 # E : mask original data for zero test | ||
186 | |||
187 | cmpbge zero, t6, t8 # E : | ||
188 | beq a2, $u_eocfin # U : | ||
189 | bne t8, $u_final # U : bad news - 2nd branch in a quad | ||
190 | lda t6, -1 # E : mask out the bits we have | ||
191 | |||
192 | mskql t6, a1, t6 # U : already seen | ||
193 | stq_u t0, 0(a0) # L : store first output word | ||
194 | or t6, t2, t2 # E : | ||
195 | cmpbge zero, t2, t8 # E : find nulls in second partial | ||
196 | |||
197 | addq a0, 8, a0 # E : | ||
198 | subq a2, 1, a2 # E : | ||
199 | bne t8, $u_late_head_exit # U : | ||
200 | nop | ||
201 | |||
202 | /* Finally, we've got all the stupid leading edge cases taken care | ||
203 | of and we can set up to enter the main loop. */ | ||
204 | |||
205 | extql t2, a1, t1 # U : position hi-bits of lo word | ||
206 | EX( ldq_u t2, 8(a1) ) # L : read next high-order source word | ||
207 | addq a1, 8, a1 # E : | ||
208 | cmpbge zero, t2, t8 # E : | ||
209 | |||
210 | beq a2, $u_eoc # U : | ||
211 | bne t8, $u_eos # U : | ||
212 | nop | ||
213 | nop | ||
214 | |||
215 | /* Unaligned copy main loop. In order to avoid reading too much, | ||
216 | the loop is structured to detect zeros in aligned source words. | ||
217 | This has, unfortunately, effectively pulled half of a loop | ||
218 | iteration out into the head and half into the tail, but it does | ||
219 | prevent nastiness from accumulating in the very thing we want | ||
220 | to run as fast as possible. | ||
221 | |||
222 | On entry to this basic block: | ||
223 | t1 == the shifted high-order bits from the previous source word | ||
224 | t2 == the unshifted current source word | ||
225 | |||
226 | We further know that t2 does not contain a null terminator. */ | ||
227 | |||
228 | /* | ||
229 | * Extra nops here: | ||
230 | * separate load quads from store quads | ||
231 | * only one branch/quad to permit predictor training | ||
232 | */ | ||
233 | |||
234 | .align 4 | ||
235 | $u_loop: | ||
236 | extqh t2, a1, t0 # U : extract high bits for current word | ||
237 | addq a1, 8, a1 # E : | ||
238 | extql t2, a1, t3 # U : extract low bits for next time | ||
239 | addq a0, 8, a0 # E : | ||
240 | |||
241 | or t0, t1, t0 # E : current dst word now complete | ||
242 | EX( ldq_u t2, 0(a1) ) # L : load high word for next time | ||
243 | subq a2, 1, a2 # E : | ||
244 | nop | ||
245 | |||
246 | stq_u t0, -8(a0) # L : save the current word | ||
247 | mov t3, t1 # E : | ||
248 | cmpbge zero, t2, t8 # E : test new word for eos | ||
249 | beq a2, $u_eoc # U : | ||
250 | |||
251 | beq t8, $u_loop # U : | ||
252 | nop | ||
253 | nop | ||
254 | nop | ||
255 | |||
256 | /* We've found a zero somewhere in the source word we just read. | ||
257 | If it resides in the lower half, we have one (probably partial) | ||
258 | word to write out, and if it resides in the upper half, we | ||
259 | have one full and one partial word left to write out. | ||
260 | |||
261 | On entry to this basic block: | ||
262 | t1 == the shifted high-order bits from the previous source word | ||
263 | t2 == the unshifted current source word. */ | ||
264 | .align 4 | ||
265 | $u_eos: | ||
266 | extqh t2, a1, t0 # U : | ||
267 | or t0, t1, t0 # E : first (partial) source word complete | ||
268 | cmpbge zero, t0, t8 # E : is the null in this first bit? | ||
269 | nop | ||
270 | |||
271 | bne t8, $u_final # U : | ||
272 | stq_u t0, 0(a0) # L : the null was in the high-order bits | ||
273 | addq a0, 8, a0 # E : | ||
274 | subq a2, 1, a2 # E : | ||
275 | |||
276 | .align 4 | ||
277 | $u_late_head_exit: | ||
278 | extql t2, a1, t0 # U : | ||
279 | cmpbge zero, t0, t8 # E : | ||
280 | or t8, t10, t6 # E : | ||
281 | cmoveq a2, t6, t8 # E : | ||
282 | |||
283 | /* Take care of a final (probably partial) result word. | ||
284 | On entry to this basic block: | ||
285 | t0 == assembled source word | ||
286 | t8 == cmpbge mask that found the null. */ | ||
287 | .align 4 | ||
288 | $u_final: | ||
289 | negq t8, t6 # E : isolate low bit set | ||
290 | and t6, t8, t12 # E : | ||
291 | ldq_u t1, 0(a0) # L : | ||
292 | subq t12, 1, t6 # E : | ||
293 | |||
294 | or t6, t12, t8 # E : | ||
295 | zapnot t0, t8, t0 # U : kill source bytes > null | ||
296 | zap t1, t8, t1 # U : kill dest bytes <= null | ||
297 | or t0, t1, t0 # E : | ||
298 | |||
299 | stq_u t0, 0(a0) # E : | ||
300 | br $finish_up # U : | ||
301 | nop | ||
302 | nop | ||
303 | |||
304 | .align 4 | ||
305 | $u_eoc: # end-of-count | ||
306 | extqh t2, a1, t0 # U : | ||
307 | or t0, t1, t0 # E : | ||
308 | cmpbge zero, t0, t8 # E : | ||
309 | nop | ||
310 | |||
311 | .align 4 | ||
312 | $u_eocfin: # end-of-count, final word | ||
313 | or t10, t8, t8 # E : | ||
314 | br $u_final # U : | ||
315 | nop | ||
316 | nop | ||
317 | |||
318 | /* Unaligned copy entry point. */ | ||
319 | .align 4 | ||
320 | $unaligned: | ||
321 | |||
322 | srl a3, 3, a2 # U : a2 = loop counter = (count - 1)/8 | ||
323 | and a0, 7, t4 # E : find dest misalignment | ||
324 | and a1, 7, t5 # E : find src misalignment | ||
325 | mov zero, t0 # E : | ||
326 | |||
327 | /* Conditionally load the first destination word and a bytemask | ||
328 | with 0xff indicating that the destination byte is sacrosanct. */ | ||
329 | |||
330 | mov zero, t6 # E : | ||
331 | beq t4, 1f # U : | ||
332 | ldq_u t0, 0(a0) # L : | ||
333 | lda t6, -1 # E : | ||
334 | |||
335 | mskql t6, a0, t6 # E : | ||
336 | nop | ||
337 | nop | ||
338 | nop | ||
339 | |||
340 | .align 4 | ||
341 | 1: | ||
342 | subq a1, t4, a1 # E : sub dest misalignment from src addr | ||
343 | /* If source misalignment is larger than dest misalignment, we need | ||
344 | extra startup checks to avoid SEGV. */ | ||
345 | cmplt t4, t5, t12 # E : | ||
346 | extql t1, a1, t1 # U : shift src into place | ||
347 | lda t2, -1 # E : for creating masks later | ||
348 | |||
349 | beq t12, $u_head # U : | ||
350 | mskqh t2, t5, t2 # U : begin src byte validity mask | ||
351 | cmpbge zero, t1, t8 # E : is there a zero? | ||
352 | nop | ||
353 | |||
354 | extql t2, a1, t2 # U : | ||
355 | or t8, t10, t5 # E : test for end-of-count too | ||
356 | cmpbge zero, t2, t3 # E : | ||
357 | cmoveq a2, t5, t8 # E : Latency=2, extra map slot | ||
358 | |||
359 | nop # E : goes with cmov | ||
360 | andnot t8, t3, t8 # E : | ||
361 | beq t8, $u_head # U : | ||
362 | nop | ||
363 | |||
364 | /* At this point we've found a zero in the first partial word of | ||
365 | the source. We need to isolate the valid source data and mask | ||
366 | it into the original destination data. (Incidentally, we know | ||
367 | that we'll need at least one byte of that original dest word.) */ | ||
368 | |||
369 | ldq_u t0, 0(a0) # L : | ||
370 | negq t8, t6 # E : build bitmask of bytes <= zero | ||
371 | mskqh t1, t4, t1 # U : | ||
372 | and t6, t8, t12 # E : | ||
373 | |||
374 | subq t12, 1, t6 # E : | ||
375 | or t6, t12, t8 # E : | ||
376 | zapnot t2, t8, t2 # U : prepare source word; mirror changes | ||
377 | zapnot t1, t8, t1 # U : to source validity mask | ||
378 | |||
379 | andnot t0, t2, t0 # E : zero place for source to reside | ||
380 | or t0, t1, t0 # E : and put it there | ||
381 | stq_u t0, 0(a0) # L : | ||
382 | nop | ||
383 | |||
384 | .align 4 | ||
385 | $finish_up: | ||
386 | zapnot t0, t12, t4 # U : was last byte written null? | ||
387 | and t12, 0xf0, t3 # E : binary search for the address of the | ||
388 | cmovne t4, 1, t4 # E : Latency=2, extra map slot | ||
389 | nop # E : with cmovne | ||
390 | |||
391 | and t12, 0xcc, t2 # E : last byte written | ||
392 | and t12, 0xaa, t1 # E : | ||
393 | cmovne t3, 4, t3 # E : Latency=2, extra map slot | ||
394 | nop # E : with cmovne | ||
395 | |||
396 | bic a0, 7, t0 | ||
397 | cmovne t2, 2, t2 # E : Latency=2, extra map slot | ||
398 | nop # E : with cmovne | ||
399 | nop | ||
400 | |||
401 | cmovne t1, 1, t1 # E : Latency=2, extra map slot | ||
402 | nop # E : with cmovne | ||
403 | addq t0, t3, t0 # E : | ||
404 | addq t1, t2, t1 # E : | ||
405 | |||
406 | addq t0, t1, t0 # E : | ||
407 | addq t0, t4, t0 # add one if we filled the buffer | ||
408 | subq t0, v0, v0 # find string length | ||
409 | ret # L0 : | ||
410 | |||
411 | .align 4 | ||
412 | $zerolength: | ||
413 | nop | ||
414 | nop | ||
415 | nop | ||
416 | clr v0 | ||
417 | |||
418 | $exception: | ||
419 | nop | ||
420 | nop | ||
421 | nop | ||
422 | ret | ||
423 | |||
424 | .end __strncpy_from_user | ||
diff --git a/arch/alpha/lib/ev6-stxcpy.S b/arch/alpha/lib/ev6-stxcpy.S new file mode 100644 index 000000000000..4643ff2ffc8d --- /dev/null +++ b/arch/alpha/lib/ev6-stxcpy.S | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-stxcpy.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Copy a null-terminated string from SRC to DST. | ||
6 | * | ||
7 | * This is an internal routine used by strcpy, stpcpy, and strcat. | ||
8 | * As such, it uses special linkage conventions to make implementation | ||
9 | * of these public functions more efficient. | ||
10 | * | ||
11 | * On input: | ||
12 | * t9 = return address | ||
13 | * a0 = DST | ||
14 | * a1 = SRC | ||
15 | * | ||
16 | * On output: | ||
17 | * t12 = bitmask (with one bit set) indicating the last byte written | ||
18 | * a0 = unaligned address of the last *word* written | ||
19 | * | ||
20 | * Furthermore, v0, a3-a5, t11, and t12 are untouched. | ||
21 | * | ||
22 | * Much of the information about 21264 scheduling/coding comes from: | ||
23 | * Compiler Writer's Guide for the Alpha 21264 | ||
24 | * abbreviated as 'CWG' in other comments here | ||
25 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
26 | * Scheduling notation: | ||
27 | * E - either cluster | ||
28 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
29 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
30 | * Try not to change the actual algorithm if possible for consistency. | ||
31 | */ | ||
32 | |||
33 | #include <asm/regdef.h> | ||
34 | |||
35 | .set noat | ||
36 | .set noreorder | ||
37 | |||
38 | .text | ||
39 | |||
40 | /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that | ||
41 | doesn't like putting the entry point for a procedure somewhere in the | ||
42 | middle of the procedure descriptor. Work around this by putting the | ||
43 | aligned copy in its own procedure descriptor */ | ||
44 | |||
45 | |||
46 | .ent stxcpy_aligned | ||
47 | .align 4 | ||
48 | stxcpy_aligned: | ||
49 | .frame sp, 0, t9 | ||
50 | .prologue 0 | ||
51 | |||
52 | /* On entry to this basic block: | ||
53 | t0 == the first destination word for masking back in | ||
54 | t1 == the first source word. */ | ||
55 | |||
56 | /* Create the 1st output word and detect 0's in the 1st input word. */ | ||
57 | lda t2, -1 # E : build a mask against false zero | ||
58 | mskqh t2, a1, t2 # U : detection in the src word (stall) | ||
59 | mskqh t1, a1, t3 # U : | ||
60 | ornot t1, t2, t2 # E : (stall) | ||
61 | |||
62 | mskql t0, a1, t0 # U : assemble the first output word | ||
63 | cmpbge zero, t2, t8 # E : bits set iff null found | ||
64 | or t0, t3, t1 # E : (stall) | ||
65 | bne t8, $a_eos # U : (stall) | ||
66 | |||
67 | /* On entry to this basic block: | ||
68 | t0 == the first destination word for masking back in | ||
69 | t1 == a source word not containing a null. */ | ||
70 | /* Nops here to separate store quads from load quads */ | ||
71 | |||
72 | $a_loop: | ||
73 | stq_u t1, 0(a0) # L : | ||
74 | addq a0, 8, a0 # E : | ||
75 | nop | ||
76 | nop | ||
77 | |||
78 | ldq_u t1, 0(a1) # L : Latency=3 | ||
79 | addq a1, 8, a1 # E : | ||
80 | cmpbge zero, t1, t8 # E : (3 cycle stall) | ||
81 | beq t8, $a_loop # U : (stall for t8) | ||
82 | |||
83 | /* Take care of the final (partial) word store. | ||
84 | On entry to this basic block we have: | ||
85 | t1 == the source word containing the null | ||
86 | t8 == the cmpbge mask that found it. */ | ||
87 | $a_eos: | ||
88 | negq t8, t6 # E : find low bit set | ||
89 | and t8, t6, t12 # E : (stall) | ||
90 | /* For the sake of the cache, don't read a destination word | ||
91 | if we're not going to need it. */ | ||
92 | and t12, 0x80, t6 # E : (stall) | ||
93 | bne t6, 1f # U : (stall) | ||
94 | |||
95 | /* We're doing a partial word store and so need to combine | ||
96 | our source and original destination words. */ | ||
97 | ldq_u t0, 0(a0) # L : Latency=3 | ||
98 | subq t12, 1, t6 # E : | ||
99 | zapnot t1, t6, t1 # U : clear src bytes >= null (stall) | ||
100 | or t12, t6, t8 # E : (stall) | ||
101 | |||
102 | zap t0, t8, t0 # E : clear dst bytes <= null | ||
103 | or t0, t1, t1 # E : (stall) | ||
104 | nop | ||
105 | nop | ||
106 | |||
107 | 1: stq_u t1, 0(a0) # L : | ||
108 | ret (t9) # L0 : Latency=3 | ||
109 | nop | ||
110 | nop | ||
111 | |||
112 | .end stxcpy_aligned | ||
113 | |||
114 | .align 4 | ||
115 | .ent __stxcpy | ||
116 | .globl __stxcpy | ||
117 | __stxcpy: | ||
118 | .frame sp, 0, t9 | ||
119 | .prologue 0 | ||
120 | |||
121 | /* Are source and destination co-aligned? */ | ||
122 | xor a0, a1, t0 # E : | ||
123 | unop # E : | ||
124 | and t0, 7, t0 # E : (stall) | ||
125 | bne t0, $unaligned # U : (stall) | ||
126 | |||
127 | /* We are co-aligned; take care of a partial first word. */ | ||
128 | ldq_u t1, 0(a1) # L : load first src word | ||
129 | and a0, 7, t0 # E : take care not to load a word ... | ||
130 | addq a1, 8, a1 # E : | ||
131 | beq t0, stxcpy_aligned # U : ... if we wont need it (stall) | ||
132 | |||
133 | ldq_u t0, 0(a0) # L : | ||
134 | br stxcpy_aligned # L0 : Latency=3 | ||
135 | nop | ||
136 | nop | ||
137 | |||
138 | |||
139 | /* The source and destination are not co-aligned. Align the destination | ||
140 | and cope. We have to be very careful about not reading too much and | ||
141 | causing a SEGV. */ | ||
142 | |||
143 | .align 4 | ||
144 | $u_head: | ||
145 | /* We know just enough now to be able to assemble the first | ||
146 | full source word. We can still find a zero at the end of it | ||
147 | that prevents us from outputting the whole thing. | ||
148 | |||
149 | On entry to this basic block: | ||
150 | t0 == the first dest word, for masking back in, if needed else 0 | ||
151 | t1 == the low bits of the first source word | ||
152 | t6 == bytemask that is -1 in dest word bytes */ | ||
153 | |||
154 | ldq_u t2, 8(a1) # L : | ||
155 | addq a1, 8, a1 # E : | ||
156 | extql t1, a1, t1 # U : (stall on a1) | ||
157 | extqh t2, a1, t4 # U : (stall on a1) | ||
158 | |||
159 | mskql t0, a0, t0 # U : | ||
160 | or t1, t4, t1 # E : | ||
161 | mskqh t1, a0, t1 # U : (stall on t1) | ||
162 | or t0, t1, t1 # E : (stall on t1) | ||
163 | |||
164 | or t1, t6, t6 # E : | ||
165 | cmpbge zero, t6, t8 # E : (stall) | ||
166 | lda t6, -1 # E : for masking just below | ||
167 | bne t8, $u_final # U : (stall) | ||
168 | |||
169 | mskql t6, a1, t6 # U : mask out the bits we have | ||
170 | or t6, t2, t2 # E : already extracted before (stall) | ||
171 | cmpbge zero, t2, t8 # E : testing eos (stall) | ||
172 | bne t8, $u_late_head_exit # U : (stall) | ||
173 | |||
174 | /* Finally, we've got all the stupid leading edge cases taken care | ||
175 | of and we can set up to enter the main loop. */ | ||
176 | |||
177 | stq_u t1, 0(a0) # L : store first output word | ||
178 | addq a0, 8, a0 # E : | ||
179 | extql t2, a1, t0 # U : position ho-bits of lo word | ||
180 | ldq_u t2, 8(a1) # U : read next high-order source word | ||
181 | |||
182 | addq a1, 8, a1 # E : | ||
183 | cmpbge zero, t2, t8 # E : (stall for t2) | ||
184 | nop # E : | ||
185 | bne t8, $u_eos # U : (stall) | ||
186 | |||
187 | /* Unaligned copy main loop. In order to avoid reading too much, | ||
188 | the loop is structured to detect zeros in aligned source words. | ||
189 | This has, unfortunately, effectively pulled half of a loop | ||
190 | iteration out into the head and half into the tail, but it does | ||
191 | prevent nastiness from accumulating in the very thing we want | ||
192 | to run as fast as possible. | ||
193 | |||
194 | On entry to this basic block: | ||
195 | t0 == the shifted high-order bits from the previous source word | ||
196 | t2 == the unshifted current source word | ||
197 | |||
198 | We further know that t2 does not contain a null terminator. */ | ||
199 | |||
200 | .align 3 | ||
201 | $u_loop: | ||
202 | extqh t2, a1, t1 # U : extract high bits for current word | ||
203 | addq a1, 8, a1 # E : (stall) | ||
204 | extql t2, a1, t3 # U : extract low bits for next time (stall) | ||
205 | addq a0, 8, a0 # E : | ||
206 | |||
207 | or t0, t1, t1 # E : current dst word now complete | ||
208 | ldq_u t2, 0(a1) # L : Latency=3 load high word for next time | ||
209 | stq_u t1, -8(a0) # L : save the current word (stall) | ||
210 | mov t3, t0 # E : | ||
211 | |||
212 | cmpbge zero, t2, t8 # E : test new word for eos | ||
213 | beq t8, $u_loop # U : (stall) | ||
214 | nop | ||
215 | nop | ||
216 | |||
217 | /* We've found a zero somewhere in the source word we just read. | ||
218 | If it resides in the lower half, we have one (probably partial) | ||
219 | word to write out, and if it resides in the upper half, we | ||
220 | have one full and one partial word left to write out. | ||
221 | |||
222 | On entry to this basic block: | ||
223 | t0 == the shifted high-order bits from the previous source word | ||
224 | t2 == the unshifted current source word. */ | ||
225 | $u_eos: | ||
226 | extqh t2, a1, t1 # U : | ||
227 | or t0, t1, t1 # E : first (partial) source word complete (stall) | ||
228 | cmpbge zero, t1, t8 # E : is the null in this first bit? (stall) | ||
229 | bne t8, $u_final # U : (stall) | ||
230 | |||
231 | $u_late_head_exit: | ||
232 | stq_u t1, 0(a0) # L : the null was in the high-order bits | ||
233 | addq a0, 8, a0 # E : | ||
234 | extql t2, a1, t1 # U : | ||
235 | cmpbge zero, t1, t8 # E : (stall) | ||
236 | |||
237 | /* Take care of a final (probably partial) result word. | ||
238 | On entry to this basic block: | ||
239 | t1 == assembled source word | ||
240 | t8 == cmpbge mask that found the null. */ | ||
241 | $u_final: | ||
242 | negq t8, t6 # E : isolate low bit set | ||
243 | and t6, t8, t12 # E : (stall) | ||
244 | and t12, 0x80, t6 # E : avoid dest word load if we can (stall) | ||
245 | bne t6, 1f # U : (stall) | ||
246 | |||
247 | ldq_u t0, 0(a0) # E : | ||
248 | subq t12, 1, t6 # E : | ||
249 | or t6, t12, t8 # E : (stall) | ||
250 | zapnot t1, t6, t1 # U : kill source bytes >= null (stall) | ||
251 | |||
252 | zap t0, t8, t0 # U : kill dest bytes <= null (2 cycle data stall) | ||
253 | or t0, t1, t1 # E : (stall) | ||
254 | nop | ||
255 | nop | ||
256 | |||
257 | 1: stq_u t1, 0(a0) # L : | ||
258 | ret (t9) # L0 : Latency=3 | ||
259 | nop | ||
260 | nop | ||
261 | |||
262 | /* Unaligned copy entry point. */ | ||
263 | .align 4 | ||
264 | $unaligned: | ||
265 | |||
266 | ldq_u t1, 0(a1) # L : load first source word | ||
267 | and a0, 7, t4 # E : find dest misalignment | ||
268 | and a1, 7, t5 # E : find src misalignment | ||
269 | /* Conditionally load the first destination word and a bytemask | ||
270 | with 0xff indicating that the destination byte is sacrosanct. */ | ||
271 | mov zero, t0 # E : | ||
272 | |||
273 | mov zero, t6 # E : | ||
274 | beq t4, 1f # U : | ||
275 | ldq_u t0, 0(a0) # L : | ||
276 | lda t6, -1 # E : | ||
277 | |||
278 | mskql t6, a0, t6 # U : | ||
279 | nop | ||
280 | nop | ||
281 | nop | ||
282 | 1: | ||
283 | subq a1, t4, a1 # E : sub dest misalignment from src addr | ||
284 | /* If source misalignment is larger than dest misalignment, we need | ||
285 | extra startup checks to avoid SEGV. */ | ||
286 | cmplt t4, t5, t12 # E : | ||
287 | beq t12, $u_head # U : | ||
288 | lda t2, -1 # E : mask out leading garbage in source | ||
289 | |||
290 | mskqh t2, t5, t2 # U : | ||
291 | ornot t1, t2, t3 # E : (stall) | ||
292 | cmpbge zero, t3, t8 # E : is there a zero? (stall) | ||
293 | beq t8, $u_head # U : (stall) | ||
294 | |||
295 | /* At this point we've found a zero in the first partial word of | ||
296 | the source. We need to isolate the valid source data and mask | ||
297 | it into the original destination data. (Incidentally, we know | ||
298 | that we'll need at least one byte of that original dest word.) */ | ||
299 | |||
300 | ldq_u t0, 0(a0) # L : | ||
301 | negq t8, t6 # E : build bitmask of bytes <= zero | ||
302 | and t6, t8, t12 # E : (stall) | ||
303 | and a1, 7, t5 # E : | ||
304 | |||
305 | subq t12, 1, t6 # E : | ||
306 | or t6, t12, t8 # E : (stall) | ||
307 | srl t12, t5, t12 # U : adjust final null return value | ||
308 | zapnot t2, t8, t2 # U : prepare source word; mirror changes (stall) | ||
309 | |||
310 | and t1, t2, t1 # E : to source validity mask | ||
311 | extql t2, a1, t2 # U : | ||
312 | extql t1, a1, t1 # U : (stall) | ||
313 | andnot t0, t2, t0 # .. e1 : zero place for source to reside (stall) | ||
314 | |||
315 | or t0, t1, t1 # e1 : and put it there | ||
316 | stq_u t1, 0(a0) # .. e0 : (stall) | ||
317 | ret (t9) # e1 : | ||
318 | nop | ||
319 | |||
320 | .end __stxcpy | ||
321 | |||
diff --git a/arch/alpha/lib/ev6-stxncpy.S b/arch/alpha/lib/ev6-stxncpy.S new file mode 100644 index 000000000000..b581a7af2456 --- /dev/null +++ b/arch/alpha/lib/ev6-stxncpy.S | |||
@@ -0,0 +1,397 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev6-stxncpy.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com> | ||
4 | * | ||
5 | * Copy no more than COUNT bytes of the null-terminated string from | ||
6 | * SRC to DST. | ||
7 | * | ||
8 | * This is an internal routine used by strncpy, stpncpy, and strncat. | ||
9 | * As such, it uses special linkage conventions to make implementation | ||
10 | * of these public functions more efficient. | ||
11 | * | ||
12 | * On input: | ||
13 | * t9 = return address | ||
14 | * a0 = DST | ||
15 | * a1 = SRC | ||
16 | * a2 = COUNT | ||
17 | * | ||
18 | * Furthermore, COUNT may not be zero. | ||
19 | * | ||
20 | * On output: | ||
21 | * t0 = last word written | ||
22 | * t10 = bitmask (with one bit set) indicating the byte position of | ||
23 | * the end of the range specified by COUNT | ||
24 | * t12 = bitmask (with one bit set) indicating the last byte written | ||
25 | * a0 = unaligned address of the last *word* written | ||
26 | * a2 = the number of full words left in COUNT | ||
27 | * | ||
28 | * Furthermore, v0, a3-a5, t11, and $at are untouched. | ||
29 | * | ||
30 | * Much of the information about 21264 scheduling/coding comes from: | ||
31 | * Compiler Writer's Guide for the Alpha 21264 | ||
32 | * abbreviated as 'CWG' in other comments here | ||
33 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
34 | * Scheduling notation: | ||
35 | * E - either cluster | ||
36 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
37 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
38 | * Try not to change the actual algorithm if possible for consistency. | ||
39 | */ | ||
40 | |||
41 | #include <asm/regdef.h> | ||
42 | |||
43 | .set noat | ||
44 | .set noreorder | ||
45 | |||
46 | .text | ||
47 | |||
48 | /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that | ||
49 | doesn't like putting the entry point for a procedure somewhere in the | ||
50 | middle of the procedure descriptor. Work around this by putting the | ||
51 | aligned copy in its own procedure descriptor */ | ||
52 | |||
53 | |||
54 | .ent stxncpy_aligned | ||
55 | .align 4 | ||
56 | stxncpy_aligned: | ||
57 | .frame sp, 0, t9, 0 | ||
58 | .prologue 0 | ||
59 | |||
60 | /* On entry to this basic block: | ||
61 | t0 == the first destination word for masking back in | ||
62 | t1 == the first source word. */ | ||
63 | |||
64 | /* Create the 1st output word and detect 0's in the 1st input word. */ | ||
65 | lda t2, -1 # E : build a mask against false zero | ||
66 | mskqh t2, a1, t2 # U : detection in the src word (stall) | ||
67 | mskqh t1, a1, t3 # U : | ||
68 | ornot t1, t2, t2 # E : (stall) | ||
69 | |||
70 | mskql t0, a1, t0 # U : assemble the first output word | ||
71 | cmpbge zero, t2, t8 # E : bits set iff null found | ||
72 | or t0, t3, t0 # E : (stall) | ||
73 | beq a2, $a_eoc # U : | ||
74 | |||
75 | bne t8, $a_eos # U : | ||
76 | nop | ||
77 | nop | ||
78 | nop | ||
79 | |||
80 | /* On entry to this basic block: | ||
81 | t0 == a source word not containing a null. */ | ||
82 | |||
83 | /* | ||
84 | * nops here to: | ||
85 | * separate store quads from load quads | ||
86 | * limit of 1 bcond/quad to permit training | ||
87 | */ | ||
88 | $a_loop: | ||
89 | stq_u t0, 0(a0) # L : | ||
90 | addq a0, 8, a0 # E : | ||
91 | subq a2, 1, a2 # E : | ||
92 | nop | ||
93 | |||
94 | ldq_u t0, 0(a1) # L : | ||
95 | addq a1, 8, a1 # E : | ||
96 | cmpbge zero, t0, t8 # E : | ||
97 | beq a2, $a_eoc # U : | ||
98 | |||
99 | beq t8, $a_loop # U : | ||
100 | nop | ||
101 | nop | ||
102 | nop | ||
103 | |||
104 | /* Take care of the final (partial) word store. At this point | ||
105 | the end-of-count bit is set in t8 iff it applies. | ||
106 | |||
107 | On entry to this basic block we have: | ||
108 | t0 == the source word containing the null | ||
109 | t8 == the cmpbge mask that found it. */ | ||
110 | |||
111 | $a_eos: | ||
112 | negq t8, t12 # E : find low bit set | ||
113 | and t8, t12, t12 # E : (stall) | ||
114 | /* For the sake of the cache, don't read a destination word | ||
115 | if we're not going to need it. */ | ||
116 | and t12, 0x80, t6 # E : (stall) | ||
117 | bne t6, 1f # U : (stall) | ||
118 | |||
119 | /* We're doing a partial word store and so need to combine | ||
120 | our source and original destination words. */ | ||
121 | ldq_u t1, 0(a0) # L : | ||
122 | subq t12, 1, t6 # E : | ||
123 | or t12, t6, t8 # E : (stall) | ||
124 | zapnot t0, t8, t0 # U : clear src bytes > null (stall) | ||
125 | |||
126 | zap t1, t8, t1 # .. e1 : clear dst bytes <= null | ||
127 | or t0, t1, t0 # e1 : (stall) | ||
128 | nop | ||
129 | nop | ||
130 | |||
131 | 1: stq_u t0, 0(a0) # L : | ||
132 | ret (t9) # L0 : Latency=3 | ||
133 | nop | ||
134 | nop | ||
135 | |||
136 | /* Add the end-of-count bit to the eos detection bitmask. */ | ||
137 | $a_eoc: | ||
138 | or t10, t8, t8 # E : | ||
139 | br $a_eos # L0 : Latency=3 | ||
140 | nop | ||
141 | nop | ||
142 | |||
143 | .end stxncpy_aligned | ||
144 | |||
145 | .align 4 | ||
146 | .ent __stxncpy | ||
147 | .globl __stxncpy | ||
148 | __stxncpy: | ||
149 | .frame sp, 0, t9, 0 | ||
150 | .prologue 0 | ||
151 | |||
152 | /* Are source and destination co-aligned? */ | ||
153 | xor a0, a1, t1 # E : | ||
154 | and a0, 7, t0 # E : find dest misalignment | ||
155 | and t1, 7, t1 # E : (stall) | ||
156 | addq a2, t0, a2 # E : bias count by dest misalignment (stall) | ||
157 | |||
158 | subq a2, 1, a2 # E : | ||
159 | and a2, 7, t2 # E : (stall) | ||
160 | srl a2, 3, a2 # U : a2 = loop counter = (count - 1)/8 (stall) | ||
161 | addq zero, 1, t10 # E : | ||
162 | |||
163 | sll t10, t2, t10 # U : t10 = bitmask of last count byte | ||
164 | bne t1, $unaligned # U : | ||
165 | /* We are co-aligned; take care of a partial first word. */ | ||
166 | ldq_u t1, 0(a1) # L : load first src word | ||
167 | addq a1, 8, a1 # E : | ||
168 | |||
169 | beq t0, stxncpy_aligned # U : avoid loading dest word if not needed | ||
170 | ldq_u t0, 0(a0) # L : | ||
171 | nop | ||
172 | nop | ||
173 | |||
174 | br stxncpy_aligned # .. e1 : | ||
175 | nop | ||
176 | nop | ||
177 | nop | ||
178 | |||
179 | |||
180 | |||
181 | /* The source and destination are not co-aligned. Align the destination | ||
182 | and cope. We have to be very careful about not reading too much and | ||
183 | causing a SEGV. */ | ||
184 | |||
185 | .align 4 | ||
186 | $u_head: | ||
187 | /* We know just enough now to be able to assemble the first | ||
188 | full source word. We can still find a zero at the end of it | ||
189 | that prevents us from outputting the whole thing. | ||
190 | |||
191 | On entry to this basic block: | ||
192 | t0 == the first dest word, unmasked | ||
193 | t1 == the shifted low bits of the first source word | ||
194 | t6 == bytemask that is -1 in dest word bytes */ | ||
195 | |||
196 | ldq_u t2, 8(a1) # L : Latency=3 load second src word | ||
197 | addq a1, 8, a1 # E : | ||
198 | mskql t0, a0, t0 # U : mask trailing garbage in dst | ||
199 | extqh t2, a1, t4 # U : (3 cycle stall on t2) | ||
200 | |||
201 | or t1, t4, t1 # E : first aligned src word complete (stall) | ||
202 | mskqh t1, a0, t1 # U : mask leading garbage in src (stall) | ||
203 | or t0, t1, t0 # E : first output word complete (stall) | ||
204 | or t0, t6, t6 # E : mask original data for zero test (stall) | ||
205 | |||
206 | cmpbge zero, t6, t8 # E : | ||
207 | beq a2, $u_eocfin # U : | ||
208 | lda t6, -1 # E : | ||
209 | nop | ||
210 | |||
211 | bne t8, $u_final # U : | ||
212 | mskql t6, a1, t6 # U : mask out bits already seen | ||
213 | stq_u t0, 0(a0) # L : store first output word | ||
214 | or t6, t2, t2 # E : (stall) | ||
215 | |||
216 | cmpbge zero, t2, t8 # E : find nulls in second partial | ||
217 | addq a0, 8, a0 # E : | ||
218 | subq a2, 1, a2 # E : | ||
219 | bne t8, $u_late_head_exit # U : | ||
220 | |||
221 | /* Finally, we've got all the stupid leading edge cases taken care | ||
222 | of and we can set up to enter the main loop. */ | ||
223 | extql t2, a1, t1 # U : position hi-bits of lo word | ||
224 | beq a2, $u_eoc # U : | ||
225 | ldq_u t2, 8(a1) # L : read next high-order source word | ||
226 | addq a1, 8, a1 # E : | ||
227 | |||
228 | extqh t2, a1, t0 # U : position lo-bits of hi word (stall) | ||
229 | cmpbge zero, t2, t8 # E : | ||
230 | nop | ||
231 | bne t8, $u_eos # U : | ||
232 | |||
233 | /* Unaligned copy main loop. In order to avoid reading too much, | ||
234 | the loop is structured to detect zeros in aligned source words. | ||
235 | This has, unfortunately, effectively pulled half of a loop | ||
236 | iteration out into the head and half into the tail, but it does | ||
237 | prevent nastiness from accumulating in the very thing we want | ||
238 | to run as fast as possible. | ||
239 | |||
240 | On entry to this basic block: | ||
241 | t0 == the shifted low-order bits from the current source word | ||
242 | t1 == the shifted high-order bits from the previous source word | ||
243 | t2 == the unshifted current source word | ||
244 | |||
245 | We further know that t2 does not contain a null terminator. */ | ||
246 | |||
247 | .align 4 | ||
248 | $u_loop: | ||
249 | or t0, t1, t0 # E : current dst word now complete | ||
250 | subq a2, 1, a2 # E : decrement word count | ||
251 | extql t2, a1, t1 # U : extract low bits for next time | ||
252 | addq a0, 8, a0 # E : | ||
253 | |||
254 | stq_u t0, -8(a0) # U : save the current word | ||
255 | beq a2, $u_eoc # U : | ||
256 | ldq_u t2, 8(a1) # U : Latency=3 load high word for next time | ||
257 | addq a1, 8, a1 # E : | ||
258 | |||
259 | extqh t2, a1, t0 # U : extract low bits (2 cycle stall) | ||
260 | cmpbge zero, t2, t8 # E : test new word for eos | ||
261 | nop | ||
262 | beq t8, $u_loop # U : | ||
263 | |||
264 | /* We've found a zero somewhere in the source word we just read. | ||
265 | If it resides in the lower half, we have one (probably partial) | ||
266 | word to write out, and if it resides in the upper half, we | ||
267 | have one full and one partial word left to write out. | ||
268 | |||
269 | On entry to this basic block: | ||
270 | t0 == the shifted low-order bits from the current source word | ||
271 | t1 == the shifted high-order bits from the previous source word | ||
272 | t2 == the unshifted current source word. */ | ||
273 | $u_eos: | ||
274 | or t0, t1, t0 # E : first (partial) source word complete | ||
275 | nop | ||
276 | cmpbge zero, t0, t8 # E : is the null in this first bit? (stall) | ||
277 | bne t8, $u_final # U : (stall) | ||
278 | |||
279 | stq_u t0, 0(a0) # L : the null was in the high-order bits | ||
280 | addq a0, 8, a0 # E : | ||
281 | subq a2, 1, a2 # E : | ||
282 | nop | ||
283 | |||
284 | $u_late_head_exit: | ||
285 | extql t2, a1, t0 # U : | ||
286 | cmpbge zero, t0, t8 # E : | ||
287 | or t8, t10, t6 # E : (stall) | ||
288 | cmoveq a2, t6, t8 # E : Latency=2, extra map slot (stall) | ||
289 | |||
290 | /* Take care of a final (probably partial) result word. | ||
291 | On entry to this basic block: | ||
292 | t0 == assembled source word | ||
293 | t8 == cmpbge mask that found the null. */ | ||
294 | $u_final: | ||
295 | negq t8, t6 # E : isolate low bit set | ||
296 | and t6, t8, t12 # E : (stall) | ||
297 | and t12, 0x80, t6 # E : avoid dest word load if we can (stall) | ||
298 | bne t6, 1f # U : (stall) | ||
299 | |||
300 | ldq_u t1, 0(a0) # L : | ||
301 | subq t12, 1, t6 # E : | ||
302 | or t6, t12, t8 # E : (stall) | ||
303 | zapnot t0, t8, t0 # U : kill source bytes > null | ||
304 | |||
305 | zap t1, t8, t1 # U : kill dest bytes <= null | ||
306 | or t0, t1, t0 # E : (stall) | ||
307 | nop | ||
308 | nop | ||
309 | |||
310 | 1: stq_u t0, 0(a0) # L : | ||
311 | ret (t9) # L0 : Latency=3 | ||
312 | |||
313 | /* Got to end-of-count before end of string. | ||
314 | On entry to this basic block: | ||
315 | t1 == the shifted high-order bits from the previous source word */ | ||
316 | $u_eoc: | ||
317 | and a1, 7, t6 # E : avoid final load if possible | ||
318 | sll t10, t6, t6 # U : (stall) | ||
319 | and t6, 0xff, t6 # E : (stall) | ||
320 | bne t6, 1f # U : (stall) | ||
321 | |||
322 | ldq_u t2, 8(a1) # L : load final src word | ||
323 | nop | ||
324 | extqh t2, a1, t0 # U : extract low bits for last word (stall) | ||
325 | or t1, t0, t1 # E : (stall) | ||
326 | |||
327 | 1: cmpbge zero, t1, t8 # E : | ||
328 | mov t1, t0 # E : | ||
329 | |||
330 | $u_eocfin: # end-of-count, final word | ||
331 | or t10, t8, t8 # E : | ||
332 | br $u_final # L0 : Latency=3 | ||
333 | |||
334 | /* Unaligned copy entry point. */ | ||
335 | .align 4 | ||
336 | $unaligned: | ||
337 | |||
338 | ldq_u t1, 0(a1) # L : load first source word | ||
339 | and a0, 7, t4 # E : find dest misalignment | ||
340 | and a1, 7, t5 # E : find src misalignment | ||
341 | /* Conditionally load the first destination word and a bytemask | ||
342 | with 0xff indicating that the destination byte is sacrosanct. */ | ||
343 | mov zero, t0 # E : | ||
344 | |||
345 | mov zero, t6 # E : | ||
346 | beq t4, 1f # U : | ||
347 | ldq_u t0, 0(a0) # L : | ||
348 | lda t6, -1 # E : | ||
349 | |||
350 | mskql t6, a0, t6 # U : | ||
351 | nop | ||
352 | nop | ||
353 | subq a1, t4, a1 # E : sub dest misalignment from src addr | ||
354 | |||
355 | /* If source misalignment is larger than dest misalignment, we need | ||
356 | extra startup checks to avoid SEGV. */ | ||
357 | |||
358 | 1: cmplt t4, t5, t12 # E : | ||
359 | extql t1, a1, t1 # U : shift src into place | ||
360 | lda t2, -1 # E : for creating masks later | ||
361 | beq t12, $u_head # U : (stall) | ||
362 | |||
363 | extql t2, a1, t2 # U : | ||
364 | cmpbge zero, t1, t8 # E : is there a zero? | ||
365 | andnot t2, t6, t12 # E : dest mask for a single word copy | ||
366 | or t8, t10, t5 # E : test for end-of-count too | ||
367 | |||
368 | cmpbge zero, t12, t3 # E : | ||
369 | cmoveq a2, t5, t8 # E : Latency=2, extra map slot | ||
370 | nop # E : keep with cmoveq | ||
371 | andnot t8, t3, t8 # E : (stall) | ||
372 | |||
373 | beq t8, $u_head # U : | ||
374 | /* At this point we've found a zero in the first partial word of | ||
375 | the source. We need to isolate the valid source data and mask | ||
376 | it into the original destination data. (Incidentally, we know | ||
377 | that we'll need at least one byte of that original dest word.) */ | ||
378 | ldq_u t0, 0(a0) # L : | ||
379 | negq t8, t6 # E : build bitmask of bytes <= zero | ||
380 | mskqh t1, t4, t1 # U : | ||
381 | |||
382 | and t6, t8, t2 # E : | ||
383 | subq t2, 1, t6 # E : (stall) | ||
384 | or t6, t2, t8 # E : (stall) | ||
385 | zapnot t12, t8, t12 # U : prepare source word; mirror changes (stall) | ||
386 | |||
387 | zapnot t1, t8, t1 # U : to source validity mask | ||
388 | andnot t0, t12, t0 # E : zero place for source to reside | ||
389 | or t0, t1, t0 # E : and put it there (stall both t0, t1) | ||
390 | stq_u t0, 0(a0) # L : (stall) | ||
391 | |||
392 | ret (t9) # L0 : Latency=3 | ||
393 | nop | ||
394 | nop | ||
395 | nop | ||
396 | |||
397 | .end __stxncpy | ||
diff --git a/arch/alpha/lib/ev67-strcat.S b/arch/alpha/lib/ev67-strcat.S new file mode 100644 index 000000000000..c426fe3ed72f --- /dev/null +++ b/arch/alpha/lib/ev67-strcat.S | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev67-strcat.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Append a null-terminated string from SRC to DST. | ||
6 | * | ||
7 | * Much of the information about 21264 scheduling/coding comes from: | ||
8 | * Compiler Writer's Guide for the Alpha 21264 | ||
9 | * abbreviated as 'CWG' in other comments here | ||
10 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
11 | * Scheduling notation: | ||
12 | * E - either cluster | ||
13 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
14 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
15 | * Try not to change the actual algorithm if possible for consistency. | ||
16 | * Commentary: It seems bogus to walk the input string twice - once | ||
17 | * to determine the length, and then again while doing the copy. | ||
18 | * A significant (future) enhancement would be to only read the input | ||
19 | * string once. | ||
20 | */ | ||
21 | |||
22 | |||
23 | .text | ||
24 | |||
25 | .align 4 | ||
26 | .globl strcat | ||
27 | .ent strcat | ||
28 | strcat: | ||
29 | .frame $30, 0, $26 | ||
30 | .prologue 0 | ||
31 | |||
32 | mov $16, $0 # E : set up return value | ||
33 | /* Find the end of the string. */ | ||
34 | ldq_u $1, 0($16) # L : load first quadword (a0 may be misaligned) | ||
35 | lda $2, -1 # E : | ||
36 | insqh $2, $16, $2 # U : | ||
37 | |||
38 | andnot $16, 7, $16 # E : | ||
39 | or $2, $1, $1 # E : | ||
40 | cmpbge $31, $1, $2 # E : bits set iff byte == 0 | ||
41 | bne $2, $found # U : | ||
42 | |||
43 | $loop: ldq $1, 8($16) # L : | ||
44 | addq $16, 8, $16 # E : | ||
45 | cmpbge $31, $1, $2 # E : | ||
46 | beq $2, $loop # U : | ||
47 | |||
48 | $found: cttz $2, $3 # U0 : | ||
49 | addq $16, $3, $16 # E : | ||
50 | /* Now do the append. */ | ||
51 | mov $26, $23 # E : | ||
52 | br __stxcpy # L0 : | ||
53 | |||
54 | .end strcat | ||
diff --git a/arch/alpha/lib/ev67-strchr.S b/arch/alpha/lib/ev67-strchr.S new file mode 100644 index 000000000000..fbb7b4ffade9 --- /dev/null +++ b/arch/alpha/lib/ev67-strchr.S | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev67-strchr.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Return the address of a given character within a null-terminated | ||
6 | * string, or null if it is not found. | ||
7 | * | ||
8 | * Much of the information about 21264 scheduling/coding comes from: | ||
9 | * Compiler Writer's Guide for the Alpha 21264 | ||
10 | * abbreviated as 'CWG' in other comments here | ||
11 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
12 | * Scheduling notation: | ||
13 | * E - either cluster | ||
14 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
15 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
16 | * Try not to change the actual algorithm if possible for consistency. | ||
17 | */ | ||
18 | |||
19 | #include <asm/regdef.h> | ||
20 | |||
21 | .set noreorder | ||
22 | .set noat | ||
23 | |||
24 | .align 4 | ||
25 | .globl strchr | ||
26 | .ent strchr | ||
27 | strchr: | ||
28 | .frame sp, 0, ra | ||
29 | .prologue 0 | ||
30 | |||
31 | ldq_u t0, 0(a0) # L : load first quadword Latency=3 | ||
32 | and a1, 0xff, t3 # E : 00000000000000ch | ||
33 | insbl a1, 1, t5 # U : 000000000000ch00 | ||
34 | insbl a1, 7, a2 # U : ch00000000000000 | ||
35 | |||
36 | insbl t3, 6, a3 # U : 00ch000000000000 | ||
37 | or t5, t3, a1 # E : 000000000000chch | ||
38 | andnot a0, 7, v0 # E : align our loop pointer | ||
39 | lda t4, -1 # E : build garbage mask | ||
40 | |||
41 | mskqh t4, a0, t4 # U : only want relevant part of first quad | ||
42 | or a2, a3, a2 # E : chch000000000000 | ||
43 | inswl a1, 2, t5 # E : 00000000chch0000 | ||
44 | inswl a1, 4, a3 # E : 0000chch00000000 | ||
45 | |||
46 | or a1, a2, a1 # E : chch00000000chch | ||
47 | or a3, t5, t5 # E : 0000chchchch0000 | ||
48 | cmpbge zero, t0, t2 # E : bits set iff byte == zero | ||
49 | cmpbge zero, t4, t4 # E : bits set iff byte is garbage | ||
50 | |||
51 | /* This quad is _very_ serialized. Lots of stalling happens */ | ||
52 | or t5, a1, a1 # E : chchchchchchchch | ||
53 | xor t0, a1, t1 # E : make bytes == c zero | ||
54 | cmpbge zero, t1, t3 # E : bits set iff byte == c | ||
55 | or t2, t3, t0 # E : bits set iff char match or zero match | ||
56 | |||
57 | andnot t0, t4, t0 # E : clear garbage bits | ||
58 | cttz t0, a2 # U0 : speculative (in case we get a match) | ||
59 | nop # E : | ||
60 | bne t0, $found # U : | ||
61 | |||
62 | /* | ||
63 | * Yuk. This loop is going to stall like crazy waiting for the | ||
64 | * data to be loaded. Not much can be done about it unless it's | ||
65 | * unrolled multiple times - is that safe to do in kernel space? | ||
66 | * Or would exception handling recovery code do the trick here? | ||
67 | */ | ||
68 | $loop: ldq t0, 8(v0) # L : Latency=3 | ||
69 | addq v0, 8, v0 # E : | ||
70 | xor t0, a1, t1 # E : | ||
71 | cmpbge zero, t0, t2 # E : bits set iff byte == 0 | ||
72 | |||
73 | cmpbge zero, t1, t3 # E : bits set iff byte == c | ||
74 | or t2, t3, t0 # E : | ||
75 | cttz t3, a2 # U0 : speculative (in case we get a match) | ||
76 | beq t0, $loop # U : | ||
77 | |||
78 | $found: negq t0, t1 # E : clear all but least set bit | ||
79 | and t0, t1, t0 # E : | ||
80 | and t0, t3, t1 # E : bit set iff byte was the char | ||
81 | addq v0, a2, v0 # E : Add in the bit number from above | ||
82 | |||
83 | cmoveq t1, $31, v0 # E : Two mapping slots, latency = 2 | ||
84 | nop | ||
85 | nop | ||
86 | ret # L0 : | ||
87 | |||
88 | .end strchr | ||
diff --git a/arch/alpha/lib/ev67-strlen.S b/arch/alpha/lib/ev67-strlen.S new file mode 100644 index 000000000000..503928072523 --- /dev/null +++ b/arch/alpha/lib/ev67-strlen.S | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev67-strlen.S | ||
3 | * 21264 version by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Finds length of a 0-terminated string. Optimized for the | ||
6 | * Alpha architecture: | ||
7 | * | ||
8 | * - memory accessed as aligned quadwords only | ||
9 | * - uses bcmpge to compare 8 bytes in parallel | ||
10 | * | ||
11 | * Much of the information about 21264 scheduling/coding comes from: | ||
12 | * Compiler Writer's Guide for the Alpha 21264 | ||
13 | * abbreviated as 'CWG' in other comments here | ||
14 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
15 | * Scheduling notation: | ||
16 | * E - either cluster | ||
17 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
18 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
19 | */ | ||
20 | |||
21 | .set noreorder | ||
22 | .set noat | ||
23 | |||
24 | .globl strlen | ||
25 | .ent strlen | ||
26 | .align 4 | ||
27 | strlen: | ||
28 | ldq_u $1, 0($16) # L : load first quadword ($16 may be misaligned) | ||
29 | lda $2, -1($31) # E : | ||
30 | insqh $2, $16, $2 # U : | ||
31 | andnot $16, 7, $0 # E : | ||
32 | |||
33 | or $2, $1, $1 # E : | ||
34 | cmpbge $31, $1, $2 # E : $2 <- bitmask: bit i == 1 <==> i-th byte == 0 | ||
35 | nop # E : | ||
36 | bne $2, $found # U : | ||
37 | |||
38 | $loop: ldq $1, 8($0) # L : | ||
39 | addq $0, 8, $0 # E : addr += 8 | ||
40 | cmpbge $31, $1, $2 # E : | ||
41 | beq $2, $loop # U : | ||
42 | |||
43 | $found: | ||
44 | cttz $2, $3 # U0 : | ||
45 | addq $0, $3, $0 # E : | ||
46 | subq $0, $16, $0 # E : | ||
47 | ret $31, ($26) # L0 : | ||
48 | |||
49 | .end strlen | ||
diff --git a/arch/alpha/lib/ev67-strlen_user.S b/arch/alpha/lib/ev67-strlen_user.S new file mode 100644 index 000000000000..57e0d77b81a6 --- /dev/null +++ b/arch/alpha/lib/ev67-strlen_user.S | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev67-strlen_user.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com> | ||
4 | * | ||
5 | * Return the length of the string including the NULL terminator | ||
6 | * (strlen+1) or zero if an error occurred. | ||
7 | * | ||
8 | * In places where it is critical to limit the processing time, | ||
9 | * and the data is not trusted, strnlen_user() should be used. | ||
10 | * It will return a value greater than its second argument if | ||
11 | * that limit would be exceeded. This implementation is allowed | ||
12 | * to access memory beyond the limit, but will not cross a page | ||
13 | * boundary when doing so. | ||
14 | * | ||
15 | * Much of the information about 21264 scheduling/coding comes from: | ||
16 | * Compiler Writer's Guide for the Alpha 21264 | ||
17 | * abbreviated as 'CWG' in other comments here | ||
18 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
19 | * Scheduling notation: | ||
20 | * E - either cluster | ||
21 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
22 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
23 | * Try not to change the actual algorithm if possible for consistency. | ||
24 | */ | ||
25 | |||
26 | #include <asm/regdef.h> | ||
27 | |||
28 | |||
29 | /* Allow an exception for an insn; exit if we get one. */ | ||
30 | #define EX(x,y...) \ | ||
31 | 99: x,##y; \ | ||
32 | .section __ex_table,"a"; \ | ||
33 | .long 99b - .; \ | ||
34 | lda v0, $exception-99b(zero); \ | ||
35 | .previous | ||
36 | |||
37 | |||
38 | .set noreorder | ||
39 | .set noat | ||
40 | .text | ||
41 | |||
42 | .globl __strlen_user | ||
43 | .ent __strlen_user | ||
44 | .frame sp, 0, ra | ||
45 | |||
46 | .align 4 | ||
47 | __strlen_user: | ||
48 | ldah a1, 32767(zero) # do not use plain strlen_user() for strings | ||
49 | # that might be almost 2 GB long; you should | ||
50 | # be using strnlen_user() instead | ||
51 | nop | ||
52 | nop | ||
53 | nop | ||
54 | |||
55 | .globl __strnlen_user | ||
56 | |||
57 | .align 4 | ||
58 | __strnlen_user: | ||
59 | .prologue 0 | ||
60 | EX( ldq_u t0, 0(a0) ) # L : load first quadword (a0 may be misaligned) | ||
61 | lda t1, -1(zero) # E : | ||
62 | |||
63 | insqh t1, a0, t1 # U : | ||
64 | andnot a0, 7, v0 # E : | ||
65 | or t1, t0, t0 # E : | ||
66 | subq a0, 1, a0 # E : get our +1 for the return | ||
67 | |||
68 | cmpbge zero, t0, t1 # E : t1 <- bitmask: bit i == 1 <==> i-th byte == 0 | ||
69 | subq a1, 7, t2 # E : | ||
70 | subq a0, v0, t0 # E : | ||
71 | bne t1, $found # U : | ||
72 | |||
73 | addq t2, t0, t2 # E : | ||
74 | addq a1, 1, a1 # E : | ||
75 | nop # E : | ||
76 | nop # E : | ||
77 | |||
78 | .align 4 | ||
79 | $loop: ble t2, $limit # U : | ||
80 | EX( ldq t0, 8(v0) ) # L : | ||
81 | nop # E : | ||
82 | nop # E : | ||
83 | |||
84 | cmpbge zero, t0, t1 # E : | ||
85 | subq t2, 8, t2 # E : | ||
86 | addq v0, 8, v0 # E : addr += 8 | ||
87 | beq t1, $loop # U : | ||
88 | |||
89 | $found: cttz t1, t2 # U0 : | ||
90 | addq v0, t2, v0 # E : | ||
91 | subq v0, a0, v0 # E : | ||
92 | ret # L0 : | ||
93 | |||
94 | $exception: | ||
95 | nop | ||
96 | nop | ||
97 | nop | ||
98 | ret | ||
99 | |||
100 | .align 4 # currently redundant | ||
101 | $limit: | ||
102 | nop | ||
103 | nop | ||
104 | subq a1, t2, v0 | ||
105 | ret | ||
106 | |||
107 | .end __strlen_user | ||
diff --git a/arch/alpha/lib/ev67-strncat.S b/arch/alpha/lib/ev67-strncat.S new file mode 100644 index 000000000000..4ae716cd2bfb --- /dev/null +++ b/arch/alpha/lib/ev67-strncat.S | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev67-strncat.S | ||
3 | * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com> | ||
4 | * | ||
5 | * Append no more than COUNT characters from the null-terminated string SRC | ||
6 | * to the null-terminated string DST. Always null-terminate the new DST. | ||
7 | * | ||
8 | * This differs slightly from the semantics in libc in that we never write | ||
9 | * past count, whereas libc may write to count+1. This follows the generic | ||
10 | * implementation in lib/string.c and is, IMHO, more sensible. | ||
11 | * | ||
12 | * Much of the information about 21264 scheduling/coding comes from: | ||
13 | * Compiler Writer's Guide for the Alpha 21264 | ||
14 | * abbreviated as 'CWG' in other comments here | ||
15 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
16 | * Scheduling notation: | ||
17 | * E - either cluster | ||
18 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
19 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
20 | * Try not to change the actual algorithm if possible for consistency. | ||
21 | */ | ||
22 | |||
23 | |||
24 | .text | ||
25 | |||
26 | .align 4 | ||
27 | .globl strncat | ||
28 | .ent strncat | ||
29 | strncat: | ||
30 | .frame $30, 0, $26 | ||
31 | .prologue 0 | ||
32 | |||
33 | mov $16, $0 # set up return value | ||
34 | beq $18, $zerocount # U : | ||
35 | /* Find the end of the string. */ | ||
36 | ldq_u $1, 0($16) # L : load first quadword ($16 may be misaligned) | ||
37 | lda $2, -1($31) # E : | ||
38 | |||
39 | insqh $2, $0, $2 # U : | ||
40 | andnot $16, 7, $16 # E : | ||
41 | nop # E : | ||
42 | or $2, $1, $1 # E : | ||
43 | |||
44 | nop # E : | ||
45 | nop # E : | ||
46 | cmpbge $31, $1, $2 # E : bits set iff byte == 0 | ||
47 | bne $2, $found # U : | ||
48 | |||
49 | $loop: ldq $1, 8($16) # L : | ||
50 | addq $16, 8, $16 # E : | ||
51 | cmpbge $31, $1, $2 # E : | ||
52 | beq $2, $loop # U : | ||
53 | |||
54 | $found: cttz $2, $3 # U0 : | ||
55 | addq $16, $3, $16 # E : | ||
56 | nop # E : | ||
57 | bsr $23, __stxncpy # L0 :/* Now do the append. */ | ||
58 | |||
59 | /* Worry about the null termination. */ | ||
60 | |||
61 | zapnot $1, $27, $2 # U : was last byte a null? | ||
62 | cmplt $27, $24, $5 # E : did we fill the buffer completely? | ||
63 | bne $2, 0f # U : | ||
64 | ret # L0 : | ||
65 | |||
66 | 0: or $5, $18, $2 # E : | ||
67 | nop | ||
68 | bne $2, 2f # U : | ||
69 | and $24, 0x80, $3 # E : no zero next byte | ||
70 | |||
71 | nop # E : | ||
72 | bne $3, 1f # U : | ||
73 | /* Here there are bytes left in the current word. Clear one. */ | ||
74 | addq $24, $24, $24 # E : end-of-count bit <<= 1 | ||
75 | nop # E : | ||
76 | |||
77 | 2: zap $1, $24, $1 # U : | ||
78 | nop # E : | ||
79 | stq_u $1, 0($16) # L : | ||
80 | ret # L0 : | ||
81 | |||
82 | 1: /* Here we must clear the first byte of the next DST word */ | ||
83 | stb $31, 8($16) # L : | ||
84 | nop # E : | ||
85 | nop # E : | ||
86 | ret # L0 : | ||
87 | |||
88 | $zerocount: | ||
89 | nop # E : | ||
90 | nop # E : | ||
91 | nop # E : | ||
92 | ret # L0 : | ||
93 | |||
94 | .end strncat | ||
diff --git a/arch/alpha/lib/ev67-strrchr.S b/arch/alpha/lib/ev67-strrchr.S new file mode 100644 index 000000000000..3fd8bf414c7b --- /dev/null +++ b/arch/alpha/lib/ev67-strrchr.S | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/ev67-strrchr.S | ||
3 | * 21264 version by Rick Gorton <rick.gorton@alpha-processor.com> | ||
4 | * | ||
5 | * Finds length of a 0-terminated string. Optimized for the | ||
6 | * Alpha architecture: | ||
7 | * | ||
8 | * - memory accessed as aligned quadwords only | ||
9 | * - uses bcmpge to compare 8 bytes in parallel | ||
10 | * | ||
11 | * Much of the information about 21264 scheduling/coding comes from: | ||
12 | * Compiler Writer's Guide for the Alpha 21264 | ||
13 | * abbreviated as 'CWG' in other comments here | ||
14 | * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html | ||
15 | * Scheduling notation: | ||
16 | * E - either cluster | ||
17 | * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 | ||
18 | * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 | ||
19 | */ | ||
20 | |||
21 | |||
22 | #include <asm/regdef.h> | ||
23 | |||
24 | .set noreorder | ||
25 | .set noat | ||
26 | |||
27 | .align 4 | ||
28 | .ent strrchr | ||
29 | .globl strrchr | ||
30 | strrchr: | ||
31 | .frame sp, 0, ra | ||
32 | .prologue 0 | ||
33 | |||
34 | and a1, 0xff, t2 # E : 00000000000000ch | ||
35 | insbl a1, 1, t4 # U : 000000000000ch00 | ||
36 | insbl a1, 2, t5 # U : 0000000000ch0000 | ||
37 | ldq_u t0, 0(a0) # L : load first quadword Latency=3 | ||
38 | |||
39 | mov zero, t6 # E : t6 is last match aligned addr | ||
40 | or t2, t4, a1 # E : 000000000000chch | ||
41 | sll t5, 8, t3 # U : 00000000ch000000 | ||
42 | mov zero, t8 # E : t8 is last match byte compare mask | ||
43 | |||
44 | andnot a0, 7, v0 # E : align source addr | ||
45 | or t5, t3, t3 # E : 00000000chch0000 | ||
46 | sll a1, 32, t2 # U : 0000chch00000000 | ||
47 | sll a1, 48, t4 # U : chch000000000000 | ||
48 | |||
49 | or t4, a1, a1 # E : chch00000000chch | ||
50 | or t2, t3, t2 # E : 0000chchchch0000 | ||
51 | or a1, t2, a1 # E : chchchchchchchch | ||
52 | lda t5, -1 # E : build garbage mask | ||
53 | |||
54 | cmpbge zero, t0, t1 # E : bits set iff byte == zero | ||
55 | mskqh t5, a0, t4 # E : Complete garbage mask | ||
56 | xor t0, a1, t2 # E : make bytes == c zero | ||
57 | cmpbge zero, t4, t4 # E : bits set iff byte is garbage | ||
58 | |||
59 | cmpbge zero, t2, t3 # E : bits set iff byte == c | ||
60 | andnot t1, t4, t1 # E : clear garbage from null test | ||
61 | andnot t3, t4, t3 # E : clear garbage from char test | ||
62 | bne t1, $eos # U : did we already hit the terminator? | ||
63 | |||
64 | /* Character search main loop */ | ||
65 | $loop: | ||
66 | ldq t0, 8(v0) # L : load next quadword | ||
67 | cmovne t3, v0, t6 # E : save previous comparisons match | ||
68 | nop # : Latency=2, extra map slot (keep nop with cmov) | ||
69 | nop | ||
70 | |||
71 | cmovne t3, t3, t8 # E : Latency=2, extra map slot | ||
72 | nop # : keep with cmovne | ||
73 | addq v0, 8, v0 # E : | ||
74 | xor t0, a1, t2 # E : | ||
75 | |||
76 | cmpbge zero, t0, t1 # E : bits set iff byte == zero | ||
77 | cmpbge zero, t2, t3 # E : bits set iff byte == c | ||
78 | beq t1, $loop # U : if we havnt seen a null, loop | ||
79 | nop | ||
80 | |||
81 | /* Mask out character matches after terminator */ | ||
82 | $eos: | ||
83 | negq t1, t4 # E : isolate first null byte match | ||
84 | and t1, t4, t4 # E : | ||
85 | subq t4, 1, t5 # E : build a mask of the bytes upto... | ||
86 | or t4, t5, t4 # E : ... and including the null | ||
87 | |||
88 | and t3, t4, t3 # E : mask out char matches after null | ||
89 | cmovne t3, t3, t8 # E : save it, if match found Latency=2, extra map slot | ||
90 | nop # : Keep with cmovne | ||
91 | nop | ||
92 | |||
93 | cmovne t3, v0, t6 # E : | ||
94 | nop # : Keep with cmovne | ||
95 | /* Locate the address of the last matched character */ | ||
96 | ctlz t8, t2 # U0 : Latency=3 (0x40 for t8=0) | ||
97 | nop | ||
98 | |||
99 | cmoveq t8, 0x3f, t2 # E : Compensate for case when no match is seen | ||
100 | nop # E : hide the cmov latency (2) behind ctlz latency | ||
101 | lda t5, 0x3f($31) # E : | ||
102 | subq t5, t2, t5 # E : Normalize leading zero count | ||
103 | |||
104 | addq t6, t5, v0 # E : and add to quadword address | ||
105 | ret # L0 : Latency=3 | ||
106 | nop | ||
107 | nop | ||
108 | |||
109 | .end strrchr | ||
diff --git a/arch/alpha/lib/fpreg.c b/arch/alpha/lib/fpreg.c new file mode 100644 index 000000000000..97c4d9d7a4d5 --- /dev/null +++ b/arch/alpha/lib/fpreg.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/fpreg.c | ||
3 | * | ||
4 | * (C) Copyright 1998 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #if defined(__alpha_cix__) || defined(__alpha_fix__) | ||
8 | #define STT(reg,val) asm volatile ("ftoit $f"#reg",%0" : "=r"(val)); | ||
9 | #else | ||
10 | #define STT(reg,val) asm volatile ("stt $f"#reg",%0" : "=m"(val)); | ||
11 | #endif | ||
12 | |||
13 | unsigned long | ||
14 | alpha_read_fp_reg (unsigned long reg) | ||
15 | { | ||
16 | unsigned long val; | ||
17 | |||
18 | switch (reg) { | ||
19 | case 0: STT( 0, val); break; | ||
20 | case 1: STT( 1, val); break; | ||
21 | case 2: STT( 2, val); break; | ||
22 | case 3: STT( 3, val); break; | ||
23 | case 4: STT( 4, val); break; | ||
24 | case 5: STT( 5, val); break; | ||
25 | case 6: STT( 6, val); break; | ||
26 | case 7: STT( 7, val); break; | ||
27 | case 8: STT( 8, val); break; | ||
28 | case 9: STT( 9, val); break; | ||
29 | case 10: STT(10, val); break; | ||
30 | case 11: STT(11, val); break; | ||
31 | case 12: STT(12, val); break; | ||
32 | case 13: STT(13, val); break; | ||
33 | case 14: STT(14, val); break; | ||
34 | case 15: STT(15, val); break; | ||
35 | case 16: STT(16, val); break; | ||
36 | case 17: STT(17, val); break; | ||
37 | case 18: STT(18, val); break; | ||
38 | case 19: STT(19, val); break; | ||
39 | case 20: STT(20, val); break; | ||
40 | case 21: STT(21, val); break; | ||
41 | case 22: STT(22, val); break; | ||
42 | case 23: STT(23, val); break; | ||
43 | case 24: STT(24, val); break; | ||
44 | case 25: STT(25, val); break; | ||
45 | case 26: STT(26, val); break; | ||
46 | case 27: STT(27, val); break; | ||
47 | case 28: STT(28, val); break; | ||
48 | case 29: STT(29, val); break; | ||
49 | case 30: STT(30, val); break; | ||
50 | case 31: STT(31, val); break; | ||
51 | default: return 0; | ||
52 | } | ||
53 | return val; | ||
54 | } | ||
55 | |||
56 | #if defined(__alpha_cix__) || defined(__alpha_fix__) | ||
57 | #define LDT(reg,val) asm volatile ("itoft %0,$f"#reg : : "r"(val)); | ||
58 | #else | ||
59 | #define LDT(reg,val) asm volatile ("ldt $f"#reg",%0" : : "m"(val)); | ||
60 | #endif | ||
61 | |||
62 | void | ||
63 | alpha_write_fp_reg (unsigned long reg, unsigned long val) | ||
64 | { | ||
65 | switch (reg) { | ||
66 | case 0: LDT( 0, val); break; | ||
67 | case 1: LDT( 1, val); break; | ||
68 | case 2: LDT( 2, val); break; | ||
69 | case 3: LDT( 3, val); break; | ||
70 | case 4: LDT( 4, val); break; | ||
71 | case 5: LDT( 5, val); break; | ||
72 | case 6: LDT( 6, val); break; | ||
73 | case 7: LDT( 7, val); break; | ||
74 | case 8: LDT( 8, val); break; | ||
75 | case 9: LDT( 9, val); break; | ||
76 | case 10: LDT(10, val); break; | ||
77 | case 11: LDT(11, val); break; | ||
78 | case 12: LDT(12, val); break; | ||
79 | case 13: LDT(13, val); break; | ||
80 | case 14: LDT(14, val); break; | ||
81 | case 15: LDT(15, val); break; | ||
82 | case 16: LDT(16, val); break; | ||
83 | case 17: LDT(17, val); break; | ||
84 | case 18: LDT(18, val); break; | ||
85 | case 19: LDT(19, val); break; | ||
86 | case 20: LDT(20, val); break; | ||
87 | case 21: LDT(21, val); break; | ||
88 | case 22: LDT(22, val); break; | ||
89 | case 23: LDT(23, val); break; | ||
90 | case 24: LDT(24, val); break; | ||
91 | case 25: LDT(25, val); break; | ||
92 | case 26: LDT(26, val); break; | ||
93 | case 27: LDT(27, val); break; | ||
94 | case 28: LDT(28, val); break; | ||
95 | case 29: LDT(29, val); break; | ||
96 | case 30: LDT(30, val); break; | ||
97 | case 31: LDT(31, val); break; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | #if defined(__alpha_cix__) || defined(__alpha_fix__) | ||
102 | #define STS(reg,val) asm volatile ("ftois $f"#reg",%0" : "=r"(val)); | ||
103 | #else | ||
104 | #define STS(reg,val) asm volatile ("sts $f"#reg",%0" : "=m"(val)); | ||
105 | #endif | ||
106 | |||
107 | unsigned long | ||
108 | alpha_read_fp_reg_s (unsigned long reg) | ||
109 | { | ||
110 | unsigned long val; | ||
111 | |||
112 | switch (reg) { | ||
113 | case 0: STS( 0, val); break; | ||
114 | case 1: STS( 1, val); break; | ||
115 | case 2: STS( 2, val); break; | ||
116 | case 3: STS( 3, val); break; | ||
117 | case 4: STS( 4, val); break; | ||
118 | case 5: STS( 5, val); break; | ||
119 | case 6: STS( 6, val); break; | ||
120 | case 7: STS( 7, val); break; | ||
121 | case 8: STS( 8, val); break; | ||
122 | case 9: STS( 9, val); break; | ||
123 | case 10: STS(10, val); break; | ||
124 | case 11: STS(11, val); break; | ||
125 | case 12: STS(12, val); break; | ||
126 | case 13: STS(13, val); break; | ||
127 | case 14: STS(14, val); break; | ||
128 | case 15: STS(15, val); break; | ||
129 | case 16: STS(16, val); break; | ||
130 | case 17: STS(17, val); break; | ||
131 | case 18: STS(18, val); break; | ||
132 | case 19: STS(19, val); break; | ||
133 | case 20: STS(20, val); break; | ||
134 | case 21: STS(21, val); break; | ||
135 | case 22: STS(22, val); break; | ||
136 | case 23: STS(23, val); break; | ||
137 | case 24: STS(24, val); break; | ||
138 | case 25: STS(25, val); break; | ||
139 | case 26: STS(26, val); break; | ||
140 | case 27: STS(27, val); break; | ||
141 | case 28: STS(28, val); break; | ||
142 | case 29: STS(29, val); break; | ||
143 | case 30: STS(30, val); break; | ||
144 | case 31: STS(31, val); break; | ||
145 | default: return 0; | ||
146 | } | ||
147 | return val; | ||
148 | } | ||
149 | |||
150 | #if defined(__alpha_cix__) || defined(__alpha_fix__) | ||
151 | #define LDS(reg,val) asm volatile ("itofs %0,$f"#reg : : "r"(val)); | ||
152 | #else | ||
153 | #define LDS(reg,val) asm volatile ("lds $f"#reg",%0" : : "m"(val)); | ||
154 | #endif | ||
155 | |||
156 | void | ||
157 | alpha_write_fp_reg_s (unsigned long reg, unsigned long val) | ||
158 | { | ||
159 | switch (reg) { | ||
160 | case 0: LDS( 0, val); break; | ||
161 | case 1: LDS( 1, val); break; | ||
162 | case 2: LDS( 2, val); break; | ||
163 | case 3: LDS( 3, val); break; | ||
164 | case 4: LDS( 4, val); break; | ||
165 | case 5: LDS( 5, val); break; | ||
166 | case 6: LDS( 6, val); break; | ||
167 | case 7: LDS( 7, val); break; | ||
168 | case 8: LDS( 8, val); break; | ||
169 | case 9: LDS( 9, val); break; | ||
170 | case 10: LDS(10, val); break; | ||
171 | case 11: LDS(11, val); break; | ||
172 | case 12: LDS(12, val); break; | ||
173 | case 13: LDS(13, val); break; | ||
174 | case 14: LDS(14, val); break; | ||
175 | case 15: LDS(15, val); break; | ||
176 | case 16: LDS(16, val); break; | ||
177 | case 17: LDS(17, val); break; | ||
178 | case 18: LDS(18, val); break; | ||
179 | case 19: LDS(19, val); break; | ||
180 | case 20: LDS(20, val); break; | ||
181 | case 21: LDS(21, val); break; | ||
182 | case 22: LDS(22, val); break; | ||
183 | case 23: LDS(23, val); break; | ||
184 | case 24: LDS(24, val); break; | ||
185 | case 25: LDS(25, val); break; | ||
186 | case 26: LDS(26, val); break; | ||
187 | case 27: LDS(27, val); break; | ||
188 | case 28: LDS(28, val); break; | ||
189 | case 29: LDS(29, val); break; | ||
190 | case 30: LDS(30, val); break; | ||
191 | case 31: LDS(31, val); break; | ||
192 | } | ||
193 | } | ||
diff --git a/arch/alpha/lib/memchr.S b/arch/alpha/lib/memchr.S new file mode 100644 index 000000000000..14427eeb555e --- /dev/null +++ b/arch/alpha/lib/memchr.S | |||
@@ -0,0 +1,164 @@ | |||
1 | /* Copyright (C) 1996 Free Software Foundation, Inc. | ||
2 | This file is part of the GNU C Library. | ||
3 | Contributed by David Mosberger (davidm@cs.arizona.edu). | ||
4 | |||
5 | The GNU C Library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Library General Public License as | ||
7 | published by the Free Software Foundation; either version 2 of the | ||
8 | License, or (at your option) any later version. | ||
9 | |||
10 | The GNU C Library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Library General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Library General Public | ||
16 | License along with the GNU C Library; see the file COPYING.LIB. If not, | ||
17 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. */ | ||
19 | |||
20 | /* Finds characters in a memory area. Optimized for the Alpha: | ||
21 | |||
22 | - memory accessed as aligned quadwords only | ||
23 | - uses cmpbge to compare 8 bytes in parallel | ||
24 | - does binary search to find 0 byte in last | ||
25 | quadword (HAKMEM needed 12 instructions to | ||
26 | do this instead of the 9 instructions that | ||
27 | binary search needs). | ||
28 | |||
29 | For correctness consider that: | ||
30 | |||
31 | - only minimum number of quadwords may be accessed | ||
32 | - the third argument is an unsigned long | ||
33 | */ | ||
34 | |||
35 | .set noreorder | ||
36 | .set noat | ||
37 | |||
38 | .globl memchr | ||
39 | .ent memchr | ||
40 | memchr: | ||
41 | .frame $30,0,$26,0 | ||
42 | .prologue 0 | ||
43 | |||
44 | # Hack -- if someone passes in (size_t)-1, hoping to just | ||
45 | # search til the end of the address space, we will overflow | ||
46 | # below when we find the address of the last byte. Given | ||
47 | # that we will never have a 56-bit address space, cropping | ||
48 | # the length is the easiest way to avoid trouble. | ||
49 | zap $18, 0x80, $5 #-e0 : | ||
50 | |||
51 | beq $18, $not_found # .. e1 : | ||
52 | ldq_u $1, 0($16) # e1 : load first quadword | ||
53 | insbl $17, 1, $2 # .. e0 : $2 = 000000000000ch00 | ||
54 | and $17, 0xff, $17 #-e0 : $17 = 00000000000000ch | ||
55 | cmpult $18, 9, $4 # .. e1 : | ||
56 | or $2, $17, $17 # e0 : $17 = 000000000000chch | ||
57 | lda $3, -1($31) # .. e1 : | ||
58 | sll $17, 16, $2 #-e0 : $2 = 00000000chch0000 | ||
59 | addq $16, $5, $5 # .. e1 : | ||
60 | or $2, $17, $17 # e1 : $17 = 00000000chchchch | ||
61 | unop # : | ||
62 | sll $17, 32, $2 #-e0 : $2 = chchchch00000000 | ||
63 | or $2, $17, $17 # e1 : $17 = chchchchchchchch | ||
64 | extql $1, $16, $7 # e0 : | ||
65 | beq $4, $first_quad # .. e1 : | ||
66 | |||
67 | ldq_u $6, -1($5) #-e1 : eight or less bytes to search | ||
68 | extqh $6, $16, $6 # .. e0 : | ||
69 | mov $16, $0 # e0 : | ||
70 | or $7, $6, $1 # .. e1 : $1 = quadword starting at $16 | ||
71 | |||
72 | # Deal with the case where at most 8 bytes remain to be searched | ||
73 | # in $1. E.g.: | ||
74 | # $18 = 6 | ||
75 | # $1 = ????c6c5c4c3c2c1 | ||
76 | $last_quad: | ||
77 | negq $18, $6 #-e0 : | ||
78 | xor $17, $1, $1 # .. e1 : | ||
79 | srl $3, $6, $6 # e0 : $6 = mask of $18 bits set | ||
80 | cmpbge $31, $1, $2 # .. e1 : | ||
81 | and $2, $6, $2 #-e0 : | ||
82 | beq $2, $not_found # .. e1 : | ||
83 | |||
84 | $found_it: | ||
85 | # Now, determine which byte matched: | ||
86 | negq $2, $3 # e0 : | ||
87 | and $2, $3, $2 # e1 : | ||
88 | |||
89 | and $2, 0x0f, $1 #-e0 : | ||
90 | addq $0, 4, $3 # .. e1 : | ||
91 | cmoveq $1, $3, $0 # e0 : | ||
92 | |||
93 | addq $0, 2, $3 # .. e1 : | ||
94 | and $2, 0x33, $1 #-e0 : | ||
95 | cmoveq $1, $3, $0 # .. e1 : | ||
96 | |||
97 | and $2, 0x55, $1 # e0 : | ||
98 | addq $0, 1, $3 # .. e1 : | ||
99 | cmoveq $1, $3, $0 #-e0 : | ||
100 | |||
101 | $done: ret # .. e1 : | ||
102 | |||
103 | # Deal with the case where $18 > 8 bytes remain to be | ||
104 | # searched. $16 may not be aligned. | ||
105 | .align 4 | ||
106 | $first_quad: | ||
107 | andnot $16, 0x7, $0 #-e1 : | ||
108 | insqh $3, $16, $2 # .. e0 : $2 = 0000ffffffffffff ($16<0:2> ff) | ||
109 | xor $1, $17, $1 # e0 : | ||
110 | or $1, $2, $1 # e1 : $1 = ====ffffffffffff | ||
111 | cmpbge $31, $1, $2 #-e0 : | ||
112 | bne $2, $found_it # .. e1 : | ||
113 | |||
114 | # At least one byte left to process. | ||
115 | |||
116 | ldq $1, 8($0) # e0 : | ||
117 | subq $5, 1, $18 # .. e1 : | ||
118 | addq $0, 8, $0 #-e0 : | ||
119 | |||
120 | # Make $18 point to last quad to be accessed (the | ||
121 | # last quad may or may not be partial). | ||
122 | |||
123 | andnot $18, 0x7, $18 # .. e1 : | ||
124 | cmpult $0, $18, $2 # e0 : | ||
125 | beq $2, $final # .. e1 : | ||
126 | |||
127 | # At least two quads remain to be accessed. | ||
128 | |||
129 | subq $18, $0, $4 #-e0 : $4 <- nr quads to be processed | ||
130 | and $4, 8, $4 # e1 : odd number of quads? | ||
131 | bne $4, $odd_quad_count # e1 : | ||
132 | |||
133 | # At least three quads remain to be accessed | ||
134 | |||
135 | mov $1, $4 # e0 : move prefetched value to correct reg | ||
136 | |||
137 | .align 4 | ||
138 | $unrolled_loop: | ||
139 | ldq $1, 8($0) #-e0 : prefetch $1 | ||
140 | xor $17, $4, $2 # .. e1 : | ||
141 | cmpbge $31, $2, $2 # e0 : | ||
142 | bne $2, $found_it # .. e1 : | ||
143 | |||
144 | addq $0, 8, $0 #-e0 : | ||
145 | $odd_quad_count: | ||
146 | xor $17, $1, $2 # .. e1 : | ||
147 | ldq $4, 8($0) # e0 : prefetch $4 | ||
148 | cmpbge $31, $2, $2 # .. e1 : | ||
149 | addq $0, 8, $6 #-e0 : | ||
150 | bne $2, $found_it # .. e1 : | ||
151 | |||
152 | cmpult $6, $18, $6 # e0 : | ||
153 | addq $0, 8, $0 # .. e1 : | ||
154 | bne $6, $unrolled_loop #-e1 : | ||
155 | |||
156 | mov $4, $1 # e0 : move prefetched value into $1 | ||
157 | $final: subq $5, $0, $18 # .. e1 : $18 <- number of bytes left to do | ||
158 | bne $18, $last_quad # e1 : | ||
159 | |||
160 | $not_found: | ||
161 | mov $31, $0 #-e0 : | ||
162 | ret # .. e1 : | ||
163 | |||
164 | .end memchr | ||
diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c new file mode 100644 index 000000000000..64083fc73238 --- /dev/null +++ b/arch/alpha/lib/memcpy.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/lib/memcpy.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This is a reasonably optimized memcpy() routine. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Note that the C code is written to be optimized into good assembly. However, | ||
13 | * at this point gcc is unable to sanely compile "if (n >= 0)", resulting in a | ||
14 | * explicit compare against 0 (instead of just using the proper "blt reg, xx" or | ||
15 | * "bge reg, xx"). I hope alpha-gcc will be fixed to notice this eventually.. | ||
16 | */ | ||
17 | |||
18 | #include <linux/types.h> | ||
19 | |||
20 | /* | ||
21 | * This should be done in one go with ldq_u*2/mask/stq_u. Do it | ||
22 | * with a macro so that we can fix it up later.. | ||
23 | */ | ||
24 | #define ALIGN_DEST_TO8_UP(d,s,n) \ | ||
25 | while (d & 7) { \ | ||
26 | if (n <= 0) return; \ | ||
27 | n--; \ | ||
28 | *(char *) d = *(char *) s; \ | ||
29 | d++; s++; \ | ||
30 | } | ||
31 | #define ALIGN_DEST_TO8_DN(d,s,n) \ | ||
32 | while (d & 7) { \ | ||
33 | if (n <= 0) return; \ | ||
34 | n--; \ | ||
35 | d--; s--; \ | ||
36 | *(char *) d = *(char *) s; \ | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * This should similarly be done with ldq_u*2/mask/stq. The destination | ||
41 | * is aligned, but we don't fill in a full quad-word | ||
42 | */ | ||
43 | #define DO_REST_UP(d,s,n) \ | ||
44 | while (n > 0) { \ | ||
45 | n--; \ | ||
46 | *(char *) d = *(char *) s; \ | ||
47 | d++; s++; \ | ||
48 | } | ||
49 | #define DO_REST_DN(d,s,n) \ | ||
50 | while (n > 0) { \ | ||
51 | n--; \ | ||
52 | d--; s--; \ | ||
53 | *(char *) d = *(char *) s; \ | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * This should be done with ldq/mask/stq. The source and destination are | ||
58 | * aligned, but we don't fill in a full quad-word | ||
59 | */ | ||
60 | #define DO_REST_ALIGNED_UP(d,s,n) DO_REST_UP(d,s,n) | ||
61 | #define DO_REST_ALIGNED_DN(d,s,n) DO_REST_DN(d,s,n) | ||
62 | |||
63 | /* | ||
64 | * This does unaligned memory copies. We want to avoid storing to | ||
65 | * an unaligned address, as that would do a read-modify-write cycle. | ||
66 | * We also want to avoid double-reading the unaligned reads. | ||
67 | * | ||
68 | * Note the ordering to try to avoid load (and address generation) latencies. | ||
69 | */ | ||
70 | static inline void __memcpy_unaligned_up (unsigned long d, unsigned long s, | ||
71 | long n) | ||
72 | { | ||
73 | ALIGN_DEST_TO8_UP(d,s,n); | ||
74 | n -= 8; /* to avoid compare against 8 in the loop */ | ||
75 | if (n >= 0) { | ||
76 | unsigned long low_word, high_word; | ||
77 | __asm__("ldq_u %0,%1":"=r" (low_word):"m" (*(unsigned long *) s)); | ||
78 | do { | ||
79 | unsigned long tmp; | ||
80 | __asm__("ldq_u %0,%1":"=r" (high_word):"m" (*(unsigned long *)(s+8))); | ||
81 | n -= 8; | ||
82 | __asm__("extql %1,%2,%0" | ||
83 | :"=r" (low_word) | ||
84 | :"r" (low_word), "r" (s)); | ||
85 | __asm__("extqh %1,%2,%0" | ||
86 | :"=r" (tmp) | ||
87 | :"r" (high_word), "r" (s)); | ||
88 | s += 8; | ||
89 | *(unsigned long *) d = low_word | tmp; | ||
90 | d += 8; | ||
91 | low_word = high_word; | ||
92 | } while (n >= 0); | ||
93 | } | ||
94 | n += 8; | ||
95 | DO_REST_UP(d,s,n); | ||
96 | } | ||
97 | |||
98 | static inline void __memcpy_unaligned_dn (unsigned long d, unsigned long s, | ||
99 | long n) | ||
100 | { | ||
101 | /* I don't understand AXP assembler well enough for this. -Tim */ | ||
102 | s += n; | ||
103 | d += n; | ||
104 | while (n--) | ||
105 | * (char *) --d = * (char *) --s; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Hmm.. Strange. The __asm__ here is there to make gcc use an integer register | ||
110 | * for the load-store. I don't know why, but it would seem that using a floating | ||
111 | * point register for the move seems to slow things down (very small difference, | ||
112 | * though). | ||
113 | * | ||
114 | * Note the ordering to try to avoid load (and address generation) latencies. | ||
115 | */ | ||
116 | static inline void __memcpy_aligned_up (unsigned long d, unsigned long s, | ||
117 | long n) | ||
118 | { | ||
119 | ALIGN_DEST_TO8_UP(d,s,n); | ||
120 | n -= 8; | ||
121 | while (n >= 0) { | ||
122 | unsigned long tmp; | ||
123 | __asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s)); | ||
124 | n -= 8; | ||
125 | s += 8; | ||
126 | *(unsigned long *) d = tmp; | ||
127 | d += 8; | ||
128 | } | ||
129 | n += 8; | ||
130 | DO_REST_ALIGNED_UP(d,s,n); | ||
131 | } | ||
132 | static inline void __memcpy_aligned_dn (unsigned long d, unsigned long s, | ||
133 | long n) | ||
134 | { | ||
135 | s += n; | ||
136 | d += n; | ||
137 | ALIGN_DEST_TO8_DN(d,s,n); | ||
138 | n -= 8; | ||
139 | while (n >= 0) { | ||
140 | unsigned long tmp; | ||
141 | s -= 8; | ||
142 | __asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s)); | ||
143 | n -= 8; | ||
144 | d -= 8; | ||
145 | *(unsigned long *) d = tmp; | ||
146 | } | ||
147 | n += 8; | ||
148 | DO_REST_ALIGNED_DN(d,s,n); | ||
149 | } | ||
150 | |||
151 | void * memcpy(void * dest, const void *src, size_t n) | ||
152 | { | ||
153 | if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) { | ||
154 | __memcpy_aligned_up ((unsigned long) dest, (unsigned long) src, | ||
155 | n); | ||
156 | return dest; | ||
157 | } | ||
158 | __memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n); | ||
159 | return dest; | ||
160 | } | ||
161 | |||
162 | /* For backward modules compatibility, define __memcpy. */ | ||
163 | asm("__memcpy = memcpy; .globl __memcpy"); | ||
diff --git a/arch/alpha/lib/memmove.S b/arch/alpha/lib/memmove.S new file mode 100644 index 000000000000..eb3b6e02242f --- /dev/null +++ b/arch/alpha/lib/memmove.S | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/memmove.S | ||
3 | * | ||
4 | * Barely optimized memmove routine for Alpha EV5. | ||
5 | * | ||
6 | * This is hand-massaged output from the original memcpy.c. We defer to | ||
7 | * memcpy whenever possible; the backwards copy loops are not unrolled. | ||
8 | */ | ||
9 | |||
10 | .set noat | ||
11 | .set noreorder | ||
12 | .text | ||
13 | |||
14 | .align 4 | ||
15 | .globl memmove | ||
16 | .ent memmove | ||
17 | memmove: | ||
18 | ldgp $29, 0($27) | ||
19 | unop | ||
20 | nop | ||
21 | .prologue 1 | ||
22 | |||
23 | addq $16,$18,$4 | ||
24 | addq $17,$18,$5 | ||
25 | cmpule $4,$17,$1 /* dest + n <= src */ | ||
26 | cmpule $5,$16,$2 /* dest >= src + n */ | ||
27 | |||
28 | bis $1,$2,$1 | ||
29 | mov $16,$0 | ||
30 | xor $16,$17,$2 | ||
31 | bne $1,memcpy !samegp | ||
32 | |||
33 | and $2,7,$2 /* Test for src/dest co-alignment. */ | ||
34 | and $16,7,$1 | ||
35 | cmpule $16,$17,$3 | ||
36 | bne $3,$memmove_up /* dest < src */ | ||
37 | |||
38 | and $4,7,$1 | ||
39 | bne $2,$misaligned_dn | ||
40 | unop | ||
41 | beq $1,$skip_aligned_byte_loop_head_dn | ||
42 | |||
43 | $aligned_byte_loop_head_dn: | ||
44 | lda $4,-1($4) | ||
45 | lda $5,-1($5) | ||
46 | unop | ||
47 | ble $18,$egress | ||
48 | |||
49 | ldq_u $3,0($5) | ||
50 | ldq_u $2,0($4) | ||
51 | lda $18,-1($18) | ||
52 | extbl $3,$5,$1 | ||
53 | |||
54 | insbl $1,$4,$1 | ||
55 | mskbl $2,$4,$2 | ||
56 | bis $1,$2,$1 | ||
57 | and $4,7,$6 | ||
58 | |||
59 | stq_u $1,0($4) | ||
60 | bne $6,$aligned_byte_loop_head_dn | ||
61 | |||
62 | $skip_aligned_byte_loop_head_dn: | ||
63 | lda $18,-8($18) | ||
64 | blt $18,$skip_aligned_word_loop_dn | ||
65 | |||
66 | $aligned_word_loop_dn: | ||
67 | ldq $1,-8($5) | ||
68 | nop | ||
69 | lda $5,-8($5) | ||
70 | lda $18,-8($18) | ||
71 | |||
72 | stq $1,-8($4) | ||
73 | nop | ||
74 | lda $4,-8($4) | ||
75 | bge $18,$aligned_word_loop_dn | ||
76 | |||
77 | $skip_aligned_word_loop_dn: | ||
78 | lda $18,8($18) | ||
79 | bgt $18,$byte_loop_tail_dn | ||
80 | unop | ||
81 | ret $31,($26),1 | ||
82 | |||
83 | .align 4 | ||
84 | $misaligned_dn: | ||
85 | nop | ||
86 | fnop | ||
87 | unop | ||
88 | beq $18,$egress | ||
89 | |||
90 | $byte_loop_tail_dn: | ||
91 | ldq_u $3,-1($5) | ||
92 | ldq_u $2,-1($4) | ||
93 | lda $5,-1($5) | ||
94 | lda $4,-1($4) | ||
95 | |||
96 | lda $18,-1($18) | ||
97 | extbl $3,$5,$1 | ||
98 | insbl $1,$4,$1 | ||
99 | mskbl $2,$4,$2 | ||
100 | |||
101 | bis $1,$2,$1 | ||
102 | stq_u $1,0($4) | ||
103 | bgt $18,$byte_loop_tail_dn | ||
104 | br $egress | ||
105 | |||
106 | $memmove_up: | ||
107 | mov $16,$4 | ||
108 | mov $17,$5 | ||
109 | bne $2,$misaligned_up | ||
110 | beq $1,$skip_aligned_byte_loop_head_up | ||
111 | |||
112 | $aligned_byte_loop_head_up: | ||
113 | unop | ||
114 | ble $18,$egress | ||
115 | ldq_u $3,0($5) | ||
116 | ldq_u $2,0($4) | ||
117 | |||
118 | lda $18,-1($18) | ||
119 | extbl $3,$5,$1 | ||
120 | insbl $1,$4,$1 | ||
121 | mskbl $2,$4,$2 | ||
122 | |||
123 | bis $1,$2,$1 | ||
124 | lda $5,1($5) | ||
125 | stq_u $1,0($4) | ||
126 | lda $4,1($4) | ||
127 | |||
128 | and $4,7,$6 | ||
129 | bne $6,$aligned_byte_loop_head_up | ||
130 | |||
131 | $skip_aligned_byte_loop_head_up: | ||
132 | lda $18,-8($18) | ||
133 | blt $18,$skip_aligned_word_loop_up | ||
134 | |||
135 | $aligned_word_loop_up: | ||
136 | ldq $1,0($5) | ||
137 | nop | ||
138 | lda $5,8($5) | ||
139 | lda $18,-8($18) | ||
140 | |||
141 | stq $1,0($4) | ||
142 | nop | ||
143 | lda $4,8($4) | ||
144 | bge $18,$aligned_word_loop_up | ||
145 | |||
146 | $skip_aligned_word_loop_up: | ||
147 | lda $18,8($18) | ||
148 | bgt $18,$byte_loop_tail_up | ||
149 | unop | ||
150 | ret $31,($26),1 | ||
151 | |||
152 | .align 4 | ||
153 | $misaligned_up: | ||
154 | nop | ||
155 | fnop | ||
156 | unop | ||
157 | beq $18,$egress | ||
158 | |||
159 | $byte_loop_tail_up: | ||
160 | ldq_u $3,0($5) | ||
161 | ldq_u $2,0($4) | ||
162 | lda $18,-1($18) | ||
163 | extbl $3,$5,$1 | ||
164 | |||
165 | insbl $1,$4,$1 | ||
166 | mskbl $2,$4,$2 | ||
167 | bis $1,$2,$1 | ||
168 | stq_u $1,0($4) | ||
169 | |||
170 | lda $5,1($5) | ||
171 | lda $4,1($4) | ||
172 | nop | ||
173 | bgt $18,$byte_loop_tail_up | ||
174 | |||
175 | $egress: | ||
176 | ret $31,($26),1 | ||
177 | nop | ||
178 | nop | ||
179 | nop | ||
180 | |||
181 | .end memmove | ||
diff --git a/arch/alpha/lib/memset.S b/arch/alpha/lib/memset.S new file mode 100644 index 000000000000..8ff6e7e1773e --- /dev/null +++ b/arch/alpha/lib/memset.S | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/memset.S | ||
3 | * | ||
4 | * This is an efficient (and small) implementation of the C library "memset()" | ||
5 | * function for the alpha. | ||
6 | * | ||
7 | * (C) Copyright 1996 Linus Torvalds | ||
8 | * | ||
9 | * This routine is "moral-ware": you are free to use it any way you wish, and | ||
10 | * the only obligation I put on you is a moral one: if you make any improvements | ||
11 | * to the routine, please send me your improvements for me to use similarly. | ||
12 | * | ||
13 | * The scheduling comments are according to the EV5 documentation (and done by | ||
14 | * hand, so they might well be incorrect, please do tell me about it..) | ||
15 | */ | ||
16 | |||
17 | .set noat | ||
18 | .set noreorder | ||
19 | .text | ||
20 | .globl memset | ||
21 | .globl __memset | ||
22 | .globl __memsetw | ||
23 | .globl __constant_c_memset | ||
24 | .ent __memset | ||
25 | .align 5 | ||
26 | __memset: | ||
27 | .frame $30,0,$26,0 | ||
28 | .prologue 0 | ||
29 | |||
30 | and $17,255,$1 /* E1 */ | ||
31 | insbl $17,1,$17 /* .. E0 */ | ||
32 | bis $17,$1,$17 /* E0 (p-c latency, next cycle) */ | ||
33 | sll $17,16,$1 /* E1 (p-c latency, next cycle) */ | ||
34 | |||
35 | bis $17,$1,$17 /* E0 (p-c latency, next cycle) */ | ||
36 | sll $17,32,$1 /* E1 (p-c latency, next cycle) */ | ||
37 | bis $17,$1,$17 /* E0 (p-c latency, next cycle) */ | ||
38 | ldq_u $31,0($30) /* .. E1 */ | ||
39 | |||
40 | .align 5 | ||
41 | __constant_c_memset: | ||
42 | addq $18,$16,$6 /* E0 */ | ||
43 | bis $16,$16,$0 /* .. E1 */ | ||
44 | xor $16,$6,$1 /* E0 */ | ||
45 | ble $18,end /* .. E1 */ | ||
46 | |||
47 | bic $1,7,$1 /* E0 */ | ||
48 | beq $1,within_one_quad /* .. E1 (note EV5 zero-latency forwarding) */ | ||
49 | and $16,7,$3 /* E0 */ | ||
50 | beq $3,aligned /* .. E1 (note EV5 zero-latency forwarding) */ | ||
51 | |||
52 | ldq_u $4,0($16) /* E0 */ | ||
53 | bis $16,$16,$5 /* .. E1 */ | ||
54 | insql $17,$16,$2 /* E0 */ | ||
55 | subq $3,8,$3 /* .. E1 */ | ||
56 | |||
57 | addq $18,$3,$18 /* E0 $18 is new count ($3 is negative) */ | ||
58 | mskql $4,$16,$4 /* .. E1 (and possible load stall) */ | ||
59 | subq $16,$3,$16 /* E0 $16 is new aligned destination */ | ||
60 | bis $2,$4,$1 /* .. E1 */ | ||
61 | |||
62 | bis $31,$31,$31 /* E0 */ | ||
63 | ldq_u $31,0($30) /* .. E1 */ | ||
64 | stq_u $1,0($5) /* E0 */ | ||
65 | bis $31,$31,$31 /* .. E1 */ | ||
66 | |||
67 | .align 4 | ||
68 | aligned: | ||
69 | sra $18,3,$3 /* E0 */ | ||
70 | and $18,7,$18 /* .. E1 */ | ||
71 | bis $16,$16,$5 /* E0 */ | ||
72 | beq $3,no_quad /* .. E1 */ | ||
73 | |||
74 | .align 3 | ||
75 | loop: | ||
76 | stq $17,0($5) /* E0 */ | ||
77 | subq $3,1,$3 /* .. E1 */ | ||
78 | addq $5,8,$5 /* E0 */ | ||
79 | bne $3,loop /* .. E1 */ | ||
80 | |||
81 | no_quad: | ||
82 | bis $31,$31,$31 /* E0 */ | ||
83 | beq $18,end /* .. E1 */ | ||
84 | ldq $7,0($5) /* E0 */ | ||
85 | mskqh $7,$6,$2 /* .. E1 (and load stall) */ | ||
86 | |||
87 | insqh $17,$6,$4 /* E0 */ | ||
88 | bis $2,$4,$1 /* .. E1 */ | ||
89 | stq $1,0($5) /* E0 */ | ||
90 | ret $31,($26),1 /* .. E1 */ | ||
91 | |||
92 | .align 3 | ||
93 | within_one_quad: | ||
94 | ldq_u $1,0($16) /* E0 */ | ||
95 | insql $17,$16,$2 /* E1 */ | ||
96 | mskql $1,$16,$4 /* E0 (after load stall) */ | ||
97 | bis $2,$4,$2 /* E0 */ | ||
98 | |||
99 | mskql $2,$6,$4 /* E0 */ | ||
100 | mskqh $1,$6,$2 /* .. E1 */ | ||
101 | bis $2,$4,$1 /* E0 */ | ||
102 | stq_u $1,0($16) /* E0 */ | ||
103 | |||
104 | end: | ||
105 | ret $31,($26),1 /* E1 */ | ||
106 | .end __memset | ||
107 | |||
108 | .align 5 | ||
109 | .ent __memsetw | ||
110 | __memsetw: | ||
111 | .prologue 0 | ||
112 | |||
113 | inswl $17,0,$1 /* E0 */ | ||
114 | inswl $17,2,$2 /* E0 */ | ||
115 | inswl $17,4,$3 /* E0 */ | ||
116 | or $1,$2,$1 /* .. E1 */ | ||
117 | inswl $17,6,$4 /* E0 */ | ||
118 | or $1,$3,$1 /* .. E1 */ | ||
119 | or $1,$4,$17 /* E0 */ | ||
120 | br __constant_c_memset /* .. E1 */ | ||
121 | |||
122 | .end __memsetw | ||
123 | |||
124 | memset = __memset | ||
diff --git a/arch/alpha/lib/srm_printk.c b/arch/alpha/lib/srm_printk.c new file mode 100644 index 000000000000..31b53c49435e --- /dev/null +++ b/arch/alpha/lib/srm_printk.c | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/srm_printk.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <asm/console.h> | ||
7 | |||
8 | long | ||
9 | srm_printk(const char *fmt, ...) | ||
10 | { | ||
11 | static char buf[1024]; | ||
12 | va_list args; | ||
13 | long len, num_lf; | ||
14 | char *src, *dst; | ||
15 | |||
16 | va_start(args, fmt); | ||
17 | len = vsprintf(buf, fmt, args); | ||
18 | va_end(args); | ||
19 | |||
20 | /* count number of linefeeds in string: */ | ||
21 | |||
22 | num_lf = 0; | ||
23 | for (src = buf; *src; ++src) { | ||
24 | if (*src == '\n') { | ||
25 | ++num_lf; | ||
26 | } | ||
27 | } | ||
28 | |||
29 | if (num_lf) { | ||
30 | /* expand each linefeed into carriage-return/linefeed: */ | ||
31 | for (dst = src + num_lf; src >= buf; ) { | ||
32 | if (*src == '\n') { | ||
33 | *dst-- = '\r'; | ||
34 | } | ||
35 | *dst-- = *src--; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | srm_puts(buf, num_lf+len); | ||
40 | return len; | ||
41 | } | ||
diff --git a/arch/alpha/lib/srm_puts.c b/arch/alpha/lib/srm_puts.c new file mode 100644 index 000000000000..7b60a6f75a78 --- /dev/null +++ b/arch/alpha/lib/srm_puts.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/srm_puts.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/string.h> | ||
6 | #include <asm/console.h> | ||
7 | |||
8 | long | ||
9 | srm_puts(const char *str, long len) | ||
10 | { | ||
11 | long remaining, written; | ||
12 | |||
13 | if (!callback_init_done) | ||
14 | return len; | ||
15 | |||
16 | for (remaining = len; remaining > 0; remaining -= written) | ||
17 | { | ||
18 | written = callback_puts(0, str, remaining); | ||
19 | written &= 0xffffffff; | ||
20 | str += written; | ||
21 | } | ||
22 | return len; | ||
23 | } | ||
diff --git a/arch/alpha/lib/stacktrace.c b/arch/alpha/lib/stacktrace.c new file mode 100644 index 000000000000..6d432e42aedc --- /dev/null +++ b/arch/alpha/lib/stacktrace.c | |||
@@ -0,0 +1,103 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <asm/system.h> | ||
3 | |||
4 | typedef unsigned int instr; | ||
5 | |||
6 | #define MAJOR_OP 0xfc000000 | ||
7 | #define LDA_OP 0x20000000 | ||
8 | #define STQ_OP 0xb4000000 | ||
9 | #define BR_OP 0xc0000000 | ||
10 | |||
11 | #define STK_ALLOC_1 0x23de8000 /* lda $30,-X($30) */ | ||
12 | #define STK_ALLOC_1M 0xffff8000 | ||
13 | #define STK_ALLOC_2 0x43c0153e /* subq $30,X,$30 */ | ||
14 | #define STK_ALLOC_2M 0xffe01fff | ||
15 | |||
16 | #define MEM_REG 0x03e00000 | ||
17 | #define MEM_BASE 0x001f0000 | ||
18 | #define MEM_OFF 0x0000ffff | ||
19 | #define MEM_OFF_SIGN 0x00008000 | ||
20 | #define BASE_SP 0x001e0000 | ||
21 | |||
22 | #define STK_ALLOC_MATCH(INSTR) \ | ||
23 | (((INSTR) & STK_ALLOC_1M) == STK_ALLOC_1 \ | ||
24 | || ((INSTR) & STK_ALLOC_2M) == STK_ALLOC_2) | ||
25 | #define STK_PUSH_MATCH(INSTR) \ | ||
26 | (((INSTR) & (MAJOR_OP | MEM_BASE | MEM_OFF_SIGN)) == (STQ_OP | BASE_SP)) | ||
27 | #define MEM_OP_OFFSET(INSTR) \ | ||
28 | (((long)((INSTR) & MEM_OFF) << 48) >> 48) | ||
29 | #define MEM_OP_REG(INSTR) \ | ||
30 | (((INSTR) & MEM_REG) >> 22) | ||
31 | |||
32 | /* Branches, jumps, PAL calls, and illegal opcodes end a basic block. */ | ||
33 | #define BB_END(INSTR) \ | ||
34 | (((instr)(INSTR) >= BR_OP) | ((instr)(INSTR) < LDA_OP) | \ | ||
35 | ((((instr)(INSTR) ^ 0x60000000) < 0x20000000) & \ | ||
36 | (((instr)(INSTR) & 0x0c000000) != 0))) | ||
37 | |||
38 | #define IS_KERNEL_TEXT(PC) ((unsigned long)(PC) > START_ADDR) | ||
39 | |||
40 | static char reg_name[][4] = { | ||
41 | "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ", "t7 ", | ||
42 | "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "s6 ", "a0 ", "a1 ", | ||
43 | "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ", "t10", "t11", "ra ", | ||
44 | "pv ", "at ", "gp ", "sp ", "0" | ||
45 | }; | ||
46 | |||
47 | |||
48 | static instr * | ||
49 | display_stored_regs(instr * pro_pc, unsigned char * sp) | ||
50 | { | ||
51 | instr * ret_pc = 0; | ||
52 | int reg; | ||
53 | unsigned long value; | ||
54 | |||
55 | printk("Prologue [<%p>], Frame %p:\n", pro_pc, sp); | ||
56 | while (!BB_END(*pro_pc)) | ||
57 | if (STK_PUSH_MATCH(*pro_pc)) { | ||
58 | reg = (*pro_pc & MEM_REG) >> 21; | ||
59 | value = *(unsigned long *)(sp + (*pro_pc & MEM_OFF)); | ||
60 | if (reg == 26) | ||
61 | ret_pc = (instr *)value; | ||
62 | printk("\t\t%s / 0x%016lx\n", reg_name[reg], value); | ||
63 | } | ||
64 | return ret_pc; | ||
65 | } | ||
66 | |||
67 | static instr * | ||
68 | seek_prologue(instr * pc) | ||
69 | { | ||
70 | while (!STK_ALLOC_MATCH(*pc)) | ||
71 | --pc; | ||
72 | while (!BB_END(*(pc - 1))) | ||
73 | --pc; | ||
74 | return pc; | ||
75 | } | ||
76 | |||
77 | static long | ||
78 | stack_increment(instr * prologue_pc) | ||
79 | { | ||
80 | while (!STK_ALLOC_MATCH(*prologue_pc)) | ||
81 | ++prologue_pc; | ||
82 | |||
83 | /* Count the bytes allocated. */ | ||
84 | if ((*prologue_pc & STK_ALLOC_1M) == STK_ALLOC_1M) | ||
85 | return -(((long)(*prologue_pc) << 48) >> 48); | ||
86 | else | ||
87 | return (*prologue_pc >> 13) & 0xff; | ||
88 | } | ||
89 | |||
90 | void | ||
91 | stacktrace(void) | ||
92 | { | ||
93 | instr * ret_pc; | ||
94 | instr * prologue = (instr *)stacktrace; | ||
95 | register unsigned char * sp __asm__ ("$30"); | ||
96 | |||
97 | printk("\tstack trace:\n"); | ||
98 | do { | ||
99 | ret_pc = display_stored_regs(prologue, sp); | ||
100 | sp += stack_increment(prologue); | ||
101 | prologue = seek_prologue(ret_pc); | ||
102 | } while (IS_KERNEL_TEXT(ret_pc)); | ||
103 | } | ||
diff --git a/arch/alpha/lib/strcasecmp.c b/arch/alpha/lib/strcasecmp.c new file mode 100644 index 000000000000..4e57a216feaf --- /dev/null +++ b/arch/alpha/lib/strcasecmp.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/lib/strcasecmp.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/string.h> | ||
6 | |||
7 | |||
8 | /* We handle nothing here except the C locale. Since this is used in | ||
9 | only one place, on strings known to contain only 7 bit ASCII, this | ||
10 | is ok. */ | ||
11 | |||
12 | int strcasecmp(const char *a, const char *b) | ||
13 | { | ||
14 | int ca, cb; | ||
15 | |||
16 | do { | ||
17 | ca = *a++ & 0xff; | ||
18 | cb = *b++ & 0xff; | ||
19 | if (ca >= 'A' && ca <= 'Z') | ||
20 | ca += 'a' - 'A'; | ||
21 | if (cb >= 'A' && cb <= 'Z') | ||
22 | cb += 'a' - 'A'; | ||
23 | } while (ca == cb && ca != '\0'); | ||
24 | |||
25 | return ca - cb; | ||
26 | } | ||
diff --git a/arch/alpha/lib/strcat.S b/arch/alpha/lib/strcat.S new file mode 100644 index 000000000000..393f50384878 --- /dev/null +++ b/arch/alpha/lib/strcat.S | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strcat.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Append a null-terminated string from SRC to DST. | ||
6 | */ | ||
7 | |||
8 | .text | ||
9 | |||
10 | .align 3 | ||
11 | .globl strcat | ||
12 | .ent strcat | ||
13 | strcat: | ||
14 | .frame $30, 0, $26 | ||
15 | .prologue 0 | ||
16 | |||
17 | mov $16, $0 # set up return value | ||
18 | |||
19 | /* Find the end of the string. */ | ||
20 | |||
21 | ldq_u $1, 0($16) # load first quadword (a0 may be misaligned) | ||
22 | lda $2, -1 | ||
23 | insqh $2, $16, $2 | ||
24 | andnot $16, 7, $16 | ||
25 | or $2, $1, $1 | ||
26 | cmpbge $31, $1, $2 # bits set iff byte == 0 | ||
27 | bne $2, $found | ||
28 | |||
29 | $loop: ldq $1, 8($16) | ||
30 | addq $16, 8, $16 | ||
31 | cmpbge $31, $1, $2 | ||
32 | beq $2, $loop | ||
33 | |||
34 | $found: negq $2, $3 # clear all but least set bit | ||
35 | and $2, $3, $2 | ||
36 | |||
37 | and $2, 0xf0, $3 # binary search for that set bit | ||
38 | and $2, 0xcc, $4 | ||
39 | and $2, 0xaa, $5 | ||
40 | cmovne $3, 4, $3 | ||
41 | cmovne $4, 2, $4 | ||
42 | cmovne $5, 1, $5 | ||
43 | addq $3, $4, $3 | ||
44 | addq $16, $5, $16 | ||
45 | addq $16, $3, $16 | ||
46 | |||
47 | /* Now do the append. */ | ||
48 | |||
49 | mov $26, $23 | ||
50 | br __stxcpy | ||
51 | |||
52 | .end strcat | ||
diff --git a/arch/alpha/lib/strchr.S b/arch/alpha/lib/strchr.S new file mode 100644 index 000000000000..011a175e8329 --- /dev/null +++ b/arch/alpha/lib/strchr.S | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strchr.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Return the address of a given character within a null-terminated | ||
6 | * string, or null if it is not found. | ||
7 | */ | ||
8 | |||
9 | #include <asm/regdef.h> | ||
10 | |||
11 | .set noreorder | ||
12 | .set noat | ||
13 | |||
14 | .align 3 | ||
15 | .globl strchr | ||
16 | .ent strchr | ||
17 | strchr: | ||
18 | .frame sp, 0, ra | ||
19 | .prologue 0 | ||
20 | |||
21 | zapnot a1, 1, a1 # e0 : zero extend the search character | ||
22 | ldq_u t0, 0(a0) # .. e1 : load first quadword | ||
23 | sll a1, 8, t5 # e0 : replicate the search character | ||
24 | andnot a0, 7, v0 # .. e1 : align our loop pointer | ||
25 | or t5, a1, a1 # e0 : | ||
26 | lda t4, -1 # .. e1 : build garbage mask | ||
27 | sll a1, 16, t5 # e0 : | ||
28 | cmpbge zero, t0, t2 # .. e1 : bits set iff byte == zero | ||
29 | mskqh t4, a0, t4 # e0 : | ||
30 | or t5, a1, a1 # .. e1 : | ||
31 | sll a1, 32, t5 # e0 : | ||
32 | cmpbge zero, t4, t4 # .. e1 : bits set iff byte is garbage | ||
33 | or t5, a1, a1 # e0 : | ||
34 | xor t0, a1, t1 # .. e1 : make bytes == c zero | ||
35 | cmpbge zero, t1, t3 # e0 : bits set iff byte == c | ||
36 | or t2, t3, t0 # e1 : bits set iff char match or zero match | ||
37 | andnot t0, t4, t0 # e0 : clear garbage bits | ||
38 | bne t0, $found # .. e1 (zdb) | ||
39 | |||
40 | $loop: ldq t0, 8(v0) # e0 : | ||
41 | addq v0, 8, v0 # .. e1 : | ||
42 | nop # e0 : | ||
43 | xor t0, a1, t1 # .. e1 (ev5 data stall) | ||
44 | cmpbge zero, t0, t2 # e0 : bits set iff byte == 0 | ||
45 | cmpbge zero, t1, t3 # .. e1 : bits set iff byte == c | ||
46 | or t2, t3, t0 # e0 : | ||
47 | beq t0, $loop # .. e1 (zdb) | ||
48 | |||
49 | $found: negq t0, t1 # e0 : clear all but least set bit | ||
50 | and t0, t1, t0 # e1 (stall) | ||
51 | |||
52 | and t0, t3, t1 # e0 : bit set iff byte was the char | ||
53 | beq t1, $retnull # .. e1 (zdb) | ||
54 | |||
55 | and t0, 0xf0, t2 # e0 : binary search for that set bit | ||
56 | and t0, 0xcc, t3 # .. e1 : | ||
57 | and t0, 0xaa, t4 # e0 : | ||
58 | cmovne t2, 4, t2 # .. e1 : | ||
59 | cmovne t3, 2, t3 # e0 : | ||
60 | cmovne t4, 1, t4 # .. e1 : | ||
61 | addq t2, t3, t2 # e0 : | ||
62 | addq v0, t4, v0 # .. e1 : | ||
63 | addq v0, t2, v0 # e0 : | ||
64 | ret # .. e1 : | ||
65 | |||
66 | $retnull: | ||
67 | mov zero, v0 # e0 : | ||
68 | ret # .. e1 : | ||
69 | |||
70 | .end strchr | ||
diff --git a/arch/alpha/lib/strcpy.S b/arch/alpha/lib/strcpy.S new file mode 100644 index 000000000000..e0728e4ad21f --- /dev/null +++ b/arch/alpha/lib/strcpy.S | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strcpy.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Copy a null-terminated string from SRC to DST. Return a pointer | ||
6 | * to the null-terminator in the source. | ||
7 | */ | ||
8 | |||
9 | .text | ||
10 | |||
11 | .align 3 | ||
12 | .globl strcpy | ||
13 | .ent strcpy | ||
14 | strcpy: | ||
15 | .frame $30, 0, $26 | ||
16 | .prologue 0 | ||
17 | |||
18 | mov $16, $0 # set up return value | ||
19 | mov $26, $23 # set up return address | ||
20 | unop | ||
21 | br __stxcpy # do the copy | ||
22 | |||
23 | .end strcpy | ||
diff --git a/arch/alpha/lib/strlen.S b/arch/alpha/lib/strlen.S new file mode 100644 index 000000000000..fe63353de152 --- /dev/null +++ b/arch/alpha/lib/strlen.S | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * strlen.S (c) 1995 David Mosberger (davidm@cs.arizona.edu) | ||
3 | * | ||
4 | * Finds length of a 0-terminated string. Optimized for the | ||
5 | * Alpha architecture: | ||
6 | * | ||
7 | * - memory accessed as aligned quadwords only | ||
8 | * - uses bcmpge to compare 8 bytes in parallel | ||
9 | * - does binary search to find 0 byte in last | ||
10 | * quadword (HAKMEM needed 12 instructions to | ||
11 | * do this instead of the 9 instructions that | ||
12 | * binary search needs). | ||
13 | */ | ||
14 | |||
15 | .set noreorder | ||
16 | .set noat | ||
17 | |||
18 | .align 3 | ||
19 | |||
20 | .globl strlen | ||
21 | .ent strlen | ||
22 | |||
23 | strlen: | ||
24 | ldq_u $1, 0($16) # load first quadword ($16 may be misaligned) | ||
25 | lda $2, -1($31) | ||
26 | insqh $2, $16, $2 | ||
27 | andnot $16, 7, $0 | ||
28 | or $2, $1, $1 | ||
29 | cmpbge $31, $1, $2 # $2 <- bitmask: bit i == 1 <==> i-th byte == 0 | ||
30 | bne $2, found | ||
31 | |||
32 | loop: ldq $1, 8($0) | ||
33 | addq $0, 8, $0 # addr += 8 | ||
34 | nop # helps dual issue last two insns | ||
35 | cmpbge $31, $1, $2 | ||
36 | beq $2, loop | ||
37 | |||
38 | found: blbs $2, done # make aligned case fast | ||
39 | negq $2, $3 | ||
40 | and $2, $3, $2 | ||
41 | |||
42 | and $2, 0x0f, $1 | ||
43 | addq $0, 4, $3 | ||
44 | cmoveq $1, $3, $0 | ||
45 | |||
46 | and $2, 0x33, $1 | ||
47 | addq $0, 2, $3 | ||
48 | cmoveq $1, $3, $0 | ||
49 | |||
50 | and $2, 0x55, $1 | ||
51 | addq $0, 1, $3 | ||
52 | cmoveq $1, $3, $0 | ||
53 | |||
54 | done: subq $0, $16, $0 | ||
55 | ret $31, ($26) | ||
56 | |||
57 | .end strlen | ||
diff --git a/arch/alpha/lib/strlen_user.S b/arch/alpha/lib/strlen_user.S new file mode 100644 index 000000000000..508a18e96479 --- /dev/null +++ b/arch/alpha/lib/strlen_user.S | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strlen_user.S | ||
3 | * | ||
4 | * Return the length of the string including the NUL terminator | ||
5 | * (strlen+1) or zero if an error occurred. | ||
6 | * | ||
7 | * In places where it is critical to limit the processing time, | ||
8 | * and the data is not trusted, strnlen_user() should be used. | ||
9 | * It will return a value greater than its second argument if | ||
10 | * that limit would be exceeded. This implementation is allowed | ||
11 | * to access memory beyond the limit, but will not cross a page | ||
12 | * boundary when doing so. | ||
13 | */ | ||
14 | |||
15 | #include <asm/regdef.h> | ||
16 | |||
17 | |||
18 | /* Allow an exception for an insn; exit if we get one. */ | ||
19 | #define EX(x,y...) \ | ||
20 | 99: x,##y; \ | ||
21 | .section __ex_table,"a"; \ | ||
22 | .long 99b - .; \ | ||
23 | lda v0, $exception-99b(zero); \ | ||
24 | .previous | ||
25 | |||
26 | |||
27 | .set noreorder | ||
28 | .set noat | ||
29 | .text | ||
30 | |||
31 | .globl __strlen_user | ||
32 | .ent __strlen_user | ||
33 | .frame sp, 0, ra | ||
34 | |||
35 | .align 3 | ||
36 | __strlen_user: | ||
37 | ldah a1, 32767(zero) # do not use plain strlen_user() for strings | ||
38 | # that might be almost 2 GB long; you should | ||
39 | # be using strnlen_user() instead | ||
40 | |||
41 | .globl __strnlen_user | ||
42 | |||
43 | .align 3 | ||
44 | __strnlen_user: | ||
45 | .prologue 0 | ||
46 | |||
47 | EX( ldq_u t0, 0(a0) ) # load first quadword (a0 may be misaligned) | ||
48 | lda t1, -1(zero) | ||
49 | insqh t1, a0, t1 | ||
50 | andnot a0, 7, v0 | ||
51 | or t1, t0, t0 | ||
52 | subq a0, 1, a0 # get our +1 for the return | ||
53 | cmpbge zero, t0, t1 # t1 <- bitmask: bit i == 1 <==> i-th byte == 0 | ||
54 | subq a1, 7, t2 | ||
55 | subq a0, v0, t0 | ||
56 | bne t1, $found | ||
57 | |||
58 | addq t2, t0, t2 | ||
59 | addq a1, 1, a1 | ||
60 | |||
61 | .align 3 | ||
62 | $loop: ble t2, $limit | ||
63 | EX( ldq t0, 8(v0) ) | ||
64 | subq t2, 8, t2 | ||
65 | addq v0, 8, v0 # addr += 8 | ||
66 | cmpbge zero, t0, t1 | ||
67 | beq t1, $loop | ||
68 | |||
69 | $found: negq t1, t2 # clear all but least set bit | ||
70 | and t1, t2, t1 | ||
71 | |||
72 | and t1, 0xf0, t2 # binary search for that set bit | ||
73 | and t1, 0xcc, t3 | ||
74 | and t1, 0xaa, t4 | ||
75 | cmovne t2, 4, t2 | ||
76 | cmovne t3, 2, t3 | ||
77 | cmovne t4, 1, t4 | ||
78 | addq t2, t3, t2 | ||
79 | addq v0, t4, v0 | ||
80 | addq v0, t2, v0 | ||
81 | nop # dual issue next two on ev4 and ev5 | ||
82 | subq v0, a0, v0 | ||
83 | $exception: | ||
84 | ret | ||
85 | |||
86 | .align 3 # currently redundant | ||
87 | $limit: | ||
88 | subq a1, t2, v0 | ||
89 | ret | ||
90 | |||
91 | .end __strlen_user | ||
diff --git a/arch/alpha/lib/strncat.S b/arch/alpha/lib/strncat.S new file mode 100644 index 000000000000..a8278163c972 --- /dev/null +++ b/arch/alpha/lib/strncat.S | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strncat.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Append no more than COUNT characters from the null-terminated string SRC | ||
6 | * to the null-terminated string DST. Always null-terminate the new DST. | ||
7 | * | ||
8 | * This differs slightly from the semantics in libc in that we never write | ||
9 | * past count, whereas libc may write to count+1. This follows the generic | ||
10 | * implementation in lib/string.c and is, IMHO, more sensible. | ||
11 | */ | ||
12 | |||
13 | .text | ||
14 | |||
15 | .align 3 | ||
16 | .globl strncat | ||
17 | .ent strncat | ||
18 | strncat: | ||
19 | .frame $30, 0, $26 | ||
20 | .prologue 0 | ||
21 | |||
22 | mov $16, $0 # set up return value | ||
23 | beq $18, $zerocount | ||
24 | |||
25 | /* Find the end of the string. */ | ||
26 | |||
27 | ldq_u $1, 0($16) # load first quadword ($16 may be misaligned) | ||
28 | lda $2, -1($31) | ||
29 | insqh $2, $16, $2 | ||
30 | andnot $16, 7, $16 | ||
31 | or $2, $1, $1 | ||
32 | cmpbge $31, $1, $2 # bits set iff byte == 0 | ||
33 | bne $2, $found | ||
34 | |||
35 | $loop: ldq $1, 8($16) | ||
36 | addq $16, 8, $16 | ||
37 | cmpbge $31, $1, $2 | ||
38 | beq $2, $loop | ||
39 | |||
40 | $found: negq $2, $3 # clear all but least set bit | ||
41 | and $2, $3, $2 | ||
42 | |||
43 | and $2, 0xf0, $3 # binary search for that set bit | ||
44 | and $2, 0xcc, $4 | ||
45 | and $2, 0xaa, $5 | ||
46 | cmovne $3, 4, $3 | ||
47 | cmovne $4, 2, $4 | ||
48 | cmovne $5, 1, $5 | ||
49 | addq $3, $4, $3 | ||
50 | addq $16, $5, $16 | ||
51 | addq $16, $3, $16 | ||
52 | |||
53 | /* Now do the append. */ | ||
54 | |||
55 | bsr $23, __stxncpy | ||
56 | |||
57 | /* Worry about the null termination. */ | ||
58 | |||
59 | zapnot $1, $27, $2 # was last byte a null? | ||
60 | bne $2, 0f | ||
61 | ret | ||
62 | |||
63 | 0: cmplt $27, $24, $2 # did we fill the buffer completely? | ||
64 | or $2, $18, $2 | ||
65 | bne $2, 2f | ||
66 | |||
67 | and $24, 0x80, $2 # no zero next byte | ||
68 | bne $2, 1f | ||
69 | |||
70 | /* Here there are bytes left in the current word. Clear one. */ | ||
71 | addq $24, $24, $24 # end-of-count bit <<= 1 | ||
72 | 2: zap $1, $24, $1 | ||
73 | stq_u $1, 0($16) | ||
74 | ret | ||
75 | |||
76 | 1: /* Here we must read the next DST word and clear the first byte. */ | ||
77 | ldq_u $1, 8($16) | ||
78 | zap $1, 1, $1 | ||
79 | stq_u $1, 8($16) | ||
80 | |||
81 | $zerocount: | ||
82 | ret | ||
83 | |||
84 | .end strncat | ||
diff --git a/arch/alpha/lib/strncpy.S b/arch/alpha/lib/strncpy.S new file mode 100644 index 000000000000..338551c7113c --- /dev/null +++ b/arch/alpha/lib/strncpy.S | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strncpy.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Copy no more than COUNT bytes of the null-terminated string from | ||
6 | * SRC to DST. If SRC does not cover all of COUNT, the balance is | ||
7 | * zeroed. | ||
8 | * | ||
9 | * Or, rather, if the kernel cared about that weird ANSI quirk. This | ||
10 | * version has cropped that bit o' nastiness as well as assuming that | ||
11 | * __stxncpy is in range of a branch. | ||
12 | */ | ||
13 | |||
14 | .set noat | ||
15 | .set noreorder | ||
16 | |||
17 | .text | ||
18 | |||
19 | .align 4 | ||
20 | .globl strncpy | ||
21 | .ent strncpy | ||
22 | strncpy: | ||
23 | .frame $30, 0, $26 | ||
24 | .prologue 0 | ||
25 | |||
26 | mov $16, $0 # set return value now | ||
27 | beq $18, $zerolen | ||
28 | unop | ||
29 | bsr $23, __stxncpy # do the work of the copy | ||
30 | |||
31 | unop | ||
32 | bne $18, $multiword # do we have full words left? | ||
33 | subq $24, 1, $3 # nope | ||
34 | subq $27, 1, $4 | ||
35 | |||
36 | or $3, $24, $3 # clear the bits between the last | ||
37 | or $4, $27, $4 # written byte and the last byte in COUNT | ||
38 | andnot $4, $3, $4 | ||
39 | zap $1, $4, $1 | ||
40 | |||
41 | stq_u $1, 0($16) | ||
42 | ret | ||
43 | |||
44 | .align 4 | ||
45 | $multiword: | ||
46 | subq $24, 1, $2 # clear the final bits in the prev word | ||
47 | or $2, $24, $2 | ||
48 | zapnot $1, $2, $1 | ||
49 | subq $18, 1, $18 | ||
50 | |||
51 | stq_u $1, 0($16) | ||
52 | addq $16, 8, $16 | ||
53 | unop | ||
54 | beq $18, 1f | ||
55 | |||
56 | nop | ||
57 | unop | ||
58 | nop | ||
59 | blbc $18, 0f | ||
60 | |||
61 | stq_u $31, 0($16) # zero one word | ||
62 | subq $18, 1, $18 | ||
63 | addq $16, 8, $16 | ||
64 | beq $18, 1f | ||
65 | |||
66 | 0: stq_u $31, 0($16) # zero two words | ||
67 | subq $18, 2, $18 | ||
68 | stq_u $31, 8($16) | ||
69 | addq $16, 16, $16 | ||
70 | bne $18, 0b | ||
71 | |||
72 | 1: ldq_u $1, 0($16) # clear the leading bits in the final word | ||
73 | subq $27, 1, $2 | ||
74 | or $2, $27, $2 | ||
75 | |||
76 | zap $1, $2, $1 | ||
77 | stq_u $1, 0($16) | ||
78 | $zerolen: | ||
79 | ret | ||
80 | |||
81 | .end strncpy | ||
diff --git a/arch/alpha/lib/strncpy_from_user.S b/arch/alpha/lib/strncpy_from_user.S new file mode 100644 index 000000000000..73ee21160ff7 --- /dev/null +++ b/arch/alpha/lib/strncpy_from_user.S | |||
@@ -0,0 +1,339 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strncpy_from_user.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Just like strncpy except in the return value: | ||
6 | * | ||
7 | * -EFAULT if an exception occurs before the terminator is copied. | ||
8 | * N if the buffer filled. | ||
9 | * | ||
10 | * Otherwise the length of the string is returned. | ||
11 | */ | ||
12 | |||
13 | |||
14 | #include <asm/errno.h> | ||
15 | #include <asm/regdef.h> | ||
16 | |||
17 | |||
18 | /* Allow an exception for an insn; exit if we get one. */ | ||
19 | #define EX(x,y...) \ | ||
20 | 99: x,##y; \ | ||
21 | .section __ex_table,"a"; \ | ||
22 | .long 99b - .; \ | ||
23 | lda $31, $exception-99b($0); \ | ||
24 | .previous | ||
25 | |||
26 | |||
27 | .set noat | ||
28 | .set noreorder | ||
29 | .text | ||
30 | |||
31 | .globl __strncpy_from_user | ||
32 | .ent __strncpy_from_user | ||
33 | .frame $30, 0, $26 | ||
34 | .prologue 0 | ||
35 | |||
36 | .align 3 | ||
37 | $aligned: | ||
38 | /* On entry to this basic block: | ||
39 | t0 == the first destination word for masking back in | ||
40 | t1 == the first source word. */ | ||
41 | |||
42 | /* Create the 1st output word and detect 0's in the 1st input word. */ | ||
43 | lda t2, -1 # e1 : build a mask against false zero | ||
44 | mskqh t2, a1, t2 # e0 : detection in the src word | ||
45 | mskqh t1, a1, t3 # e0 : | ||
46 | ornot t1, t2, t2 # .. e1 : | ||
47 | mskql t0, a1, t0 # e0 : assemble the first output word | ||
48 | cmpbge zero, t2, t8 # .. e1 : bits set iff null found | ||
49 | or t0, t3, t0 # e0 : | ||
50 | beq a2, $a_eoc # .. e1 : | ||
51 | bne t8, $a_eos # .. e1 : | ||
52 | |||
53 | /* On entry to this basic block: | ||
54 | t0 == a source word not containing a null. */ | ||
55 | |||
56 | $a_loop: | ||
57 | stq_u t0, 0(a0) # e0 : | ||
58 | addq a0, 8, a0 # .. e1 : | ||
59 | EX( ldq_u t0, 0(a1) ) # e0 : | ||
60 | addq a1, 8, a1 # .. e1 : | ||
61 | subq a2, 1, a2 # e0 : | ||
62 | cmpbge zero, t0, t8 # .. e1 (stall) | ||
63 | beq a2, $a_eoc # e1 : | ||
64 | beq t8, $a_loop # e1 : | ||
65 | |||
66 | /* Take care of the final (partial) word store. At this point | ||
67 | the end-of-count bit is set in t8 iff it applies. | ||
68 | |||
69 | On entry to this basic block we have: | ||
70 | t0 == the source word containing the null | ||
71 | t8 == the cmpbge mask that found it. */ | ||
72 | |||
73 | $a_eos: | ||
74 | negq t8, t12 # e0 : find low bit set | ||
75 | and t8, t12, t12 # e1 (stall) | ||
76 | |||
77 | /* For the sake of the cache, don't read a destination word | ||
78 | if we're not going to need it. */ | ||
79 | and t12, 0x80, t6 # e0 : | ||
80 | bne t6, 1f # .. e1 (zdb) | ||
81 | |||
82 | /* We're doing a partial word store and so need to combine | ||
83 | our source and original destination words. */ | ||
84 | ldq_u t1, 0(a0) # e0 : | ||
85 | subq t12, 1, t6 # .. e1 : | ||
86 | or t12, t6, t8 # e0 : | ||
87 | unop # | ||
88 | zapnot t0, t8, t0 # e0 : clear src bytes > null | ||
89 | zap t1, t8, t1 # .. e1 : clear dst bytes <= null | ||
90 | or t0, t1, t0 # e1 : | ||
91 | |||
92 | 1: stq_u t0, 0(a0) | ||
93 | br $finish_up | ||
94 | |||
95 | /* Add the end-of-count bit to the eos detection bitmask. */ | ||
96 | $a_eoc: | ||
97 | or t10, t8, t8 | ||
98 | br $a_eos | ||
99 | |||
100 | /*** The Function Entry Point ***/ | ||
101 | .align 3 | ||
102 | __strncpy_from_user: | ||
103 | mov a0, v0 # save the string start | ||
104 | beq a2, $zerolength | ||
105 | |||
106 | /* Are source and destination co-aligned? */ | ||
107 | xor a0, a1, t1 # e0 : | ||
108 | and a0, 7, t0 # .. e1 : find dest misalignment | ||
109 | and t1, 7, t1 # e0 : | ||
110 | addq a2, t0, a2 # .. e1 : bias count by dest misalignment | ||
111 | subq a2, 1, a2 # e0 : | ||
112 | and a2, 7, t2 # e1 : | ||
113 | srl a2, 3, a2 # e0 : a2 = loop counter = (count - 1)/8 | ||
114 | addq zero, 1, t10 # .. e1 : | ||
115 | sll t10, t2, t10 # e0 : t10 = bitmask of last count byte | ||
116 | bne t1, $unaligned # .. e1 : | ||
117 | |||
118 | /* We are co-aligned; take care of a partial first word. */ | ||
119 | |||
120 | EX( ldq_u t1, 0(a1) ) # e0 : load first src word | ||
121 | addq a1, 8, a1 # .. e1 : | ||
122 | |||
123 | beq t0, $aligned # avoid loading dest word if not needed | ||
124 | ldq_u t0, 0(a0) # e0 : | ||
125 | br $aligned # .. e1 : | ||
126 | |||
127 | |||
128 | /* The source and destination are not co-aligned. Align the destination | ||
129 | and cope. We have to be very careful about not reading too much and | ||
130 | causing a SEGV. */ | ||
131 | |||
132 | .align 3 | ||
133 | $u_head: | ||
134 | /* We know just enough now to be able to assemble the first | ||
135 | full source word. We can still find a zero at the end of it | ||
136 | that prevents us from outputting the whole thing. | ||
137 | |||
138 | On entry to this basic block: | ||
139 | t0 == the first dest word, unmasked | ||
140 | t1 == the shifted low bits of the first source word | ||
141 | t6 == bytemask that is -1 in dest word bytes */ | ||
142 | |||
143 | EX( ldq_u t2, 8(a1) ) # e0 : load second src word | ||
144 | addq a1, 8, a1 # .. e1 : | ||
145 | mskql t0, a0, t0 # e0 : mask trailing garbage in dst | ||
146 | extqh t2, a1, t4 # e0 : | ||
147 | or t1, t4, t1 # e1 : first aligned src word complete | ||
148 | mskqh t1, a0, t1 # e0 : mask leading garbage in src | ||
149 | or t0, t1, t0 # e0 : first output word complete | ||
150 | or t0, t6, t6 # e1 : mask original data for zero test | ||
151 | cmpbge zero, t6, t8 # e0 : | ||
152 | beq a2, $u_eocfin # .. e1 : | ||
153 | bne t8, $u_final # e1 : | ||
154 | |||
155 | lda t6, -1 # e1 : mask out the bits we have | ||
156 | mskql t6, a1, t6 # e0 : already seen | ||
157 | stq_u t0, 0(a0) # e0 : store first output word | ||
158 | or t6, t2, t2 # .. e1 : | ||
159 | cmpbge zero, t2, t8 # e0 : find nulls in second partial | ||
160 | addq a0, 8, a0 # .. e1 : | ||
161 | subq a2, 1, a2 # e0 : | ||
162 | bne t8, $u_late_head_exit # .. e1 : | ||
163 | |||
164 | /* Finally, we've got all the stupid leading edge cases taken care | ||
165 | of and we can set up to enter the main loop. */ | ||
166 | |||
167 | extql t2, a1, t1 # e0 : position hi-bits of lo word | ||
168 | EX( ldq_u t2, 8(a1) ) # .. e1 : read next high-order source word | ||
169 | addq a1, 8, a1 # e0 : | ||
170 | cmpbge zero, t2, t8 # e1 (stall) | ||
171 | beq a2, $u_eoc # e1 : | ||
172 | bne t8, $u_eos # e1 : | ||
173 | |||
174 | /* Unaligned copy main loop. In order to avoid reading too much, | ||
175 | the loop is structured to detect zeros in aligned source words. | ||
176 | This has, unfortunately, effectively pulled half of a loop | ||
177 | iteration out into the head and half into the tail, but it does | ||
178 | prevent nastiness from accumulating in the very thing we want | ||
179 | to run as fast as possible. | ||
180 | |||
181 | On entry to this basic block: | ||
182 | t1 == the shifted high-order bits from the previous source word | ||
183 | t2 == the unshifted current source word | ||
184 | |||
185 | We further know that t2 does not contain a null terminator. */ | ||
186 | |||
187 | .align 3 | ||
188 | $u_loop: | ||
189 | extqh t2, a1, t0 # e0 : extract high bits for current word | ||
190 | addq a1, 8, a1 # .. e1 : | ||
191 | extql t2, a1, t3 # e0 : extract low bits for next time | ||
192 | addq a0, 8, a0 # .. e1 : | ||
193 | or t0, t1, t0 # e0 : current dst word now complete | ||
194 | EX( ldq_u t2, 0(a1) ) # .. e1 : load high word for next time | ||
195 | stq_u t0, -8(a0) # e0 : save the current word | ||
196 | mov t3, t1 # .. e1 : | ||
197 | subq a2, 1, a2 # e0 : | ||
198 | cmpbge zero, t2, t8 # .. e1 : test new word for eos | ||
199 | beq a2, $u_eoc # e1 : | ||
200 | beq t8, $u_loop # e1 : | ||
201 | |||
202 | /* We've found a zero somewhere in the source word we just read. | ||
203 | If it resides in the lower half, we have one (probably partial) | ||
204 | word to write out, and if it resides in the upper half, we | ||
205 | have one full and one partial word left to write out. | ||
206 | |||
207 | On entry to this basic block: | ||
208 | t1 == the shifted high-order bits from the previous source word | ||
209 | t2 == the unshifted current source word. */ | ||
210 | $u_eos: | ||
211 | extqh t2, a1, t0 # e0 : | ||
212 | or t0, t1, t0 # e1 : first (partial) source word complete | ||
213 | |||
214 | cmpbge zero, t0, t8 # e0 : is the null in this first bit? | ||
215 | bne t8, $u_final # .. e1 (zdb) | ||
216 | |||
217 | stq_u t0, 0(a0) # e0 : the null was in the high-order bits | ||
218 | addq a0, 8, a0 # .. e1 : | ||
219 | subq a2, 1, a2 # e1 : | ||
220 | |||
221 | $u_late_head_exit: | ||
222 | extql t2, a1, t0 # .. e0 : | ||
223 | cmpbge zero, t0, t8 # e0 : | ||
224 | or t8, t10, t6 # e1 : | ||
225 | cmoveq a2, t6, t8 # e0 : | ||
226 | nop # .. e1 : | ||
227 | |||
228 | /* Take care of a final (probably partial) result word. | ||
229 | On entry to this basic block: | ||
230 | t0 == assembled source word | ||
231 | t8 == cmpbge mask that found the null. */ | ||
232 | $u_final: | ||
233 | negq t8, t6 # e0 : isolate low bit set | ||
234 | and t6, t8, t12 # e1 : | ||
235 | |||
236 | and t12, 0x80, t6 # e0 : avoid dest word load if we can | ||
237 | bne t6, 1f # .. e1 (zdb) | ||
238 | |||
239 | ldq_u t1, 0(a0) # e0 : | ||
240 | subq t12, 1, t6 # .. e1 : | ||
241 | or t6, t12, t8 # e0 : | ||
242 | zapnot t0, t8, t0 # .. e1 : kill source bytes > null | ||
243 | zap t1, t8, t1 # e0 : kill dest bytes <= null | ||
244 | or t0, t1, t0 # e1 : | ||
245 | |||
246 | 1: stq_u t0, 0(a0) # e0 : | ||
247 | br $finish_up | ||
248 | |||
249 | $u_eoc: # end-of-count | ||
250 | extqh t2, a1, t0 | ||
251 | or t0, t1, t0 | ||
252 | cmpbge zero, t0, t8 | ||
253 | |||
254 | $u_eocfin: # end-of-count, final word | ||
255 | or t10, t8, t8 | ||
256 | br $u_final | ||
257 | |||
258 | /* Unaligned copy entry point. */ | ||
259 | .align 3 | ||
260 | $unaligned: | ||
261 | |||
262 | EX( ldq_u t1, 0(a1) ) # e0 : load first source word | ||
263 | |||
264 | and a0, 7, t4 # .. e1 : find dest misalignment | ||
265 | and a1, 7, t5 # e0 : find src misalignment | ||
266 | |||
267 | /* Conditionally load the first destination word and a bytemask | ||
268 | with 0xff indicating that the destination byte is sacrosanct. */ | ||
269 | |||
270 | mov zero, t0 # .. e1 : | ||
271 | mov zero, t6 # e0 : | ||
272 | beq t4, 1f # .. e1 : | ||
273 | ldq_u t0, 0(a0) # e0 : | ||
274 | lda t6, -1 # .. e1 : | ||
275 | mskql t6, a0, t6 # e0 : | ||
276 | 1: | ||
277 | subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr | ||
278 | |||
279 | /* If source misalignment is larger than dest misalignment, we need | ||
280 | extra startup checks to avoid SEGV. */ | ||
281 | |||
282 | cmplt t4, t5, t12 # e1 : | ||
283 | extql t1, a1, t1 # .. e0 : shift src into place | ||
284 | lda t2, -1 # e0 : for creating masks later | ||
285 | beq t12, $u_head # e1 : | ||
286 | |||
287 | mskqh t2, t5, t2 # e0 : begin src byte validity mask | ||
288 | cmpbge zero, t1, t8 # .. e1 : is there a zero? | ||
289 | extql t2, a1, t2 # e0 : | ||
290 | or t8, t10, t5 # .. e1 : test for end-of-count too | ||
291 | cmpbge zero, t2, t3 # e0 : | ||
292 | cmoveq a2, t5, t8 # .. e1 : | ||
293 | andnot t8, t3, t8 # e0 : | ||
294 | beq t8, $u_head # .. e1 (zdb) | ||
295 | |||
296 | /* At this point we've found a zero in the first partial word of | ||
297 | the source. We need to isolate the valid source data and mask | ||
298 | it into the original destination data. (Incidentally, we know | ||
299 | that we'll need at least one byte of that original dest word.) */ | ||
300 | |||
301 | ldq_u t0, 0(a0) # e0 : | ||
302 | negq t8, t6 # .. e1 : build bitmask of bytes <= zero | ||
303 | mskqh t1, t4, t1 # e0 : | ||
304 | and t6, t8, t12 # .. e1 : | ||
305 | subq t12, 1, t6 # e0 : | ||
306 | or t6, t12, t8 # e1 : | ||
307 | |||
308 | zapnot t2, t8, t2 # e0 : prepare source word; mirror changes | ||
309 | zapnot t1, t8, t1 # .. e1 : to source validity mask | ||
310 | |||
311 | andnot t0, t2, t0 # e0 : zero place for source to reside | ||
312 | or t0, t1, t0 # e1 : and put it there | ||
313 | stq_u t0, 0(a0) # e0 : | ||
314 | |||
315 | $finish_up: | ||
316 | zapnot t0, t12, t4 # was last byte written null? | ||
317 | cmovne t4, 1, t4 | ||
318 | |||
319 | and t12, 0xf0, t3 # binary search for the address of the | ||
320 | and t12, 0xcc, t2 # last byte written | ||
321 | and t12, 0xaa, t1 | ||
322 | bic a0, 7, t0 | ||
323 | cmovne t3, 4, t3 | ||
324 | cmovne t2, 2, t2 | ||
325 | cmovne t1, 1, t1 | ||
326 | addq t0, t3, t0 | ||
327 | addq t1, t2, t1 | ||
328 | addq t0, t1, t0 | ||
329 | addq t0, t4, t0 # add one if we filled the buffer | ||
330 | |||
331 | subq t0, v0, v0 # find string length | ||
332 | ret | ||
333 | |||
334 | $zerolength: | ||
335 | clr v0 | ||
336 | $exception: | ||
337 | ret | ||
338 | |||
339 | .end __strncpy_from_user | ||
diff --git a/arch/alpha/lib/strrchr.S b/arch/alpha/lib/strrchr.S new file mode 100644 index 000000000000..82cfd0ac907b --- /dev/null +++ b/arch/alpha/lib/strrchr.S | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/strrchr.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Return the address of the last occurrence of a given character | ||
6 | * within a null-terminated string, or null if it is not found. | ||
7 | */ | ||
8 | |||
9 | #include <asm/regdef.h> | ||
10 | |||
11 | .set noreorder | ||
12 | .set noat | ||
13 | |||
14 | .align 3 | ||
15 | .ent strrchr | ||
16 | .globl strrchr | ||
17 | strrchr: | ||
18 | .frame sp, 0, ra | ||
19 | .prologue 0 | ||
20 | |||
21 | zapnot a1, 1, a1 # e0 : zero extend our test character | ||
22 | mov zero, t6 # .. e1 : t6 is last match aligned addr | ||
23 | sll a1, 8, t5 # e0 : replicate our test character | ||
24 | mov zero, t8 # .. e1 : t8 is last match byte compare mask | ||
25 | or t5, a1, a1 # e0 : | ||
26 | ldq_u t0, 0(a0) # .. e1 : load first quadword | ||
27 | sll a1, 16, t5 # e0 : | ||
28 | andnot a0, 7, v0 # .. e1 : align source addr | ||
29 | or t5, a1, a1 # e0 : | ||
30 | lda t4, -1 # .. e1 : build garbage mask | ||
31 | sll a1, 32, t5 # e0 : | ||
32 | cmpbge zero, t0, t1 # .. e1 : bits set iff byte == zero | ||
33 | mskqh t4, a0, t4 # e0 : | ||
34 | or t5, a1, a1 # .. e1 : character replication complete | ||
35 | xor t0, a1, t2 # e0 : make bytes == c zero | ||
36 | cmpbge zero, t4, t4 # .. e1 : bits set iff byte is garbage | ||
37 | cmpbge zero, t2, t3 # e0 : bits set iff byte == c | ||
38 | andnot t1, t4, t1 # .. e1 : clear garbage from null test | ||
39 | andnot t3, t4, t3 # e0 : clear garbage from char test | ||
40 | bne t1, $eos # .. e1 : did we already hit the terminator? | ||
41 | |||
42 | /* Character search main loop */ | ||
43 | $loop: | ||
44 | ldq t0, 8(v0) # e0 : load next quadword | ||
45 | cmovne t3, v0, t6 # .. e1 : save previous comparisons match | ||
46 | cmovne t3, t3, t8 # e0 : | ||
47 | addq v0, 8, v0 # .. e1 : | ||
48 | xor t0, a1, t2 # e0 : | ||
49 | cmpbge zero, t0, t1 # .. e1 : bits set iff byte == zero | ||
50 | cmpbge zero, t2, t3 # e0 : bits set iff byte == c | ||
51 | beq t1, $loop # .. e1 : if we havnt seen a null, loop | ||
52 | |||
53 | /* Mask out character matches after terminator */ | ||
54 | $eos: | ||
55 | negq t1, t4 # e0 : isolate first null byte match | ||
56 | and t1, t4, t4 # e1 : | ||
57 | subq t4, 1, t5 # e0 : build a mask of the bytes upto... | ||
58 | or t4, t5, t4 # e1 : ... and including the null | ||
59 | |||
60 | and t3, t4, t3 # e0 : mask out char matches after null | ||
61 | cmovne t3, t3, t8 # .. e1 : save it, if match found | ||
62 | cmovne t3, v0, t6 # e0 : | ||
63 | |||
64 | /* Locate the address of the last matched character */ | ||
65 | |||
66 | /* Retain the early exit for the ev4 -- the ev5 mispredict penalty | ||
67 | is 5 cycles -- the same as just falling through. */ | ||
68 | beq t8, $retnull # .. e1 : | ||
69 | |||
70 | and t8, 0xf0, t2 # e0 : binary search for the high bit set | ||
71 | cmovne t2, t2, t8 # .. e1 (zdb) | ||
72 | cmovne t2, 4, t2 # e0 : | ||
73 | and t8, 0xcc, t1 # .. e1 : | ||
74 | cmovne t1, t1, t8 # e0 : | ||
75 | cmovne t1, 2, t1 # .. e1 : | ||
76 | and t8, 0xaa, t0 # e0 : | ||
77 | cmovne t0, 1, t0 # .. e1 (zdb) | ||
78 | addq t2, t1, t1 # e0 : | ||
79 | addq t6, t0, v0 # .. e1 : add our aligned base ptr to the mix | ||
80 | addq v0, t1, v0 # e0 : | ||
81 | ret # .. e1 : | ||
82 | |||
83 | $retnull: | ||
84 | mov zero, v0 # e0 : | ||
85 | ret # .. e1 : | ||
86 | |||
87 | .end strrchr | ||
diff --git a/arch/alpha/lib/stxcpy.S b/arch/alpha/lib/stxcpy.S new file mode 100644 index 000000000000..2a8d51bfc05d --- /dev/null +++ b/arch/alpha/lib/stxcpy.S | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/stxcpy.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Copy a null-terminated string from SRC to DST. | ||
6 | * | ||
7 | * This is an internal routine used by strcpy, stpcpy, and strcat. | ||
8 | * As such, it uses special linkage conventions to make implementation | ||
9 | * of these public functions more efficient. | ||
10 | * | ||
11 | * On input: | ||
12 | * t9 = return address | ||
13 | * a0 = DST | ||
14 | * a1 = SRC | ||
15 | * | ||
16 | * On output: | ||
17 | * t12 = bitmask (with one bit set) indicating the last byte written | ||
18 | * a0 = unaligned address of the last *word* written | ||
19 | * | ||
20 | * Furthermore, v0, a3-a5, t11, and t12 are untouched. | ||
21 | */ | ||
22 | |||
23 | #include <asm/regdef.h> | ||
24 | |||
25 | .set noat | ||
26 | .set noreorder | ||
27 | |||
28 | .text | ||
29 | |||
30 | /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that | ||
31 | doesn't like putting the entry point for a procedure somewhere in the | ||
32 | middle of the procedure descriptor. Work around this by putting the | ||
33 | aligned copy in its own procedure descriptor */ | ||
34 | |||
35 | .ent stxcpy_aligned | ||
36 | .align 3 | ||
37 | stxcpy_aligned: | ||
38 | .frame sp, 0, t9 | ||
39 | .prologue 0 | ||
40 | |||
41 | /* On entry to this basic block: | ||
42 | t0 == the first destination word for masking back in | ||
43 | t1 == the first source word. */ | ||
44 | |||
45 | /* Create the 1st output word and detect 0's in the 1st input word. */ | ||
46 | lda t2, -1 # e1 : build a mask against false zero | ||
47 | mskqh t2, a1, t2 # e0 : detection in the src word | ||
48 | mskqh t1, a1, t3 # e0 : | ||
49 | ornot t1, t2, t2 # .. e1 : | ||
50 | mskql t0, a1, t0 # e0 : assemble the first output word | ||
51 | cmpbge zero, t2, t8 # .. e1 : bits set iff null found | ||
52 | or t0, t3, t1 # e0 : | ||
53 | bne t8, $a_eos # .. e1 : | ||
54 | |||
55 | /* On entry to this basic block: | ||
56 | t0 == the first destination word for masking back in | ||
57 | t1 == a source word not containing a null. */ | ||
58 | |||
59 | $a_loop: | ||
60 | stq_u t1, 0(a0) # e0 : | ||
61 | addq a0, 8, a0 # .. e1 : | ||
62 | ldq_u t1, 0(a1) # e0 : | ||
63 | addq a1, 8, a1 # .. e1 : | ||
64 | cmpbge zero, t1, t8 # e0 (stall) | ||
65 | beq t8, $a_loop # .. e1 (zdb) | ||
66 | |||
67 | /* Take care of the final (partial) word store. | ||
68 | On entry to this basic block we have: | ||
69 | t1 == the source word containing the null | ||
70 | t8 == the cmpbge mask that found it. */ | ||
71 | $a_eos: | ||
72 | negq t8, t6 # e0 : find low bit set | ||
73 | and t8, t6, t12 # e1 (stall) | ||
74 | |||
75 | /* For the sake of the cache, don't read a destination word | ||
76 | if we're not going to need it. */ | ||
77 | and t12, 0x80, t6 # e0 : | ||
78 | bne t6, 1f # .. e1 (zdb) | ||
79 | |||
80 | /* We're doing a partial word store and so need to combine | ||
81 | our source and original destination words. */ | ||
82 | ldq_u t0, 0(a0) # e0 : | ||
83 | subq t12, 1, t6 # .. e1 : | ||
84 | zapnot t1, t6, t1 # e0 : clear src bytes >= null | ||
85 | or t12, t6, t8 # .. e1 : | ||
86 | zap t0, t8, t0 # e0 : clear dst bytes <= null | ||
87 | or t0, t1, t1 # e1 : | ||
88 | |||
89 | 1: stq_u t1, 0(a0) # e0 : | ||
90 | ret (t9) # .. e1 : | ||
91 | |||
92 | .end stxcpy_aligned | ||
93 | |||
94 | .align 3 | ||
95 | .ent __stxcpy | ||
96 | .globl __stxcpy | ||
97 | __stxcpy: | ||
98 | .frame sp, 0, t9 | ||
99 | .prologue 0 | ||
100 | |||
101 | /* Are source and destination co-aligned? */ | ||
102 | xor a0, a1, t0 # e0 : | ||
103 | unop # : | ||
104 | and t0, 7, t0 # e0 : | ||
105 | bne t0, $unaligned # .. e1 : | ||
106 | |||
107 | /* We are co-aligned; take care of a partial first word. */ | ||
108 | ldq_u t1, 0(a1) # e0 : load first src word | ||
109 | and a0, 7, t0 # .. e1 : take care not to load a word ... | ||
110 | addq a1, 8, a1 # e0 : | ||
111 | beq t0, stxcpy_aligned # .. e1 : ... if we wont need it | ||
112 | ldq_u t0, 0(a0) # e0 : | ||
113 | br stxcpy_aligned # .. e1 : | ||
114 | |||
115 | |||
116 | /* The source and destination are not co-aligned. Align the destination | ||
117 | and cope. We have to be very careful about not reading too much and | ||
118 | causing a SEGV. */ | ||
119 | |||
120 | .align 3 | ||
121 | $u_head: | ||
122 | /* We know just enough now to be able to assemble the first | ||
123 | full source word. We can still find a zero at the end of it | ||
124 | that prevents us from outputting the whole thing. | ||
125 | |||
126 | On entry to this basic block: | ||
127 | t0 == the first dest word, for masking back in, if needed else 0 | ||
128 | t1 == the low bits of the first source word | ||
129 | t6 == bytemask that is -1 in dest word bytes */ | ||
130 | |||
131 | ldq_u t2, 8(a1) # e0 : | ||
132 | addq a1, 8, a1 # .. e1 : | ||
133 | |||
134 | extql t1, a1, t1 # e0 : | ||
135 | extqh t2, a1, t4 # e0 : | ||
136 | mskql t0, a0, t0 # e0 : | ||
137 | or t1, t4, t1 # .. e1 : | ||
138 | mskqh t1, a0, t1 # e0 : | ||
139 | or t0, t1, t1 # e1 : | ||
140 | |||
141 | or t1, t6, t6 # e0 : | ||
142 | cmpbge zero, t6, t8 # .. e1 : | ||
143 | lda t6, -1 # e0 : for masking just below | ||
144 | bne t8, $u_final # .. e1 : | ||
145 | |||
146 | mskql t6, a1, t6 # e0 : mask out the bits we have | ||
147 | or t6, t2, t2 # e1 : already extracted before | ||
148 | cmpbge zero, t2, t8 # e0 : testing eos | ||
149 | bne t8, $u_late_head_exit # .. e1 (zdb) | ||
150 | |||
151 | /* Finally, we've got all the stupid leading edge cases taken care | ||
152 | of and we can set up to enter the main loop. */ | ||
153 | |||
154 | stq_u t1, 0(a0) # e0 : store first output word | ||
155 | addq a0, 8, a0 # .. e1 : | ||
156 | extql t2, a1, t0 # e0 : position ho-bits of lo word | ||
157 | ldq_u t2, 8(a1) # .. e1 : read next high-order source word | ||
158 | addq a1, 8, a1 # e0 : | ||
159 | cmpbge zero, t2, t8 # .. e1 : | ||
160 | nop # e0 : | ||
161 | bne t8, $u_eos # .. e1 : | ||
162 | |||
163 | /* Unaligned copy main loop. In order to avoid reading too much, | ||
164 | the loop is structured to detect zeros in aligned source words. | ||
165 | This has, unfortunately, effectively pulled half of a loop | ||
166 | iteration out into the head and half into the tail, but it does | ||
167 | prevent nastiness from accumulating in the very thing we want | ||
168 | to run as fast as possible. | ||
169 | |||
170 | On entry to this basic block: | ||
171 | t0 == the shifted high-order bits from the previous source word | ||
172 | t2 == the unshifted current source word | ||
173 | |||
174 | We further know that t2 does not contain a null terminator. */ | ||
175 | |||
176 | .align 3 | ||
177 | $u_loop: | ||
178 | extqh t2, a1, t1 # e0 : extract high bits for current word | ||
179 | addq a1, 8, a1 # .. e1 : | ||
180 | extql t2, a1, t3 # e0 : extract low bits for next time | ||
181 | addq a0, 8, a0 # .. e1 : | ||
182 | or t0, t1, t1 # e0 : current dst word now complete | ||
183 | ldq_u t2, 0(a1) # .. e1 : load high word for next time | ||
184 | stq_u t1, -8(a0) # e0 : save the current word | ||
185 | mov t3, t0 # .. e1 : | ||
186 | cmpbge zero, t2, t8 # e0 : test new word for eos | ||
187 | beq t8, $u_loop # .. e1 : | ||
188 | |||
189 | /* We've found a zero somewhere in the source word we just read. | ||
190 | If it resides in the lower half, we have one (probably partial) | ||
191 | word to write out, and if it resides in the upper half, we | ||
192 | have one full and one partial word left to write out. | ||
193 | |||
194 | On entry to this basic block: | ||
195 | t0 == the shifted high-order bits from the previous source word | ||
196 | t2 == the unshifted current source word. */ | ||
197 | $u_eos: | ||
198 | extqh t2, a1, t1 # e0 : | ||
199 | or t0, t1, t1 # e1 : first (partial) source word complete | ||
200 | |||
201 | cmpbge zero, t1, t8 # e0 : is the null in this first bit? | ||
202 | bne t8, $u_final # .. e1 (zdb) | ||
203 | |||
204 | $u_late_head_exit: | ||
205 | stq_u t1, 0(a0) # e0 : the null was in the high-order bits | ||
206 | addq a0, 8, a0 # .. e1 : | ||
207 | extql t2, a1, t1 # e0 : | ||
208 | cmpbge zero, t1, t8 # .. e1 : | ||
209 | |||
210 | /* Take care of a final (probably partial) result word. | ||
211 | On entry to this basic block: | ||
212 | t1 == assembled source word | ||
213 | t8 == cmpbge mask that found the null. */ | ||
214 | $u_final: | ||
215 | negq t8, t6 # e0 : isolate low bit set | ||
216 | and t6, t8, t12 # e1 : | ||
217 | |||
218 | and t12, 0x80, t6 # e0 : avoid dest word load if we can | ||
219 | bne t6, 1f # .. e1 (zdb) | ||
220 | |||
221 | ldq_u t0, 0(a0) # e0 : | ||
222 | subq t12, 1, t6 # .. e1 : | ||
223 | or t6, t12, t8 # e0 : | ||
224 | zapnot t1, t6, t1 # .. e1 : kill source bytes >= null | ||
225 | zap t0, t8, t0 # e0 : kill dest bytes <= null | ||
226 | or t0, t1, t1 # e1 : | ||
227 | |||
228 | 1: stq_u t1, 0(a0) # e0 : | ||
229 | ret (t9) # .. e1 : | ||
230 | |||
231 | /* Unaligned copy entry point. */ | ||
232 | .align 3 | ||
233 | $unaligned: | ||
234 | |||
235 | ldq_u t1, 0(a1) # e0 : load first source word | ||
236 | |||
237 | and a0, 7, t4 # .. e1 : find dest misalignment | ||
238 | and a1, 7, t5 # e0 : find src misalignment | ||
239 | |||
240 | /* Conditionally load the first destination word and a bytemask | ||
241 | with 0xff indicating that the destination byte is sacrosanct. */ | ||
242 | |||
243 | mov zero, t0 # .. e1 : | ||
244 | mov zero, t6 # e0 : | ||
245 | beq t4, 1f # .. e1 : | ||
246 | ldq_u t0, 0(a0) # e0 : | ||
247 | lda t6, -1 # .. e1 : | ||
248 | mskql t6, a0, t6 # e0 : | ||
249 | 1: | ||
250 | subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr | ||
251 | |||
252 | /* If source misalignment is larger than dest misalignment, we need | ||
253 | extra startup checks to avoid SEGV. */ | ||
254 | |||
255 | cmplt t4, t5, t12 # e0 : | ||
256 | beq t12, $u_head # .. e1 (zdb) | ||
257 | |||
258 | lda t2, -1 # e1 : mask out leading garbage in source | ||
259 | mskqh t2, t5, t2 # e0 : | ||
260 | nop # e0 : | ||
261 | ornot t1, t2, t3 # .. e1 : | ||
262 | cmpbge zero, t3, t8 # e0 : is there a zero? | ||
263 | beq t8, $u_head # .. e1 (zdb) | ||
264 | |||
265 | /* At this point we've found a zero in the first partial word of | ||
266 | the source. We need to isolate the valid source data and mask | ||
267 | it into the original destination data. (Incidentally, we know | ||
268 | that we'll need at least one byte of that original dest word.) */ | ||
269 | |||
270 | ldq_u t0, 0(a0) # e0 : | ||
271 | |||
272 | negq t8, t6 # .. e1 : build bitmask of bytes <= zero | ||
273 | and t6, t8, t12 # e0 : | ||
274 | and a1, 7, t5 # .. e1 : | ||
275 | subq t12, 1, t6 # e0 : | ||
276 | or t6, t12, t8 # e1 : | ||
277 | srl t12, t5, t12 # e0 : adjust final null return value | ||
278 | |||
279 | zapnot t2, t8, t2 # .. e1 : prepare source word; mirror changes | ||
280 | and t1, t2, t1 # e1 : to source validity mask | ||
281 | extql t2, a1, t2 # .. e0 : | ||
282 | extql t1, a1, t1 # e0 : | ||
283 | |||
284 | andnot t0, t2, t0 # .. e1 : zero place for source to reside | ||
285 | or t0, t1, t1 # e1 : and put it there | ||
286 | stq_u t1, 0(a0) # .. e0 : | ||
287 | ret (t9) # e1 : | ||
288 | |||
289 | .end __stxcpy | ||
diff --git a/arch/alpha/lib/stxncpy.S b/arch/alpha/lib/stxncpy.S new file mode 100644 index 000000000000..da1a72740d29 --- /dev/null +++ b/arch/alpha/lib/stxncpy.S | |||
@@ -0,0 +1,345 @@ | |||
1 | /* | ||
2 | * arch/alpha/lib/stxncpy.S | ||
3 | * Contributed by Richard Henderson (rth@tamu.edu) | ||
4 | * | ||
5 | * Copy no more than COUNT bytes of the null-terminated string from | ||
6 | * SRC to DST. | ||
7 | * | ||
8 | * This is an internal routine used by strncpy, stpncpy, and strncat. | ||
9 | * As such, it uses special linkage conventions to make implementation | ||
10 | * of these public functions more efficient. | ||
11 | * | ||
12 | * On input: | ||
13 | * t9 = return address | ||
14 | * a0 = DST | ||
15 | * a1 = SRC | ||
16 | * a2 = COUNT | ||
17 | * | ||
18 | * Furthermore, COUNT may not be zero. | ||
19 | * | ||
20 | * On output: | ||
21 | * t0 = last word written | ||
22 | * t10 = bitmask (with one bit set) indicating the byte position of | ||
23 | * the end of the range specified by COUNT | ||
24 | * t12 = bitmask (with one bit set) indicating the last byte written | ||
25 | * a0 = unaligned address of the last *word* written | ||
26 | * a2 = the number of full words left in COUNT | ||
27 | * | ||
28 | * Furthermore, v0, a3-a5, t11, and $at are untouched. | ||
29 | */ | ||
30 | |||
31 | #include <asm/regdef.h> | ||
32 | |||
33 | .set noat | ||
34 | .set noreorder | ||
35 | |||
36 | .text | ||
37 | |||
38 | /* There is a problem with either gdb (as of 4.16) or gas (as of 2.7) that | ||
39 | doesn't like putting the entry point for a procedure somewhere in the | ||
40 | middle of the procedure descriptor. Work around this by putting the | ||
41 | aligned copy in its own procedure descriptor */ | ||
42 | |||
43 | .ent stxncpy_aligned | ||
44 | .align 3 | ||
45 | stxncpy_aligned: | ||
46 | .frame sp, 0, t9, 0 | ||
47 | .prologue 0 | ||
48 | |||
49 | /* On entry to this basic block: | ||
50 | t0 == the first destination word for masking back in | ||
51 | t1 == the first source word. */ | ||
52 | |||
53 | /* Create the 1st output word and detect 0's in the 1st input word. */ | ||
54 | lda t2, -1 # e1 : build a mask against false zero | ||
55 | mskqh t2, a1, t2 # e0 : detection in the src word | ||
56 | mskqh t1, a1, t3 # e0 : | ||
57 | ornot t1, t2, t2 # .. e1 : | ||
58 | mskql t0, a1, t0 # e0 : assemble the first output word | ||
59 | cmpbge zero, t2, t8 # .. e1 : bits set iff null found | ||
60 | or t0, t3, t0 # e0 : | ||
61 | beq a2, $a_eoc # .. e1 : | ||
62 | bne t8, $a_eos # .. e1 : | ||
63 | |||
64 | /* On entry to this basic block: | ||
65 | t0 == a source word not containing a null. */ | ||
66 | |||
67 | $a_loop: | ||
68 | stq_u t0, 0(a0) # e0 : | ||
69 | addq a0, 8, a0 # .. e1 : | ||
70 | ldq_u t0, 0(a1) # e0 : | ||
71 | addq a1, 8, a1 # .. e1 : | ||
72 | subq a2, 1, a2 # e0 : | ||
73 | cmpbge zero, t0, t8 # .. e1 (stall) | ||
74 | beq a2, $a_eoc # e1 : | ||
75 | beq t8, $a_loop # e1 : | ||
76 | |||
77 | /* Take care of the final (partial) word store. At this point | ||
78 | the end-of-count bit is set in t8 iff it applies. | ||
79 | |||
80 | On entry to this basic block we have: | ||
81 | t0 == the source word containing the null | ||
82 | t8 == the cmpbge mask that found it. */ | ||
83 | |||
84 | $a_eos: | ||
85 | negq t8, t12 # e0 : find low bit set | ||
86 | and t8, t12, t12 # e1 (stall) | ||
87 | |||
88 | /* For the sake of the cache, don't read a destination word | ||
89 | if we're not going to need it. */ | ||
90 | and t12, 0x80, t6 # e0 : | ||
91 | bne t6, 1f # .. e1 (zdb) | ||
92 | |||
93 | /* We're doing a partial word store and so need to combine | ||
94 | our source and original destination words. */ | ||
95 | ldq_u t1, 0(a0) # e0 : | ||
96 | subq t12, 1, t6 # .. e1 : | ||
97 | or t12, t6, t8 # e0 : | ||
98 | unop # | ||
99 | zapnot t0, t8, t0 # e0 : clear src bytes > null | ||
100 | zap t1, t8, t1 # .. e1 : clear dst bytes <= null | ||
101 | or t0, t1, t0 # e1 : | ||
102 | |||
103 | 1: stq_u t0, 0(a0) # e0 : | ||
104 | ret (t9) # e1 : | ||
105 | |||
106 | /* Add the end-of-count bit to the eos detection bitmask. */ | ||
107 | $a_eoc: | ||
108 | or t10, t8, t8 | ||
109 | br $a_eos | ||
110 | |||
111 | .end stxncpy_aligned | ||
112 | |||
113 | .align 3 | ||
114 | .ent __stxncpy | ||
115 | .globl __stxncpy | ||
116 | __stxncpy: | ||
117 | .frame sp, 0, t9, 0 | ||
118 | .prologue 0 | ||
119 | |||
120 | /* Are source and destination co-aligned? */ | ||
121 | xor a0, a1, t1 # e0 : | ||
122 | and a0, 7, t0 # .. e1 : find dest misalignment | ||
123 | and t1, 7, t1 # e0 : | ||
124 | addq a2, t0, a2 # .. e1 : bias count by dest misalignment | ||
125 | subq a2, 1, a2 # e0 : | ||
126 | and a2, 7, t2 # e1 : | ||
127 | srl a2, 3, a2 # e0 : a2 = loop counter = (count - 1)/8 | ||
128 | addq zero, 1, t10 # .. e1 : | ||
129 | sll t10, t2, t10 # e0 : t10 = bitmask of last count byte | ||
130 | bne t1, $unaligned # .. e1 : | ||
131 | |||
132 | /* We are co-aligned; take care of a partial first word. */ | ||
133 | |||
134 | ldq_u t1, 0(a1) # e0 : load first src word | ||
135 | addq a1, 8, a1 # .. e1 : | ||
136 | |||
137 | beq t0, stxncpy_aligned # avoid loading dest word if not needed | ||
138 | ldq_u t0, 0(a0) # e0 : | ||
139 | br stxncpy_aligned # .. e1 : | ||
140 | |||
141 | |||
142 | /* The source and destination are not co-aligned. Align the destination | ||
143 | and cope. We have to be very careful about not reading too much and | ||
144 | causing a SEGV. */ | ||
145 | |||
146 | .align 3 | ||
147 | $u_head: | ||
148 | /* We know just enough now to be able to assemble the first | ||
149 | full source word. We can still find a zero at the end of it | ||
150 | that prevents us from outputting the whole thing. | ||
151 | |||
152 | On entry to this basic block: | ||
153 | t0 == the first dest word, unmasked | ||
154 | t1 == the shifted low bits of the first source word | ||
155 | t6 == bytemask that is -1 in dest word bytes */ | ||
156 | |||
157 | ldq_u t2, 8(a1) # e0 : load second src word | ||
158 | addq a1, 8, a1 # .. e1 : | ||
159 | mskql t0, a0, t0 # e0 : mask trailing garbage in dst | ||
160 | extqh t2, a1, t4 # e0 : | ||
161 | or t1, t4, t1 # e1 : first aligned src word complete | ||
162 | mskqh t1, a0, t1 # e0 : mask leading garbage in src | ||
163 | or t0, t1, t0 # e0 : first output word complete | ||
164 | or t0, t6, t6 # e1 : mask original data for zero test | ||
165 | cmpbge zero, t6, t8 # e0 : | ||
166 | beq a2, $u_eocfin # .. e1 : | ||
167 | lda t6, -1 # e0 : | ||
168 | bne t8, $u_final # .. e1 : | ||
169 | |||
170 | mskql t6, a1, t6 # e0 : mask out bits already seen | ||
171 | nop # .. e1 : | ||
172 | stq_u t0, 0(a0) # e0 : store first output word | ||
173 | or t6, t2, t2 # .. e1 : | ||
174 | cmpbge zero, t2, t8 # e0 : find nulls in second partial | ||
175 | addq a0, 8, a0 # .. e1 : | ||
176 | subq a2, 1, a2 # e0 : | ||
177 | bne t8, $u_late_head_exit # .. e1 : | ||
178 | |||
179 | /* Finally, we've got all the stupid leading edge cases taken care | ||
180 | of and we can set up to enter the main loop. */ | ||
181 | |||
182 | extql t2, a1, t1 # e0 : position hi-bits of lo word | ||
183 | beq a2, $u_eoc # .. e1 : | ||
184 | ldq_u t2, 8(a1) # e0 : read next high-order source word | ||
185 | addq a1, 8, a1 # .. e1 : | ||
186 | extqh t2, a1, t0 # e0 : position lo-bits of hi word (stall) | ||
187 | cmpbge zero, t2, t8 # .. e1 : | ||
188 | nop # e0 : | ||
189 | bne t8, $u_eos # .. e1 : | ||
190 | |||
191 | /* Unaligned copy main loop. In order to avoid reading too much, | ||
192 | the loop is structured to detect zeros in aligned source words. | ||
193 | This has, unfortunately, effectively pulled half of a loop | ||
194 | iteration out into the head and half into the tail, but it does | ||
195 | prevent nastiness from accumulating in the very thing we want | ||
196 | to run as fast as possible. | ||
197 | |||
198 | On entry to this basic block: | ||
199 | t0 == the shifted low-order bits from the current source word | ||
200 | t1 == the shifted high-order bits from the previous source word | ||
201 | t2 == the unshifted current source word | ||
202 | |||
203 | We further know that t2 does not contain a null terminator. */ | ||
204 | |||
205 | .align 3 | ||
206 | $u_loop: | ||
207 | or t0, t1, t0 # e0 : current dst word now complete | ||
208 | subq a2, 1, a2 # .. e1 : decrement word count | ||
209 | stq_u t0, 0(a0) # e0 : save the current word | ||
210 | addq a0, 8, a0 # .. e1 : | ||
211 | extql t2, a1, t1 # e0 : extract high bits for next time | ||
212 | beq a2, $u_eoc # .. e1 : | ||
213 | ldq_u t2, 8(a1) # e0 : load high word for next time | ||
214 | addq a1, 8, a1 # .. e1 : | ||
215 | nop # e0 : | ||
216 | cmpbge zero, t2, t8 # e1 : test new word for eos (stall) | ||
217 | extqh t2, a1, t0 # e0 : extract low bits for current word | ||
218 | beq t8, $u_loop # .. e1 : | ||
219 | |||
220 | /* We've found a zero somewhere in the source word we just read. | ||
221 | If it resides in the lower half, we have one (probably partial) | ||
222 | word to write out, and if it resides in the upper half, we | ||
223 | have one full and one partial word left to write out. | ||
224 | |||
225 | On entry to this basic block: | ||
226 | t0 == the shifted low-order bits from the current source word | ||
227 | t1 == the shifted high-order bits from the previous source word | ||
228 | t2 == the unshifted current source word. */ | ||
229 | $u_eos: | ||
230 | or t0, t1, t0 # e0 : first (partial) source word complete | ||
231 | nop # .. e1 : | ||
232 | cmpbge zero, t0, t8 # e0 : is the null in this first bit? | ||
233 | bne t8, $u_final # .. e1 (zdb) | ||
234 | |||
235 | stq_u t0, 0(a0) # e0 : the null was in the high-order bits | ||
236 | addq a0, 8, a0 # .. e1 : | ||
237 | subq a2, 1, a2 # e1 : | ||
238 | |||
239 | $u_late_head_exit: | ||
240 | extql t2, a1, t0 # .. e0 : | ||
241 | cmpbge zero, t0, t8 # e0 : | ||
242 | or t8, t10, t6 # e1 : | ||
243 | cmoveq a2, t6, t8 # e0 : | ||
244 | nop # .. e1 : | ||
245 | |||
246 | /* Take care of a final (probably partial) result word. | ||
247 | On entry to this basic block: | ||
248 | t0 == assembled source word | ||
249 | t8 == cmpbge mask that found the null. */ | ||
250 | $u_final: | ||
251 | negq t8, t6 # e0 : isolate low bit set | ||
252 | and t6, t8, t12 # e1 : | ||
253 | |||
254 | and t12, 0x80, t6 # e0 : avoid dest word load if we can | ||
255 | bne t6, 1f # .. e1 (zdb) | ||
256 | |||
257 | ldq_u t1, 0(a0) # e0 : | ||
258 | subq t12, 1, t6 # .. e1 : | ||
259 | or t6, t12, t8 # e0 : | ||
260 | zapnot t0, t8, t0 # .. e1 : kill source bytes > null | ||
261 | zap t1, t8, t1 # e0 : kill dest bytes <= null | ||
262 | or t0, t1, t0 # e1 : | ||
263 | |||
264 | 1: stq_u t0, 0(a0) # e0 : | ||
265 | ret (t9) # .. e1 : | ||
266 | |||
267 | /* Got to end-of-count before end of string. | ||
268 | On entry to this basic block: | ||
269 | t1 == the shifted high-order bits from the previous source word */ | ||
270 | $u_eoc: | ||
271 | and a1, 7, t6 # e1 : | ||
272 | sll t10, t6, t6 # e0 : | ||
273 | and t6, 0xff, t6 # e0 : | ||
274 | bne t6, 1f # .. e1 : | ||
275 | |||
276 | ldq_u t2, 8(a1) # e0 : load final src word | ||
277 | nop # .. e1 : | ||
278 | extqh t2, a1, t0 # e0 : extract low bits for last word | ||
279 | or t1, t0, t1 # e1 : | ||
280 | |||
281 | 1: cmpbge zero, t1, t8 | ||
282 | mov t1, t0 | ||
283 | |||
284 | $u_eocfin: # end-of-count, final word | ||
285 | or t10, t8, t8 | ||
286 | br $u_final | ||
287 | |||
288 | /* Unaligned copy entry point. */ | ||
289 | .align 3 | ||
290 | $unaligned: | ||
291 | |||
292 | ldq_u t1, 0(a1) # e0 : load first source word | ||
293 | |||
294 | and a0, 7, t4 # .. e1 : find dest misalignment | ||
295 | and a1, 7, t5 # e0 : find src misalignment | ||
296 | |||
297 | /* Conditionally load the first destination word and a bytemask | ||
298 | with 0xff indicating that the destination byte is sacrosanct. */ | ||
299 | |||
300 | mov zero, t0 # .. e1 : | ||
301 | mov zero, t6 # e0 : | ||
302 | beq t4, 1f # .. e1 : | ||
303 | ldq_u t0, 0(a0) # e0 : | ||
304 | lda t6, -1 # .. e1 : | ||
305 | mskql t6, a0, t6 # e0 : | ||
306 | subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr | ||
307 | |||
308 | /* If source misalignment is larger than dest misalignment, we need | ||
309 | extra startup checks to avoid SEGV. */ | ||
310 | |||
311 | 1: cmplt t4, t5, t12 # e1 : | ||
312 | extql t1, a1, t1 # .. e0 : shift src into place | ||
313 | lda t2, -1 # e0 : for creating masks later | ||
314 | beq t12, $u_head # .. e1 : | ||
315 | |||
316 | extql t2, a1, t2 # e0 : | ||
317 | cmpbge zero, t1, t8 # .. e1 : is there a zero? | ||
318 | andnot t2, t6, t12 # e0 : dest mask for a single word copy | ||
319 | or t8, t10, t5 # .. e1 : test for end-of-count too | ||
320 | cmpbge zero, t12, t3 # e0 : | ||
321 | cmoveq a2, t5, t8 # .. e1 : | ||
322 | andnot t8, t3, t8 # e0 : | ||
323 | beq t8, $u_head # .. e1 (zdb) | ||
324 | |||
325 | /* At this point we've found a zero in the first partial word of | ||
326 | the source. We need to isolate the valid source data and mask | ||
327 | it into the original destination data. (Incidentally, we know | ||
328 | that we'll need at least one byte of that original dest word.) */ | ||
329 | |||
330 | ldq_u t0, 0(a0) # e0 : | ||
331 | negq t8, t6 # .. e1 : build bitmask of bytes <= zero | ||
332 | mskqh t1, t4, t1 # e0 : | ||
333 | and t6, t8, t2 # .. e1 : | ||
334 | subq t2, 1, t6 # e0 : | ||
335 | or t6, t2, t8 # e1 : | ||
336 | |||
337 | zapnot t12, t8, t12 # e0 : prepare source word; mirror changes | ||
338 | zapnot t1, t8, t1 # .. e1 : to source validity mask | ||
339 | |||
340 | andnot t0, t12, t0 # e0 : zero place for source to reside | ||
341 | or t0, t1, t0 # e1 : and put it there | ||
342 | stq_u t0, 0(a0) # e0 : | ||
343 | ret (t9) # .. e1 : | ||
344 | |||
345 | .end __stxncpy | ||
diff --git a/arch/alpha/lib/udelay.c b/arch/alpha/lib/udelay.c new file mode 100644 index 000000000000..1c879bbce419 --- /dev/null +++ b/arch/alpha/lib/udelay.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993, 2000 Linus Torvalds | ||
3 | * | ||
4 | * Delay routines, using a pre-computed "loops_per_jiffy" value. | ||
5 | */ | ||
6 | |||
7 | #include <linux/config.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/sched.h> /* for udelay's use of smp_processor_id */ | ||
10 | #include <asm/param.h> | ||
11 | #include <asm/smp.h> | ||
12 | #include <linux/delay.h> | ||
13 | |||
14 | /* | ||
15 | * Use only for very small delays (< 1 msec). | ||
16 | * | ||
17 | * The active part of our cycle counter is only 32-bits wide, and | ||
18 | * we're treating the difference between two marks as signed. On | ||
19 | * a 1GHz box, that's about 2 seconds. | ||
20 | */ | ||
21 | |||
22 | void | ||
23 | __delay(int loops) | ||
24 | { | ||
25 | int tmp; | ||
26 | __asm__ __volatile__( | ||
27 | " rpcc %0\n" | ||
28 | " addl %1,%0,%1\n" | ||
29 | "1: rpcc %0\n" | ||
30 | " subl %1,%0,%0\n" | ||
31 | " bgt %0,1b" | ||
32 | : "=&r" (tmp), "=r" (loops) : "1"(loops)); | ||
33 | } | ||
34 | |||
35 | #ifdef CONFIG_SMP | ||
36 | #define LPJ cpu_data[smp_processor_id()].loops_per_jiffy | ||
37 | #else | ||
38 | #define LPJ loops_per_jiffy | ||
39 | #endif | ||
40 | |||
41 | void | ||
42 | udelay(unsigned long usecs) | ||
43 | { | ||
44 | usecs *= (((unsigned long)HZ << 32) / 1000000) * LPJ; | ||
45 | __delay((long)usecs >> 32); | ||
46 | } | ||
47 | EXPORT_SYMBOL(udelay); | ||
48 | |||
49 | void | ||
50 | ndelay(unsigned long nsecs) | ||
51 | { | ||
52 | nsecs *= (((unsigned long)HZ << 32) / 1000000000) * LPJ; | ||
53 | __delay((long)nsecs >> 32); | ||
54 | } | ||
55 | EXPORT_SYMBOL(ndelay); | ||
diff --git a/arch/alpha/math-emu/Makefile b/arch/alpha/math-emu/Makefile new file mode 100644 index 000000000000..359ef087e69e --- /dev/null +++ b/arch/alpha/math-emu/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for the FPU instruction emulation. | ||
3 | # | ||
4 | |||
5 | EXTRA_CFLAGS := -w | ||
6 | |||
7 | obj-$(CONFIG_MATHEMU) += math-emu.o | ||
8 | |||
9 | math-emu-objs := math.o qrnnd.o | ||
diff --git a/arch/alpha/math-emu/math.c b/arch/alpha/math-emu/math.c new file mode 100644 index 000000000000..ae79dd970b02 --- /dev/null +++ b/arch/alpha/math-emu/math.c | |||
@@ -0,0 +1,400 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/types.h> | ||
3 | #include <linux/kernel.h> | ||
4 | #include <linux/sched.h> | ||
5 | |||
6 | #include <asm/uaccess.h> | ||
7 | |||
8 | #include "sfp-util.h" | ||
9 | #include <math-emu/soft-fp.h> | ||
10 | #include <math-emu/single.h> | ||
11 | #include <math-emu/double.h> | ||
12 | |||
13 | #define OPC_PAL 0x00 | ||
14 | #define OPC_INTA 0x10 | ||
15 | #define OPC_INTL 0x11 | ||
16 | #define OPC_INTS 0x12 | ||
17 | #define OPC_INTM 0x13 | ||
18 | #define OPC_FLTC 0x14 | ||
19 | #define OPC_FLTV 0x15 | ||
20 | #define OPC_FLTI 0x16 | ||
21 | #define OPC_FLTL 0x17 | ||
22 | #define OPC_MISC 0x18 | ||
23 | #define OPC_JSR 0x1a | ||
24 | |||
25 | #define FOP_SRC_S 0 | ||
26 | #define FOP_SRC_T 2 | ||
27 | #define FOP_SRC_Q 3 | ||
28 | |||
29 | #define FOP_FNC_ADDx 0 | ||
30 | #define FOP_FNC_CVTQL 0 | ||
31 | #define FOP_FNC_SUBx 1 | ||
32 | #define FOP_FNC_MULx 2 | ||
33 | #define FOP_FNC_DIVx 3 | ||
34 | #define FOP_FNC_CMPxUN 4 | ||
35 | #define FOP_FNC_CMPxEQ 5 | ||
36 | #define FOP_FNC_CMPxLT 6 | ||
37 | #define FOP_FNC_CMPxLE 7 | ||
38 | #define FOP_FNC_SQRTx 11 | ||
39 | #define FOP_FNC_CVTxS 12 | ||
40 | #define FOP_FNC_CVTxT 14 | ||
41 | #define FOP_FNC_CVTxQ 15 | ||
42 | |||
43 | #define MISC_TRAPB 0x0000 | ||
44 | #define MISC_EXCB 0x0400 | ||
45 | |||
46 | extern unsigned long alpha_read_fp_reg (unsigned long reg); | ||
47 | extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); | ||
48 | extern unsigned long alpha_read_fp_reg_s (unsigned long reg); | ||
49 | extern void alpha_write_fp_reg_s (unsigned long reg, unsigned long val); | ||
50 | |||
51 | |||
52 | #ifdef MODULE | ||
53 | |||
54 | MODULE_DESCRIPTION("FP Software completion module"); | ||
55 | |||
56 | extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); | ||
57 | extern long (*alpha_fp_emul) (unsigned long pc); | ||
58 | |||
59 | static long (*save_emul_imprecise)(struct pt_regs *, unsigned long); | ||
60 | static long (*save_emul) (unsigned long pc); | ||
61 | |||
62 | long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long); | ||
63 | long do_alpha_fp_emul(unsigned long); | ||
64 | |||
65 | int init_module(void) | ||
66 | { | ||
67 | save_emul_imprecise = alpha_fp_emul_imprecise; | ||
68 | save_emul = alpha_fp_emul; | ||
69 | alpha_fp_emul_imprecise = do_alpha_fp_emul_imprecise; | ||
70 | alpha_fp_emul = do_alpha_fp_emul; | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | void cleanup_module(void) | ||
75 | { | ||
76 | alpha_fp_emul_imprecise = save_emul_imprecise; | ||
77 | alpha_fp_emul = save_emul; | ||
78 | } | ||
79 | |||
80 | #undef alpha_fp_emul_imprecise | ||
81 | #define alpha_fp_emul_imprecise do_alpha_fp_emul_imprecise | ||
82 | #undef alpha_fp_emul | ||
83 | #define alpha_fp_emul do_alpha_fp_emul | ||
84 | |||
85 | #endif /* MODULE */ | ||
86 | |||
87 | |||
88 | /* | ||
89 | * Emulate the floating point instruction at address PC. Returns -1 if the | ||
90 | * instruction to be emulated is illegal (such as with the opDEC trap), else | ||
91 | * the SI_CODE for a SIGFPE signal, else 0 if everything's ok. | ||
92 | * | ||
93 | * Notice that the kernel does not and cannot use FP regs. This is good | ||
94 | * because it means that instead of saving/restoring all fp regs, we simply | ||
95 | * stick the result of the operation into the appropriate register. | ||
96 | */ | ||
97 | long | ||
98 | alpha_fp_emul (unsigned long pc) | ||
99 | { | ||
100 | FP_DECL_EX; | ||
101 | FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); | ||
102 | FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); | ||
103 | |||
104 | unsigned long fa, fb, fc, func, mode, src; | ||
105 | unsigned long res, va, vb, vc, swcr, fpcr; | ||
106 | __u32 insn; | ||
107 | long si_code; | ||
108 | |||
109 | get_user(insn, (__u32 __user *)pc); | ||
110 | fc = (insn >> 0) & 0x1f; /* destination register */ | ||
111 | fb = (insn >> 16) & 0x1f; | ||
112 | fa = (insn >> 21) & 0x1f; | ||
113 | func = (insn >> 5) & 0xf; | ||
114 | src = (insn >> 9) & 0x3; | ||
115 | mode = (insn >> 11) & 0x3; | ||
116 | |||
117 | fpcr = rdfpcr(); | ||
118 | swcr = swcr_update_status(current_thread_info()->ieee_state, fpcr); | ||
119 | |||
120 | if (mode == 3) { | ||
121 | /* Dynamic -- get rounding mode from fpcr. */ | ||
122 | mode = (fpcr >> FPCR_DYN_SHIFT) & 3; | ||
123 | } | ||
124 | |||
125 | switch (src) { | ||
126 | case FOP_SRC_S: | ||
127 | va = alpha_read_fp_reg_s(fa); | ||
128 | vb = alpha_read_fp_reg_s(fb); | ||
129 | |||
130 | FP_UNPACK_SP(SA, &va); | ||
131 | FP_UNPACK_SP(SB, &vb); | ||
132 | |||
133 | switch (func) { | ||
134 | case FOP_FNC_SUBx: | ||
135 | FP_SUB_S(SR, SA, SB); | ||
136 | goto pack_s; | ||
137 | |||
138 | case FOP_FNC_ADDx: | ||
139 | FP_ADD_S(SR, SA, SB); | ||
140 | goto pack_s; | ||
141 | |||
142 | case FOP_FNC_MULx: | ||
143 | FP_MUL_S(SR, SA, SB); | ||
144 | goto pack_s; | ||
145 | |||
146 | case FOP_FNC_DIVx: | ||
147 | FP_DIV_S(SR, SA, SB); | ||
148 | goto pack_s; | ||
149 | |||
150 | case FOP_FNC_SQRTx: | ||
151 | FP_SQRT_S(SR, SB); | ||
152 | goto pack_s; | ||
153 | } | ||
154 | goto bad_insn; | ||
155 | |||
156 | case FOP_SRC_T: | ||
157 | va = alpha_read_fp_reg(fa); | ||
158 | vb = alpha_read_fp_reg(fb); | ||
159 | |||
160 | if ((func & ~3) == FOP_FNC_CMPxUN) { | ||
161 | FP_UNPACK_RAW_DP(DA, &va); | ||
162 | FP_UNPACK_RAW_DP(DB, &vb); | ||
163 | if (!DA_e && !_FP_FRAC_ZEROP_1(DA)) { | ||
164 | FP_SET_EXCEPTION(FP_EX_DENORM); | ||
165 | if (FP_DENORM_ZERO) | ||
166 | _FP_FRAC_SET_1(DA, _FP_ZEROFRAC_1); | ||
167 | } | ||
168 | if (!DB_e && !_FP_FRAC_ZEROP_1(DB)) { | ||
169 | FP_SET_EXCEPTION(FP_EX_DENORM); | ||
170 | if (FP_DENORM_ZERO) | ||
171 | _FP_FRAC_SET_1(DB, _FP_ZEROFRAC_1); | ||
172 | } | ||
173 | FP_CMP_D(res, DA, DB, 3); | ||
174 | vc = 0x4000000000000000UL; | ||
175 | /* CMPTEQ, CMPTUN don't trap on QNaN, | ||
176 | while CMPTLT and CMPTLE do */ | ||
177 | if (res == 3 | ||
178 | && ((func & 3) >= 2 | ||
179 | || FP_ISSIGNAN_D(DA) | ||
180 | || FP_ISSIGNAN_D(DB))) { | ||
181 | FP_SET_EXCEPTION(FP_EX_INVALID); | ||
182 | } | ||
183 | switch (func) { | ||
184 | case FOP_FNC_CMPxUN: if (res != 3) vc = 0; break; | ||
185 | case FOP_FNC_CMPxEQ: if (res) vc = 0; break; | ||
186 | case FOP_FNC_CMPxLT: if (res != -1) vc = 0; break; | ||
187 | case FOP_FNC_CMPxLE: if ((long)res > 0) vc = 0; break; | ||
188 | } | ||
189 | goto done_d; | ||
190 | } | ||
191 | |||
192 | FP_UNPACK_DP(DA, &va); | ||
193 | FP_UNPACK_DP(DB, &vb); | ||
194 | |||
195 | switch (func) { | ||
196 | case FOP_FNC_SUBx: | ||
197 | FP_SUB_D(DR, DA, DB); | ||
198 | goto pack_d; | ||
199 | |||
200 | case FOP_FNC_ADDx: | ||
201 | FP_ADD_D(DR, DA, DB); | ||
202 | goto pack_d; | ||
203 | |||
204 | case FOP_FNC_MULx: | ||
205 | FP_MUL_D(DR, DA, DB); | ||
206 | goto pack_d; | ||
207 | |||
208 | case FOP_FNC_DIVx: | ||
209 | FP_DIV_D(DR, DA, DB); | ||
210 | goto pack_d; | ||
211 | |||
212 | case FOP_FNC_SQRTx: | ||
213 | FP_SQRT_D(DR, DB); | ||
214 | goto pack_d; | ||
215 | |||
216 | case FOP_FNC_CVTxS: | ||
217 | /* It is irritating that DEC encoded CVTST with | ||
218 | SRC == T_floating. It is also interesting that | ||
219 | the bit used to tell the two apart is /U... */ | ||
220 | if (insn & 0x2000) { | ||
221 | FP_CONV(S,D,1,1,SR,DB); | ||
222 | goto pack_s; | ||
223 | } else { | ||
224 | vb = alpha_read_fp_reg_s(fb); | ||
225 | FP_UNPACK_SP(SB, &vb); | ||
226 | DR_c = DB_c; | ||
227 | DR_s = DB_s; | ||
228 | DR_e = DB_e; | ||
229 | DR_f = SB_f << (52 - 23); | ||
230 | goto pack_d; | ||
231 | } | ||
232 | |||
233 | case FOP_FNC_CVTxQ: | ||
234 | if (DB_c == FP_CLS_NAN | ||
235 | && (_FP_FRAC_HIGH_RAW_D(DB) & _FP_QNANBIT_D)) { | ||
236 | /* AAHB Table B-2 says QNaN should not trigger INV */ | ||
237 | vc = 0; | ||
238 | } else | ||
239 | FP_TO_INT_ROUND_D(vc, DB, 64, 2); | ||
240 | goto done_d; | ||
241 | } | ||
242 | goto bad_insn; | ||
243 | |||
244 | case FOP_SRC_Q: | ||
245 | vb = alpha_read_fp_reg(fb); | ||
246 | |||
247 | switch (func) { | ||
248 | case FOP_FNC_CVTQL: | ||
249 | /* Notice: We can get here only due to an integer | ||
250 | overflow. Such overflows are reported as invalid | ||
251 | ops. We return the result the hw would have | ||
252 | computed. */ | ||
253 | vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ | ||
254 | (vb & 0x3fffffff) << 29); /* rest of the int */ | ||
255 | FP_SET_EXCEPTION (FP_EX_INVALID); | ||
256 | goto done_d; | ||
257 | |||
258 | case FOP_FNC_CVTxS: | ||
259 | FP_FROM_INT_S(SR, ((long)vb), 64, long); | ||
260 | goto pack_s; | ||
261 | |||
262 | case FOP_FNC_CVTxT: | ||
263 | FP_FROM_INT_D(DR, ((long)vb), 64, long); | ||
264 | goto pack_d; | ||
265 | } | ||
266 | goto bad_insn; | ||
267 | } | ||
268 | goto bad_insn; | ||
269 | |||
270 | pack_s: | ||
271 | FP_PACK_SP(&vc, SR); | ||
272 | if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ)) | ||
273 | vc = 0; | ||
274 | alpha_write_fp_reg_s(fc, vc); | ||
275 | goto done; | ||
276 | |||
277 | pack_d: | ||
278 | FP_PACK_DP(&vc, DR); | ||
279 | if ((_fex & FP_EX_UNDERFLOW) && (swcr & IEEE_MAP_UMZ)) | ||
280 | vc = 0; | ||
281 | done_d: | ||
282 | alpha_write_fp_reg(fc, vc); | ||
283 | goto done; | ||
284 | |||
285 | /* | ||
286 | * Take the appropriate action for each possible | ||
287 | * floating-point result: | ||
288 | * | ||
289 | * - Set the appropriate bits in the FPCR | ||
290 | * - If the specified exception is enabled in the FPCR, | ||
291 | * return. The caller (entArith) will dispatch | ||
292 | * the appropriate signal to the translated program. | ||
293 | * | ||
294 | * In addition, properly track the exception state in software | ||
295 | * as described in the Alpha Architecture Handbook section 4.7.7.3. | ||
296 | */ | ||
297 | done: | ||
298 | if (_fex) { | ||
299 | /* Record exceptions in software control word. */ | ||
300 | swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); | ||
301 | current_thread_info()->ieee_state | ||
302 | |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); | ||
303 | |||
304 | /* Update hardware control register. */ | ||
305 | fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); | ||
306 | fpcr |= ieee_swcr_to_fpcr(swcr); | ||
307 | wrfpcr(fpcr); | ||
308 | |||
309 | /* Do we generate a signal? */ | ||
310 | _fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK; | ||
311 | si_code = 0; | ||
312 | if (_fex) { | ||
313 | if (_fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND; | ||
314 | if (_fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES; | ||
315 | if (_fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND; | ||
316 | if (_fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF; | ||
317 | if (_fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV; | ||
318 | if (_fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV; | ||
319 | } | ||
320 | |||
321 | return si_code; | ||
322 | } | ||
323 | |||
324 | /* We used to write the destination register here, but DEC FORTRAN | ||
325 | requires that the result *always* be written... so we do the write | ||
326 | immediately after the operations above. */ | ||
327 | |||
328 | return 0; | ||
329 | |||
330 | bad_insn: | ||
331 | printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n", | ||
332 | insn, pc); | ||
333 | return -1; | ||
334 | } | ||
335 | |||
336 | long | ||
337 | alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) | ||
338 | { | ||
339 | unsigned long trigger_pc = regs->pc - 4; | ||
340 | unsigned long insn, opcode, rc, si_code = 0; | ||
341 | |||
342 | /* | ||
343 | * Turn off the bits corresponding to registers that are the | ||
344 | * target of instructions that set bits in the exception | ||
345 | * summary register. We have some slack doing this because a | ||
346 | * register that is the target of a trapping instruction can | ||
347 | * be written at most once in the trap shadow. | ||
348 | * | ||
349 | * Branches, jumps, TRAPBs, EXCBs and calls to PALcode all | ||
350 | * bound the trap shadow, so we need not look any further than | ||
351 | * up to the first occurrence of such an instruction. | ||
352 | */ | ||
353 | while (write_mask) { | ||
354 | get_user(insn, (__u32 __user *)(trigger_pc)); | ||
355 | opcode = insn >> 26; | ||
356 | rc = insn & 0x1f; | ||
357 | |||
358 | switch (opcode) { | ||
359 | case OPC_PAL: | ||
360 | case OPC_JSR: | ||
361 | case 0x30 ... 0x3f: /* branches */ | ||
362 | goto egress; | ||
363 | |||
364 | case OPC_MISC: | ||
365 | switch (insn & 0xffff) { | ||
366 | case MISC_TRAPB: | ||
367 | case MISC_EXCB: | ||
368 | goto egress; | ||
369 | |||
370 | default: | ||
371 | break; | ||
372 | } | ||
373 | break; | ||
374 | |||
375 | case OPC_INTA: | ||
376 | case OPC_INTL: | ||
377 | case OPC_INTS: | ||
378 | case OPC_INTM: | ||
379 | write_mask &= ~(1UL << rc); | ||
380 | break; | ||
381 | |||
382 | case OPC_FLTC: | ||
383 | case OPC_FLTV: | ||
384 | case OPC_FLTI: | ||
385 | case OPC_FLTL: | ||
386 | write_mask &= ~(1UL << (rc + 32)); | ||
387 | break; | ||
388 | } | ||
389 | if (!write_mask) { | ||
390 | /* Re-execute insns in the trap-shadow. */ | ||
391 | regs->pc = trigger_pc + 4; | ||
392 | si_code = alpha_fp_emul(trigger_pc); | ||
393 | goto egress; | ||
394 | } | ||
395 | trigger_pc -= 4; | ||
396 | } | ||
397 | |||
398 | egress: | ||
399 | return si_code; | ||
400 | } | ||
diff --git a/arch/alpha/math-emu/qrnnd.S b/arch/alpha/math-emu/qrnnd.S new file mode 100644 index 000000000000..d6373ec1bff9 --- /dev/null +++ b/arch/alpha/math-emu/qrnnd.S | |||
@@ -0,0 +1,163 @@ | |||
1 | # Alpha 21064 __udiv_qrnnd | ||
2 | # Copyright (C) 1992, 1994, 1995, 2000 Free Software Foundation, Inc. | ||
3 | |||
4 | # This file is part of GCC. | ||
5 | |||
6 | # The GNU MP Library is free software; you can redistribute it and/or modify | ||
7 | # it under the terms of the GNU General Public License as published by | ||
8 | # the Free Software Foundation; either version 2 of the License, or (at your | ||
9 | # option) any later version. | ||
10 | |||
11 | # In addition to the permissions in the GNU General Public License, the | ||
12 | # Free Software Foundation gives you unlimited permission to link the | ||
13 | # compiled version of this file with other programs, and to distribute | ||
14 | # those programs without any restriction coming from the use of this | ||
15 | # file. (The General Public License restrictions do apply in other | ||
16 | # respects; for example, they cover modification of the file, and | ||
17 | # distribution when not linked into another program.) | ||
18 | |||
19 | # This file is distributed in the hope that it will be useful, but | ||
20 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
21 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
22 | # License for more details. | ||
23 | |||
24 | # You should have received a copy of the GNU General Public License | ||
25 | # along with GCC; see the file COPYING. If not, write to the | ||
26 | # Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||
27 | # MA 02111-1307, USA. | ||
28 | |||
29 | .set noreorder | ||
30 | .set noat | ||
31 | |||
32 | .text | ||
33 | |||
34 | .globl __udiv_qrnnd | ||
35 | .ent __udiv_qrnnd | ||
36 | __udiv_qrnnd: | ||
37 | .frame $30,0,$26,0 | ||
38 | .prologue 0 | ||
39 | |||
40 | #define cnt $2 | ||
41 | #define tmp $3 | ||
42 | #define rem_ptr $16 | ||
43 | #define n1 $17 | ||
44 | #define n0 $18 | ||
45 | #define d $19 | ||
46 | #define qb $20 | ||
47 | #define AT $at | ||
48 | |||
49 | ldiq cnt,16 | ||
50 | blt d,$largedivisor | ||
51 | |||
52 | $loop1: cmplt n0,0,tmp | ||
53 | addq n1,n1,n1 | ||
54 | bis n1,tmp,n1 | ||
55 | addq n0,n0,n0 | ||
56 | cmpule d,n1,qb | ||
57 | subq n1,d,tmp | ||
58 | cmovne qb,tmp,n1 | ||
59 | bis n0,qb,n0 | ||
60 | cmplt n0,0,tmp | ||
61 | addq n1,n1,n1 | ||
62 | bis n1,tmp,n1 | ||
63 | addq n0,n0,n0 | ||
64 | cmpule d,n1,qb | ||
65 | subq n1,d,tmp | ||
66 | cmovne qb,tmp,n1 | ||
67 | bis n0,qb,n0 | ||
68 | cmplt n0,0,tmp | ||
69 | addq n1,n1,n1 | ||
70 | bis n1,tmp,n1 | ||
71 | addq n0,n0,n0 | ||
72 | cmpule d,n1,qb | ||
73 | subq n1,d,tmp | ||
74 | cmovne qb,tmp,n1 | ||
75 | bis n0,qb,n0 | ||
76 | cmplt n0,0,tmp | ||
77 | addq n1,n1,n1 | ||
78 | bis n1,tmp,n1 | ||
79 | addq n0,n0,n0 | ||
80 | cmpule d,n1,qb | ||
81 | subq n1,d,tmp | ||
82 | cmovne qb,tmp,n1 | ||
83 | bis n0,qb,n0 | ||
84 | subq cnt,1,cnt | ||
85 | bgt cnt,$loop1 | ||
86 | stq n1,0(rem_ptr) | ||
87 | bis $31,n0,$0 | ||
88 | ret $31,($26),1 | ||
89 | |||
90 | $largedivisor: | ||
91 | and n0,1,$4 | ||
92 | |||
93 | srl n0,1,n0 | ||
94 | sll n1,63,tmp | ||
95 | or tmp,n0,n0 | ||
96 | srl n1,1,n1 | ||
97 | |||
98 | and d,1,$6 | ||
99 | srl d,1,$5 | ||
100 | addq $5,$6,$5 | ||
101 | |||
102 | $loop2: cmplt n0,0,tmp | ||
103 | addq n1,n1,n1 | ||
104 | bis n1,tmp,n1 | ||
105 | addq n0,n0,n0 | ||
106 | cmpule $5,n1,qb | ||
107 | subq n1,$5,tmp | ||
108 | cmovne qb,tmp,n1 | ||
109 | bis n0,qb,n0 | ||
110 | cmplt n0,0,tmp | ||
111 | addq n1,n1,n1 | ||
112 | bis n1,tmp,n1 | ||
113 | addq n0,n0,n0 | ||
114 | cmpule $5,n1,qb | ||
115 | subq n1,$5,tmp | ||
116 | cmovne qb,tmp,n1 | ||
117 | bis n0,qb,n0 | ||
118 | cmplt n0,0,tmp | ||
119 | addq n1,n1,n1 | ||
120 | bis n1,tmp,n1 | ||
121 | addq n0,n0,n0 | ||
122 | cmpule $5,n1,qb | ||
123 | subq n1,$5,tmp | ||
124 | cmovne qb,tmp,n1 | ||
125 | bis n0,qb,n0 | ||
126 | cmplt n0,0,tmp | ||
127 | addq n1,n1,n1 | ||
128 | bis n1,tmp,n1 | ||
129 | addq n0,n0,n0 | ||
130 | cmpule $5,n1,qb | ||
131 | subq n1,$5,tmp | ||
132 | cmovne qb,tmp,n1 | ||
133 | bis n0,qb,n0 | ||
134 | subq cnt,1,cnt | ||
135 | bgt cnt,$loop2 | ||
136 | |||
137 | addq n1,n1,n1 | ||
138 | addq $4,n1,n1 | ||
139 | bne $6,$Odd | ||
140 | stq n1,0(rem_ptr) | ||
141 | bis $31,n0,$0 | ||
142 | ret $31,($26),1 | ||
143 | |||
144 | $Odd: | ||
145 | /* q' in n0. r' in n1 */ | ||
146 | addq n1,n0,n1 | ||
147 | |||
148 | cmpult n1,n0,tmp # tmp := carry from addq | ||
149 | subq n1,d,AT | ||
150 | addq n0,tmp,n0 | ||
151 | cmovne tmp,AT,n1 | ||
152 | |||
153 | cmpult n1,d,tmp | ||
154 | addq n0,1,AT | ||
155 | cmoveq tmp,AT,n0 | ||
156 | subq n1,d,AT | ||
157 | cmoveq tmp,AT,n1 | ||
158 | |||
159 | stq n1,0(rem_ptr) | ||
160 | bis $31,n0,$0 | ||
161 | ret $31,($26),1 | ||
162 | |||
163 | .end __udiv_qrnnd | ||
diff --git a/arch/alpha/math-emu/sfp-util.h b/arch/alpha/math-emu/sfp-util.h new file mode 100644 index 000000000000..f53707f77455 --- /dev/null +++ b/arch/alpha/math-emu/sfp-util.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <linux/sched.h> | ||
3 | #include <linux/types.h> | ||
4 | #include <asm/byteorder.h> | ||
5 | #include <asm/fpu.h> | ||
6 | |||
7 | #define add_ssaaaa(sh, sl, ah, al, bh, bl) \ | ||
8 | ((sl) = (al) + (bl), (sh) = (ah) + (bh) + ((sl) < (al))) | ||
9 | |||
10 | #define sub_ddmmss(sh, sl, ah, al, bh, bl) \ | ||
11 | ((sl) = (al) - (bl), (sh) = (ah) - (bh) - ((al) < (bl))) | ||
12 | |||
13 | #define umul_ppmm(wh, wl, u, v) \ | ||
14 | __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ | ||
15 | : "=r" ((UDItype)(wh)), \ | ||
16 | "=&r" ((UDItype)(wl)) \ | ||
17 | : "r" ((UDItype)(u)), \ | ||
18 | "r" ((UDItype)(v))) | ||
19 | |||
20 | #define udiv_qrnnd(q, r, n1, n0, d) \ | ||
21 | do { unsigned long __r; \ | ||
22 | (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \ | ||
23 | (r) = __r; \ | ||
24 | } while (0) | ||
25 | extern unsigned long __udiv_qrnnd (unsigned long *, unsigned long, | ||
26 | unsigned long , unsigned long); | ||
27 | |||
28 | #define UDIV_NEEDS_NORMALIZATION 1 | ||
29 | |||
30 | #define abort() goto bad_insn | ||
31 | |||
32 | #ifndef __LITTLE_ENDIAN | ||
33 | #define __LITTLE_ENDIAN -1 | ||
34 | #endif | ||
35 | #define __BYTE_ORDER __LITTLE_ENDIAN | ||
diff --git a/arch/alpha/mm/Makefile b/arch/alpha/mm/Makefile new file mode 100644 index 000000000000..6edd9a09ea4f --- /dev/null +++ b/arch/alpha/mm/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for the linux alpha-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | EXTRA_CFLAGS := -Werror | ||
6 | |||
7 | obj-y := init.o fault.o extable.o remap.o | ||
8 | |||
9 | obj-$(CONFIG_DISCONTIGMEM) += numa.o | ||
diff --git a/arch/alpha/mm/extable.c b/arch/alpha/mm/extable.c new file mode 100644 index 000000000000..c3849baebd57 --- /dev/null +++ b/arch/alpha/mm/extable.c | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/mm/extable.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <asm/uaccess.h> | ||
8 | |||
9 | void sort_extable(struct exception_table_entry *start, | ||
10 | struct exception_table_entry *finish) | ||
11 | { | ||
12 | } | ||
13 | |||
14 | const struct exception_table_entry * | ||
15 | search_extable(const struct exception_table_entry *first, | ||
16 | const struct exception_table_entry *last, | ||
17 | unsigned long value) | ||
18 | { | ||
19 | while (first <= last) { | ||
20 | const struct exception_table_entry *mid; | ||
21 | unsigned long mid_value; | ||
22 | |||
23 | mid = (last - first) / 2 + first; | ||
24 | mid_value = (unsigned long)&mid->insn + mid->insn; | ||
25 | if (mid_value == value) | ||
26 | return mid; | ||
27 | else if (mid_value < value) | ||
28 | first = mid+1; | ||
29 | else | ||
30 | last = mid-1; | ||
31 | } | ||
32 | |||
33 | return NULL; | ||
34 | } | ||
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c new file mode 100644 index 000000000000..64ace5a9cd3d --- /dev/null +++ b/arch/alpha/mm/fault.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/mm/fault.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | #include <linux/config.h> | ||
8 | #include <linux/sched.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <asm/io.h> | ||
12 | |||
13 | #define __EXTERN_INLINE inline | ||
14 | #include <asm/mmu_context.h> | ||
15 | #include <asm/tlbflush.h> | ||
16 | #undef __EXTERN_INLINE | ||
17 | |||
18 | #include <linux/signal.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/ptrace.h> | ||
23 | #include <linux/mman.h> | ||
24 | #include <linux/smp.h> | ||
25 | #include <linux/smp_lock.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/module.h> | ||
28 | |||
29 | #include <asm/system.h> | ||
30 | #include <asm/uaccess.h> | ||
31 | |||
32 | extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *); | ||
33 | |||
34 | |||
35 | /* | ||
36 | * Force a new ASN for a task. | ||
37 | */ | ||
38 | |||
39 | #ifndef CONFIG_SMP | ||
40 | unsigned long last_asn = ASN_FIRST_VERSION; | ||
41 | #endif | ||
42 | |||
43 | void | ||
44 | __load_new_mm_context(struct mm_struct *next_mm) | ||
45 | { | ||
46 | unsigned long mmc; | ||
47 | struct pcb_struct *pcb; | ||
48 | |||
49 | mmc = __get_new_mm_context(next_mm, smp_processor_id()); | ||
50 | next_mm->context[smp_processor_id()] = mmc; | ||
51 | |||
52 | pcb = ¤t_thread_info()->pcb; | ||
53 | pcb->asn = mmc & HARDWARE_ASN_MASK; | ||
54 | pcb->ptbr = ((unsigned long) next_mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; | ||
55 | |||
56 | __reload_thread(pcb); | ||
57 | } | ||
58 | |||
59 | |||
60 | /* | ||
61 | * This routine handles page faults. It determines the address, | ||
62 | * and the problem, and then passes it off to handle_mm_fault(). | ||
63 | * | ||
64 | * mmcsr: | ||
65 | * 0 = translation not valid | ||
66 | * 1 = access violation | ||
67 | * 2 = fault-on-read | ||
68 | * 3 = fault-on-execute | ||
69 | * 4 = fault-on-write | ||
70 | * | ||
71 | * cause: | ||
72 | * -1 = instruction fetch | ||
73 | * 0 = load | ||
74 | * 1 = store | ||
75 | * | ||
76 | * Registers $9 through $15 are saved in a block just prior to `regs' and | ||
77 | * are saved and restored around the call to allow exception code to | ||
78 | * modify them. | ||
79 | */ | ||
80 | |||
81 | /* Macro for exception fixup code to access integer registers. */ | ||
82 | #define dpf_reg(r) \ | ||
83 | (((unsigned long *)regs)[(r) <= 8 ? (r) : (r) <= 15 ? (r)-16 : \ | ||
84 | (r) <= 18 ? (r)+8 : (r)-10]) | ||
85 | |||
86 | asmlinkage void | ||
87 | do_page_fault(unsigned long address, unsigned long mmcsr, | ||
88 | long cause, struct pt_regs *regs) | ||
89 | { | ||
90 | struct vm_area_struct * vma; | ||
91 | struct mm_struct *mm = current->mm; | ||
92 | const struct exception_table_entry *fixup; | ||
93 | int fault, si_code = SEGV_MAPERR; | ||
94 | siginfo_t info; | ||
95 | |||
96 | /* As of EV6, a load into $31/$f31 is a prefetch, and never faults | ||
97 | (or is suppressed by the PALcode). Support that for older CPUs | ||
98 | by ignoring such an instruction. */ | ||
99 | if (cause == 0) { | ||
100 | unsigned int insn; | ||
101 | __get_user(insn, (unsigned int __user *)regs->pc); | ||
102 | if ((insn >> 21 & 0x1f) == 0x1f && | ||
103 | /* ldq ldl ldt lds ldg ldf ldwu ldbu */ | ||
104 | (1ul << (insn >> 26) & 0x30f00001400ul)) { | ||
105 | regs->pc += 4; | ||
106 | return; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /* If we're in an interrupt context, or have no user context, | ||
111 | we must not take the fault. */ | ||
112 | if (!mm || in_interrupt()) | ||
113 | goto no_context; | ||
114 | |||
115 | #ifdef CONFIG_ALPHA_LARGE_VMALLOC | ||
116 | if (address >= TASK_SIZE) | ||
117 | goto vmalloc_fault; | ||
118 | #endif | ||
119 | |||
120 | down_read(&mm->mmap_sem); | ||
121 | vma = find_vma(mm, address); | ||
122 | if (!vma) | ||
123 | goto bad_area; | ||
124 | if (vma->vm_start <= address) | ||
125 | goto good_area; | ||
126 | if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
127 | goto bad_area; | ||
128 | if (expand_stack(vma, address)) | ||
129 | goto bad_area; | ||
130 | |||
131 | /* Ok, we have a good vm_area for this memory access, so | ||
132 | we can handle it. */ | ||
133 | good_area: | ||
134 | si_code = SEGV_ACCERR; | ||
135 | if (cause < 0) { | ||
136 | if (!(vma->vm_flags & VM_EXEC)) | ||
137 | goto bad_area; | ||
138 | } else if (!cause) { | ||
139 | /* Allow reads even for write-only mappings */ | ||
140 | if (!(vma->vm_flags & (VM_READ | VM_WRITE))) | ||
141 | goto bad_area; | ||
142 | } else { | ||
143 | if (!(vma->vm_flags & VM_WRITE)) | ||
144 | goto bad_area; | ||
145 | } | ||
146 | |||
147 | survive: | ||
148 | /* If for any reason at all we couldn't handle the fault, | ||
149 | make sure we exit gracefully rather than endlessly redo | ||
150 | the fault. */ | ||
151 | fault = handle_mm_fault(mm, vma, address, cause > 0); | ||
152 | up_read(&mm->mmap_sem); | ||
153 | |||
154 | switch (fault) { | ||
155 | case VM_FAULT_MINOR: | ||
156 | current->min_flt++; | ||
157 | break; | ||
158 | case VM_FAULT_MAJOR: | ||
159 | current->maj_flt++; | ||
160 | break; | ||
161 | case VM_FAULT_SIGBUS: | ||
162 | goto do_sigbus; | ||
163 | case VM_FAULT_OOM: | ||
164 | goto out_of_memory; | ||
165 | default: | ||
166 | BUG(); | ||
167 | } | ||
168 | return; | ||
169 | |||
170 | /* Something tried to access memory that isn't in our memory map. | ||
171 | Fix it, but check if it's kernel or user first. */ | ||
172 | bad_area: | ||
173 | up_read(&mm->mmap_sem); | ||
174 | |||
175 | if (user_mode(regs)) | ||
176 | goto do_sigsegv; | ||
177 | |||
178 | no_context: | ||
179 | /* Are we prepared to handle this fault as an exception? */ | ||
180 | if ((fixup = search_exception_tables(regs->pc)) != 0) { | ||
181 | unsigned long newpc; | ||
182 | newpc = fixup_exception(dpf_reg, fixup, regs->pc); | ||
183 | regs->pc = newpc; | ||
184 | return; | ||
185 | } | ||
186 | |||
187 | /* Oops. The kernel tried to access some bad page. We'll have to | ||
188 | terminate things with extreme prejudice. */ | ||
189 | printk(KERN_ALERT "Unable to handle kernel paging request at " | ||
190 | "virtual address %016lx\n", address); | ||
191 | die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16); | ||
192 | do_exit(SIGKILL); | ||
193 | |||
194 | /* We ran out of memory, or some other thing happened to us that | ||
195 | made us unable to handle the page fault gracefully. */ | ||
196 | out_of_memory: | ||
197 | if (current->pid == 1) { | ||
198 | yield(); | ||
199 | down_read(&mm->mmap_sem); | ||
200 | goto survive; | ||
201 | } | ||
202 | printk(KERN_ALERT "VM: killing process %s(%d)\n", | ||
203 | current->comm, current->pid); | ||
204 | if (!user_mode(regs)) | ||
205 | goto no_context; | ||
206 | do_exit(SIGKILL); | ||
207 | |||
208 | do_sigbus: | ||
209 | /* Send a sigbus, regardless of whether we were in kernel | ||
210 | or user mode. */ | ||
211 | info.si_signo = SIGBUS; | ||
212 | info.si_errno = 0; | ||
213 | info.si_code = BUS_ADRERR; | ||
214 | info.si_addr = (void __user *) address; | ||
215 | force_sig_info(SIGBUS, &info, current); | ||
216 | if (!user_mode(regs)) | ||
217 | goto no_context; | ||
218 | return; | ||
219 | |||
220 | do_sigsegv: | ||
221 | info.si_signo = SIGSEGV; | ||
222 | info.si_errno = 0; | ||
223 | info.si_code = si_code; | ||
224 | info.si_addr = (void __user *) address; | ||
225 | force_sig_info(SIGSEGV, &info, current); | ||
226 | return; | ||
227 | |||
228 | #ifdef CONFIG_ALPHA_LARGE_VMALLOC | ||
229 | vmalloc_fault: | ||
230 | if (user_mode(regs)) | ||
231 | goto do_sigsegv; | ||
232 | else { | ||
233 | /* Synchronize this task's top level page-table | ||
234 | with the "reference" page table from init. */ | ||
235 | long index = pgd_index(address); | ||
236 | pgd_t *pgd, *pgd_k; | ||
237 | |||
238 | pgd = current->active_mm->pgd + index; | ||
239 | pgd_k = swapper_pg_dir + index; | ||
240 | if (!pgd_present(*pgd) && pgd_present(*pgd_k)) { | ||
241 | pgd_val(*pgd) = pgd_val(*pgd_k); | ||
242 | return; | ||
243 | } | ||
244 | goto no_context; | ||
245 | } | ||
246 | #endif | ||
247 | } | ||
diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c new file mode 100644 index 000000000000..90752f6d8867 --- /dev/null +++ b/arch/alpha/mm/init.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/mm/init.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | */ | ||
6 | |||
7 | /* 2.3.x zone allocator, 1999 Andrea Arcangeli <andrea@suse.de> */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/signal.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/ptrace.h> | ||
17 | #include <linux/mman.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/swap.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/bootmem.h> /* max_low_pfn */ | ||
22 | #include <linux/vmalloc.h> | ||
23 | |||
24 | #include <asm/system.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/pgtable.h> | ||
27 | #include <asm/pgalloc.h> | ||
28 | #include <asm/hwrpb.h> | ||
29 | #include <asm/dma.h> | ||
30 | #include <asm/mmu_context.h> | ||
31 | #include <asm/console.h> | ||
32 | #include <asm/tlb.h> | ||
33 | |||
34 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
35 | |||
36 | extern void die_if_kernel(char *,struct pt_regs *,long); | ||
37 | |||
38 | static struct pcb_struct original_pcb; | ||
39 | |||
40 | pgd_t * | ||
41 | pgd_alloc(struct mm_struct *mm) | ||
42 | { | ||
43 | pgd_t *ret, *init; | ||
44 | |||
45 | ret = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); | ||
46 | init = pgd_offset(&init_mm, 0UL); | ||
47 | if (ret) { | ||
48 | #ifdef CONFIG_ALPHA_LARGE_VMALLOC | ||
49 | memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, | ||
50 | (PTRS_PER_PGD - USER_PTRS_PER_PGD - 1)*sizeof(pgd_t)); | ||
51 | #else | ||
52 | pgd_val(ret[PTRS_PER_PGD-2]) = pgd_val(init[PTRS_PER_PGD-2]); | ||
53 | #endif | ||
54 | |||
55 | /* The last PGD entry is the VPTB self-map. */ | ||
56 | pgd_val(ret[PTRS_PER_PGD-1]) | ||
57 | = pte_val(mk_pte(virt_to_page(ret), PAGE_KERNEL)); | ||
58 | } | ||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | pte_t * | ||
63 | pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) | ||
64 | { | ||
65 | pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); | ||
66 | return pte; | ||
67 | } | ||
68 | |||
69 | |||
70 | /* | ||
71 | * BAD_PAGE is the page that is used for page faults when linux | ||
72 | * is out-of-memory. Older versions of linux just did a | ||
73 | * do_exit(), but using this instead means there is less risk | ||
74 | * for a process dying in kernel mode, possibly leaving an inode | ||
75 | * unused etc.. | ||
76 | * | ||
77 | * BAD_PAGETABLE is the accompanying page-table: it is initialized | ||
78 | * to point to BAD_PAGE entries. | ||
79 | * | ||
80 | * ZERO_PAGE is a special page that is used for zero-initialized | ||
81 | * data and COW. | ||
82 | */ | ||
83 | pmd_t * | ||
84 | __bad_pagetable(void) | ||
85 | { | ||
86 | memset((void *) EMPTY_PGT, 0, PAGE_SIZE); | ||
87 | return (pmd_t *) EMPTY_PGT; | ||
88 | } | ||
89 | |||
90 | pte_t | ||
91 | __bad_page(void) | ||
92 | { | ||
93 | memset((void *) EMPTY_PGE, 0, PAGE_SIZE); | ||
94 | return pte_mkdirty(mk_pte(virt_to_page(EMPTY_PGE), PAGE_SHARED)); | ||
95 | } | ||
96 | |||
97 | #ifndef CONFIG_DISCONTIGMEM | ||
98 | void | ||
99 | show_mem(void) | ||
100 | { | ||
101 | long i,free = 0,total = 0,reserved = 0; | ||
102 | long shared = 0, cached = 0; | ||
103 | |||
104 | printk("\nMem-info:\n"); | ||
105 | show_free_areas(); | ||
106 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | ||
107 | i = max_mapnr; | ||
108 | while (i-- > 0) { | ||
109 | total++; | ||
110 | if (PageReserved(mem_map+i)) | ||
111 | reserved++; | ||
112 | else if (PageSwapCache(mem_map+i)) | ||
113 | cached++; | ||
114 | else if (!page_count(mem_map+i)) | ||
115 | free++; | ||
116 | else | ||
117 | shared += page_count(mem_map + i) - 1; | ||
118 | } | ||
119 | printk("%ld pages of RAM\n",total); | ||
120 | printk("%ld free pages\n",free); | ||
121 | printk("%ld reserved pages\n",reserved); | ||
122 | printk("%ld pages shared\n",shared); | ||
123 | printk("%ld pages swap cached\n",cached); | ||
124 | } | ||
125 | #endif | ||
126 | |||
127 | static inline unsigned long | ||
128 | load_PCB(struct pcb_struct *pcb) | ||
129 | { | ||
130 | register unsigned long sp __asm__("$30"); | ||
131 | pcb->ksp = sp; | ||
132 | return __reload_thread(pcb); | ||
133 | } | ||
134 | |||
135 | /* Set up initial PCB, VPTB, and other such nicities. */ | ||
136 | |||
137 | static inline void | ||
138 | switch_to_system_map(void) | ||
139 | { | ||
140 | unsigned long newptbr; | ||
141 | unsigned long original_pcb_ptr; | ||
142 | |||
143 | /* Initialize the kernel's page tables. Linux puts the vptb in | ||
144 | the last slot of the L1 page table. */ | ||
145 | memset(swapper_pg_dir, 0, PAGE_SIZE); | ||
146 | newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT; | ||
147 | pgd_val(swapper_pg_dir[1023]) = | ||
148 | (newptbr << 32) | pgprot_val(PAGE_KERNEL); | ||
149 | |||
150 | /* Set the vptb. This is often done by the bootloader, but | ||
151 | shouldn't be required. */ | ||
152 | if (hwrpb->vptb != 0xfffffffe00000000UL) { | ||
153 | wrvptptr(0xfffffffe00000000UL); | ||
154 | hwrpb->vptb = 0xfffffffe00000000UL; | ||
155 | hwrpb_update_checksum(hwrpb); | ||
156 | } | ||
157 | |||
158 | /* Also set up the real kernel PCB while we're at it. */ | ||
159 | init_thread_info.pcb.ptbr = newptbr; | ||
160 | init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */ | ||
161 | original_pcb_ptr = load_PCB(&init_thread_info.pcb); | ||
162 | tbia(); | ||
163 | |||
164 | /* Save off the contents of the original PCB so that we can | ||
165 | restore the original console's page tables for a clean reboot. | ||
166 | |||
167 | Note that the PCB is supposed to be a physical address, but | ||
168 | since KSEG values also happen to work, folks get confused. | ||
169 | Check this here. */ | ||
170 | |||
171 | if (original_pcb_ptr < PAGE_OFFSET) { | ||
172 | original_pcb_ptr = (unsigned long) | ||
173 | phys_to_virt(original_pcb_ptr); | ||
174 | } | ||
175 | original_pcb = *(struct pcb_struct *) original_pcb_ptr; | ||
176 | } | ||
177 | |||
178 | int callback_init_done; | ||
179 | |||
180 | void * __init | ||
181 | callback_init(void * kernel_end) | ||
182 | { | ||
183 | struct crb_struct * crb; | ||
184 | pgd_t *pgd; | ||
185 | pmd_t *pmd; | ||
186 | void *two_pages; | ||
187 | |||
188 | /* Starting at the HWRPB, locate the CRB. */ | ||
189 | crb = (struct crb_struct *)((char *)hwrpb + hwrpb->crb_offset); | ||
190 | |||
191 | if (alpha_using_srm) { | ||
192 | /* Tell the console whither it is to be remapped. */ | ||
193 | if (srm_fixup(VMALLOC_START, (unsigned long)hwrpb)) | ||
194 | __halt(); /* "We're boned." --Bender */ | ||
195 | |||
196 | /* Edit the procedure descriptors for DISPATCH and FIXUP. */ | ||
197 | crb->dispatch_va = (struct procdesc_struct *) | ||
198 | (VMALLOC_START + (unsigned long)crb->dispatch_va | ||
199 | - crb->map[0].va); | ||
200 | crb->fixup_va = (struct procdesc_struct *) | ||
201 | (VMALLOC_START + (unsigned long)crb->fixup_va | ||
202 | - crb->map[0].va); | ||
203 | } | ||
204 | |||
205 | switch_to_system_map(); | ||
206 | |||
207 | /* Allocate one PGD and one PMD. In the case of SRM, we'll need | ||
208 | these to actually remap the console. There is an assumption | ||
209 | here that only one of each is needed, and this allows for 8MB. | ||
210 | On systems with larger consoles, additional pages will be | ||
211 | allocated as needed during the mapping process. | ||
212 | |||
213 | In the case of not SRM, but not CONFIG_ALPHA_LARGE_VMALLOC, | ||
214 | we need to allocate the PGD we use for vmalloc before we start | ||
215 | forking other tasks. */ | ||
216 | |||
217 | two_pages = (void *) | ||
218 | (((unsigned long)kernel_end + ~PAGE_MASK) & PAGE_MASK); | ||
219 | kernel_end = two_pages + 2*PAGE_SIZE; | ||
220 | memset(two_pages, 0, 2*PAGE_SIZE); | ||
221 | |||
222 | pgd = pgd_offset_k(VMALLOC_START); | ||
223 | pgd_set(pgd, (pmd_t *)two_pages); | ||
224 | pmd = pmd_offset(pgd, VMALLOC_START); | ||
225 | pmd_set(pmd, (pte_t *)(two_pages + PAGE_SIZE)); | ||
226 | |||
227 | if (alpha_using_srm) { | ||
228 | static struct vm_struct console_remap_vm; | ||
229 | unsigned long vaddr = VMALLOC_START; | ||
230 | unsigned long i, j; | ||
231 | |||
232 | /* Set up the third level PTEs and update the virtual | ||
233 | addresses of the CRB entries. */ | ||
234 | for (i = 0; i < crb->map_entries; ++i) { | ||
235 | unsigned long pfn = crb->map[i].pa >> PAGE_SHIFT; | ||
236 | crb->map[i].va = vaddr; | ||
237 | for (j = 0; j < crb->map[i].count; ++j) { | ||
238 | /* Newer console's (especially on larger | ||
239 | systems) may require more pages of | ||
240 | PTEs. Grab additional pages as needed. */ | ||
241 | if (pmd != pmd_offset(pgd, vaddr)) { | ||
242 | memset(kernel_end, 0, PAGE_SIZE); | ||
243 | pmd = pmd_offset(pgd, vaddr); | ||
244 | pmd_set(pmd, (pte_t *)kernel_end); | ||
245 | kernel_end += PAGE_SIZE; | ||
246 | } | ||
247 | set_pte(pte_offset_kernel(pmd, vaddr), | ||
248 | pfn_pte(pfn, PAGE_KERNEL)); | ||
249 | pfn++; | ||
250 | vaddr += PAGE_SIZE; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | /* Let vmalloc know that we've allocated some space. */ | ||
255 | console_remap_vm.flags = VM_ALLOC; | ||
256 | console_remap_vm.addr = (void *) VMALLOC_START; | ||
257 | console_remap_vm.size = vaddr - VMALLOC_START; | ||
258 | vmlist = &console_remap_vm; | ||
259 | } | ||
260 | |||
261 | callback_init_done = 1; | ||
262 | return kernel_end; | ||
263 | } | ||
264 | |||
265 | |||
266 | #ifndef CONFIG_DISCONTIGMEM | ||
267 | /* | ||
268 | * paging_init() sets up the memory map. | ||
269 | */ | ||
270 | void | ||
271 | paging_init(void) | ||
272 | { | ||
273 | unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; | ||
274 | unsigned long dma_pfn, high_pfn; | ||
275 | |||
276 | dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; | ||
277 | high_pfn = max_pfn = max_low_pfn; | ||
278 | |||
279 | if (dma_pfn >= high_pfn) | ||
280 | zones_size[ZONE_DMA] = high_pfn; | ||
281 | else { | ||
282 | zones_size[ZONE_DMA] = dma_pfn; | ||
283 | zones_size[ZONE_NORMAL] = high_pfn - dma_pfn; | ||
284 | } | ||
285 | |||
286 | /* Initialize mem_map[]. */ | ||
287 | free_area_init(zones_size); | ||
288 | |||
289 | /* Initialize the kernel's ZERO_PGE. */ | ||
290 | memset((void *)ZERO_PGE, 0, PAGE_SIZE); | ||
291 | } | ||
292 | #endif /* CONFIG_DISCONTIGMEM */ | ||
293 | |||
294 | #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM) | ||
295 | void | ||
296 | srm_paging_stop (void) | ||
297 | { | ||
298 | /* Move the vptb back to where the SRM console expects it. */ | ||
299 | swapper_pg_dir[1] = swapper_pg_dir[1023]; | ||
300 | tbia(); | ||
301 | wrvptptr(0x200000000UL); | ||
302 | hwrpb->vptb = 0x200000000UL; | ||
303 | hwrpb_update_checksum(hwrpb); | ||
304 | |||
305 | /* Reload the page tables that the console had in use. */ | ||
306 | load_PCB(&original_pcb); | ||
307 | tbia(); | ||
308 | } | ||
309 | #endif | ||
310 | |||
311 | #ifndef CONFIG_DISCONTIGMEM | ||
312 | static void __init | ||
313 | printk_memory_info(void) | ||
314 | { | ||
315 | unsigned long codesize, reservedpages, datasize, initsize, tmp; | ||
316 | extern int page_is_ram(unsigned long) __init; | ||
317 | extern char _text, _etext, _data, _edata; | ||
318 | extern char __init_begin, __init_end; | ||
319 | |||
320 | /* printk all informations */ | ||
321 | reservedpages = 0; | ||
322 | for (tmp = 0; tmp < max_low_pfn; tmp++) | ||
323 | /* | ||
324 | * Only count reserved RAM pages | ||
325 | */ | ||
326 | if (page_is_ram(tmp) && PageReserved(mem_map+tmp)) | ||
327 | reservedpages++; | ||
328 | |||
329 | codesize = (unsigned long) &_etext - (unsigned long) &_text; | ||
330 | datasize = (unsigned long) &_edata - (unsigned long) &_data; | ||
331 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | ||
332 | |||
333 | printk("Memory: %luk/%luk available (%luk kernel code, %luk reserved, %luk data, %luk init)\n", | ||
334 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), | ||
335 | max_mapnr << (PAGE_SHIFT-10), | ||
336 | codesize >> 10, | ||
337 | reservedpages << (PAGE_SHIFT-10), | ||
338 | datasize >> 10, | ||
339 | initsize >> 10); | ||
340 | } | ||
341 | |||
342 | void __init | ||
343 | mem_init(void) | ||
344 | { | ||
345 | max_mapnr = num_physpages = max_low_pfn; | ||
346 | totalram_pages += free_all_bootmem(); | ||
347 | high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); | ||
348 | |||
349 | printk_memory_info(); | ||
350 | } | ||
351 | #endif /* CONFIG_DISCONTIGMEM */ | ||
352 | |||
353 | void | ||
354 | free_reserved_mem(void *start, void *end) | ||
355 | { | ||
356 | void *__start = start; | ||
357 | for (; __start < end; __start += PAGE_SIZE) { | ||
358 | ClearPageReserved(virt_to_page(__start)); | ||
359 | set_page_count(virt_to_page(__start), 1); | ||
360 | free_page((long)__start); | ||
361 | totalram_pages++; | ||
362 | } | ||
363 | } | ||
364 | |||
365 | void | ||
366 | free_initmem(void) | ||
367 | { | ||
368 | extern char __init_begin, __init_end; | ||
369 | |||
370 | free_reserved_mem(&__init_begin, &__init_end); | ||
371 | printk ("Freeing unused kernel memory: %ldk freed\n", | ||
372 | (&__init_end - &__init_begin) >> 10); | ||
373 | } | ||
374 | |||
375 | #ifdef CONFIG_BLK_DEV_INITRD | ||
376 | void | ||
377 | free_initrd_mem(unsigned long start, unsigned long end) | ||
378 | { | ||
379 | free_reserved_mem((void *)start, (void *)end); | ||
380 | printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); | ||
381 | } | ||
382 | #endif | ||
diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c new file mode 100644 index 000000000000..ba81c4422aaf --- /dev/null +++ b/arch/alpha/mm/numa.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* | ||
2 | * linux/arch/alpha/mm/numa.c | ||
3 | * | ||
4 | * DISCONTIGMEM NUMA alpha support. | ||
5 | * | ||
6 | * Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE | ||
7 | */ | ||
8 | |||
9 | #include <linux/config.h> | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/bootmem.h> | ||
14 | #include <linux/swap.h> | ||
15 | #include <linux/initrd.h> | ||
16 | |||
17 | #include <asm/hwrpb.h> | ||
18 | #include <asm/pgalloc.h> | ||
19 | |||
20 | pg_data_t node_data[MAX_NUMNODES]; | ||
21 | bootmem_data_t node_bdata[MAX_NUMNODES]; | ||
22 | |||
23 | #undef DEBUG_DISCONTIG | ||
24 | #ifdef DEBUG_DISCONTIG | ||
25 | #define DBGDCONT(args...) printk(args) | ||
26 | #else | ||
27 | #define DBGDCONT(args...) | ||
28 | #endif | ||
29 | |||
30 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
31 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
32 | #define PFN_PHYS(x) ((x) << PAGE_SHIFT) | ||
33 | #define for_each_mem_cluster(memdesc, cluster, i) \ | ||
34 | for ((cluster) = (memdesc)->cluster, (i) = 0; \ | ||
35 | (i) < (memdesc)->numclusters; (i)++, (cluster)++) | ||
36 | |||
37 | static void __init show_mem_layout(void) | ||
38 | { | ||
39 | struct memclust_struct * cluster; | ||
40 | struct memdesc_struct * memdesc; | ||
41 | int i; | ||
42 | |||
43 | /* Find free clusters, and init and free the bootmem accordingly. */ | ||
44 | memdesc = (struct memdesc_struct *) | ||
45 | (hwrpb->mddt_offset + (unsigned long) hwrpb); | ||
46 | |||
47 | printk("Raw memory layout:\n"); | ||
48 | for_each_mem_cluster(memdesc, cluster, i) { | ||
49 | printk(" memcluster %2d, usage %1lx, start %8lu, end %8lu\n", | ||
50 | i, cluster->usage, cluster->start_pfn, | ||
51 | cluster->start_pfn + cluster->numpages); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | static void __init | ||
56 | setup_memory_node(int nid, void *kernel_end) | ||
57 | { | ||
58 | extern unsigned long mem_size_limit; | ||
59 | struct memclust_struct * cluster; | ||
60 | struct memdesc_struct * memdesc; | ||
61 | unsigned long start_kernel_pfn, end_kernel_pfn; | ||
62 | unsigned long bootmap_size, bootmap_pages, bootmap_start; | ||
63 | unsigned long start, end; | ||
64 | unsigned long node_pfn_start, node_pfn_end; | ||
65 | unsigned long node_min_pfn, node_max_pfn; | ||
66 | int i; | ||
67 | unsigned long node_datasz = PFN_UP(sizeof(pg_data_t)); | ||
68 | int show_init = 0; | ||
69 | |||
70 | /* Find the bounds of current node */ | ||
71 | node_pfn_start = (node_mem_start(nid)) >> PAGE_SHIFT; | ||
72 | node_pfn_end = node_pfn_start + (node_mem_size(nid) >> PAGE_SHIFT); | ||
73 | |||
74 | /* Find free clusters, and init and free the bootmem accordingly. */ | ||
75 | memdesc = (struct memdesc_struct *) | ||
76 | (hwrpb->mddt_offset + (unsigned long) hwrpb); | ||
77 | |||
78 | /* find the bounds of this node (node_min_pfn/node_max_pfn) */ | ||
79 | node_min_pfn = ~0UL; | ||
80 | node_max_pfn = 0UL; | ||
81 | for_each_mem_cluster(memdesc, cluster, i) { | ||
82 | /* Bit 0 is console/PALcode reserved. Bit 1 is | ||
83 | non-volatile memory -- we might want to mark | ||
84 | this for later. */ | ||
85 | if (cluster->usage & 3) | ||
86 | continue; | ||
87 | |||
88 | start = cluster->start_pfn; | ||
89 | end = start + cluster->numpages; | ||
90 | |||
91 | if (start >= node_pfn_end || end <= node_pfn_start) | ||
92 | continue; | ||
93 | |||
94 | if (!show_init) { | ||
95 | show_init = 1; | ||
96 | printk("Initializing bootmem allocator on Node ID %d\n", nid); | ||
97 | } | ||
98 | printk(" memcluster %2d, usage %1lx, start %8lu, end %8lu\n", | ||
99 | i, cluster->usage, cluster->start_pfn, | ||
100 | cluster->start_pfn + cluster->numpages); | ||
101 | |||
102 | if (start < node_pfn_start) | ||
103 | start = node_pfn_start; | ||
104 | if (end > node_pfn_end) | ||
105 | end = node_pfn_end; | ||
106 | |||
107 | if (start < node_min_pfn) | ||
108 | node_min_pfn = start; | ||
109 | if (end > node_max_pfn) | ||
110 | node_max_pfn = end; | ||
111 | } | ||
112 | |||
113 | if (mem_size_limit && node_max_pfn > mem_size_limit) { | ||
114 | static int msg_shown = 0; | ||
115 | if (!msg_shown) { | ||
116 | msg_shown = 1; | ||
117 | printk("setup: forcing memory size to %ldK (from %ldK).\n", | ||
118 | mem_size_limit << (PAGE_SHIFT - 10), | ||
119 | node_max_pfn << (PAGE_SHIFT - 10)); | ||
120 | } | ||
121 | node_max_pfn = mem_size_limit; | ||
122 | } | ||
123 | |||
124 | if (node_min_pfn >= node_max_pfn) | ||
125 | return; | ||
126 | |||
127 | /* Update global {min,max}_low_pfn from node information. */ | ||
128 | if (node_min_pfn < min_low_pfn) | ||
129 | min_low_pfn = node_min_pfn; | ||
130 | if (node_max_pfn > max_low_pfn) | ||
131 | max_pfn = max_low_pfn = node_max_pfn; | ||
132 | |||
133 | num_physpages += node_max_pfn - node_min_pfn; | ||
134 | |||
135 | #if 0 /* we'll try this one again in a little while */ | ||
136 | /* Cute trick to make sure our local node data is on local memory */ | ||
137 | node_data[nid] = (pg_data_t *)(__va(node_min_pfn << PAGE_SHIFT)); | ||
138 | #endif | ||
139 | /* Quasi-mark the pg_data_t as in-use */ | ||
140 | node_min_pfn += node_datasz; | ||
141 | if (node_min_pfn >= node_max_pfn) { | ||
142 | printk(" not enough mem to reserve NODE_DATA"); | ||
143 | return; | ||
144 | } | ||
145 | NODE_DATA(nid)->bdata = &node_bdata[nid]; | ||
146 | |||
147 | printk(" Detected node memory: start %8lu, end %8lu\n", | ||
148 | node_min_pfn, node_max_pfn); | ||
149 | |||
150 | DBGDCONT(" DISCONTIG: node_data[%d] is at 0x%p\n", nid, NODE_DATA(nid)); | ||
151 | DBGDCONT(" DISCONTIG: NODE_DATA(%d)->bdata is at 0x%p\n", nid, NODE_DATA(nid)->bdata); | ||
152 | |||
153 | /* Find the bounds of kernel memory. */ | ||
154 | start_kernel_pfn = PFN_DOWN(KERNEL_START_PHYS); | ||
155 | end_kernel_pfn = PFN_UP(virt_to_phys(kernel_end)); | ||
156 | bootmap_start = -1; | ||
157 | |||
158 | if (!nid && (node_max_pfn < end_kernel_pfn || node_min_pfn > start_kernel_pfn)) | ||
159 | panic("kernel loaded out of ram"); | ||
160 | |||
161 | /* Zone start phys-addr must be 2^(MAX_ORDER-1) aligned. | ||
162 | Note that we round this down, not up - node memory | ||
163 | has much larger alignment than 8Mb, so it's safe. */ | ||
164 | node_min_pfn &= ~((1UL << (MAX_ORDER-1))-1); | ||
165 | |||
166 | /* We need to know how many physically contiguous pages | ||
167 | we'll need for the bootmap. */ | ||
168 | bootmap_pages = bootmem_bootmap_pages(node_max_pfn-node_min_pfn); | ||
169 | |||
170 | /* Now find a good region where to allocate the bootmap. */ | ||
171 | for_each_mem_cluster(memdesc, cluster, i) { | ||
172 | if (cluster->usage & 3) | ||
173 | continue; | ||
174 | |||
175 | start = cluster->start_pfn; | ||
176 | end = start + cluster->numpages; | ||
177 | |||
178 | if (start >= node_max_pfn || end <= node_min_pfn) | ||
179 | continue; | ||
180 | |||
181 | if (end > node_max_pfn) | ||
182 | end = node_max_pfn; | ||
183 | if (start < node_min_pfn) | ||
184 | start = node_min_pfn; | ||
185 | |||
186 | if (start < start_kernel_pfn) { | ||
187 | if (end > end_kernel_pfn | ||
188 | && end - end_kernel_pfn >= bootmap_pages) { | ||
189 | bootmap_start = end_kernel_pfn; | ||
190 | break; | ||
191 | } else if (end > start_kernel_pfn) | ||
192 | end = start_kernel_pfn; | ||
193 | } else if (start < end_kernel_pfn) | ||
194 | start = end_kernel_pfn; | ||
195 | if (end - start >= bootmap_pages) { | ||
196 | bootmap_start = start; | ||
197 | break; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | if (bootmap_start == -1) | ||
202 | panic("couldn't find a contigous place for the bootmap"); | ||
203 | |||
204 | /* Allocate the bootmap and mark the whole MM as reserved. */ | ||
205 | bootmap_size = init_bootmem_node(NODE_DATA(nid), bootmap_start, | ||
206 | node_min_pfn, node_max_pfn); | ||
207 | DBGDCONT(" bootmap_start %lu, bootmap_size %lu, bootmap_pages %lu\n", | ||
208 | bootmap_start, bootmap_size, bootmap_pages); | ||
209 | |||
210 | /* Mark the free regions. */ | ||
211 | for_each_mem_cluster(memdesc, cluster, i) { | ||
212 | if (cluster->usage & 3) | ||
213 | continue; | ||
214 | |||
215 | start = cluster->start_pfn; | ||
216 | end = cluster->start_pfn + cluster->numpages; | ||
217 | |||
218 | if (start >= node_max_pfn || end <= node_min_pfn) | ||
219 | continue; | ||
220 | |||
221 | if (end > node_max_pfn) | ||
222 | end = node_max_pfn; | ||
223 | if (start < node_min_pfn) | ||
224 | start = node_min_pfn; | ||
225 | |||
226 | if (start < start_kernel_pfn) { | ||
227 | if (end > end_kernel_pfn) { | ||
228 | free_bootmem_node(NODE_DATA(nid), PFN_PHYS(start), | ||
229 | (PFN_PHYS(start_kernel_pfn) | ||
230 | - PFN_PHYS(start))); | ||
231 | printk(" freeing pages %ld:%ld\n", | ||
232 | start, start_kernel_pfn); | ||
233 | start = end_kernel_pfn; | ||
234 | } else if (end > start_kernel_pfn) | ||
235 | end = start_kernel_pfn; | ||
236 | } else if (start < end_kernel_pfn) | ||
237 | start = end_kernel_pfn; | ||
238 | if (start >= end) | ||
239 | continue; | ||
240 | |||
241 | free_bootmem_node(NODE_DATA(nid), PFN_PHYS(start), PFN_PHYS(end) - PFN_PHYS(start)); | ||
242 | printk(" freeing pages %ld:%ld\n", start, end); | ||
243 | } | ||
244 | |||
245 | /* Reserve the bootmap memory. */ | ||
246 | reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(bootmap_start), bootmap_size); | ||
247 | printk(" reserving pages %ld:%ld\n", bootmap_start, bootmap_start+PFN_UP(bootmap_size)); | ||
248 | |||
249 | node_set_online(nid); | ||
250 | } | ||
251 | |||
252 | void __init | ||
253 | setup_memory(void *kernel_end) | ||
254 | { | ||
255 | int nid; | ||
256 | |||
257 | show_mem_layout(); | ||
258 | |||
259 | nodes_clear(node_online_map); | ||
260 | |||
261 | min_low_pfn = ~0UL; | ||
262 | max_low_pfn = 0UL; | ||
263 | for (nid = 0; nid < MAX_NUMNODES; nid++) | ||
264 | setup_memory_node(nid, kernel_end); | ||
265 | |||
266 | #ifdef CONFIG_BLK_DEV_INITRD | ||
267 | initrd_start = INITRD_START; | ||
268 | if (initrd_start) { | ||
269 | extern void *move_initrd(unsigned long); | ||
270 | |||
271 | initrd_end = initrd_start+INITRD_SIZE; | ||
272 | printk("Initial ramdisk at: 0x%p (%lu bytes)\n", | ||
273 | (void *) initrd_start, INITRD_SIZE); | ||
274 | |||
275 | if ((void *)initrd_end > phys_to_virt(PFN_PHYS(max_low_pfn))) { | ||
276 | if (!move_initrd(PFN_PHYS(max_low_pfn))) | ||
277 | printk("initrd extends beyond end of memory " | ||
278 | "(0x%08lx > 0x%p)\ndisabling initrd\n", | ||
279 | initrd_end, | ||
280 | phys_to_virt(PFN_PHYS(max_low_pfn))); | ||
281 | } else { | ||
282 | nid = kvaddr_to_nid(initrd_start); | ||
283 | reserve_bootmem_node(NODE_DATA(nid), | ||
284 | virt_to_phys((void *)initrd_start), | ||
285 | INITRD_SIZE); | ||
286 | } | ||
287 | } | ||
288 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
289 | } | ||
290 | |||
291 | void __init paging_init(void) | ||
292 | { | ||
293 | unsigned int nid; | ||
294 | unsigned long zones_size[MAX_NR_ZONES] = {0, }; | ||
295 | unsigned long dma_local_pfn; | ||
296 | |||
297 | /* | ||
298 | * The old global MAX_DMA_ADDRESS per-arch API doesn't fit | ||
299 | * in the NUMA model, for now we convert it to a pfn and | ||
300 | * we interpret this pfn as a local per-node information. | ||
301 | * This issue isn't very important since none of these machines | ||
302 | * have legacy ISA slots anyways. | ||
303 | */ | ||
304 | dma_local_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; | ||
305 | |||
306 | for_each_online_node(nid) { | ||
307 | unsigned long start_pfn = node_bdata[nid].node_boot_start >> PAGE_SHIFT; | ||
308 | unsigned long end_pfn = node_bdata[nid].node_low_pfn; | ||
309 | |||
310 | if (dma_local_pfn >= end_pfn - start_pfn) | ||
311 | zones_size[ZONE_DMA] = end_pfn - start_pfn; | ||
312 | else { | ||
313 | zones_size[ZONE_DMA] = dma_local_pfn; | ||
314 | zones_size[ZONE_NORMAL] = (end_pfn - start_pfn) - dma_local_pfn; | ||
315 | } | ||
316 | free_area_init_node(nid, NODE_DATA(nid), zones_size, start_pfn, NULL); | ||
317 | } | ||
318 | |||
319 | /* Initialize the kernel's ZERO_PGE. */ | ||
320 | memset((void *)ZERO_PGE, 0, PAGE_SIZE); | ||
321 | } | ||
322 | |||
323 | void __init mem_init(void) | ||
324 | { | ||
325 | unsigned long codesize, reservedpages, datasize, initsize, pfn; | ||
326 | extern int page_is_ram(unsigned long) __init; | ||
327 | extern char _text, _etext, _data, _edata; | ||
328 | extern char __init_begin, __init_end; | ||
329 | unsigned long nid, i; | ||
330 | struct page * lmem_map; | ||
331 | |||
332 | high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); | ||
333 | |||
334 | reservedpages = 0; | ||
335 | for_each_online_node(nid) { | ||
336 | /* | ||
337 | * This will free up the bootmem, ie, slot 0 memory | ||
338 | */ | ||
339 | totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); | ||
340 | |||
341 | lmem_map = node_mem_map(nid); | ||
342 | pfn = NODE_DATA(nid)->node_start_pfn; | ||
343 | for (i = 0; i < node_spanned_pages(nid); i++, pfn++) | ||
344 | if (page_is_ram(pfn) && PageReserved(lmem_map+i)) | ||
345 | reservedpages++; | ||
346 | } | ||
347 | |||
348 | codesize = (unsigned long) &_etext - (unsigned long) &_text; | ||
349 | datasize = (unsigned long) &_edata - (unsigned long) &_data; | ||
350 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | ||
351 | |||
352 | printk("Memory: %luk/%luk available (%luk kernel code, %luk reserved, " | ||
353 | "%luk data, %luk init)\n", | ||
354 | (unsigned long)nr_free_pages() << (PAGE_SHIFT-10), | ||
355 | num_physpages << (PAGE_SHIFT-10), | ||
356 | codesize >> 10, | ||
357 | reservedpages << (PAGE_SHIFT-10), | ||
358 | datasize >> 10, | ||
359 | initsize >> 10); | ||
360 | #if 0 | ||
361 | mem_stress(); | ||
362 | #endif | ||
363 | } | ||
364 | |||
365 | void | ||
366 | show_mem(void) | ||
367 | { | ||
368 | long i,free = 0,total = 0,reserved = 0; | ||
369 | long shared = 0, cached = 0; | ||
370 | int nid; | ||
371 | |||
372 | printk("\nMem-info:\n"); | ||
373 | show_free_areas(); | ||
374 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | ||
375 | for_each_online_node(nid) { | ||
376 | struct page * lmem_map = node_mem_map(nid); | ||
377 | i = node_spanned_pages(nid); | ||
378 | while (i-- > 0) { | ||
379 | total++; | ||
380 | if (PageReserved(lmem_map+i)) | ||
381 | reserved++; | ||
382 | else if (PageSwapCache(lmem_map+i)) | ||
383 | cached++; | ||
384 | else if (!page_count(lmem_map+i)) | ||
385 | free++; | ||
386 | else | ||
387 | shared += page_count(lmem_map + i) - 1; | ||
388 | } | ||
389 | } | ||
390 | printk("%ld pages of RAM\n",total); | ||
391 | printk("%ld free pages\n",free); | ||
392 | printk("%ld reserved pages\n",reserved); | ||
393 | printk("%ld pages shared\n",shared); | ||
394 | printk("%ld pages swap cached\n",cached); | ||
395 | } | ||
diff --git a/arch/alpha/mm/remap.c b/arch/alpha/mm/remap.c new file mode 100644 index 000000000000..19817ad3d89b --- /dev/null +++ b/arch/alpha/mm/remap.c | |||
@@ -0,0 +1,90 @@ | |||
1 | #include <linux/vmalloc.h> | ||
2 | #include <asm/pgalloc.h> | ||
3 | #include <asm/cacheflush.h> | ||
4 | |||
5 | /* called with the page_table_lock held */ | ||
6 | static inline void | ||
7 | remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, | ||
8 | unsigned long phys_addr, unsigned long flags) | ||
9 | { | ||
10 | unsigned long end; | ||
11 | unsigned long pfn; | ||
12 | |||
13 | address &= ~PMD_MASK; | ||
14 | end = address + size; | ||
15 | if (end > PMD_SIZE) | ||
16 | end = PMD_SIZE; | ||
17 | if (address >= end) | ||
18 | BUG(); | ||
19 | pfn = phys_addr >> PAGE_SHIFT; | ||
20 | do { | ||
21 | if (!pte_none(*pte)) { | ||
22 | printk("remap_area_pte: page already exists\n"); | ||
23 | BUG(); | ||
24 | } | ||
25 | set_pte(pte, pfn_pte(pfn, | ||
26 | __pgprot(_PAGE_VALID | _PAGE_ASM | | ||
27 | _PAGE_KRE | _PAGE_KWE | flags))); | ||
28 | address += PAGE_SIZE; | ||
29 | pfn++; | ||
30 | pte++; | ||
31 | } while (address && (address < end)); | ||
32 | } | ||
33 | |||
34 | /* called with the page_table_lock held */ | ||
35 | static inline int | ||
36 | remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, | ||
37 | unsigned long phys_addr, unsigned long flags) | ||
38 | { | ||
39 | unsigned long end; | ||
40 | |||
41 | address &= ~PGDIR_MASK; | ||
42 | end = address + size; | ||
43 | if (end > PGDIR_SIZE) | ||
44 | end = PGDIR_SIZE; | ||
45 | phys_addr -= address; | ||
46 | if (address >= end) | ||
47 | BUG(); | ||
48 | do { | ||
49 | pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); | ||
50 | if (!pte) | ||
51 | return -ENOMEM; | ||
52 | remap_area_pte(pte, address, end - address, | ||
53 | address + phys_addr, flags); | ||
54 | address = (address + PMD_SIZE) & PMD_MASK; | ||
55 | pmd++; | ||
56 | } while (address && (address < end)); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | int | ||
61 | __alpha_remap_area_pages(unsigned long address, unsigned long phys_addr, | ||
62 | unsigned long size, unsigned long flags) | ||
63 | { | ||
64 | pgd_t * dir; | ||
65 | int error = 0; | ||
66 | unsigned long end = address + size; | ||
67 | |||
68 | phys_addr -= address; | ||
69 | dir = pgd_offset(&init_mm, address); | ||
70 | flush_cache_all(); | ||
71 | if (address >= end) | ||
72 | BUG(); | ||
73 | spin_lock(&init_mm.page_table_lock); | ||
74 | do { | ||
75 | pmd_t *pmd; | ||
76 | pmd = pmd_alloc(&init_mm, dir, address); | ||
77 | error = -ENOMEM; | ||
78 | if (!pmd) | ||
79 | break; | ||
80 | if (remap_area_pmd(pmd, address, end - address, | ||
81 | phys_addr + address, flags)) | ||
82 | break; | ||
83 | error = 0; | ||
84 | address = (address + PGDIR_SIZE) & PGDIR_MASK; | ||
85 | dir++; | ||
86 | } while (address && (address < end)); | ||
87 | spin_unlock(&init_mm.page_table_lock); | ||
88 | return error; | ||
89 | } | ||
90 | |||
diff --git a/arch/alpha/oprofile/Kconfig b/arch/alpha/oprofile/Kconfig new file mode 100644 index 000000000000..5ade19801b97 --- /dev/null +++ b/arch/alpha/oprofile/Kconfig | |||
@@ -0,0 +1,23 @@ | |||
1 | |||
2 | menu "Profiling support" | ||
3 | depends on EXPERIMENTAL | ||
4 | |||
5 | config PROFILING | ||
6 | bool "Profiling support (EXPERIMENTAL)" | ||
7 | help | ||
8 | Say Y here to enable the extended profiling support mechanisms used | ||
9 | by profilers such as OProfile. | ||
10 | |||
11 | |||
12 | config OPROFILE | ||
13 | tristate "OProfile system profiling (EXPERIMENTAL)" | ||
14 | depends on PROFILING | ||
15 | help | ||
16 | OProfile is a profiling system capable of profiling the | ||
17 | whole system, include the kernel, kernel modules, libraries, | ||
18 | and applications. | ||
19 | |||
20 | If unsure, say N. | ||
21 | |||
22 | endmenu | ||
23 | |||
diff --git a/arch/alpha/oprofile/Makefile b/arch/alpha/oprofile/Makefile new file mode 100644 index 000000000000..4aa56247bdc6 --- /dev/null +++ b/arch/alpha/oprofile/Makefile | |||
@@ -0,0 +1,19 @@ | |||
1 | EXTRA_CFLAGS := -Werror -Wno-sign-compare | ||
2 | |||
3 | obj-$(CONFIG_OPROFILE) += oprofile.o | ||
4 | |||
5 | DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ | ||
6 | oprof.o cpu_buffer.o buffer_sync.o \ | ||
7 | event_buffer.o oprofile_files.o \ | ||
8 | oprofilefs.o oprofile_stats.o \ | ||
9 | timer_int.o ) | ||
10 | |||
11 | oprofile-y := $(DRIVER_OBJS) common.o | ||
12 | oprofile-$(CONFIG_ALPHA_GENERIC) += op_model_ev4.o \ | ||
13 | op_model_ev5.o \ | ||
14 | op_model_ev6.o \ | ||
15 | op_model_ev67.o | ||
16 | oprofile-$(CONFIG_ALPHA_EV4) += op_model_ev4.o | ||
17 | oprofile-$(CONFIG_ALPHA_EV5) += op_model_ev5.o | ||
18 | oprofile-$(CONFIG_ALPHA_EV6) += op_model_ev6.o \ | ||
19 | op_model_ev67.o | ||
diff --git a/arch/alpha/oprofile/common.c b/arch/alpha/oprofile/common.c new file mode 100644 index 000000000000..908eb4af8dec --- /dev/null +++ b/arch/alpha/oprofile/common.c | |||
@@ -0,0 +1,189 @@ | |||
1 | /** | ||
2 | * @file arch/alpha/oprofile/common.c | ||
3 | * | ||
4 | * @remark Copyright 2002 OProfile authors | ||
5 | * @remark Read the file COPYING | ||
6 | * | ||
7 | * @author Richard Henderson <rth@twiddle.net> | ||
8 | */ | ||
9 | |||
10 | #include <linux/oprofile.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/smp.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <asm/ptrace.h> | ||
15 | #include <asm/system.h> | ||
16 | |||
17 | #include "op_impl.h" | ||
18 | |||
19 | extern struct op_axp_model op_model_ev4 __attribute__((weak)); | ||
20 | extern struct op_axp_model op_model_ev5 __attribute__((weak)); | ||
21 | extern struct op_axp_model op_model_pca56 __attribute__((weak)); | ||
22 | extern struct op_axp_model op_model_ev6 __attribute__((weak)); | ||
23 | extern struct op_axp_model op_model_ev67 __attribute__((weak)); | ||
24 | |||
25 | static struct op_axp_model *model; | ||
26 | |||
27 | extern void (*perf_irq)(unsigned long, struct pt_regs *); | ||
28 | static void (*save_perf_irq)(unsigned long, struct pt_regs *); | ||
29 | |||
30 | static struct op_counter_config ctr[20]; | ||
31 | static struct op_system_config sys; | ||
32 | static struct op_register_config reg; | ||
33 | |||
34 | /* Called from do_entInt to handle the performance monitor interrupt. */ | ||
35 | |||
36 | static void | ||
37 | op_handle_interrupt(unsigned long which, struct pt_regs *regs) | ||
38 | { | ||
39 | model->handle_interrupt(which, regs, ctr); | ||
40 | |||
41 | /* If the user has selected an interrupt frequency that is | ||
42 | not exactly the width of the counter, write a new value | ||
43 | into the counter such that it'll overflow after N more | ||
44 | events. */ | ||
45 | if ((reg.need_reset >> which) & 1) | ||
46 | model->reset_ctr(®, which); | ||
47 | } | ||
48 | |||
49 | static int | ||
50 | op_axp_setup(void) | ||
51 | { | ||
52 | unsigned long i, e; | ||
53 | |||
54 | /* Install our interrupt handler into the existing hook. */ | ||
55 | save_perf_irq = perf_irq; | ||
56 | perf_irq = op_handle_interrupt; | ||
57 | |||
58 | /* Compute the mask of enabled counters. */ | ||
59 | for (i = e = 0; i < model->num_counters; ++i) | ||
60 | if (ctr[i].enabled) | ||
61 | e |= 1 << i; | ||
62 | reg.enable = e; | ||
63 | |||
64 | /* Pre-compute the values to stuff in the hardware registers. */ | ||
65 | model->reg_setup(®, ctr, &sys); | ||
66 | |||
67 | /* Configure the registers on all cpus. */ | ||
68 | smp_call_function(model->cpu_setup, ®, 0, 1); | ||
69 | model->cpu_setup(®); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static void | ||
74 | op_axp_shutdown(void) | ||
75 | { | ||
76 | /* Remove our interrupt handler. We may be removing this module. */ | ||
77 | perf_irq = save_perf_irq; | ||
78 | } | ||
79 | |||
80 | static void | ||
81 | op_axp_cpu_start(void *dummy) | ||
82 | { | ||
83 | wrperfmon(1, reg.enable); | ||
84 | } | ||
85 | |||
86 | static int | ||
87 | op_axp_start(void) | ||
88 | { | ||
89 | smp_call_function(op_axp_cpu_start, NULL, 0, 1); | ||
90 | op_axp_cpu_start(NULL); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static inline void | ||
95 | op_axp_cpu_stop(void *dummy) | ||
96 | { | ||
97 | /* Disable performance monitoring for all counters. */ | ||
98 | wrperfmon(0, -1); | ||
99 | } | ||
100 | |||
101 | static void | ||
102 | op_axp_stop(void) | ||
103 | { | ||
104 | smp_call_function(op_axp_cpu_stop, NULL, 0, 1); | ||
105 | op_axp_cpu_stop(NULL); | ||
106 | } | ||
107 | |||
108 | static int | ||
109 | op_axp_create_files(struct super_block * sb, struct dentry * root) | ||
110 | { | ||
111 | int i; | ||
112 | |||
113 | for (i = 0; i < model->num_counters; ++i) { | ||
114 | struct dentry *dir; | ||
115 | char buf[3]; | ||
116 | |||
117 | snprintf(buf, sizeof buf, "%d", i); | ||
118 | dir = oprofilefs_mkdir(sb, root, buf); | ||
119 | |||
120 | oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled); | ||
121 | oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event); | ||
122 | oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count); | ||
123 | /* Dummies. */ | ||
124 | oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel); | ||
125 | oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user); | ||
126 | oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask); | ||
127 | } | ||
128 | |||
129 | if (model->can_set_proc_mode) { | ||
130 | oprofilefs_create_ulong(sb, root, "enable_pal", | ||
131 | &sys.enable_pal); | ||
132 | oprofilefs_create_ulong(sb, root, "enable_kernel", | ||
133 | &sys.enable_kernel); | ||
134 | oprofilefs_create_ulong(sb, root, "enable_user", | ||
135 | &sys.enable_user); | ||
136 | } | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | int __init | ||
142 | oprofile_arch_init(struct oprofile_operations *ops) | ||
143 | { | ||
144 | struct op_axp_model *lmodel = NULL; | ||
145 | |||
146 | switch (implver()) { | ||
147 | case IMPLVER_EV4: | ||
148 | lmodel = &op_model_ev4; | ||
149 | break; | ||
150 | case IMPLVER_EV5: | ||
151 | /* 21164PC has a slightly different set of events. | ||
152 | Recognize the chip by the presence of the MAX insns. */ | ||
153 | if (!amask(AMASK_MAX)) | ||
154 | lmodel = &op_model_pca56; | ||
155 | else | ||
156 | lmodel = &op_model_ev5; | ||
157 | break; | ||
158 | case IMPLVER_EV6: | ||
159 | /* 21264A supports ProfileMe. | ||
160 | Recognize the chip by the presence of the CIX insns. */ | ||
161 | if (!amask(AMASK_CIX)) | ||
162 | lmodel = &op_model_ev67; | ||
163 | else | ||
164 | lmodel = &op_model_ev6; | ||
165 | break; | ||
166 | } | ||
167 | |||
168 | if (!lmodel) | ||
169 | return -ENODEV; | ||
170 | model = lmodel; | ||
171 | |||
172 | ops->create_files = op_axp_create_files; | ||
173 | ops->setup = op_axp_setup; | ||
174 | ops->shutdown = op_axp_shutdown; | ||
175 | ops->start = op_axp_start; | ||
176 | ops->stop = op_axp_stop; | ||
177 | ops->cpu_type = lmodel->cpu_type; | ||
178 | |||
179 | printk(KERN_INFO "oprofile: using %s performance monitoring.\n", | ||
180 | lmodel->cpu_type); | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | |||
186 | void | ||
187 | oprofile_arch_exit(void) | ||
188 | { | ||
189 | } | ||
diff --git a/arch/alpha/oprofile/op_impl.h b/arch/alpha/oprofile/op_impl.h new file mode 100644 index 000000000000..6b97893c1a80 --- /dev/null +++ b/arch/alpha/oprofile/op_impl.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /** | ||
2 | * @file arch/alpha/oprofile/op_impl.h | ||
3 | * | ||
4 | * @remark Copyright 2002 OProfile authors | ||
5 | * @remark Read the file COPYING | ||
6 | * | ||
7 | * @author Richard Henderson <rth@twiddle.net> | ||
8 | */ | ||
9 | |||
10 | #ifndef OP_IMPL_H | ||
11 | #define OP_IMPL_H 1 | ||
12 | |||
13 | /* Per-counter configuration as set via oprofilefs. */ | ||
14 | struct op_counter_config { | ||
15 | unsigned long enabled; | ||
16 | unsigned long event; | ||
17 | unsigned long count; | ||
18 | /* Dummies because I am too lazy to hack the userspace tools. */ | ||
19 | unsigned long kernel; | ||
20 | unsigned long user; | ||
21 | unsigned long unit_mask; | ||
22 | }; | ||
23 | |||
24 | /* System-wide configuration as set via oprofilefs. */ | ||
25 | struct op_system_config { | ||
26 | unsigned long enable_pal; | ||
27 | unsigned long enable_kernel; | ||
28 | unsigned long enable_user; | ||
29 | }; | ||
30 | |||
31 | /* Cached values for the various performance monitoring registers. */ | ||
32 | struct op_register_config { | ||
33 | unsigned long enable; | ||
34 | unsigned long mux_select; | ||
35 | unsigned long proc_mode; | ||
36 | unsigned long freq; | ||
37 | unsigned long reset_values; | ||
38 | unsigned long need_reset; | ||
39 | }; | ||
40 | |||
41 | /* Per-architecture configury and hooks. */ | ||
42 | struct op_axp_model { | ||
43 | void (*reg_setup) (struct op_register_config *, | ||
44 | struct op_counter_config *, | ||
45 | struct op_system_config *); | ||
46 | void (*cpu_setup) (void *); | ||
47 | void (*reset_ctr) (struct op_register_config *, unsigned long); | ||
48 | void (*handle_interrupt) (unsigned long, struct pt_regs *, | ||
49 | struct op_counter_config *); | ||
50 | char *cpu_type; | ||
51 | unsigned char num_counters; | ||
52 | unsigned char can_set_proc_mode; | ||
53 | }; | ||
54 | |||
55 | #endif | ||
diff --git a/arch/alpha/oprofile/op_model_ev4.c b/arch/alpha/oprofile/op_model_ev4.c new file mode 100644 index 000000000000..80d764dbf22f --- /dev/null +++ b/arch/alpha/oprofile/op_model_ev4.c | |||
@@ -0,0 +1,116 @@ | |||
1 | /** | ||
2 | * @file arch/alpha/oprofile/op_model_ev4.c | ||
3 | * | ||
4 | * @remark Copyright 2002 OProfile authors | ||
5 | * @remark Read the file COPYING | ||
6 | * | ||
7 | * @author Richard Henderson <rth@twiddle.net> | ||
8 | */ | ||
9 | |||
10 | #include <linux/oprofile.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/smp.h> | ||
13 | #include <asm/ptrace.h> | ||
14 | #include <asm/system.h> | ||
15 | |||
16 | #include "op_impl.h" | ||
17 | |||
18 | |||
19 | /* Compute all of the registers in preparation for enabling profiling. */ | ||
20 | |||
21 | static void | ||
22 | ev4_reg_setup(struct op_register_config *reg, | ||
23 | struct op_counter_config *ctr, | ||
24 | struct op_system_config *sys) | ||
25 | { | ||
26 | unsigned long ctl = 0, count, hilo; | ||
27 | |||
28 | /* Select desired events. We've mapped the event numbers | ||
29 | such that they fit directly into the event selection fields. | ||
30 | |||
31 | Note that there is no "off" setting. In both cases we select | ||
32 | the EXTERNAL event source, hoping that it'll be the lowest | ||
33 | frequency, and set the frequency counter to LOW. The interrupts | ||
34 | for these "disabled" counter overflows are ignored by the | ||
35 | interrupt handler. | ||
36 | |||
37 | This is most irritating, because the hardware *can* enable and | ||
38 | disable the interrupts for these counters independently, but the | ||
39 | wrperfmon interface doesn't allow it. */ | ||
40 | |||
41 | ctl |= (ctr[0].enabled ? ctr[0].event << 8 : 14 << 8); | ||
42 | ctl |= (ctr[1].enabled ? (ctr[1].event - 16) << 32 : 7ul << 32); | ||
43 | |||
44 | /* EV4 can not read or write its counter registers. The only | ||
45 | thing one can do at all is see if you overflow and get an | ||
46 | interrupt. We can set the width of the counters, to some | ||
47 | extent. Take the interrupt count selected by the user, | ||
48 | map it onto one of the possible values, and write it back. */ | ||
49 | |||
50 | count = ctr[0].count; | ||
51 | if (count <= 4096) | ||
52 | count = 4096, hilo = 1; | ||
53 | else | ||
54 | count = 65536, hilo = 0; | ||
55 | ctr[0].count = count; | ||
56 | ctl |= (ctr[0].enabled && hilo) << 3; | ||
57 | |||
58 | count = ctr[1].count; | ||
59 | if (count <= 256) | ||
60 | count = 256, hilo = 1; | ||
61 | else | ||
62 | count = 4096, hilo = 0; | ||
63 | ctr[1].count = count; | ||
64 | ctl |= (ctr[1].enabled && hilo); | ||
65 | |||
66 | reg->mux_select = ctl; | ||
67 | |||
68 | /* Select performance monitoring options. */ | ||
69 | /* ??? Need to come up with some mechanism to trace only | ||
70 | selected processes. EV4 does not have a mechanism to | ||
71 | select kernel or user mode only. For now, enable always. */ | ||
72 | reg->proc_mode = 0; | ||
73 | |||
74 | /* Frequency is folded into mux_select for EV4. */ | ||
75 | reg->freq = 0; | ||
76 | |||
77 | /* See above regarding no writes. */ | ||
78 | reg->reset_values = 0; | ||
79 | reg->need_reset = 0; | ||
80 | |||
81 | } | ||
82 | |||
83 | /* Program all of the registers in preparation for enabling profiling. */ | ||
84 | |||
85 | static void | ||
86 | ev4_cpu_setup(void *x) | ||
87 | { | ||
88 | struct op_register_config *reg = x; | ||
89 | |||
90 | wrperfmon(2, reg->mux_select); | ||
91 | wrperfmon(3, reg->proc_mode); | ||
92 | } | ||
93 | |||
94 | static void | ||
95 | ev4_handle_interrupt(unsigned long which, struct pt_regs *regs, | ||
96 | struct op_counter_config *ctr) | ||
97 | { | ||
98 | /* EV4 can't properly disable counters individually. | ||
99 | Discard "disabled" events now. */ | ||
100 | if (!ctr[which].enabled) | ||
101 | return; | ||
102 | |||
103 | /* Record the sample. */ | ||
104 | oprofile_add_sample(regs, which); | ||
105 | } | ||
106 | |||
107 | |||
108 | struct op_axp_model op_model_ev4 = { | ||
109 | .reg_setup = ev4_reg_setup, | ||
110 | .cpu_setup = ev4_cpu_setup, | ||
111 | .reset_ctr = NULL, | ||
112 | .handle_interrupt = ev4_handle_interrupt, | ||
113 | .cpu_type = "alpha/ev4", | ||
114 | .num_counters = 2, | ||
115 | .can_set_proc_mode = 0, | ||
116 | }; | ||
diff --git a/arch/alpha/oprofile/op_model_ev5.c b/arch/alpha/oprofile/op_model_ev5.c new file mode 100644 index 000000000000..ceea6e1ad79a --- /dev/null +++ b/arch/alpha/oprofile/op_model_ev5.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /** | ||
2 | * @file arch/alpha/oprofile/op_model_ev5.c | ||
3 | * | ||
4 | * @remark Copyright 2002 OProfile authors | ||
5 | * @remark Read the file COPYING | ||
6 | * | ||
7 | * @author Richard Henderson <rth@twiddle.net> | ||
8 | */ | ||
9 | |||
10 | #include <linux/oprofile.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/smp.h> | ||
13 | #include <asm/ptrace.h> | ||
14 | #include <asm/system.h> | ||
15 | |||
16 | #include "op_impl.h" | ||
17 | |||
18 | |||
19 | /* Compute all of the registers in preparation for enabling profiling. | ||
20 | |||
21 | The 21164 (EV5) and 21164PC (PCA65) vary in the bit placement and | ||
22 | meaning of the "CBOX" events. Given that we don't care about meaning | ||
23 | at this point, arrange for the difference in bit placement to be | ||
24 | handled by common code. */ | ||
25 | |||
26 | static void | ||
27 | common_reg_setup(struct op_register_config *reg, | ||
28 | struct op_counter_config *ctr, | ||
29 | struct op_system_config *sys, | ||
30 | int cbox1_ofs, int cbox2_ofs) | ||
31 | { | ||
32 | int i, ctl, reset, need_reset; | ||
33 | |||
34 | /* Select desired events. The event numbers are selected such | ||
35 | that they map directly into the event selection fields: | ||
36 | |||
37 | PCSEL0: 0, 1 | ||
38 | PCSEL1: 24-39 | ||
39 | CBOX1: 40-47 | ||
40 | PCSEL2: 48-63 | ||
41 | CBOX2: 64-71 | ||
42 | |||
43 | There are two special cases, in that CYCLES can be measured | ||
44 | on PCSEL[02], and SCACHE_WRITE can be measured on CBOX[12]. | ||
45 | These event numbers are canonicalizes to their first appearance. */ | ||
46 | |||
47 | ctl = 0; | ||
48 | for (i = 0; i < 3; ++i) { | ||
49 | unsigned long event = ctr[i].event; | ||
50 | if (!ctr[i].enabled) | ||
51 | continue; | ||
52 | |||
53 | /* Remap the duplicate events, as described above. */ | ||
54 | if (i == 2) { | ||
55 | if (event == 0) | ||
56 | event = 12+48; | ||
57 | else if (event == 2+41) | ||
58 | event = 4+65; | ||
59 | } | ||
60 | |||
61 | /* Convert the event numbers onto mux_select bit mask. */ | ||
62 | if (event < 2) | ||
63 | ctl |= event << 31; | ||
64 | else if (event < 24) | ||
65 | /* error */; | ||
66 | else if (event < 40) | ||
67 | ctl |= (event - 24) << 4; | ||
68 | else if (event < 48) | ||
69 | ctl |= (event - 40) << cbox1_ofs | 15 << 4; | ||
70 | else if (event < 64) | ||
71 | ctl |= event - 48; | ||
72 | else if (event < 72) | ||
73 | ctl |= (event - 64) << cbox2_ofs | 15; | ||
74 | } | ||
75 | reg->mux_select = ctl; | ||
76 | |||
77 | /* Select processor mode. */ | ||
78 | /* ??? Need to come up with some mechanism to trace only selected | ||
79 | processes. For now select from pal, kernel and user mode. */ | ||
80 | ctl = 0; | ||
81 | ctl |= !sys->enable_pal << 9; | ||
82 | ctl |= !sys->enable_kernel << 8; | ||
83 | ctl |= !sys->enable_user << 30; | ||
84 | reg->proc_mode = ctl; | ||
85 | |||
86 | /* Select interrupt frequencies. Take the interrupt count selected | ||
87 | by the user, and map it onto one of the possible counter widths. | ||
88 | If the user value is in between, compute a value to which the | ||
89 | counter is reset at each interrupt. */ | ||
90 | |||
91 | ctl = reset = need_reset = 0; | ||
92 | for (i = 0; i < 3; ++i) { | ||
93 | unsigned long max, hilo, count = ctr[i].count; | ||
94 | if (!ctr[i].enabled) | ||
95 | continue; | ||
96 | |||
97 | if (count <= 256) | ||
98 | count = 256, hilo = 3, max = 256; | ||
99 | else { | ||
100 | max = (i == 2 ? 16384 : 65536); | ||
101 | hilo = 2; | ||
102 | if (count > max) | ||
103 | count = max; | ||
104 | } | ||
105 | ctr[i].count = count; | ||
106 | |||
107 | ctl |= hilo << (8 - i*2); | ||
108 | reset |= (max - count) << (48 - 16*i); | ||
109 | if (count != max) | ||
110 | need_reset |= 1 << i; | ||
111 | } | ||
112 | reg->freq = ctl; | ||
113 | reg->reset_values = reset; | ||
114 | reg->need_reset = need_reset; | ||
115 | } | ||
116 | |||
117 | static void | ||
118 | ev5_reg_setup(struct op_register_config *reg, | ||
119 | struct op_counter_config *ctr, | ||
120 | struct op_system_config *sys) | ||
121 | { | ||
122 | common_reg_setup(reg, ctr, sys, 19, 22); | ||
123 | } | ||
124 | |||
125 | static void | ||
126 | pca56_reg_setup(struct op_register_config *reg, | ||
127 | struct op_counter_config *ctr, | ||
128 | struct op_system_config *sys) | ||
129 | { | ||
130 | common_reg_setup(reg, ctr, sys, 8, 11); | ||
131 | } | ||
132 | |||
133 | /* Program all of the registers in preparation for enabling profiling. */ | ||
134 | |||
135 | static void | ||
136 | ev5_cpu_setup (void *x) | ||
137 | { | ||
138 | struct op_register_config *reg = x; | ||
139 | |||
140 | wrperfmon(2, reg->mux_select); | ||
141 | wrperfmon(3, reg->proc_mode); | ||
142 | wrperfmon(4, reg->freq); | ||
143 | wrperfmon(6, reg->reset_values); | ||
144 | } | ||
145 | |||
146 | /* CTR is a counter for which the user has requested an interrupt count | ||
147 | in between one of the widths selectable in hardware. Reset the count | ||
148 | for CTR to the value stored in REG->RESET_VALUES. | ||
149 | |||
150 | For EV5, this means disabling profiling, reading the current values, | ||
151 | masking in the value for the desired register, writing, then turning | ||
152 | profiling back on. | ||
153 | |||
154 | This can be streamlined if profiling is only enabled for user mode. | ||
155 | In that case we know that the counters are not currently incrementing | ||
156 | (due to being in kernel mode). */ | ||
157 | |||
158 | static void | ||
159 | ev5_reset_ctr(struct op_register_config *reg, unsigned long ctr) | ||
160 | { | ||
161 | unsigned long values, mask, not_pk, reset_values; | ||
162 | |||
163 | mask = (ctr == 0 ? 0xfffful << 48 | ||
164 | : ctr == 1 ? 0xfffful << 32 | ||
165 | : 0x3fff << 16); | ||
166 | |||
167 | not_pk = 1 << 9 | 1 << 8; | ||
168 | |||
169 | reset_values = reg->reset_values; | ||
170 | |||
171 | if ((reg->proc_mode & not_pk) == not_pk) { | ||
172 | values = wrperfmon(5, 0); | ||
173 | values = (reset_values & mask) | (values & ~mask & -2); | ||
174 | wrperfmon(6, values); | ||
175 | } else { | ||
176 | wrperfmon(0, -1); | ||
177 | values = wrperfmon(5, 0); | ||
178 | values = (reset_values & mask) | (values & ~mask & -2); | ||
179 | wrperfmon(6, values); | ||
180 | wrperfmon(1, reg->enable); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | static void | ||
185 | ev5_handle_interrupt(unsigned long which, struct pt_regs *regs, | ||
186 | struct op_counter_config *ctr) | ||
187 | { | ||
188 | /* Record the sample. */ | ||
189 | oprofile_add_sample(regs, which); | ||
190 | } | ||
191 | |||
192 | |||
193 | struct op_axp_model op_model_ev5 = { | ||
194 | .reg_setup = ev5_reg_setup, | ||
195 | .cpu_setup = ev5_cpu_setup, | ||
196 | .reset_ctr = ev5_reset_ctr, | ||
197 | .handle_interrupt = ev5_handle_interrupt, | ||
198 | .cpu_type = "alpha/ev5", | ||
199 | .num_counters = 3, | ||
200 | .can_set_proc_mode = 1, | ||
201 | }; | ||
202 | |||
203 | struct op_axp_model op_model_pca56 = { | ||
204 | .reg_setup = pca56_reg_setup, | ||
205 | .cpu_setup = ev5_cpu_setup, | ||
206 | .reset_ctr = ev5_reset_ctr, | ||
207 | .handle_interrupt = ev5_handle_interrupt, | ||
208 | .cpu_type = "alpha/pca56", | ||
209 | .num_counters = 3, | ||
210 | .can_set_proc_mode = 1, | ||
211 | }; | ||
diff --git a/arch/alpha/oprofile/op_model_ev6.c b/arch/alpha/oprofile/op_model_ev6.c new file mode 100644 index 000000000000..0869f85f5748 --- /dev/null +++ b/arch/alpha/oprofile/op_model_ev6.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /** | ||
2 | * @file arch/alpha/oprofile/op_model_ev6.c | ||
3 | * | ||
4 | * @remark Copyright 2002 OProfile authors | ||
5 | * @remark Read the file COPYING | ||
6 | * | ||
7 | * @author Richard Henderson <rth@twiddle.net> | ||
8 | */ | ||
9 | |||
10 | #include <linux/oprofile.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/smp.h> | ||
13 | #include <asm/ptrace.h> | ||
14 | #include <asm/system.h> | ||
15 | |||
16 | #include "op_impl.h" | ||
17 | |||
18 | |||
19 | /* Compute all of the registers in preparation for enabling profiling. */ | ||
20 | |||
21 | static void | ||
22 | ev6_reg_setup(struct op_register_config *reg, | ||
23 | struct op_counter_config *ctr, | ||
24 | struct op_system_config *sys) | ||
25 | { | ||
26 | unsigned long ctl, reset, need_reset, i; | ||
27 | |||
28 | /* Select desired events. We've mapped the event numbers | ||
29 | such that they fit directly into the event selection fields. */ | ||
30 | ctl = 0; | ||
31 | if (ctr[0].enabled && ctr[0].event) | ||
32 | ctl |= (ctr[0].event & 1) << 4; | ||
33 | if (ctr[1].enabled) | ||
34 | ctl |= (ctr[1].event - 2) & 15; | ||
35 | reg->mux_select = ctl; | ||
36 | |||
37 | /* Select logging options. */ | ||
38 | /* ??? Need to come up with some mechanism to trace only | ||
39 | selected processes. EV6 does not have a mechanism to | ||
40 | select kernel or user mode only. For now, enable always. */ | ||
41 | reg->proc_mode = 0; | ||
42 | |||
43 | /* EV6 cannot change the width of the counters as with the | ||
44 | other implementations. But fortunately, we can write to | ||
45 | the counters and set the value such that it will overflow | ||
46 | at the right time. */ | ||
47 | reset = need_reset = 0; | ||
48 | for (i = 0; i < 2; ++i) { | ||
49 | unsigned long count = ctr[i].count; | ||
50 | if (!ctr[i].enabled) | ||
51 | continue; | ||
52 | |||
53 | if (count > 0x100000) | ||
54 | count = 0x100000; | ||
55 | ctr[i].count = count; | ||
56 | reset |= (0x100000 - count) << (i ? 6 : 28); | ||
57 | if (count != 0x100000) | ||
58 | need_reset |= 1 << i; | ||
59 | } | ||
60 | reg->reset_values = reset; | ||
61 | reg->need_reset = need_reset; | ||
62 | } | ||
63 | |||
64 | /* Program all of the registers in preparation for enabling profiling. */ | ||
65 | |||
66 | static void | ||
67 | ev6_cpu_setup (void *x) | ||
68 | { | ||
69 | struct op_register_config *reg = x; | ||
70 | |||
71 | wrperfmon(2, reg->mux_select); | ||
72 | wrperfmon(3, reg->proc_mode); | ||
73 | wrperfmon(6, reg->reset_values | 3); | ||
74 | } | ||
75 | |||
76 | /* CTR is a counter for which the user has requested an interrupt count | ||
77 | in between one of the widths selectable in hardware. Reset the count | ||
78 | for CTR to the value stored in REG->RESET_VALUES. */ | ||
79 | |||
80 | static void | ||
81 | ev6_reset_ctr(struct op_register_config *reg, unsigned long ctr) | ||
82 | { | ||
83 | wrperfmon(6, reg->reset_values | (1 << ctr)); | ||
84 | } | ||
85 | |||
86 | static void | ||
87 | ev6_handle_interrupt(unsigned long which, struct pt_regs *regs, | ||
88 | struct op_counter_config *ctr) | ||
89 | { | ||
90 | /* Record the sample. */ | ||
91 | oprofile_add_sample(regs, which); | ||
92 | } | ||
93 | |||
94 | |||
95 | struct op_axp_model op_model_ev6 = { | ||
96 | .reg_setup = ev6_reg_setup, | ||
97 | .cpu_setup = ev6_cpu_setup, | ||
98 | .reset_ctr = ev6_reset_ctr, | ||
99 | .handle_interrupt = ev6_handle_interrupt, | ||
100 | .cpu_type = "alpha/ev6", | ||
101 | .num_counters = 2, | ||
102 | .can_set_proc_mode = 0, | ||
103 | }; | ||
diff --git a/arch/alpha/oprofile/op_model_ev67.c b/arch/alpha/oprofile/op_model_ev67.c new file mode 100644 index 000000000000..70302086283c --- /dev/null +++ b/arch/alpha/oprofile/op_model_ev67.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /** | ||
2 | * @file arch/alpha/oprofile/op_model_ev67.c | ||
3 | * | ||
4 | * @remark Copyright 2002 OProfile authors | ||
5 | * @remark Read the file COPYING | ||
6 | * | ||
7 | * @author Richard Henderson <rth@twiddle.net> | ||
8 | * @author Falk Hueffner <falk@debian.org> | ||
9 | */ | ||
10 | |||
11 | #include <linux/oprofile.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/smp.h> | ||
14 | #include <asm/ptrace.h> | ||
15 | #include <asm/system.h> | ||
16 | |||
17 | #include "op_impl.h" | ||
18 | |||
19 | |||
20 | /* Compute all of the registers in preparation for enabling profiling. */ | ||
21 | |||
22 | static void | ||
23 | ev67_reg_setup(struct op_register_config *reg, | ||
24 | struct op_counter_config *ctr, | ||
25 | struct op_system_config *sys) | ||
26 | { | ||
27 | unsigned long ctl, reset, need_reset, i; | ||
28 | |||
29 | /* Select desired events. */ | ||
30 | ctl = 1UL << 4; /* Enable ProfileMe mode. */ | ||
31 | |||
32 | /* The event numbers are chosen so we can use them directly if | ||
33 | PCTR1 is enabled. */ | ||
34 | if (ctr[1].enabled) { | ||
35 | ctl |= (ctr[1].event & 3) << 2; | ||
36 | } else { | ||
37 | if (ctr[0].event == 0) /* cycles */ | ||
38 | ctl |= 1UL << 2; | ||
39 | } | ||
40 | reg->mux_select = ctl; | ||
41 | |||
42 | /* Select logging options. */ | ||
43 | /* ??? Need to come up with some mechanism to trace only | ||
44 | selected processes. EV67 does not have a mechanism to | ||
45 | select kernel or user mode only. For now, enable always. */ | ||
46 | reg->proc_mode = 0; | ||
47 | |||
48 | /* EV67 cannot change the width of the counters as with the | ||
49 | other implementations. But fortunately, we can write to | ||
50 | the counters and set the value such that it will overflow | ||
51 | at the right time. */ | ||
52 | reset = need_reset = 0; | ||
53 | for (i = 0; i < 2; ++i) { | ||
54 | unsigned long count = ctr[i].count; | ||
55 | if (!ctr[i].enabled) | ||
56 | continue; | ||
57 | |||
58 | if (count > 0x100000) | ||
59 | count = 0x100000; | ||
60 | ctr[i].count = count; | ||
61 | reset |= (0x100000 - count) << (i ? 6 : 28); | ||
62 | if (count != 0x100000) | ||
63 | need_reset |= 1 << i; | ||
64 | } | ||
65 | reg->reset_values = reset; | ||
66 | reg->need_reset = need_reset; | ||
67 | } | ||
68 | |||
69 | /* Program all of the registers in preparation for enabling profiling. */ | ||
70 | |||
71 | static void | ||
72 | ev67_cpu_setup (void *x) | ||
73 | { | ||
74 | struct op_register_config *reg = x; | ||
75 | |||
76 | wrperfmon(2, reg->mux_select); | ||
77 | wrperfmon(3, reg->proc_mode); | ||
78 | wrperfmon(6, reg->reset_values | 3); | ||
79 | } | ||
80 | |||
81 | /* CTR is a counter for which the user has requested an interrupt count | ||
82 | in between one of the widths selectable in hardware. Reset the count | ||
83 | for CTR to the value stored in REG->RESET_VALUES. */ | ||
84 | |||
85 | static void | ||
86 | ev67_reset_ctr(struct op_register_config *reg, unsigned long ctr) | ||
87 | { | ||
88 | wrperfmon(6, reg->reset_values | (1 << ctr)); | ||
89 | } | ||
90 | |||
91 | /* ProfileMe conditions which will show up as counters. We can also | ||
92 | detect the following, but it seems unlikely that anybody is | ||
93 | interested in counting them: | ||
94 | * Reset | ||
95 | * MT_FPCR (write to floating point control register) | ||
96 | * Arithmetic trap | ||
97 | * Dstream Fault | ||
98 | * Machine Check (ECC fault, etc.) | ||
99 | * OPCDEC (illegal opcode) | ||
100 | * Floating point disabled | ||
101 | * Differentiate between DTB single/double misses and 3 or 4 level | ||
102 | page tables | ||
103 | * Istream access violation | ||
104 | * Interrupt | ||
105 | * Icache Parity Error. | ||
106 | * Instruction killed (nop, trapb) | ||
107 | |||
108 | Unfortunately, there seems to be no way to detect Dcache and Bcache | ||
109 | misses; the latter could be approximated by making the counter | ||
110 | count Bcache misses, but that is not precise. | ||
111 | |||
112 | We model this as 20 counters: | ||
113 | * PCTR0 | ||
114 | * PCTR1 | ||
115 | * 9 ProfileMe events, induced by PCTR0 | ||
116 | * 9 ProfileMe events, induced by PCTR1 | ||
117 | */ | ||
118 | |||
119 | enum profileme_counters { | ||
120 | PM_STALLED, /* Stalled for at least one cycle | ||
121 | between the fetch and map stages */ | ||
122 | PM_TAKEN, /* Conditional branch taken */ | ||
123 | PM_MISPREDICT, /* Branch caused mispredict trap */ | ||
124 | PM_ITB_MISS, /* ITB miss */ | ||
125 | PM_DTB_MISS, /* DTB miss */ | ||
126 | PM_REPLAY, /* Replay trap */ | ||
127 | PM_LOAD_STORE, /* Load-store order trap */ | ||
128 | PM_ICACHE_MISS, /* Icache miss */ | ||
129 | PM_UNALIGNED, /* Unaligned Load/Store */ | ||
130 | PM_NUM_COUNTERS | ||
131 | }; | ||
132 | |||
133 | static inline void | ||
134 | op_add_pm(unsigned long pc, int kern, unsigned long counter, | ||
135 | struct op_counter_config *ctr, unsigned long event) | ||
136 | { | ||
137 | unsigned long fake_counter = 2 + event; | ||
138 | if (counter == 1) | ||
139 | fake_counter += PM_NUM_COUNTERS; | ||
140 | if (ctr[fake_counter].enabled) | ||
141 | oprofile_add_pc(pc, kern, fake_counter); | ||
142 | } | ||
143 | |||
144 | static void | ||
145 | ev67_handle_interrupt(unsigned long which, struct pt_regs *regs, | ||
146 | struct op_counter_config *ctr) | ||
147 | { | ||
148 | unsigned long pmpc, pctr_ctl; | ||
149 | int kern = !user_mode(regs); | ||
150 | int mispredict = 0; | ||
151 | union { | ||
152 | unsigned long v; | ||
153 | struct { | ||
154 | unsigned reserved: 30; /* 0-29 */ | ||
155 | unsigned overcount: 3; /* 30-32 */ | ||
156 | unsigned icache_miss: 1; /* 33 */ | ||
157 | unsigned trap_type: 4; /* 34-37 */ | ||
158 | unsigned load_store: 1; /* 38 */ | ||
159 | unsigned trap: 1; /* 39 */ | ||
160 | unsigned mispredict: 1; /* 40 */ | ||
161 | } fields; | ||
162 | } i_stat; | ||
163 | |||
164 | enum trap_types { | ||
165 | TRAP_REPLAY, | ||
166 | TRAP_INVALID0, | ||
167 | TRAP_DTB_DOUBLE_MISS_3, | ||
168 | TRAP_DTB_DOUBLE_MISS_4, | ||
169 | TRAP_FP_DISABLED, | ||
170 | TRAP_UNALIGNED, | ||
171 | TRAP_DTB_SINGLE_MISS, | ||
172 | TRAP_DSTREAM_FAULT, | ||
173 | TRAP_OPCDEC, | ||
174 | TRAP_INVALID1, | ||
175 | TRAP_MACHINE_CHECK, | ||
176 | TRAP_INVALID2, | ||
177 | TRAP_ARITHMETIC, | ||
178 | TRAP_INVALID3, | ||
179 | TRAP_MT_FPCR, | ||
180 | TRAP_RESET | ||
181 | }; | ||
182 | |||
183 | pmpc = wrperfmon(9, 0); | ||
184 | /* ??? Don't know how to handle physical-mode PALcode address. */ | ||
185 | if (pmpc & 1) | ||
186 | return; | ||
187 | pmpc &= ~2; /* clear reserved bit */ | ||
188 | |||
189 | i_stat.v = wrperfmon(8, 0); | ||
190 | if (i_stat.fields.trap) { | ||
191 | switch (i_stat.fields.trap_type) { | ||
192 | case TRAP_INVALID1: | ||
193 | case TRAP_INVALID2: | ||
194 | case TRAP_INVALID3: | ||
195 | /* Pipeline redirection ocurred. PMPC points | ||
196 | to PALcode. Recognize ITB miss by PALcode | ||
197 | offset address, and get actual PC from | ||
198 | EXC_ADDR. */ | ||
199 | oprofile_add_pc(regs->pc, kern, which); | ||
200 | if ((pmpc & ((1 << 15) - 1)) == 581) | ||
201 | op_add_pm(regs->pc, kern, which, | ||
202 | ctr, PM_ITB_MISS); | ||
203 | /* Most other bit and counter values will be | ||
204 | those for the first instruction in the | ||
205 | fault handler, so we're done. */ | ||
206 | return; | ||
207 | case TRAP_REPLAY: | ||
208 | op_add_pm(pmpc, kern, which, ctr, | ||
209 | (i_stat.fields.load_store | ||
210 | ? PM_LOAD_STORE : PM_REPLAY)); | ||
211 | break; | ||
212 | case TRAP_DTB_DOUBLE_MISS_3: | ||
213 | case TRAP_DTB_DOUBLE_MISS_4: | ||
214 | case TRAP_DTB_SINGLE_MISS: | ||
215 | op_add_pm(pmpc, kern, which, ctr, PM_DTB_MISS); | ||
216 | break; | ||
217 | case TRAP_UNALIGNED: | ||
218 | op_add_pm(pmpc, kern, which, ctr, PM_UNALIGNED); | ||
219 | break; | ||
220 | case TRAP_INVALID0: | ||
221 | case TRAP_FP_DISABLED: | ||
222 | case TRAP_DSTREAM_FAULT: | ||
223 | case TRAP_OPCDEC: | ||
224 | case TRAP_MACHINE_CHECK: | ||
225 | case TRAP_ARITHMETIC: | ||
226 | case TRAP_MT_FPCR: | ||
227 | case TRAP_RESET: | ||
228 | break; | ||
229 | } | ||
230 | |||
231 | /* ??? JSR/JMP/RET/COR or HW_JSR/HW_JMP/HW_RET/HW_COR | ||
232 | mispredicts do not set this bit but can be | ||
233 | recognized by the presence of one of these | ||
234 | instructions at the PMPC location with bit 39 | ||
235 | set. */ | ||
236 | if (i_stat.fields.mispredict) { | ||
237 | mispredict = 1; | ||
238 | op_add_pm(pmpc, kern, which, ctr, PM_MISPREDICT); | ||
239 | } | ||
240 | } | ||
241 | |||
242 | oprofile_add_pc(pmpc, kern, which); | ||
243 | |||
244 | pctr_ctl = wrperfmon(5, 0); | ||
245 | if (pctr_ctl & (1UL << 27)) | ||
246 | op_add_pm(pmpc, kern, which, ctr, PM_STALLED); | ||
247 | |||
248 | /* Unfortunately, TAK is undefined on mispredicted branches. | ||
249 | ??? It is also undefined for non-cbranch insns, should | ||
250 | check that. */ | ||
251 | if (!mispredict && pctr_ctl & (1UL << 0)) | ||
252 | op_add_pm(pmpc, kern, which, ctr, PM_TAKEN); | ||
253 | } | ||
254 | |||
255 | struct op_axp_model op_model_ev67 = { | ||
256 | .reg_setup = ev67_reg_setup, | ||
257 | .cpu_setup = ev67_cpu_setup, | ||
258 | .reset_ctr = ev67_reset_ctr, | ||
259 | .handle_interrupt = ev67_handle_interrupt, | ||
260 | .cpu_type = "alpha/ev67", | ||
261 | .num_counters = 20, | ||
262 | .can_set_proc_mode = 0, | ||
263 | }; | ||