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 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# +config ALPHA + bool + default y + help + The Alpha is a 64-bit general-purpose processor designed and + marketed by the Digital Equipment Corporation of blessed memory, + now Hewlett-Packard. The Alpha Linux project has a home page at + <http://www.alphalinux.org/>. + +config 64BIT + def_bool y + +config MMU + bool + default y + +config UID16 + bool + +config RWSEM_GENERIC_SPINLOCK + bool + +config RWSEM_XCHGADD_ALGORITHM + bool + default y + +config GENERIC_CALIBRATE_DELAY + bool + default y + +config GENERIC_ISA_DMA + bool + default y + +config GENERIC_IOMAP + bool + default n + +source "init/Kconfig" + + +menu "System setup" + +choice + prompt "Alpha system type" + default ALPHA_GENERIC + ---help--- + This is the system type of your hardware. A "generic" kernel will + run on any supported Alpha system. However, if you configure a + kernel for your specific system, it will be faster and smaller. + + To find out what type of Alpha system you have, you may want to + check out the Linux/Alpha FAQ, accessible on the WWW from + <http://www.alphalinux.org/>. In summary: + + Alcor/Alpha-XLT AS 600 + Alpha-XL XL-233, XL-266 + AlphaBook1 Alpha laptop + Avanti AS 200, AS 205, AS 250, AS 255, AS 300, AS 400 + Cabriolet AlphaPC64, AlphaPCI64 + DP264 DP264 + EB164 EB164 21164 evaluation board + EB64+ EB64+ 21064 evaluation board + EB66 EB66 21066 evaluation board + EB66+ EB66+ 21066 evaluation board + Jensen DECpc 150, DEC 2000 model 300, + DEC 2000 model 500 + LX164 AlphaPC164-LX + Lynx AS 2100A + Miata Personal Workstation 433a, 433au, 500a, + 500au, 600a, or 600au + Marvel AlphaServer ES47 / ES80 / GS1280 + Mikasa AS 1000 + Noname AXPpci33, UDB (Multia) + Noritake AS 1000A, AS 600A, AS 800 + PC164 AlphaPC164 + Rawhide AS 1200, AS 4000, AS 4100 + Ruffian RPX164-2, AlphaPC164-UX, AlphaPC164-BX + SX164 AlphaPC164-SX + Sable AS 2000, AS 2100 + Shark DS 20L + Takara Takara + Titan AlphaServer ES45 / DS25 + Wildfire AlphaServer GS 40/80/160/320 + + If you don't know what to do, choose "generic". + +config ALPHA_GENERIC + bool "Generic" + help + A generic kernel will run on all supported Alpha hardware. + +config ALPHA_ALCOR + bool "Alcor/Alpha-XLT" + help + For systems using the Digital ALCOR chipset: 5 chips (4, 64-bit data + slices (Data Switch, DSW) - 208-pin PQFP and 1 control (Control, I/O + Address, CIA) - a 383 pin plastic PGA). It provides a DRAM + controller (256-bit memory bus) and a PCI interface. It also does + all the work required to support an external Bcache and to maintain + memory coherence when a PCI device DMAs into (or out of) memory. + +config ALPHA_XL + bool "Alpha-XL" + help + XL-233 and XL-266-based Alpha systems. + +config ALPHA_BOOK1 + bool "AlphaBook1" + help + Dec AlphaBook1/Burns Alpha-based laptops. + +config ALPHA_AVANTI_CH + bool "Avanti" + +config ALPHA_CABRIOLET + bool "Cabriolet" + help + Cabriolet AlphaPC64, AlphaPCI64 systems. Derived from EB64+ but now + baby-AT with Flash boot ROM, no on-board SCSI or Ethernet. 3 ISA + slots, 4 PCI slots (one pair are on a shared slot), uses plug-in + Bcache SIMMs. Requires power supply with 3.3V output. + +config ALPHA_DP264 + bool "DP264" + help + Various 21264 systems with the tsunami core logic chipset. + API Networks: 264DP, UP2000(+), CS20; + Compaq: DS10(E,L), XP900, XP1000, DS20(E), ES40. + +config ALPHA_EB164 + bool "EB164" + help + EB164 21164 evaluation board from DEC. Uses 21164 and ALCOR. Has + ISA and PCI expansion (3 ISA slots, 2 64-bit PCI slots (one is + shared with an ISA slot) and 2 32-bit PCI slots. Uses plus-in + Bcache SIMMs. I/O sub-system provides SuperI/O (2S, 1P, FD), KBD, + MOUSE (PS2 style), RTC/NVRAM. Boot ROM is Flash. PC-AT-sized + motherboard. Requires power supply with 3.3V output. + +config ALPHA_EB64P_CH + bool "EB64+" + +config ALPHA_EB66 + bool "EB66" + help + A Digital DS group board. Uses 21066 or 21066A. I/O sub-system is + identical to EB64+. Baby PC-AT size. Runs from standard PC power + supply. The EB66 schematic was published as a marketing poster + advertising the 21066 as "the first microprocessor in the world with + embedded PCI". + +config ALPHA_EB66P + bool "EB66+" + help + Later variant of the EB66 board. + +config ALPHA_EIGER + bool "Eiger" + help + Apparently an obscure OEM single-board computer based on the + Typhoon/Tsunami chipset family. Information on it is scanty. + +config ALPHA_JENSEN + bool "Jensen" + help + DEC PC 150 AXP (aka Jensen): This is a very old Digital system - one + of the first-generation Alpha systems. A number of these systems + seem to be available on the second- hand market. The Jensen is a + floor-standing tower system which originally used a 150MHz 21064 It + used programmable logic to interface a 486 EISA I/O bridge to the + CPU. + +config ALPHA_LX164 + bool "LX164" + help + A technical overview of this board is available at + <http://www.unix-ag.org/Linux-Alpha/Architectures/LX164.html>. + +config ALPHA_LYNX + bool "Lynx" + help + AlphaServer 2100A-based systems. + +config ALPHA_MARVEL + bool "Marvel" + help + AlphaServer ES47 / ES80 / GS1280 based on EV7. + +config ALPHA_MIATA + bool "Miata" + help + The Digital PersonalWorkStation (PWS 433a, 433au, 500a, 500au, 600a, + or 600au). There is an Installation HOWTO for this hardware at + <http://eijk.homelinux.org/~stefan/miata.html>. + +config ALPHA_MIKASA + bool "Mikasa" + help + AlphaServer 1000-based Alpha systems. + +config ALPHA_NAUTILUS + bool "Nautilus" + help + Alpha systems based on the AMD 751 & ALI 1543C chipsets. + +config ALPHA_NONAME_CH + bool "Noname" + +config ALPHA_NORITAKE + bool "Noritake" + help + AlphaServer 1000A, AlphaServer 600A, and AlphaServer 800-based + systems. + +config ALPHA_PC164 + bool "PC164" + +config ALPHA_P2K + bool "Platform2000" + +config ALPHA_RAWHIDE + bool "Rawhide" + help + AlphaServer 1200, AlphaServer 4000 and AlphaServer 4100 machines. + See HOWTO at + <http://www.alphalinux.org/docs/rawhide/4100_install.shtml>. + +config ALPHA_RUFFIAN + bool "Ruffian" + help + Samsung APC164UX. There is a page on known problems and workarounds + at <http://www.alphalinux.org/faq/FAQ-11.html>. + +config ALPHA_RX164 + bool "RX164" + +config ALPHA_SX164 + bool "SX164" + +config ALPHA_SABLE + bool "Sable" + help + Digital AlphaServer 2000 and 2100-based systems. + +config ALPHA_SHARK + bool "Shark" + +config ALPHA_TAKARA + bool "Takara" + help + Alpha 11164-based OEM single-board computer. + +config ALPHA_TITAN + bool "Titan" + help + AlphaServer ES45/DS25 SMP based on EV68 and Titan chipset. + +config ALPHA_WILDFIRE + bool "Wildfire" + help + AlphaServer GS 40/80/160/320 SMP based on the EV67 core. + +endchoice + +# clear all implied options (don't want default values for those): +# Most of these machines have ISA slots; not exactly sure which don't, +# and this doesn't activate hordes of code, so do it always. +config ISA + bool + default y + help + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. Other bus systems are PCI, EISA, MicroChannel + (MCA) or VESA. ISA is an older system, now being displaced by PCI; + newer boards don't support it. If you have ISA, say Y, otherwise N. + +config PCI + bool + depends on !ALPHA_JENSEN + default y + help + Find out whether you have a PCI motherboard. PCI is the name of a + bus system, i.e. the way the CPU talks to the other stuff inside + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. + + The PCI-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +config PCI_DOMAINS + bool + default y + +config ALPHA_CORE_AGP + bool + depends on ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL + default y + +config ALPHA_NONAME + bool + depends on ALPHA_BOOK1 || ALPHA_NONAME_CH + default y + help + The AXPpci33 (aka NoName), is based on the EB66 (includes the Multia + UDB). This design was produced by Digital's Technical OEM (TOEM) + group. It uses the 21066 processor running at 166MHz or 233MHz. It + is a baby-AT size, and runs from a standard PC power supply. It has + 5 ISA slots and 3 PCI slots (one pair are a shared slot). There are + 2 versions, with either PS/2 or large DIN connectors for the + keyboard. + +config ALPHA_EV4 + bool + 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 + default y if !ALPHA_LYNX + +config ALPHA_LCA + bool + depends on ALPHA_NONAME || ALPHA_EB66 || ALPHA_EB66P || ALPHA_P2K + default y + +config ALPHA_APECS + bool + depends on !ALPHA_PRIMO && (ALPHA_NORITAKE || ALPHA_MIKASA) || ALPHA_CABRIOLET || ALPHA_AVANTI_CH || ALPHA_EB64P_CH || ALPHA_XL + default y + +config ALPHA_EB64P + bool + depends on ALPHA_CABRIOLET || ALPHA_EB64P_CH + default y + help + Uses 21064 or 21064A and APECs. Has ISA and PCI expansion (3 ISA, + 2 PCI, one pair are on a shared slot). Supports 36-bit DRAM SIMs. + ISA bus generated by Intel SaturnI/O PCI-ISA bridge. On-board SCSI + (NCR 810 on PCI) Ethernet (Digital 21040), KBD, MOUSE (PS2 style), + SuperI/O (2S, 1P, FD), RTC/NVRAM. Boot ROM is EPROM. PC-AT size. + Runs from standard PC power supply. + +config ALPHA_EV5 + bool "EV5 CPU(s) (model 5/xxx)?" if ALPHA_LYNX + 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 + +config ALPHA_EV4 + bool + default y if ALPHA_LYNX && !ALPHA_EV5 + +config ALPHA_CIA + bool + 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 + default y + +config ALPHA_EV56 + bool "EV56 CPU (speed >= 366MHz)?" if ALPHA_ALCOR + default y if ALPHA_RX164 || ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_RUFFIAN || ALPHA_PC164 || ALPHA_TAKARA + +config ALPHA_EV56 + prompt "EV56 CPU (speed >= 333MHz)?" + depends on ALPHA_NORITAKE && ALPHA_PRIMO + +config ALPHA_EV56 + prompt "EV56 CPU (speed >= 400MHz)?" + depends on ALPHA_RAWHIDE + +config ALPHA_PRIMO + bool "EV5 CPU daughtercard (model 5/xxx)?" + depends on ALPHA_NORITAKE || ALPHA_MIKASA + help + Say Y if you have an AS 1000 5/xxx or an AS 1000A 5/xxx. + +config ALPHA_GAMMA + bool "EV5 CPU(s) (model 5/xxx)?" + depends on ALPHA_SABLE + help + Say Y if you have an AS 2000 5/xxx or an AS 2100 5/xxx. + +config ALPHA_GAMMA + bool + depends on ALPHA_LYNX + default y + +config ALPHA_T2 + bool + depends on ALPHA_SABLE || ALPHA_LYNX + default y + +config ALPHA_PYXIS + bool + depends on ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_RUFFIAN + default y + +config ALPHA_EV6 + bool + depends on ALPHA_NAUTILUS || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_SHARK || ALPHA_DP264 || ALPHA_EIGER || ALPHA_MARVEL + default y + +config ALPHA_TSUNAMI + bool + depends on ALPHA_SHARK || ALPHA_DP264 || ALPHA_EIGER + default y + +config ALPHA_EV67 + bool "EV67 (or later) CPU (speed > 600MHz)?" if ALPHA_DP264 || ALPHA_EIGER + default y if ALPHA_NAUTILUS || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_SHARK || ALPHA_MARVEL + help + Is this a machine based on the EV67 core? If in doubt, select N here + and the machine will be treated as an EV6. + +config ALPHA_EV7 + bool + depends on ALPHA_MARVEL + default y + +config ALPHA_MCPCIA + bool + depends on ALPHA_RAWHIDE + default y + +config ALPHA_POLARIS + bool + depends on ALPHA_RX164 + default y + +config ALPHA_IRONGATE + bool + depends on ALPHA_NAUTILUS + default y + +config ALPHA_AVANTI + bool + depends on ALPHA_XL || ALPHA_AVANTI_CH + default y + help + Avanti AS 200, AS 205, AS 250, AS 255, AS 300, and AS 400-based + Alphas. Info at + <http://www.unix-ag.org/Linux-Alpha/Architectures/Avanti.html>. + +config ALPHA_BROKEN_IRQ_MASK + bool + depends on ALPHA_GENERIC || ALPHA_PC164 + default y + +config ALPHA_SRM + 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 + 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 + ---help--- + There are two different types of booting firmware on Alphas: SRM, + which is command line driven, and ARC, which uses menus and arrow + keys. Details about the Linux/Alpha booting process are contained in + the Linux/Alpha FAQ, accessible on the WWW from + <http://www.alphalinux.org/>. + + The usual way to load Linux on an Alpha machine is to use MILO + (a bootloader that lets you pass command line parameters to the + kernel just like lilo does for the x86 architecture) which can be + loaded either from ARC or can be installed directly as a permanent + firmware replacement from floppy (which requires changing a certain + jumper on the motherboard). If you want to do either of these, say N + here. If MILO doesn't work on your system (true for Jensen + motherboards), you can bypass it altogether and boot Linux directly + from an SRM console; say Y here in order to do that. Note that you + won't be able to boot from an IDE disk using SRM. + + If unsure, say N. + +config EISA + bool + depends on ALPHA_GENERIC || ALPHA_JENSEN || ALPHA_ALCOR || ALPHA_MIKASA || ALPHA_SABLE || ALPHA_LYNX || ALPHA_NORITAKE || ALPHA_RAWHIDE + default y + +config SMP + bool "Symmetric multi-processing support" + depends on ALPHA_SABLE || ALPHA_LYNX || ALPHA_RAWHIDE || ALPHA_DP264 || ALPHA_WILDFIRE || ALPHA_TITAN || ALPHA_GENERIC || ALPHA_SHARK || ALPHA_MARVEL + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + See also the <file:Documentation/smp.txt>, and the SMP-HOWTO + available at <http://www.tldp.org/docs.html#howto>. + + If you don't know what to do here, say N. + +config HAVE_DEC_LOCK + bool + depends on SMP + default y + +config NR_CPUS + int "Maximum number of CPUs (2-64)" + range 2 64 + depends on SMP + default "64" + +config DISCONTIGMEM + bool "Discontiguous Memory Support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Say Y to upport efficient handling of discontiguous physical memory, + for architectures which are either NUMA (Non-Uniform Memory Access) + or have huge holes in the physical address space for other reasons. + See <file:Documentation/vm/numa> for more. + +config NUMA + bool "NUMA Support (EXPERIMENTAL)" + depends on DISCONTIGMEM + help + Say Y to compile the kernel to support NUMA (Non-Uniform Memory + Access). This option is for configuring high-end multiprocessor + server machines. If in doubt, say N. + +# LARGE_VMALLOC is racy, if you *really* need it then fix it first +config ALPHA_LARGE_VMALLOC + bool + ---help--- + Process creation and other aspects of virtual memory management can + be streamlined if we restrict the kernel to one PGD for all vmalloc + allocations. This equates to about 8GB. + + Under normal circumstances, this is so far and above what is needed + as to be laughable. However, there are certain applications (such + as benchmark-grade in-kernel web serving) that can make use of as + much vmalloc space as is available. + + Say N unless you know you need gobs and gobs of vmalloc space. + +config VERBOSE_MCHECK + bool "Verbose Machine Checks" + +config VERBOSE_MCHECK_ON + int "Verbose Printing Mode (0=off, 1=on, 2=all)" + depends on VERBOSE_MCHECK + default 1 + ---help--- + This option allows the default printing mode to be set, and then + possibly overridden by a boot command argument. + + For example, if one wanted the option of printing verbose + machine checks, but wanted the default to be as if verbose + machine check printing was turned off, then one would choose + the printing mode to be 0. Then, upon reboot, one could add + the boot command line "verbose_mcheck=1" to get the normal + verbose machine check printing, or "verbose_mcheck=2" to get + the maximum information available. + + Take the default (1) unless you want more control or more info. + +source "drivers/pci/Kconfig" +source "drivers/eisa/Kconfig" + +source "drivers/pcmcia/Kconfig" + +config SRM_ENV + tristate "SRM environment through procfs" + depends on PROC_FS + ---help--- + If you enable this option, a subdirectory inside /proc called + /proc/srm_environment will give you access to the all important + SRM environment variables (those which have a name) and also + to all others (by their internal number). + + SRM is something like a BIOS for Alpha machines. There are some + other such BIOSes, like AlphaBIOS, which this driver cannot + support (hey, that's not SRM!). + + Despite the fact that this driver doesn't work on all Alphas (but + only on those which have SRM as their firmware), it's save to + build it even if your particular machine doesn't know about SRM + (or if you intend to compile a generic kernel). It will simply + not create those subdirectory in /proc (and give you some warning, + of course). + + This driver is also available as a module and will be called + srm_env then. + +source "fs/Kconfig.binfmt" + +endmenu + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/alpha/oprofile/Kconfig" + +source "arch/alpha/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + 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 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config EARLY_PRINTK + bool + depends on ALPHA_GENERIC || ALPHA_SRM + default y + +config DEBUG_RWLOCK + bool "Read-write spinlock debugging" + depends on DEBUG_KERNEL + help + If you say Y here then read-write lock processing will count how many + times it has tried to get the lock and issue an error message after + too many attempts. If you suspect a rwlock problem or a kernel + hacker asks for this option then say Y. Otherwise say N. + +config DEBUG_SEMAPHORE + bool "Semaphore debugging" + depends on DEBUG_KERNEL + help + If you say Y here then semaphore processing will issue lots of + verbose debugging messages. If you suspect a semaphore problem or a + kernel hacker asks for this option then say Y. Otherwise say N. + +config ALPHA_LEGACY_START_ADDRESS + bool "Legacy kernel start address" + depends on ALPHA_GENERIC + default n + ---help--- + The 2.4 kernel changed the kernel start address from 0x310000 + to 0x810000 to make room for the Wildfire's larger SRM console. + Recent consoles on Titan and Marvel machines also require the + extra room. + + If you're using aboot 0.7 or later, the bootloader will examine the + ELF headers to determine where to transfer control. Unfortunately, + most older bootloaders -- APB or MILO -- hardcoded the kernel start + address rather than examining the ELF headers, and the result is a + hard lockup. + + Say Y if you have a broken bootloader. Say N if you do not, or if + you wish to run on Wildfire, Titan, or Marvel. + +config ALPHA_LEGACY_START_ADDRESS + bool + depends on !ALPHA_GENERIC && !ALPHA_TITAN && !ALPHA_MARVEL && !ALPHA_WILDFIRE + default y + +config MATHEMU + tristate "Kernel FP software completion" if DEBUG_KERNEL && !SMP + default y if !DEBUG_KERNEL || SMP + help + This option is required for IEEE compliant floating point arithmetic + on the Alpha. The only time you would ever not say Y is to say M in + order to debug the code. Say Y unless you know what you are doing. + +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 @@ +# +# alpha/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +NM := $(NM) -B + +LDFLAGS_vmlinux := -static -N #-relax +CHECKFLAGS += -D__alpha__ -m64 +cflags-y := -pipe -mno-fp-regs -ffixed-8 + +# Determine if we can use the BWX instructions with GAS. +old_gas := $(shell if $(AS) --version 2>&1 | grep 'version 2.7' > /dev/null; then echo y; else echo n; fi) + +ifeq ($(old_gas),y) +$(error The assembler '$(AS)' does not support the BWX instruction) +endif + +# Determine if GCC understands the -mcpu= option. +have_mcpu := $(call cc-option-yn, -mcpu=ev5) +have_mcpu_pca56 := $(call cc-option-yn, -mcpu=pca56) +have_mcpu_ev6 := $(call cc-option-yn, -mcpu=ev6) +have_mcpu_ev67 := $(call cc-option-yn, -mcpu=ev67) +have_msmall_data := $(call cc-option-yn, -msmall-data) + +cflags-$(have_msmall_data) += -msmall-data + +# Turn on the proper cpu optimizations. +ifeq ($(have_mcpu),y) + mcpu_done := n + # If GENERIC, make sure to turn off any instruction set extensions that + # the host compiler might have on by default. Given that EV4 and EV5 + # have the same instruction set, prefer EV5 because an EV5 schedule is + # more likely to keep an EV4 processor busy than vice-versa. + ifeq ($(CONFIG_ALPHA_GENERIC),y) + mcpu := ev5 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_SX164)$(have_mcpu_pca56),nyy) + mcpu := pca56 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_POLARIS)$(have_mcpu_pca56),nyy) + mcpu := pca56 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV4),ny) + mcpu := ev4 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV56),ny) + mcpu := ev56 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV5),ny) + mcpu := ev5 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV67)$(have_mcpu_ev67),nyy) + mcpu := ev67 + mcpu_done := y + endif + ifeq ($(mcpu_done)$(CONFIG_ALPHA_EV6),ny) + ifeq ($(have_mcpu_ev6),y) + mcpu := ev6 + else + ifeq ($(have_mcpu_pca56),y) + mcpu := pca56 + else + mcpu=ev56 + endif + endif + mcpu_done := y + endif + cflags-$(mcpu_done) += -mcpu=$(mcpu) +endif + + +# For TSUNAMI, we must have the assembler not emulate our instructions. +# The same is true for IRONGATE, POLARIS, PYXIS. +# BWX is most important, but we don't really want any emulation ever. +CFLAGS += $(cflags-y) -Wa,-mev6 + +head-y := arch/alpha/kernel/head.o + +core-y += arch/alpha/kernel/ arch/alpha/mm/ +core-$(CONFIG_MATHEMU) += arch/alpha/math-emu/ +drivers-$(CONFIG_OPROFILE) += arch/alpha/oprofile/ +libs-y += arch/alpha/lib/ + +# export what is needed by arch/alpha/boot/Makefile +LIBS_Y := $(patsubst %/, %/lib.a, $(libs-y)) +export LIBS_Y + +boot := arch/alpha/boot + +#Default target when executing make with no arguments +all boot: $(boot)/vmlinux.gz + +$(boot)/vmlinux.gz: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $@ + +bootimage bootpfile bootpzfile: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + + +prepare: include/asm-$(ARCH)/asm_offsets.h + +arch/$(ARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \ + include/config/MARKER + +include/asm-$(ARCH)/asm_offsets.h: arch/$(ARCH)/kernel/asm-offsets.s + $(call filechk,gen-asm-offsets) + +archclean: + $(Q)$(MAKE) $(clean)=$(boot) + +CLEAN_FILES += include/asm-$(ARCH)/asm_offsets.h + +define archhelp + echo '* boot - Compressed kernel image (arch/alpha/boot/vmlinux.gz)' + echo ' bootimage - SRM bootable image (arch/alpha/boot/bootimage)' + echo ' bootpfile - BOOTP bootable image (arch/alpha/boot/bootpfile)' + echo ' bootpzfile - compressed kernel BOOTP image (arch/alpha/boot/bootpzfile)' +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 @@ +# +# arch/alpha/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +hostprogs-y := tools/mkbb tools/objstrip +targets := vmlinux.gz vmlinux \ + vmlinux.nh tools/lxboot tools/bootlx tools/bootph \ + tools/bootpzh bootloader bootpheader bootpzheader +OBJSTRIP := $(obj)/tools/objstrip + +# SRM bootable image. Copy to offset 512 of a partition. +$(obj)/bootimage: $(addprefix $(obj)/tools/,mkbb lxboot bootlx) $(obj)/vmlinux.nh + ( cat $(obj)/tools/lxboot $(obj)/tools/bootlx $(obj)/vmlinux.nh ) > $@ + $(obj)/tools/mkbb $@ $(obj)/tools/lxboot + @echo ' Bootimage $@ is ready' + +# BOOTP bootable image. Define INITRD during make to append initrd image. +$(obj)/bootpfile: $(obj)/tools/bootph $(obj)/vmlinux.nh + cat $(obj)/tools/bootph $(obj)/vmlinux.nh > $@ +ifdef INITRD + cat $(INITRD) >> $@ +endif + +# Compressed kernel BOOTP bootable image. +# Define INITRD during make to append initrd image. +$(obj)/bootpzfile: $(obj)/tools/bootpzh $(obj)/vmlinux.nh.gz + cat $(obj)/tools/bootpzh $(obj)/vmlinux.nh.gz > $@ +ifdef INITRD + cat $(INITRD) >> $@ +endif + +# Compressed kernel image +$(obj)/vmlinux.gz: $(obj)/vmlinux FORCE + $(call if_changed,gzip) + @echo ' Kernel $@ is ready' + +$(obj)/main.o: $(obj)/ksize.h +$(obj)/bootp.o: $(obj)/ksize.h +$(obj)/bootpz.o: $(obj)/kzsize.h + +$(obj)/ksize.h: $(obj)/vmlinux.nh FORCE + echo "#define KERNEL_SIZE `ls -l $(obj)/vmlinux.nh | awk '{print $$5}'`" > $@T +ifdef INITRD + [ -f $(INITRD) ] || exit 1 + echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T +endif + cmp -s $@T $@ || mv -f $@T $@ + rm -f $@T + +$(obj)/kzsize.h: $(obj)/vmlinux.nh.gz FORCE + echo "#define KERNEL_SIZE `ls -l $(obj)/vmlinux.nh | awk '{print $$5}'`" > $@T + echo "#define KERNEL_Z_SIZE `ls -l $(obj)/vmlinux.nh.gz | awk '{print $$5}'`" >> $@T +ifdef INITRD + [ -f $(INITRD) ] || exit 1 + echo "#define INITRD_IMAGE_SIZE `ls -l $(INITRD) | awk '{print $$5}'`" >> $@T +endif + cmp -s $@T $@ || mv -f $@T $@ + rm -f $@T + +quiet_cmd_strip = STRIP $@ + cmd_strip = $(STRIP) -o $@ $< + +$(obj)/vmlinux: vmlinux FORCE + $(call if_changed,strip) + +quiet_cmd_objstrip = OBJSTRIP $@ + cmd_objstrip = $(OBJSTRIP) $(OSFLAGS_$(@F)) $< $@ + +OSFLAGS_vmlinux.nh := -v +OSFLAGS_lxboot := -p +OSFLAGS_bootlx := -vb +OSFLAGS_bootph := -vb +OSFLAGS_bootpzh := -vb + +$(obj)/vmlinux.nh: vmlinux $(OBJSTRIP) FORCE + $(call if_changed,objstrip) + +$(obj)/vmlinux.nh.gz: $(obj)/vmlinux.nh FORCE + $(call if_changed,gzip) + +$(obj)/tools/lxboot: $(obj)/bootloader $(OBJSTRIP) FORCE + $(call if_changed,objstrip) + +$(obj)/tools/bootlx: $(obj)/bootloader $(OBJSTRIP) FORCE + $(call if_changed,objstrip) + +$(obj)/tools/bootph: $(obj)/bootpheader $(OBJSTRIP) FORCE + $(call if_changed,objstrip) + +$(obj)/tools/bootpzh: $(obj)/bootpzheader $(OBJSTRIP) FORCE + $(call if_changed,objstrip) + +LDFLAGS_bootloader := -static -uvsprintf -T #-N -relax +LDFLAGS_bootpheader := -static -uvsprintf -T #-N -relax +LDFLAGS_bootpzheader := -static -uvsprintf -T #-N -relax + +OBJ_bootlx := $(obj)/head.o $(obj)/main.o +OBJ_bootph := $(obj)/head.o $(obj)/bootp.o +OBJ_bootpzh := $(obj)/head.o $(obj)/bootpz.o $(obj)/misc.o + +$(obj)/bootloader: $(obj)/bootloader.lds $(OBJ_bootlx) FORCE + $(call if_changed,ld) + +$(obj)/bootpheader: $(obj)/bootloader.lds $(OBJ_bootph) $(LIBS_Y) FORCE + $(call if_changed,ld) + +$(obj)/bootpzheader: $(obj)/bootloader.lds $(OBJ_bootpzh) $(LIBS_Y) FORCE + $(call if_changed,ld) + +$(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 @@ +OUTPUT_FORMAT("elf64-alpha") +ENTRY(__start) +printk = srm_printk; +SECTIONS +{ + . = 0x20000000; + .text : { *(.text) } + _etext = .; + PROVIDE (etext = .); + .rodata : { *(.rodata) *(.rodata.*) } + .data : { *(.data) CONSTRUCTORS } + .got : { *(.got) } + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + .sbss : { *(.sbss) *(.scommon) } + .bss : { *(.bss) *(COMMON) } + _end = . ; + PROVIDE (end = .); + + .mdebug 0 : { *(.mdebug) } + .note 0 : { *(.note) } + .comment 0 : { *(.comment) } +} 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 @@ +/* + * arch/alpha/boot/bootp.c + * + * Copyright (C) 1997 Jay Estabrook + * + * This file is used for creating a bootp file for the Linux/AXP kernel + * + * based significantly on the arch/alpha/boot/main.c of Linus Torvalds + */ +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/version.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/console.h> +#include <asm/hwrpb.h> +#include <asm/pgtable.h> +#include <asm/io.h> + +#include <stdarg.h> + +#include "ksize.h" + +extern unsigned long switch_to_osf_pal(unsigned long nr, + struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, + unsigned long *vptb); + +extern void move_stack(unsigned long new_stack); + +struct hwrpb_struct *hwrpb = INIT_HWRPB; +static struct pcb_struct pcb_va[1]; + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ + +static inline void * +find_pa(unsigned long *vptb, void *ptr) +{ + unsigned long address = (unsigned long) ptr; + unsigned long result; + + result = vptb[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return (void *) result; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + */ + +#define VPTB ((unsigned long *) 0x200000000) +#define L1 ((unsigned long *) 0x200802000) + +void +pal_init(void) +{ + unsigned long i, rev; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Create the dummy PCB. */ + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_va->res1 = 0; + pcb_va->res2 = 0; + pcb_pa = find_pa(VPTB, pcb_va); + + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the vaddr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but the asm sets it) + */ + srm_printk("Switching to OSF PAL-code .. "); + + i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); + if (i) { + srm_printk("failed, code %ld\n", i); + __halt(); + } + + percpu = (struct percpu_struct *) + (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + srm_printk("Ok (rev %lx)\n", rev); + + tbia(); /* do it directly in case we are SMP */ +} + +static inline void +load(unsigned long dst, unsigned long src, unsigned long count) +{ + memcpy((void *)dst, (void *)src, count); +} + +/* + * Start the kernel. + */ +static inline void +runkernel(void) +{ + __asm__ __volatile__( + "bis %0,%0,$27\n\t" + "jmp ($27)" + : /* no outputs: it doesn't even return */ + : "r" (START_ADDR)); +} + +extern char _end; +#define KERNEL_ORIGIN \ + ((((unsigned long)&_end) + 511) & ~511) + +void +start_kernel(void) +{ + /* + * Note that this crufty stuff with static and envval + * and envbuf is because: + * + * 1. Frequently, the stack is short, and we don't want to overrun; + * 2. Frequently the stack is where we are going to copy the kernel to; + * 3. A certain SRM console required the GET_ENV output to stack. + * ??? A comment in the aboot sources indicates that the GET_ENV + * destination must be quadword aligned. Might this explain the + * behaviour, rather than requiring output to the stack, which + * seems rather far-fetched. + */ + static long nbytes; + static char envval[256] __attribute__((aligned(8))); + static unsigned long initrd_start; + + srm_printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); + if (INIT_HWRPB->pagesize != 8192) { + srm_printk("Expected 8kB pages, got %ldkB\n", + INIT_HWRPB->pagesize >> 10); + return; + } + if (INIT_HWRPB->vptb != (unsigned long) VPTB) { + srm_printk("Expected vptb at %p, got %p\n", + VPTB, (void *)INIT_HWRPB->vptb); + return; + } + pal_init(); + + /* The initrd must be page-aligned. See below for the + cause of the magic number 5. */ + initrd_start = ((START_ADDR + 5*KERNEL_SIZE + PAGE_SIZE) | + (PAGE_SIZE-1)) + 1; +#ifdef INITRD_IMAGE_SIZE + srm_printk("Initrd positioned at %#lx\n", initrd_start); +#endif + + /* + * Move the stack to a safe place to ensure it won't be + * overwritten by kernel image. + */ + move_stack(initrd_start - PAGE_SIZE); + + nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); + if (nbytes < 0 || nbytes >= sizeof(envval)) { + nbytes = 0; + } + envval[nbytes] = '\0'; + srm_printk("Loading the kernel...'%s'\n", envval); + + /* NOTE: *no* callbacks or printouts from here on out!!! */ + + /* This is a hack, as some consoles seem to get virtual 20000000 (ie + * where the SRM console puts the kernel bootp image) memory + * overlapping physical memory where the kernel wants to be put, + * which causes real problems when attempting to copy the former to + * the latter... :-( + * + * So, we first move the kernel virtual-to-physical way above where + * we physically want the kernel to end up, then copy it from there + * to its final resting place... ;-} + * + * Sigh... */ + +#ifdef INITRD_IMAGE_SIZE + load(initrd_start, KERNEL_ORIGIN+KERNEL_SIZE, INITRD_IMAGE_SIZE); +#endif + load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); + load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); + + memset((char*)ZERO_PGE, 0, PAGE_SIZE); + strcpy((char*)ZERO_PGE, envval); +#ifdef INITRD_IMAGE_SIZE + ((long *)(ZERO_PGE+256))[0] = initrd_start; + ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; +#endif + + runkernel(); +} 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 @@ +/* + * arch/alpha/boot/bootpz.c + * + * Copyright (C) 1997 Jay Estabrook + * + * This file is used for creating a compressed BOOTP file for the + * Linux/AXP kernel + * + * based significantly on the arch/alpha/boot/main.c of Linus Torvalds + * and the decompression code from MILO. + */ +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/version.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/console.h> +#include <asm/hwrpb.h> +#include <asm/pgtable.h> +#include <asm/io.h> + +#include <stdarg.h> + +#include "kzsize.h" + +/* FIXME FIXME FIXME */ +#define MALLOC_AREA_SIZE 0x200000 /* 2MB for now */ +/* FIXME FIXME FIXME */ + + +/* + WARNING NOTE + + It is very possible that turning on additional messages may cause + kernel image corruption due to stack usage to do the printing. + +*/ + +#undef DEBUG_CHECK_RANGE +#undef DEBUG_ADDRESSES +#undef DEBUG_LAST_STEPS + +extern unsigned long switch_to_osf_pal(unsigned long nr, + struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, + unsigned long *vptb); + +extern int decompress_kernel(void* destination, void *source, + size_t ksize, size_t kzsize); + +extern void move_stack(unsigned long new_stack); + +struct hwrpb_struct *hwrpb = INIT_HWRPB; +static struct pcb_struct pcb_va[1]; + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ +#define VPTB ((unsigned long *) 0x200000000) + +static inline unsigned long +find_pa(unsigned long address) +{ + unsigned long result; + + result = VPTB[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return result; +} + +int +check_range(unsigned long vstart, unsigned long vend, + unsigned long kstart, unsigned long kend) +{ + unsigned long vaddr, kaddr; + +#ifdef DEBUG_CHECK_RANGE + srm_printk("check_range: V[0x%lx:0x%lx] K[0x%lx:0x%lx]\n", + vstart, vend, kstart, kend); +#endif + /* do some range checking for detecting an overlap... */ + for (vaddr = vstart; vaddr <= vend; vaddr += PAGE_SIZE) + { + kaddr = (find_pa(vaddr) | PAGE_OFFSET); + if (kaddr >= kstart && kaddr <= kend) + { +#ifdef DEBUG_CHECK_RANGE + srm_printk("OVERLAP: vaddr 0x%lx kaddr 0x%lx" + " [0x%lx:0x%lx]\n", + vaddr, kaddr, kstart, kend); +#endif + return 1; + } + } + return 0; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + */ + +#define L1 ((unsigned long *) 0x200802000) + +void +pal_init(void) +{ + unsigned long i, rev; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Create the dummy PCB. */ + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_va->res1 = 0; + pcb_va->res2 = 0; + pcb_pa = (struct pcb_struct *)find_pa((unsigned long)pcb_va); + + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the vaddr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but the asm sets it) + */ + srm_printk("Switching to OSF PAL-code... "); + + i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); + if (i) { + srm_printk("failed, code %ld\n", i); + __halt(); + } + + percpu = (struct percpu_struct *) + (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + srm_printk("OK (rev %lx)\n", rev); + + tbia(); /* do it directly in case we are SMP */ +} + +/* + * Start the kernel. + */ +static inline void +runkernel(void) +{ + __asm__ __volatile__( + "bis %0,%0,$27\n\t" + "jmp ($27)" + : /* no outputs: it doesn't even return */ + : "r" (START_ADDR)); +} + +/* Must record the SP (it is virtual) on entry, so we can make sure + not to overwrite it during movement or decompression. */ +unsigned long SP_on_entry; + +/* Calculate the kernel image address based on the end of the BOOTP + bootstrapper (ie this program). +*/ +extern char _end; +#define KERNEL_ORIGIN \ + ((((unsigned long)&_end) + 511) & ~511) + +/* Round address to next higher page boundary. */ +#define NEXT_PAGE(a) (((a) | (PAGE_SIZE - 1)) + 1) + +#ifdef INITRD_IMAGE_SIZE +# define REAL_INITRD_SIZE INITRD_IMAGE_SIZE +#else +# define REAL_INITRD_SIZE 0 +#endif + +/* Defines from include/asm-alpha/system.h + + BOOT_ADDR Virtual address at which the consoles loads + the BOOTP image. + + KERNEL_START KSEG address at which the kernel is built to run, + which includes some initial data pages before the + code. + + START_ADDR KSEG address of the entry point of kernel code. + + ZERO_PGE KSEG address of page full of zeroes, but + upon entry to kerne cvan be expected + to hold the parameter list and possible + INTRD information. + + These are used in the local defines below. +*/ + + +/* Virtual addresses for the BOOTP image. Note that this includes the + bootstrapper code as well as the compressed kernel image, and + possibly the INITRD image. + + Oh, and do NOT forget the STACK, which appears to be placed virtually + beyond the end of the loaded image. +*/ +#define V_BOOT_IMAGE_START BOOT_ADDR +#define V_BOOT_IMAGE_END SP_on_entry + +/* Virtual addresses for just the bootstrapper part of the BOOTP image. */ +#define V_BOOTSTRAPPER_START BOOT_ADDR +#define V_BOOTSTRAPPER_END KERNEL_ORIGIN + +/* Virtual addresses for just the data part of the BOOTP + image. This may also include the INITRD image, but always + includes the STACK. +*/ +#define V_DATA_START KERNEL_ORIGIN +#define V_INITRD_START (KERNEL_ORIGIN + KERNEL_Z_SIZE) +#define V_INTRD_END (V_INITRD_START + REAL_INITRD_SIZE) +#define V_DATA_END V_BOOT_IMAGE_END + +/* KSEG addresses for the uncompressed kernel. + + Note that the end address includes workspace for the decompression. + Note also that the DATA_START address is ZERO_PGE, to which we write + just before jumping to the kernel image at START_ADDR. + */ +#define K_KERNEL_DATA_START ZERO_PGE +#define K_KERNEL_IMAGE_START START_ADDR +#define K_KERNEL_IMAGE_END (START_ADDR + KERNEL_SIZE) + +/* Define to where we may have to decompress the kernel image, before + we move it to the final position, in case of overlap. This will be + above the final position of the kernel. + + Regardless of overlap, we move the INITRD image to the end of this + copy area, because there needs to be a buffer area after the kernel + for "bootmem" anyway. +*/ +#define K_COPY_IMAGE_START NEXT_PAGE(K_KERNEL_IMAGE_END) +/* Reserve one page below INITRD for the new stack. */ +#define K_INITRD_START \ + NEXT_PAGE(K_COPY_IMAGE_START + KERNEL_SIZE + PAGE_SIZE) +#define K_COPY_IMAGE_END \ + (K_INITRD_START + REAL_INITRD_SIZE + MALLOC_AREA_SIZE) +#define K_COPY_IMAGE_SIZE \ + NEXT_PAGE(K_COPY_IMAGE_END - K_COPY_IMAGE_START) + +void +start_kernel(void) +{ + int must_move = 0; + + /* Initialize these for the decompression-in-place situation, + which is the smallest amount of work and most likely to + occur when using the normal START_ADDR of the kernel + (currently set to 16MB, to clear all console code. + */ + unsigned long uncompressed_image_start = K_KERNEL_IMAGE_START; + unsigned long uncompressed_image_end = K_KERNEL_IMAGE_END; + + unsigned long initrd_image_start = K_INITRD_START; + + /* + * Note that this crufty stuff with static and envval + * and envbuf is because: + * + * 1. Frequently, the stack is short, and we don't want to overrun; + * 2. Frequently the stack is where we are going to copy the kernel to; + * 3. A certain SRM console required the GET_ENV output to stack. + * ??? A comment in the aboot sources indicates that the GET_ENV + * destination must be quadword aligned. Might this explain the + * behaviour, rather than requiring output to the stack, which + * seems rather far-fetched. + */ + static long nbytes; + static char envval[256] __attribute__((aligned(8))); + register unsigned long asm_sp asm("30"); + + SP_on_entry = asm_sp; + + srm_printk("Linux/Alpha BOOTPZ Loader for Linux " UTS_RELEASE "\n"); + + /* Validity check the HWRPB. */ + if (INIT_HWRPB->pagesize != 8192) { + srm_printk("Expected 8kB pages, got %ldkB\n", + INIT_HWRPB->pagesize >> 10); + return; + } + if (INIT_HWRPB->vptb != (unsigned long) VPTB) { + srm_printk("Expected vptb at %p, got %p\n", + VPTB, (void *)INIT_HWRPB->vptb); + return; + } + + /* PALcode (re)initialization. */ + pal_init(); + + /* Get the parameter list from the console environment variable. */ + nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); + if (nbytes < 0 || nbytes >= sizeof(envval)) { + nbytes = 0; + } + envval[nbytes] = '\0'; + +#ifdef DEBUG_ADDRESSES + srm_printk("START_ADDR 0x%lx\n", START_ADDR); + srm_printk("KERNEL_ORIGIN 0x%lx\n", KERNEL_ORIGIN); + srm_printk("KERNEL_SIZE 0x%x\n", KERNEL_SIZE); + srm_printk("KERNEL_Z_SIZE 0x%x\n", KERNEL_Z_SIZE); +#endif + + /* Since all the SRM consoles load the BOOTP image at virtual + * 0x20000000, we have to ensure that the physical memory + * pages occupied by that image do NOT overlap the physical + * address range where the kernel wants to be run. This + * causes real problems when attempting to cdecompress the + * former into the latter... :-( + * + * So, we may have to decompress/move the kernel/INITRD image + * virtual-to-physical someplace else first before moving + * kernel /INITRD to their final resting places... ;-} + * + * Sigh... + */ + + /* First, check to see if the range of addresses occupied by + the bootstrapper part of the BOOTP image include any of the + physical pages into which the kernel will be placed for + execution. + + We only need check on the final kernel image range, since we + will put the INITRD someplace that we can be sure is not + in conflict. + */ + if (check_range(V_BOOTSTRAPPER_START, V_BOOTSTRAPPER_END, + K_KERNEL_DATA_START, K_KERNEL_IMAGE_END)) + { + srm_printk("FATAL ERROR: overlap of bootstrapper code\n"); + __halt(); + } + + /* Next, check to see if the range of addresses occupied by + the compressed kernel/INITRD/stack portion of the BOOTP + image include any of the physical pages into which the + decompressed kernel or the INITRD will be placed for + execution. + */ + if (check_range(V_DATA_START, V_DATA_END, + K_KERNEL_IMAGE_START, K_COPY_IMAGE_END)) + { +#ifdef DEBUG_ADDRESSES + srm_printk("OVERLAP: cannot decompress in place\n"); +#endif + uncompressed_image_start = K_COPY_IMAGE_START; + uncompressed_image_end = K_COPY_IMAGE_END; + must_move = 1; + + /* Finally, check to see if the range of addresses + occupied by the compressed kernel/INITRD part of + the BOOTP image include any of the physical pages + into which that part is to be copied for + decompression. + */ + while (check_range(V_DATA_START, V_DATA_END, + uncompressed_image_start, + uncompressed_image_end)) + { +#if 0 + uncompressed_image_start += K_COPY_IMAGE_SIZE; + uncompressed_image_end += K_COPY_IMAGE_SIZE; + initrd_image_start += K_COPY_IMAGE_SIZE; +#else + /* Keep as close as possible to end of BOOTP image. */ + uncompressed_image_start += PAGE_SIZE; + uncompressed_image_end += PAGE_SIZE; + initrd_image_start += PAGE_SIZE; +#endif + } + } + + srm_printk("Starting to load the kernel with args '%s'\n", envval); + +#ifdef DEBUG_ADDRESSES + srm_printk("Decompressing the kernel...\n" + "...from 0x%lx to 0x%lx size 0x%x\n", + V_DATA_START, + uncompressed_image_start, + KERNEL_SIZE); +#endif + decompress_kernel((void *)uncompressed_image_start, + (void *)V_DATA_START, + KERNEL_SIZE, KERNEL_Z_SIZE); + + /* + * Now, move things to their final positions, if/as required. + */ + +#ifdef INITRD_IMAGE_SIZE + + /* First, we always move the INITRD image, if present. */ +#ifdef DEBUG_ADDRESSES + srm_printk("Moving the INITRD image...\n" + " from 0x%lx to 0x%lx size 0x%x\n", + V_INITRD_START, + initrd_image_start, + INITRD_IMAGE_SIZE); +#endif + memcpy((void *)initrd_image_start, (void *)V_INITRD_START, + INITRD_IMAGE_SIZE); + +#endif /* INITRD_IMAGE_SIZE */ + + /* Next, we may have to move the uncompressed kernel to the + final destination. + */ + if (must_move) { +#ifdef DEBUG_ADDRESSES + srm_printk("Moving the uncompressed kernel...\n" + "...from 0x%lx to 0x%lx size 0x%x\n", + uncompressed_image_start, + K_KERNEL_IMAGE_START, + (unsigned)KERNEL_SIZE); +#endif + /* + * Move the stack to a safe place to ensure it won't be + * overwritten by kernel image. + */ + move_stack(initrd_image_start - PAGE_SIZE); + + memcpy((void *)K_KERNEL_IMAGE_START, + (void *)uncompressed_image_start, KERNEL_SIZE); + } + + /* Clear the zero page, then move the argument list in. */ +#ifdef DEBUG_LAST_STEPS + srm_printk("Preparing ZERO_PGE...\n"); +#endif + memset((char*)ZERO_PGE, 0, PAGE_SIZE); + strcpy((char*)ZERO_PGE, envval); + +#ifdef INITRD_IMAGE_SIZE + +#ifdef DEBUG_LAST_STEPS + srm_printk("Preparing INITRD info...\n"); +#endif + /* Finally, set the INITRD paramenters for the kernel. */ + ((long *)(ZERO_PGE+256))[0] = initrd_image_start; + ((long *)(ZERO_PGE+256))[1] = INITRD_IMAGE_SIZE; + +#endif /* INITRD_IMAGE_SIZE */ + +#ifdef DEBUG_LAST_STEPS + srm_printk("Doing 'runkernel()'...\n"); +#endif + runkernel(); +} 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 @@ +/* + * arch/alpha/boot/head.S + * + * initial bootloader stuff.. + */ + +#include <asm/system.h> + + .set noreorder + .globl __start + .ent __start +__start: + br $29,2f +2: ldgp $29,0($29) + jsr $26,start_kernel + call_pal PAL_halt + .end __start + + .align 5 + .globl wrent + .ent wrent +wrent: + .prologue 0 + call_pal PAL_wrent + ret ($26) + .end wrent + + .align 5 + .globl wrkgp + .ent wrkgp +wrkgp: + .prologue 0 + call_pal PAL_wrkgp + ret ($26) + .end wrkgp + + .align 5 + .globl switch_to_osf_pal + .ent switch_to_osf_pal +switch_to_osf_pal: + subq $30,128,$30 + .frame $30,128,$26 + stq $26,0($30) + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + stq $4,32($30) + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $9,72($30) + stq $10,80($30) + stq $11,88($30) + stq $12,96($30) + stq $13,104($30) + stq $14,112($30) + stq $15,120($30) + .prologue 0 + + stq $30,0($17) /* save KSP in PCB */ + + bis $30,$30,$20 /* a4 = KSP */ + br $17,1f + + ldq $26,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $9,72($30) + ldq $10,80($30) + ldq $11,88($30) + ldq $12,96($30) + ldq $13,104($30) + ldq $14,112($30) + ldq $15,120($30) + addq $30,128,$30 + ret ($26) +1: call_pal PAL_swppal + .end switch_to_osf_pal + + .align 3 + .globl tbi + .ent tbi +tbi: + .prologue 0 + call_pal PAL_tbi + ret ($26) + .end tbi + + .align 3 + .globl halt + .ent halt +halt: + .prologue 0 + call_pal PAL_halt + .end halt + +/* $16 - new stack page */ + .align 3 + .globl move_stack + .ent move_stack +move_stack: + .prologue 0 + lda $0, 0x1fff($31) + and $0, $30, $1 /* Stack offset */ + or $1, $16, $16 /* New stack pointer */ + mov $30, $1 + mov $16, $2 +1: ldq $3, 0($1) /* Move the stack */ + addq $1, 8, $1 + stq $3, 0($2) + and $0, $1, $4 + addq $2, 8, $2 + bne $4, 1b + mov $16, $30 + ret ($26) + .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 @@ +/* + * arch/alpha/boot/main.c + * + * Copyright (C) 1994, 1995 Linus Torvalds + * + * This file is the bootloader for the Linux/AXP kernel + */ +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/version.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/console.h> +#include <asm/hwrpb.h> +#include <asm/pgtable.h> + +#include <stdarg.h> + +#include "ksize.h" + +extern int vsprintf(char *, const char *, va_list); +extern unsigned long switch_to_osf_pal(unsigned long nr, + struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, + unsigned long *vptb); +struct hwrpb_struct *hwrpb = INIT_HWRPB; +static struct pcb_struct pcb_va[1]; + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ + +static inline void * +find_pa(unsigned long *vptb, void *ptr) +{ + unsigned long address = (unsigned long) ptr; + unsigned long result; + + result = vptb[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return (void *) result; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + */ + +#define VPTB ((unsigned long *) 0x200000000) +#define L1 ((unsigned long *) 0x200802000) + +void +pal_init(void) +{ + unsigned long i, rev; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Create the dummy PCB. */ + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_va->res1 = 0; + pcb_va->res2 = 0; + pcb_pa = find_pa(VPTB, pcb_va); + + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the vaddr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but the asm sets it) + */ + srm_printk("Switching to OSF PAL-code .. "); + + i = switch_to_osf_pal(2, pcb_va, pcb_pa, VPTB); + if (i) { + srm_printk("failed, code %ld\n", i); + __halt(); + } + + percpu = (struct percpu_struct *) + (INIT_HWRPB->processor_offset + (unsigned long) INIT_HWRPB); + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + srm_printk("Ok (rev %lx)\n", rev); + + tbia(); /* do it directly in case we are SMP */ +} + +static inline long openboot(void) +{ + char bootdev[256]; + long result; + + result = callback_getenv(ENV_BOOTED_DEV, bootdev, 255); + if (result < 0) + return result; + return callback_open(bootdev, result & 255); +} + +static inline long close(long dev) +{ + return callback_close(dev); +} + +static inline long load(long dev, unsigned long addr, unsigned long count) +{ + char bootfile[256]; + extern char _end; + long result, boot_size = &_end - (char *) BOOT_ADDR; + + result = callback_getenv(ENV_BOOTED_FILE, bootfile, 255); + if (result < 0) + return result; + result &= 255; + bootfile[result] = '\0'; + if (result) + srm_printk("Boot file specification (%s) not implemented\n", + bootfile); + return callback_read(dev, count, addr, boot_size/512 + 1); +} + +/* + * Start the kernel. + */ +static void runkernel(void) +{ + __asm__ __volatile__( + "bis %1,%1,$30\n\t" + "bis %0,%0,$26\n\t" + "ret ($26)" + : /* no outputs: it doesn't even return */ + : "r" (START_ADDR), + "r" (PAGE_SIZE + INIT_STACK)); +} + +void start_kernel(void) +{ + long i; + long dev; + int nbytes; + char envval[256]; + + srm_printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n"); + if (INIT_HWRPB->pagesize != 8192) { + srm_printk("Expected 8kB pages, got %ldkB\n", INIT_HWRPB->pagesize >> 10); + return; + } + pal_init(); + dev = openboot(); + if (dev < 0) { + srm_printk("Unable to open boot device: %016lx\n", dev); + return; + } + dev &= 0xffffffff; + srm_printk("Loading vmlinux ..."); + i = load(dev, START_ADDR, KERNEL_SIZE); + close(dev); + if (i != KERNEL_SIZE) { + srm_printk("Failed (%lx)\n", i); + return; + } + + nbytes = callback_getenv(ENV_BOOTED_OSFLAGS, envval, sizeof(envval)); + if (nbytes < 0) { + nbytes = 0; + } + envval[nbytes] = '\0'; + strcpy((char*)ZERO_PGE, envval); + + srm_printk(" Ok\nNow booting the kernel\n"); + runkernel(); + for (i = 0 ; i < 0x100000000 ; i++) + /* nothing */; + __halt(); +} 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 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * + * Modified for ARM Linux by Russell King + * + * Nicolas Pitre <nico@visuaide.com> 1999/04/14 : + * For this code to run directly from Flash, all constant variables must + * be marked with 'const' and all other variables initialized at run-time + * only. This way all non constant variables will end up in the bss segment, + * which should point to addresses in RAM and cleared to 0 on start. + * This allows for a much quicker boot time. + * + * Modified for Alpha, from the ARM version, by Jay Estabrook 2003. + */ + +#include <linux/kernel.h> + +#include <asm/uaccess.h> + +#define memzero(s,n) memset ((s),0,(n)) +#define puts srm_printk +extern long srm_printk(const char *, ...) + __attribute__ ((format (printf, 1, 2))); + +/* + * gzip delarations + */ +#define OF(args) args +#define STATIC static + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch *window; /* Sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static char *input_data; +static int input_data_size; + +static uch *output_data; +static ulg output_ptr; +static ulg bytes_out; + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern int end; +static ulg free_mem_ptr; +static ulg free_mem_ptr_end; + +#define HEAP_SIZE 0x2000 + +#include "../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error"); + if (free_mem_ptr <= 0) error("Memory error"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_ptr_end) + error("Out of memory"); + return p; +} + +static void free(void *where) +{ /* gzip_mark & gzip_release do the free */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +int fill_inbuf(void) +{ + if (insize != 0) + error("ran out of input data"); + + inbuf = input_data; + insize = input_data_size; + + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +void flush_window(void) +{ + ulg c = crc; + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +/* puts("."); */ +} + +static void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +unsigned int +decompress_kernel(void *output_start, + void *input_start, + size_t ksize, + size_t kzsize) +{ + output_data = (uch *)output_start; + input_data = (uch *)input_start; + input_data_size = kzsize; /* use compressed size */ + + /* FIXME FIXME FIXME */ + free_mem_ptr = (ulg)output_start + ksize; + free_mem_ptr_end = (ulg)output_start + ksize + 0x200000; + /* FIXME FIXME FIXME */ + + /* put in temp area to reduce initial footprint */ + window = malloc(WSIZE); + + makecrc(); +/* puts("Uncompressing Linux..."); */ + gunzip(); +/* puts(" done, booting the kernel.\n"); */ + return output_ptr; +} 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 @@ +/* This utility makes a bootblock suitable for the SRM console/miniloader */ + +/* Usage: + * mkbb <device> <lxboot> + * + * Where <device> is the name of the device to install the bootblock on, + * and <lxboot> is the name of a bootblock to merge in. This bootblock + * contains the offset and size of the bootloader. It must be exactly + * 512 bytes long. + */ + +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +/* Minimal definition of disklabel, so we don't have to include + * asm/disklabel.h (confuses make) + */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 /* max. # of partitions */ +#endif + +#ifndef u8 +#define u8 unsigned char +#endif + +#ifndef u16 +#define u16 unsigned short +#endif + +#ifndef u32 +#define u32 unsigned int +#endif + +struct disklabel { + u32 d_magic; /* must be DISKLABELMAGIC */ + u16 d_type, d_subtype; + u8 d_typename[16]; + u8 d_packname[16]; + u32 d_secsize; + u32 d_nsectors; + u32 d_ntracks; + u32 d_ncylinders; + u32 d_secpercyl; + u32 d_secprtunit; + u16 d_sparespertrack; + u16 d_sparespercyl; + u32 d_acylinders; + u16 d_rpm, d_interleave, d_trackskew, d_cylskew; + u32 d_headswitch, d_trkseek, d_flags; + u32 d_drivedata[5]; + u32 d_spare[5]; + u32 d_magic2; /* must be DISKLABELMAGIC */ + u16 d_checksum; + u16 d_npartitions; + u32 d_bbsize, d_sbsize; + struct d_partition { + u32 p_size; + u32 p_offset; + u32 p_fsize; + u8 p_fstype; + u8 p_frag; + u16 p_cpg; + } d_partitions[MAXPARTITIONS]; +}; + + +typedef union __bootblock { + struct { + char __pad1[64]; + struct disklabel __label; + } __u1; + struct { + unsigned long __pad2[63]; + unsigned long __checksum; + } __u2; + char bootblock_bytes[512]; + unsigned long bootblock_quadwords[64]; +} bootblock; + +#define bootblock_label __u1.__label +#define bootblock_checksum __u2.__checksum + +main(int argc, char ** argv) +{ + bootblock bootblock_from_disk; + bootblock bootloader_image; + int dev, fd; + int i; + int nread; + + /* Make sure of the arg count */ + if(argc != 3) { + fprintf(stderr, "Usage: %s device lxboot\n", argv[0]); + exit(0); + } + + /* First, open the device and make sure it's accessible */ + dev = open(argv[1], O_RDWR); + if(dev < 0) { + perror(argv[1]); + exit(0); + } + + /* Now open the lxboot and make sure it's reasonable */ + fd = open(argv[2], O_RDONLY); + if(fd < 0) { + perror(argv[2]); + close(dev); + exit(0); + } + + /* Read in the lxboot */ + nread = read(fd, &bootloader_image, sizeof(bootblock)); + if(nread != sizeof(bootblock)) { + perror("lxboot read"); + fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread); + exit(0); + } + + /* Read in the bootblock from disk. */ + nread = read(dev, &bootblock_from_disk, sizeof(bootblock)); + if(nread != sizeof(bootblock)) { + perror("bootblock read"); + fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread); + exit(0); + } + + /* Swap the bootblock's disklabel into the bootloader */ + bootloader_image.bootblock_label = bootblock_from_disk.bootblock_label; + + /* Calculate the bootblock checksum */ + bootloader_image.bootblock_checksum = 0; + for(i = 0; i < 63; i++) { + bootloader_image.bootblock_checksum += + bootloader_image.bootblock_quadwords[i]; + } + + /* Write the whole thing out! */ + lseek(dev, 0L, SEEK_SET); + if(write(dev, &bootloader_image, sizeof(bootblock)) != sizeof(bootblock)) { + perror("bootblock write"); + exit(0); + } + + close(fd); + close(dev); + exit(0); +} + + 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 @@ +/* + * arch/alpha/boot/tools/objstrip.c + * + * Strip the object file headers/trailers from an executable (ELF or ECOFF). + * + * Copyright (C) 1996 David Mosberger-Tang. + */ +/* + * Converts an ECOFF or ELF object file into a bootable file. The + * object file must be a OMAGIC file (i.e., data and bss follow immediately + * behind the text). See DEC "Assembly Language Programmer's Guide" + * documentation for details. The SRM boot process is documented in + * the Alpha AXP Architecture Reference Manual, Second Edition by + * Richard L. Sites and Richard T. Witek. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <linux/a.out.h> +#include <linux/coff.h> +#include <linux/param.h> +#include <linux/string.h> +#ifdef __ELF__ +# include <linux/elf.h> +#endif + +/* bootfile size must be multiple of BLOCK_SIZE: */ +#define BLOCK_SIZE 512 + +const char * prog_name; + + +void +usage (void) +{ + fprintf(stderr, + "usage: %s [-v] -p file primary\n" + " %s [-vb] file [secondary]\n", prog_name, prog_name); + exit(1); +} + + +int +main (int argc, char *argv[]) +{ + size_t nwritten, tocopy, n, mem_size, fil_size, pad = 0; + int fd, ofd, i, j, verbose = 0, primary = 0; + char buf[8192], *inname; + struct exec * aout; /* includes file & aout header */ + long offset; +#ifdef __ELF__ + struct elfhdr *elf; + struct elf_phdr *elf_phdr; /* program header */ + unsigned long long e_entry; +#endif + + prog_name = argv[0]; + + for (i = 1; i < argc && argv[i][0] == '-'; ++i) { + for (j = 1; argv[i][j]; ++j) { + switch (argv[i][j]) { + case 'v': + verbose = ~verbose; + break; + + case 'b': + pad = BLOCK_SIZE; + break; + + case 'p': + primary = 1; /* make primary bootblock */ + break; + } + } + } + + if (i >= argc) { + usage(); + } + inname = argv[i++]; + + fd = open(inname, O_RDONLY); + if (fd == -1) { + perror("open"); + exit(1); + } + + ofd = 1; + if (i < argc) { + ofd = open(argv[i++], O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd == -1) { + perror("open"); + exit(1); + } + } + + if (primary) { + /* generate bootblock for primary loader */ + + unsigned long bb[64], sum = 0; + struct stat st; + off_t size; + int i; + + if (ofd == 1) { + usage(); + } + + if (fstat(fd, &st) == -1) { + perror("fstat"); + exit(1); + } + + size = (st.st_size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1); + memset(bb, 0, sizeof(bb)); + strcpy((char *) bb, "Linux SRM bootblock"); + bb[60] = size / BLOCK_SIZE; /* count */ + bb[61] = 1; /* starting sector # */ + bb[62] = 0; /* flags---must be 0 */ + for (i = 0; i < 63; ++i) { + sum += bb[i]; + } + bb[63] = sum; + if (write(ofd, bb, sizeof(bb)) != sizeof(bb)) { + perror("boot-block write"); + exit(1); + } + printf("%lu\n", size); + return 0; + } + + /* read and inspect exec header: */ + + if (read(fd, buf, sizeof(buf)) < 0) { + perror("read"); + exit(1); + } + +#ifdef __ELF__ + elf = (struct elfhdr *) buf; + + if (elf->e_ident[0] == 0x7f && strncmp(elf->e_ident + 1, "ELF", 3) == 0) { + if (elf->e_type != ET_EXEC) { + fprintf(stderr, "%s: %s is not an ELF executable\n", + prog_name, inname); + exit(1); + } + if (!elf_check_arch(elf)) { + fprintf(stderr, "%s: is not for this processor (e_machine=%d)\n", + prog_name, elf->e_machine); + exit(1); + } + if (elf->e_phnum != 1) { + fprintf(stderr, + "%s: %d program headers (forgot to link with -N?)\n", + prog_name, elf->e_phnum); + } + + e_entry = elf->e_entry; + + lseek(fd, elf->e_phoff, SEEK_SET); + if (read(fd, buf, sizeof(*elf_phdr)) != sizeof(*elf_phdr)) { + perror("read"); + exit(1); + } + + elf_phdr = (struct elf_phdr *) buf; + offset = elf_phdr->p_offset; + mem_size = elf_phdr->p_memsz; + fil_size = elf_phdr->p_filesz; + + /* work around ELF bug: */ + if (elf_phdr->p_vaddr < e_entry) { + unsigned long delta = e_entry - elf_phdr->p_vaddr; + offset += delta; + mem_size -= delta; + fil_size -= delta; + elf_phdr->p_vaddr += delta; + } + + if (verbose) { + fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n", + prog_name, (long) elf_phdr->p_vaddr, + elf_phdr->p_vaddr + fil_size, offset); + } + } else +#endif + { + aout = (struct exec *) buf; + + if (!(aout->fh.f_flags & COFF_F_EXEC)) { + fprintf(stderr, "%s: %s is not in executable format\n", + prog_name, inname); + exit(1); + } + + if (aout->fh.f_opthdr != sizeof(aout->ah)) { + fprintf(stderr, "%s: %s has unexpected optional header size\n", + prog_name, inname); + exit(1); + } + + if (N_MAGIC(*aout) != OMAGIC) { + fprintf(stderr, "%s: %s is not an OMAGIC file\n", + prog_name, inname); + exit(1); + } + offset = N_TXTOFF(*aout); + fil_size = aout->ah.tsize + aout->ah.dsize; + mem_size = fil_size + aout->ah.bsize; + + if (verbose) { + fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n", + prog_name, aout->ah.text_start, + aout->ah.text_start + fil_size, offset); + } + } + + if (lseek(fd, offset, SEEK_SET) != offset) { + perror("lseek"); + exit(1); + } + + if (verbose) { + fprintf(stderr, "%s: copying %lu byte from %s\n", + prog_name, (unsigned long) fil_size, inname); + } + + tocopy = fil_size; + while (tocopy > 0) { + n = tocopy; + if (n > sizeof(buf)) { + n = sizeof(buf); + } + tocopy -= n; + if ((size_t) read(fd, buf, n) != n) { + perror("read"); + exit(1); + } + do { + nwritten = write(ofd, buf, n); + if ((ssize_t) nwritten == -1) { + perror("write"); + exit(1); + } + n -= nwritten; + } while (n > 0); + } + + if (pad) { + mem_size = ((mem_size + pad - 1) / pad) * pad; + } + + tocopy = mem_size - fil_size; + if (tocopy > 0) { + fprintf(stderr, + "%s: zero-filling bss and aligning to %lu with %lu bytes\n", + prog_name, pad, (unsigned long) tocopy); + + memset(buf, 0x00, sizeof(buf)); + do { + n = tocopy; + if (n > sizeof(buf)) { + n = sizeof(buf); + } + nwritten = write(ofd, buf, n); + if ((ssize_t) nwritten == -1) { + perror("write"); + exit(1); + } + tocopy -= nwritten; + } while (tocopy > 0); + } + return 0; +} 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 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc2 +# Sat Sep 25 15:38:35 2004 +# +CONFIG_ALPHA=y +CONFIG_64BIT=y +CONFIG_MMU=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_ISA_DMA=y +# CONFIG_GENERIC_IOMAP is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System setup +# +CONFIG_ALPHA_GENERIC=y +# CONFIG_ALPHA_ALCOR is not set +# CONFIG_ALPHA_XL is not set +# CONFIG_ALPHA_BOOK1 is not set +# CONFIG_ALPHA_AVANTI_CH is not set +# CONFIG_ALPHA_CABRIOLET is not set +# CONFIG_ALPHA_DP264 is not set +# CONFIG_ALPHA_EB164 is not set +# CONFIG_ALPHA_EB64P_CH is not set +# CONFIG_ALPHA_EB66 is not set +# CONFIG_ALPHA_EB66P is not set +# CONFIG_ALPHA_EIGER is not set +# CONFIG_ALPHA_JENSEN is not set +# CONFIG_ALPHA_LX164 is not set +# CONFIG_ALPHA_LYNX is not set +# CONFIG_ALPHA_MARVEL is not set +# CONFIG_ALPHA_MIATA is not set +# CONFIG_ALPHA_MIKASA is not set +# CONFIG_ALPHA_NAUTILUS is not set +# CONFIG_ALPHA_NONAME_CH is not set +# CONFIG_ALPHA_NORITAKE is not set +# CONFIG_ALPHA_PC164 is not set +# CONFIG_ALPHA_P2K is not set +# CONFIG_ALPHA_RAWHIDE is not set +# CONFIG_ALPHA_RUFFIAN is not set +# CONFIG_ALPHA_RX164 is not set +# CONFIG_ALPHA_SX164 is not set +# CONFIG_ALPHA_SABLE is not set +# CONFIG_ALPHA_SHARK is not set +# CONFIG_ALPHA_TAKARA is not set +# CONFIG_ALPHA_TITAN is not set +# CONFIG_ALPHA_WILDFIRE is not set +CONFIG_ISA=y +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_ALPHA_CORE_AGP=y +CONFIG_ALPHA_BROKEN_IRQ_MASK=y +CONFIG_EISA=y +# CONFIG_SMP is not set +# CONFIG_DISCONTIGMEM is not set +CONFIG_VERBOSE_MCHECK=y +CONFIG_VERBOSE_MCHECK_ON=1 +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y +CONFIG_EISA_PCI_EISA=y +CONFIG_EISA_VIRTUAL_ROOT=y +CONFIG_EISA_NAMES=y +CONFIG_SRM_ENV=m +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_EM86 is not set +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +CONFIG_ISAPNP=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_RAM is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_IDEPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_IDEPCI_SHARE_IRQ is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +CONFIG_BLK_DEV_ALI15X3=y +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +CONFIG_BLK_DEV_CMD64X=y +# CONFIG_BLK_DEV_TRIFLEX is not set +CONFIG_BLK_DEV_CY82C693=y +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AACRAID is not set +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=253 +CONFIG_AIC7XXX_RESET_DELAY_MS=5000 +# CONFIG_AIC7XXX_PROBE_EISA_VL is not set +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +CONFIG_AIC7XXX_REG_PRETTY_PRINT=y +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_SIM710 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK_DEV=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +# CONFIG_IP_NF_CT_ACCT is not set +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +# CONFIG_IP_NF_TFTP is not set +# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +# CONFIG_IP_NF_MATCH_LIMIT is not set +# CONFIG_IP_NF_MATCH_IPRANGE is not set +# CONFIG_IP_NF_MATCH_MAC is not set +# CONFIG_IP_NF_MATCH_PKTTYPE is not set +# CONFIG_IP_NF_MATCH_MARK is not set +# CONFIG_IP_NF_MATCH_MULTIPORT is not set +# CONFIG_IP_NF_MATCH_TOS is not set +# CONFIG_IP_NF_MATCH_RECENT is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_DSCP is not set +# CONFIG_IP_NF_MATCH_AH_ESP is not set +# CONFIG_IP_NF_MATCH_LENGTH is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_MATCH_TCPMSS is not set +# CONFIG_IP_NF_MATCH_HELPER is not set +# CONFIG_IP_NF_MATCH_STATE is not set +# CONFIG_IP_NF_MATCH_CONNTRACK is not set +# CONFIG_IP_NF_MATCH_OWNER is not set +# CONFIG_IP_NF_MATCH_ADDRTYPE is not set +# CONFIG_IP_NF_MATCH_REALM is not set +# CONFIG_IP_NF_MATCH_SCTP is not set +# CONFIG_IP_NF_MATCH_COMMENT is not set +CONFIG_IP_NF_FILTER=m +# CONFIG_IP_NF_TARGET_REJECT is not set +# CONFIG_IP_NF_TARGET_LOG is not set +# CONFIG_IP_NF_TARGET_ULOG is not set +# CONFIG_IP_NF_TARGET_TCPMSS is not set +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +# CONFIG_IP_NF_TARGET_REDIRECT is not set +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_SAME is not set +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_ARPTABLES is not set +CONFIG_IP_NF_COMPAT_IPCHAINS=y +CONFIG_XFRM=y +CONFIG_XFRM_USER=m + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +# CONFIG_EL3 is not set +# CONFIG_3C515 is not set +CONFIG_VORTEX=y +# CONFIG_TYPHOON is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set + +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +# CONFIG_TULIP_NAPI is not set +# CONFIG_DE4X5 is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_DM9102 is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_CS89x0 is not set +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +CONFIG_YELLOWFIN=y +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +CONFIG_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_AUTOFS_FS=m +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V4 is not set +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_OSF_PARTITION=y +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_INFO=y +CONFIG_EARLY_PRINTK=y +# CONFIG_DEBUG_RWLOCK is not set +# CONFIG_DEBUG_SEMAPHORE is not set +CONFIG_ALPHA_LEGACY_START_ADDRESS=y +CONFIG_MATHEMU=y + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=m +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WHIRLPOOL is not set +CONFIG_CRYPTO_DES=m +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# 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 @@ +# +# Makefile for the linux kernel. +# + +extra-y := head.o vmlinux.lds +EXTRA_AFLAGS := $(CFLAGS) +EXTRA_CFLAGS := -Werror -Wno-sign-compare + +obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \ + irq_alpha.o signal.o setup.o ptrace.o time.o semaphore.o \ + alpha_ksyms.o systbls.o err_common.o io.o + +obj-$(CONFIG_VGA_HOSE) += console.o +obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_PCI) += pci.o pci_iommu.o +obj-$(CONFIG_SRM_ENV) += srm_env.o +obj-$(CONFIG_MODULES) += module.o + +ifdef CONFIG_ALPHA_GENERIC + +obj-y += core_apecs.o core_cia.o core_irongate.o core_lca.o \ + core_mcpcia.o core_polaris.o core_t2.o \ + core_tsunami.o + +obj-y += sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o sys_eiger.o \ + sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \ + sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \ + sys_sable.o sys_sio.o sys_sx164.o sys_takara.o + +ifndef CONFIG_ALPHA_LEGACY_START_ADDRESS +obj-y += core_marvel.o core_titan.o core_wildfire.o +obj-y += sys_marvel.o sys_titan.o sys_wildfire.o +obj-y += err_ev7.o err_titan.o err_marvel.o +endif + +obj-y += irq_pyxis.o irq_i8259.o irq_srm.o +obj-y += err_ev6.o +obj-y += es1888.o smc37c669.o smc37c93x.o ns87312.o gct.o +obj-y += srmcons.o + +else + +# Misc support +obj-$(CONFIG_ALPHA_SRM) += srmcons.o + +# Core logic support +obj-$(CONFIG_ALPHA_APECS) += core_apecs.o +obj-$(CONFIG_ALPHA_CIA) += core_cia.o +obj-$(CONFIG_ALPHA_IRONGATE) += core_irongate.o +obj-$(CONFIG_ALPHA_LCA) += core_lca.o +obj-$(CONFIG_ALPHA_MARVEL) += core_marvel.o gct.o +obj-$(CONFIG_ALPHA_MCPCIA) += core_mcpcia.o +obj-$(CONFIG_ALPHA_POLARIS) += core_polaris.o +obj-$(CONFIG_ALPHA_T2) += core_t2.o +obj-$(CONFIG_ALPHA_TSUNAMI) += core_tsunami.o +obj-$(CONFIG_ALPHA_TITAN) += core_titan.o +obj-$(CONFIG_ALPHA_WILDFIRE) += core_wildfire.o + +# Board support +obj-$(CONFIG_ALPHA_ALCOR) += sys_alcor.o irq_i8259.o irq_srm.o +obj-$(CONFIG_ALPHA_CABRIOLET) += sys_cabriolet.o irq_i8259.o irq_srm.o \ + ns87312.o +obj-$(CONFIG_ALPHA_EB164) += sys_cabriolet.o irq_i8259.o irq_srm.o \ + ns87312.o +obj-$(CONFIG_ALPHA_EB66P) += sys_cabriolet.o irq_i8259.o irq_srm.o \ + ns87312.o +obj-$(CONFIG_ALPHA_LX164) += sys_cabriolet.o irq_i8259.o irq_srm.o \ + smc37c93x.o +obj-$(CONFIG_ALPHA_PC164) += sys_cabriolet.o irq_i8259.o irq_srm.o \ + smc37c93x.o +obj-$(CONFIG_ALPHA_DP264) += sys_dp264.o irq_i8259.o es1888.o smc37c669.o +obj-$(CONFIG_ALPHA_SHARK) += sys_dp264.o irq_i8259.o es1888.o smc37c669.o +obj-$(CONFIG_ALPHA_TITAN) += sys_titan.o irq_i8259.o smc37c669.o +obj-$(CONFIG_ALPHA_EB64P) += sys_eb64p.o irq_i8259.o +obj-$(CONFIG_ALPHA_EB66) += sys_eb64p.o irq_i8259.o +obj-$(CONFIG_ALPHA_EIGER) += sys_eiger.o irq_i8259.o +obj-$(CONFIG_ALPHA_JENSEN) += sys_jensen.o pci-noop.o irq_i8259.o +obj-$(CONFIG_ALPHA_MARVEL) += sys_marvel.o +obj-$(CONFIG_ALPHA_MIATA) += sys_miata.o irq_pyxis.o irq_i8259.o \ + es1888.o smc37c669.o +obj-$(CONFIG_ALPHA_MIKASA) += sys_mikasa.o irq_i8259.o irq_srm.o +obj-$(CONFIG_ALPHA_NAUTILUS) += sys_nautilus.o irq_i8259.o irq_srm.o +obj-$(CONFIG_ALPHA_NORITAKE) += sys_noritake.o irq_i8259.o +obj-$(CONFIG_ALPHA_RAWHIDE) += sys_rawhide.o irq_i8259.o +obj-$(CONFIG_ALPHA_RUFFIAN) += sys_ruffian.o irq_pyxis.o irq_i8259.o +obj-$(CONFIG_ALPHA_RX164) += sys_rx164.o irq_i8259.o +obj-$(CONFIG_ALPHA_SABLE) += sys_sable.o +obj-$(CONFIG_ALPHA_LYNX) += sys_sable.o +obj-$(CONFIG_ALPHA_BOOK1) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o +obj-$(CONFIG_ALPHA_AVANTI) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o +obj-$(CONFIG_ALPHA_NONAME) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o +obj-$(CONFIG_ALPHA_P2K) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o +obj-$(CONFIG_ALPHA_XL) += sys_sio.o irq_i8259.o irq_srm.o ns87312.o +obj-$(CONFIG_ALPHA_SX164) += sys_sx164.o irq_pyxis.o irq_i8259.o \ + irq_srm.o smc37c669.o +obj-$(CONFIG_ALPHA_TAKARA) += sys_takara.o irq_i8259.o ns87312.o +obj-$(CONFIG_ALPHA_WILDFIRE) += sys_wildfire.o irq_i8259.o + +# Error support +obj-$(CONFIG_ALPHA_MARVEL) += err_ev7.o err_marvel.o +obj-$(CONFIG_ALPHA_NAUTILUS) += err_ev6.o +obj-$(CONFIG_ALPHA_TITAN) += err_ev6.o err_titan.o + +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 @@ +/* + * linux/arch/alpha/kernel/ksyms.c + * + * Export the alpha-specific functions that are needed for loadable + * modules. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/user.h> +#include <linux/elfcore.h> +#include <linux/socket.h> +#include <linux/syscalls.h> +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/pci.h> +#include <linux/tty.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/console.h> +#include <asm/hwrpb.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/checksum.h> +#include <linux/interrupt.h> +#include <asm/fpu.h> +#include <asm/irq.h> +#include <asm/machvec.h> +#include <asm/pgalloc.h> +#include <asm/semaphore.h> +#include <asm/tlbflush.h> +#include <asm/cacheflush.h> +#include <asm/vga.h> + +#define __KERNEL_SYSCALLS__ +#include <asm/unistd.h> + +extern struct hwrpb_struct *hwrpb; +extern void dump_thread(struct pt_regs *, struct user *); +extern spinlock_t rtc_lock; + +/* these are C runtime functions with special calling conventions: */ +extern void __divl (void); +extern void __reml (void); +extern void __divq (void); +extern void __remq (void); +extern void __divlu (void); +extern void __remlu (void); +extern void __divqu (void); +extern void __remqu (void); + +EXPORT_SYMBOL(alpha_mv); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(probe_irq_mask); +EXPORT_SYMBOL(screen_info); +EXPORT_SYMBOL(perf_irq); +EXPORT_SYMBOL(callback_getenv); +EXPORT_SYMBOL(callback_setenv); +EXPORT_SYMBOL(callback_save_env); +#ifdef CONFIG_ALPHA_GENERIC +EXPORT_SYMBOL(alpha_using_srm); +#endif /* CONFIG_ALPHA_GENERIC */ + +/* platform dependent support */ +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(__memcpy); +EXPORT_SYMBOL(__memset); +EXPORT_SYMBOL(__memsetw); +EXPORT_SYMBOL(__constant_c_memset); +EXPORT_SYMBOL(copy_page); +EXPORT_SYMBOL(clear_page); + +EXPORT_SYMBOL(__direct_map_base); +EXPORT_SYMBOL(__direct_map_size); + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); +EXPORT_SYMBOL(pci_map_single); +EXPORT_SYMBOL(pci_map_page); +EXPORT_SYMBOL(pci_unmap_single); +EXPORT_SYMBOL(pci_unmap_page); +EXPORT_SYMBOL(pci_map_sg); +EXPORT_SYMBOL(pci_unmap_sg); +EXPORT_SYMBOL(pci_dma_supported); +EXPORT_SYMBOL(pci_dac_dma_supported); +EXPORT_SYMBOL(pci_dac_page_to_dma); +EXPORT_SYMBOL(pci_dac_dma_to_page); +EXPORT_SYMBOL(pci_dac_dma_to_offset); +EXPORT_SYMBOL(alpha_gendev_to_pci); +#endif +EXPORT_SYMBOL(dma_set_mask); + +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(dump_elf_thread); +EXPORT_SYMBOL(dump_elf_task); +EXPORT_SYMBOL(dump_elf_task_fp); +EXPORT_SYMBOL(hwrpb); +EXPORT_SYMBOL(start_thread); +EXPORT_SYMBOL(alpha_read_fp_reg); +EXPORT_SYMBOL(alpha_read_fp_reg_s); +EXPORT_SYMBOL(alpha_write_fp_reg); +EXPORT_SYMBOL(alpha_write_fp_reg_s); + +/* In-kernel system calls. */ +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(sys_open); +EXPORT_SYMBOL(sys_dup); +EXPORT_SYMBOL(sys_exit); +EXPORT_SYMBOL(sys_write); +EXPORT_SYMBOL(sys_read); +EXPORT_SYMBOL(sys_lseek); +EXPORT_SYMBOL(execve); +EXPORT_SYMBOL(sys_setsid); +EXPORT_SYMBOL(sys_wait4); + +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_tcpudp_magic); +EXPORT_SYMBOL(ip_compute_csum); +EXPORT_SYMBOL(ip_fast_csum); +EXPORT_SYMBOL(csum_partial_copy_nocheck); +EXPORT_SYMBOL(csum_partial_copy_from_user); +EXPORT_SYMBOL(csum_ipv6_magic); + +#ifdef CONFIG_MATHEMU_MODULE +extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); +extern long (*alpha_fp_emul) (unsigned long pc); +EXPORT_SYMBOL(alpha_fp_emul_imprecise); +EXPORT_SYMBOL(alpha_fp_emul); +#endif + +#ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK +EXPORT_SYMBOL(__min_ipl); +#endif + +/* + * The following are specially called from the uaccess assembly stubs. + */ +EXPORT_SYMBOL(__copy_user); +EXPORT_SYMBOL(__do_clear_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(__strnlen_user); + +/* Semaphore helper functions. */ +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_failed_interruptible); +EXPORT_SYMBOL(__up_wakeup); +EXPORT_SYMBOL(down); +EXPORT_SYMBOL(down_interruptible); +EXPORT_SYMBOL(down_trylock); +EXPORT_SYMBOL(up); + +/* + * SMP-specific symbols. + */ + +#ifdef CONFIG_SMP +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(flush_tlb_mm); +EXPORT_SYMBOL(flush_tlb_range); +EXPORT_SYMBOL(flush_tlb_page); +EXPORT_SYMBOL(smp_imb); +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(smp_num_cpus); +EXPORT_SYMBOL(smp_call_function); +EXPORT_SYMBOL(smp_call_function_on_cpu); +EXPORT_SYMBOL(_atomic_dec_and_lock); +#ifdef CONFIG_DEBUG_SPINLOCK +EXPORT_SYMBOL(_raw_spin_unlock); +EXPORT_SYMBOL(debug_spin_lock); +EXPORT_SYMBOL(debug_spin_trylock); +#endif +#ifdef CONFIG_DEBUG_RWLOCK +EXPORT_SYMBOL(_raw_write_lock); +EXPORT_SYMBOL(_raw_read_lock); +#endif +EXPORT_SYMBOL(cpu_present_mask); +#endif /* CONFIG_SMP */ + +/* + * NUMA specific symbols + */ +#ifdef CONFIG_DISCONTIGMEM +EXPORT_SYMBOL(node_data); +#endif /* CONFIG_DISCONTIGMEM */ + +EXPORT_SYMBOL(rtc_lock); + +/* + * The following are special because they're not called + * explicitly (the C compiler or assembler generates them in + * response to division operations). Fortunately, their + * interface isn't gonna change any time soon now, so it's OK + * to leave it out of version control. + */ +# undef memcpy +# undef memset +EXPORT_SYMBOL(__divl); +EXPORT_SYMBOL(__divlu); +EXPORT_SYMBOL(__divq); +EXPORT_SYMBOL(__divqu); +EXPORT_SYMBOL(__reml); +EXPORT_SYMBOL(__remlu); +EXPORT_SYMBOL(__remq); +EXPORT_SYMBOL(__remqu); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memchr); + +EXPORT_SYMBOL(get_wchan); + +#ifdef CONFIG_ALPHA_IRONGATE +EXPORT_SYMBOL(irongate_ioremap); +EXPORT_SYMBOL(irongate_iounmap); +#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 @@ +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + */ + +#include <linux/types.h> +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <asm/io.h> + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +void foo(void) +{ + DEFINE(TI_TASK, offsetof(struct thread_info, task)); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); + BLANK(); + + DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); + DEFINE(TASK_UID, offsetof(struct task_struct, uid)); + DEFINE(TASK_EUID, offsetof(struct task_struct, euid)); + DEFINE(TASK_GID, offsetof(struct task_struct, gid)); + DEFINE(TASK_EGID, offsetof(struct task_struct, egid)); + DEFINE(TASK_REAL_PARENT, offsetof(struct task_struct, real_parent)); + DEFINE(TASK_TGID, offsetof(struct task_struct, tgid)); + BLANK(); + + DEFINE(SIZEOF_PT_REGS, sizeof(struct pt_regs)); + DEFINE(PT_PTRACED, PT_PTRACED); + DEFINE(CLONE_VM, CLONE_VM); + DEFINE(CLONE_UNTRACED, CLONE_UNTRACED); + DEFINE(SIGCHLD, SIGCHLD); + BLANK(); + + DEFINE(HAE_CACHE, offsetof(struct alpha_machine_vector, hae_cache)); + DEFINE(HAE_REG, offsetof(struct alpha_machine_vector, hae_register)); +} 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 @@ +/* + * linux/arch/alpha/kernel/console.c + * + * Architecture-specific specific support for VGA device on + * non-0 I/O hose + */ + +#include <linux/config.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/console.h> +#include <asm/vga.h> +#include <asm/machvec.h> + +#ifdef CONFIG_VGA_HOSE + +/* + * Externally-visible vga hose bases + */ +unsigned long __vga_hose_io_base = 0; /* base for default hose */ +unsigned long __vga_hose_mem_base = 0; /* base for default hose */ + +static struct pci_controller * __init +default_vga_hose_select(struct pci_controller *h1, struct pci_controller *h2) +{ + if (h2->index < h1->index) + return h2; + + return h1; +} + +void __init +set_vga_hose(struct pci_controller *hose) +{ + if (hose) { + __vga_hose_io_base = hose->io_space->start; + __vga_hose_mem_base = hose->mem_space->start; + } +} + +void __init +locate_and_init_vga(void *(*sel_func)(void *, void *)) +{ + struct pci_controller *hose = NULL; + struct pci_dev *dev = NULL; + + if (!sel_func) sel_func = (void *)default_vga_hose_select; + + for(dev=NULL; (dev=pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, dev));) { + if (!hose) hose = dev->sysdata; + else hose = sel_func(hose, dev->sysdata); + } + + /* Did we already inititialize the correct one? */ + if (conswitchp == &vga_con && + __vga_hose_io_base == hose->io_space->start && + __vga_hose_mem_base == hose->mem_space->start) + return; + + /* Set the VGA hose and init the new console */ + set_vga_hose(hose); + take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1); +} + +#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 @@ +/* + * linux/arch/alpha/kernel/core_apecs.c + * + * Rewritten for Apecs from the lca.c from: + * + * Written by David Mosberger (davidm@cs.arizona.edu) with some code + * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit + * bios code. + * + * Code common to all APECS core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_apecs.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/init.h> + +#include <asm/ptrace.h> +#include <asm/smp.h> + +#include "proto.h" +#include "pci_impl.h" + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBGC(args) printk args +#else +# define DBGC(args) +#endif + +#define vuip volatile unsigned int * + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the APECS_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + unsigned long addr; + u8 bus = pbus->number; + + DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + int device = device_fn >> 3; + + /* type 0 configuration cycle: */ + + if (device > 20) { + DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); + return -1; + } + + *type1 = 0; + addr = (device_fn << 8) | (where); + } else { + /* type 1 configuration cycle: */ + *type1 = 1; + addr = (bus << 16) | (device_fn << 8) | (where); + } + *pci_addr = addr; + DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static unsigned int +conf_read(unsigned long addr, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0, value; + unsigned int haxr2 = 0; + + local_irq_save(flags); /* avoid getting hit by machine check */ + + DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + + /* Reset status register to avoid losing errors. */ + stat0 = *(vuip)APECS_IOC_DCSR; + *(vuip)APECS_IOC_DCSR = stat0; + mb(); + DBGC(("conf_read: APECS DCSR was 0x%x\n", stat0)); + + /* If Type1 access, must set HAE #2. */ + if (type1) { + haxr2 = *(vuip)APECS_IOC_HAXR2; + mb(); + *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; + DBGC(("conf_read: TYPE1 access\n")); + } + + draina(); + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + + /* Access configuration space. */ + + /* Some SRMs step on these registers during a machine check. */ + asm volatile("ldl %0,%1; mb; mb" : "=r"(value) : "m"(*(vuip)addr) + : "$9", "$10", "$11", "$12", "$13", "$14", "memory"); + + if (mcheck_taken(0)) { + mcheck_taken(0) = 0; + value = 0xffffffffU; + mb(); + } + mcheck_expected(0) = 0; + mb(); + +#if 1 + /* + * david.rusling@reo.mts.dec.com. This code is needed for the + * EB64+ as it does not generate a machine check (why I don't + * know). When we build kernels for one particular platform + * then we can make this conditional on the type. + */ + draina(); + + /* Now look for any errors. */ + stat0 = *(vuip)APECS_IOC_DCSR; + DBGC(("conf_read: APECS DCSR after read 0x%x\n", stat0)); + + /* Is any error bit set? */ + if (stat0 & 0xffe0U) { + /* If not NDEV, print status. */ + if (!(stat0 & 0x0800)) { + printk("apecs.c:conf_read: got stat0=%x\n", stat0); + } + + /* Reset error status. */ + *(vuip)APECS_IOC_DCSR = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + value = 0xffffffff; + } +#endif + + /* If Type1 access, must reset HAE #2 so normal IO space ops work. */ + if (type1) { + *(vuip)APECS_IOC_HAXR2 = haxr2 & ~1; + mb(); + } + local_irq_restore(flags); + + return value; +} + +static void +conf_write(unsigned long addr, unsigned int value, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0; + unsigned int haxr2 = 0; + + local_irq_save(flags); /* avoid getting hit by machine check */ + + /* Reset status register to avoid losing errors. */ + stat0 = *(vuip)APECS_IOC_DCSR; + *(vuip)APECS_IOC_DCSR = stat0; + mb(); + + /* If Type1 access, must set HAE #2. */ + if (type1) { + haxr2 = *(vuip)APECS_IOC_HAXR2; + mb(); + *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; + } + + draina(); + mcheck_expected(0) = 1; + mb(); + + /* Access configuration space. */ + *(vuip)addr = value; + mb(); + mb(); /* magic */ + mcheck_expected(0) = 0; + mb(); + +#if 1 + /* + * david.rusling@reo.mts.dec.com. This code is needed for the + * EB64+ as it does not generate a machine check (why I don't + * know). When we build kernels for one particular platform + * then we can make this conditional on the type. + */ + draina(); + + /* Now look for any errors. */ + stat0 = *(vuip)APECS_IOC_DCSR; + + /* Is any error bit set? */ + if (stat0 & 0xffe0U) { + /* If not NDEV, print status. */ + if (!(stat0 & 0x0800)) { + printk("apecs.c:conf_write: got stat0=%x\n", stat0); + } + + /* Reset error status. */ + *(vuip)APECS_IOC_DCSR = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + } +#endif + + /* If Type1 access, must reset HAE #2 so normal IO space ops work. */ + if (type1) { + *(vuip)APECS_IOC_HAXR2 = haxr2 & ~1; + mb(); + } + local_irq_restore(flags); +} + +static int +apecs_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr, pci_addr; + unsigned char type1; + long mask; + int shift; + + if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + shift = (where & 3) * 8; + addr = (pci_addr << 5) + mask + APECS_CONF; + *value = conf_read(addr, type1) >> (shift); + return PCIBIOS_SUCCESSFUL; +} + +static int +apecs_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr, pci_addr; + unsigned char type1; + long mask; + + if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + addr = (pci_addr << 5) + mask + APECS_CONF; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops apecs_pci_ops = +{ + .read = apecs_read_config, + .write = apecs_write_config, +}; + +void +apecs_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vip)APECS_IOC_TBIA = 0; + mb(); +} + +void __init +apecs_init_arch(void) +{ + struct pci_controller *hose; + + /* + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + hose->sparse_mem_base = APECS_SPARSE_MEM - IDENT_ADDR; + hose->dense_mem_base = APECS_DENSE_MEM - IDENT_ADDR; + hose->sparse_io_base = APECS_IO - IDENT_ADDR; + hose->dense_io_base = 0; + + /* + * Set up the PCI to main memory translation windows. + * + * Window 1 is direct access 1GB at 1GB + * Window 2 is scatter-gather 8MB at 8MB (for isa) + */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = NULL; + __direct_map_base = 0x40000000; + __direct_map_size = 0x40000000; + + *(vuip)APECS_IOC_PB1R = __direct_map_base | 0x00080000; + *(vuip)APECS_IOC_PM1R = (__direct_map_size - 1) & 0xfff00000U; + *(vuip)APECS_IOC_TB1R = 0; + + *(vuip)APECS_IOC_PB2R = hose->sg_isa->dma_base | 0x000c0000; + *(vuip)APECS_IOC_PM2R = (hose->sg_isa->size - 1) & 0xfff00000; + *(vuip)APECS_IOC_TB2R = virt_to_phys(hose->sg_isa->ptes) >> 1; + + apecs_pci_tbi(hose, 0, -1); + + /* + * Finally, clear the HAXR2 register, which gets used + * for PCI Config Space accesses. That is the way + * we want to use it, and we do not want to depend on + * what ARC or SRM might have left behind... + */ + *(vuip)APECS_IOC_HAXR2 = 0; + mb(); +} + +void +apecs_pci_clr_err(void) +{ + unsigned int jd; + + jd = *(vuip)APECS_IOC_DCSR; + if (jd & 0xffe0L) { + *(vuip)APECS_IOC_SEAR; + *(vuip)APECS_IOC_DCSR = jd | 0xffe1L; + mb(); + *(vuip)APECS_IOC_DCSR; + } + *(vuip)APECS_IOC_TBIA = (unsigned int)APECS_IOC_TBIA; + mb(); + *(vuip)APECS_IOC_TBIA; +} + +void +apecs_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + struct el_common *mchk_header; + struct el_apecs_procdata *mchk_procdata; + struct el_apecs_sysdata_mcheck *mchk_sysdata; + + mchk_header = (struct el_common *)la_ptr; + + mchk_procdata = (struct el_apecs_procdata *) + (la_ptr + mchk_header->proc_offset + - sizeof(mchk_procdata->paltemp)); + + mchk_sysdata = (struct el_apecs_sysdata_mcheck *) + (la_ptr + mchk_header->sys_offset); + + + /* Clear the error before any reporting. */ + mb(); + mb(); /* magic */ + draina(); + apecs_pci_clr_err(); + wrmces(0x7); /* reset machine check pending flag */ + mb(); + + process_mcheck_info(vector, la_ptr, regs, "APECS", + (mcheck_expected(0) + && (mchk_sysdata->epic_dcsr & 0x0c00UL))); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_cia.c + * + * Written by David A Rusling (david.rusling@reo.mts.dec.com). + * December 1995. + * + * Copyright (C) 1995 David A Rusling + * Copyright (C) 1997, 1998 Jay Estabrook + * Copyright (C) 1998, 1999, 2000 Richard Henderson + * + * Code common to all CIA core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_cia.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/bootmem.h> + +#include <asm/ptrace.h> + +#include "proto.h" +#include "pci_impl.h" + + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +#define DEBUG_CONFIG 0 +#if DEBUG_CONFIG +# define DBGC(args) printk args +#else +# define DBGC(args) +#endif + +#define vip volatile int * + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address. It is therefore not safe to have + * concurrent invocations to configuration space access routines, but + * there really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |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| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *bus_dev, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + u8 bus = bus_dev->number; + + *type1 = (bus != 0); + *pci_addr = (bus << 16) | (device_fn << 8) | where; + + DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " returning address 0x%p\n" + bus, device_fn, where, *pci_addr)); + + return 0; +} + +static unsigned int +conf_read(unsigned long addr, unsigned char type1) +{ + unsigned long flags; + int stat0, value; + int cia_cfg = 0; + + DBGC(("conf_read(addr=0x%lx, type1=%d) ", addr, type1)); + local_irq_save(flags); + + /* Reset status register to avoid losing errors. */ + stat0 = *(vip)CIA_IOC_CIA_ERR; + *(vip)CIA_IOC_CIA_ERR = stat0; + mb(); + *(vip)CIA_IOC_CIA_ERR; /* re-read to force write */ + + /* If Type1 access, must set CIA CFG. */ + if (type1) { + cia_cfg = *(vip)CIA_IOC_CFG; + *(vip)CIA_IOC_CFG = (cia_cfg & ~3) | 1; + mb(); + *(vip)CIA_IOC_CFG; + } + + mb(); + draina(); + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + + /* Access configuration space. */ + value = *(vip)addr; + mb(); + mb(); /* magic */ + if (mcheck_taken(0)) { + mcheck_taken(0) = 0; + value = 0xffffffff; + mb(); + } + mcheck_expected(0) = 0; + mb(); + + /* If Type1 access, must reset IOC CFG so normal IO space ops work. */ + if (type1) { + *(vip)CIA_IOC_CFG = cia_cfg; + mb(); + *(vip)CIA_IOC_CFG; + } + + local_irq_restore(flags); + DBGC(("done\n")); + + return value; +} + +static void +conf_write(unsigned long addr, unsigned int value, unsigned char type1) +{ + unsigned long flags; + int stat0, cia_cfg = 0; + + DBGC(("conf_write(addr=0x%lx, type1=%d) ", addr, type1)); + local_irq_save(flags); + + /* Reset status register to avoid losing errors. */ + stat0 = *(vip)CIA_IOC_CIA_ERR; + *(vip)CIA_IOC_CIA_ERR = stat0; + mb(); + *(vip)CIA_IOC_CIA_ERR; /* re-read to force write */ + + /* If Type1 access, must set CIA CFG. */ + if (type1) { + cia_cfg = *(vip)CIA_IOC_CFG; + *(vip)CIA_IOC_CFG = (cia_cfg & ~3) | 1; + mb(); + *(vip)CIA_IOC_CFG; + } + + mb(); + draina(); + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + + /* Access configuration space. */ + *(vip)addr = value; + mb(); + *(vip)addr; /* read back to force the write */ + + mcheck_expected(0) = 0; + mb(); + + /* If Type1 access, must reset IOC CFG so normal IO space ops work. */ + if (type1) { + *(vip)CIA_IOC_CFG = cia_cfg; + mb(); + *(vip)CIA_IOC_CFG; + } + + local_irq_restore(flags); + DBGC(("done\n")); +} + +static int +cia_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, + u32 *value) +{ + unsigned long addr, pci_addr; + long mask; + unsigned char type1; + int shift; + + if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + shift = (where & 3) * 8; + addr = (pci_addr << 5) + mask + CIA_CONF; + *value = conf_read(addr, type1) >> (shift); + return PCIBIOS_SUCCESSFUL; +} + +static int +cia_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, + u32 value) +{ + unsigned long addr, pci_addr; + long mask; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + addr = (pci_addr << 5) + mask + CIA_CONF; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops cia_pci_ops = +{ + .read = cia_read_config, + .write = cia_write_config, +}; + +/* + * CIA Pass 1 and PYXIS Pass 1 and 2 have a broken scatter-gather tlb. + * It cannot be invalidated. Rather than hard code the pass numbers, + * actually try the tbia to see if it works. + */ + +void +cia_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vip)CIA_IOC_PCI_TBIA = 3; /* Flush all locked and unlocked. */ + mb(); + *(vip)CIA_IOC_PCI_TBIA; +} + +/* + * On PYXIS, even if the tbia works, we cannot use it. It effectively locks + * the chip (as well as direct write to the tag registers) if there is a + * SG DMA operation in progress. This is true at least for PYXIS rev. 1, + * so always use the method below. + */ +/* + * This is the method NT and NetBSD use. + * + * Allocate mappings, and put the chip into DMA loopback mode to read a + * garbage page. This works by causing TLB misses, causing old entries to + * be purged to make room for the new entries coming in for the garbage page. + */ + +#define CIA_BROKEN_TBIA_BASE 0x30000000 +#define CIA_BROKEN_TBIA_SIZE 1024 + +/* Always called with interrupts disabled */ +void +cia_pci_tbi_try2(struct pci_controller *hose, + dma_addr_t start, dma_addr_t end) +{ + void __iomem *bus_addr; + int ctrl; + + /* Put the chip into PCI loopback mode. */ + mb(); + ctrl = *(vip)CIA_IOC_CIA_CTRL; + *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN; + mb(); + *(vip)CIA_IOC_CIA_CTRL; + mb(); + + /* Read from PCI dense memory space at TBI_ADDR, skipping 32k on + each read. This forces SG TLB misses. NetBSD claims that the + TLB entries are not quite LRU, meaning that we need to read more + times than there are actual tags. The 2117x docs claim strict + round-robin. Oh well, we've come this far... */ + /* Even better - as seen on the PYXIS rev 1 the TLB tags 0-3 can + be filled by the TLB misses *only once* after being invalidated + (by tbia or direct write). Next misses won't update them even + though the lock bits are cleared. Tags 4-7 are "quite LRU" though, + so use them and read at window 3 base exactly 4 times. Reading + more sometimes makes the chip crazy. -ink */ + + bus_addr = cia_ioremap(CIA_BROKEN_TBIA_BASE, 32768 * 4); + + cia_readl(bus_addr + 0x00000); + cia_readl(bus_addr + 0x08000); + cia_readl(bus_addr + 0x10000); + cia_readl(bus_addr + 0x18000); + + cia_iounmap(bus_addr); + + /* Restore normal PCI operation. */ + mb(); + *(vip)CIA_IOC_CIA_CTRL = ctrl; + mb(); + *(vip)CIA_IOC_CIA_CTRL; + mb(); +} + +static inline void +cia_prepare_tbia_workaround(int window) +{ + unsigned long *ppte, pte; + long i; + + /* Use minimal 1K map. */ + ppte = __alloc_bootmem(CIA_BROKEN_TBIA_SIZE, 32768, 0); + pte = (virt_to_phys(ppte) >> (PAGE_SHIFT - 1)) | 1; + + for (i = 0; i < CIA_BROKEN_TBIA_SIZE / sizeof(unsigned long); ++i) + ppte[i] = pte; + + *(vip)CIA_IOC_PCI_Wn_BASE(window) = CIA_BROKEN_TBIA_BASE | 3; + *(vip)CIA_IOC_PCI_Wn_MASK(window) + = (CIA_BROKEN_TBIA_SIZE*1024 - 1) & 0xfff00000; + *(vip)CIA_IOC_PCI_Tn_BASE(window) = virt_to_phys(ppte) >> 2; +} + +static void __init +verify_tb_operation(void) +{ + static int page[PAGE_SIZE/4] + __attribute__((aligned(PAGE_SIZE))) + __initdata = { 0 }; + + struct pci_iommu_arena *arena = pci_isa_hose->sg_isa; + int ctrl, addr0, tag0, pte0, data0; + int temp, use_tbia_try2 = 0; + void __iomem *bus_addr; + + /* pyxis -- tbia is broken */ + if (pci_isa_hose->dense_io_base) + use_tbia_try2 = 1; + + /* Put the chip into PCI loopback mode. */ + mb(); + ctrl = *(vip)CIA_IOC_CIA_CTRL; + *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN; + mb(); + *(vip)CIA_IOC_CIA_CTRL; + mb(); + + /* Write a valid entry directly into the TLB registers. */ + + addr0 = arena->dma_base; + tag0 = addr0 | 1; + pte0 = (virt_to_phys(page) >> (PAGE_SHIFT - 1)) | 1; + + *(vip)CIA_IOC_TB_TAGn(0) = tag0; + *(vip)CIA_IOC_TB_TAGn(1) = 0; + *(vip)CIA_IOC_TB_TAGn(2) = 0; + *(vip)CIA_IOC_TB_TAGn(3) = 0; + *(vip)CIA_IOC_TB_TAGn(4) = 0; + *(vip)CIA_IOC_TB_TAGn(5) = 0; + *(vip)CIA_IOC_TB_TAGn(6) = 0; + *(vip)CIA_IOC_TB_TAGn(7) = 0; + *(vip)CIA_IOC_TBn_PAGEm(0,0) = pte0; + *(vip)CIA_IOC_TBn_PAGEm(0,1) = 0; + *(vip)CIA_IOC_TBn_PAGEm(0,2) = 0; + *(vip)CIA_IOC_TBn_PAGEm(0,3) = 0; + mb(); + + /* Get a usable bus address */ + bus_addr = cia_ioremap(addr0, 8*PAGE_SIZE); + + /* First, verify we can read back what we've written. If + this fails, we can't be sure of any of the other testing + we're going to do, so bail. */ + /* ??? Actually, we could do the work with machine checks. + By passing this register update test, we pretty much + guarantee that cia_pci_tbi_try1 works. If this test + fails, cia_pci_tbi_try2 might still work. */ + + temp = *(vip)CIA_IOC_TB_TAGn(0); + if (temp != tag0) { + printk("pci: failed tb register update test " + "(tag0 %#x != %#x)\n", temp, tag0); + goto failed; + } + temp = *(vip)CIA_IOC_TB_TAGn(1); + if (temp != 0) { + printk("pci: failed tb register update test " + "(tag1 %#x != 0)\n", temp); + goto failed; + } + temp = *(vip)CIA_IOC_TBn_PAGEm(0,0); + if (temp != pte0) { + printk("pci: failed tb register update test " + "(pte0 %#x != %#x)\n", temp, pte0); + goto failed; + } + printk("pci: passed tb register update test\n"); + + /* Second, verify we can actually do I/O through this entry. */ + + data0 = 0xdeadbeef; + page[0] = data0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + temp = cia_readl(bus_addr); + mb(); + mcheck_expected(0) = 0; + mb(); + if (mcheck_taken(0)) { + printk("pci: failed sg loopback i/o read test (mcheck)\n"); + goto failed; + } + if (temp != data0) { + printk("pci: failed sg loopback i/o read test " + "(%#x != %#x)\n", temp, data0); + goto failed; + } + printk("pci: passed sg loopback i/o read test\n"); + + /* Third, try to invalidate the TLB. */ + + if (! use_tbia_try2) { + cia_pci_tbi(arena->hose, 0, -1); + temp = *(vip)CIA_IOC_TB_TAGn(0); + if (temp & 1) { + use_tbia_try2 = 1; + printk("pci: failed tbia test; workaround available\n"); + } else { + printk("pci: passed tbia test\n"); + } + } + + /* Fourth, verify the TLB snoops the EV5's caches when + doing a tlb fill. */ + + data0 = 0x5adda15e; + page[0] = data0; + arena->ptes[4] = pte0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + temp = cia_readl(bus_addr + 4*PAGE_SIZE); + mb(); + mcheck_expected(0) = 0; + mb(); + if (mcheck_taken(0)) { + printk("pci: failed pte write cache snoop test (mcheck)\n"); + goto failed; + } + if (temp != data0) { + printk("pci: failed pte write cache snoop test " + "(%#x != %#x)\n", temp, data0); + goto failed; + } + printk("pci: passed pte write cache snoop test\n"); + + /* Fifth, verify that a previously invalid PTE entry gets + filled from the page table. */ + + data0 = 0xabcdef12; + page[0] = data0; + arena->ptes[5] = pte0; + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + temp = cia_readl(bus_addr + 5*PAGE_SIZE); + mb(); + mcheck_expected(0) = 0; + mb(); + if (mcheck_taken(0)) { + printk("pci: failed valid tag invalid pte reload test " + "(mcheck; workaround available)\n"); + /* Work around this bug by aligning new allocations + on 4 page boundaries. */ + arena->align_entry = 4; + } else if (temp != data0) { + printk("pci: failed valid tag invalid pte reload test " + "(%#x != %#x)\n", temp, data0); + goto failed; + } else { + printk("pci: passed valid tag invalid pte reload test\n"); + } + + /* Sixth, verify machine checks are working. Test invalid + pte under the same valid tag as we used above. */ + + mcheck_expected(0) = 1; + mcheck_taken(0) = 0; + mb(); + temp = cia_readl(bus_addr + 6*PAGE_SIZE); + mb(); + mcheck_expected(0) = 0; + mb(); + printk("pci: %s pci machine check test\n", + mcheck_taken(0) ? "passed" : "failed"); + + /* Clean up after the tests. */ + arena->ptes[4] = 0; + arena->ptes[5] = 0; + + if (use_tbia_try2) { + alpha_mv.mv_pci_tbi = cia_pci_tbi_try2; + + /* Tags 0-3 must be disabled if we use this workaraund. */ + wmb(); + *(vip)CIA_IOC_TB_TAGn(0) = 2; + *(vip)CIA_IOC_TB_TAGn(1) = 2; + *(vip)CIA_IOC_TB_TAGn(2) = 2; + *(vip)CIA_IOC_TB_TAGn(3) = 2; + + printk("pci: tbia workaround enabled\n"); + } + alpha_mv.mv_pci_tbi(arena->hose, 0, -1); + +exit: + /* unmap the bus addr */ + cia_iounmap(bus_addr); + + /* Restore normal PCI operation. */ + mb(); + *(vip)CIA_IOC_CIA_CTRL = ctrl; + mb(); + *(vip)CIA_IOC_CIA_CTRL; + mb(); + return; + +failed: + printk("pci: disabling sg translation window\n"); + *(vip)CIA_IOC_PCI_W0_BASE = 0; + *(vip)CIA_IOC_PCI_W1_BASE = 0; + pci_isa_hose->sg_isa = NULL; + alpha_mv.mv_pci_tbi = NULL; + goto exit; +} + +#if defined(ALPHA_RESTORE_SRM_SETUP) +/* Save CIA configuration data as the console had it set up. */ +struct +{ + unsigned int hae_mem; + unsigned int hae_io; + unsigned int pci_dac_offset; + unsigned int err_mask; + unsigned int cia_ctrl; + unsigned int cia_cnfg; + struct { + unsigned int w_base; + unsigned int w_mask; + unsigned int t_base; + } window[4]; +} saved_config __attribute((common)); + +void +cia_save_srm_settings(int is_pyxis) +{ + int i; + + /* Save some important registers. */ + saved_config.err_mask = *(vip)CIA_IOC_ERR_MASK; + saved_config.cia_ctrl = *(vip)CIA_IOC_CIA_CTRL; + saved_config.hae_mem = *(vip)CIA_IOC_HAE_MEM; + saved_config.hae_io = *(vip)CIA_IOC_HAE_IO; + saved_config.pci_dac_offset = *(vip)CIA_IOC_PCI_W_DAC; + + if (is_pyxis) + saved_config.cia_cnfg = *(vip)CIA_IOC_CIA_CNFG; + else + saved_config.cia_cnfg = 0; + + /* Save DMA windows configuration. */ + for (i = 0; i < 4; i++) { + saved_config.window[i].w_base = *(vip)CIA_IOC_PCI_Wn_BASE(i); + saved_config.window[i].w_mask = *(vip)CIA_IOC_PCI_Wn_MASK(i); + saved_config.window[i].t_base = *(vip)CIA_IOC_PCI_Tn_BASE(i); + } + mb(); +} + +void +cia_restore_srm_settings(void) +{ + int i; + + for (i = 0; i < 4; i++) { + *(vip)CIA_IOC_PCI_Wn_BASE(i) = saved_config.window[i].w_base; + *(vip)CIA_IOC_PCI_Wn_MASK(i) = saved_config.window[i].w_mask; + *(vip)CIA_IOC_PCI_Tn_BASE(i) = saved_config.window[i].t_base; + } + + *(vip)CIA_IOC_HAE_MEM = saved_config.hae_mem; + *(vip)CIA_IOC_HAE_IO = saved_config.hae_io; + *(vip)CIA_IOC_PCI_W_DAC = saved_config.pci_dac_offset; + *(vip)CIA_IOC_ERR_MASK = saved_config.err_mask; + *(vip)CIA_IOC_CIA_CTRL = saved_config.cia_ctrl; + + if (saved_config.cia_cnfg) /* Must be pyxis. */ + *(vip)CIA_IOC_CIA_CNFG = saved_config.cia_cnfg; + + mb(); +} +#else /* ALPHA_RESTORE_SRM_SETUP */ +#define cia_save_srm_settings(p) do {} while (0) +#define cia_restore_srm_settings() do {} while (0) +#endif /* ALPHA_RESTORE_SRM_SETUP */ + + +static void __init +do_init_arch(int is_pyxis) +{ + struct pci_controller *hose; + int temp, cia_rev, tbia_window; + + cia_rev = *(vip)CIA_IOC_CIA_REV & CIA_REV_MASK; + printk("pci: cia revision %d%s\n", + cia_rev, is_pyxis ? " (pyxis)" : ""); + + if (alpha_using_srm) + cia_save_srm_settings(is_pyxis); + + /* Set up error reporting. */ + temp = *(vip)CIA_IOC_ERR_MASK; + temp &= ~(CIA_ERR_CPU_PE | CIA_ERR_MEM_NEM | CIA_ERR_PA_PTE_INV + | CIA_ERR_RCVD_MAS_ABT | CIA_ERR_RCVD_TAR_ABT); + *(vip)CIA_IOC_ERR_MASK = temp; + + /* Clear all currently pending errors. */ + temp = *(vip)CIA_IOC_CIA_ERR; + *(vip)CIA_IOC_CIA_ERR = temp; + + /* Turn on mchecks. */ + temp = *(vip)CIA_IOC_CIA_CTRL; + temp |= CIA_CTRL_FILL_ERR_EN | CIA_CTRL_MCHK_ERR_EN; + *(vip)CIA_IOC_CIA_CTRL = temp; + + /* Clear the CFG register, which gets used for PCI config space + accesses. That is the way we want to use it, and we do not + want to depend on what ARC or SRM might have left behind. */ + *(vip)CIA_IOC_CFG = 0; + + /* Zero the HAEs. */ + *(vip)CIA_IOC_HAE_MEM = 0; + *(vip)CIA_IOC_HAE_IO = 0; + + /* For PYXIS, we always use BWX bus and i/o accesses. To that end, + make sure they're enabled on the controller. At the same time, + enable the monster window. */ + if (is_pyxis) { + temp = *(vip)CIA_IOC_CIA_CNFG; + temp |= CIA_CNFG_IOA_BWEN | CIA_CNFG_PCI_MWEN; + *(vip)CIA_IOC_CIA_CNFG = temp; + } + + /* Synchronize with all previous changes. */ + mb(); + *(vip)CIA_IOC_CIA_REV; + + /* + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + if (! is_pyxis) { + struct resource *hae_mem = alloc_resource(); + hose->mem_space = hae_mem; + + hae_mem->start = 0; + hae_mem->end = CIA_MEM_R1_MASK; + hae_mem->name = pci_hae0_name; + hae_mem->flags = IORESOURCE_MEM; + + if (request_resource(&iomem_resource, hae_mem) < 0) + printk(KERN_ERR "Failed to request HAE_MEM\n"); + + hose->sparse_mem_base = CIA_SPARSE_MEM - IDENT_ADDR; + hose->dense_mem_base = CIA_DENSE_MEM - IDENT_ADDR; + hose->sparse_io_base = CIA_IO - IDENT_ADDR; + hose->dense_io_base = 0; + } else { + hose->sparse_mem_base = 0; + hose->dense_mem_base = CIA_BW_MEM - IDENT_ADDR; + hose->sparse_io_base = 0; + hose->dense_io_base = CIA_BW_IO - IDENT_ADDR; + } + + /* + * Set up the PCI to main memory translation windows. + * + * Window 0 is S/G 8MB at 8MB (for isa) + * Window 1 is S/G 1MB at 768MB (for tbia) (unused for CIA rev 1) + * Window 2 is direct access 2GB at 2GB + * Window 3 is DAC access 4GB at 8GB (or S/G for tbia if CIA rev 1) + * + * ??? NetBSD hints that page tables must be aligned to 32K, + * possibly due to a hardware bug. This is over-aligned + * from the 8K alignment one would expect for an 8MB window. + * No description of what revisions affected. + */ + + hose->sg_pci = NULL; + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 32768); + + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; + + *(vip)CIA_IOC_PCI_W0_BASE = hose->sg_isa->dma_base | 3; + *(vip)CIA_IOC_PCI_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000; + *(vip)CIA_IOC_PCI_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2; + + *(vip)CIA_IOC_PCI_W2_BASE = __direct_map_base | 1; + *(vip)CIA_IOC_PCI_W2_MASK = (__direct_map_size - 1) & 0xfff00000; + *(vip)CIA_IOC_PCI_T2_BASE = 0 >> 2; + + /* On PYXIS we have the monster window, selected by bit 40, so + there is no need for window3 to be enabled. + + On CIA, we don't have true arbitrary addressing -- bits <39:32> + are compared against W_DAC. We can, however, directly map 4GB, + which is better than before. However, due to assumptions made + elsewhere, we should not claim that we support DAC unless that + 4GB covers all of physical memory. + + On CIA rev 1, apparently W1 and W2 can't be used for SG. + At least, there are reports that it doesn't work for Alcor. + In that case, we have no choice but to use W3 for the TBIA + workaround, which means we can't use DAC at all. */ + + tbia_window = 1; + if (is_pyxis) { + *(vip)CIA_IOC_PCI_W3_BASE = 0; + } else if (cia_rev == 1) { + *(vip)CIA_IOC_PCI_W1_BASE = 0; + tbia_window = 3; + } else if (max_low_pfn > (0x100000000UL >> PAGE_SHIFT)) { + *(vip)CIA_IOC_PCI_W3_BASE = 0; + } else { + *(vip)CIA_IOC_PCI_W3_BASE = 0x00000000 | 1 | 8; + *(vip)CIA_IOC_PCI_W3_MASK = 0xfff00000; + *(vip)CIA_IOC_PCI_T3_BASE = 0 >> 2; + + alpha_mv.pci_dac_offset = 0x200000000UL; + *(vip)CIA_IOC_PCI_W_DAC = alpha_mv.pci_dac_offset >> 32; + } + + /* Prepare workaround for apparently broken tbia. */ + cia_prepare_tbia_workaround(tbia_window); +} + +void __init +cia_init_arch(void) +{ + do_init_arch(0); +} + +void __init +pyxis_init_arch(void) +{ + /* On pyxis machines we can precisely calculate the + CPU clock frequency using pyxis real time counter. + It's especially useful for SX164 with broken RTC. + + Both CPU and chipset are driven by the single 16.666M + or 16.667M crystal oscillator. PYXIS_RT_COUNT clock is + 66.66 MHz. -ink */ + + unsigned int cc0, cc1; + unsigned long pyxis_cc; + + __asm__ __volatile__ ("rpcc %0" : "=r"(cc0)); + pyxis_cc = *(vulp)PYXIS_RT_COUNT; + do { } while(*(vulp)PYXIS_RT_COUNT - pyxis_cc < 4096); + __asm__ __volatile__ ("rpcc %0" : "=r"(cc1)); + cc1 -= cc0; + hwrpb->cycle_freq = ((cc1 >> 11) * 100000000UL) / 3; + hwrpb_update_checksum(hwrpb); + + do_init_arch(1); +} + +void +cia_kill_arch(int mode) +{ + if (alpha_using_srm) + cia_restore_srm_settings(); +} + +void __init +cia_init_pci(void) +{ + /* Must delay this from init_arch, as we need machine checks. */ + verify_tb_operation(); + common_init_pci(); +} + +static inline void +cia_pci_clr_err(void) +{ + int jd; + + jd = *(vip)CIA_IOC_CIA_ERR; + *(vip)CIA_IOC_CIA_ERR = jd; + mb(); + *(vip)CIA_IOC_CIA_ERR; /* re-read to force write. */ +} + +#ifdef CONFIG_VERBOSE_MCHECK +static void +cia_decode_pci_error(struct el_CIA_sysdata_mcheck *cia, const char *msg) +{ + static const char * const pci_cmd_desc[16] = { + "Interrupt Acknowledge", "Special Cycle", "I/O Read", + "I/O Write", "Reserved 0x4", "Reserved 0x5", "Memory Read", + "Memory Write", "Reserved 0x8", "Reserved 0x9", + "Configuration Read", "Configuration Write", + "Memory Read Multiple", "Dual Address Cycle", + "Memory Read Line", "Memory Write and Invalidate" + }; + + if (cia->cia_err & (CIA_ERR_COR_ERR + | CIA_ERR_UN_COR_ERR + | CIA_ERR_MEM_NEM + | CIA_ERR_PA_PTE_INV)) { + static const char * const window_desc[6] = { + "No window active", "Window 0 hit", "Window 1 hit", + "Window 2 hit", "Window 3 hit", "Monster window hit" + }; + + const char *window; + const char *cmd; + unsigned long addr, tmp; + int lock, dac; + + cmd = pci_cmd_desc[cia->pci_err0 & 0x7]; + lock = (cia->pci_err0 >> 4) & 1; + dac = (cia->pci_err0 >> 5) & 1; + + tmp = (cia->pci_err0 >> 8) & 0x1F; + tmp = ffs(tmp); + window = window_desc[tmp]; + + addr = cia->pci_err1; + if (dac) { + tmp = *(vip)CIA_IOC_PCI_W_DAC & 0xFFUL; + addr |= tmp << 32; + } + + printk(KERN_CRIT "CIA machine check: %s\n", msg); + printk(KERN_CRIT " DMA command: %s\n", cmd); + printk(KERN_CRIT " PCI address: %#010lx\n", addr); + printk(KERN_CRIT " %s, Lock: %d, DAC: %d\n", + window, lock, dac); + } else if (cia->cia_err & (CIA_ERR_PERR + | CIA_ERR_PCI_ADDR_PE + | CIA_ERR_RCVD_MAS_ABT + | CIA_ERR_RCVD_TAR_ABT + | CIA_ERR_IOA_TIMEOUT)) { + static const char * const master_st_desc[16] = { + "Idle", "Drive bus", "Address step cycle", + "Address cycle", "Data cycle", "Last read data cycle", + "Last write data cycle", "Read stop cycle", + "Write stop cycle", "Read turnaround cycle", + "Write turnaround cycle", "Reserved 0xB", + "Reserved 0xC", "Reserved 0xD", "Reserved 0xE", + "Unknown state" + }; + static const char * const target_st_desc[16] = { + "Idle", "Busy", "Read data cycle", "Write data cycle", + "Read stop cycle", "Write stop cycle", + "Read turnaround cycle", "Write turnaround cycle", + "Read wait cycle", "Write wait cycle", + "Reserved 0xA", "Reserved 0xB", "Reserved 0xC", + "Reserved 0xD", "Reserved 0xE", "Unknown state" + }; + + const char *cmd; + const char *master, *target; + unsigned long addr, tmp; + int dac; + + master = master_st_desc[(cia->pci_err0 >> 16) & 0xF]; + target = target_st_desc[(cia->pci_err0 >> 20) & 0xF]; + cmd = pci_cmd_desc[(cia->pci_err0 >> 24) & 0xF]; + dac = (cia->pci_err0 >> 28) & 1; + + addr = cia->pci_err2; + if (dac) { + tmp = *(volatile int *)CIA_IOC_PCI_W_DAC & 0xFFUL; + addr |= tmp << 32; + } + + printk(KERN_CRIT "CIA machine check: %s\n", msg); + printk(KERN_CRIT " PCI command: %s\n", cmd); + printk(KERN_CRIT " Master state: %s, Target state: %s\n", + master, target); + printk(KERN_CRIT " PCI address: %#010lx, DAC: %d\n", + addr, dac); + } else { + printk(KERN_CRIT "CIA machine check: %s\n", msg); + printk(KERN_CRIT " Unknown PCI error\n"); + printk(KERN_CRIT " PCI_ERR0 = %#08lx", cia->pci_err0); + printk(KERN_CRIT " PCI_ERR1 = %#08lx", cia->pci_err1); + printk(KERN_CRIT " PCI_ERR2 = %#08lx", cia->pci_err2); + } +} + +static void +cia_decode_mem_error(struct el_CIA_sysdata_mcheck *cia, const char *msg) +{ + unsigned long mem_port_addr; + unsigned long mem_port_mask; + const char *mem_port_cmd; + const char *seq_state; + const char *set_select; + unsigned long tmp; + + /* If this is a DMA command, also decode the PCI bits. */ + if ((cia->mem_err1 >> 20) & 1) + cia_decode_pci_error(cia, msg); + else + printk(KERN_CRIT "CIA machine check: %s\n", msg); + + mem_port_addr = cia->mem_err0 & 0xfffffff0; + mem_port_addr |= (cia->mem_err1 & 0x83UL) << 32; + + mem_port_mask = (cia->mem_err1 >> 12) & 0xF; + + tmp = (cia->mem_err1 >> 8) & 0xF; + tmp |= ((cia->mem_err1 >> 20) & 1) << 4; + if ((tmp & 0x1E) == 0x06) + mem_port_cmd = "WRITE BLOCK or WRITE BLOCK LOCK"; + else if ((tmp & 0x1C) == 0x08) + mem_port_cmd = "READ MISS or READ MISS MODIFY"; + else if (tmp == 0x1C) + mem_port_cmd = "BC VICTIM"; + else if ((tmp & 0x1E) == 0x0E) + mem_port_cmd = "READ MISS MODIFY"; + else if ((tmp & 0x1C) == 0x18) + mem_port_cmd = "DMA READ or DMA READ MODIFY"; + else if ((tmp & 0x1E) == 0x12) + mem_port_cmd = "DMA WRITE"; + else + mem_port_cmd = "Unknown"; + + tmp = (cia->mem_err1 >> 16) & 0xF; + switch (tmp) { + case 0x0: + seq_state = "Idle"; + break; + case 0x1: + seq_state = "DMA READ or DMA WRITE"; + break; + case 0x2: case 0x3: + seq_state = "READ MISS (or READ MISS MODIFY) with victim"; + break; + case 0x4: case 0x5: case 0x6: + seq_state = "READ MISS (or READ MISS MODIFY) with no victim"; + break; + case 0x8: case 0x9: case 0xB: + seq_state = "Refresh"; + break; + case 0xC: + seq_state = "Idle, waiting for DMA pending read"; + break; + case 0xE: case 0xF: + seq_state = "Idle, ras precharge"; + break; + default: + seq_state = "Unknown"; + break; + } + + tmp = (cia->mem_err1 >> 24) & 0x1F; + switch (tmp) { + case 0x00: set_select = "Set 0 selected"; break; + case 0x01: set_select = "Set 1 selected"; break; + case 0x02: set_select = "Set 2 selected"; break; + case 0x03: set_select = "Set 3 selected"; break; + case 0x04: set_select = "Set 4 selected"; break; + case 0x05: set_select = "Set 5 selected"; break; + case 0x06: set_select = "Set 6 selected"; break; + case 0x07: set_select = "Set 7 selected"; break; + case 0x08: set_select = "Set 8 selected"; break; + case 0x09: set_select = "Set 9 selected"; break; + case 0x0A: set_select = "Set A selected"; break; + case 0x0B: set_select = "Set B selected"; break; + case 0x0C: set_select = "Set C selected"; break; + case 0x0D: set_select = "Set D selected"; break; + case 0x0E: set_select = "Set E selected"; break; + case 0x0F: set_select = "Set F selected"; break; + case 0x10: set_select = "No set selected"; break; + case 0x1F: set_select = "Refresh cycle"; break; + default: set_select = "Unknown"; break; + } + + printk(KERN_CRIT " Memory port command: %s\n", mem_port_cmd); + printk(KERN_CRIT " Memory port address: %#010lx, mask: %#lx\n", + mem_port_addr, mem_port_mask); + printk(KERN_CRIT " Memory sequencer state: %s\n", seq_state); + printk(KERN_CRIT " Memory set: %s\n", set_select); +} + +static void +cia_decode_ecc_error(struct el_CIA_sysdata_mcheck *cia, const char *msg) +{ + long syn; + long i; + const char *fmt; + + cia_decode_mem_error(cia, msg); + + syn = cia->cia_syn & 0xff; + if (syn == (syn & -syn)) { + fmt = KERN_CRIT " ECC syndrome %#x -- check bit %d\n"; + i = ffs(syn) - 1; + } else { + static unsigned char const data_bit[64] = { + 0xCE, 0xCB, 0xD3, 0xD5, + 0xD6, 0xD9, 0xDA, 0xDC, + 0x23, 0x25, 0x26, 0x29, + 0x2A, 0x2C, 0x31, 0x34, + 0x0E, 0x0B, 0x13, 0x15, + 0x16, 0x19, 0x1A, 0x1C, + 0xE3, 0xE5, 0xE6, 0xE9, + 0xEA, 0xEC, 0xF1, 0xF4, + 0x4F, 0x4A, 0x52, 0x54, + 0x57, 0x58, 0x5B, 0x5D, + 0xA2, 0xA4, 0xA7, 0xA8, + 0xAB, 0xAD, 0xB0, 0xB5, + 0x8F, 0x8A, 0x92, 0x94, + 0x97, 0x98, 0x9B, 0x9D, + 0x62, 0x64, 0x67, 0x68, + 0x6B, 0x6D, 0x70, 0x75 + }; + + for (i = 0; i < 64; ++i) + if (data_bit[i] == syn) + break; + + if (i < 64) + fmt = KERN_CRIT " ECC syndrome %#x -- data bit %d\n"; + else + fmt = KERN_CRIT " ECC syndrome %#x -- unknown bit\n"; + } + + printk (fmt, syn, i); +} + +static void +cia_decode_parity_error(struct el_CIA_sysdata_mcheck *cia) +{ + static const char * const cmd_desc[16] = { + "NOP", "LOCK", "FETCH", "FETCH_M", "MEMORY BARRIER", + "SET DIRTY", "WRITE BLOCK", "WRITE BLOCK LOCK", + "READ MISS0", "READ MISS1", "READ MISS MOD0", + "READ MISS MOD1", "BCACHE VICTIM", "Spare", + "READ MISS MOD STC0", "READ MISS MOD STC1" + }; + + unsigned long addr; + unsigned long mask; + const char *cmd; + int par; + + addr = cia->cpu_err0 & 0xfffffff0; + addr |= (cia->cpu_err1 & 0x83UL) << 32; + cmd = cmd_desc[(cia->cpu_err1 >> 8) & 0xF]; + mask = (cia->cpu_err1 >> 12) & 0xF; + par = (cia->cpu_err1 >> 21) & 1; + + printk(KERN_CRIT "CIA machine check: System bus parity error\n"); + printk(KERN_CRIT " Command: %s, Parity bit: %d\n", cmd, par); + printk(KERN_CRIT " Address: %#010lx, Mask: %#lx\n", addr, mask); +} +#endif /* CONFIG_VERBOSE_MCHECK */ + + +static int +cia_decode_mchk(unsigned long la_ptr) +{ + struct el_common *com; + struct el_CIA_sysdata_mcheck *cia; + + com = (void *)la_ptr; + cia = (void *)(la_ptr + com->sys_offset); + + if ((cia->cia_err & CIA_ERR_VALID) == 0) + return 0; + +#ifdef CONFIG_VERBOSE_MCHECK + if (!alpha_verbose_mcheck) + return 1; + + switch (ffs(cia->cia_err & 0xfff) - 1) { + case 0: /* CIA_ERR_COR_ERR */ + cia_decode_ecc_error(cia, "Corrected ECC error"); + break; + case 1: /* CIA_ERR_UN_COR_ERR */ + cia_decode_ecc_error(cia, "Uncorrected ECC error"); + break; + case 2: /* CIA_ERR_CPU_PE */ + cia_decode_parity_error(cia); + break; + case 3: /* CIA_ERR_MEM_NEM */ + cia_decode_mem_error(cia, "Access to nonexistent memory"); + break; + case 4: /* CIA_ERR_PCI_SERR */ + cia_decode_pci_error(cia, "PCI bus system error"); + break; + case 5: /* CIA_ERR_PERR */ + cia_decode_pci_error(cia, "PCI data parity error"); + break; + case 6: /* CIA_ERR_PCI_ADDR_PE */ + cia_decode_pci_error(cia, "PCI address parity error"); + break; + case 7: /* CIA_ERR_RCVD_MAS_ABT */ + cia_decode_pci_error(cia, "PCI master abort"); + break; + case 8: /* CIA_ERR_RCVD_TAR_ABT */ + cia_decode_pci_error(cia, "PCI target abort"); + break; + case 9: /* CIA_ERR_PA_PTE_INV */ + cia_decode_pci_error(cia, "PCI invalid PTE"); + break; + case 10: /* CIA_ERR_FROM_WRT_ERR */ + cia_decode_mem_error(cia, "Write to flash ROM attempted"); + break; + case 11: /* CIA_ERR_IOA_TIMEOUT */ + cia_decode_pci_error(cia, "I/O timeout"); + break; + } + + if (cia->cia_err & CIA_ERR_LOST_CORR_ERR) + printk(KERN_CRIT "CIA lost machine check: " + "Correctable ECC error\n"); + if (cia->cia_err & CIA_ERR_LOST_UN_CORR_ERR) + printk(KERN_CRIT "CIA lost machine check: " + "Uncorrectable ECC error\n"); + if (cia->cia_err & CIA_ERR_LOST_CPU_PE) + printk(KERN_CRIT "CIA lost machine check: " + "System bus parity error\n"); + if (cia->cia_err & CIA_ERR_LOST_MEM_NEM) + printk(KERN_CRIT "CIA lost machine check: " + "Access to nonexistent memory\n"); + if (cia->cia_err & CIA_ERR_LOST_PERR) + printk(KERN_CRIT "CIA lost machine check: " + "PCI data parity error\n"); + if (cia->cia_err & CIA_ERR_LOST_PCI_ADDR_PE) + printk(KERN_CRIT "CIA lost machine check: " + "PCI address parity error\n"); + if (cia->cia_err & CIA_ERR_LOST_RCVD_MAS_ABT) + printk(KERN_CRIT "CIA lost machine check: " + "PCI master abort\n"); + if (cia->cia_err & CIA_ERR_LOST_RCVD_TAR_ABT) + printk(KERN_CRIT "CIA lost machine check: " + "PCI target abort\n"); + if (cia->cia_err & CIA_ERR_LOST_PA_PTE_INV) + printk(KERN_CRIT "CIA lost machine check: " + "PCI invalid PTE\n"); + if (cia->cia_err & CIA_ERR_LOST_FROM_WRT_ERR) + printk(KERN_CRIT "CIA lost machine check: " + "Write to flash ROM attempted\n"); + if (cia->cia_err & CIA_ERR_LOST_IOA_TIMEOUT) + printk(KERN_CRIT "CIA lost machine check: " + "I/O timeout\n"); +#endif /* CONFIG_VERBOSE_MCHECK */ + + return 1; +} + +void +cia_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + int expected; + + /* Clear the error before any reporting. */ + mb(); + mb(); /* magic */ + draina(); + cia_pci_clr_err(); + wrmces(rdmces()); /* reset machine check pending flag. */ + mb(); + + expected = mcheck_expected(0); + if (!expected && vector == 0x660) + expected = cia_decode_mchk(la_ptr); + process_mcheck_info(vector, la_ptr, regs, "CIA", expected); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_irongate.c + * + * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com). + * + * Copyright (C) 1999 Alpha Processor, Inc., + * (David Daniel, Stig Telfer, Soohoon Lee) + * + * Code common to all IRONGATE core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_irongate.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/bootmem.h> + +#include <asm/ptrace.h> +#include <asm/pci.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> + +#include "proto.h" +#include "pci_impl.h" + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +igcsr32 *IronECC; + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address accordingly. It is therefore not safe + * to have concurrent invocations to configuration space access + * routines, but there really shouldn't be any need for this. + * + * addr[31:24] reserved + * addr[23:16] bus number (8 bits = 128 possible buses) + * addr[15:11] Device number (5 bits) + * addr[10: 8] function number + * addr[ 7: 2] register number + * + * For IRONGATE: + * if (bus = addr[23:16]) == 0 + * then + * type 0 config cycle: + * addr_on_pci[31:11] = id selection for device = addr[15:11] + * addr_on_pci[10: 2] = addr[10: 2] ??? + * addr_on_pci[ 1: 0] = 00 + * else + * type 1 config cycle (pass on with no decoding): + * addr_on_pci[31:24] = 0 + * addr_on_pci[23: 2] = addr[23: 2] + * addr_on_pci[ 1: 0] = 01 + * fi + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + unsigned long addr; + u8 bus = pbus->number; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + *type1 = (bus != 0); + + addr = (bus << 16) | (device_fn << 8) | where; + addr |= IRONGATE_CONF; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static int +irongate_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int +irongate_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops irongate_pci_ops = +{ + .read = irongate_read_config, + .write = irongate_write_config, +}; + +int +irongate_pci_clr_err(void) +{ + unsigned int nmi_ctl=0; + unsigned int IRONGATE_jd; + +again: + IRONGATE_jd = IRONGATE0->stat_cmd; + printk("Iron stat_cmd %x\n", IRONGATE_jd); + IRONGATE0->stat_cmd = IRONGATE_jd; /* write again clears error bits */ + mb(); + IRONGATE_jd = IRONGATE0->stat_cmd; /* re-read to force write */ + + IRONGATE_jd = *IronECC; + printk("Iron ECC %x\n", IRONGATE_jd); + *IronECC = IRONGATE_jd; /* write again clears error bits */ + mb(); + IRONGATE_jd = *IronECC; /* re-read to force write */ + + /* Clear ALI NMI */ + nmi_ctl = inb(0x61); + nmi_ctl |= 0x0c; + outb(nmi_ctl, 0x61); + nmi_ctl &= ~0x0c; + outb(nmi_ctl, 0x61); + + IRONGATE_jd = *IronECC; + if (IRONGATE_jd & 0x300) goto again; + + return 0; +} + +#define IRONGATE_3GB 0xc0000000UL + +/* On Albacore (aka UP1500) with 4Gb of RAM we have to reserve some + memory for PCI. At this point we just reserve memory above 3Gb. Most + of this memory will be freed after PCI setup is done. */ +static void __init +albacore_init_arch(void) +{ + unsigned long memtop = max_low_pfn << PAGE_SHIFT; + unsigned long pci_mem = (memtop + 0x1000000UL) & ~0xffffffUL; + struct percpu_struct *cpu; + int pal_rev, pal_var; + + cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); + pal_rev = cpu->pal_revision & 0xffff; + pal_var = (cpu->pal_revision >> 16) & 0xff; + + /* Consoles earlier than A5.6-18 (OSF PALcode v1.62-2) set up + the CPU incorrectly (leave speculative stores enabled), + which causes memory corruption under certain conditions. + Issue a warning for such consoles. */ + if (alpha_using_srm && + (pal_rev < 0x13e || (pal_rev == 0x13e && pal_var < 2))) + printk(KERN_WARNING "WARNING! Upgrade to SRM A5.6-19 " + "or later\n"); + + if (pci_mem > IRONGATE_3GB) + pci_mem = IRONGATE_3GB; + IRONGATE0->pci_mem = pci_mem; + alpha_mv.min_mem_address = pci_mem; + if (memtop > pci_mem) { +#ifdef CONFIG_BLK_DEV_INITRD + extern unsigned long initrd_start, initrd_end; + extern void *move_initrd(unsigned long); + + /* Move the initrd out of the way. */ + if (initrd_end && __pa(initrd_end) > pci_mem) { + unsigned long size; + + size = initrd_end - initrd_start; + free_bootmem_node(NODE_DATA(0), __pa(initrd_start), + PAGE_ALIGN(size)); + if (!move_initrd(pci_mem)) + printk("irongate_init_arch: initrd too big " + "(%ldK)\ndisabling initrd\n", + size / 1024); + } +#endif + reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - pci_mem); + printk("irongate_init_arch: temporarily reserving " + "region %08lx-%08lx for PCI\n", pci_mem, memtop - 1); + } +} + +static void __init +irongate_setup_agp(void) +{ + /* Disable the GART window. AGPGART doesn't work due to yet + unresolved memory coherency issues... */ + IRONGATE0->agpva = IRONGATE0->agpva & ~0xf; + alpha_agpgart_size = 0; +} + +void __init +irongate_init_arch(void) +{ + struct pci_controller *hose; + int amd761 = (IRONGATE0->dev_vendor >> 16) > 0x7006; /* Albacore? */ + + IronECC = amd761 ? &IRONGATE0->bacsr54_eccms761 : &IRONGATE0->dramms; + + irongate_pci_clr_err(); + + if (amd761) + albacore_init_arch(); + + irongate_setup_agp(); + + /* + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + /* This is for userland consumption. For some reason, the 40-bit + PIO bias that we use in the kernel through KSEG didn't work for + the page table based user mappings. So make sure we get the + 43-bit PIO bias. */ + hose->sparse_mem_base = 0; + hose->sparse_io_base = 0; + hose->dense_mem_base + = (IRONGATE_MEM & 0xffffffffffUL) | 0x80000000000UL; + hose->dense_io_base + = (IRONGATE_IO & 0xffffffffffUL) | 0x80000000000UL; + + hose->sg_isa = hose->sg_pci = NULL; + __direct_map_base = 0; + __direct_map_size = 0xffffffff; +} + +/* + * IO map and AGP support + */ +#include <linux/vmalloc.h> +#include <linux/agp_backend.h> +#include <linux/agpgart.h> +#include <asm/pgalloc.h> + +#define GET_PAGE_DIR_OFF(addr) (addr >> 22) +#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr)) + +#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) +#define GET_GATT(addr) (gatt_pages[GET_PAGE_DIR_IDX(addr)]) + +void __iomem * +irongate_ioremap(unsigned long addr, unsigned long size) +{ + struct vm_struct *area; + unsigned long vaddr; + unsigned long baddr, last; + u32 *mmio_regs, *gatt_pages, *cur_gatt, pte; + unsigned long gart_bus_addr; + + if (!alpha_agpgart_size) + return (void __iomem *)(addr + IRONGATE_MEM); + + gart_bus_addr = (unsigned long)IRONGATE0->bar0 & + PCI_BASE_ADDRESS_MEM_MASK; + + /* + * Check for within the AGP aperture... + */ + do { + /* + * Check the AGP area + */ + if (addr >= gart_bus_addr && addr + size - 1 < + gart_bus_addr + alpha_agpgart_size) + break; + + /* + * Not found - assume legacy ioremap + */ + return (void __iomem *)(addr + IRONGATE_MEM); + } while(0); + + mmio_regs = (u32 *)(((unsigned long)IRONGATE0->bar1 & + PCI_BASE_ADDRESS_MEM_MASK) + IRONGATE_MEM); + + gatt_pages = (u32 *)(phys_to_virt(mmio_regs[1])); /* FIXME */ + + /* + * Adjust the limits (mappings must be page aligned) + */ + if (addr & ~PAGE_MASK) { + printk("AGP ioremap failed... addr not page aligned (0x%lx)\n", + addr); + return (void __iomem *)(addr + IRONGATE_MEM); + } + last = addr + size - 1; + size = PAGE_ALIGN(last) - addr; + +#if 0 + printk("irongate_ioremap(0x%lx, 0x%lx)\n", addr, size); + printk("irongate_ioremap: gart_bus_addr 0x%lx\n", gart_bus_addr); + printk("irongate_ioremap: gart_aper_size 0x%lx\n", gart_aper_size); + printk("irongate_ioremap: mmio_regs %p\n", mmio_regs); + printk("irongate_ioremap: gatt_pages %p\n", gatt_pages); + + for(baddr = addr; baddr <= last; baddr += PAGE_SIZE) + { + cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1); + pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1; + printk("irongate_ioremap: cur_gatt %p pte 0x%x\n", + cur_gatt, pte); + } +#endif + + /* + * Map it + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) return NULL; + + for(baddr = addr, vaddr = (unsigned long)area->addr; + baddr <= last; + baddr += PAGE_SIZE, vaddr += PAGE_SIZE) + { + cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1); + pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1; + + if (__alpha_remap_area_pages(vaddr, + pte, PAGE_SIZE, 0)) { + printk("AGP ioremap: FAILED to map...\n"); + vfree(area->addr); + return NULL; + } + } + + flush_tlb_all(); + + vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); +#if 0 + printk("irongate_ioremap(0x%lx, 0x%lx) returning 0x%lx\n", + addr, size, vaddr); +#endif + return (void __iomem *)vaddr; +} + +void +irongate_iounmap(volatile void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + if (((long)addr >> 41) == -2) + return; /* kseg map, nothing to do */ + if (addr) + return vfree((void *)(PAGE_MASK & addr)); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_lca.c + * + * Written by David Mosberger (davidm@cs.arizona.edu) with some code + * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit + * bios code. + * + * Code common to all LCA core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_lca.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/tty.h> + +#include <asm/ptrace.h> +#include <asm/smp.h> + +#include "proto.h" +#include "pci_impl.h" + + +/* + * BIOS32-style PCI interface: + */ + +/* + * Machine check reasons. Defined according to PALcode sources + * (osf.h and platform.h). + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_UNKNOWN 0x008A +#define MCHK_K_CACKSOFT 0x008C +#define MCHK_K_BUGCHECK 0x008E +#define MCHK_K_OS_BUGCHECK 0x0090 +#define MCHK_K_DCPERR 0x0092 +#define MCHK_K_ICPERR 0x0094 + + +/* + * Platform-specific machine-check reasons: + */ +#define MCHK_K_SIO_SERR 0x204 /* all platforms so far */ +#define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */ +#define MCHK_K_DCSR 0x208 /* all but Noname */ + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the LCA_IOC_CONF register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr) +{ + unsigned long addr; + u8 bus = pbus->number; + + if (bus == 0) { + int device = device_fn >> 3; + int func = device_fn & 0x7; + + /* Type 0 configuration cycle. */ + + if (device > 12) { + return -1; + } + + *(vulp)LCA_IOC_CONF = 0; + addr = (1 << (11 + device)) | (func << 8) | where; + } else { + /* Type 1 configuration cycle. */ + *(vulp)LCA_IOC_CONF = 1; + addr = (bus << 16) | (device_fn << 8) | where; + } + *pci_addr = addr; + return 0; +} + +static unsigned int +conf_read(unsigned long addr) +{ + unsigned long flags, code, stat0; + unsigned int value; + + local_irq_save(flags); + + /* Reset status register to avoid loosing errors. */ + stat0 = *(vulp)LCA_IOC_STAT0; + *(vulp)LCA_IOC_STAT0 = stat0; + mb(); + + /* Access configuration space. */ + value = *(vuip)addr; + draina(); + + stat0 = *(vulp)LCA_IOC_STAT0; + if (stat0 & LCA_IOC_STAT0_ERR) { + code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) + & LCA_IOC_STAT0_CODE_MASK); + if (code != 1) { + printk("lca.c:conf_read: got stat0=%lx\n", stat0); + } + + /* Reset error status. */ + *(vulp)LCA_IOC_STAT0 = stat0; + mb(); + + /* Reset machine check. */ + wrmces(0x7); + + value = 0xffffffff; + } + local_irq_restore(flags); + return value; +} + +static void +conf_write(unsigned long addr, unsigned int value) +{ + unsigned long flags, code, stat0; + + local_irq_save(flags); /* avoid getting hit by machine check */ + + /* Reset status register to avoid loosing errors. */ + stat0 = *(vulp)LCA_IOC_STAT0; + *(vulp)LCA_IOC_STAT0 = stat0; + mb(); + + /* Access configuration space. */ + *(vuip)addr = value; + draina(); + + stat0 = *(vulp)LCA_IOC_STAT0; + if (stat0 & LCA_IOC_STAT0_ERR) { + code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) + & LCA_IOC_STAT0_CODE_MASK); + if (code != 1) { + printk("lca.c:conf_write: got stat0=%lx\n", stat0); + } + + /* Reset error status. */ + *(vulp)LCA_IOC_STAT0 = stat0; + mb(); + + /* Reset machine check. */ + wrmces(0x7); + } + local_irq_restore(flags); +} + +static int +lca_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr, pci_addr; + long mask; + int shift; + + if (mk_conf_addr(bus, devfn, where, &pci_addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = (where & 3) * 8; + mask = (size - 1) * 8; + addr = (pci_addr << 5) + mask + LCA_CONF; + *value = conf_read(addr) >> (shift); + return PCIBIOS_SUCCESSFUL; +} + +static int +lca_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, + u32 value) +{ + unsigned long addr, pci_addr; + long mask; + + if (mk_conf_addr(bus, devfn, where, &pci_addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + addr = (pci_addr << 5) + mask + LCA_CONF; + conf_write(addr, value << ((where & 3) * 8)); + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops lca_pci_ops = +{ + .read = lca_read_config, + .write = lca_write_config, +}; + +void +lca_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vulp)LCA_IOC_TBIA = 0; + mb(); +} + +void __init +lca_init_arch(void) +{ + struct pci_controller *hose; + + /* + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + hose->sparse_mem_base = LCA_SPARSE_MEM - IDENT_ADDR; + hose->dense_mem_base = LCA_DENSE_MEM - IDENT_ADDR; + hose->sparse_io_base = LCA_IO - IDENT_ADDR; + hose->dense_io_base = 0; + + /* + * Set up the PCI to main memory translation windows. + * + * Mimic the SRM settings for the direct-map window. + * Window 0 is scatter-gather 8MB at 8MB (for isa). + * Window 1 is direct access 1GB at 1GB. + * + * Note that we do not try to save any of the DMA window CSRs + * before setting them, since we cannot read those CSRs on LCA. + */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = NULL; + __direct_map_base = 0x40000000; + __direct_map_size = 0x40000000; + + *(vulp)LCA_IOC_W_BASE0 = hose->sg_isa->dma_base | (3UL << 32); + *(vulp)LCA_IOC_W_MASK0 = (hose->sg_isa->size - 1) & 0xfff00000; + *(vulp)LCA_IOC_T_BASE0 = virt_to_phys(hose->sg_isa->ptes); + + *(vulp)LCA_IOC_W_BASE1 = __direct_map_base | (2UL << 32); + *(vulp)LCA_IOC_W_MASK1 = (__direct_map_size - 1) & 0xfff00000; + *(vulp)LCA_IOC_T_BASE1 = 0; + + *(vulp)LCA_IOC_TB_ENA = 0x80; + + lca_pci_tbi(hose, 0, -1); + + /* + * Disable PCI parity for now. The NCR53c810 chip has + * troubles meeting the PCI spec which results in + * data parity errors. + */ + *(vulp)LCA_IOC_PAR_DIS = 1UL<<5; + + /* + * Finally, set up for restoring the correct HAE if using SRM. + * Again, since we cannot read many of the CSRs on the LCA, + * one of which happens to be the HAE, we save the value that + * the SRM will expect... + */ + if (alpha_using_srm) + srm_hae = 0x80000000UL; +} + +/* + * Constants used during machine-check handling. I suppose these + * could be moved into lca.h but I don't see much reason why anybody + * else would want to use them. + */ + +#define ESR_EAV (1UL<< 0) /* error address valid */ +#define ESR_CEE (1UL<< 1) /* correctable error */ +#define ESR_UEE (1UL<< 2) /* uncorrectable error */ +#define ESR_WRE (1UL<< 3) /* write-error */ +#define ESR_SOR (1UL<< 4) /* error source */ +#define ESR_CTE (1UL<< 7) /* cache-tag error */ +#define ESR_MSE (1UL<< 9) /* multiple soft errors */ +#define ESR_MHE (1UL<<10) /* multiple hard errors */ +#define ESR_NXM (1UL<<12) /* non-existent memory */ + +#define IOC_ERR ( 1<<4) /* ioc logs an error */ +#define IOC_CMD_SHIFT 0 +#define IOC_CMD (0xf<<IOC_CMD_SHIFT) +#define IOC_CODE_SHIFT 8 +#define IOC_CODE (0xf<<IOC_CODE_SHIFT) +#define IOC_LOST ( 1<<5) +#define IOC_P_NBR ((__u32) ~((1<<13) - 1)) + +static void +mem_error(unsigned long esr, unsigned long ear) +{ + printk(" %s %s error to %s occurred at address %x\n", + ((esr & ESR_CEE) ? "Correctable" : + (esr & ESR_UEE) ? "Uncorrectable" : "A"), + (esr & ESR_WRE) ? "write" : "read", + (esr & ESR_SOR) ? "memory" : "b-cache", + (unsigned) (ear & 0x1ffffff8)); + if (esr & ESR_CTE) { + printk(" A b-cache tag parity error was detected.\n"); + } + if (esr & ESR_MSE) { + printk(" Several other correctable errors occurred.\n"); + } + if (esr & ESR_MHE) { + printk(" Several other uncorrectable errors occurred.\n"); + } + if (esr & ESR_NXM) { + printk(" Attempted to access non-existent memory.\n"); + } +} + +static void +ioc_error(__u32 stat0, __u32 stat1) +{ + static const char * const pci_cmd[] = { + "Interrupt Acknowledge", "Special", "I/O Read", "I/O Write", + "Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", + "Rsvd4", "Configuration Read", "Configuration Write", + "Memory Read Multiple", "Dual Address", "Memory Read Line", + "Memory Write and Invalidate" + }; + static const char * const err_name[] = { + "exceeded retry limit", "no device", "bad data parity", + "target abort", "bad address parity", "page table read error", + "invalid page", "data error" + }; + unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT; + unsigned cmd = (stat0 & IOC_CMD) >> IOC_CMD_SHIFT; + + printk(" %s initiated PCI %s cycle to address %x" + " failed due to %s.\n", + code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]); + + if (code == 5 || code == 6) { + printk(" (Error occurred at PCI memory address %x.)\n", + (stat0 & ~IOC_P_NBR)); + } + if (stat0 & IOC_LOST) { + printk(" Other PCI errors occurred simultaneously.\n"); + } +} + +void +lca_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs) +{ + const char * reason; + union el_lca el; + + el.c = (struct el_common *) la_ptr; + + wrmces(rdmces()); /* reset machine check pending flag */ + + printk(KERN_CRIT "LCA machine check: vector=%#lx pc=%#lx code=%#x\n", + vector, regs->pc, (unsigned int) el.c->code); + + /* + * The first quadword after the common header always seems to + * be the machine check reason---don't know why this isn't + * part of the common header instead. In the case of a long + * logout frame, the upper 32 bits is the machine check + * revision level, which we ignore for now. + */ + switch ((unsigned int) el.c->code) { + case MCHK_K_TPERR: reason = "tag parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; + case MCHK_K_HERR: reason = "access to non-existent memory"; break; + case MCHK_K_ECC_C: reason = "correctable ECC error"; break; + case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; + case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; + case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break; + case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; + case MCHK_K_DCPERR: reason = "d-cache parity error"; break; + case MCHK_K_ICPERR: reason = "i-cache parity error"; break; + case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on PCI bus"; break; + case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; + case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; + case MCHK_K_UNKNOWN: + default: reason = "unknown"; break; + } + + switch (el.c->size) { + case sizeof(struct el_lca_mcheck_short): + printk(KERN_CRIT + " Reason: %s (short frame%s, dc_stat=%#lx):\n", + reason, el.c->retry ? ", retryable" : "", + el.s->dc_stat); + if (el.s->esr & ESR_EAV) { + mem_error(el.s->esr, el.s->ear); + } + if (el.s->ioc_stat0 & IOC_ERR) { + ioc_error(el.s->ioc_stat0, el.s->ioc_stat1); + } + break; + + case sizeof(struct el_lca_mcheck_long): + printk(KERN_CRIT " Reason: %s (long frame%s):\n", + reason, el.c->retry ? ", retryable" : ""); + printk(KERN_CRIT + " reason: %#lx exc_addr: %#lx dc_stat: %#lx\n", + el.l->pt[0], el.l->exc_addr, el.l->dc_stat); + printk(KERN_CRIT " car: %#lx\n", el.l->car); + if (el.l->esr & ESR_EAV) { + mem_error(el.l->esr, el.l->ear); + } + if (el.l->ioc_stat0 & IOC_ERR) { + ioc_error(el.l->ioc_stat0, el.l->ioc_stat1); + } + break; + + default: + printk(KERN_CRIT " Unknown errorlog size %d\n", el.c->size); + } + + /* Dump the logout area to give all info. */ +#ifdef CONFIG_VERBOSE_MCHECK + if (alpha_verbose_mcheck > 1) { + unsigned long * ptr = (unsigned long *) la_ptr; + long i; + for (i = 0; i < el.c->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif /* CONFIG_VERBOSE_MCHECK */ +} + +/* + * The following routines are needed to support the SPEED changing + * necessary to successfully manage the thermal problem on the AlphaBook1. + */ + +void +lca_clock_print(void) +{ + long pmr_reg; + + pmr_reg = LCA_READ_PMR; + + printk("Status of clock control:\n"); + printk("\tPrimary clock divisor\t0x%lx\n", LCA_GET_PRIMARY(pmr_reg)); + printk("\tOverride clock divisor\t0x%lx\n", LCA_GET_OVERRIDE(pmr_reg)); + printk("\tInterrupt override is %s\n", + (pmr_reg & LCA_PMR_INTO) ? "on" : "off"); + printk("\tDMA override is %s\n", + (pmr_reg & LCA_PMR_DMAO) ? "on" : "off"); + +} + +int +lca_get_clock(void) +{ + long pmr_reg; + + pmr_reg = LCA_READ_PMR; + return(LCA_GET_PRIMARY(pmr_reg)); + +} + +void +lca_clock_fiddle(int divisor) +{ + long pmr_reg; + + pmr_reg = LCA_READ_PMR; + LCA_SET_PRIMARY_CLOCK(pmr_reg, divisor); + /* lca_norm_clock = divisor; */ + LCA_WRITE_PMR(pmr_reg); + mb(); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_marvel.c + * + * Code common to all Marvel based systems. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_marvel.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/mc146818rtc.h> +#include <linux/rtc.h> +#include <linux/module.h> +#include <linux/bootmem.h> + +#include <asm/ptrace.h> +#include <asm/smp.h> +#include <asm/gct.h> +#include <asm/pgalloc.h> +#include <asm/tlbflush.h> +#include <asm/rtc.h> + +#include "proto.h" +#include "pci_impl.h" + + +/* + * Debug helpers + */ +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + + +/* + * Private data + */ +static struct io7 *io7_head = NULL; + + +/* + * Helper functions + */ +static unsigned long __attribute__ ((unused)) +read_ev7_csr(int pe, unsigned long offset) +{ + ev7_csr *ev7csr = EV7_CSR_KERN(pe, offset); + unsigned long q; + + mb(); + q = ev7csr->csr; + mb(); + + return q; +} + +static void __attribute__ ((unused)) +write_ev7_csr(int pe, unsigned long offset, unsigned long q) +{ + ev7_csr *ev7csr = EV7_CSR_KERN(pe, offset); + + mb(); + ev7csr->csr = q; + mb(); +} + +static char * __init +mk_resource_name(int pe, int port, char *str) +{ + char tmp[80]; + char *name; + + sprintf(tmp, "PCI %s PE %d PORT %d", str, pe, port); + name = alloc_bootmem(strlen(tmp) + 1); + strcpy(name, tmp); + + return name; +} + +inline struct io7 * +marvel_next_io7(struct io7 *prev) +{ + return (prev ? prev->next : io7_head); +} + +struct io7 * +marvel_find_io7(int pe) +{ + struct io7 *io7; + + for (io7 = io7_head; io7 && io7->pe != pe; io7 = io7->next) + continue; + + return io7; +} + +static struct io7 * __init +alloc_io7(unsigned int pe) +{ + struct io7 *io7; + struct io7 *insp; + int h; + + if (marvel_find_io7(pe)) { + printk(KERN_WARNING "IO7 at PE %d already allocated!\n", pe); + return NULL; + } + + io7 = alloc_bootmem(sizeof(*io7)); + io7->pe = pe; + spin_lock_init(&io7->irq_lock); + + for (h = 0; h < 4; h++) { + io7->ports[h].io7 = io7; + io7->ports[h].port = h; + io7->ports[h].enabled = 0; /* default to disabled */ + } + + /* + * Insert in pe sorted order. + */ + if (NULL == io7_head) /* empty list */ + io7_head = io7; + else if (io7_head->pe > io7->pe) { /* insert at head */ + io7->next = io7_head; + io7_head = io7; + } else { /* insert at position */ + for (insp = io7_head; insp; insp = insp->next) { + if (insp->pe == io7->pe) { + printk(KERN_ERR "Too many IO7s at PE %d\n", + io7->pe); + return NULL; + } + + if (NULL == insp->next || + insp->next->pe > io7->pe) { /* insert here */ + io7->next = insp->next; + insp->next = io7; + break; + } + } + + if (NULL == insp) { /* couldn't insert ?!? */ + printk(KERN_WARNING "Failed to insert IO7 at PE %d " + " - adding at head of list\n", io7->pe); + io7->next = io7_head; + io7_head = io7; + } + } + + return io7; +} + +void +io7_clear_errors(struct io7 *io7) +{ + io7_port7_csrs *p7csrs; + io7_ioport_csrs *csrs; + int port; + + + /* + * First the IO ports. + */ + for (port = 0; port < 4; port++) { + csrs = IO7_CSRS_KERN(io7->pe, port); + + csrs->POx_ERR_SUM.csr = -1UL; + csrs->POx_TLB_ERR.csr = -1UL; + csrs->POx_SPL_COMPLT.csr = -1UL; + csrs->POx_TRANS_SUM.csr = -1UL; + } + + /* + * Then the common ones. + */ + p7csrs = IO7_PORT7_CSRS_KERN(io7->pe); + + p7csrs->PO7_ERROR_SUM.csr = -1UL; + p7csrs->PO7_UNCRR_SYM.csr = -1UL; + p7csrs->PO7_CRRCT_SYM.csr = -1UL; +} + + +/* + * IO7 PCI, PCI/X, AGP configuration. + */ +static void __init +io7_init_hose(struct io7 *io7, int port) +{ + static int hose_index = 0; + + struct pci_controller *hose = alloc_pci_controller(); + struct io7_port *io7_port = &io7->ports[port]; + io7_ioport_csrs *csrs = IO7_CSRS_KERN(io7->pe, port); + int i; + + hose->index = hose_index++; /* arbitrary */ + + /* + * We don't have an isa or legacy hose, but glibc expects to be + * able to use the bus == 0 / dev == 0 form of the iobase syscall + * to determine information about the i/o system. Since XFree86 + * relies on glibc's determination to tell whether or not to use + * sparse access, we need to point the pci_isa_hose at a real hose + * so at least that determination is correct. + */ + if (hose->index == 0) + pci_isa_hose = hose; + + io7_port->csrs = csrs; + io7_port->hose = hose; + hose->sysdata = io7_port; + + hose->io_space = alloc_resource(); + hose->mem_space = alloc_resource(); + + /* + * Base addresses for userland consumption. Since these are going + * to be mapped, they are pure physical addresses. + */ + hose->sparse_mem_base = hose->sparse_io_base = 0; + hose->dense_mem_base = IO7_MEM_PHYS(io7->pe, port); + hose->dense_io_base = IO7_IO_PHYS(io7->pe, port); + + /* + * Base addresses and resource ranges for kernel consumption. + */ + hose->config_space_base = (unsigned long)IO7_CONF_KERN(io7->pe, port); + + hose->io_space->start = (unsigned long)IO7_IO_KERN(io7->pe, port); + hose->io_space->end = hose->io_space->start + IO7_IO_SPACE - 1; + hose->io_space->name = mk_resource_name(io7->pe, port, "IO"); + hose->io_space->flags = IORESOURCE_IO; + + hose->mem_space->start = (unsigned long)IO7_MEM_KERN(io7->pe, port); + hose->mem_space->end = hose->mem_space->start + IO7_MEM_SPACE - 1; + hose->mem_space->name = mk_resource_name(io7->pe, port, "MEM"); + hose->mem_space->flags = IORESOURCE_MEM; + + if (request_resource(&ioport_resource, hose->io_space) < 0) + printk(KERN_ERR "Failed to request IO on hose %d\n", + hose->index); + if (request_resource(&iomem_resource, hose->mem_space) < 0) + printk(KERN_ERR "Failed to request MEM on hose %d\n", + hose->index); + + /* + * Save the existing DMA window settings for later restoration. + */ + for (i = 0; i < 4; i++) { + io7_port->saved_wbase[i] = csrs->POx_WBASE[i].csr; + io7_port->saved_wmask[i] = csrs->POx_WMASK[i].csr; + io7_port->saved_tbase[i] = csrs->POx_TBASE[i].csr; + } + + /* + * Set up the PCI to main memory translation windows. + * + * Window 0 is scatter-gather 8MB at 8MB + * Window 1 is direct access 1GB at 2GB + * Window 2 is scatter-gather (up-to) 1GB at 3GB + * Window 3 is disabled + */ + + /* + * TBIA before modifying windows. + */ + marvel_pci_tbi(hose, 0, -1); + + /* + * Set up window 0 for scatter-gather 8MB at 8MB. + */ + hose->sg_isa = iommu_arena_new_node(marvel_cpuid_to_nid(io7->pe), + hose, 0x00800000, 0x00800000, 0); + hose->sg_isa->align_entry = 8; /* cache line boundary */ + csrs->POx_WBASE[0].csr = + hose->sg_isa->dma_base | wbase_m_ena | wbase_m_sg; + csrs->POx_WMASK[0].csr = (hose->sg_isa->size - 1) & wbase_m_addr; + csrs->POx_TBASE[0].csr = virt_to_phys(hose->sg_isa->ptes); + + /* + * Set up window 1 for direct-mapped 1GB at 2GB. + */ + csrs->POx_WBASE[1].csr = __direct_map_base | wbase_m_ena; + csrs->POx_WMASK[1].csr = (__direct_map_size - 1) & wbase_m_addr; + csrs->POx_TBASE[1].csr = 0; + + /* + * Set up window 2 for scatter-gather (up-to) 1GB at 3GB. + */ + hose->sg_pci = iommu_arena_new_node(marvel_cpuid_to_nid(io7->pe), + hose, 0xc0000000, 0x40000000, 0); + hose->sg_pci->align_entry = 8; /* cache line boundary */ + csrs->POx_WBASE[2].csr = + hose->sg_pci->dma_base | wbase_m_ena | wbase_m_sg; + csrs->POx_WMASK[2].csr = (hose->sg_pci->size - 1) & wbase_m_addr; + csrs->POx_TBASE[2].csr = virt_to_phys(hose->sg_pci->ptes); + + /* + * Disable window 3. + */ + csrs->POx_WBASE[3].csr = 0; + + /* + * Make sure that the AGP Monster Window is disabled. + */ + csrs->POx_CTRL.csr &= ~(1UL << 61); + +#if 1 + printk("FIXME: disabling master aborts\n"); + csrs->POx_MSK_HEI.csr &= ~(3UL << 14); +#endif + /* + * TBIA after modifying windows. + */ + marvel_pci_tbi(hose, 0, -1); +} + +static void __init +marvel_init_io7(struct io7 *io7) +{ + int i; + + printk("Initializing IO7 at PID %d\n", io7->pe); + + /* + * Get the Port 7 CSR pointer. + */ + io7->csrs = IO7_PORT7_CSRS_KERN(io7->pe); + + /* + * Init this IO7's hoses. + */ + for (i = 0; i < IO7_NUM_PORTS; i++) { + io7_ioport_csrs *csrs = IO7_CSRS_KERN(io7->pe, i); + if (csrs->POx_CACHE_CTL.csr == 8) { + io7->ports[i].enabled = 1; + io7_init_hose(io7, i); + } + } +} + +void +marvel_io7_present(gct6_node *node) +{ + int pe; + + if (node->type != GCT_TYPE_HOSE || + node->subtype != GCT_SUBTYPE_IO_PORT_MODULE) + return; + + pe = (node->id >> 8) & 0xff; + printk("Found an IO7 at PID %d\n", pe); + + alloc_io7(pe); +} + +static void __init +marvel_init_vga_hose(void) +{ +#ifdef CONFIG_VGA_HOSE + u64 *pu64 = (u64 *)((u64)hwrpb + hwrpb->ctbt_offset); + + if (pu64[7] == 3) { /* TERM_TYPE == graphics */ + struct pci_controller *hose = NULL; + int h = (pu64[30] >> 24) & 0xff; /* TERM_OUT_LOC, hose # */ + struct io7 *io7; + int pid, port; + + /* FIXME - encoding is going to have to change for Marvel + * since hose will be able to overflow a byte... + * need to fix this decode when the console + * changes its encoding + */ + printk("console graphics is on hose %d (console)\n", h); + + /* + * The console's hose numbering is: + * + * hose<n:2>: PID + * hose<1:0>: PORT + * + * We need to find the hose at that pid and port + */ + pid = h >> 2; + port = h & 3; + if ((io7 = marvel_find_io7(pid))) + hose = io7->ports[port].hose; + + if (hose) { + printk("Console graphics on hose %d\n", hose->index); + pci_vga_hose = hose; + } + } +#endif /* CONFIG_VGA_HOSE */ +} + +gct6_search_struct gct_wanted_node_list[] = { + { GCT_TYPE_HOSE, GCT_SUBTYPE_IO_PORT_MODULE, marvel_io7_present }, + { 0, 0, NULL } +}; + +/* + * In case the GCT is not complete, let the user specify PIDs with IO7s + * at boot time. Syntax is 'io7=a,b,c,...,n' where a-n are the PIDs (decimal) + * where IO7s are connected + */ +static int __init +marvel_specify_io7(char *str) +{ + unsigned long pid; + struct io7 *io7; + char *pchar; + + do { + pid = simple_strtoul(str, &pchar, 0); + if (pchar != str) { + printk("User-specified IO7 at PID %lu\n", pid); + io7 = alloc_io7(pid); + if (io7) marvel_init_io7(io7); + } + + if (pchar == str) pchar++; + str = pchar; + } while(*str); + + return 0; +} +__setup("io7=", marvel_specify_io7); + +void __init +marvel_init_arch(void) +{ + struct io7 *io7; + + /* With multiple PCI busses, we play with I/O as physical addrs. */ + ioport_resource.end = ~0UL; + + /* PCI DMA Direct Mapping is 1GB at 2GB. */ + __direct_map_base = 0x80000000; + __direct_map_size = 0x40000000; + + /* Parse the config tree. */ + gct6_find_nodes(GCT_NODE_PTR(0), gct_wanted_node_list); + + /* Init the io7s. */ + for (io7 = NULL; NULL != (io7 = marvel_next_io7(io7)); ) + marvel_init_io7(io7); + + /* Check for graphic console location (if any). */ + marvel_init_vga_hose(); +} + +void +marvel_kill_arch(int mode) +{ +} + + +/* + * PCI Configuration Space access functions + * + * Configuration space addresses have the following format: + * + * |2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * |3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|R|R| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * n:24 reserved for hose base + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * IO7 determines whether to use a type 0 or type 1 config cycle + * based on the bus number. Therefore the bus number must be set + * to 0 for the root bus on any hose. + * + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + */ + +static inline unsigned long +build_conf_addr(struct pci_controller *hose, u8 bus, + unsigned int devfn, int where) +{ + return (hose->config_space_base | (bus << 16) | (devfn << 8) | where); +} + +static unsigned long +mk_conf_addr(struct pci_bus *pbus, unsigned int devfn, int where) +{ + struct pci_controller *hose = pbus->sysdata; + struct io7_port *io7_port; + unsigned long addr = 0; + u8 bus = pbus->number; + + if (!hose) + return addr; + + /* Check for enabled. */ + io7_port = hose->sysdata; + if (!io7_port->enabled) + return addr; + + if (!pbus->parent) { /* No parent means peer PCI bus. */ + /* Don't support idsel > 20 on primary bus. */ + if (devfn >= PCI_DEVFN(21, 0)) + return addr; + bus = 0; + } + + addr = build_conf_addr(hose, bus, devfn, where); + + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return addr; +} + +static int +marvel_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + + if (0 == (addr = mk_conf_addr(bus, devfn, where))) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch(size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int +marvel_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + + if (0 == (addr = mk_conf_addr(bus, devfn, where))) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops marvel_pci_ops = +{ + .read = marvel_read_config, + .write = marvel_write_config, +}; + + +/* + * Other PCI helper functions. + */ +void +marvel_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + io7_ioport_csrs *csrs = ((struct io7_port *)hose->sysdata)->csrs; + + wmb(); + csrs->POx_SG_TBIA.csr = 0; + mb(); + csrs->POx_SG_TBIA.csr; +} + + + +/* + * RTC Support + */ +struct marvel_rtc_access_info { + unsigned long function; + unsigned long index; + unsigned long data; +}; + +static void +__marvel_access_rtc(void *info) +{ + struct marvel_rtc_access_info *rtc_access = info; + + register unsigned long __r0 __asm__("$0"); + register unsigned long __r16 __asm__("$16") = rtc_access->function; + register unsigned long __r17 __asm__("$17") = rtc_access->index; + register unsigned long __r18 __asm__("$18") = rtc_access->data; + + __asm__ __volatile__( + "call_pal %4 # cserve rtc" + : "=r"(__r16), "=r"(__r17), "=r"(__r18), "=r"(__r0) + : "i"(PAL_cserve), "0"(__r16), "1"(__r17), "2"(__r18) + : "$1", "$22", "$23", "$24", "$25"); + + rtc_access->data = __r0; +} + +static u8 +__marvel_rtc_io(u8 b, unsigned long addr, int write) +{ + static u8 index = 0; + + struct marvel_rtc_access_info rtc_access; + u8 ret = 0; + + switch(addr) { + case 0x70: /* RTC_PORT(0) */ + if (write) index = b; + ret = index; + break; + + case 0x71: /* RTC_PORT(1) */ + rtc_access.index = index; + rtc_access.data = BCD_TO_BIN(b); + rtc_access.function = 0x48 + !write; /* GET/PUT_TOY */ + +#ifdef CONFIG_SMP + if (smp_processor_id() != boot_cpuid) + smp_call_function_on_cpu(__marvel_access_rtc, + &rtc_access, 1, 1, + cpumask_of_cpu(boot_cpuid)); + else + __marvel_access_rtc(&rtc_access); +#else + __marvel_access_rtc(&rtc_access); +#endif + ret = BIN_TO_BCD(rtc_access.data); + break; + + default: + printk(KERN_WARNING "Illegal RTC port %lx\n", addr); + break; + } + + return ret; +} + + +/* + * IO map support. + */ + +#define __marvel_is_mem_vga(a) (((a) >= 0xa0000) && ((a) <= 0xc0000)) + +void __iomem * +marvel_ioremap(unsigned long addr, unsigned long size) +{ + struct pci_controller *hose; + unsigned long baddr, last; + struct vm_struct *area; + unsigned long vaddr; + unsigned long *ptes; + unsigned long pfn; + + /* + * Adjust the addr. + */ +#ifdef CONFIG_VGA_HOSE + if (pci_vga_hose && __marvel_is_mem_vga(addr)) { + addr += pci_vga_hose->mem_space->start; + } +#endif + + /* + * Find the hose. + */ + for (hose = hose_head; hose; hose = hose->next) { + if ((addr >> 32) == (hose->mem_space->start >> 32)) + break; + } + if (!hose) + return NULL; + + /* + * We have the hose - calculate the bus limits. + */ + baddr = addr - hose->mem_space->start; + last = baddr + size - 1; + + /* + * Is it direct-mapped? + */ + if ((baddr >= __direct_map_base) && + ((baddr + size - 1) < __direct_map_base + __direct_map_size)) { + addr = IDENT_ADDR | (baddr - __direct_map_base); + return (void __iomem *) addr; + } + + /* + * Check the scatter-gather arena. + */ + if (hose->sg_pci && + baddr >= (unsigned long)hose->sg_pci->dma_base && + last < (unsigned long)hose->sg_pci->dma_base + hose->sg_pci->size) { + + /* + * Adjust the limits (mappings must be page aligned) + */ + baddr -= hose->sg_pci->dma_base; + last -= hose->sg_pci->dma_base; + baddr &= PAGE_MASK; + size = PAGE_ALIGN(last) - baddr; + + /* + * Map it. + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + + ptes = hose->sg_pci->ptes; + for (vaddr = (unsigned long)area->addr; + baddr <= last; + baddr += PAGE_SIZE, vaddr += PAGE_SIZE) { + pfn = ptes[baddr >> PAGE_SHIFT]; + if (!(pfn & 1)) { + printk("ioremap failed... pte not valid...\n"); + vfree(area->addr); + return NULL; + } + pfn >>= 1; /* make it a true pfn */ + + if (__alpha_remap_area_pages(vaddr, + pfn << PAGE_SHIFT, + PAGE_SIZE, 0)) { + printk("FAILED to map...\n"); + vfree(area->addr); + return NULL; + } + } + + flush_tlb_all(); + + vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); + + return (void __iomem *) vaddr; + } + + return NULL; +} + +void +marvel_iounmap(volatile void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + if (addr >= VMALLOC_START) + vfree((void *)(PAGE_MASK & addr)); +} + +int +marvel_is_mmio(const volatile void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + + if (addr >= VMALLOC_START) + return 1; + else + return (addr & 0xFF000000UL) == 0; +} + +#define __marvel_is_port_vga(a) \ + (((a) >= 0x3b0) && ((a) < 0x3e0) && ((a) != 0x3b3) && ((a) != 0x3d3)) +#define __marvel_is_port_kbd(a) (((a) == 0x60) || ((a) == 0x64)) +#define __marvel_is_port_rtc(a) (((a) == 0x70) || ((a) == 0x71)) + +void __iomem *marvel_ioportmap (unsigned long addr) +{ + if (__marvel_is_port_rtc (addr) || __marvel_is_port_kbd(addr)) + ; +#ifdef CONFIG_VGA_HOSE + else if (__marvel_is_port_vga (addr) && pci_vga_hose) + addr += pci_vga_hose->io_space->start; +#endif + else + return NULL; + return (void __iomem *)addr; +} + +unsigned int +marvel_ioread8(void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + if (__marvel_is_port_kbd(addr)) + return 0; + else if (__marvel_is_port_rtc(addr)) + return __marvel_rtc_io(0, addr, 0); + else + return __kernel_ldbu(*(vucp)addr); +} + +void +marvel_iowrite8(u8 b, void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + if (__marvel_is_port_kbd(addr)) + return; + else if (__marvel_is_port_rtc(addr)) + __marvel_rtc_io(b, addr, 1); + else + __kernel_stb(b, *(vucp)addr); +} + +#ifndef CONFIG_ALPHA_GENERIC +EXPORT_SYMBOL(marvel_ioremap); +EXPORT_SYMBOL(marvel_iounmap); +EXPORT_SYMBOL(marvel_is_mmio); +EXPORT_SYMBOL(marvel_ioportmap); +EXPORT_SYMBOL(marvel_ioread8); +EXPORT_SYMBOL(marvel_iowrite8); +#endif + +/* + * NUMA Support + */ +/********** + * FIXME - for now each cpu is a node by itself + * -- no real support for striped mode + ********** + */ +int +marvel_pa_to_nid(unsigned long pa) +{ + int cpuid; + + if ((pa >> 43) & 1) /* I/O */ + cpuid = (~(pa >> 35) & 0xff); + else /* mem */ + cpuid = ((pa >> 34) & 0x3) | ((pa >> (37 - 2)) & (0x1f << 2)); + + return marvel_cpuid_to_nid(cpuid); +} + +int +marvel_cpuid_to_nid(int cpuid) +{ + return cpuid; +} + +unsigned long +marvel_node_mem_start(int nid) +{ + unsigned long pa; + + pa = (nid & 0x3) | ((nid & (0x1f << 2)) << 1); + pa <<= 34; + + return pa; +} + +unsigned long +marvel_node_mem_size(int nid) +{ + return 16UL * 1024 * 1024 * 1024; /* 16GB */ +} + + +/* + * AGP GART Support. + */ +#include <linux/agp_backend.h> +#include <asm/agp_backend.h> +#include <linux/slab.h> +#include <linux/delay.h> + +struct marvel_agp_aperture { + struct pci_iommu_arena *arena; + long pg_start; + long pg_count; +}; + +static int +marvel_agp_setup(alpha_agp_info *agp) +{ + struct marvel_agp_aperture *aper; + + if (!alpha_agpgart_size) + return -ENOMEM; + + aper = kmalloc(sizeof(*aper), GFP_KERNEL); + if (aper == NULL) return -ENOMEM; + + aper->arena = agp->hose->sg_pci; + aper->pg_count = alpha_agpgart_size / PAGE_SIZE; + aper->pg_start = iommu_reserve(aper->arena, aper->pg_count, + aper->pg_count - 1); + + if (aper->pg_start < 0) { + printk(KERN_ERR "Failed to reserve AGP memory\n"); + kfree(aper); + return -ENOMEM; + } + + agp->aperture.bus_base = + aper->arena->dma_base + aper->pg_start * PAGE_SIZE; + agp->aperture.size = aper->pg_count * PAGE_SIZE; + agp->aperture.sysdata = aper; + + return 0; +} + +static void +marvel_agp_cleanup(alpha_agp_info *agp) +{ + struct marvel_agp_aperture *aper = agp->aperture.sysdata; + int status; + + status = iommu_release(aper->arena, aper->pg_start, aper->pg_count); + if (status == -EBUSY) { + printk(KERN_WARNING + "Attempted to release bound AGP memory - unbinding\n"); + iommu_unbind(aper->arena, aper->pg_start, aper->pg_count); + status = iommu_release(aper->arena, aper->pg_start, + aper->pg_count); + } + if (status < 0) + printk(KERN_ERR "Failed to release AGP memory\n"); + + kfree(aper); + kfree(agp); +} + +static int +marvel_agp_configure(alpha_agp_info *agp) +{ + io7_ioport_csrs *csrs = ((struct io7_port *)agp->hose->sysdata)->csrs; + struct io7 *io7 = ((struct io7_port *)agp->hose->sysdata)->io7; + unsigned int new_rate = 0; + unsigned long agp_pll; + + /* + * Check the requested mode against the PLL setting. + * The agpgart_be code has not programmed the card yet, + * so we can still tweak mode here. + */ + agp_pll = io7->csrs->POx_RST[IO7_AGP_PORT].csr; + switch(IO7_PLL_RNGB(agp_pll)) { + case 0x4: /* 2x only */ + /* + * The PLL is only programmed for 2x, so adjust the + * rate to 2x, if necessary. + */ + if (agp->mode.bits.rate != 2) + new_rate = 2; + break; + + case 0x6: /* 1x / 4x */ + /* + * The PLL is programmed for 1x or 4x. Don't go faster + * than requested, so if the requested rate is 2x, use 1x. + */ + if (agp->mode.bits.rate == 2) + new_rate = 1; + break; + + default: /* ??????? */ + /* + * Don't know what this PLL setting is, take the requested + * rate, but warn the user. + */ + printk("%s: unknown PLL setting RNGB=%lx (PLL6_CTL=%016lx)\n", + __FUNCTION__, IO7_PLL_RNGB(agp_pll), agp_pll); + break; + } + + /* + * Set the new rate, if necessary. + */ + if (new_rate) { + printk("Requested AGP Rate %dX not compatible " + "with PLL setting - using %dX\n", + agp->mode.bits.rate, + new_rate); + + agp->mode.bits.rate = new_rate; + } + + printk("Enabling AGP on hose %d: %dX%s RQ %d\n", + agp->hose->index, agp->mode.bits.rate, + agp->mode.bits.sba ? " - SBA" : "", agp->mode.bits.rq); + + csrs->AGP_CMD.csr = agp->mode.lw; + + return 0; +} + +static int +marvel_agp_bind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) +{ + struct marvel_agp_aperture *aper = agp->aperture.sysdata; + return iommu_bind(aper->arena, aper->pg_start + pg_start, + mem->page_count, mem->memory); +} + +static int +marvel_agp_unbind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) +{ + struct marvel_agp_aperture *aper = agp->aperture.sysdata; + return iommu_unbind(aper->arena, aper->pg_start + pg_start, + mem->page_count); +} + +static unsigned long +marvel_agp_translate(alpha_agp_info *agp, dma_addr_t addr) +{ + struct marvel_agp_aperture *aper = agp->aperture.sysdata; + unsigned long baddr = addr - aper->arena->dma_base; + unsigned long pte; + + if (addr < agp->aperture.bus_base || + addr >= agp->aperture.bus_base + agp->aperture.size) { + printk("%s: addr out of range\n", __FUNCTION__); + return -EINVAL; + } + + pte = aper->arena->ptes[baddr >> PAGE_SHIFT]; + if (!(pte & 1)) { + printk("%s: pte not valid\n", __FUNCTION__); + return -EINVAL; + } + return (pte >> 1) << PAGE_SHIFT; +} + +struct alpha_agp_ops marvel_agp_ops = +{ + .setup = marvel_agp_setup, + .cleanup = marvel_agp_cleanup, + .configure = marvel_agp_configure, + .bind = marvel_agp_bind_memory, + .unbind = marvel_agp_unbind_memory, + .translate = marvel_agp_translate +}; + +alpha_agp_info * +marvel_agp_info(void) +{ + struct pci_controller *hose; + io7_ioport_csrs *csrs; + alpha_agp_info *agp; + struct io7 *io7; + + /* + * Find the first IO7 with an AGP card. + * + * FIXME -- there should be a better way (we want to be able to + * specify and what if the agp card is not video???) + */ + hose = NULL; + for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; ) { + struct pci_controller *h; + vuip addr; + + if (!io7->ports[IO7_AGP_PORT].enabled) + continue; + + h = io7->ports[IO7_AGP_PORT].hose; + addr = (vuip)build_conf_addr(h, 0, PCI_DEVFN(5, 0), 0); + + if (*addr != 0xffffffffu) { + hose = h; + break; + } + } + + if (!hose || !hose->sg_pci) + return NULL; + + printk("MARVEL - using hose %d as AGP\n", hose->index); + + /* + * Get the csrs from the hose. + */ + csrs = ((struct io7_port *)hose->sysdata)->csrs; + + /* + * Allocate the info structure. + */ + agp = kmalloc(sizeof(*agp), GFP_KERNEL); + + /* + * Fill it in. + */ + agp->hose = hose; + agp->private = NULL; + agp->ops = &marvel_agp_ops; + + /* + * Aperture - not configured until ops.setup(). + */ + agp->aperture.bus_base = 0; + agp->aperture.size = 0; + agp->aperture.sysdata = NULL; + + /* + * Capabilities. + * + * NOTE: IO7 reports through AGP_STAT that it can support a read queue + * depth of 17 (rq = 0x10). It actually only supports a depth of + * 16 (rq = 0xf). + */ + agp->capability.lw = csrs->AGP_STAT.csr; + agp->capability.bits.rq = 0xf; + + /* + * Mode. + */ + agp->mode.lw = csrs->AGP_CMD.csr; + + return agp; +} 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 @@ +/* + * linux/arch/alpha/kernel/core_mcpcia.c + * + * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). + * + * Code common to all MCbus-PCI Adaptor core logic chipsets + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_mcpcia.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/ptrace.h> + +#include "proto.h" +#include "pci_impl.h" + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CFG 0 + +#if DEBUG_CFG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +#define MCPCIA_MAX_HOSES 4 + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the MCPCIA_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |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| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static unsigned int +conf_read(unsigned long addr, unsigned char type1, + struct pci_controller *hose) +{ + unsigned long flags; + unsigned long mid = MCPCIA_HOSE2MID(hose->index); + unsigned int stat0, value, temp, cpu; + + cpu = smp_processor_id(); + + local_irq_save(flags); + + DBG_CFG(("conf_read(addr=0x%lx, type1=%d, hose=%d)\n", + addr, type1, mid)); + + /* Reset status register to avoid losing errors. */ + stat0 = *(vuip)MCPCIA_CAP_ERR(mid); + *(vuip)MCPCIA_CAP_ERR(mid) = stat0; + mb(); + temp = *(vuip)MCPCIA_CAP_ERR(mid); + DBG_CFG(("conf_read: MCPCIA_CAP_ERR(%d) was 0x%x\n", mid, stat0)); + + mb(); + draina(); + mcheck_expected(cpu) = 1; + mcheck_taken(cpu) = 0; + mcheck_extra(cpu) = mid; + mb(); + + /* Access configuration space. */ + value = *((vuip)addr); + mb(); + mb(); /* magic */ + + if (mcheck_taken(cpu)) { + mcheck_taken(cpu) = 0; + value = 0xffffffffU; + mb(); + } + mcheck_expected(cpu) = 0; + mb(); + + DBG_CFG(("conf_read(): finished\n")); + + local_irq_restore(flags); + return value; +} + +static void +conf_write(unsigned long addr, unsigned int value, unsigned char type1, + struct pci_controller *hose) +{ + unsigned long flags; + unsigned long mid = MCPCIA_HOSE2MID(hose->index); + unsigned int stat0, temp, cpu; + + cpu = smp_processor_id(); + + local_irq_save(flags); /* avoid getting hit by machine check */ + + /* Reset status register to avoid losing errors. */ + stat0 = *(vuip)MCPCIA_CAP_ERR(mid); + *(vuip)MCPCIA_CAP_ERR(mid) = stat0; mb(); + temp = *(vuip)MCPCIA_CAP_ERR(mid); + DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", mid, stat0)); + + draina(); + mcheck_expected(cpu) = 1; + mcheck_extra(cpu) = mid; + mb(); + + /* Access configuration space. */ + *((vuip)addr) = value; + mb(); + mb(); /* magic */ + temp = *(vuip)MCPCIA_CAP_ERR(mid); /* read to force the write */ + mcheck_expected(cpu) = 0; + mb(); + + DBG_CFG(("conf_write(): finished\n")); + local_irq_restore(flags); +} + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int devfn, int where, + struct pci_controller *hose, unsigned long *pci_addr, + unsigned char *type1) +{ + u8 bus = pbus->number; + unsigned long addr; + + DBG_CFG(("mk_conf_addr(bus=%d,devfn=0x%x,hose=%d,where=0x%x," + " pci_addr=0x%p, type1=0x%p)\n", + bus, devfn, hose->index, where, pci_addr, type1)); + + /* Type 1 configuration cycle for *ALL* busses. */ + *type1 = 1; + + if (!pbus->parent) /* No parent means peer PCI bus. */ + bus = 0; + addr = (bus << 16) | (devfn << 8) | (where); + addr <<= 5; /* swizzle for SPARSE */ + addr |= hose->config_space_base; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static int +mcpcia_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + struct pci_controller *hose = bus->sysdata; + unsigned long addr, w; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr |= (size - 1) * 8; + w = conf_read(addr, type1, hose); + switch (size) { + case 1: + *value = __kernel_extbl(w, where & 3); + break; + case 2: + *value = __kernel_extwl(w, where & 3); + break; + case 4: + *value = w; + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int +mcpcia_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + struct pci_controller *hose = bus->sysdata; + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, hose, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr |= (size - 1) * 8; + value = __kernel_insql(value, where & 3); + conf_write(addr, value, type1, hose); + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops mcpcia_pci_ops = +{ + .read = mcpcia_read_config, + .write = mcpcia_write_config, +}; + +void +mcpcia_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + wmb(); + *(vuip)MCPCIA_SG_TBIA(MCPCIA_HOSE2MID(hose->index)) = 0; + mb(); +} + +static int __init +mcpcia_probe_hose(int h) +{ + int cpu = smp_processor_id(); + int mid = MCPCIA_HOSE2MID(h); + unsigned int pci_rev; + + /* Gotta be REAL careful. If hose is absent, we get an mcheck. */ + + mb(); + mb(); + draina(); + wrmces(7); + + mcheck_expected(cpu) = 2; /* indicates probing */ + mcheck_taken(cpu) = 0; + mcheck_extra(cpu) = mid; + mb(); + + /* Access the bus revision word. */ + pci_rev = *(vuip)MCPCIA_REV(mid); + + mb(); + mb(); /* magic */ + if (mcheck_taken(cpu)) { + mcheck_taken(cpu) = 0; + pci_rev = 0xffffffff; + mb(); + } + mcheck_expected(cpu) = 0; + mb(); + + return (pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST; +} + +static void __init +mcpcia_new_hose(int h) +{ + struct pci_controller *hose; + struct resource *io, *mem, *hae_mem; + int mid = MCPCIA_HOSE2MID(h); + + hose = alloc_pci_controller(); + if (h == 0) + pci_isa_hose = hose; + io = alloc_resource(); + mem = alloc_resource(); + hae_mem = alloc_resource(); + + hose->io_space = io; + hose->mem_space = hae_mem; + hose->sparse_mem_base = MCPCIA_SPARSE(mid) - IDENT_ADDR; + hose->dense_mem_base = MCPCIA_DENSE(mid) - IDENT_ADDR; + hose->sparse_io_base = MCPCIA_IO(mid) - IDENT_ADDR; + hose->dense_io_base = 0; + hose->config_space_base = MCPCIA_CONF(mid); + hose->index = h; + + io->start = MCPCIA_IO(mid) - MCPCIA_IO_BIAS; + io->end = io->start + 0xffff; + io->name = pci_io_names[h]; + io->flags = IORESOURCE_IO; + + mem->start = MCPCIA_DENSE(mid) - MCPCIA_MEM_BIAS; + mem->end = mem->start + 0xffffffff; + mem->name = pci_mem_names[h]; + mem->flags = IORESOURCE_MEM; + + hae_mem->start = mem->start; + hae_mem->end = mem->start + MCPCIA_MEM_MASK; + hae_mem->name = pci_hae0_name; + hae_mem->flags = IORESOURCE_MEM; + + if (request_resource(&ioport_resource, io) < 0) + printk(KERN_ERR "Failed to request IO on hose %d\n", h); + if (request_resource(&iomem_resource, mem) < 0) + printk(KERN_ERR "Failed to request MEM on hose %d\n", h); + if (request_resource(mem, hae_mem) < 0) + printk(KERN_ERR "Failed to request HAE_MEM on hose %d\n", h); +} + +static void +mcpcia_pci_clr_err(int mid) +{ + *(vuip)MCPCIA_CAP_ERR(mid); + *(vuip)MCPCIA_CAP_ERR(mid) = 0xffffffff; /* Clear them all. */ + mb(); + *(vuip)MCPCIA_CAP_ERR(mid); /* Re-read for force write. */ +} + +static void __init +mcpcia_startup_hose(struct pci_controller *hose) +{ + int mid = MCPCIA_HOSE2MID(hose->index); + unsigned int tmp; + + mcpcia_pci_clr_err(mid); + + /* + * Set up error reporting. + */ + tmp = *(vuip)MCPCIA_CAP_ERR(mid); + tmp |= 0x0006; /* master/target abort */ + *(vuip)MCPCIA_CAP_ERR(mid) = tmp; + mb(); + tmp = *(vuip)MCPCIA_CAP_ERR(mid); + + /* + * Set up the PCI->physical memory translation windows. + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is scatter-gather (up to) 1GB at 1GB (for pci) + * Window 2 is direct access 2GB at 2GB + */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = iommu_arena_new(hose, 0x40000000, + size_for_memory(0x40000000), 0); + + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; + + *(vuip)MCPCIA_W0_BASE(mid) = hose->sg_isa->dma_base | 3; + *(vuip)MCPCIA_W0_MASK(mid) = (hose->sg_isa->size - 1) & 0xfff00000; + *(vuip)MCPCIA_T0_BASE(mid) = virt_to_phys(hose->sg_isa->ptes) >> 8; + + *(vuip)MCPCIA_W1_BASE(mid) = hose->sg_pci->dma_base | 3; + *(vuip)MCPCIA_W1_MASK(mid) = (hose->sg_pci->size - 1) & 0xfff00000; + *(vuip)MCPCIA_T1_BASE(mid) = virt_to_phys(hose->sg_pci->ptes) >> 8; + + *(vuip)MCPCIA_W2_BASE(mid) = __direct_map_base | 1; + *(vuip)MCPCIA_W2_MASK(mid) = (__direct_map_size - 1) & 0xfff00000; + *(vuip)MCPCIA_T2_BASE(mid) = 0; + + *(vuip)MCPCIA_W3_BASE(mid) = 0x0; + + mcpcia_pci_tbi(hose, 0, -1); + + *(vuip)MCPCIA_HBASE(mid) = 0x0; + mb(); + + *(vuip)MCPCIA_HAE_MEM(mid) = 0U; + mb(); + *(vuip)MCPCIA_HAE_MEM(mid); /* read it back. */ + *(vuip)MCPCIA_HAE_IO(mid) = 0; + mb(); + *(vuip)MCPCIA_HAE_IO(mid); /* read it back. */ +} + +void __init +mcpcia_init_arch(void) +{ + /* With multiple PCI busses, we play with I/O as physical addrs. */ + ioport_resource.end = ~0UL; + + /* Allocate hose 0. That's the one that all the ISA junk hangs + off of, from which we'll be registering stuff here in a bit. + Other hose detection is done in mcpcia_init_hoses, which is + called from init_IRQ. */ + + mcpcia_new_hose(0); +} + +/* This is called from init_IRQ, since we cannot take interrupts + before then. Which means we cannot do this in init_arch. */ + +void __init +mcpcia_init_hoses(void) +{ + struct pci_controller *hose; + int hose_count; + int h; + + /* First, find how many hoses we have. */ + hose_count = 0; + for (h = 0; h < MCPCIA_MAX_HOSES; ++h) { + if (mcpcia_probe_hose(h)) { + if (h != 0) + mcpcia_new_hose(h); + hose_count++; + } + } + + printk("mcpcia_init_hoses: found %d hoses\n", hose_count); + + /* Now do init for each hose. */ + for (hose = hose_head; hose; hose = hose->next) + mcpcia_startup_hose(hose); +} + +static void +mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout) +{ + struct el_common_EV5_uncorrectable_mcheck *frame; + int i; + + frame = &logout->procdata; + + /* Print PAL fields */ + for (i = 0; i < 24; i += 2) { + printk(" paltmp[%d-%d] = %16lx %16lx\n", + i, i+1, frame->paltemp[i], frame->paltemp[i+1]); + } + for (i = 0; i < 8; i += 2) { + printk(" shadow[%d-%d] = %16lx %16lx\n", + i, i+1, frame->shadow[i], + frame->shadow[i+1]); + } + printk(" Addr of excepting instruction = %16lx\n", + frame->exc_addr); + printk(" Summary of arithmetic traps = %16lx\n", + frame->exc_sum); + printk(" Exception mask = %16lx\n", + frame->exc_mask); + printk(" Base address for PALcode = %16lx\n", + frame->pal_base); + printk(" Interrupt Status Reg = %16lx\n", + frame->isr); + printk(" CURRENT SETUP OF EV5 IBOX = %16lx\n", + frame->icsr); + printk(" I-CACHE Reg %s parity error = %16lx\n", + (frame->ic_perr_stat & 0x800L) ? + "Data" : "Tag", + frame->ic_perr_stat); + printk(" D-CACHE error Reg = %16lx\n", + frame->dc_perr_stat); + if (frame->dc_perr_stat & 0x2) { + switch (frame->dc_perr_stat & 0x03c) { + case 8: + printk(" Data error in bank 1\n"); + break; + case 4: + printk(" Data error in bank 0\n"); + break; + case 20: + printk(" Tag error in bank 1\n"); + break; + case 10: + printk(" Tag error in bank 0\n"); + break; + } + } + printk(" Effective VA = %16lx\n", + frame->va); + printk(" Reason for D-stream = %16lx\n", + frame->mm_stat); + printk(" EV5 SCache address = %16lx\n", + frame->sc_addr); + printk(" EV5 SCache TAG/Data parity = %16lx\n", + frame->sc_stat); + printk(" EV5 BC_TAG_ADDR = %16lx\n", + frame->bc_tag_addr); + printk(" EV5 EI_ADDR: Phys addr of Xfer = %16lx\n", + frame->ei_addr); + printk(" Fill Syndrome = %16lx\n", + frame->fill_syndrome); + printk(" EI_STAT reg = %16lx\n", + frame->ei_stat); + printk(" LD_LOCK = %16lx\n", + frame->ld_lock); +} + +static void +mcpcia_print_system_area(unsigned long la_ptr) +{ + struct el_common *frame; + struct pci_controller *hose; + + struct IOD_subpacket { + unsigned long base; + unsigned int whoami; + unsigned int rsvd1; + unsigned int pci_rev; + unsigned int cap_ctrl; + unsigned int hae_mem; + unsigned int hae_io; + unsigned int int_ctl; + unsigned int int_reg; + unsigned int int_mask0; + unsigned int int_mask1; + unsigned int mc_err0; + unsigned int mc_err1; + unsigned int cap_err; + unsigned int rsvd2; + unsigned int pci_err1; + unsigned int mdpa_stat; + unsigned int mdpa_syn; + unsigned int mdpb_stat; + unsigned int mdpb_syn; + unsigned int rsvd3; + unsigned int rsvd4; + unsigned int rsvd5; + } *iodpp; + + frame = (struct el_common *)la_ptr; + iodpp = (struct IOD_subpacket *) (la_ptr + frame->sys_offset); + + for (hose = hose_head; hose; hose = hose->next, iodpp++) { + + printk("IOD %d Register Subpacket - Bridge Base Address %16lx\n", + hose->index, iodpp->base); + printk(" WHOAMI = %8x\n", iodpp->whoami); + printk(" PCI_REV = %8x\n", iodpp->pci_rev); + printk(" CAP_CTRL = %8x\n", iodpp->cap_ctrl); + printk(" HAE_MEM = %8x\n", iodpp->hae_mem); + printk(" HAE_IO = %8x\n", iodpp->hae_io); + printk(" INT_CTL = %8x\n", iodpp->int_ctl); + printk(" INT_REG = %8x\n", iodpp->int_reg); + printk(" INT_MASK0 = %8x\n", iodpp->int_mask0); + printk(" INT_MASK1 = %8x\n", iodpp->int_mask1); + printk(" MC_ERR0 = %8x\n", iodpp->mc_err0); + printk(" MC_ERR1 = %8x\n", iodpp->mc_err1); + printk(" CAP_ERR = %8x\n", iodpp->cap_err); + printk(" PCI_ERR1 = %8x\n", iodpp->pci_err1); + printk(" MDPA_STAT = %8x\n", iodpp->mdpa_stat); + printk(" MDPA_SYN = %8x\n", iodpp->mdpa_syn); + printk(" MDPB_STAT = %8x\n", iodpp->mdpb_stat); + printk(" MDPB_SYN = %8x\n", iodpp->mdpb_syn); + } +} + +void +mcpcia_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + struct el_common *mchk_header; + struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout; + unsigned int cpu = smp_processor_id(); + int expected; + + mchk_header = (struct el_common *)la_ptr; + mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr; + expected = mcheck_expected(cpu); + + mb(); + mb(); /* magic */ + draina(); + + switch (expected) { + case 0: + { + /* FIXME: how do we figure out which hose the + error was on? */ + struct pci_controller *hose; + for (hose = hose_head; hose; hose = hose->next) + mcpcia_pci_clr_err(MCPCIA_HOSE2MID(hose->index)); + break; + } + case 1: + mcpcia_pci_clr_err(mcheck_extra(cpu)); + break; + default: + /* Otherwise, we're being called from mcpcia_probe_hose + and there's no hose clear an error from. */ + break; + } + + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "MCPCIA", expected != 0); + if (!expected && vector != 0x620 && vector != 0x630) { + mcpcia_print_uncorrectable(mchk_logout); + mcpcia_print_system_area(la_ptr); + } +} 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 @@ +/* + * linux/arch/alpha/kernel/core_polaris.c + * + * POLARIS chip-specific code + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_polaris.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/ptrace.h> + +#include "proto.h" +#include "pci_impl.h" + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address. This is fairly straightforward + * on POLARIS, since the chip itself generates Type 0 or Type 1 + * cycles automatically depending on the bus number (Bus 0 is + * hardwired to Type 0, all others are Type 1. Peer bridges + * are not supported). + * + * All types: + * + * 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 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |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| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, u8 *type1) +{ + u8 bus = pbus->number; + + *type1 = (bus == 0) ? 0 : 1; + *pci_addr = (bus << 16) | (device_fn << 8) | (where) | + POLARIS_DENSE_CONFIG_BASE; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " returning address 0x%p\n" + bus, device_fn, where, *pci_addr)); + + return 0; +} + +static int +polaris_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + + +static int +polaris_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops polaris_pci_ops = +{ + .read = polaris_read_config, + .write = polaris_write_config, +}; + +void __init +polaris_init_arch(void) +{ + struct pci_controller *hose; + + /* May need to initialize error reporting (see PCICTL0/1), but + * for now assume that the firmware has done the right thing + * already. + */ +#if 0 + printk("polaris_init_arch(): trusting firmware for setup\n"); +#endif + + /* + * Create our single hose. + */ + + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + hose->sparse_mem_base = 0; + hose->dense_mem_base = POLARIS_DENSE_MEM_BASE - IDENT_ADDR; + hose->sparse_io_base = 0; + hose->dense_io_base = POLARIS_DENSE_IO_BASE - IDENT_ADDR; + + hose->sg_isa = hose->sg_pci = NULL; + + /* The I/O window is fixed at 2G @ 2G. */ + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; +} + +static inline void +polaris_pci_clr_err(void) +{ + *(vusp)POLARIS_W_STATUS; + /* Write 1's to settable bits to clear errors */ + *(vusp)POLARIS_W_STATUS = 0x7800; + mb(); + *(vusp)POLARIS_W_STATUS; +} + +void +polaris_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + /* Clear the error before any reporting. */ + mb(); + mb(); + draina(); + polaris_pci_clr_err(); + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "POLARIS", + mcheck_expected(0)); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_t2.c + * + * Written by Jay A Estabrook (jestabro@amt.tay1.dec.com). + * December 1996. + * + * based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com) + * + * Code common to all T2 core logic chips. + */ + +#define __EXTERN_INLINE +#include <asm/io.h> +#include <asm/core_t2.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/ptrace.h> +#include <asm/delay.h> + +#include "proto.h" +#include "pci_impl.h" + +/* For dumping initial DMA window settings. */ +#define DEBUG_PRINT_INITIAL_SETTINGS 0 + +/* For dumping final DMA window settings. */ +#define DEBUG_PRINT_FINAL_SETTINGS 0 + +/* + * By default, we direct-map starting at 2GB, in order to allow the + * maximum size direct-map window (2GB) to match the maximum amount of + * memory (2GB) that can be present on SABLEs. But that limits the + * floppy to DMA only via the scatter/gather window set up for 8MB + * ISA DMA, since the maximum ISA DMA address is 2GB-1. + * + * For now, this seems a reasonable trade-off: even though most SABLEs + * have less than 1GB of memory, floppy usage/performance will not + * really be affected by forcing it to go via scatter/gather... + */ +#define T2_DIRECTMAP_2G 1 + +#if T2_DIRECTMAP_2G +# define T2_DIRECTMAP_START 0x80000000UL +# define T2_DIRECTMAP_LENGTH 0x80000000UL +#else +# define T2_DIRECTMAP_START 0x40000000UL +# define T2_DIRECTMAP_LENGTH 0x40000000UL +#endif + +/* The ISA scatter/gather window settings. */ +#define T2_ISA_SG_START 0x00800000UL +#define T2_ISA_SG_LENGTH 0x00800000UL + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the i/o controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG(args) printk args +#else +# define DBG(args) +#endif + +static volatile unsigned int t2_mcheck_any_expected; +static volatile unsigned int t2_mcheck_last_taken; + +/* Place to save the DMA Window registers as set up by SRM + for restoration during shutdown. */ +static struct +{ + struct { + unsigned long wbase; + unsigned long wmask; + unsigned long tbase; + } window[2]; + unsigned long hae_1; + unsigned long hae_2; + unsigned long hae_3; + unsigned long hae_4; + unsigned long hbase; +} t2_saved_config __attribute((common)); + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the T2_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |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| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + unsigned long addr; + u8 bus = pbus->number; + + DBG(("mk_conf_addr(bus=%d, dfn=0x%x, where=0x%x," + " addr=0x%lx, type1=0x%x)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + int device = device_fn >> 3; + + /* Type 0 configuration cycle. */ + + if (device > 8) { + DBG(("mk_conf_addr: device (%d)>20, returning -1\n", + device)); + return -1; + } + + *type1 = 0; + addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where); + } else { + /* Type 1 configuration cycle. */ + *type1 = 1; + addr = (bus << 16) | (device_fn << 8) | (where); + } + *pci_addr = addr; + DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +/* + * NOTE: both conf_read() and conf_write() may set HAE_3 when needing + * to do type1 access. This is protected by the use of spinlock IRQ + * primitives in the wrapper functions pci_{read,write}_config_*() + * defined in drivers/pci/pci.c. + */ +static unsigned int +conf_read(unsigned long addr, unsigned char type1) +{ + unsigned int value, cpu, taken; + unsigned long t2_cfg = 0; + + cpu = smp_processor_id(); + + DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + + /* If Type1 access, must set T2 CFG. */ + if (type1) { + t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL; + *(vulp)T2_HAE_3 = 0x40000000UL | t2_cfg; + mb(); + } + mb(); + draina(); + + mcheck_expected(cpu) = 1; + mcheck_taken(cpu) = 0; + t2_mcheck_any_expected |= (1 << cpu); + mb(); + + /* Access configuration space. */ + value = *(vuip)addr; + mb(); + mb(); /* magic */ + + /* Wait for possible mcheck. Also, this lets other CPUs clear + their mchecks as well, as they can reliably tell when + another CPU is in the midst of handling a real mcheck via + the "taken" function. */ + udelay(100); + + if ((taken = mcheck_taken(cpu))) { + mcheck_taken(cpu) = 0; + t2_mcheck_last_taken |= (1 << cpu); + value = 0xffffffffU; + mb(); + } + mcheck_expected(cpu) = 0; + t2_mcheck_any_expected = 0; + mb(); + + /* If Type1 access, must reset T2 CFG so normal IO space ops work. */ + if (type1) { + *(vulp)T2_HAE_3 = t2_cfg; + mb(); + } + + return value; +} + +static void +conf_write(unsigned long addr, unsigned int value, unsigned char type1) +{ + unsigned int cpu, taken; + unsigned long t2_cfg = 0; + + cpu = smp_processor_id(); + + /* If Type1 access, must set T2 CFG. */ + if (type1) { + t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL; + *(vulp)T2_HAE_3 = t2_cfg | 0x40000000UL; + mb(); + } + mb(); + draina(); + + mcheck_expected(cpu) = 1; + mcheck_taken(cpu) = 0; + t2_mcheck_any_expected |= (1 << cpu); + mb(); + + /* Access configuration space. */ + *(vuip)addr = value; + mb(); + mb(); /* magic */ + + /* Wait for possible mcheck. Also, this lets other CPUs clear + their mchecks as well, as they can reliably tell when + this CPU is in the midst of handling a real mcheck via + the "taken" function. */ + udelay(100); + + if ((taken = mcheck_taken(cpu))) { + mcheck_taken(cpu) = 0; + t2_mcheck_last_taken |= (1 << cpu); + mb(); + } + mcheck_expected(cpu) = 0; + t2_mcheck_any_expected = 0; + mb(); + + /* If Type1 access, must reset T2 CFG so normal IO space ops work. */ + if (type1) { + *(vulp)T2_HAE_3 = t2_cfg; + mb(); + } +} + +static int +t2_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr, pci_addr; + unsigned char type1; + int shift; + long mask; + + if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + shift = (where & 3) * 8; + addr = (pci_addr << 5) + mask + T2_CONF; + *value = conf_read(addr, type1) >> (shift); + return PCIBIOS_SUCCESSFUL; +} + +static int +t2_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, + u32 value) +{ + unsigned long addr, pci_addr; + unsigned char type1; + long mask; + + if (mk_conf_addr(bus, devfn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + mask = (size - 1) * 8; + addr = (pci_addr << 5) + mask + T2_CONF; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops t2_pci_ops = +{ + .read = t2_read_config, + .write = t2_write_config, +}; + +static void __init +t2_direct_map_window1(unsigned long base, unsigned long length) +{ + unsigned long temp; + + __direct_map_base = base; + __direct_map_size = length; + + temp = (base & 0xfff00000UL) | ((base + length - 1) >> 20); + *(vulp)T2_WBASE1 = temp | 0x80000UL; /* OR in ENABLE bit */ + temp = (length - 1) & 0xfff00000UL; + *(vulp)T2_WMASK1 = temp; + *(vulp)T2_TBASE1 = 0; + +#if DEBUG_PRINT_FINAL_SETTINGS + printk("%s: setting WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", + __FUNCTION__, + *(vulp)T2_WBASE1, + *(vulp)T2_WMASK1, + *(vulp)T2_TBASE1); +#endif +} + +static void __init +t2_sg_map_window2(struct pci_controller *hose, + unsigned long base, + unsigned long length) +{ + unsigned long temp; + + /* Note we can only do 1 SG window, as the other is for direct, so + do an ISA SG area, especially for the floppy. */ + hose->sg_isa = iommu_arena_new(hose, base, length, 0); + hose->sg_pci = NULL; + + temp = (base & 0xfff00000UL) | ((base + length - 1) >> 20); + *(vulp)T2_WBASE2 = temp | 0xc0000UL; /* OR in ENABLE/SG bits */ + temp = (length - 1) & 0xfff00000UL; + *(vulp)T2_WMASK2 = temp; + *(vulp)T2_TBASE2 = virt_to_phys(hose->sg_isa->ptes) >> 1; + mb(); + + t2_pci_tbi(hose, 0, -1); /* flush TLB all */ + +#if DEBUG_PRINT_FINAL_SETTINGS + printk("%s: setting WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", + __FUNCTION__, + *(vulp)T2_WBASE2, + *(vulp)T2_WMASK2, + *(vulp)T2_TBASE2); +#endif +} + +static void __init +t2_save_configuration(void) +{ +#if DEBUG_PRINT_INITIAL_SETTINGS + printk("%s: HAE_1 was 0x%lx\n", __FUNCTION__, srm_hae); /* HW is 0 */ + printk("%s: HAE_2 was 0x%lx\n", __FUNCTION__, *(vulp)T2_HAE_2); + printk("%s: HAE_3 was 0x%lx\n", __FUNCTION__, *(vulp)T2_HAE_3); + printk("%s: HAE_4 was 0x%lx\n", __FUNCTION__, *(vulp)T2_HAE_4); + printk("%s: HBASE was 0x%lx\n", __FUNCTION__, *(vulp)T2_HBASE); + + printk("%s: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", __FUNCTION__, + *(vulp)T2_WBASE1, *(vulp)T2_WMASK1, *(vulp)T2_TBASE1); + printk("%s: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", __FUNCTION__, + *(vulp)T2_WBASE2, *(vulp)T2_WMASK2, *(vulp)T2_TBASE2); +#endif + + /* + * Save the DMA Window registers. + */ + t2_saved_config.window[0].wbase = *(vulp)T2_WBASE1; + t2_saved_config.window[0].wmask = *(vulp)T2_WMASK1; + t2_saved_config.window[0].tbase = *(vulp)T2_TBASE1; + t2_saved_config.window[1].wbase = *(vulp)T2_WBASE2; + t2_saved_config.window[1].wmask = *(vulp)T2_WMASK2; + t2_saved_config.window[1].tbase = *(vulp)T2_TBASE2; + + t2_saved_config.hae_1 = srm_hae; /* HW is already set to 0 */ + t2_saved_config.hae_2 = *(vulp)T2_HAE_2; + t2_saved_config.hae_3 = *(vulp)T2_HAE_3; + t2_saved_config.hae_4 = *(vulp)T2_HAE_4; + t2_saved_config.hbase = *(vulp)T2_HBASE; +} + +void __init +t2_init_arch(void) +{ + struct pci_controller *hose; + unsigned long temp; + unsigned int i; + + for (i = 0; i < NR_CPUS; i++) { + mcheck_expected(i) = 0; + mcheck_taken(i) = 0; + } + t2_mcheck_any_expected = 0; + t2_mcheck_last_taken = 0; + + /* Enable scatter/gather TLB use. */ + temp = *(vulp)T2_IOCSR; + if (!(temp & (0x1UL << 26))) { + printk("t2_init_arch: enabling SG TLB, IOCSR was 0x%lx\n", + temp); + *(vulp)T2_IOCSR = temp | (0x1UL << 26); + mb(); + *(vulp)T2_IOCSR; /* read it back to make sure */ + } + + t2_save_configuration(); + + /* + * Create our single hose. + */ + pci_isa_hose = hose = alloc_pci_controller(); + hose->io_space = &ioport_resource; + hose->mem_space = &iomem_resource; + hose->index = 0; + + hose->sparse_mem_base = T2_SPARSE_MEM - IDENT_ADDR; + hose->dense_mem_base = T2_DENSE_MEM - IDENT_ADDR; + hose->sparse_io_base = T2_IO - IDENT_ADDR; + hose->dense_io_base = 0; + + /* + * Set up the PCI->physical memory translation windows. + * + * Window 1 is direct mapped. + * Window 2 is scatter/gather (for ISA). + */ + + t2_direct_map_window1(T2_DIRECTMAP_START, T2_DIRECTMAP_LENGTH); + + /* Always make an ISA DMA window. */ + t2_sg_map_window2(hose, T2_ISA_SG_START, T2_ISA_SG_LENGTH); + + *(vulp)T2_HBASE = 0x0; /* Disable HOLES. */ + + /* Zero HAE. */ + *(vulp)T2_HAE_1 = 0; mb(); /* Sparse MEM HAE */ + *(vulp)T2_HAE_2 = 0; mb(); /* Sparse I/O HAE */ + *(vulp)T2_HAE_3 = 0; mb(); /* Config Space HAE */ + + /* + * We also now zero out HAE_4, the dense memory HAE, so that + * we need not account for its "offset" when accessing dense + * memory resources which we allocated in our normal way. This + * HAE would need to stay untouched were we to keep the SRM + * resource settings. + * + * Thus we can now run standard X servers on SABLE/LYNX. :-) + */ + *(vulp)T2_HAE_4 = 0; mb(); +} + +void +t2_kill_arch(int mode) +{ + /* + * Restore the DMA Window registers. + */ + *(vulp)T2_WBASE1 = t2_saved_config.window[0].wbase; + *(vulp)T2_WMASK1 = t2_saved_config.window[0].wmask; + *(vulp)T2_TBASE1 = t2_saved_config.window[0].tbase; + *(vulp)T2_WBASE2 = t2_saved_config.window[1].wbase; + *(vulp)T2_WMASK2 = t2_saved_config.window[1].wmask; + *(vulp)T2_TBASE2 = t2_saved_config.window[1].tbase; + mb(); + + *(vulp)T2_HAE_1 = srm_hae; + *(vulp)T2_HAE_2 = t2_saved_config.hae_2; + *(vulp)T2_HAE_3 = t2_saved_config.hae_3; + *(vulp)T2_HAE_4 = t2_saved_config.hae_4; + *(vulp)T2_HBASE = t2_saved_config.hbase; + mb(); + *(vulp)T2_HBASE; /* READ it back to ensure WRITE occurred. */ +} + +void +t2_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + unsigned long t2_iocsr; + + t2_iocsr = *(vulp)T2_IOCSR; + + /* set the TLB Clear bit */ + *(vulp)T2_IOCSR = t2_iocsr | (0x1UL << 28); + mb(); + *(vulp)T2_IOCSR; /* read it back to make sure */ + + /* clear the TLB Clear bit */ + *(vulp)T2_IOCSR = t2_iocsr & ~(0x1UL << 28); + mb(); + *(vulp)T2_IOCSR; /* read it back to make sure */ +} + +#define SIC_SEIC (1UL << 33) /* System Event Clear */ + +static void +t2_clear_errors(int cpu) +{ + struct sable_cpu_csr *cpu_regs; + + cpu_regs = (struct sable_cpu_csr *)T2_CPUn_BASE(cpu); + + cpu_regs->sic &= ~SIC_SEIC; + + /* Clear CPU errors. */ + cpu_regs->bcce |= cpu_regs->bcce; + cpu_regs->cbe |= cpu_regs->cbe; + cpu_regs->bcue |= cpu_regs->bcue; + cpu_regs->dter |= cpu_regs->dter; + + *(vulp)T2_CERR1 |= *(vulp)T2_CERR1; + *(vulp)T2_PERR1 |= *(vulp)T2_PERR1; + + mb(); + mb(); /* magic */ +} + +/* + * SABLE seems to have a "broadcast" style machine check, in that all + * CPUs receive it. And, the issuing CPU, in the case of PCI Config + * space read/write faults, will also receive a second mcheck, upon + * lowering IPL during completion processing in pci_read_config_byte() + * et al. + * + * Hence all the taken/expected/any_expected/last_taken stuff... + */ +void +t2_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + int cpu = smp_processor_id(); +#ifdef CONFIG_VERBOSE_MCHECK + struct el_common *mchk_header = (struct el_common *)la_ptr; +#endif + + /* Clear the error before any reporting. */ + mb(); + mb(); /* magic */ + draina(); + t2_clear_errors(cpu); + + /* This should not actually be done until the logout frame is + examined, but, since we don't do that, go on and do this... */ + wrmces(0x7); + mb(); + + /* Now, do testing for the anomalous conditions. */ + if (!mcheck_expected(cpu) && t2_mcheck_any_expected) { + /* + * FUNKY: Received mcheck on a CPU and not + * expecting it, but another CPU is expecting one. + * + * Just dismiss it for now on this CPU... + */ +#ifdef CONFIG_VERBOSE_MCHECK + if (alpha_verbose_mcheck > 1) { + printk("t2_machine_check(cpu%d): any_expected 0x%x -" + " (assumed) spurious -" + " code 0x%x\n", cpu, t2_mcheck_any_expected, + (unsigned int)mchk_header->code); + } +#endif + return; + } + + if (!mcheck_expected(cpu) && !t2_mcheck_any_expected) { + if (t2_mcheck_last_taken & (1 << cpu)) { +#ifdef CONFIG_VERBOSE_MCHECK + if (alpha_verbose_mcheck > 1) { + printk("t2_machine_check(cpu%d): last_taken 0x%x - " + "unexpected mcheck - code 0x%x\n", + cpu, t2_mcheck_last_taken, + (unsigned int)mchk_header->code); + } +#endif + t2_mcheck_last_taken = 0; + mb(); + return; + } else { + t2_mcheck_last_taken = 0; + mb(); + } + } + +#ifdef CONFIG_VERBOSE_MCHECK + if (alpha_verbose_mcheck > 1) { + printk("%s t2_mcheck(cpu%d): last_taken 0x%x - " + "any_expected 0x%x - code 0x%x\n", + (mcheck_expected(cpu) ? "EX" : "UN"), cpu, + t2_mcheck_last_taken, t2_mcheck_any_expected, + (unsigned int)mchk_header->code); + } +#endif + + process_mcheck_info(vector, la_ptr, regs, "T2", mcheck_expected(cpu)); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_titan.c + * + * Code common to all TITAN core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_titan.h> +#undef __EXTERN_INLINE + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/bootmem.h> + +#include <asm/ptrace.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/tlbflush.h> + +#include "proto.h" +#include "pci_impl.h" + +/* Save Titan configuration data as the console had it set up. */ + +struct +{ + unsigned long wsba[4]; + unsigned long wsm[4]; + unsigned long tba[4]; +} saved_config[4] __attribute__((common)); + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + + +/* + * Routines to access TIG registers. + */ +static inline volatile unsigned long * +mk_tig_addr(int offset) +{ + return (volatile unsigned long *)(TITAN_TIG_SPACE + (offset << 6)); +} + +static inline u8 +titan_read_tig(int offset, u8 value) +{ + volatile unsigned long *tig_addr = mk_tig_addr(offset); + return (u8)(*tig_addr & 0xff); +} + +static inline void +titan_write_tig(int offset, u8 value) +{ + volatile unsigned long *tig_addr = mk_tig_addr(offset); + *tig_addr = (unsigned long)value; +} + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Note that all config space accesses use Type 1 address format. + * + * Note also that type 1 is determined by non-zero bus number. + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + struct pci_controller *hose = pbus->sysdata; + unsigned long addr; + u8 bus = pbus->number; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (!pbus->parent) /* No parent means peer PCI bus. */ + bus = 0; + *type1 = (bus != 0); + + addr = (bus << 16) | (device_fn << 8) | where; + addr |= hose->config_space_base; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static int +titan_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int +titan_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops titan_pci_ops = +{ + .read = titan_read_config, + .write = titan_write_config, +}; + + +void +titan_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + titan_pachip *pachip = + (hose->index & 1) ? TITAN_pachip1 : TITAN_pachip0; + titan_pachip_port *port; + volatile unsigned long *csr; + unsigned long value; + + /* Get the right hose. */ + port = &pachip->g_port; + if (hose->index & 2) + port = &pachip->a_port; + + /* We can invalidate up to 8 tlb entries in a go. The flush + matches against <31:16> in the pci address. + Note that gtlbi* and atlbi* are in the same place in the g_port + and a_port, respectively, so the g_port offset can be used + even if hose is an a_port */ + csr = &port->port_specific.g.gtlbia.csr; + if (((start ^ end) & 0xffff0000) == 0) + csr = &port->port_specific.g.gtlbiv.csr; + + /* For TBIA, it doesn't matter what value we write. For TBI, + it's the shifted tag bits. */ + value = (start & 0xffff0000) >> 12; + + wmb(); + *csr = value; + mb(); + *csr; +} + +static int +titan_query_agp(titan_pachip_port *port) +{ + union TPAchipPCTL pctl; + + /* set up APCTL */ + pctl.pctl_q_whole = port->pctl.csr; + + return pctl.pctl_r_bits.apctl_v_agp_present; + +} + +static void __init +titan_init_one_pachip_port(titan_pachip_port *port, int index) +{ + struct pci_controller *hose; + + hose = alloc_pci_controller(); + if (index == 0) + pci_isa_hose = hose; + hose->io_space = alloc_resource(); + hose->mem_space = alloc_resource(); + + /* + * This is for userland consumption. The 40-bit PIO bias that we + * use in the kernel through KSEG doesn't work in the page table + * based user mappings. (43-bit KSEG sign extends the physical + * address from bit 40 to hit the I/O bit - mapped addresses don't). + * So make sure we get the 43-bit PIO bias. + */ + hose->sparse_mem_base = 0; + hose->sparse_io_base = 0; + hose->dense_mem_base + = (TITAN_MEM(index) & 0xffffffffffUL) | 0x80000000000UL; + hose->dense_io_base + = (TITAN_IO(index) & 0xffffffffffUL) | 0x80000000000UL; + + hose->config_space_base = TITAN_CONF(index); + hose->index = index; + + hose->io_space->start = TITAN_IO(index) - TITAN_IO_BIAS; + hose->io_space->end = hose->io_space->start + TITAN_IO_SPACE - 1; + hose->io_space->name = pci_io_names[index]; + hose->io_space->flags = IORESOURCE_IO; + + hose->mem_space->start = TITAN_MEM(index) - TITAN_MEM_BIAS; + hose->mem_space->end = hose->mem_space->start + 0xffffffff; + hose->mem_space->name = pci_mem_names[index]; + hose->mem_space->flags = IORESOURCE_MEM; + + if (request_resource(&ioport_resource, hose->io_space) < 0) + printk(KERN_ERR "Failed to request IO on hose %d\n", index); + if (request_resource(&iomem_resource, hose->mem_space) < 0) + printk(KERN_ERR "Failed to request MEM on hose %d\n", index); + + /* + * Save the existing PCI window translations. SRM will + * need them when we go to reboot. + */ + saved_config[index].wsba[0] = port->wsba[0].csr; + saved_config[index].wsm[0] = port->wsm[0].csr; + saved_config[index].tba[0] = port->tba[0].csr; + + saved_config[index].wsba[1] = port->wsba[1].csr; + saved_config[index].wsm[1] = port->wsm[1].csr; + saved_config[index].tba[1] = port->tba[1].csr; + + saved_config[index].wsba[2] = port->wsba[2].csr; + saved_config[index].wsm[2] = port->wsm[2].csr; + saved_config[index].tba[2] = port->tba[2].csr; + + saved_config[index].wsba[3] = port->wsba[3].csr; + saved_config[index].wsm[3] = port->wsm[3].csr; + saved_config[index].tba[3] = port->tba[3].csr; + + /* + * Set up the PCI to main memory translation windows. + * + * Note: Window 3 on Titan is Scatter-Gather ONLY. + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is direct access 1GB at 2GB + * Window 2 is scatter-gather 1GB at 3GB + */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_isa->align_entry = 8; /* 64KB for ISA */ + + hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x40000000, 0); + hose->sg_pci->align_entry = 4; /* Titan caches 4 PTEs at a time */ + + port->wsba[0].csr = hose->sg_isa->dma_base | 3; + port->wsm[0].csr = (hose->sg_isa->size - 1) & 0xfff00000; + port->tba[0].csr = virt_to_phys(hose->sg_isa->ptes); + + port->wsba[1].csr = __direct_map_base | 1; + port->wsm[1].csr = (__direct_map_size - 1) & 0xfff00000; + port->tba[1].csr = 0; + + port->wsba[2].csr = hose->sg_pci->dma_base | 3; + port->wsm[2].csr = (hose->sg_pci->size - 1) & 0xfff00000; + port->tba[2].csr = virt_to_phys(hose->sg_pci->ptes); + + port->wsba[3].csr = 0; + + /* Enable the Monster Window to make DAC pci64 possible. */ + port->pctl.csr |= pctl_m_mwin; + + /* + * If it's an AGP port, initialize agplastwr. + */ + if (titan_query_agp(port)) + port->port_specific.a.agplastwr.csr = __direct_map_base; + + titan_pci_tbi(hose, 0, -1); +} + +static void __init +titan_init_pachips(titan_pachip *pachip0, titan_pachip *pachip1) +{ + int pchip1_present = TITAN_cchip->csc.csr & 1L<<14; + + /* Init the ports in hose order... */ + titan_init_one_pachip_port(&pachip0->g_port, 0); /* hose 0 */ + if (pchip1_present) + titan_init_one_pachip_port(&pachip1->g_port, 1);/* hose 1 */ + titan_init_one_pachip_port(&pachip0->a_port, 2); /* hose 2 */ + if (pchip1_present) + titan_init_one_pachip_port(&pachip1->a_port, 3);/* hose 3 */ +} + +static void __init +titan_init_vga_hose(void) +{ +#ifdef CONFIG_VGA_HOSE + u64 *pu64 = (u64 *)((u64)hwrpb + hwrpb->ctbt_offset); + + if (pu64[7] == 3) { /* TERM_TYPE == graphics */ + struct pci_controller *hose; + int h = (pu64[30] >> 24) & 0xff; /* console hose # */ + + /* + * Our hose numbering matches the console's, so just find + * the right one... + */ + for (hose = hose_head; hose; hose = hose->next) { + if (hose->index == h) break; + } + + if (hose) { + printk("Console graphics on hose %d\n", hose->index); + pci_vga_hose = hose; + } + } +#endif /* CONFIG_VGA_HOSE */ +} + +void __init +titan_init_arch(void) +{ +#if 0 + printk("%s: titan_init_arch()\n", __FUNCTION__); + printk("%s: CChip registers:\n", __FUNCTION__); + printk("%s: CSR_CSC 0x%lx\n", __FUNCTION__, TITAN_cchip->csc.csr); + printk("%s: CSR_MTR 0x%lx\n", __FUNCTION__, TITAN_cchip->mtr.csr); + printk("%s: CSR_MISC 0x%lx\n", __FUNCTION__, TITAN_cchip->misc.csr); + printk("%s: CSR_DIM0 0x%lx\n", __FUNCTION__, TITAN_cchip->dim0.csr); + printk("%s: CSR_DIM1 0x%lx\n", __FUNCTION__, TITAN_cchip->dim1.csr); + printk("%s: CSR_DIR0 0x%lx\n", __FUNCTION__, TITAN_cchip->dir0.csr); + printk("%s: CSR_DIR1 0x%lx\n", __FUNCTION__, TITAN_cchip->dir1.csr); + printk("%s: CSR_DRIR 0x%lx\n", __FUNCTION__, TITAN_cchip->drir.csr); + + printk("%s: DChip registers:\n", __FUNCTION__); + printk("%s: CSR_DSC 0x%lx\n", __FUNCTION__, TITAN_dchip->dsc.csr); + printk("%s: CSR_STR 0x%lx\n", __FUNCTION__, TITAN_dchip->str.csr); + printk("%s: CSR_DREV 0x%lx\n", __FUNCTION__, TITAN_dchip->drev.csr); +#endif + + boot_cpuid = __hard_smp_processor_id(); + + /* With multiple PCI busses, we play with I/O as physical addrs. */ + ioport_resource.end = ~0UL; + + /* PCI DMA Direct Mapping is 1GB at 2GB. */ + __direct_map_base = 0x80000000; + __direct_map_size = 0x40000000; + + /* Init the PA chip(s). */ + titan_init_pachips(TITAN_pachip0, TITAN_pachip1); + + /* Check for graphic console location (if any). */ + titan_init_vga_hose(); +} + +static void +titan_kill_one_pachip_port(titan_pachip_port *port, int index) +{ + port->wsba[0].csr = saved_config[index].wsba[0]; + port->wsm[0].csr = saved_config[index].wsm[0]; + port->tba[0].csr = saved_config[index].tba[0]; + + port->wsba[1].csr = saved_config[index].wsba[1]; + port->wsm[1].csr = saved_config[index].wsm[1]; + port->tba[1].csr = saved_config[index].tba[1]; + + port->wsba[2].csr = saved_config[index].wsba[2]; + port->wsm[2].csr = saved_config[index].wsm[2]; + port->tba[2].csr = saved_config[index].tba[2]; + + port->wsba[3].csr = saved_config[index].wsba[3]; + port->wsm[3].csr = saved_config[index].wsm[3]; + port->tba[3].csr = saved_config[index].tba[3]; +} + +static void +titan_kill_pachips(titan_pachip *pachip0, titan_pachip *pachip1) +{ + int pchip1_present = TITAN_cchip->csc.csr & 1L<<14; + + if (pchip1_present) { + titan_kill_one_pachip_port(&pachip1->g_port, 1); + titan_kill_one_pachip_port(&pachip1->a_port, 3); + } + titan_kill_one_pachip_port(&pachip0->g_port, 0); + titan_kill_one_pachip_port(&pachip0->a_port, 2); +} + +void +titan_kill_arch(int mode) +{ + titan_kill_pachips(TITAN_pachip0, TITAN_pachip1); +} + + +/* + * IO map support. + */ + +void __iomem * +titan_ioremap(unsigned long addr, unsigned long size) +{ + int h = (addr & TITAN_HOSE_MASK) >> TITAN_HOSE_SHIFT; + unsigned long baddr = addr & ~TITAN_HOSE_MASK; + unsigned long last = baddr + size - 1; + struct pci_controller *hose; + struct vm_struct *area; + unsigned long vaddr; + unsigned long *ptes; + unsigned long pfn; + + /* + * Adjust the addr. + */ +#ifdef CONFIG_VGA_HOSE + if (pci_vga_hose && __titan_is_mem_vga(addr)) { + h = pci_vga_hose->index; + addr += pci_vga_hose->mem_space->start; + } +#endif + + /* + * Find the hose. + */ + for (hose = hose_head; hose; hose = hose->next) + if (hose->index == h) + break; + if (!hose) + return NULL; + + /* + * Is it direct-mapped? + */ + if ((baddr >= __direct_map_base) && + ((baddr + size - 1) < __direct_map_base + __direct_map_size)) { + vaddr = addr - __direct_map_base + TITAN_MEM_BIAS; + return (void __iomem *) vaddr; + } + + /* + * Check the scatter-gather arena. + */ + if (hose->sg_pci && + baddr >= (unsigned long)hose->sg_pci->dma_base && + last < (unsigned long)hose->sg_pci->dma_base + hose->sg_pci->size){ + + /* + * Adjust the limits (mappings must be page aligned) + */ + baddr -= hose->sg_pci->dma_base; + last -= hose->sg_pci->dma_base; + baddr &= PAGE_MASK; + size = PAGE_ALIGN(last) - baddr; + + /* + * Map it + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + + ptes = hose->sg_pci->ptes; + for (vaddr = (unsigned long)area->addr; + baddr <= last; + baddr += PAGE_SIZE, vaddr += PAGE_SIZE) { + pfn = ptes[baddr >> PAGE_SHIFT]; + if (!(pfn & 1)) { + printk("ioremap failed... pte not valid...\n"); + vfree(area->addr); + return NULL; + } + pfn >>= 1; /* make it a true pfn */ + + if (__alpha_remap_area_pages(vaddr, + pfn << PAGE_SHIFT, + PAGE_SIZE, 0)) { + printk("FAILED to map...\n"); + vfree(area->addr); + return NULL; + } + } + + flush_tlb_all(); + + vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK); + return (void __iomem *) vaddr; + } + + return NULL; +} + +void +titan_iounmap(volatile void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + if (addr >= VMALLOC_START) + vfree((void *)(PAGE_MASK & addr)); +} + +int +titan_is_mmio(const volatile void __iomem *xaddr) +{ + unsigned long addr = (unsigned long) xaddr; + + if (addr >= VMALLOC_START) + return 1; + else + return (addr & 0x100000000UL) == 0; +} + +#ifndef CONFIG_ALPHA_GENERIC +EXPORT_SYMBOL(titan_ioremap); +EXPORT_SYMBOL(titan_iounmap); +EXPORT_SYMBOL(titan_is_mmio); +#endif + +/* + * AGP GART Support. + */ +#include <linux/agp_backend.h> +#include <asm/agp_backend.h> +#include <linux/slab.h> +#include <linux/delay.h> + +struct titan_agp_aperture { + struct pci_iommu_arena *arena; + long pg_start; + long pg_count; +}; + +static int +titan_agp_setup(alpha_agp_info *agp) +{ + struct titan_agp_aperture *aper; + + if (!alpha_agpgart_size) + return -ENOMEM; + + aper = kmalloc(sizeof(struct titan_agp_aperture), GFP_KERNEL); + if (aper == NULL) + return -ENOMEM; + + aper->arena = agp->hose->sg_pci; + aper->pg_count = alpha_agpgart_size / PAGE_SIZE; + aper->pg_start = iommu_reserve(aper->arena, aper->pg_count, + aper->pg_count - 1); + if (aper->pg_start < 0) { + printk(KERN_ERR "Failed to reserve AGP memory\n"); + kfree(aper); + return -ENOMEM; + } + + agp->aperture.bus_base = + aper->arena->dma_base + aper->pg_start * PAGE_SIZE; + agp->aperture.size = aper->pg_count * PAGE_SIZE; + agp->aperture.sysdata = aper; + + return 0; +} + +static void +titan_agp_cleanup(alpha_agp_info *agp) +{ + struct titan_agp_aperture *aper = agp->aperture.sysdata; + int status; + + status = iommu_release(aper->arena, aper->pg_start, aper->pg_count); + if (status == -EBUSY) { + printk(KERN_WARNING + "Attempted to release bound AGP memory - unbinding\n"); + iommu_unbind(aper->arena, aper->pg_start, aper->pg_count); + status = iommu_release(aper->arena, aper->pg_start, + aper->pg_count); + } + if (status < 0) + printk(KERN_ERR "Failed to release AGP memory\n"); + + kfree(aper); + kfree(agp); +} + +static int +titan_agp_configure(alpha_agp_info *agp) +{ + union TPAchipPCTL pctl; + titan_pachip_port *port = agp->private; + pctl.pctl_q_whole = port->pctl.csr; + + /* Side-Band Addressing? */ + pctl.pctl_r_bits.apctl_v_agp_sba_en = agp->mode.bits.sba; + + /* AGP Rate? */ + pctl.pctl_r_bits.apctl_v_agp_rate = 0; /* 1x */ + if (agp->mode.bits.rate & 2) + pctl.pctl_r_bits.apctl_v_agp_rate = 1; /* 2x */ +#if 0 + if (agp->mode.bits.rate & 4) + pctl.pctl_r_bits.apctl_v_agp_rate = 2; /* 4x */ +#endif + + /* RQ Depth? */ + pctl.pctl_r_bits.apctl_v_agp_hp_rd = 2; + pctl.pctl_r_bits.apctl_v_agp_lp_rd = 7; + + /* + * AGP Enable. + */ + pctl.pctl_r_bits.apctl_v_agp_en = agp->mode.bits.enable; + + /* Tell the user. */ + printk("Enabling AGP: %dX%s\n", + 1 << pctl.pctl_r_bits.apctl_v_agp_rate, + pctl.pctl_r_bits.apctl_v_agp_sba_en ? " - SBA" : ""); + + /* Write it. */ + port->pctl.csr = pctl.pctl_q_whole; + + /* And wait at least 5000 66MHz cycles (per Titan spec). */ + udelay(100); + + return 0; +} + +static int +titan_agp_bind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) +{ + struct titan_agp_aperture *aper = agp->aperture.sysdata; + return iommu_bind(aper->arena, aper->pg_start + pg_start, + mem->page_count, mem->memory); +} + +static int +titan_agp_unbind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem) +{ + struct titan_agp_aperture *aper = agp->aperture.sysdata; + return iommu_unbind(aper->arena, aper->pg_start + pg_start, + mem->page_count); +} + +static unsigned long +titan_agp_translate(alpha_agp_info *agp, dma_addr_t addr) +{ + struct titan_agp_aperture *aper = agp->aperture.sysdata; + unsigned long baddr = addr - aper->arena->dma_base; + unsigned long pte; + + if (addr < agp->aperture.bus_base || + addr >= agp->aperture.bus_base + agp->aperture.size) { + printk("%s: addr out of range\n", __FUNCTION__); + return -EINVAL; + } + + pte = aper->arena->ptes[baddr >> PAGE_SHIFT]; + if (!(pte & 1)) { + printk("%s: pte not valid\n", __FUNCTION__); + return -EINVAL; + } + + return (pte >> 1) << PAGE_SHIFT; +} + +struct alpha_agp_ops titan_agp_ops = +{ + .setup = titan_agp_setup, + .cleanup = titan_agp_cleanup, + .configure = titan_agp_configure, + .bind = titan_agp_bind_memory, + .unbind = titan_agp_unbind_memory, + .translate = titan_agp_translate +}; + +alpha_agp_info * +titan_agp_info(void) +{ + alpha_agp_info *agp; + struct pci_controller *hose; + titan_pachip_port *port; + int hosenum = -1; + union TPAchipPCTL pctl; + + /* + * Find the AGP port. + */ + port = &TITAN_pachip0->a_port; + if (titan_query_agp(port)) + hosenum = 2; + if (hosenum < 0 && + titan_query_agp(port = &TITAN_pachip1->a_port)) + hosenum = 3; + + /* + * Find the hose the port is on. + */ + for (hose = hose_head; hose; hose = hose->next) + if (hose->index == hosenum) + break; + + if (!hose || !hose->sg_pci) + return NULL; + + /* + * Allocate the info structure. + */ + agp = kmalloc(sizeof(*agp), GFP_KERNEL); + + /* + * Fill it in. + */ + agp->hose = hose; + agp->private = port; + agp->ops = &titan_agp_ops; + + /* + * Aperture - not configured until ops.setup(). + * + * FIXME - should we go ahead and allocate it here? + */ + agp->aperture.bus_base = 0; + agp->aperture.size = 0; + agp->aperture.sysdata = NULL; + + /* + * Capabilities. + */ + agp->capability.lw = 0; + agp->capability.bits.rate = 3; /* 2x, 1x */ + agp->capability.bits.sba = 1; + agp->capability.bits.rq = 7; /* 8 - 1 */ + + /* + * Mode. + */ + pctl.pctl_q_whole = port->pctl.csr; + agp->mode.lw = 0; + agp->mode.bits.rate = 1 << pctl.pctl_r_bits.apctl_v_agp_rate; + agp->mode.bits.sba = pctl.pctl_r_bits.apctl_v_agp_sba_en; + agp->mode.bits.rq = 7; /* RQ Depth? */ + agp->mode.bits.enable = pctl.pctl_r_bits.apctl_v_agp_en; + + return agp; +} 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 @@ +/* + * linux/arch/alpha/kernel/core_tsunami.c + * + * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com). + * + * Code common to all TSUNAMI core logic chips. + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_tsunami.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/bootmem.h> + +#include <asm/ptrace.h> +#include <asm/smp.h> + +#include "proto.h" +#include "pci_impl.h" + +/* Save Tsunami configuration data as the console had it set up. */ + +struct +{ + unsigned long wsba[4]; + unsigned long wsm[4]; + unsigned long tba[4]; +} saved_config[2] __attribute__((common)); + +/* + * NOTE: Herein lie back-to-back mb instructions. They are magic. + * One plausible explanation is that the I/O controller does not properly + * handle the system transaction. Another involves timing. Ho hum. + */ + +/* + * BIOS32-style PCI interface: + */ + +#define DEBUG_CONFIG 0 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Note that all config space accesses use Type 1 address format. + * + * Note also that type 1 is determined by non-zero bus number. + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., SCSI and Ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + struct pci_controller *hose = pbus->sysdata; + unsigned long addr; + u8 bus = pbus->number; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (!pbus->parent) /* No parent means peer PCI bus. */ + bus = 0; + *type1 = (bus != 0); + + addr = (bus << 16) | (device_fn << 8) | where; + addr |= hose->config_space_base; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static int +tsunami_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int +tsunami_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops tsunami_pci_ops = +{ + .read = tsunami_read_config, + .write = tsunami_write_config, +}; + +void +tsunami_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + tsunami_pchip *pchip = hose->index ? TSUNAMI_pchip1 : TSUNAMI_pchip0; + volatile unsigned long *csr; + unsigned long value; + + /* We can invalidate up to 8 tlb entries in a go. The flush + matches against <31:16> in the pci address. */ + csr = &pchip->tlbia.csr; + if (((start ^ end) & 0xffff0000) == 0) + csr = &pchip->tlbiv.csr; + + /* For TBIA, it doesn't matter what value we write. For TBI, + it's the shifted tag bits. */ + value = (start & 0xffff0000) >> 12; + + *csr = value; + mb(); + *csr; +} + +#ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI +static long __init +tsunami_probe_read(volatile unsigned long *vaddr) +{ + long dont_care, probe_result; + int cpu = smp_processor_id(); + int s = swpipl(IPL_MCHECK - 1); + + mcheck_taken(cpu) = 0; + mcheck_expected(cpu) = 1; + mb(); + dont_care = *vaddr; + draina(); + mcheck_expected(cpu) = 0; + probe_result = !mcheck_taken(cpu); + mcheck_taken(cpu) = 0; + setipl(s); + + printk("dont_care == 0x%lx\n", dont_care); + + return probe_result; +} + +static long __init +tsunami_probe_write(volatile unsigned long *vaddr) +{ + long true_contents, probe_result = 1; + + TSUNAMI_cchip->misc.csr |= (1L << 28); /* clear NXM... */ + true_contents = *vaddr; + *vaddr = 0; + draina(); + if (TSUNAMI_cchip->misc.csr & (1L << 28)) { + int source = (TSUNAMI_cchip->misc.csr >> 29) & 7; + TSUNAMI_cchip->misc.csr |= (1L << 28); /* ...and unlock NXS. */ + probe_result = 0; + printk("tsunami_probe_write: unit %d at 0x%016lx\n", source, + (unsigned long)vaddr); + } + if (probe_result) + *vaddr = true_contents; + return probe_result; +} +#else +#define tsunami_probe_read(ADDR) 1 +#endif /* NXM_MACHINE_CHECKS_ON_TSUNAMI */ + +#define FN __FUNCTION__ + +static void __init +tsunami_init_one_pchip(tsunami_pchip *pchip, int index) +{ + struct pci_controller *hose; + + if (tsunami_probe_read(&pchip->pctl.csr) == 0) + return; + + hose = alloc_pci_controller(); + if (index == 0) + pci_isa_hose = hose; + hose->io_space = alloc_resource(); + hose->mem_space = alloc_resource(); + + /* This is for userland consumption. For some reason, the 40-bit + PIO bias that we use in the kernel through KSEG didn't work for + the page table based user mappings. So make sure we get the + 43-bit PIO bias. */ + hose->sparse_mem_base = 0; + hose->sparse_io_base = 0; + hose->dense_mem_base + = (TSUNAMI_MEM(index) & 0xffffffffffL) | 0x80000000000L; + hose->dense_io_base + = (TSUNAMI_IO(index) & 0xffffffffffL) | 0x80000000000L; + + hose->config_space_base = TSUNAMI_CONF(index); + hose->index = index; + + hose->io_space->start = TSUNAMI_IO(index) - TSUNAMI_IO_BIAS; + hose->io_space->end = hose->io_space->start + TSUNAMI_IO_SPACE - 1; + hose->io_space->name = pci_io_names[index]; + hose->io_space->flags = IORESOURCE_IO; + + hose->mem_space->start = TSUNAMI_MEM(index) - TSUNAMI_MEM_BIAS; + hose->mem_space->end = hose->mem_space->start + 0xffffffff; + hose->mem_space->name = pci_mem_names[index]; + hose->mem_space->flags = IORESOURCE_MEM; + + if (request_resource(&ioport_resource, hose->io_space) < 0) + printk(KERN_ERR "Failed to request IO on hose %d\n", index); + if (request_resource(&iomem_resource, hose->mem_space) < 0) + printk(KERN_ERR "Failed to request MEM on hose %d\n", index); + + /* + * Save the existing PCI window translations. SRM will + * need them when we go to reboot. + */ + + saved_config[index].wsba[0] = pchip->wsba[0].csr; + saved_config[index].wsm[0] = pchip->wsm[0].csr; + saved_config[index].tba[0] = pchip->tba[0].csr; + + saved_config[index].wsba[1] = pchip->wsba[1].csr; + saved_config[index].wsm[1] = pchip->wsm[1].csr; + saved_config[index].tba[1] = pchip->tba[1].csr; + + saved_config[index].wsba[2] = pchip->wsba[2].csr; + saved_config[index].wsm[2] = pchip->wsm[2].csr; + saved_config[index].tba[2] = pchip->tba[2].csr; + + saved_config[index].wsba[3] = pchip->wsba[3].csr; + saved_config[index].wsm[3] = pchip->wsm[3].csr; + saved_config[index].tba[3] = pchip->tba[3].csr; + + /* + * Set up the PCI to main memory translation windows. + * + * Note: Window 3 is scatter-gather only + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is scatter-gather (up to) 1GB at 1GB + * Window 2 is direct access 2GB at 2GB + * + * NOTE: we need the align_entry settings for Acer devices on ES40, + * specifically floppy and IDE when memory is larger than 2GB. + */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + /* Initially set for 4 PTEs, but will be overridden to 64K for ISA. */ + hose->sg_isa->align_entry = 4; + + hose->sg_pci = iommu_arena_new(hose, 0x40000000, + size_for_memory(0x40000000), 0); + hose->sg_pci->align_entry = 4; /* Tsunami caches 4 PTEs at a time */ + + __direct_map_base = 0x80000000; + __direct_map_size = 0x80000000; + + pchip->wsba[0].csr = hose->sg_isa->dma_base | 3; + pchip->wsm[0].csr = (hose->sg_isa->size - 1) & 0xfff00000; + pchip->tba[0].csr = virt_to_phys(hose->sg_isa->ptes); + + pchip->wsba[1].csr = hose->sg_pci->dma_base | 3; + pchip->wsm[1].csr = (hose->sg_pci->size - 1) & 0xfff00000; + pchip->tba[1].csr = virt_to_phys(hose->sg_pci->ptes); + + pchip->wsba[2].csr = 0x80000000 | 1; + pchip->wsm[2].csr = (0x80000000 - 1) & 0xfff00000; + pchip->tba[2].csr = 0; + + pchip->wsba[3].csr = 0; + + /* Enable the Monster Window to make DAC pci64 possible. */ + pchip->pctl.csr |= pctl_m_mwin; + + tsunami_pci_tbi(hose, 0, -1); +} + +void __init +tsunami_init_arch(void) +{ +#ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI + unsigned long tmp; + + /* Ho hum.. init_arch is called before init_IRQ, but we need to be + able to handle machine checks. So install the handler now. */ + wrent(entInt, 0); + + /* NXMs just don't matter to Tsunami--unless they make it + choke completely. */ + tmp = (unsigned long)(TSUNAMI_cchip - 1); + printk("%s: probing bogus address: 0x%016lx\n", FN, bogus_addr); + printk("\tprobe %s\n", + tsunami_probe_write((unsigned long *)bogus_addr) + ? "succeeded" : "failed"); +#endif /* NXM_MACHINE_CHECKS_ON_TSUNAMI */ + +#if 0 + printk("%s: CChip registers:\n", FN); + printk("%s: CSR_CSC 0x%lx\n", FN, TSUNAMI_cchip->csc.csr); + printk("%s: CSR_MTR 0x%lx\n", FN, TSUNAMI_cchip.mtr.csr); + printk("%s: CSR_MISC 0x%lx\n", FN, TSUNAMI_cchip->misc.csr); + printk("%s: CSR_DIM0 0x%lx\n", FN, TSUNAMI_cchip->dim0.csr); + printk("%s: CSR_DIM1 0x%lx\n", FN, TSUNAMI_cchip->dim1.csr); + printk("%s: CSR_DIR0 0x%lx\n", FN, TSUNAMI_cchip->dir0.csr); + printk("%s: CSR_DIR1 0x%lx\n", FN, TSUNAMI_cchip->dir1.csr); + printk("%s: CSR_DRIR 0x%lx\n", FN, TSUNAMI_cchip->drir.csr); + + printk("%s: DChip registers:\n"); + printk("%s: CSR_DSC 0x%lx\n", FN, TSUNAMI_dchip->dsc.csr); + printk("%s: CSR_STR 0x%lx\n", FN, TSUNAMI_dchip->str.csr); + printk("%s: CSR_DREV 0x%lx\n", FN, TSUNAMI_dchip->drev.csr); +#endif + /* With multiple PCI busses, we play with I/O as physical addrs. */ + ioport_resource.end = ~0UL; + + /* Find how many hoses we have, and initialize them. TSUNAMI + and TYPHOON can have 2, but might only have 1 (DS10). */ + + tsunami_init_one_pchip(TSUNAMI_pchip0, 0); + if (TSUNAMI_cchip->csc.csr & 1L<<14) + tsunami_init_one_pchip(TSUNAMI_pchip1, 1); +} + +static void +tsunami_kill_one_pchip(tsunami_pchip *pchip, int index) +{ + pchip->wsba[0].csr = saved_config[index].wsba[0]; + pchip->wsm[0].csr = saved_config[index].wsm[0]; + pchip->tba[0].csr = saved_config[index].tba[0]; + + pchip->wsba[1].csr = saved_config[index].wsba[1]; + pchip->wsm[1].csr = saved_config[index].wsm[1]; + pchip->tba[1].csr = saved_config[index].tba[1]; + + pchip->wsba[2].csr = saved_config[index].wsba[2]; + pchip->wsm[2].csr = saved_config[index].wsm[2]; + pchip->tba[2].csr = saved_config[index].tba[2]; + + pchip->wsba[3].csr = saved_config[index].wsba[3]; + pchip->wsm[3].csr = saved_config[index].wsm[3]; + pchip->tba[3].csr = saved_config[index].tba[3]; +} + +void +tsunami_kill_arch(int mode) +{ + tsunami_kill_one_pchip(TSUNAMI_pchip0, 0); + if (TSUNAMI_cchip->csc.csr & 1L<<14) + tsunami_kill_one_pchip(TSUNAMI_pchip1, 1); +} + +static inline void +tsunami_pci_clr_err_1(tsunami_pchip *pchip) +{ + pchip->perror.csr; + pchip->perror.csr = 0x040; + mb(); + pchip->perror.csr; +} + +static inline void +tsunami_pci_clr_err(void) +{ + tsunami_pci_clr_err_1(TSUNAMI_pchip0); + + /* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */ + if (TSUNAMI_cchip->csc.csr & 1L<<14) + tsunami_pci_clr_err_1(TSUNAMI_pchip1); +} + +void +tsunami_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + /* Clear error before any reporting. */ + mb(); + mb(); /* magic */ + draina(); + tsunami_pci_clr_err(); + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "TSUNAMI", + mcheck_expected(smp_processor_id())); +} 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 @@ +/* + * linux/arch/alpha/kernel/core_wildfire.c + * + * Wildfire support. + * + * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE + */ + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_wildfire.h> +#undef __EXTERN_INLINE + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/ptrace.h> +#include <asm/smp.h> + +#include "proto.h" +#include "pci_impl.h" + +#define DEBUG_CONFIG 0 +#define DEBUG_DUMP_REGS 0 +#define DEBUG_DUMP_CONFIG 1 + +#if DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +#if DEBUG_DUMP_REGS +static void wildfire_dump_pci_regs(int qbbno, int hoseno); +static void wildfire_dump_pca_regs(int qbbno, int pcano); +static void wildfire_dump_qsa_regs(int qbbno); +static void wildfire_dump_qsd_regs(int qbbno); +static void wildfire_dump_iop_regs(int qbbno); +static void wildfire_dump_gp_regs(int qbbno); +#endif +#if DEBUG_DUMP_CONFIG +static void wildfire_dump_hardware_config(void); +#endif + +unsigned char wildfire_hard_qbb_map[WILDFIRE_MAX_QBB]; +unsigned char wildfire_soft_qbb_map[WILDFIRE_MAX_QBB]; +#define QBB_MAP_EMPTY 0xff + +unsigned long wildfire_hard_qbb_mask; +unsigned long wildfire_soft_qbb_mask; +unsigned long wildfire_gp_mask; +unsigned long wildfire_hs_mask; +unsigned long wildfire_iop_mask; +unsigned long wildfire_ior_mask; +unsigned long wildfire_pca_mask; +unsigned long wildfire_cpu_mask; +unsigned long wildfire_mem_mask; + +void __init +wildfire_init_hose(int qbbno, int hoseno) +{ + struct pci_controller *hose; + wildfire_pci *pci; + + hose = alloc_pci_controller(); + hose->io_space = alloc_resource(); + hose->mem_space = alloc_resource(); + + /* This is for userland consumption. */ + hose->sparse_mem_base = 0; + hose->sparse_io_base = 0; + hose->dense_mem_base = WILDFIRE_MEM(qbbno, hoseno); + hose->dense_io_base = WILDFIRE_IO(qbbno, hoseno); + + hose->config_space_base = WILDFIRE_CONF(qbbno, hoseno); + hose->index = (qbbno << 3) + hoseno; + + hose->io_space->start = WILDFIRE_IO(qbbno, hoseno) - WILDFIRE_IO_BIAS; + hose->io_space->end = hose->io_space->start + WILDFIRE_IO_SPACE - 1; + hose->io_space->name = pci_io_names[hoseno]; + hose->io_space->flags = IORESOURCE_IO; + + hose->mem_space->start = WILDFIRE_MEM(qbbno, hoseno)-WILDFIRE_MEM_BIAS; + hose->mem_space->end = hose->mem_space->start + 0xffffffff; + hose->mem_space->name = pci_mem_names[hoseno]; + hose->mem_space->flags = IORESOURCE_MEM; + + if (request_resource(&ioport_resource, hose->io_space) < 0) + printk(KERN_ERR "Failed to request IO on qbb %d hose %d\n", + qbbno, hoseno); + if (request_resource(&iomem_resource, hose->mem_space) < 0) + printk(KERN_ERR "Failed to request MEM on qbb %d hose %d\n", + qbbno, hoseno); + +#if DEBUG_DUMP_REGS + wildfire_dump_pci_regs(qbbno, hoseno); +#endif + + /* + * Set up the PCI to main memory translation windows. + * + * Note: Window 3 is scatter-gather only + * + * Window 0 is scatter-gather 8MB at 8MB (for isa) + * Window 1 is direct access 1GB at 1GB + * Window 2 is direct access 1GB at 2GB + * Window 3 is scatter-gather 128MB at 3GB + * ??? We ought to scale window 3 memory. + * + */ + hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0); + hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000, 0); + + pci = WILDFIRE_pci(qbbno, hoseno); + + pci->pci_window[0].wbase.csr = hose->sg_isa->dma_base | 3; + pci->pci_window[0].wmask.csr = (hose->sg_isa->size - 1) & 0xfff00000; + pci->pci_window[0].tbase.csr = virt_to_phys(hose->sg_isa->ptes); + + pci->pci_window[1].wbase.csr = 0x40000000 | 1; + pci->pci_window[1].wmask.csr = (0x40000000 -1) & 0xfff00000; + pci->pci_window[1].tbase.csr = 0; + + pci->pci_window[2].wbase.csr = 0x80000000 | 1; + pci->pci_window[2].wmask.csr = (0x40000000 -1) & 0xfff00000; + pci->pci_window[2].tbase.csr = 0x40000000; + + pci->pci_window[3].wbase.csr = hose->sg_pci->dma_base | 3; + pci->pci_window[3].wmask.csr = (hose->sg_pci->size - 1) & 0xfff00000; + pci->pci_window[3].tbase.csr = virt_to_phys(hose->sg_pci->ptes); + + wildfire_pci_tbi(hose, 0, 0); /* Flush TLB at the end. */ +} + +void __init +wildfire_init_pca(int qbbno, int pcano) +{ + + /* Test for PCA existence first. */ + if (!WILDFIRE_PCA_EXISTS(qbbno, pcano)) + return; + +#if DEBUG_DUMP_REGS + wildfire_dump_pca_regs(qbbno, pcano); +#endif + + /* Do both hoses of the PCA. */ + wildfire_init_hose(qbbno, (pcano << 1) + 0); + wildfire_init_hose(qbbno, (pcano << 1) + 1); +} + +void __init +wildfire_init_qbb(int qbbno) +{ + int pcano; + + /* Test for QBB existence first. */ + if (!WILDFIRE_QBB_EXISTS(qbbno)) + return; + +#if DEBUG_DUMP_REGS + wildfire_dump_qsa_regs(qbbno); + wildfire_dump_qsd_regs(qbbno); + wildfire_dump_iop_regs(qbbno); + wildfire_dump_gp_regs(qbbno); +#endif + + /* Init all PCAs here. */ + for (pcano = 0; pcano < WILDFIRE_PCA_PER_QBB; pcano++) { + wildfire_init_pca(qbbno, pcano); + } +} + +void __init +wildfire_hardware_probe(void) +{ + unsigned long temp; + unsigned int hard_qbb, soft_qbb; + wildfire_fast_qsd *fast = WILDFIRE_fast_qsd(); + wildfire_qsd *qsd; + wildfire_qsa *qsa; + wildfire_iop *iop; + wildfire_gp *gp; + wildfire_ne *ne; + wildfire_fe *fe; + int i; + + temp = fast->qsd_whami.csr; +#if 0 + printk(KERN_ERR "fast QSD_WHAMI at base %p is 0x%lx\n", fast, temp); +#endif + + hard_qbb = (temp >> 8) & 7; + soft_qbb = (temp >> 4) & 7; + + /* Init the HW configuration variables. */ + wildfire_hard_qbb_mask = (1 << hard_qbb); + wildfire_soft_qbb_mask = (1 << soft_qbb); + + wildfire_gp_mask = 0; + wildfire_hs_mask = 0; + wildfire_iop_mask = 0; + wildfire_ior_mask = 0; + wildfire_pca_mask = 0; + + wildfire_cpu_mask = 0; + wildfire_mem_mask = 0; + + memset(wildfire_hard_qbb_map, QBB_MAP_EMPTY, WILDFIRE_MAX_QBB); + memset(wildfire_soft_qbb_map, QBB_MAP_EMPTY, WILDFIRE_MAX_QBB); + + /* First, determine which QBBs are present. */ + qsa = WILDFIRE_qsa(soft_qbb); + + temp = qsa->qsa_qbb_id.csr; +#if 0 + printk(KERN_ERR "QSA_QBB_ID at base %p is 0x%lx\n", qsa, temp); +#endif + + if (temp & 0x40) /* Is there an HS? */ + wildfire_hs_mask = 1; + + if (temp & 0x20) { /* Is there a GP? */ + gp = WILDFIRE_gp(soft_qbb); + temp = 0; + for (i = 0; i < 4; i++) { + temp |= gp->gpa_qbb_map[i].csr << (i * 8); +#if 0 + printk(KERN_ERR "GPA_QBB_MAP[%d] at base %p is 0x%lx\n", + i, gp, temp); +#endif + } + + for (hard_qbb = 0; hard_qbb < WILDFIRE_MAX_QBB; hard_qbb++) { + if (temp & 8) { /* Is there a QBB? */ + soft_qbb = temp & 7; + wildfire_hard_qbb_mask |= (1 << hard_qbb); + wildfire_soft_qbb_mask |= (1 << soft_qbb); + } + temp >>= 4; + } + wildfire_gp_mask = wildfire_soft_qbb_mask; + } + + /* Next determine each QBBs resources. */ + for (soft_qbb = 0; soft_qbb < WILDFIRE_MAX_QBB; soft_qbb++) { + if (WILDFIRE_QBB_EXISTS(soft_qbb)) { + qsd = WILDFIRE_qsd(soft_qbb); + temp = qsd->qsd_whami.csr; +#if 0 + printk(KERN_ERR "QSD_WHAMI at base %p is 0x%lx\n", qsd, temp); +#endif + hard_qbb = (temp >> 8) & 7; + wildfire_hard_qbb_map[hard_qbb] = soft_qbb; + wildfire_soft_qbb_map[soft_qbb] = hard_qbb; + + qsa = WILDFIRE_qsa(soft_qbb); + temp = qsa->qsa_qbb_pop[0].csr; +#if 0 + printk(KERN_ERR "QSA_QBB_POP_0 at base %p is 0x%lx\n", qsa, temp); +#endif + wildfire_cpu_mask |= ((temp >> 0) & 0xf) << (soft_qbb << 2); + wildfire_mem_mask |= ((temp >> 4) & 0xf) << (soft_qbb << 2); + + temp = qsa->qsa_qbb_pop[1].csr; +#if 0 + printk(KERN_ERR "QSA_QBB_POP_1 at base %p is 0x%lx\n", qsa, temp); +#endif + wildfire_iop_mask |= (1 << soft_qbb); + wildfire_ior_mask |= ((temp >> 4) & 0xf) << (soft_qbb << 2); + + temp = qsa->qsa_qbb_id.csr; +#if 0 + printk(KERN_ERR "QSA_QBB_ID at %p is 0x%lx\n", qsa, temp); +#endif + if (temp & 0x20) + wildfire_gp_mask |= (1 << soft_qbb); + + /* Probe for PCA existence here. */ + for (i = 0; i < WILDFIRE_PCA_PER_QBB; i++) { + iop = WILDFIRE_iop(soft_qbb); + ne = WILDFIRE_ne(soft_qbb, i); + fe = WILDFIRE_fe(soft_qbb, i); + + if ((iop->iop_hose[i].init.csr & 1) == 1 && + ((ne->ne_what_am_i.csr & 0xf00000300UL) == 0x100000300UL) && + ((fe->fe_what_am_i.csr & 0xf00000300UL) == 0x100000200UL)) + { + wildfire_pca_mask |= 1 << ((soft_qbb << 2) + i); + } + } + + } + } +#if DEBUG_DUMP_CONFIG + wildfire_dump_hardware_config(); +#endif +} + +void __init +wildfire_init_arch(void) +{ + int qbbno; + + /* With multiple PCI buses, we play with I/O as physical addrs. */ + ioport_resource.end = ~0UL; + + + /* Probe the hardware for info about configuration. */ + wildfire_hardware_probe(); + + /* Now init all the found QBBs. */ + for (qbbno = 0; qbbno < WILDFIRE_MAX_QBB; qbbno++) { + wildfire_init_qbb(qbbno); + } + + /* Normal direct PCI DMA mapping. */ + __direct_map_base = 0x40000000UL; + __direct_map_size = 0x80000000UL; +} + +void +wildfire_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + mb(); + mb(); /* magic */ + draina(); + /* FIXME: clear pci errors */ + wrmces(0x7); + mb(); + + process_mcheck_info(vector, la_ptr, regs, "WILDFIRE", + mcheck_expected(smp_processor_id())); +} + +void +wildfire_kill_arch(int mode) +{ +} + +void +wildfire_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end) +{ + int qbbno = hose->index >> 3; + int hoseno = hose->index & 7; + wildfire_pci *pci = WILDFIRE_pci(qbbno, hoseno); + + mb(); + pci->pci_flush_tlb.csr; /* reading does the trick */ +} + +static int +mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where, + unsigned long *pci_addr, unsigned char *type1) +{ + struct pci_controller *hose = pbus->sysdata; + unsigned long addr; + u8 bus = pbus->number; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (!pbus->parent) /* No parent means peer PCI bus. */ + bus = 0; + *type1 = (bus != 0); + + addr = (bus << 16) | (device_fn << 8) | where; + addr |= hose->config_space_base; + + *pci_addr = addr; + DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + +static int +wildfire_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *value = __kernel_ldbu(*(vucp)addr); + break; + case 2: + *value = __kernel_ldwu(*(vusp)addr); + break; + case 4: + *value = *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int +wildfire_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + unsigned long addr; + unsigned char type1; + + if (mk_conf_addr(bus, devfn, where, &addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + __kernel_stb(value, *(vucp)addr); + mb(); + __kernel_ldbu(*(vucp)addr); + break; + case 2: + __kernel_stw(value, *(vusp)addr); + mb(); + __kernel_ldwu(*(vusp)addr); + break; + case 4: + *(vuip)addr = value; + mb(); + *(vuip)addr; + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops wildfire_pci_ops = +{ + .read = wildfire_read_config, + .write = wildfire_write_config, +}; + + +/* + * NUMA Support + */ +int wildfire_pa_to_nid(unsigned long pa) +{ + return pa >> 36; +} + +int wildfire_cpuid_to_nid(int cpuid) +{ + /* assume 4 CPUs per node */ + return cpuid >> 2; +} + +unsigned long wildfire_node_mem_start(int nid) +{ + /* 64GB per node */ + return (unsigned long)nid * (64UL * 1024 * 1024 * 1024); +} + +unsigned long wildfire_node_mem_size(int nid) +{ + /* 64GB per node */ + return 64UL * 1024 * 1024 * 1024; +} + +#if DEBUG_DUMP_REGS + +static void __init +wildfire_dump_pci_regs(int qbbno, int hoseno) +{ + wildfire_pci *pci = WILDFIRE_pci(qbbno, hoseno); + int i; + + printk(KERN_ERR "PCI registers for QBB %d hose %d (%p)\n", + qbbno, hoseno, pci); + + printk(KERN_ERR " PCI_IO_ADDR_EXT: 0x%16lx\n", + pci->pci_io_addr_ext.csr); + printk(KERN_ERR " PCI_CTRL: 0x%16lx\n", pci->pci_ctrl.csr); + printk(KERN_ERR " PCI_ERR_SUM: 0x%16lx\n", pci->pci_err_sum.csr); + printk(KERN_ERR " PCI_ERR_ADDR: 0x%16lx\n", pci->pci_err_addr.csr); + printk(KERN_ERR " PCI_STALL_CNT: 0x%16lx\n", pci->pci_stall_cnt.csr); + printk(KERN_ERR " PCI_PEND_INT: 0x%16lx\n", pci->pci_pend_int.csr); + printk(KERN_ERR " PCI_SENT_INT: 0x%16lx\n", pci->pci_sent_int.csr); + + printk(KERN_ERR " DMA window registers for QBB %d hose %d (%p)\n", + qbbno, hoseno, pci); + for (i = 0; i < 4; i++) { + printk(KERN_ERR " window %d: 0x%16lx 0x%16lx 0x%16lx\n", i, + pci->pci_window[i].wbase.csr, + pci->pci_window[i].wmask.csr, + pci->pci_window[i].tbase.csr); + } + printk(KERN_ERR "\n"); +} + +static void __init +wildfire_dump_pca_regs(int qbbno, int pcano) +{ + wildfire_pca *pca = WILDFIRE_pca(qbbno, pcano); + int i; + + printk(KERN_ERR "PCA registers for QBB %d PCA %d (%p)\n", + qbbno, pcano, pca); + + printk(KERN_ERR " PCA_WHAT_AM_I: 0x%16lx\n", pca->pca_what_am_i.csr); + printk(KERN_ERR " PCA_ERR_SUM: 0x%16lx\n", pca->pca_err_sum.csr); + printk(KERN_ERR " PCA_PEND_INT: 0x%16lx\n", pca->pca_pend_int.csr); + printk(KERN_ERR " PCA_SENT_INT: 0x%16lx\n", pca->pca_sent_int.csr); + printk(KERN_ERR " PCA_STDIO_EL: 0x%16lx\n", + pca->pca_stdio_edge_level.csr); + + printk(KERN_ERR " PCA target registers for QBB %d PCA %d (%p)\n", + qbbno, pcano, pca); + for (i = 0; i < 4; i++) { + printk(KERN_ERR " target %d: 0x%16lx 0x%16lx\n", i, + pca->pca_int[i].target.csr, + pca->pca_int[i].enable.csr); + } + + printk(KERN_ERR "\n"); +} + +static void __init +wildfire_dump_qsa_regs(int qbbno) +{ + wildfire_qsa *qsa = WILDFIRE_qsa(qbbno); + int i; + + printk(KERN_ERR "QSA registers for QBB %d (%p)\n", qbbno, qsa); + + printk(KERN_ERR " QSA_QBB_ID: 0x%16lx\n", qsa->qsa_qbb_id.csr); + printk(KERN_ERR " QSA_PORT_ENA: 0x%16lx\n", qsa->qsa_port_ena.csr); + printk(KERN_ERR " QSA_REF_INT: 0x%16lx\n", qsa->qsa_ref_int.csr); + + for (i = 0; i < 5; i++) + printk(KERN_ERR " QSA_CONFIG_%d: 0x%16lx\n", + i, qsa->qsa_config[i].csr); + + for (i = 0; i < 2; i++) + printk(KERN_ERR " QSA_QBB_POP_%d: 0x%16lx\n", + i, qsa->qsa_qbb_pop[0].csr); + + printk(KERN_ERR "\n"); +} + +static void __init +wildfire_dump_qsd_regs(int qbbno) +{ + wildfire_qsd *qsd = WILDFIRE_qsd(qbbno); + + printk(KERN_ERR "QSD registers for QBB %d (%p)\n", qbbno, qsd); + + printk(KERN_ERR " QSD_WHAMI: 0x%16lx\n", qsd->qsd_whami.csr); + printk(KERN_ERR " QSD_REV: 0x%16lx\n", qsd->qsd_rev.csr); + printk(KERN_ERR " QSD_PORT_PRESENT: 0x%16lx\n", + qsd->qsd_port_present.csr); + printk(KERN_ERR " QSD_PORT_ACTUVE: 0x%16lx\n", + qsd->qsd_port_active.csr); + printk(KERN_ERR " QSD_FAULT_ENA: 0x%16lx\n", + qsd->qsd_fault_ena.csr); + printk(KERN_ERR " QSD_CPU_INT_ENA: 0x%16lx\n", + qsd->qsd_cpu_int_ena.csr); + printk(KERN_ERR " QSD_MEM_CONFIG: 0x%16lx\n", + qsd->qsd_mem_config.csr); + printk(KERN_ERR " QSD_ERR_SUM: 0x%16lx\n", + qsd->qsd_err_sum.csr); + + printk(KERN_ERR "\n"); +} + +static void __init +wildfire_dump_iop_regs(int qbbno) +{ + wildfire_iop *iop = WILDFIRE_iop(qbbno); + int i; + + printk(KERN_ERR "IOP registers for QBB %d (%p)\n", qbbno, iop); + + printk(KERN_ERR " IOA_CONFIG: 0x%16lx\n", iop->ioa_config.csr); + printk(KERN_ERR " IOD_CONFIG: 0x%16lx\n", iop->iod_config.csr); + printk(KERN_ERR " IOP_SWITCH_CREDITS: 0x%16lx\n", + iop->iop_switch_credits.csr); + printk(KERN_ERR " IOP_HOSE_CREDITS: 0x%16lx\n", + iop->iop_hose_credits.csr); + + for (i = 0; i < 4; i++) + printk(KERN_ERR " IOP_HOSE_%d_INIT: 0x%16lx\n", + i, iop->iop_hose[i].init.csr); + for (i = 0; i < 4; i++) + printk(KERN_ERR " IOP_DEV_INT_TARGET_%d: 0x%16lx\n", + i, iop->iop_dev_int[i].target.csr); + + printk(KERN_ERR "\n"); +} + +static void __init +wildfire_dump_gp_regs(int qbbno) +{ + wildfire_gp *gp = WILDFIRE_gp(qbbno); + int i; + + printk(KERN_ERR "GP registers for QBB %d (%p)\n", qbbno, gp); + for (i = 0; i < 4; i++) + printk(KERN_ERR " GPA_QBB_MAP_%d: 0x%16lx\n", + i, gp->gpa_qbb_map[i].csr); + + printk(KERN_ERR " GPA_MEM_POP_MAP: 0x%16lx\n", + gp->gpa_mem_pop_map.csr); + printk(KERN_ERR " GPA_SCRATCH: 0x%16lx\n", gp->gpa_scratch.csr); + printk(KERN_ERR " GPA_DIAG: 0x%16lx\n", gp->gpa_diag.csr); + printk(KERN_ERR " GPA_CONFIG_0: 0x%16lx\n", gp->gpa_config_0.csr); + printk(KERN_ERR " GPA_INIT_ID: 0x%16lx\n", gp->gpa_init_id.csr); + printk(KERN_ERR " GPA_CONFIG_2: 0x%16lx\n", gp->gpa_config_2.csr); + + printk(KERN_ERR "\n"); +} +#endif /* DUMP_REGS */ + +#if DEBUG_DUMP_CONFIG +static void __init +wildfire_dump_hardware_config(void) +{ + int i; + + printk(KERN_ERR "Probed Hardware Configuration\n"); + + printk(KERN_ERR " hard_qbb_mask: 0x%16lx\n", wildfire_hard_qbb_mask); + printk(KERN_ERR " soft_qbb_mask: 0x%16lx\n", wildfire_soft_qbb_mask); + + printk(KERN_ERR " gp_mask: 0x%16lx\n", wildfire_gp_mask); + printk(KERN_ERR " hs_mask: 0x%16lx\n", wildfire_hs_mask); + printk(KERN_ERR " iop_mask: 0x%16lx\n", wildfire_iop_mask); + printk(KERN_ERR " ior_mask: 0x%16lx\n", wildfire_ior_mask); + printk(KERN_ERR " pca_mask: 0x%16lx\n", wildfire_pca_mask); + + printk(KERN_ERR " cpu_mask: 0x%16lx\n", wildfire_cpu_mask); + printk(KERN_ERR " mem_mask: 0x%16lx\n", wildfire_mem_mask); + + printk(" hard_qbb_map: "); + for (i = 0; i < WILDFIRE_MAX_QBB; i++) + if (wildfire_hard_qbb_map[i] == QBB_MAP_EMPTY) + printk("--- "); + else + printk("%3d ", wildfire_hard_qbb_map[i]); + printk("\n"); + + printk(" soft_qbb_map: "); + for (i = 0; i < WILDFIRE_MAX_QBB; i++) + if (wildfire_soft_qbb_map[i] == QBB_MAP_EMPTY) + printk("--- "); + else + printk("%3d ", wildfire_soft_qbb_map[i]); + printk("\n"); +} +#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 @@ +/* + * arch/alpha/kernel/entry.S + * + * Kernel entry-points. + */ + +#include <linux/config.h> +#include <asm/asm_offsets.h> +#include <asm/thread_info.h> +#include <asm/pal.h> +#include <asm/errno.h> +#include <asm/unistd.h> + + .text + .set noat + +/* Stack offsets. */ +#define SP_OFF 184 +#define SWITCH_STACK_SIZE 320 + +/* + * This defines the normal kernel pt-regs layout. + * + * regs 9-15 preserved by C code + * regs 16-18 saved by PAL-code + * regs 29-30 saved and set up by PAL-code + * JRP - Save regs 16-18 in a special area of the stack, so that + * the palcode-provided values are available to the signal handler. + */ + +#define SAVE_ALL \ + subq $sp, SP_OFF, $sp; \ + stq $0, 0($sp); \ + stq $1, 8($sp); \ + stq $2, 16($sp); \ + stq $3, 24($sp); \ + stq $4, 32($sp); \ + stq $28, 144($sp); \ + lda $2, alpha_mv; \ + stq $5, 40($sp); \ + stq $6, 48($sp); \ + stq $7, 56($sp); \ + stq $8, 64($sp); \ + stq $19, 72($sp); \ + stq $20, 80($sp); \ + stq $21, 88($sp); \ + ldq $2, HAE_CACHE($2); \ + stq $22, 96($sp); \ + stq $23, 104($sp); \ + stq $24, 112($sp); \ + stq $25, 120($sp); \ + stq $26, 128($sp); \ + stq $27, 136($sp); \ + stq $2, 152($sp); \ + stq $16, 160($sp); \ + stq $17, 168($sp); \ + stq $18, 176($sp) + +#define RESTORE_ALL \ + lda $19, alpha_mv; \ + ldq $0, 0($sp); \ + ldq $1, 8($sp); \ + ldq $2, 16($sp); \ + ldq $3, 24($sp); \ + ldq $21, 152($sp); \ + ldq $20, HAE_CACHE($19); \ + ldq $4, 32($sp); \ + ldq $5, 40($sp); \ + ldq $6, 48($sp); \ + ldq $7, 56($sp); \ + subq $20, $21, $20; \ + ldq $8, 64($sp); \ + beq $20, 99f; \ + ldq $20, HAE_REG($19); \ + stq $21, HAE_CACHE($19); \ + stq $21, 0($20); \ + ldq $0, 0($sp); \ + ldq $1, 8($sp); \ +99:; \ + ldq $19, 72($sp); \ + ldq $20, 80($sp); \ + ldq $21, 88($sp); \ + ldq $22, 96($sp); \ + ldq $23, 104($sp); \ + ldq $24, 112($sp); \ + ldq $25, 120($sp); \ + ldq $26, 128($sp); \ + ldq $27, 136($sp); \ + ldq $28, 144($sp); \ + addq $sp, SP_OFF, $sp + +/* + * Non-syscall kernel entry points. + */ + + .align 4 + .globl entInt + .ent entInt +entInt: + SAVE_ALL + lda $8, 0x3fff + lda $26, ret_from_sys_call + bic $sp, $8, $8 + mov $sp, $19 + jsr $31, do_entInt +.end entInt + + .align 4 + .globl entArith + .ent entArith +entArith: + SAVE_ALL + lda $8, 0x3fff + lda $26, ret_from_sys_call + bic $sp, $8, $8 + mov $sp, $18 + jsr $31, do_entArith +.end entArith + + .align 4 + .globl entMM + .ent entMM +entMM: + SAVE_ALL +/* save $9 - $15 so the inline exception code can manipulate them. */ + subq $sp, 56, $sp + stq $9, 0($sp) + stq $10, 8($sp) + stq $11, 16($sp) + stq $12, 24($sp) + stq $13, 32($sp) + stq $14, 40($sp) + stq $15, 48($sp) + addq $sp, 56, $19 +/* handle the fault */ + lda $8, 0x3fff + bic $sp, $8, $8 + jsr $26, do_page_fault +/* reload the registers after the exception code played. */ + ldq $9, 0($sp) + ldq $10, 8($sp) + ldq $11, 16($sp) + ldq $12, 24($sp) + ldq $13, 32($sp) + ldq $14, 40($sp) + ldq $15, 48($sp) + addq $sp, 56, $sp +/* finish up the syscall as normal. */ + br ret_from_sys_call +.end entMM + + .align 4 + .globl entIF + .ent entIF +entIF: + SAVE_ALL + lda $8, 0x3fff + lda $26, ret_from_sys_call + bic $sp, $8, $8 + mov $sp, $17 + jsr $31, do_entIF +.end entIF + + .align 4 + .globl entUna + .ent entUna +entUna: + lda $sp, -256($sp) + stq $0, 0($sp) + ldq $0, 256($sp) /* get PS */ + stq $1, 8($sp) + stq $2, 16($sp) + stq $3, 24($sp) + and $0, 8, $0 /* user mode? */ + stq $4, 32($sp) + bne $0, entUnaUser /* yup -> do user-level unaligned fault */ + stq $5, 40($sp) + stq $6, 48($sp) + stq $7, 56($sp) + stq $8, 64($sp) + stq $9, 72($sp) + stq $10, 80($sp) + stq $11, 88($sp) + stq $12, 96($sp) + stq $13, 104($sp) + stq $14, 112($sp) + stq $15, 120($sp) + /* 16-18 PAL-saved */ + stq $19, 152($sp) + stq $20, 160($sp) + stq $21, 168($sp) + stq $22, 176($sp) + stq $23, 184($sp) + stq $24, 192($sp) + stq $25, 200($sp) + stq $26, 208($sp) + stq $27, 216($sp) + stq $28, 224($sp) + stq $gp, 232($sp) + lda $8, 0x3fff + stq $31, 248($sp) + bic $sp, $8, $8 + jsr $26, do_entUna + ldq $0, 0($sp) + ldq $1, 8($sp) + ldq $2, 16($sp) + ldq $3, 24($sp) + ldq $4, 32($sp) + ldq $5, 40($sp) + ldq $6, 48($sp) + ldq $7, 56($sp) + ldq $8, 64($sp) + ldq $9, 72($sp) + ldq $10, 80($sp) + ldq $11, 88($sp) + ldq $12, 96($sp) + ldq $13, 104($sp) + ldq $14, 112($sp) + ldq $15, 120($sp) + /* 16-18 PAL-saved */ + ldq $19, 152($sp) + ldq $20, 160($sp) + ldq $21, 168($sp) + ldq $22, 176($sp) + ldq $23, 184($sp) + ldq $24, 192($sp) + ldq $25, 200($sp) + ldq $26, 208($sp) + ldq $27, 216($sp) + ldq $28, 224($sp) + ldq $gp, 232($sp) + lda $sp, 256($sp) + call_pal PAL_rti +.end entUna + + .align 4 + .ent entUnaUser +entUnaUser: + ldq $0, 0($sp) /* restore original $0 */ + lda $sp, 256($sp) /* pop entUna's stack frame */ + SAVE_ALL /* setup normal kernel stack */ + lda $sp, -56($sp) + stq $9, 0($sp) + stq $10, 8($sp) + stq $11, 16($sp) + stq $12, 24($sp) + stq $13, 32($sp) + stq $14, 40($sp) + stq $15, 48($sp) + lda $8, 0x3fff + addq $sp, 56, $19 + bic $sp, $8, $8 + jsr $26, do_entUnaUser + ldq $9, 0($sp) + ldq $10, 8($sp) + ldq $11, 16($sp) + ldq $12, 24($sp) + ldq $13, 32($sp) + ldq $14, 40($sp) + ldq $15, 48($sp) + lda $sp, 56($sp) + br ret_from_sys_call +.end entUnaUser + + .align 4 + .globl entDbg + .ent entDbg +entDbg: + SAVE_ALL + lda $8, 0x3fff + lda $26, ret_from_sys_call + bic $sp, $8, $8 + mov $sp, $16 + jsr $31, do_entDbg +.end entDbg + +/* + * The system call entry point is special. Most importantly, it looks + * like a function call to userspace as far as clobbered registers. We + * do preserve the argument registers (for syscall restarts) and $26 + * (for leaf syscall functions). + * + * So much for theory. We don't take advantage of this yet. + * + * Note that a0-a2 are not saved by PALcode as with the other entry points. + */ + + .align 4 + .globl entSys + .globl ret_from_sys_call + .ent entSys +entSys: + SAVE_ALL + lda $8, 0x3fff + bic $sp, $8, $8 + lda $4, NR_SYSCALLS($31) + stq $16, SP_OFF+24($sp) + lda $5, sys_call_table + lda $27, sys_ni_syscall + cmpult $0, $4, $4 + ldl $3, TI_FLAGS($8) + stq $17, SP_OFF+32($sp) + s8addq $0, $5, $5 + stq $18, SP_OFF+40($sp) + blbs $3, strace + beq $4, 1f + ldq $27, 0($5) +1: jsr $26, ($27), alpha_ni_syscall + ldgp $gp, 0($26) + blt $0, $syscall_error /* the call failed */ + stq $0, 0($sp) + stq $31, 72($sp) /* a3=0 => no error */ + + .align 4 +ret_from_sys_call: + cmovne $26, 0, $19 /* $19 = 0 => non-restartable */ + ldq $0, SP_OFF($sp) + and $0, 8, $0 + beq $0, restore_all +ret_from_reschedule: + /* Make sure need_resched and sigpending don't change between + sampling and the rti. */ + lda $16, 7 + call_pal PAL_swpipl + ldl $5, TI_FLAGS($8) + and $5, _TIF_WORK_MASK, $2 + bne $5, work_pending +restore_all: + RESTORE_ALL + call_pal PAL_rti + + .align 3 +$syscall_error: + /* + * Some system calls (e.g., ptrace) can return arbitrary + * values which might normally be mistaken as error numbers. + * Those functions must zero $0 (v0) directly in the stack + * frame to indicate that a negative return value wasn't an + * error number.. + */ + ldq $19, 0($sp) /* old syscall nr (zero if success) */ + beq $19, $ret_success + + ldq $20, 72($sp) /* .. and this a3 */ + subq $31, $0, $0 /* with error in v0 */ + addq $31, 1, $1 /* set a3 for errno return */ + stq $0, 0($sp) + mov $31, $26 /* tell "ret_from_sys_call" we can restart */ + stq $1, 72($sp) /* a3 for return */ + br ret_from_sys_call + +$ret_success: + stq $0, 0($sp) + stq $31, 72($sp) /* a3=0 => no error */ + br ret_from_sys_call +.end entSys + +/* + * Do all cleanup when returning from all interrupts and system calls. + * + * Arguments: + * $5: TI_FLAGS. + * $8: current. + * $19: The old syscall number, or zero if this is not a return + * from a syscall that errored and is possibly restartable. + * $20: Error indication. + */ + + .align 4 + .ent work_pending +work_pending: + and $5, _TIF_NEED_RESCHED, $2 + beq $2, $work_notifysig + +$work_resched: + subq $sp, 16, $sp + stq $19, 0($sp) /* save syscall nr */ + stq $20, 8($sp) /* and error indication (a3) */ + jsr $26, schedule + ldq $19, 0($sp) + ldq $20, 8($sp) + addq $sp, 16, $sp + /* Make sure need_resched and sigpending don't change between + sampling and the rti. */ + lda $16, 7 + call_pal PAL_swpipl + ldl $5, TI_FLAGS($8) + and $5, _TIF_WORK_MASK, $2 + beq $2, restore_all + and $5, _TIF_NEED_RESCHED, $2 + bne $2, $work_resched + +$work_notifysig: + mov $sp, $17 + br $1, do_switch_stack + mov $5, $21 + mov $sp, $18 + mov $31, $16 + jsr $26, do_notify_resume + bsr $1, undo_switch_stack + br restore_all +.end work_pending + +/* + * PTRACE syscall handler + */ + + .align 4 + .ent strace +strace: + /* set up signal stack, call syscall_trace */ + bsr $1, do_switch_stack + jsr $26, syscall_trace + bsr $1, undo_switch_stack + + /* get the system call number and the arguments back.. */ + ldq $0, 0($sp) + ldq $16, SP_OFF+24($sp) + ldq $17, SP_OFF+32($sp) + ldq $18, SP_OFF+40($sp) + ldq $19, 72($sp) + ldq $20, 80($sp) + ldq $21, 88($sp) + + /* get the system call pointer.. */ + lda $1, NR_SYSCALLS($31) + lda $2, sys_call_table + lda $27, alpha_ni_syscall + cmpult $0, $1, $1 + s8addq $0, $2, $2 + beq $1, 1f + ldq $27, 0($2) +1: jsr $26, ($27), sys_gettimeofday + ldgp $gp, 0($26) + + /* check return.. */ + blt $0, $strace_error /* the call failed */ + stq $31, 72($sp) /* a3=0 => no error */ +$strace_success: + stq $0, 0($sp) /* save return value */ + + bsr $1, do_switch_stack + jsr $26, syscall_trace + bsr $1, undo_switch_stack + br $31, ret_from_sys_call + + .align 3 +$strace_error: + ldq $19, 0($sp) /* old syscall nr (zero if success) */ + beq $19, $strace_success + ldq $20, 72($sp) /* .. and this a3 */ + + subq $31, $0, $0 /* with error in v0 */ + addq $31, 1, $1 /* set a3 for errno return */ + stq $0, 0($sp) + stq $1, 72($sp) /* a3 for return */ + + bsr $1, do_switch_stack + mov $19, $9 /* save old syscall number */ + mov $20, $10 /* save old a3 */ + jsr $26, syscall_trace + mov $9, $19 + mov $10, $20 + bsr $1, undo_switch_stack + + mov $31, $26 /* tell "ret_from_sys_call" we can restart */ + br ret_from_sys_call +.end strace + +/* + * Save and restore the switch stack -- aka the balance of the user context. + */ + + .align 4 + .ent do_switch_stack +do_switch_stack: + lda $sp, -SWITCH_STACK_SIZE($sp) + stq $9, 0($sp) + stq $10, 8($sp) + stq $11, 16($sp) + stq $12, 24($sp) + stq $13, 32($sp) + stq $14, 40($sp) + stq $15, 48($sp) + stq $26, 56($sp) + stt $f0, 64($sp) + stt $f1, 72($sp) + stt $f2, 80($sp) + stt $f3, 88($sp) + stt $f4, 96($sp) + stt $f5, 104($sp) + stt $f6, 112($sp) + stt $f7, 120($sp) + stt $f8, 128($sp) + stt $f9, 136($sp) + stt $f10, 144($sp) + stt $f11, 152($sp) + stt $f12, 160($sp) + stt $f13, 168($sp) + stt $f14, 176($sp) + stt $f15, 184($sp) + stt $f16, 192($sp) + stt $f17, 200($sp) + stt $f18, 208($sp) + stt $f19, 216($sp) + stt $f20, 224($sp) + stt $f21, 232($sp) + stt $f22, 240($sp) + stt $f23, 248($sp) + stt $f24, 256($sp) + stt $f25, 264($sp) + stt $f26, 272($sp) + stt $f27, 280($sp) + mf_fpcr $f0 # get fpcr + stt $f28, 288($sp) + stt $f29, 296($sp) + stt $f30, 304($sp) + stt $f0, 312($sp) # save fpcr in slot of $f31 + ldt $f0, 64($sp) # dont let "do_switch_stack" change fp state. + ret $31, ($1), 1 +.end do_switch_stack + + .align 4 + .ent undo_switch_stack +undo_switch_stack: + ldq $9, 0($sp) + ldq $10, 8($sp) + ldq $11, 16($sp) + ldq $12, 24($sp) + ldq $13, 32($sp) + ldq $14, 40($sp) + ldq $15, 48($sp) + ldq $26, 56($sp) + ldt $f30, 312($sp) # get saved fpcr + ldt $f0, 64($sp) + ldt $f1, 72($sp) + ldt $f2, 80($sp) + ldt $f3, 88($sp) + mt_fpcr $f30 # install saved fpcr + ldt $f4, 96($sp) + ldt $f5, 104($sp) + ldt $f6, 112($sp) + ldt $f7, 120($sp) + ldt $f8, 128($sp) + ldt $f9, 136($sp) + ldt $f10, 144($sp) + ldt $f11, 152($sp) + ldt $f12, 160($sp) + ldt $f13, 168($sp) + ldt $f14, 176($sp) + ldt $f15, 184($sp) + ldt $f16, 192($sp) + ldt $f17, 200($sp) + ldt $f18, 208($sp) + ldt $f19, 216($sp) + ldt $f20, 224($sp) + ldt $f21, 232($sp) + ldt $f22, 240($sp) + ldt $f23, 248($sp) + ldt $f24, 256($sp) + ldt $f25, 264($sp) + ldt $f26, 272($sp) + ldt $f27, 280($sp) + ldt $f28, 288($sp) + ldt $f29, 296($sp) + ldt $f30, 304($sp) + lda $sp, SWITCH_STACK_SIZE($sp) + ret $31, ($1), 1 +.end undo_switch_stack + +/* + * The meat of the context switch code. + */ + + .align 4 + .globl alpha_switch_to + .ent alpha_switch_to +alpha_switch_to: + .prologue 0 + bsr $1, do_switch_stack + call_pal PAL_swpctx + lda $8, 0x3fff + bsr $1, undo_switch_stack + bic $sp, $8, $8 + mov $17, $0 + ret +.end alpha_switch_to + +/* + * New processes begin life here. + */ + + .globl ret_from_fork + .align 4 + .ent ret_from_fork +ret_from_fork: + lda $26, ret_from_sys_call + mov $17, $16 + jmp $31, schedule_tail +.end ret_from_fork + +/* + * kernel_thread(fn, arg, clone_flags) + */ + .align 4 + .globl kernel_thread + .ent kernel_thread +kernel_thread: + /* We can be called from a module. */ + ldgp $gp, 0($27) + .prologue 1 + subq $sp, SP_OFF+6*8, $sp + br $1, 2f /* load start address */ + + /* We've now "returned" from a fake system call. */ + unop + blt $0, 1f /* error? */ + ldi $1, 0x3fff + beq $20, 1f /* parent or child? */ + + bic $sp, $1, $8 /* in child. */ + jsr $26, ($27) + ldgp $gp, 0($26) + mov $0, $16 + mov $31, $26 + jmp $31, sys_exit + +1: ret /* in parent. */ + + .align 4 +2: /* Fake a system call stack frame, as we can't do system calls + from kernel space. Note that we store FN and ARG as they + need to be set up in the child for the call. Also store $8 + and $26 for use in the parent. */ + stq $31, SP_OFF($sp) /* ps */ + stq $1, SP_OFF+8($sp) /* pc */ + stq $gp, SP_OFF+16($sp) /* gp */ + stq $16, 136($sp) /* $27; FN for child */ + stq $17, SP_OFF+24($sp) /* $16; ARG for child */ + stq $8, 64($sp) /* $8 */ + stq $26, 128($sp) /* $26 */ + /* Avoid the HAE being gratuitously wrong, to avoid restoring it. */ + ldq $2, alpha_mv+HAE_CACHE + stq $2, 152($sp) /* HAE */ + + /* Shuffle FLAGS to the front; add CLONE_VM. */ + ldi $1, CLONE_VM|CLONE_UNTRACED + or $18, $1, $16 + bsr $26, sys_clone + + /* We don't actually care for a3 success widgetry in the kernel. + Not for positive errno values. */ + stq $0, 0($sp) /* $0 */ + br restore_all +.end kernel_thread + +/* + * execve(path, argv, envp) + */ + .align 4 + .globl execve + .ent execve +execve: + /* We can be called from a module. */ + ldgp $gp, 0($27) + lda $sp, -(32+SIZEOF_PT_REGS+8)($sp) + .frame $sp, 32+SIZEOF_PT_REGS+8, $26, 0 + stq $26, 0($sp) + stq $16, 8($sp) + stq $17, 16($sp) + stq $18, 24($sp) + .prologue 1 + + lda $16, 32($sp) + lda $17, 0 + lda $18, SIZEOF_PT_REGS + bsr $26, memset !samegp + + /* Avoid the HAE being gratuitously wrong, which would cause us + to do the whole turn off interrupts thing and restore it. */ + ldq $2, alpha_mv+HAE_CACHE + stq $2, 152+32($sp) + + ldq $16, 8($sp) + ldq $17, 16($sp) + ldq $18, 24($sp) + lda $19, 32($sp) + bsr $26, do_execve !samegp + + ldq $26, 0($sp) + bne $0, 1f /* error! */ + + /* Move the temporary pt_regs struct from its current location + to the top of the kernel stack frame. See copy_thread for + details for a normal process. */ + lda $16, 0x4000 - SIZEOF_PT_REGS($8) + lda $17, 32($sp) + lda $18, SIZEOF_PT_REGS + bsr $26, memmove !samegp + + /* Take that over as our new stack frame and visit userland! */ + lda $sp, 0x4000 - SIZEOF_PT_REGS($8) + br $31, ret_from_sys_call + +1: lda $sp, 32+SIZEOF_PT_REGS+8($sp) + ret +.end execve + + +/* + * Special system calls. Most of these are special in that they either + * have to play switch_stack games or in some way use the pt_regs struct. + */ + .align 4 + .globl sys_fork + .ent sys_fork +sys_fork: + .prologue 0 + mov $sp, $21 + bsr $1, do_switch_stack + bis $31, SIGCHLD, $16 + mov $31, $17 + mov $31, $18 + mov $31, $19 + mov $31, $20 + jsr $26, alpha_clone + bsr $1, undo_switch_stack + ret +.end sys_fork + + .align 4 + .globl sys_clone + .ent sys_clone +sys_clone: + .prologue 0 + mov $sp, $21 + bsr $1, do_switch_stack + /* $16, $17, $18, $19, $20 come from the user. */ + jsr $26, alpha_clone + bsr $1, undo_switch_stack + ret +.end sys_clone + + .align 4 + .globl sys_vfork + .ent sys_vfork +sys_vfork: + .prologue 0 + mov $sp, $16 + bsr $1, do_switch_stack + jsr $26, alpha_vfork + bsr $1, undo_switch_stack + ret +.end sys_vfork + + .align 4 + .globl sys_sigreturn + .ent sys_sigreturn +sys_sigreturn: + .prologue 0 + mov $sp, $17 + lda $18, -SWITCH_STACK_SIZE($sp) + lda $sp, -SWITCH_STACK_SIZE($sp) + jsr $26, do_sigreturn + br $1, undo_switch_stack + br ret_from_sys_call +.end sys_sigreturn + + .align 4 + .globl sys_rt_sigreturn + .ent sys_rt_sigreturn +sys_rt_sigreturn: + .prologue 0 + mov $sp, $17 + lda $18, -SWITCH_STACK_SIZE($sp) + lda $sp, -SWITCH_STACK_SIZE($sp) + jsr $26, do_rt_sigreturn + br $1, undo_switch_stack + br ret_from_sys_call +.end sys_rt_sigreturn + + .align 4 + .globl sys_sigsuspend + .ent sys_sigsuspend +sys_sigsuspend: + .prologue 0 + mov $sp, $17 + br $1, do_switch_stack + mov $sp, $18 + subq $sp, 16, $sp + stq $26, 0($sp) + jsr $26, do_sigsuspend + ldq $26, 0($sp) + lda $sp, SWITCH_STACK_SIZE+16($sp) + ret +.end sys_sigsuspend + + .align 4 + .globl sys_rt_sigsuspend + .ent sys_rt_sigsuspend +sys_rt_sigsuspend: + .prologue 0 + mov $sp, $18 + br $1, do_switch_stack + mov $sp, $19 + subq $sp, 16, $sp + stq $26, 0($sp) + jsr $26, do_rt_sigsuspend + ldq $26, 0($sp) + lda $sp, SWITCH_STACK_SIZE+16($sp) + ret +.end sys_rt_sigsuspend + + .align 4 + .globl sys_sethae + .ent sys_sethae +sys_sethae: + .prologue 0 + stq $16, 152($sp) + ret +.end sys_sethae + + .align 4 + .globl osf_getpriority + .ent osf_getpriority +osf_getpriority: + lda $sp, -16($sp) + stq $26, 0($sp) + .prologue 0 + + jsr $26, sys_getpriority + + ldq $26, 0($sp) + blt $0, 1f + + /* Return value is the unbiased priority, i.e. 20 - prio. + This does result in negative return values, so signal + no error by writing into the R0 slot. */ + lda $1, 20 + stq $31, 16($sp) + subl $1, $0, $0 + unop + +1: lda $sp, 16($sp) + ret +.end osf_getpriority + + .align 4 + .globl sys_getxuid + .ent sys_getxuid +sys_getxuid: + .prologue 0 + ldq $2, TI_TASK($8) + ldl $0, TASK_UID($2) + ldl $1, TASK_EUID($2) + stq $1, 80($sp) + ret +.end sys_getxuid + + .align 4 + .globl sys_getxgid + .ent sys_getxgid +sys_getxgid: + .prologue 0 + ldq $2, TI_TASK($8) + ldl $0, TASK_GID($2) + ldl $1, TASK_EGID($2) + stq $1, 80($sp) + ret +.end sys_getxgid + + .align 4 + .globl sys_getxpid + .ent sys_getxpid +sys_getxpid: + .prologue 0 + ldq $2, TI_TASK($8) + + /* See linux/kernel/timer.c sys_getppid for discussion + about this loop. */ + ldq $3, TASK_REAL_PARENT($2) +1: ldl $1, TASK_TGID($3) +#ifdef CONFIG_SMP + mov $3, $4 + mb + ldq $3, TASK_REAL_PARENT($2) + cmpeq $3, $4, $4 + beq $4, 1b +#endif + stq $1, 80($sp) + ldl $0, TASK_TGID($2) + ret +.end sys_getxpid + + .align 4 + .globl sys_pipe + .ent sys_pipe +sys_pipe: + lda $sp, -16($sp) + stq $26, 0($sp) + .prologue 0 + + lda $16, 8($sp) + jsr $26, do_pipe + + ldq $26, 0($sp) + bne $0, 1f + + /* The return values are in $0 and $20. */ + ldl $1, 12($sp) + ldl $0, 8($sp) + + stq $1, 80+16($sp) +1: lda $sp, 16($sp) + ret +.end sys_pipe + + .align 4 + .globl sys_ptrace + .ent sys_ptrace +sys_ptrace: + .prologue 0 + mov $sp, $20 + jmp $31, do_sys_ptrace +.end sys_ptrace + + .align 4 + .globl sys_execve + .ent sys_execve +sys_execve: + .prologue 0 + mov $sp, $19 + jmp $31, do_sys_execve +.end sys_execve + + .align 4 + .globl osf_sigprocmask + .ent osf_sigprocmask +osf_sigprocmask: + .prologue 0 + mov $sp, $18 + jmp $31, do_osf_sigprocmask +.end osf_sigprocmask + + .align 4 + .globl alpha_ni_syscall + .ent alpha_ni_syscall +alpha_ni_syscall: + .prologue 0 + /* Special because it also implements overflow handling via + syscall number 0. And if you recall, zero is a special + trigger for "not an error". Store large non-zero there. */ + lda $0, -ENOSYS + unop + stq $0, 0($sp) + ret +.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 @@ +/* + * linux/arch/alpha/kernel/err_common.c + * + * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) + * + * Error handling code supporting Alpha systems + */ + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/io.h> +#include <asm/hwrpb.h> +#include <asm/smp.h> +#include <asm/err_common.h> + +#include "err_impl.h" +#include "proto.h" + +/* + * err_print_prefix -- error handling print routines should prefix + * all prints with this + */ +char *err_print_prefix = KERN_NOTICE; + + +/* + * Generic + */ +void +mchk_dump_mem(void *data, size_t length, char **annotation) +{ + unsigned long *ldata = data; + size_t i; + + for (i = 0; (i * sizeof(*ldata)) < length; i++) { + if (annotation && !annotation[i]) + annotation = NULL; + printk("%s %08x: %016lx %s\n", + err_print_prefix, + (unsigned)(i * sizeof(*ldata)), ldata[i], + annotation ? annotation[i] : ""); + } +} + +void +mchk_dump_logout_frame(struct el_common *mchk_header) +{ + printk("%s -- Frame Header --\n" + " Frame Size: %d (0x%x) bytes\n" + " Flags: %s%s\n" + " MCHK Code: 0x%x\n" + " Frame Rev: %d\n" + " Proc Offset: 0x%08x\n" + " Sys Offset: 0x%08x\n" + " -- Processor Region --\n", + err_print_prefix, + mchk_header->size, mchk_header->size, + mchk_header->retry ? "RETRY " : "", + mchk_header->err2 ? "SECOND_ERR " : "", + mchk_header->code, + mchk_header->frame_rev, + mchk_header->proc_offset, + mchk_header->sys_offset); + + mchk_dump_mem((void *) + ((unsigned long)mchk_header + mchk_header->proc_offset), + mchk_header->sys_offset - mchk_header->proc_offset, + NULL); + + printk("%s -- System Region --\n", err_print_prefix); + mchk_dump_mem((void *) + ((unsigned long)mchk_header + mchk_header->sys_offset), + mchk_header->size - mchk_header->sys_offset, + NULL); + printk("%s -- End of Frame --\n", err_print_prefix); +} + + +/* + * Console Data Log + */ +/* Data */ +static struct el_subpacket_handler *subpacket_handler_list = NULL; +static struct el_subpacket_annotation *subpacket_annotation_list = NULL; + +static struct el_subpacket * +el_process_header_subpacket(struct el_subpacket *header) +{ + union el_timestamp timestamp; + char *name = "UNKNOWN EVENT"; + int packet_count = 0; + int length = 0; + + if (header->class != EL_CLASS__HEADER) { + printk("%s** Unexpected header CLASS %d TYPE %d, aborting\n", + err_print_prefix, + header->class, header->type); + return NULL; + } + + switch(header->type) { + case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME: + name = "SYSTEM ERROR"; + length = header->by_type.sys_err.frame_length; + packet_count = + header->by_type.sys_err.frame_packet_count; + timestamp.as_int = 0; + break; + case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME: + name = "SYSTEM EVENT"; + length = header->by_type.sys_event.frame_length; + packet_count = + header->by_type.sys_event.frame_packet_count; + timestamp = header->by_type.sys_event.timestamp; + break; + case EL_TYPE__HEADER__HALT_FRAME: + name = "ERROR HALT"; + length = header->by_type.err_halt.frame_length; + packet_count = + header->by_type.err_halt.frame_packet_count; + timestamp = header->by_type.err_halt.timestamp; + break; + case EL_TYPE__HEADER__LOGOUT_FRAME: + name = "LOGOUT FRAME"; + length = header->by_type.logout_header.frame_length; + packet_count = 1; + timestamp.as_int = 0; + break; + default: /* Unknown */ + printk("%s** Unknown header - CLASS %d TYPE %d, aborting\n", + err_print_prefix, + header->class, header->type); + return NULL; + } + + printk("%s*** %s:\n" + " CLASS %d, TYPE %d\n", + err_print_prefix, + name, + header->class, header->type); + el_print_timestamp(×tamp); + + /* + * Process the subpackets + */ + el_process_subpackets(header, packet_count); + + /* return the next header */ + header = (struct el_subpacket *) + ((unsigned long)header + header->length + length); + return header; +} + +static struct el_subpacket * +el_process_subpacket_reg(struct el_subpacket *header) +{ + struct el_subpacket *next = NULL; + struct el_subpacket_handler *h = subpacket_handler_list; + + for (; h && h->class != header->class; h = h->next); + if (h) next = h->handler(header); + + return next; +} + +void +el_print_timestamp(union el_timestamp *timestamp) +{ + if (timestamp->as_int) + printk("%s TIMESTAMP: %d/%d/%02d %d:%02d:%0d\n", + err_print_prefix, + timestamp->b.month, timestamp->b.day, + timestamp->b.year, timestamp->b.hour, + timestamp->b.minute, timestamp->b.second); +} + +void +el_process_subpackets(struct el_subpacket *header, int packet_count) +{ + struct el_subpacket *subpacket; + int i; + + subpacket = (struct el_subpacket *) + ((unsigned long)header + header->length); + + for (i = 0; subpacket && i < packet_count; i++) { + printk("%sPROCESSING SUBPACKET %d\n", err_print_prefix, i); + subpacket = el_process_subpacket(subpacket); + } +} + +struct el_subpacket * +el_process_subpacket(struct el_subpacket *header) +{ + struct el_subpacket *next = NULL; + + switch(header->class) { + case EL_CLASS__TERMINATION: + /* Termination packet, there are no more */ + break; + case EL_CLASS__HEADER: + next = el_process_header_subpacket(header); + break; + default: + if (NULL == (next = el_process_subpacket_reg(header))) { + printk("%s** Unexpected header CLASS %d TYPE %d" + " -- aborting.\n", + err_print_prefix, + header->class, header->type); + } + break; + } + + return next; +} + +void +el_annotate_subpacket(struct el_subpacket *header) +{ + struct el_subpacket_annotation *a; + char **annotation = NULL; + + for (a = subpacket_annotation_list; a; a = a->next) { + if (a->class == header->class && + a->type == header->type && + a->revision == header->revision) { + /* + * We found the annotation + */ + annotation = a->annotation; + printk("%s %s\n", err_print_prefix, a->description); + break; + } + } + + mchk_dump_mem(header, header->length, annotation); +} + +static void __init +cdl_process_console_data_log(int cpu, struct percpu_struct *pcpu) +{ + struct el_subpacket *header = (struct el_subpacket *) + (IDENT_ADDR | pcpu->console_data_log_pa); + int err; + + printk("%s******* CONSOLE DATA LOG FOR CPU %d. *******\n" + "*** Error(s) were logged on a previous boot\n", + err_print_prefix, cpu); + + for (err = 0; header && (header->class != EL_CLASS__TERMINATION); err++) + header = el_process_subpacket(header); + + /* let the console know it's ok to clear the error(s) at restart */ + pcpu->console_data_log_pa = 0; + + printk("%s*** %d total error(s) logged\n" + "**** END OF CONSOLE DATA LOG FOR CPU %d ****\n", + err_print_prefix, err, cpu); +} + +void __init +cdl_check_console_data_log(void) +{ + struct percpu_struct *pcpu; + unsigned long cpu; + + for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) { + pcpu = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset + + cpu * hwrpb->processor_size); + if (pcpu->console_data_log_pa) + cdl_process_console_data_log(cpu, pcpu); + } + +} + +int __init +cdl_register_subpacket_annotation(struct el_subpacket_annotation *new) +{ + struct el_subpacket_annotation *a = subpacket_annotation_list; + + if (a == NULL) subpacket_annotation_list = new; + else { + for (; a->next != NULL; a = a->next) { + if ((a->class == new->class && a->type == new->type) || + a == new) { + printk("Attempted to re-register " + "subpacket annotation\n"); + return -EINVAL; + } + } + a->next = new; + } + new->next = NULL; + + return 0; +} + +int __init +cdl_register_subpacket_handler(struct el_subpacket_handler *new) +{ + struct el_subpacket_handler *h = subpacket_handler_list; + + if (h == NULL) subpacket_handler_list = new; + else { + for (; h->next != NULL; h = h->next) { + if (h->class == new->class || h == new) { + printk("Attempted to re-register " + "subpacket handler\n"); + return -EINVAL; + } + } + h->next = new; + } + new->next = NULL; + + return 0; +} + 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 @@ +/* + * linux/arch/alpha/kernel/err_ev6.c + * + * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) + * + * Error handling code supporting Alpha systems + */ + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/io.h> +#include <asm/hwrpb.h> +#include <asm/smp.h> +#include <asm/err_common.h> +#include <asm/err_ev6.h> + +#include "err_impl.h" +#include "proto.h" + +static int +ev6_parse_ibox(u64 i_stat, int print) +{ + int status = MCHK_DISPOSITION_REPORT; + +#define EV6__I_STAT__PAR (1UL << 29) +#define EV6__I_STAT__ERRMASK (EV6__I_STAT__PAR) + + if (!(i_stat & EV6__I_STAT__ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + + if (!print) + return status; + + if (i_stat & EV6__I_STAT__PAR) + printk("%s Icache parity error\n", err_print_prefix); + + return status; +} + +static int +ev6_parse_mbox(u64 mm_stat, u64 d_stat, u64 c_stat, int print) +{ + int status = MCHK_DISPOSITION_REPORT; + +#define EV6__MM_STAT__DC_TAG_PERR (1UL << 10) +#define EV6__MM_STAT__ERRMASK (EV6__MM_STAT__DC_TAG_PERR) +#define EV6__D_STAT__TPERR_P0 (1UL << 0) +#define EV6__D_STAT__TPERR_P1 (1UL << 1) +#define EV6__D_STAT__ECC_ERR_ST (1UL << 2) +#define EV6__D_STAT__ECC_ERR_LD (1UL << 3) +#define EV6__D_STAT__SEO (1UL << 4) +#define EV6__D_STAT__ERRMASK (EV6__D_STAT__TPERR_P0 | \ + EV6__D_STAT__TPERR_P1 | \ + EV6__D_STAT__ECC_ERR_ST | \ + EV6__D_STAT__ECC_ERR_LD | \ + EV6__D_STAT__SEO) + + if (!(d_stat & EV6__D_STAT__ERRMASK) && + !(mm_stat & EV6__MM_STAT__ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + + if (!print) + return status; + + if (mm_stat & EV6__MM_STAT__DC_TAG_PERR) + printk("%s Dcache tag parity error on probe\n", + err_print_prefix); + if (d_stat & EV6__D_STAT__TPERR_P0) + printk("%s Dcache tag parity error - pipe 0\n", + err_print_prefix); + if (d_stat & EV6__D_STAT__TPERR_P1) + printk("%s Dcache tag parity error - pipe 1\n", + err_print_prefix); + if (d_stat & EV6__D_STAT__ECC_ERR_ST) + printk("%s ECC error occurred on a store\n", + err_print_prefix); + if (d_stat & EV6__D_STAT__ECC_ERR_LD) + printk("%s ECC error occurred on a %s load\n", + err_print_prefix, + c_stat ? "" : "speculative "); + if (d_stat & EV6__D_STAT__SEO) + printk("%s Dcache second error\n", err_print_prefix); + + return status; +} + +static int +ev6_parse_cbox(u64 c_addr, u64 c1_syn, u64 c2_syn, + u64 c_stat, u64 c_sts, int print) +{ + char *sourcename[] = { "UNKNOWN", "UNKNOWN", "UNKNOWN", + "MEMORY", "BCACHE", "DCACHE", + "BCACHE PROBE", "BCACHE PROBE" }; + char *streamname[] = { "D", "I" }; + char *bitsname[] = { "SINGLE", "DOUBLE" }; + int status = MCHK_DISPOSITION_REPORT; + int source = -1, stream = -1, bits = -1; + +#define EV6__C_STAT__BC_PERR (0x01) +#define EV6__C_STAT__DC_PERR (0x02) +#define EV6__C_STAT__DSTREAM_MEM_ERR (0x03) +#define EV6__C_STAT__DSTREAM_BC_ERR (0x04) +#define EV6__C_STAT__DSTREAM_DC_ERR (0x05) +#define EV6__C_STAT__PROBE_BC_ERR0 (0x06) /* both 6 and 7 indicate... */ +#define EV6__C_STAT__PROBE_BC_ERR1 (0x07) /* ...probe bc error. */ +#define EV6__C_STAT__ISTREAM_MEM_ERR (0x0B) +#define EV6__C_STAT__ISTREAM_BC_ERR (0x0C) +#define EV6__C_STAT__DSTREAM_MEM_DBL (0x13) +#define EV6__C_STAT__DSTREAM_BC_DBL (0x14) +#define EV6__C_STAT__ISTREAM_MEM_DBL (0x1B) +#define EV6__C_STAT__ISTREAM_BC_DBL (0x1C) +#define EV6__C_STAT__SOURCE_MEMORY (0x03) +#define EV6__C_STAT__SOURCE_BCACHE (0x04) +#define EV6__C_STAT__SOURCE__S (0) +#define EV6__C_STAT__SOURCE__M (0x07) +#define EV6__C_STAT__ISTREAM__S (3) +#define EV6__C_STAT__ISTREAM__M (0x01) +#define EV6__C_STAT__DOUBLE__S (4) +#define EV6__C_STAT__DOUBLE__M (0x01) +#define EV6__C_STAT__ERRMASK (0x1F) +#define EV6__C_STS__SHARED (1 << 0) +#define EV6__C_STS__DIRTY (1 << 1) +#define EV6__C_STS__VALID (1 << 2) +#define EV6__C_STS__PARITY (1 << 3) + + if (!(c_stat & EV6__C_STAT__ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + + if (!print) + return status; + + source = EXTRACT(c_stat, EV6__C_STAT__SOURCE); + stream = EXTRACT(c_stat, EV6__C_STAT__ISTREAM); + bits = EXTRACT(c_stat, EV6__C_STAT__DOUBLE); + + if (c_stat & EV6__C_STAT__BC_PERR) { + printk("%s Bcache tag parity error\n", err_print_prefix); + source = -1; + } + + if (c_stat & EV6__C_STAT__DC_PERR) { + printk("%s Dcache tag parity error\n", err_print_prefix); + source = -1; + } + + if (c_stat == EV6__C_STAT__PROBE_BC_ERR0 || + c_stat == EV6__C_STAT__PROBE_BC_ERR1) { + printk("%s Bcache single-bit error on a probe hit\n", + err_print_prefix); + source = -1; + } + + if (source != -1) + printk("%s %s-STREAM %s-BIT ECC error from %s\n", + err_print_prefix, + streamname[stream], bitsname[bits], sourcename[source]); + + printk("%s Address: 0x%016lx\n" + " Syndrome[upper.lower]: %02lx.%02lx\n", + err_print_prefix, + c_addr, + c2_syn, c1_syn); + + if (source == EV6__C_STAT__SOURCE_MEMORY || + source == EV6__C_STAT__SOURCE_BCACHE) + printk("%s Block status: %s%s%s%s\n", + err_print_prefix, + (c_sts & EV6__C_STS__SHARED) ? "SHARED " : "", + (c_sts & EV6__C_STS__DIRTY) ? "DIRTY " : "", + (c_sts & EV6__C_STS__VALID) ? "VALID " : "", + (c_sts & EV6__C_STS__PARITY) ? "PARITY " : ""); + + return status; +} + +void +ev6_register_error_handlers(void) +{ + /* None right now. */ +} + +int +ev6_process_logout_frame(struct el_common *mchk_header, int print) +{ + struct el_common_EV6_mcheck *ev6mchk = + (struct el_common_EV6_mcheck *)mchk_header; + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + + status |= ev6_parse_ibox(ev6mchk->I_STAT, print); + status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT, + ev6mchk->C_STAT, print); + status |= ev6_parse_cbox(ev6mchk->C_ADDR, ev6mchk->DC1_SYNDROME, + ev6mchk->DC0_SYNDROME, ev6mchk->C_STAT, + ev6mchk->C_STS, print); + + if (!print) + return status; + + if (status != MCHK_DISPOSITION_DISMISS) { + char *saved_err_prefix = err_print_prefix; + + /* + * Dump some additional information from the frame + */ + printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx" + " ISUM: 0x%016lx\n" + " PAL_BASE: 0x%016lx I_CTL: 0x%016lx" + " PCTX: 0x%016lx\n", + err_print_prefix, + ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM, + ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX); + + if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) { + printk("%s UNKNOWN error, frame follows:\n", + err_print_prefix); + } else { + /* had decode -- downgrade print level for frame */ + err_print_prefix = KERN_NOTICE; + } + + mchk_dump_logout_frame(mchk_header); + + err_print_prefix = saved_err_prefix; + } + + return status; +} + +void +ev6_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) +{ + struct el_common *mchk_header = (struct el_common *)la_ptr; + + /* + * Sync the processor + */ + mb(); + draina(); + + /* + * Parse the logout frame without printing first. If the only error(s) + * found are have a disposition of "dismiss", then just dismiss them + * and don't print any message + */ + if (ev6_process_logout_frame(mchk_header, 0) != + MCHK_DISPOSITION_DISMISS) { + char *saved_err_prefix = err_print_prefix; + err_print_prefix = KERN_CRIT; + + /* + * Either a nondismissable error was detected or no + * recognized error was detected in the logout frame + * -- report the error in either case + */ + printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d:\n", + err_print_prefix, + (vector == SCB_Q_PROCERR)?"Correctable":"Uncorrectable", + (unsigned int)vector, (int)smp_processor_id()); + + ev6_process_logout_frame(mchk_header, 1); + dik_show_regs(regs, NULL); + + err_print_prefix = saved_err_prefix; + } + + /* + * Release the logout frame + */ + wrmces(0x7); + mb(); +} + 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 @@ +/* + * linux/arch/alpha/kernel/err_ev7.c + * + * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) + * + * Error handling code supporting Alpha systems + */ + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/io.h> +#include <asm/hwrpb.h> +#include <asm/smp.h> +#include <asm/err_common.h> +#include <asm/err_ev7.h> + +#include "err_impl.h" +#include "proto.h" + +struct ev7_lf_subpackets * +ev7_collect_logout_frame_subpackets(struct el_subpacket *el_ptr, + struct ev7_lf_subpackets *lf_subpackets) +{ + struct el_subpacket *subpacket; + int i; + + /* + * A Marvel machine check frame is always packaged in an + * el_subpacket of class HEADER, type LOGOUT_FRAME. + */ + if (el_ptr->class != EL_CLASS__HEADER || + el_ptr->type != EL_TYPE__HEADER__LOGOUT_FRAME) + return NULL; + + /* + * It is a logout frame header. Look at the one subpacket. + */ + el_ptr = (struct el_subpacket *) + ((unsigned long)el_ptr + el_ptr->length); + + /* + * It has to be class PAL, type LOGOUT_FRAME. + */ + if (el_ptr->class != EL_CLASS__PAL || + el_ptr->type != EL_TYPE__PAL__LOGOUT_FRAME) + return NULL; + + lf_subpackets->logout = (struct ev7_pal_logout_subpacket *) + el_ptr->by_type.raw.data_start; + + /* + * Process the subpackets. + */ + subpacket = (struct el_subpacket *) + ((unsigned long)el_ptr + el_ptr->length); + for (i = 0; + subpacket && i < lf_subpackets->logout->subpacket_count; + subpacket = (struct el_subpacket *) + ((unsigned long)subpacket + subpacket->length), i++) { + /* + * All subpackets should be class PAL. + */ + if (subpacket->class != EL_CLASS__PAL) { + printk("%s**UNEXPECTED SUBPACKET CLASS %d " + "IN LOGOUT FRAME (packet %d\n", + err_print_prefix, subpacket->class, i); + return NULL; + } + + /* + * Remember the subpacket. + */ + switch(subpacket->type) { + case EL_TYPE__PAL__EV7_PROCESSOR: + lf_subpackets->ev7 = + (struct ev7_pal_processor_subpacket *) + subpacket->by_type.raw.data_start; + break; + + case EL_TYPE__PAL__EV7_RBOX: + lf_subpackets->rbox = (struct ev7_pal_rbox_subpacket *) + subpacket->by_type.raw.data_start; + break; + + case EL_TYPE__PAL__EV7_ZBOX: + lf_subpackets->zbox = (struct ev7_pal_zbox_subpacket *) + subpacket->by_type.raw.data_start; + break; + + case EL_TYPE__PAL__EV7_IO: + lf_subpackets->io = (struct ev7_pal_io_subpacket *) + subpacket->by_type.raw.data_start; + break; + + case EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE: + case EL_TYPE__PAL__ENV__AIRMOVER_FAN: + case EL_TYPE__PAL__ENV__VOLTAGE: + case EL_TYPE__PAL__ENV__INTRUSION: + case EL_TYPE__PAL__ENV__POWER_SUPPLY: + case EL_TYPE__PAL__ENV__LAN: + case EL_TYPE__PAL__ENV__HOT_PLUG: + lf_subpackets->env[ev7_lf_env_index(subpacket->type)] = + (struct ev7_pal_environmental_subpacket *) + subpacket->by_type.raw.data_start; + break; + + default: + /* + * Don't know what kind of frame this is. + */ + return NULL; + } + } + + return lf_subpackets; +} + +void +ev7_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) +{ + struct el_subpacket *el_ptr = (struct el_subpacket *)la_ptr; + char *saved_err_prefix = err_print_prefix; + + /* + * Sync the processor + */ + mb(); + draina(); + + err_print_prefix = KERN_CRIT; + printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d\n", + err_print_prefix, + (vector == SCB_Q_PROCERR) ? "Correctable" : "Uncorrectable", + (unsigned int)vector, (int)smp_processor_id()); + el_process_subpacket(el_ptr); + err_print_prefix = saved_err_prefix; + + /* + * Release the logout frame + */ + wrmces(0x7); + mb(); +} + +static char *el_ev7_processor_subpacket_annotation[] = { + "Subpacket Header", "I_STAT", "DC_STAT", + "C_ADDR", "C_SYNDROME_1", "C_SYNDROME_0", + "C_STAT", "C_STS", "MM_STAT", + "EXC_ADDR", "IER_CM", "ISUM", + "PAL_BASE", "I_CTL", "PROCESS_CONTEXT", + "CBOX_CTL", "CBOX_STP_CTL", "CBOX_ACC_CTL", + "CBOX_LCL_SET", "CBOX_GLB_SET", "BBOX_CTL", + "BBOX_ERR_STS", "BBOX_ERR_IDX", "CBOX_DDP_ERR_STS", + "BBOX_DAT_RMP", NULL +}; + +static char *el_ev7_zbox_subpacket_annotation[] = { + "Subpacket Header", + "ZBOX(0): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1", + "ZBOX(0): DRAM_ERROR_CTL / DRAM_ERR_STATUS_3", + "ZBOX(0): DIFT_TIMEOUT / DRAM_ERR_ADR", + "ZBOX(0): FRC_ERR_ADR / DRAM_MAPPER_CTL", + "ZBOX(0): reserved / DIFT_ERR_STATUS", + "ZBOX(1): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1", + "ZBOX(1): DRAM_ERROR_CTL / DRAM_ERR_STATUS_3", + "ZBOX(1): DIFT_TIMEOUT / DRAM_ERR_ADR", + "ZBOX(1): FRC_ERR_ADR / DRAM_MAPPER_CTL", + "ZBOX(1): reserved / DIFT_ERR_STATUS", + "CBOX_CTL", "CBOX_STP_CTL", + "ZBOX(0)_ERROR_PA", "ZBOX(1)_ERROR_PA", + "ZBOX(0)_ORED_SYNDROME","ZBOX(1)_ORED_SYNDROME", + NULL +}; + +static char *el_ev7_rbox_subpacket_annotation[] = { + "Subpacket Header", "RBOX_CFG", "RBOX_N_CFG", + "RBOX_S_CFG", "RBOX_E_CFG", "RBOX_W_CFG", + "RBOX_N_ERR", "RBOX_S_ERR", "RBOX_E_ERR", + "RBOX_W_ERR", "RBOX_IO_CFG", "RBOX_IO_ERR", + "RBOX_L_ERR", "RBOX_WHOAMI", "RBOX_IMASL", + "RBOX_INTQ", "RBOX_INT", NULL +}; + +static char *el_ev7_io_subpacket_annotation[] = { + "Subpacket Header", "IO_ASIC_REV", "IO_SYS_REV", + "IO7_UPH", "HPI_CTL", "CRD_CTL", + "HEI_CTL", "PO7_ERROR_SUM","PO7_UNCRR_SYM", + "PO7_CRRCT_SYM", "PO7_UGBGE_SYM","PO7_ERR_PKT0", + "PO7_ERR_PKT1", "reserved", "reserved", + "PO0_ERR_SUM", "PO0_TLB_ERR", "PO0_SPL_COMPLT", + "PO0_TRANS_SUM", "PO0_FIRST_ERR","PO0_MULT_ERR", + "DM CSR PH", "DM CSR PH", "DM CSR PH", + "DM CSR PH", "reserved", + "PO1_ERR_SUM", "PO1_TLB_ERR", "PO1_SPL_COMPLT", + "PO1_TRANS_SUM", "PO1_FIRST_ERR","PO1_MULT_ERR", + "DM CSR PH", "DM CSR PH", "DM CSR PH", + "DM CSR PH", "reserved", + "PO2_ERR_SUM", "PO2_TLB_ERR", "PO2_SPL_COMPLT", + "PO2_TRANS_SUM", "PO2_FIRST_ERR","PO2_MULT_ERR", + "DM CSR PH", "DM CSR PH", "DM CSR PH", + "DM CSR PH", "reserved", + "PO3_ERR_SUM", "PO3_TLB_ERR", "PO3_SPL_COMPLT", + "PO3_TRANS_SUM", "PO3_FIRST_ERR","PO3_MULT_ERR", + "DM CSR PH", "DM CSR PH", "DM CSR PH", + "DM CSR PH", "reserved", + NULL +}; + +static struct el_subpacket_annotation el_ev7_pal_annotations[] = { + SUBPACKET_ANNOTATION(EL_CLASS__PAL, + EL_TYPE__PAL__EV7_PROCESSOR, + 1, + "EV7 Processor Subpacket", + el_ev7_processor_subpacket_annotation), + SUBPACKET_ANNOTATION(EL_CLASS__PAL, + EL_TYPE__PAL__EV7_ZBOX, + 1, + "EV7 ZBOX Subpacket", + el_ev7_zbox_subpacket_annotation), + SUBPACKET_ANNOTATION(EL_CLASS__PAL, + EL_TYPE__PAL__EV7_RBOX, + 1, + "EV7 RBOX Subpacket", + el_ev7_rbox_subpacket_annotation), + SUBPACKET_ANNOTATION(EL_CLASS__PAL, + EL_TYPE__PAL__EV7_IO, + 1, + "EV7 IO Subpacket", + el_ev7_io_subpacket_annotation) +}; + +static struct el_subpacket * +ev7_process_pal_subpacket(struct el_subpacket *header) +{ + struct ev7_pal_subpacket *packet; + + if (header->class != EL_CLASS__PAL) { + printk("%s ** Unexpected header CLASS %d TYPE %d, aborting\n", + err_print_prefix, + header->class, header->type); + return NULL; + } + + packet = (struct ev7_pal_subpacket *)header->by_type.raw.data_start; + + switch(header->type) { + case EL_TYPE__PAL__LOGOUT_FRAME: + printk("%s*** MCHK occurred on LPID %ld (RBOX %lx)\n", + err_print_prefix, + packet->by_type.logout.whami, + packet->by_type.logout.rbox_whami); + el_print_timestamp(&packet->by_type.logout.timestamp); + printk("%s EXC_ADDR: %016lx\n" + " HALT_CODE: %lx\n", + err_print_prefix, + packet->by_type.logout.exc_addr, + packet->by_type.logout.halt_code); + el_process_subpackets(header, + packet->by_type.logout.subpacket_count); + break; + default: + printk("%s ** PAL TYPE %d SUBPACKET\n", + err_print_prefix, + header->type); + el_annotate_subpacket(header); + break; + } + + return (struct el_subpacket *)((unsigned long)header + header->length); +} + +struct el_subpacket_handler ev7_pal_subpacket_handler = + SUBPACKET_HANDLER_INIT(EL_CLASS__PAL, ev7_process_pal_subpacket); + +void +ev7_register_error_handlers(void) +{ + int i; + + for(i = 0; + i<sizeof(el_ev7_pal_annotations)/sizeof(el_ev7_pal_annotations[1]); + i++) { + cdl_register_subpacket_annotation(&el_ev7_pal_annotations[i]); + } + cdl_register_subpacket_handler(&ev7_pal_subpacket_handler); +} + 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 @@ +/* + * linux/arch/alpha/kernel/err_impl.h + * + * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) + * + * Contains declarations and macros to support Alpha error handling + * implementations. + */ + +union el_timestamp; +struct el_subpacket; +struct ev7_lf_subpackets; + +struct el_subpacket_annotation { + struct el_subpacket_annotation *next; + u16 class; + u16 type; + u16 revision; + char *description; + char **annotation; +}; +#define SUBPACKET_ANNOTATION(c, t, r, d, a) {NULL, (c), (t), (r), (d), (a)} + +struct el_subpacket_handler { + struct el_subpacket_handler *next; + u16 class; + struct el_subpacket *(*handler)(struct el_subpacket *); +}; +#define SUBPACKET_HANDLER_INIT(c, h) {NULL, (c), (h)} + +/* + * Manipulate a field from a register given it's name. defines + * for the LSB (__S - shift count) and bitmask (__M) are required + * + * EXTRACT(u, f) - extracts the field and places it at bit position 0 + * GEN_MASK(f) - creates an in-position mask for the field + */ +#define EXTRACT(u, f) (((u) >> f##__S) & f##__M) +#define GEN_MASK(f) ((u64)f##__M << f##__S) + +/* + * err_common.c + */ +extern char *err_print_prefix; + +extern void mchk_dump_mem(void *, size_t, char **); +extern void mchk_dump_logout_frame(struct el_common *); +extern void el_print_timestamp(union el_timestamp *); +extern void el_process_subpackets(struct el_subpacket *, int); +extern struct el_subpacket *el_process_subpacket(struct el_subpacket *); +extern void el_annotate_subpacket(struct el_subpacket *); +extern void cdl_check_console_data_log(void); +extern int cdl_register_subpacket_annotation(struct el_subpacket_annotation *); +extern int cdl_register_subpacket_handler(struct el_subpacket_handler *); + +/* + * err_ev7.c + */ +extern struct ev7_lf_subpackets * +ev7_collect_logout_frame_subpackets(struct el_subpacket *, + struct ev7_lf_subpackets *); +extern void ev7_register_error_handlers(void); +extern void ev7_machine_check(u64, u64, struct pt_regs *); + +/* + * err_ev6.c + */ +extern void ev6_register_error_handlers(void); +extern int ev6_process_logout_frame(struct el_common *, int); +extern void ev6_machine_check(u64, u64, struct pt_regs *); + +/* + * err_marvel.c + */ +extern void marvel_machine_check(u64, u64, struct pt_regs *); +extern void marvel_register_error_handlers(void); + +/* + * err_titan.c + */ +extern int titan_process_logout_frame(struct el_common *, int); +extern void titan_machine_check(u64, u64, struct pt_regs *); +extern void titan_register_error_handlers(void); +extern int privateer_process_logout_frame(struct el_common *, int); +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 @@ +/* + * linux/arch/alpha/kernel/err_marvel.c + * + * Copyright (C) 2001 Jeff Wiedemeier (Compaq Computer Corporation) + * + */ + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/io.h> +#include <asm/console.h> +#include <asm/core_marvel.h> +#include <asm/hwrpb.h> +#include <asm/smp.h> +#include <asm/err_common.h> +#include <asm/err_ev7.h> + +#include "err_impl.h" +#include "proto.h" + +static void +marvel_print_680_frame(struct ev7_lf_subpackets *lf_subpackets) +{ +#ifdef CONFIG_VERBOSE_MCHECK + struct ev7_pal_environmental_subpacket *env; + struct { int type; char *name; } ev_packets[] = { + { EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE, + "Ambient Temperature" }, + { EL_TYPE__PAL__ENV__AIRMOVER_FAN, + "AirMover / Fan" }, + { EL_TYPE__PAL__ENV__VOLTAGE, + "Voltage" }, + { EL_TYPE__PAL__ENV__INTRUSION, + "Intrusion" }, + { EL_TYPE__PAL__ENV__POWER_SUPPLY, + "Power Supply" }, + { EL_TYPE__PAL__ENV__LAN, + "LAN" }, + { EL_TYPE__PAL__ENV__HOT_PLUG, + "Hot Plug" }, + { 0, NULL } + }; + int i; + + for (i = 0; ev_packets[i].type != 0; i++) { + env = lf_subpackets->env[ev7_lf_env_index(ev_packets[i].type)]; + if (!env) + continue; + + printk("%s**%s event (cabinet %d, drawer %d)\n", + err_print_prefix, + ev_packets[i].name, + env->cabinet, + env->drawer); + printk("%s Module Type: 0x%x - Unit ID 0x%x - " + "Condition 0x%x\n", + err_print_prefix, + env->module_type, + env->unit_id, + env->condition); + } +#endif /* CONFIG_VERBOSE_MCHECK */ +} + +static int +marvel_process_680_frame(struct ev7_lf_subpackets *lf_subpackets, int print) +{ + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + int i; + + for (i = ev7_lf_env_index(EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE); + i <= ev7_lf_env_index(EL_TYPE__PAL__ENV__HOT_PLUG); + i++) { + if (lf_subpackets->env[i]) + status = MCHK_DISPOSITION_REPORT; + } + + if (print) + marvel_print_680_frame(lf_subpackets); + + return status; +} + +#ifdef CONFIG_VERBOSE_MCHECK + +static void +marvel_print_err_cyc(u64 err_cyc) +{ + static char *packet_desc[] = { + "No Error", + "UNKNOWN", + "1 cycle (1 or 2 flit packet)", + "2 cycles (3 flit packet)", + "9 cycles (18 flit packet)", + "10 cycles (19 flit packet)", + "UNKNOWN", + "UNKNOWN", + "UNKNOWN" + }; + +#define IO7__ERR_CYC__ODD_FLT (1UL << 0) +#define IO7__ERR_CYC__EVN_FLT (1UL << 1) +#define IO7__ERR_CYC__PACKET__S (6) +#define IO7__ERR_CYC__PACKET__M (0x7) +#define IO7__ERR_CYC__LOC (1UL << 5) +#define IO7__ERR_CYC__CYCLE__S (2) +#define IO7__ERR_CYC__CYCLE__M (0x7) + + printk("%s Packet In Error: %s\n" + "%s Error in %s, cycle %ld%s%s\n", + err_print_prefix, + packet_desc[EXTRACT(err_cyc, IO7__ERR_CYC__PACKET)], + err_print_prefix, + (err_cyc & IO7__ERR_CYC__LOC) ? "DATA" : "HEADER", + EXTRACT(err_cyc, IO7__ERR_CYC__CYCLE), + (err_cyc & IO7__ERR_CYC__ODD_FLT) ? " [ODD Flit]": "", + (err_cyc & IO7__ERR_CYC__EVN_FLT) ? " [Even Flit]": ""); +} + +static void +marvel_print_po7_crrct_sym(u64 crrct_sym) +{ +#define IO7__PO7_CRRCT_SYM__SYN__S (0) +#define IO7__PO7_CRRCT_SYM__SYN__M (0x7f) +#define IO7__PO7_CRRCT_SYM__ERR_CYC__S (7) /* ERR_CYC + ODD_FLT + EVN_FLT */ +#define IO7__PO7_CRRCT_SYM__ERR_CYC__M (0x1ff) + + + printk("%s Correctable Error Symptoms:\n" + "%s Syndrome: 0x%lx\n", + err_print_prefix, + err_print_prefix, EXTRACT(crrct_sym, IO7__PO7_CRRCT_SYM__SYN)); + marvel_print_err_cyc(EXTRACT(crrct_sym, IO7__PO7_CRRCT_SYM__ERR_CYC)); +} + +static void +marvel_print_po7_uncrr_sym(u64 uncrr_sym, u64 valid_mask) +{ + static char *clk_names[] = { "_h[0]", "_h[1]", "_n[0]", "_n[1]" }; + static char *clk_decode[] = { + "No Error", + "One extra rising edge", + "Two extra rising edges", + "Lost one clock" + }; + static char *port_names[] = { "Port 0", "Port 1", + "Port 2", "Port 3", + "Unknown Port", "Unknown Port", + "Unknown Port", "Port 7" }; + int scratch, i; + +#define IO7__PO7_UNCRR_SYM__SYN__S (0) +#define IO7__PO7_UNCRR_SYM__SYN__M (0x7f) +#define IO7__PO7_UNCRR_SYM__ERR_CYC__S (7) /* ERR_CYC + ODD_FLT... */ +#define IO7__PO7_UNCRR_SYM__ERR_CYC__M (0x1ff) /* ... + EVN_FLT */ +#define IO7__PO7_UNCRR_SYM__CLK__S (16) +#define IO7__PO7_UNCRR_SYM__CLK__M (0xff) +#define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__REQ (1UL << 24) +#define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__RIO (1UL << 25) +#define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__WIO (1UL << 26) +#define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__BLK (1UL << 27) +#define IO7__PO7_UNCRR_SYM__CDT_OVF_TO__NBK (1UL << 28) +#define IO7__PO7_UNCRR_SYM__OVF__READIO (1UL << 29) +#define IO7__PO7_UNCRR_SYM__OVF__WRITEIO (1UL << 30) +#define IO7__PO7_UNCRR_SYM__OVF__FWD (1UL << 31) +#define IO7__PO7_UNCRR_SYM__VICTIM_SP__S (32) +#define IO7__PO7_UNCRR_SYM__VICTIM_SP__M (0xff) +#define IO7__PO7_UNCRR_SYM__DETECT_SP__S (40) +#define IO7__PO7_UNCRR_SYM__DETECT_SP__M (0xff) +#define IO7__PO7_UNCRR_SYM__STRV_VTR__S (48) +#define IO7__PO7_UNCRR_SYM__STRV_VTR__M (0x3ff) + +#define IO7__STRV_VTR__LSI__INTX__S (0) +#define IO7__STRV_VTR__LSI__INTX__M (0x3) +#define IO7__STRV_VTR__LSI__SLOT__S (2) +#define IO7__STRV_VTR__LSI__SLOT__M (0x7) +#define IO7__STRV_VTR__LSI__BUS__S (5) +#define IO7__STRV_VTR__LSI__BUS__M (0x3) +#define IO7__STRV_VTR__MSI__INTNUM__S (0) +#define IO7__STRV_VTR__MSI__INTNUM__M (0x1ff) +#define IO7__STRV_VTR__IS_MSI (1UL << 9) + + printk("%s Uncorrectable Error Symptoms:\n", err_print_prefix); + uncrr_sym &= valid_mask; + + if (EXTRACT(valid_mask, IO7__PO7_UNCRR_SYM__SYN)) + printk("%s Syndrome: 0x%lx\n", + err_print_prefix, + EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__SYN)); + + if (EXTRACT(valid_mask, IO7__PO7_UNCRR_SYM__ERR_CYC)) + marvel_print_err_cyc(EXTRACT(uncrr_sym, + IO7__PO7_UNCRR_SYM__ERR_CYC)); + + scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__CLK); + for (i = 0; i < 4; i++, scratch >>= 2) { + if (scratch & 0x3) + printk("%s Clock %s: %s\n", + err_print_prefix, + clk_names[i], clk_decode[scratch & 0x3]); + } + + if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__REQ) + printk("%s REQ Credit Timeout or Overflow\n", + err_print_prefix); + if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__RIO) + printk("%s RIO Credit Timeout or Overflow\n", + err_print_prefix); + if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__WIO) + printk("%s WIO Credit Timeout or Overflow\n", + err_print_prefix); + if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__BLK) + printk("%s BLK Credit Timeout or Overflow\n", + err_print_prefix); + if (uncrr_sym & IO7__PO7_UNCRR_SYM__CDT_OVF_TO__NBK) + printk("%s NBK Credit Timeout or Overflow\n", + err_print_prefix); + + if (uncrr_sym & IO7__PO7_UNCRR_SYM__OVF__READIO) + printk("%s Read I/O Buffer Overflow\n", + err_print_prefix); + if (uncrr_sym & IO7__PO7_UNCRR_SYM__OVF__WRITEIO) + printk("%s Write I/O Buffer Overflow\n", + err_print_prefix); + if (uncrr_sym & IO7__PO7_UNCRR_SYM__OVF__FWD) + printk("%s FWD Buffer Overflow\n", + err_print_prefix); + + if ((scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__VICTIM_SP))) { + int lost = scratch & (1UL << 4); + scratch &= ~lost; + for (i = 0; i < 8; i++, scratch >>= 1) { + if (!(scratch & 1)) + continue; + printk("%s Error Response sent to %s", + err_print_prefix, port_names[i]); + } + if (lost) + printk("%s Lost Error sent somewhere else\n", + err_print_prefix); + } + + if ((scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__DETECT_SP))) { + for (i = 0; i < 8; i++, scratch >>= 1) { + if (!(scratch & 1)) + continue; + printk("%s Error Reported by %s", + err_print_prefix, port_names[i]); + } + } + + if (EXTRACT(valid_mask, IO7__PO7_UNCRR_SYM__STRV_VTR)) { + char starvation_message[80]; + + scratch = EXTRACT(uncrr_sym, IO7__PO7_UNCRR_SYM__STRV_VTR); + if (scratch & IO7__STRV_VTR__IS_MSI) + sprintf(starvation_message, + "MSI Interrupt 0x%x", + EXTRACT(scratch, IO7__STRV_VTR__MSI__INTNUM)); + else + sprintf(starvation_message, + "LSI INT%c for Bus:Slot (%d:%d)\n", + 'A' + EXTRACT(scratch, + IO7__STRV_VTR__LSI__INTX), + EXTRACT(scratch, IO7__STRV_VTR__LSI__BUS), + EXTRACT(scratch, IO7__STRV_VTR__LSI__SLOT)); + + printk("%s Starvation Int Trigger By: %s\n", + err_print_prefix, starvation_message); + } +} + +static void +marvel_print_po7_ugbge_sym(u64 ugbge_sym) +{ + char opcode_str[10]; + +#define IO7__PO7_UGBGE_SYM__UPH_PKT_OFF__S (6) +#define IO7__PO7_UGBGE_SYM__UPH_PKT_OFF__M (0xfffffffful) +#define IO7__PO7_UGBGE_SYM__UPH_OPCODE__S (40) +#define IO7__PO7_UGBGE_SYM__UPH_OPCODE__M (0xff) +#define IO7__PO7_UGBGE_SYM__UPH_SRC_PORT__S (48) +#define IO7__PO7_UGBGE_SYM__UPH_SRC_PORT__M (0xf) +#define IO7__PO7_UGBGE_SYM__UPH_DEST_PID__S (52) +#define IO7__PO7_UGBGE_SYM__UPH_DEST_PID__M (0x7ff) +#define IO7__PO7_UGBGE_SYM__VALID (1UL << 63) + + if (!(ugbge_sym & IO7__PO7_UGBGE_SYM__VALID)) + return; + + switch(EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_OPCODE)) { + case 0x51: + sprintf(opcode_str, "Wr32"); + break; + case 0x50: + sprintf(opcode_str, "WrQW"); + break; + case 0x54: + sprintf(opcode_str, "WrIPR"); + break; + case 0xD8: + sprintf(opcode_str, "Victim"); + break; + case 0xC5: + sprintf(opcode_str, "BlkIO"); + break; + default: + sprintf(opcode_str, "0x%lx\n", + EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_OPCODE)); + break; + } + + printk("%s Up Hose Garbage Symptom:\n" + "%s Source Port: %ld - Dest PID: %ld - OpCode: %s\n", + err_print_prefix, + err_print_prefix, + EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_SRC_PORT), + EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_DEST_PID), + opcode_str); + + if (0xC5 != EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_OPCODE)) + printk("%s Packet Offset 0x%08lx\n", + err_print_prefix, + EXTRACT(ugbge_sym, IO7__PO7_UGBGE_SYM__UPH_PKT_OFF)); +} + +static void +marvel_print_po7_err_sum(struct ev7_pal_io_subpacket *io) +{ + u64 uncrr_sym_valid = 0; + +#define IO7__PO7_ERRSUM__CR_SBE (1UL << 32) +#define IO7__PO7_ERRSUM__CR_SBE2 (1UL << 33) +#define IO7__PO7_ERRSUM__CR_PIO_WBYTE (1UL << 34) +#define IO7__PO7_ERRSUM__CR_CSR_NXM (1UL << 35) +#define IO7__PO7_ERRSUM__CR_RPID_ACV (1UL << 36) +#define IO7__PO7_ERRSUM__CR_RSP_NXM (1UL << 37) +#define IO7__PO7_ERRSUM__CR_ERR_RESP (1UL << 38) +#define IO7__PO7_ERRSUM__CR_CLK_DERR (1UL << 39) +#define IO7__PO7_ERRSUM__CR_DAT_DBE (1UL << 40) +#define IO7__PO7_ERRSUM__CR_DAT_GRBG (1UL << 41) +#define IO7__PO7_ERRSUM__MAF_TO (1UL << 42) +#define IO7__PO7_ERRSUM__UGBGE (1UL << 43) +#define IO7__PO7_ERRSUM__UN_MAF_LOST (1UL << 44) +#define IO7__PO7_ERRSUM__UN_PKT_OVF (1UL << 45) +#define IO7__PO7_ERRSUM__UN_CDT_OVF (1UL << 46) +#define IO7__PO7_ERRSUM__UN_DEALLOC (1UL << 47) +#define IO7__PO7_ERRSUM__BH_CDT_TO (1UL << 51) +#define IO7__PO7_ERRSUM__BH_CLK_HDR (1UL << 52) +#define IO7__PO7_ERRSUM__BH_DBE_HDR (1UL << 53) +#define IO7__PO7_ERRSUM__BH_GBG_HDR (1UL << 54) +#define IO7__PO7_ERRSUM__BH_BAD_CMD (1UL << 55) +#define IO7__PO7_ERRSUM__HLT_INT (1UL << 56) +#define IO7__PO7_ERRSUM__HP_INT (1UL << 57) +#define IO7__PO7_ERRSUM__CRD_INT (1UL << 58) +#define IO7__PO7_ERRSUM__STV_INT (1UL << 59) +#define IO7__PO7_ERRSUM__HRD_INT (1UL << 60) +#define IO7__PO7_ERRSUM__BH_SUM (1UL << 61) +#define IO7__PO7_ERRSUM__ERR_LST (1UL << 62) +#define IO7__PO7_ERRSUM__ERR_VALID (1UL << 63) + +#define IO7__PO7_ERRSUM__ERR_MASK (IO7__PO7_ERRSUM__ERR_VALID | \ + IO7__PO7_ERRSUM__CR_SBE) + + /* + * Single bit errors aren't covered by ERR_VALID. + */ + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_SBE) { + printk("%s %sSingle Bit Error(s) detected/corrected\n", + err_print_prefix, + (io->po7_error_sum & IO7__PO7_ERRSUM__CR_SBE2) + ? "Multiple " : ""); + marvel_print_po7_crrct_sym(io->po7_crrct_sym); + } + + /* + * Neither are the interrupt status bits + */ + if (io->po7_error_sum & IO7__PO7_ERRSUM__HLT_INT) + printk("%s Halt Interrupt posted", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__HP_INT) { + printk("%s Hot Plug Event Interrupt posted", + err_print_prefix); + uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__DETECT_SP); + } + if (io->po7_error_sum & IO7__PO7_ERRSUM__CRD_INT) + printk("%s Correctable Error Interrupt posted", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__STV_INT) { + printk("%s Starvation Interrupt posted", err_print_prefix); + uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__STRV_VTR); + } + if (io->po7_error_sum & IO7__PO7_ERRSUM__HRD_INT) { + printk("%s Hard Error Interrupt posted", err_print_prefix); + uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__DETECT_SP); + } + + /* + * Everything else is valid only with ERR_VALID, so skip to the end + * (uncrr_sym check) unless ERR_VALID is set. + */ + if (!(io->po7_error_sum & IO7__PO7_ERRSUM__ERR_VALID)) + goto check_uncrr_sym; + + /* + * Since ERR_VALID is set, VICTIM_SP in uncrr_sym is valid. + * For bits [29:0] to also be valid, the following bits must + * not be set: + * CR_PIO_WBYTE CR_CSR_NXM CR_RSP_NXM + * CR_ERR_RESP MAF_TO + */ + uncrr_sym_valid |= GEN_MASK(IO7__PO7_UNCRR_SYM__VICTIM_SP); + if (!(io->po7_error_sum & (IO7__PO7_ERRSUM__CR_PIO_WBYTE | + IO7__PO7_ERRSUM__CR_CSR_NXM | + IO7__PO7_ERRSUM__CR_RSP_NXM | + IO7__PO7_ERRSUM__CR_ERR_RESP | + IO7__PO7_ERRSUM__MAF_TO))) + uncrr_sym_valid |= 0x3ffffffful; + + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_PIO_WBYTE) + printk("%s Write byte into IO7 CSR\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_CSR_NXM) + printk("%s PIO to non-existent CSR\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_RPID_ACV) + printk("%s Bus Requester PID (Access Violation)\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_RSP_NXM) + printk("%s Received NXM response from EV7\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_ERR_RESP) + printk("%s Received ERROR RESPONSE\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_CLK_DERR) + printk("%s Clock error on data flit\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_DAT_DBE) + printk("%s Double Bit Error Data Error Detected\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__CR_DAT_GRBG) + printk("%s Garbage Encoding Detected on the data\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__UGBGE) { + printk("%s Garbage Encoding sent up hose\n", + err_print_prefix); + marvel_print_po7_ugbge_sym(io->po7_ugbge_sym); + } + if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_MAF_LOST) + printk("%s Orphan response (unexpected response)\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_PKT_OVF) + printk("%s Down hose packet overflow\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_CDT_OVF) + printk("%s Down hose credit overflow\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__UN_DEALLOC) + printk("%s Unexpected or bad dealloc field\n", + err_print_prefix); + + /* + * The black hole events. + */ + if (io->po7_error_sum & IO7__PO7_ERRSUM__MAF_TO) + printk("%s BLACK HOLE: Timeout for all responses\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_CDT_TO) + printk("%s BLACK HOLE: Credit Timeout\n", err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_CLK_HDR) + printk("%s BLACK HOLE: Clock check on header\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_DBE_HDR) + printk("%s BLACK HOLE: Uncorrectable Error on header\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_GBG_HDR) + printk("%s BLACK HOLE: Garbage on header\n", + err_print_prefix); + if (io->po7_error_sum & IO7__PO7_ERRSUM__BH_BAD_CMD) + printk("%s BLACK HOLE: Bad EV7 command\n", + err_print_prefix); + + if (io->po7_error_sum & IO7__PO7_ERRSUM__ERR_LST) + printk("%s Lost Error\n", err_print_prefix); + + printk("%s Failing Packet:\n" + "%s Cycle 1: %016lx\n" + "%s Cycle 2: %016lx\n", + err_print_prefix, + err_print_prefix, io->po7_err_pkt0, + err_print_prefix, io->po7_err_pkt1); + /* + * If there are any valid bits in UNCRR sym for this err, + * print UNCRR_SYM as well. + */ +check_uncrr_sym: + if (uncrr_sym_valid) + marvel_print_po7_uncrr_sym(io->po7_uncrr_sym, uncrr_sym_valid); +} + +static void +marvel_print_pox_tlb_err(u64 tlb_err) +{ + static char *tlb_errors[] = { + "No Error", + "North Port Signaled Error fetching TLB entry", + "PTE invalid or UCC or GBG error on this entry", + "Address did not hit any DMA window" + }; + +#define IO7__POX_TLBERR__ERR_VALID (1UL << 63) +#define IO7__POX_TLBERR__ERRCODE__S (0) +#define IO7__POX_TLBERR__ERRCODE__M (0x3) +#define IO7__POX_TLBERR__ERR_TLB_PTR__S (3) +#define IO7__POX_TLBERR__ERR_TLB_PTR__M (0x7) +#define IO7__POX_TLBERR__FADDR__S (6) +#define IO7__POX_TLBERR__FADDR__M (0x3fffffffffful) + + if (!(tlb_err & IO7__POX_TLBERR__ERR_VALID)) + return; + + printk("%s TLB Error on index 0x%lx:\n" + "%s - %s\n" + "%s - Addr: 0x%016lx\n", + err_print_prefix, + EXTRACT(tlb_err, IO7__POX_TLBERR__ERR_TLB_PTR), + err_print_prefix, + tlb_errors[EXTRACT(tlb_err, IO7__POX_TLBERR__ERRCODE)], + err_print_prefix, + EXTRACT(tlb_err, IO7__POX_TLBERR__FADDR) << 6); +} + +static void +marvel_print_pox_spl_cmplt(u64 spl_cmplt) +{ + char message[80]; + +#define IO7__POX_SPLCMPLT__MESSAGE__S (0) +#define IO7__POX_SPLCMPLT__MESSAGE__M (0x0fffffffful) +#define IO7__POX_SPLCMPLT__SOURCE_BUS__S (40) +#define IO7__POX_SPLCMPLT__SOURCE_BUS__M (0xfful) +#define IO7__POX_SPLCMPLT__SOURCE_DEV__S (35) +#define IO7__POX_SPLCMPLT__SOURCE_DEV__M (0x1ful) +#define IO7__POX_SPLCMPLT__SOURCE_FUNC__S (32) +#define IO7__POX_SPLCMPLT__SOURCE_FUNC__M (0x07ul) + +#define IO7__POX_SPLCMPLT__MSG_CLASS__S (28) +#define IO7__POX_SPLCMPLT__MSG_CLASS__M (0xf) +#define IO7__POX_SPLCMPLT__MSG_INDEX__S (20) +#define IO7__POX_SPLCMPLT__MSG_INDEX__M (0xff) +#define IO7__POX_SPLCMPLT__MSG_CLASSINDEX__S (20) +#define IO7__POX_SPLCMPLT__MSG_CLASSINDEX__M (0xfff) +#define IO7__POX_SPLCMPLT__REM_LOWER_ADDR__S (12) +#define IO7__POX_SPLCMPLT__REM_LOWER_ADDR__M (0x7f) +#define IO7__POX_SPLCMPLT__REM_BYTE_COUNT__S (0) +#define IO7__POX_SPLCMPLT__REM_BYTE_COUNT__M (0xfff) + + printk("%s Split Completion Error:\n" + "%s Source (Bus:Dev:Func): %ld:%ld:%ld\n", + err_print_prefix, + err_print_prefix, + EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_BUS), + EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_DEV), + EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__SOURCE_FUNC)); + + switch(EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__MSG_CLASSINDEX)) { + case 0x000: + sprintf(message, "Normal completion"); + break; + case 0x100: + sprintf(message, "Bridge - Master Abort"); + break; + case 0x101: + sprintf(message, "Bridge - Target Abort"); + break; + case 0x102: + sprintf(message, "Bridge - Uncorrectable Write Data Error"); + break; + case 0x200: + sprintf(message, "Byte Count Out of Range"); + break; + case 0x201: + sprintf(message, "Uncorrectable Split Write Data Error"); + break; + default: + sprintf(message, "%08lx\n", + EXTRACT(spl_cmplt, IO7__POX_SPLCMPLT__MESSAGE)); + break; + } + printk("%s Message: %s\n", err_print_prefix, message); +} + +static void +marvel_print_pox_trans_sum(u64 trans_sum) +{ + char *pcix_cmd[] = { "Interrupt Acknowledge", + "Special Cycle", + "I/O Read", + "I/O Write", + "Reserved", + "Reserved / Device ID Message", + "Memory Read", + "Memory Write", + "Reserved / Alias to Memory Read Block", + "Reserved / Alias to Memory Write Block", + "Configuration Read", + "Configuration Write", + "Memory Read Multiple / Split Completion", + "Dual Address Cycle", + "Memory Read Line / Memory Read Block", + "Memory Write and Invalidate / Memory Write Block" + }; + +#define IO7__POX_TRANSUM__PCI_ADDR__S (0) +#define IO7__POX_TRANSUM__PCI_ADDR__M (0x3fffffffffffful) +#define IO7__POX_TRANSUM__DAC (1UL << 50) +#define IO7__POX_TRANSUM__PCIX_MASTER_SLOT__S (52) +#define IO7__POX_TRANSUM__PCIX_MASTER_SLOT__M (0xf) +#define IO7__POX_TRANSUM__PCIX_CMD__S (56) +#define IO7__POX_TRANSUM__PCIX_CMD__M (0xf) +#define IO7__POX_TRANSUM__ERR_VALID (1UL << 63) + + if (!(trans_sum & IO7__POX_TRANSUM__ERR_VALID)) + return; + + printk("%s Transaction Summary:\n" + "%s Command: 0x%lx - %s\n" + "%s Address: 0x%016lx%s\n" + "%s PCI-X Master Slot: 0x%lx\n", + err_print_prefix, + err_print_prefix, + EXTRACT(trans_sum, IO7__POX_TRANSUM__PCIX_CMD), + pcix_cmd[EXTRACT(trans_sum, IO7__POX_TRANSUM__PCIX_CMD)], + err_print_prefix, + EXTRACT(trans_sum, IO7__POX_TRANSUM__PCI_ADDR), + (trans_sum & IO7__POX_TRANSUM__DAC) ? " (DAC)" : "", + err_print_prefix, + EXTRACT(trans_sum, IO7__POX_TRANSUM__PCIX_MASTER_SLOT)); +} + +static void +marvel_print_pox_err(u64 err_sum, struct ev7_pal_io_one_port *port) +{ +#define IO7__POX_ERRSUM__AGP_REQQ_OVFL (1UL << 4) +#define IO7__POX_ERRSUM__AGP_SYNC_ERR (1UL << 5) +#define IO7__POX_ERRSUM__MRETRY_TO (1UL << 6) +#define IO7__POX_ERRSUM__PCIX_UX_SPL (1UL << 7) +#define IO7__POX_ERRSUM__PCIX_SPLIT_TO (1UL << 8) +#define IO7__POX_ERRSUM__PCIX_DISCARD_SPL (1UL << 9) +#define IO7__POX_ERRSUM__DMA_RD_TO (1UL << 10) +#define IO7__POX_ERRSUM__CSR_NXM_RD (1UL << 11) +#define IO7__POX_ERRSUM__CSR_NXM_WR (1UL << 12) +#define IO7__POX_ERRSUM__DMA_TO (1UL << 13) +#define IO7__POX_ERRSUM__ALL_MABORTS (1UL << 14) +#define IO7__POX_ERRSUM__MABORT (1UL << 15) +#define IO7__POX_ERRSUM__MABORT_MASK (IO7__POX_ERRSUM__ALL_MABORTS|\ + IO7__POX_ERRSUM__MABORT) +#define IO7__POX_ERRSUM__PT_TABORT (1UL << 16) +#define IO7__POX_ERRSUM__PM_TABORT (1UL << 17) +#define IO7__POX_ERRSUM__TABORT_MASK (IO7__POX_ERRSUM__PT_TABORT | \ + IO7__POX_ERRSUM__PM_TABORT) +#define IO7__POX_ERRSUM__SERR (1UL << 18) +#define IO7__POX_ERRSUM__ADDRERR_STB (1UL << 19) +#define IO7__POX_ERRSUM__DETECTED_SERR (1UL << 20) +#define IO7__POX_ERRSUM__PERR (1UL << 21) +#define IO7__POX_ERRSUM__DATAERR_STB_NIOW (1UL << 22) +#define IO7__POX_ERRSUM__DETECTED_PERR (1UL << 23) +#define IO7__POX_ERRSUM__PM_PERR (1UL << 24) +#define IO7__POX_ERRSUM__PT_SCERROR (1UL << 26) +#define IO7__POX_ERRSUM__HUNG_BUS (1UL << 28) +#define IO7__POX_ERRSUM__UPE_ERROR__S (51) +#define IO7__POX_ERRSUM__UPE_ERROR__M (0xffUL) +#define IO7__POX_ERRSUM__UPE_ERROR GEN_MASK(IO7__POX_ERRSUM__UPE_ERROR) +#define IO7__POX_ERRSUM__TLB_ERR (1UL << 59) +#define IO7__POX_ERRSUM__ERR_VALID (1UL << 63) + +#define IO7__POX_ERRSUM__TRANS_SUM__MASK (IO7__POX_ERRSUM__MRETRY_TO | \ + IO7__POX_ERRSUM__PCIX_UX_SPL | \ + IO7__POX_ERRSUM__PCIX_SPLIT_TO | \ + IO7__POX_ERRSUM__DMA_TO | \ + IO7__POX_ERRSUM__MABORT_MASK | \ + IO7__POX_ERRSUM__TABORT_MASK | \ + IO7__POX_ERRSUM__SERR | \ + IO7__POX_ERRSUM__ADDRERR_STB | \ + IO7__POX_ERRSUM__PERR | \ + IO7__POX_ERRSUM__DATAERR_STB_NIOW |\ + IO7__POX_ERRSUM__DETECTED_PERR | \ + IO7__POX_ERRSUM__PM_PERR | \ + IO7__POX_ERRSUM__PT_SCERROR | \ + IO7__POX_ERRSUM__UPE_ERROR) + + if (!(err_sum & IO7__POX_ERRSUM__ERR_VALID)) + return; + + /* + * First the transaction summary errors + */ + if (err_sum & IO7__POX_ERRSUM__MRETRY_TO) + printk("%s IO7 Master Retry Timeout expired\n", + err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PCIX_UX_SPL) + printk("%s Unexpected Split Completion\n", + err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PCIX_SPLIT_TO) + printk("%s IO7 Split Completion Timeout expired\n", + err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__DMA_TO) + printk("%s Hung bus during DMA transaction\n", + err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__MABORT_MASK) + printk("%s Master Abort\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PT_TABORT) + printk("%s IO7 Asserted Target Abort\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PM_TABORT) + printk("%s IO7 Received Target Abort\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__ADDRERR_STB) { + printk("%s Address or PCI-X Attribute Parity Error\n", + err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__SERR) + printk("%s IO7 Asserted SERR\n", err_print_prefix); + } + if (err_sum & IO7__POX_ERRSUM__PERR) { + if (err_sum & IO7__POX_ERRSUM__DATAERR_STB_NIOW) + printk("%s IO7 Detected Data Parity Error\n", + err_print_prefix); + else + printk("%s Split Completion Response with " + "Parity Error\n", err_print_prefix); + } + if (err_sum & IO7__POX_ERRSUM__DETECTED_PERR) + printk("%s PERR detected\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PM_PERR) + printk("%s PERR while IO7 is master\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PT_SCERROR) { + printk("%s IO7 Received Split Completion Error message\n", + err_print_prefix); + marvel_print_pox_spl_cmplt(port->pox_spl_cmplt); + } + if (err_sum & IO7__POX_ERRSUM__UPE_ERROR) { + unsigned int upe_error = EXTRACT(err_sum, + IO7__POX_ERRSUM__UPE_ERROR); + int i; + static char *upe_errors[] = { + "Parity Error on MSI write data", + "MSI read (MSI window is write only", + "TLB - Invalid WR transaction", + "TLB - Invalid RD transaction", + "DMA - WR error (see north port)", + "DMA - RD error (see north port)", + "PPR - WR error (see north port)", + "PPR - RD error (see north port)" + }; + + printk("%s UPE Error:\n", err_print_prefix); + for (i = 0; i < 8; i++) { + if (upe_error & (1 << i)) + printk("%s %s\n", err_print_prefix, + upe_errors[i]); + } + } + + /* + * POx_TRANS_SUM, if appropriate. + */ + if (err_sum & IO7__POX_ERRSUM__TRANS_SUM__MASK) + marvel_print_pox_trans_sum(port->pox_trans_sum); + + /* + * Then TLB_ERR. + */ + if (err_sum & IO7__POX_ERRSUM__TLB_ERR) { + printk("%s TLB ERROR\n", err_print_prefix); + marvel_print_pox_tlb_err(port->pox_tlb_err); + } + + /* + * And the single bit status errors. + */ + if (err_sum & IO7__POX_ERRSUM__AGP_REQQ_OVFL) + printk("%s AGP Request Queue Overflow\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__AGP_SYNC_ERR) + printk("%s AGP Sync Error\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__PCIX_DISCARD_SPL) + printk("%s Discarded split completion\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__DMA_RD_TO) + printk("%s DMA Read Timeout\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__CSR_NXM_RD) + printk("%s CSR NXM READ\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__CSR_NXM_WR) + printk("%s CSR NXM WRITE\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__DETECTED_SERR) + printk("%s SERR detected\n", err_print_prefix); + if (err_sum & IO7__POX_ERRSUM__HUNG_BUS) + printk("%s HUNG BUS detected\n", err_print_prefix); +} + +#endif /* CONFIG_VERBOSE_MCHECK */ + +static struct ev7_pal_io_subpacket * +marvel_find_io7_with_error(struct ev7_lf_subpackets *lf_subpackets) +{ + struct ev7_pal_io_subpacket *io = lf_subpackets->io; + struct io7 *io7; + int i; + + /* + * Caller must provide the packet to fill + */ + if (!io) + return NULL; + + /* + * Fill the subpacket with the console's standard fill pattern + */ + memset(io, 0x55, sizeof(*io)); + + for (io7 = NULL; NULL != (io7 = marvel_next_io7(io7)); ) { + unsigned long err_sum = 0; + + err_sum |= io7->csrs->PO7_ERROR_SUM.csr; + for (i = 0; i < IO7_NUM_PORTS; i++) { + if (!io7->ports[i].enabled) + continue; + err_sum |= io7->ports[i].csrs->POx_ERR_SUM.csr; + } + + /* + * Is there at least one error? + */ + if (err_sum & (1UL << 63)) + break; + } + + /* + * Did we find an IO7 with an error? + */ + if (!io7) + return NULL; + + /* + * We have an IO7 with an error. + * + * Fill in the IO subpacket. + */ + io->io_asic_rev = io7->csrs->IO_ASIC_REV.csr; + io->io_sys_rev = io7->csrs->IO_SYS_REV.csr; + io->io7_uph = io7->csrs->IO7_UPH.csr; + io->hpi_ctl = io7->csrs->HPI_CTL.csr; + io->crd_ctl = io7->csrs->CRD_CTL.csr; + io->hei_ctl = io7->csrs->HEI_CTL.csr; + io->po7_error_sum = io7->csrs->PO7_ERROR_SUM.csr; + io->po7_uncrr_sym = io7->csrs->PO7_UNCRR_SYM.csr; + io->po7_crrct_sym = io7->csrs->PO7_CRRCT_SYM.csr; + io->po7_ugbge_sym = io7->csrs->PO7_UGBGE_SYM.csr; + io->po7_err_pkt0 = io7->csrs->PO7_ERR_PKT[0].csr; + io->po7_err_pkt1 = io7->csrs->PO7_ERR_PKT[1].csr; + + for (i = 0; i < IO7_NUM_PORTS; i++) { + io7_ioport_csrs *csrs = io7->ports[i].csrs; + + if (!io7->ports[i].enabled) + continue; + + io->ports[i].pox_err_sum = csrs->POx_ERR_SUM.csr; + io->ports[i].pox_tlb_err = csrs->POx_TLB_ERR.csr; + io->ports[i].pox_spl_cmplt = csrs->POx_SPL_COMPLT.csr; + io->ports[i].pox_trans_sum = csrs->POx_TRANS_SUM.csr; + io->ports[i].pox_first_err = csrs->POx_FIRST_ERR.csr; + io->ports[i].pox_mult_err = csrs->POx_MULT_ERR.csr; + io->ports[i].pox_dm_source = csrs->POx_DM_SOURCE.csr; + io->ports[i].pox_dm_dest = csrs->POx_DM_DEST.csr; + io->ports[i].pox_dm_size = csrs->POx_DM_SIZE.csr; + io->ports[i].pox_dm_ctrl = csrs->POx_DM_CTRL.csr; + + /* + * Ack this port's errors, if any. POx_ERR_SUM must be last. + * + * Most of the error registers get cleared and unlocked when + * the associated bits in POx_ERR_SUM are cleared (by writing + * 1). POx_TLB_ERR is an exception and must be explicitly + * cleared. + */ + csrs->POx_TLB_ERR.csr = io->ports[i].pox_tlb_err; + csrs->POx_ERR_SUM.csr = io->ports[i].pox_err_sum; + mb(); + csrs->POx_ERR_SUM.csr; + } + + /* + * Ack any port 7 error(s). + */ + io7->csrs->PO7_ERROR_SUM.csr = io->po7_error_sum; + mb(); + io7->csrs->PO7_ERROR_SUM.csr; + + /* + * Correct the io7_pid. + */ + lf_subpackets->io_pid = io7->pe; + + return io; +} + +static int +marvel_process_io_error(struct ev7_lf_subpackets *lf_subpackets, int print) +{ + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + +#ifdef CONFIG_VERBOSE_MCHECK + struct ev7_pal_io_subpacket *io = lf_subpackets->io; + int i; +#endif /* CONFIG_VERBOSE_MCHECK */ + +#define MARVEL_IO_ERR_VALID(x) ((x) & (1UL << 63)) + + if (!lf_subpackets->logout || !lf_subpackets->io) + return status; + + /* + * The PALcode only builds an IO subpacket if there is a + * locally connected IO7. In the cases of + * 1) a uniprocessor kernel + * 2) an mp kernel before the local secondary has called in + * error interrupts are all directed to the primary processor. + * In that case, we may not have an IO subpacket at all and, event + * if we do, it may not be the right now. + * + * If the RBOX indicates an I/O error interrupt, make sure we have + * the correct IO7 information. If we don't have an IO subpacket + * or it's the wrong one, try to find the right one. + * + * RBOX I/O error interrupts are indicated by RBOX_INT<29> and + * RBOX_INT<10>. + */ + if ((lf_subpackets->io->po7_error_sum & (1UL << 32)) || + ((lf_subpackets->io->po7_error_sum | + lf_subpackets->io->ports[0].pox_err_sum | + lf_subpackets->io->ports[1].pox_err_sum | + lf_subpackets->io->ports[2].pox_err_sum | + lf_subpackets->io->ports[3].pox_err_sum) & (1UL << 63))) { + /* + * Either we have no IO subpacket or no error is + * indicated in the one we do have. Try find the + * one with the error. + */ + if (!marvel_find_io7_with_error(lf_subpackets)) + return status; + } + + /* + * We have an IO7 indicating an error - we're going to report it + */ + status = MCHK_DISPOSITION_REPORT; + +#ifdef CONFIG_VERBOSE_MCHECK + + if (!print) + return status; + + printk("%s*Error occurred on IO7 at PID %u\n", + err_print_prefix, lf_subpackets->io_pid); + + /* + * Check port 7 first + */ + if (lf_subpackets->io->po7_error_sum & IO7__PO7_ERRSUM__ERR_MASK) { + marvel_print_po7_err_sum(io); + +#if 0 + printk("%s PORT 7 ERROR:\n" + "%s PO7_ERROR_SUM: %016lx\n" + "%s PO7_UNCRR_SYM: %016lx\n" + "%s PO7_CRRCT_SYM: %016lx\n" + "%s PO7_UGBGE_SYM: %016lx\n" + "%s PO7_ERR_PKT0: %016lx\n" + "%s PO7_ERR_PKT1: %016lx\n", + err_print_prefix, + err_print_prefix, io->po7_error_sum, + err_print_prefix, io->po7_uncrr_sym, + err_print_prefix, io->po7_crrct_sym, + err_print_prefix, io->po7_ugbge_sym, + err_print_prefix, io->po7_err_pkt0, + err_print_prefix, io->po7_err_pkt1); +#endif + } + + /* + * Then loop through the ports + */ + for (i = 0; i < IO7_NUM_PORTS; i++) { + if (!MARVEL_IO_ERR_VALID(io->ports[i].pox_err_sum)) + continue; + + printk("%s PID %u PORT %d POx_ERR_SUM: %016lx\n", + err_print_prefix, + lf_subpackets->io_pid, i, io->ports[i].pox_err_sum); + marvel_print_pox_err(io->ports[i].pox_err_sum, &io->ports[i]); + + printk("%s [ POx_FIRST_ERR: %016lx ]\n", + err_print_prefix, io->ports[i].pox_first_err); + marvel_print_pox_err(io->ports[i].pox_first_err, + &io->ports[i]); + + } + + +#endif /* CONFIG_VERBOSE_MCHECK */ + + return status; +} + +static int +marvel_process_logout_frame(struct ev7_lf_subpackets *lf_subpackets, int print) +{ + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + + /* + * I/O error? + */ +#define EV7__RBOX_INT__IO_ERROR__MASK 0x20000400ul + if (lf_subpackets->logout && + (lf_subpackets->logout->rbox_int & 0x20000400ul)) + status = marvel_process_io_error(lf_subpackets, print); + + /* + * Probing behind PCI-X bridges can cause machine checks on + * Marvel when the probe is handled by the bridge as a split + * completion transaction. The symptom is an ERROR_RESPONSE + * to a CONFIG address. Since these errors will happen in + * normal operation, dismiss them. + * + * Dismiss if: + * C_STAT = 0x14 (Error Reponse) + * C_STS<3> = 0 (C_ADDR valid) + * C_ADDR<42> = 1 (I/O) + * C_ADDR<31:22> = 111110xxb (PCI Config space) + */ + if (lf_subpackets->ev7 && + (lf_subpackets->ev7->c_stat == 0x14) && + !(lf_subpackets->ev7->c_sts & 0x8) && + ((lf_subpackets->ev7->c_addr & 0x400ff000000ul) + == 0x400fe000000ul)) + status = MCHK_DISPOSITION_DISMISS; + + return status; +} + +void +marvel_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) +{ + struct el_subpacket *el_ptr = (struct el_subpacket *)la_ptr; + int (*process_frame)(struct ev7_lf_subpackets *, int) = NULL; + struct ev7_lf_subpackets subpacket_collection = { NULL, }; + struct ev7_pal_io_subpacket scratch_io_packet = { 0, }; + struct ev7_lf_subpackets *lf_subpackets = NULL; + int disposition = MCHK_DISPOSITION_UNKNOWN_ERROR; + char *saved_err_prefix = err_print_prefix; + char *error_type = NULL; + + /* + * Sync the processor + */ + mb(); + draina(); + + switch(vector) { + case SCB_Q_SYSEVENT: + process_frame = marvel_process_680_frame; + error_type = "System Event"; + break; + + case SCB_Q_SYSMCHK: + process_frame = marvel_process_logout_frame; + error_type = "System Uncorrectable Error"; + break; + + case SCB_Q_SYSERR: + process_frame = marvel_process_logout_frame; + error_type = "System Correctable Error"; + break; + + default: + /* Don't know it - pass it up. */ + ev7_machine_check(vector, la_ptr, regs); + return; + } + + /* + * A system event or error has occured, handle it here. + * + * Any errors in the logout frame have already been cleared by the + * PALcode, so just parse it. + */ + err_print_prefix = KERN_CRIT; + + /* + * Parse the logout frame without printing first. If the only error(s) + * found are classified as "dismissable", then just dismiss them and + * don't print any message + */ + lf_subpackets = + ev7_collect_logout_frame_subpackets(el_ptr, + &subpacket_collection); + if (process_frame && lf_subpackets && lf_subpackets->logout) { + /* + * We might not have the correct (or any) I/O subpacket. + * [ See marvel_process_io_error() for explanation. ] + * If we don't have one, point the io subpacket in + * lf_subpackets at scratch_io_packet so that + * marvel_find_io7_with_error() will have someplace to + * store the info. + */ + if (!lf_subpackets->io) + lf_subpackets->io = &scratch_io_packet; + + /* + * Default io_pid to the processor reporting the error + * [this will get changed in marvel_find_io7_with_error() + * if a different one is needed] + */ + lf_subpackets->io_pid = lf_subpackets->logout->whami; + + /* + * Evaluate the frames. + */ + disposition = process_frame(lf_subpackets, 0); + } + switch(disposition) { + case MCHK_DISPOSITION_DISMISS: + /* Nothing to do. */ + break; + + case MCHK_DISPOSITION_REPORT: + /* Recognized error, report it. */ + printk("%s*%s (Vector 0x%x) reported on CPU %d\n", + err_print_prefix, error_type, + (unsigned int)vector, (int)smp_processor_id()); + el_print_timestamp(&lf_subpackets->logout->timestamp); + process_frame(lf_subpackets, 1); + break; + + default: + /* Unknown - dump the annotated subpackets. */ + printk("%s*%s (Vector 0x%x) reported on CPU %d\n", + err_print_prefix, error_type, + (unsigned int)vector, (int)smp_processor_id()); + el_process_subpacket(el_ptr); + break; + + } + + err_print_prefix = saved_err_prefix; + + /* Release the logout frame. */ + wrmces(0x7); + mb(); +} + +void +marvel_register_error_handlers(void) +{ + ev7_register_error_handlers(); +} 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 @@ +/* + * linux/arch/alpha/kernel/err_titan.c + * + * Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation) + * + * Error handling code supporting TITAN systems + */ + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/sched.h> + +#include <asm/io.h> +#include <asm/core_titan.h> +#include <asm/hwrpb.h> +#include <asm/smp.h> +#include <asm/err_common.h> +#include <asm/err_ev6.h> + +#include "err_impl.h" +#include "proto.h" + + +static int +titan_parse_c_misc(u64 c_misc, int print) +{ +#ifdef CONFIG_VERBOSE_MCHECK + char *src; + int nxs = 0; +#endif + int status = MCHK_DISPOSITION_REPORT; + +#define TITAN__CCHIP_MISC__NXM (1UL << 28) +#define TITAN__CCHIP_MISC__NXS__S (29) +#define TITAN__CCHIP_MISC__NXS__M (0x7) + + if (!(c_misc & TITAN__CCHIP_MISC__NXM)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + +#ifdef CONFIG_VERBOSE_MCHECK + if (!print) + return status; + + nxs = EXTRACT(c_misc, TITAN__CCHIP_MISC__NXS); + switch(nxs) { + case 0: /* CPU 0 */ + case 1: /* CPU 1 */ + case 2: /* CPU 2 */ + case 3: /* CPU 3 */ + src = "CPU"; + /* num is already the CPU number */ + break; + case 4: /* Pchip 0 */ + case 5: /* Pchip 1 */ + src = "Pchip"; + nxs -= 4; + break; + default:/* reserved */ + src = "Unknown, NXS ="; + /* leave num untouched */ + break; + } + + printk("%s Non-existent memory access from: %s %d\n", + err_print_prefix, src, nxs); +#endif /* CONFIG_VERBOSE_MCHECK */ + + return status; +} + +static int +titan_parse_p_serror(int which, u64 serror, int print) +{ + int status = MCHK_DISPOSITION_REPORT; + +#ifdef CONFIG_VERBOSE_MCHECK + char *serror_src[] = {"GPCI", "APCI", "AGP HP", "AGP LP"}; + char *serror_cmd[] = {"DMA Read", "DMA RMW", "SGTE Read", "Reserved"}; +#endif /* CONFIG_VERBOSE_MCHECK */ + +#define TITAN__PCHIP_SERROR__LOST_UECC (1UL << 0) +#define TITAN__PCHIP_SERROR__UECC (1UL << 1) +#define TITAN__PCHIP_SERROR__CRE (1UL << 2) +#define TITAN__PCHIP_SERROR__NXIO (1UL << 3) +#define TITAN__PCHIP_SERROR__LOST_CRE (1UL << 4) +#define TITAN__PCHIP_SERROR__ECCMASK (TITAN__PCHIP_SERROR__UECC | \ + TITAN__PCHIP_SERROR__CRE) +#define TITAN__PCHIP_SERROR__ERRMASK (TITAN__PCHIP_SERROR__LOST_UECC | \ + TITAN__PCHIP_SERROR__UECC | \ + TITAN__PCHIP_SERROR__CRE | \ + TITAN__PCHIP_SERROR__NXIO | \ + TITAN__PCHIP_SERROR__LOST_CRE) +#define TITAN__PCHIP_SERROR__SRC__S (52) +#define TITAN__PCHIP_SERROR__SRC__M (0x3) +#define TITAN__PCHIP_SERROR__CMD__S (54) +#define TITAN__PCHIP_SERROR__CMD__M (0x3) +#define TITAN__PCHIP_SERROR__SYN__S (56) +#define TITAN__PCHIP_SERROR__SYN__M (0xff) +#define TITAN__PCHIP_SERROR__ADDR__S (15) +#define TITAN__PCHIP_SERROR__ADDR__M (0xffffffffUL) + + if (!(serror & TITAN__PCHIP_SERROR__ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + +#ifdef CONFIG_VERBOSE_MCHECK + if (!print) + return status; + + printk("%s PChip %d SERROR: %016lx\n", + err_print_prefix, which, serror); + if (serror & TITAN__PCHIP_SERROR__ECCMASK) { + printk("%s %sorrectable ECC Error:\n" + " Source: %-6s Command: %-8s Syndrome: 0x%08x\n" + " Address: 0x%lx\n", + err_print_prefix, + (serror & TITAN__PCHIP_SERROR__UECC) ? "Unc" : "C", + serror_src[EXTRACT(serror, TITAN__PCHIP_SERROR__SRC)], + serror_cmd[EXTRACT(serror, TITAN__PCHIP_SERROR__CMD)], + (unsigned)EXTRACT(serror, TITAN__PCHIP_SERROR__SYN), + EXTRACT(serror, TITAN__PCHIP_SERROR__ADDR)); + } + if (serror & TITAN__PCHIP_SERROR__NXIO) + printk("%s Non Existent I/O Error\n", err_print_prefix); + if (serror & TITAN__PCHIP_SERROR__LOST_UECC) + printk("%s Lost Uncorrectable ECC Error\n", + err_print_prefix); + if (serror & TITAN__PCHIP_SERROR__LOST_CRE) + printk("%s Lost Correctable ECC Error\n", err_print_prefix); +#endif /* CONFIG_VERBOSE_MCHECK */ + + return status; +} + +static int +titan_parse_p_perror(int which, int port, u64 perror, int print) +{ + int cmd; + unsigned long addr; + int status = MCHK_DISPOSITION_REPORT; + +#ifdef CONFIG_VERBOSE_MCHECK + char *perror_cmd[] = { "Interrupt Acknowledge", "Special Cycle", + "I/O Read", "I/O Write", + "Reserved", "Reserved", + "Memory Read", "Memory Write", + "Reserved", "Reserved", + "Configuration Read", "Configuration Write", + "Memory Read Multiple", "Dual Address Cycle", + "Memory Read Line","Memory Write and Invalidate" + }; +#endif /* CONFIG_VERBOSE_MCHECK */ + +#define TITAN__PCHIP_PERROR__LOST (1UL << 0) +#define TITAN__PCHIP_PERROR__SERR (1UL << 1) +#define TITAN__PCHIP_PERROR__PERR (1UL << 2) +#define TITAN__PCHIP_PERROR__DCRTO (1UL << 3) +#define TITAN__PCHIP_PERROR__SGE (1UL << 4) +#define TITAN__PCHIP_PERROR__APE (1UL << 5) +#define TITAN__PCHIP_PERROR__TA (1UL << 6) +#define TITAN__PCHIP_PERROR__DPE (1UL << 7) +#define TITAN__PCHIP_PERROR__NDS (1UL << 8) +#define TITAN__PCHIP_PERROR__IPTPR (1UL << 9) +#define TITAN__PCHIP_PERROR__IPTPW (1UL << 10) +#define TITAN__PCHIP_PERROR__ERRMASK (TITAN__PCHIP_PERROR__LOST | \ + TITAN__PCHIP_PERROR__SERR | \ + TITAN__PCHIP_PERROR__PERR | \ + TITAN__PCHIP_PERROR__DCRTO | \ + TITAN__PCHIP_PERROR__SGE | \ + TITAN__PCHIP_PERROR__APE | \ + TITAN__PCHIP_PERROR__TA | \ + TITAN__PCHIP_PERROR__DPE | \ + TITAN__PCHIP_PERROR__NDS | \ + TITAN__PCHIP_PERROR__IPTPR | \ + TITAN__PCHIP_PERROR__IPTPW) +#define TITAN__PCHIP_PERROR__DAC (1UL << 47) +#define TITAN__PCHIP_PERROR__MWIN (1UL << 48) +#define TITAN__PCHIP_PERROR__CMD__S (52) +#define TITAN__PCHIP_PERROR__CMD__M (0x0f) +#define TITAN__PCHIP_PERROR__ADDR__S (14) +#define TITAN__PCHIP_PERROR__ADDR__M (0x1fffffffful) + + if (!(perror & TITAN__PCHIP_PERROR__ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + + cmd = EXTRACT(perror, TITAN__PCHIP_PERROR__CMD); + addr = EXTRACT(perror, TITAN__PCHIP_PERROR__ADDR) << 2; + + /* + * Initializing the BIOS on a video card on a bus without + * a south bridge (subtractive decode agent) can result in + * master aborts as the BIOS probes the capabilities of the + * card. XFree86 does such initialization. If the error + * is a master abort (No DevSel as PCI Master) and the command + * is an I/O read or write below the address where we start + * assigning PCI I/O spaces (SRM uses 0x1000), then mark the + * error as dismissable so starting XFree86 doesn't result + * in a series of uncorrectable errors being reported. Also + * dismiss master aborts to VGA frame buffer space + * (0xA0000 - 0xC0000) and legacy BIOS space (0xC0000 - 0x100000) + * for the same reason. + * + * Also mark the error dismissible if it looks like the right + * error but only the Lost bit is set. Since the BIOS initialization + * can cause multiple master aborts and the error interrupt can + * be handled on a different CPU than the BIOS code is run on, + * it is possible for a second master abort to occur between the + * time the PALcode reads PERROR and the time it writes PERROR + * to acknowledge the error. If this timing happens, a second + * error will be signalled after the first, and if no additional + * errors occur, will look like a Lost error with no additional + * errors on the same transaction as the previous error. + */ + if (((perror & TITAN__PCHIP_PERROR__NDS) || + ((perror & TITAN__PCHIP_PERROR__ERRMASK) == + TITAN__PCHIP_PERROR__LOST)) && + ((((cmd & 0xE) == 2) && (addr < 0x1000)) || + (((cmd & 0xE) == 6) && (addr >= 0xA0000) && (addr < 0x100000)))) { + status = MCHK_DISPOSITION_DISMISS; + } + +#ifdef CONFIG_VERBOSE_MCHECK + if (!print) + return status; + + printk("%s PChip %d %cPERROR: %016lx\n", + err_print_prefix, which, + port ? 'A' : 'G', perror); + if (perror & TITAN__PCHIP_PERROR__IPTPW) + printk("%s Invalid Peer-to-Peer Write\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__IPTPR) + printk("%s Invalid Peer-to-Peer Read\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__NDS) + printk("%s No DEVSEL as PCI Master [Master Abort]\n", + err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__DPE) + printk("%s Data Parity Error\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__TA) + printk("%s Target Abort\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__APE) + printk("%s Address Parity Error\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__SGE) + printk("%s Scatter-Gather Error, Invalid PTE\n", + err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__DCRTO) + printk("%s Delayed-Completion Retry Timeout\n", + err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__PERR) + printk("%s PERR Asserted\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__SERR) + printk("%s SERR Asserted\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__LOST) + printk("%s Lost Error\n", err_print_prefix); + printk("%s Command: 0x%x - %s\n" + " Address: 0x%lx\n", + err_print_prefix, + cmd, perror_cmd[cmd], + addr); + if (perror & TITAN__PCHIP_PERROR__DAC) + printk("%s Dual Address Cycle\n", err_print_prefix); + if (perror & TITAN__PCHIP_PERROR__MWIN) + printk("%s Hit in Monster Window\n", err_print_prefix); +#endif /* CONFIG_VERBOSE_MCHECK */ + + return status; +} + +static int +titan_parse_p_agperror(int which, u64 agperror, int print) +{ + int status = MCHK_DISPOSITION_REPORT; +#ifdef CONFIG_VERBOSE_MCHECK + int cmd, len; + unsigned long addr; + + char *agperror_cmd[] = { "Read (low-priority)", "Read (high-priority)", + "Write (low-priority)", + "Write (high-priority)", + "Reserved", "Reserved", + "Flush", "Fence" + }; +#endif /* CONFIG_VERBOSE_MCHECK */ + +#define TITAN__PCHIP_AGPERROR__LOST (1UL << 0) +#define TITAN__PCHIP_AGPERROR__LPQFULL (1UL << 1) +#define TITAN__PCHIP_AGPERROR__HPQFULL (1UL << 2) +#define TITAN__PCHIP_AGPERROR__RESCMD (1UL << 3) +#define TITAN__PCHIP_AGPERROR__IPTE (1UL << 4) +#define TITAN__PCHIP_AGPERROR__PTP (1UL << 5) +#define TITAN__PCHIP_AGPERROR__NOWINDOW (1UL << 6) +#define TITAN__PCHIP_AGPERROR__ERRMASK (TITAN__PCHIP_AGPERROR__LOST | \ + TITAN__PCHIP_AGPERROR__LPQFULL | \ + TITAN__PCHIP_AGPERROR__HPQFULL | \ + TITAN__PCHIP_AGPERROR__RESCMD | \ + TITAN__PCHIP_AGPERROR__IPTE | \ + TITAN__PCHIP_AGPERROR__PTP | \ + TITAN__PCHIP_AGPERROR__NOWINDOW) +#define TITAN__PCHIP_AGPERROR__DAC (1UL << 48) +#define TITAN__PCHIP_AGPERROR__MWIN (1UL << 49) +#define TITAN__PCHIP_AGPERROR__FENCE (1UL << 59) +#define TITAN__PCHIP_AGPERROR__CMD__S (50) +#define TITAN__PCHIP_AGPERROR__CMD__M (0x07) +#define TITAN__PCHIP_AGPERROR__ADDR__S (15) +#define TITAN__PCHIP_AGPERROR__ADDR__M (0xffffffffUL) +#define TITAN__PCHIP_AGPERROR__LEN__S (53) +#define TITAN__PCHIP_AGPERROR__LEN__M (0x3f) + + if (!(agperror & TITAN__PCHIP_AGPERROR__ERRMASK)) + return MCHK_DISPOSITION_UNKNOWN_ERROR; + +#ifdef CONFIG_VERBOSE_MCHECK + if (!print) + return status; + + cmd = EXTRACT(agperror, TITAN__PCHIP_AGPERROR__CMD); + addr = EXTRACT(agperror, TITAN__PCHIP_AGPERROR__ADDR) << 3; + len = EXTRACT(agperror, TITAN__PCHIP_AGPERROR__LEN); + + printk("%s PChip %d AGPERROR: %016lx\n", err_print_prefix, + which, agperror); + if (agperror & TITAN__PCHIP_AGPERROR__NOWINDOW) + printk("%s No Window\n", err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__PTP) + printk("%s Peer-to-Peer set\n", err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__IPTE) + printk("%s Invalid PTE\n", err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__RESCMD) + printk("%s Reserved Command\n", err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__HPQFULL) + printk("%s HP Transaction Received while Queue Full\n", + err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__LPQFULL) + printk("%s LP Transaction Received while Queue Full\n", + err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__LOST) + printk("%s Lost Error\n", err_print_prefix); + printk("%s Command: 0x%x - %s, %d Quadwords%s\n" + " Address: 0x%lx\n", + err_print_prefix, cmd, agperror_cmd[cmd], len, + (agperror & TITAN__PCHIP_AGPERROR__FENCE) ? ", FENCE" : "", + addr); + if (agperror & TITAN__PCHIP_AGPERROR__DAC) + printk("%s Dual Address Cycle\n", err_print_prefix); + if (agperror & TITAN__PCHIP_AGPERROR__MWIN) + printk("%s Hit in Monster Window\n", err_print_prefix); +#endif /* CONFIG_VERBOSE_MCHECK */ + + return status; +} + +static int +titan_parse_p_chip(int which, u64 serror, u64 gperror, + u64 aperror, u64 agperror, int print) +{ + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + status |= titan_parse_p_serror(which, serror, print); + status |= titan_parse_p_perror(which, 0, gperror, print); + status |= titan_parse_p_perror(which, 1, aperror, print); + status |= titan_parse_p_agperror(which, agperror, print); + return status; +} + +int +titan_process_logout_frame(struct el_common *mchk_header, int print) +{ + struct el_TITAN_sysdata_mcheck *tmchk = + (struct el_TITAN_sysdata_mcheck *) + ((unsigned long)mchk_header + mchk_header->sys_offset); + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + + status |= titan_parse_c_misc(tmchk->c_misc, print); + status |= titan_parse_p_chip(0, tmchk->p0_serror, tmchk->p0_gperror, + tmchk->p0_aperror, tmchk->p0_agperror, + print); + status |= titan_parse_p_chip(1, tmchk->p1_serror, tmchk->p1_gperror, + tmchk->p1_aperror, tmchk->p1_agperror, + print); + + return status; +} + +void +titan_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) +{ + struct el_common *mchk_header = (struct el_common *)la_ptr; + struct el_TITAN_sysdata_mcheck *tmchk = + (struct el_TITAN_sysdata_mcheck *) + ((unsigned long)mchk_header + mchk_header->sys_offset); + u64 irqmask; + + /* + * Mask of Titan interrupt sources which are reported as machine checks + * + * 63 - CChip Error + * 62 - PChip 0 H_Error + * 61 - PChip 1 H_Error + * 60 - PChip 0 C_Error + * 59 - PChip 1 C_Error + */ +#define TITAN_MCHECK_INTERRUPT_MASK 0xF800000000000000UL + + /* + * Sync the processor + */ + mb(); + draina(); + + /* + * Only handle system errors here + */ + if ((vector != SCB_Q_SYSMCHK) && (vector != SCB_Q_SYSERR)) { + ev6_machine_check(vector, la_ptr, regs); + return; + } + + /* + * It's a system error, handle it here + * + * The PALcode has already cleared the error, so just parse it + */ + + /* + * Parse the logout frame without printing first. If the only error(s) + * found are classified as "dismissable", then just dismiss them and + * don't print any message + */ + if (titan_process_logout_frame(mchk_header, 0) != + MCHK_DISPOSITION_DISMISS) { + char *saved_err_prefix = err_print_prefix; + err_print_prefix = KERN_CRIT; + + /* + * Either a nondismissable error was detected or no + * recognized error was detected in the logout frame + * -- report the error in either case + */ + printk("%s" + "*System %s Error (Vector 0x%x) reported on CPU %d:\n", + err_print_prefix, + (vector == SCB_Q_SYSERR)?"Correctable":"Uncorrectable", + (unsigned int)vector, (int)smp_processor_id()); + +#ifdef CONFIG_VERBOSE_MCHECK + titan_process_logout_frame(mchk_header, alpha_verbose_mcheck); + if (alpha_verbose_mcheck) + dik_show_regs(regs, NULL); +#endif /* CONFIG_VERBOSE_MCHECK */ + + err_print_prefix = saved_err_prefix; + + /* + * Convert any pending interrupts which report as system + * machine checks to interrupts + */ + irqmask = tmchk->c_dirx & TITAN_MCHECK_INTERRUPT_MASK; + titan_dispatch_irqs(irqmask, regs); + } + + + /* + * Release the logout frame + */ + wrmces(0x7); + mb(); +} + +/* + * Subpacket Annotations + */ +static char *el_titan_pchip0_extended_annotation[] = { + "Subpacket Header", "P0_SCTL", "P0_SERREN", + "P0_APCTL", "P0_APERREN", "P0_AGPERREN", + "P0_ASPRST", "P0_AWSBA0", "P0_AWSBA1", + "P0_AWSBA2", "P0_AWSBA3", "P0_AWSM0", + "P0_AWSM1", "P0_AWSM2", "P0_AWSM3", + "P0_ATBA0", "P0_ATBA1", "P0_ATBA2", + "P0_ATBA3", "P0_GPCTL", "P0_GPERREN", + "P0_GSPRST", "P0_GWSBA0", "P0_GWSBA1", + "P0_GWSBA2", "P0_GWSBA3", "P0_GWSM0", + "P0_GWSM1", "P0_GWSM2", "P0_GWSM3", + "P0_GTBA0", "P0_GTBA1", "P0_GTBA2", + "P0_GTBA3", NULL +}; +static char *el_titan_pchip1_extended_annotation[] = { + "Subpacket Header", "P1_SCTL", "P1_SERREN", + "P1_APCTL", "P1_APERREN", "P1_AGPERREN", + "P1_ASPRST", "P1_AWSBA0", "P1_AWSBA1", + "P1_AWSBA2", "P1_AWSBA3", "P1_AWSM0", + "P1_AWSM1", "P1_AWSM2", "P1_AWSM3", + "P1_ATBA0", "P1_ATBA1", "P1_ATBA2", + "P1_ATBA3", "P1_GPCTL", "P1_GPERREN", + "P1_GSPRST", "P1_GWSBA0", "P1_GWSBA1", + "P1_GWSBA2", "P1_GWSBA3", "P1_GWSM0", + "P1_GWSM1", "P1_GWSM2", "P1_GWSM3", + "P1_GTBA0", "P1_GTBA1", "P1_GTBA2", + "P1_GTBA3", NULL +}; +static char *el_titan_memory_extended_annotation[] = { + "Subpacket Header", "AAR0", "AAR1", + "AAR2", "AAR3", "P0_SCTL", + "P0_GPCTL", "P0_APCTL", "P1_SCTL", + "P1_GPCTL", "P1_SCTL", NULL +}; + +static struct el_subpacket_annotation el_titan_annotations[] = { + SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, + EL_TYPE__REGATTA__TITAN_PCHIP0_EXTENDED, + 1, + "Titan PChip 0 Extended Frame", + el_titan_pchip0_extended_annotation), + SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, + EL_TYPE__REGATTA__TITAN_PCHIP1_EXTENDED, + 1, + "Titan PChip 1 Extended Frame", + el_titan_pchip1_extended_annotation), + SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, + EL_TYPE__REGATTA__TITAN_MEMORY_EXTENDED, + 1, + "Titan Memory Extended Frame", + el_titan_memory_extended_annotation), + SUBPACKET_ANNOTATION(EL_CLASS__REGATTA_FAMILY, + EL_TYPE__TERMINATION__TERMINATION, + 1, + "Termination Subpacket", + NULL) +}; + +static struct el_subpacket * +el_process_regatta_subpacket(struct el_subpacket *header) +{ + int status; + + if (header->class != EL_CLASS__REGATTA_FAMILY) { + printk("%s ** Unexpected header CLASS %d TYPE %d, aborting\n", + err_print_prefix, + header->class, header->type); + return NULL; + } + + switch(header->type) { + case EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME: + case EL_TYPE__REGATTA__SYSTEM_ERROR_FRAME: + case EL_TYPE__REGATTA__ENVIRONMENTAL_FRAME: + case EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT: + case EL_TYPE__REGATTA__SYSTEM_DBL_ERROR_HALT: + printk("%s ** Occurred on CPU %d:\n", + err_print_prefix, + (int)header->by_type.regatta_frame.cpuid); + status = privateer_process_logout_frame((struct el_common *) + header->by_type.regatta_frame.data_start, 1); + break; + default: + printk("%s ** REGATTA TYPE %d SUBPACKET\n", + err_print_prefix, header->type); + el_annotate_subpacket(header); + break; + } + + + return (struct el_subpacket *)((unsigned long)header + header->length); +} + +static struct el_subpacket_handler titan_subpacket_handler = + SUBPACKET_HANDLER_INIT(EL_CLASS__REGATTA_FAMILY, + el_process_regatta_subpacket); + +void +titan_register_error_handlers(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE (el_titan_annotations); i++) + cdl_register_subpacket_annotation(&el_titan_annotations[i]); + + cdl_register_subpacket_handler(&titan_subpacket_handler); + + ev6_register_error_handlers(); +} + + +/* + * Privateer + */ + +static int +privateer_process_680_frame(struct el_common *mchk_header, int print) +{ + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; +#ifdef CONFIG_VERBOSE_MCHECK + struct el_PRIVATEER_envdata_mcheck *emchk = + (struct el_PRIVATEER_envdata_mcheck *) + ((unsigned long)mchk_header + mchk_header->sys_offset); + + /* TODO - catagorize errors, for now, no error */ + + if (!print) + return status; + + /* TODO - decode instead of just dumping... */ + printk("%s Summary Flags: %016lx\n" + " CChip DIRx: %016lx\n" + " System Management IR: %016lx\n" + " CPU IR: %016lx\n" + " Power Supply IR: %016lx\n" + " LM78 Fault Status: %016lx\n" + " System Doors: %016lx\n" + " Temperature Warning: %016lx\n" + " Fan Control: %016lx\n" + " Fatal Power Down Code: %016lx\n", + err_print_prefix, + emchk->summary, + emchk->c_dirx, + emchk->smir, + emchk->cpuir, + emchk->psir, + emchk->fault, + emchk->sys_doors, + emchk->temp_warn, + emchk->fan_ctrl, + emchk->code); +#endif /* CONFIG_VERBOSE_MCHECK */ + + return status; +} + +int +privateer_process_logout_frame(struct el_common *mchk_header, int print) +{ + struct el_common_EV6_mcheck *ev6mchk = + (struct el_common_EV6_mcheck *)mchk_header; + int status = MCHK_DISPOSITION_UNKNOWN_ERROR; + + /* + * Machine check codes + */ +#define PRIVATEER_MCHK__CORR_ECC 0x86 /* 630 */ +#define PRIVATEER_MCHK__DC_TAG_PERR 0x9E /* 630 */ +#define PRIVATEER_MCHK__PAL_BUGCHECK 0x8E /* 670 */ +#define PRIVATEER_MCHK__OS_BUGCHECK 0x90 /* 670 */ +#define PRIVATEER_MCHK__PROC_HRD_ERR 0x98 /* 670 */ +#define PRIVATEER_MCHK__ISTREAM_CMOV_PRX 0xA0 /* 670 */ +#define PRIVATEER_MCHK__ISTREAM_CMOV_FLT 0xA2 /* 670 */ +#define PRIVATEER_MCHK__SYS_HRD_ERR 0x202 /* 660 */ +#define PRIVATEER_MCHK__SYS_CORR_ERR 0x204 /* 620 */ +#define PRIVATEER_MCHK__SYS_ENVIRON 0x206 /* 680 */ + + switch(ev6mchk->MCHK_Code) { + /* + * Vector 630 - Processor, Correctable + */ + case PRIVATEER_MCHK__CORR_ECC: + case PRIVATEER_MCHK__DC_TAG_PERR: + /* + * Fall through to vector 670 for processing... + */ + /* + * Vector 670 - Processor, Uncorrectable + */ + case PRIVATEER_MCHK__PAL_BUGCHECK: + case PRIVATEER_MCHK__OS_BUGCHECK: + case PRIVATEER_MCHK__PROC_HRD_ERR: + case PRIVATEER_MCHK__ISTREAM_CMOV_PRX: + case PRIVATEER_MCHK__ISTREAM_CMOV_FLT: + status |= ev6_process_logout_frame(mchk_header, print); + break; + + /* + * Vector 620 - System, Correctable + */ + case PRIVATEER_MCHK__SYS_CORR_ERR: + /* + * Fall through to vector 660 for processing... + */ + /* + * Vector 660 - System, Uncorrectable + */ + case PRIVATEER_MCHK__SYS_HRD_ERR: + status |= titan_process_logout_frame(mchk_header, print); + break; + + /* + * Vector 680 - System, Environmental + */ + case PRIVATEER_MCHK__SYS_ENVIRON: /* System, Environmental */ + status |= privateer_process_680_frame(mchk_header, print); + break; + + /* + * Unknown + */ + default: + status |= MCHK_DISPOSITION_REPORT; + if (print) { + printk("%s** Unknown Error, frame follows\n", + err_print_prefix); + mchk_dump_logout_frame(mchk_header); + } + + } + + return status; +} + +void +privateer_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs) +{ + struct el_common *mchk_header = (struct el_common *)la_ptr; + struct el_TITAN_sysdata_mcheck *tmchk = + (struct el_TITAN_sysdata_mcheck *) + (la_ptr + mchk_header->sys_offset); + u64 irqmask; + char *saved_err_prefix = err_print_prefix; + +#define PRIVATEER_680_INTERRUPT_MASK (0xE00UL) +#define PRIVATEER_HOTPLUG_INTERRUPT_MASK (0xE00UL) + + /* + * Sync the processor. + */ + mb(); + draina(); + + /* + * Only handle system events here. + */ + if (vector != SCB_Q_SYSEVENT) + return titan_machine_check(vector, la_ptr, regs); + + /* + * Report the event - System Events should be reported even if no + * error is indicated since the event could indicate the return + * to normal status. + */ + err_print_prefix = KERN_CRIT; + printk("%s*System Event (Vector 0x%x) reported on CPU %d:\n", + err_print_prefix, + (unsigned int)vector, (int)smp_processor_id()); + privateer_process_680_frame(mchk_header, 1); + err_print_prefix = saved_err_prefix; + + /* + * Convert any pending interrupts which report as 680 machine + * checks to interrupts. + */ + irqmask = tmchk->c_dirx & PRIVATEER_680_INTERRUPT_MASK; + + /* + * Dispatch the interrupt(s). + */ + titan_dispatch_irqs(irqmask, regs); + + /* + * Release the logout frame. + */ + wrmces(0x7); + mb(); +} 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 @@ +/* + * linux/arch/alpha/kernel/es1888.c + * + * Init the built-in ES1888 sound chip (SB16 compatible) + */ + +#include <linux/init.h> +#include <asm/io.h> +#include "proto.h" + +void __init +es1888_init(void) +{ + /* Sequence of IO reads to init the audio controller */ + inb(0x0229); + inb(0x0229); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x0220); /* This sets the base address to 0x220 */ + + /* Sequence to set DMA channels */ + outb(0x01, 0x0226); /* reset */ + inb(0x0226); /* pause */ + outb(0x00, 0x0226); /* release reset */ + while (!(inb(0x022e) & 0x80)) /* wait for bit 7 to assert*/ + continue; + inb(0x022a); /* pause */ + outb(0xc6, 0x022c); /* enable extended mode */ + inb(0x022a); /* pause, also forces the write */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0xb1, 0x022c); /* setup for write to Interrupt CR */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0x14, 0x022c); /* set IRQ 5 */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0xb2, 0x022c); /* setup for write to DMA CR */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0x18, 0x022c); /* set DMA channel 1 */ + inb(0x022c); /* force the write */ +} 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 @@ +/* + * linux/arch/alpha/kernel/gct.c + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> + +#include <asm/hwrpb.h> +#include <asm/gct.h> + +int +gct6_find_nodes(gct6_node *node, gct6_search_struct *search) +{ + gct6_search_struct *wanted; + int status = 0; + + /* First check the magic number. */ + if (node->magic != GCT_NODE_MAGIC) { + printk(KERN_ERR "GCT Node MAGIC incorrect - GCT invalid\n"); + return -EINVAL; + } + + /* Check against the search struct. */ + for (wanted = search; + wanted && (wanted->type | wanted->subtype); + wanted++) { + if (node->type != wanted->type) + continue; + if (node->subtype != wanted->subtype) + continue; + + /* Found it -- call out. */ + if (wanted->callout) + wanted->callout(node); + } + + /* Now walk the tree, siblings first. */ + if (node->next) + status |= gct6_find_nodes(GCT_NODE_PTR(node->next), search); + + /* Then the children. */ + if (node->child) + status |= gct6_find_nodes(GCT_NODE_PTR(node->child), search); + + return status; +} 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 @@ +/* + * alpha/boot/head.S + * + * initial boot stuff.. At this point, the bootloader has already + * switched into OSF/1 PAL-code, and loaded us at the correct address + * (START_ADDR). So there isn't much left for us to do: just set up + * the kernel global pointer and jump to the kernel entry-point. + */ + +#include <linux/config.h> +#include <asm/system.h> +#include <asm/asm_offsets.h> + +.globl swapper_pg_dir +.globl _stext +swapper_pg_dir=SWAPPER_PGD + + .set noreorder + .globl __start + .ent __start +_stext: +__start: + .prologue 0 + br $27,1f +1: ldgp $29,0($27) + /* We need to get current_task_info loaded up... */ + lda $8,init_thread_union + /* ... and find our stack ... */ + lda $30,0x4000 - SIZEOF_PT_REGS($8) + /* ... and then we can start the kernel. */ + jsr $26,start_kernel + call_pal PAL_halt + .end __start + +#ifdef CONFIG_SMP + .align 3 + .globl __smp_callin + .ent __smp_callin + /* On entry here from SRM console, the HWPCB of the per-cpu + slot for this processor has been loaded. We've arranged + for the UNIQUE value for this process to contain the PCBB + of the target idle task. */ +__smp_callin: + .prologue 1 + ldgp $29,0($27) # First order of business, load the GP. + + call_pal PAL_rduniq # Grab the target PCBB. + mov $0,$16 # Install it. + call_pal PAL_swpctx + + lda $8,0x3fff # Find "current". + bic $30,$8,$8 + + jsr $26,smp_callin + call_pal PAL_halt + .end __smp_callin +#endif /* CONFIG_SMP */ + + # + # The following two functions are needed for supporting SRM PALcode + # on the PC164 (at least), since that PALcode manages the interrupt + # masking, and we cannot duplicate the effort without causing problems + # + + .align 3 + .globl cserve_ena + .ent cserve_ena +cserve_ena: + .prologue 0 + bis $16,$16,$17 + lda $16,52($31) + call_pal PAL_cserve + ret ($26) + .end cserve_ena + + .align 3 + .globl cserve_dis + .ent cserve_dis +cserve_dis: + .prologue 0 + bis $16,$16,$17 + lda $16,53($31) + call_pal PAL_cserve + ret ($26) + .end cserve_dis + + # + # It is handy, on occasion, to make halt actually just loop. + # Putting it here means we dont have to recompile the whole + # kernel. + # + + .align 3 + .globl halt + .ent halt +halt: + .prologue 0 + call_pal PAL_halt + .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 @@ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> +#include <asm/uaccess.h> + + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_mm); +EXPORT_SYMBOL(init_task); + +union thread_union init_thread_union + __attribute__((section(".data.init_thread"))) + = { 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 @@ +/* + * Alpha IO and memory functions. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/module.h> +#include <asm/io.h> + +/* Out-of-line versions of the i/o routines that redirect into the + platform-specific version. Note that "platform-specific" may mean + "generic", which bumps through the machine vector. */ + +unsigned int +ioread8(void __iomem *addr) +{ + unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread8)(addr); + mb(); + return ret; +} + +unsigned int ioread16(void __iomem *addr) +{ + unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread16)(addr); + mb(); + return ret; +} + +unsigned int ioread32(void __iomem *addr) +{ + unsigned int ret = IO_CONCAT(__IO_PREFIX,ioread32)(addr); + mb(); + return ret; +} + +void iowrite8(u8 b, void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,iowrite8)(b, addr); + mb(); +} + +void iowrite16(u16 b, void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,iowrite16)(b, addr); + mb(); +} + +void iowrite32(u32 b, void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,iowrite32)(b, addr); + mb(); +} + +EXPORT_SYMBOL(ioread8); +EXPORT_SYMBOL(ioread16); +EXPORT_SYMBOL(ioread32); +EXPORT_SYMBOL(iowrite8); +EXPORT_SYMBOL(iowrite16); +EXPORT_SYMBOL(iowrite32); + +u8 inb(unsigned long port) +{ + return ioread8(ioport_map(port, 1)); +} + +u16 inw(unsigned long port) +{ + return ioread16(ioport_map(port, 2)); +} + +u32 inl(unsigned long port) +{ + return ioread32(ioport_map(port, 4)); +} + +void outb(u8 b, unsigned long port) +{ + iowrite8(b, ioport_map(port, 1)); +} + +void outw(u16 b, unsigned long port) +{ + iowrite16(b, ioport_map(port, 2)); +} + +void outl(u32 b, unsigned long port) +{ + iowrite32(b, ioport_map(port, 4)); +} + +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); + +u8 __raw_readb(const volatile void __iomem *addr) +{ + return IO_CONCAT(__IO_PREFIX,readb)(addr); +} + +u16 __raw_readw(const volatile void __iomem *addr) +{ + return IO_CONCAT(__IO_PREFIX,readw)(addr); +} + +u32 __raw_readl(const volatile void __iomem *addr) +{ + return IO_CONCAT(__IO_PREFIX,readl)(addr); +} + +u64 __raw_readq(const volatile void __iomem *addr) +{ + return IO_CONCAT(__IO_PREFIX,readq)(addr); +} + +void __raw_writeb(u8 b, volatile void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,writeb)(b, addr); +} + +void __raw_writew(u16 b, volatile void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,writew)(b, addr); +} + +void __raw_writel(u32 b, volatile void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,writel)(b, addr); +} + +void __raw_writeq(u64 b, volatile void __iomem *addr) +{ + IO_CONCAT(__IO_PREFIX,writeq)(b, addr); +} + +EXPORT_SYMBOL(__raw_readb); +EXPORT_SYMBOL(__raw_readw); +EXPORT_SYMBOL(__raw_readl); +EXPORT_SYMBOL(__raw_readq); +EXPORT_SYMBOL(__raw_writeb); +EXPORT_SYMBOL(__raw_writew); +EXPORT_SYMBOL(__raw_writel); +EXPORT_SYMBOL(__raw_writeq); + +u8 readb(const volatile void __iomem *addr) +{ + u8 ret = __raw_readb(addr); + mb(); + return ret; +} + +u16 readw(const volatile void __iomem *addr) +{ + u16 ret = __raw_readw(addr); + mb(); + return ret; +} + +u32 readl(const volatile void __iomem *addr) +{ + u32 ret = __raw_readl(addr); + mb(); + return ret; +} + +u64 readq(const volatile void __iomem *addr) +{ + u64 ret = __raw_readq(addr); + mb(); + return ret; +} + +void writeb(u8 b, volatile void __iomem *addr) +{ + __raw_writeb(b, addr); + mb(); +} + +void writew(u16 b, volatile void __iomem *addr) +{ + __raw_writew(b, addr); + mb(); +} + +void writel(u32 b, volatile void __iomem *addr) +{ + __raw_writel(b, addr); + mb(); +} + +void writeq(u64 b, volatile void __iomem *addr) +{ + __raw_writeq(b, addr); + mb(); +} + +EXPORT_SYMBOL(readb); +EXPORT_SYMBOL(readw); +EXPORT_SYMBOL(readl); +EXPORT_SYMBOL(readq); +EXPORT_SYMBOL(writeb); +EXPORT_SYMBOL(writew); +EXPORT_SYMBOL(writel); +EXPORT_SYMBOL(writeq); + + +/* + * Read COUNT 8-bit bytes from port PORT into memory starting at SRC. + */ +void ioread8_rep(void __iomem *port, void *dst, unsigned long count) +{ + while ((unsigned long)dst & 0x3) { + if (!count) + return; + count--; + *(unsigned char *)dst = ioread8(port); + dst += 1; + } + + while (count >= 4) { + unsigned int w; + count -= 4; + w = ioread8(port); + w |= ioread8(port) << 8; + w |= ioread8(port) << 16; + w |= ioread8(port) << 24; + *(unsigned int *)dst = w; + dst += 4; + } + + while (count) { + --count; + *(unsigned char *)dst = ioread8(port); + dst += 1; + } +} + +void insb(unsigned long port, void *dst, unsigned long count) +{ + ioread8_rep(ioport_map(port, 1), dst, count); +} + +EXPORT_SYMBOL(ioread8_rep); +EXPORT_SYMBOL(insb); + +/* + * Read COUNT 16-bit words from port PORT into memory starting at + * SRC. SRC must be at least short aligned. This is used by the + * IDE driver to read disk sectors. Performance is important, but + * the interfaces seems to be slow: just using the inlined version + * of the inw() breaks things. + */ +void ioread16_rep(void __iomem *port, void *dst, unsigned long count) +{ + if (unlikely((unsigned long)dst & 0x3)) { + if (!count) + return; + BUG_ON((unsigned long)dst & 0x1); + count--; + *(unsigned short *)dst = ioread16(port); + dst += 2; + } + + while (count >= 2) { + unsigned int w; + count -= 2; + w = ioread16(port); + w |= ioread16(port) << 16; + *(unsigned int *)dst = w; + dst += 4; + } + + if (count) { + *(unsigned short*)dst = ioread16(port); + } +} + +void insw(unsigned long port, void *dst, unsigned long count) +{ + ioread16_rep(ioport_map(port, 2), dst, count); +} + +EXPORT_SYMBOL(ioread16_rep); +EXPORT_SYMBOL(insw); + + +/* + * Read COUNT 32-bit words from port PORT into memory starting at + * SRC. Now works with any alignment in SRC. Performance is important, + * but the interfaces seems to be slow: just using the inlined version + * of the inl() breaks things. + */ +void ioread32_rep(void __iomem *port, void *dst, unsigned long count) +{ + if (unlikely((unsigned long)dst & 0x3)) { + while (count--) { + struct S { int x __attribute__((packed)); }; + ((struct S *)dst)->x = ioread32(port); + dst += 4; + } + } else { + /* Buffer 32-bit aligned. */ + while (count--) { + *(unsigned int *)dst = ioread32(port); + dst += 4; + } + } +} + +void insl(unsigned long port, void *dst, unsigned long count) +{ + ioread32_rep(ioport_map(port, 4), dst, count); +} + +EXPORT_SYMBOL(ioread32_rep); +EXPORT_SYMBOL(insl); + + +/* + * Like insb but in the opposite direction. + * Don't worry as much about doing aligned memory transfers: + * doing byte reads the "slow" way isn't nearly as slow as + * doing byte writes the slow way (no r-m-w cycle). + */ +void iowrite8_rep(void __iomem *port, const void *xsrc, unsigned long count) +{ + const unsigned char *src = xsrc; + while (count--) + iowrite8(*src++, port); +} + +void outsb(unsigned long port, const void *src, unsigned long count) +{ + iowrite8_rep(ioport_map(port, 1), src, count); +} + +EXPORT_SYMBOL(iowrite8_rep); +EXPORT_SYMBOL(outsb); + + +/* + * Like insw but in the opposite direction. This is used by the IDE + * driver to write disk sectors. Performance is important, but the + * interfaces seems to be slow: just using the inlined version of the + * outw() breaks things. + */ +void iowrite16_rep(void __iomem *port, const void *src, unsigned long count) +{ + if (unlikely((unsigned long)src & 0x3)) { + if (!count) + return; + BUG_ON((unsigned long)src & 0x1); + iowrite16(*(unsigned short *)src, port); + src += 2; + --count; + } + + while (count >= 2) { + unsigned int w; + count -= 2; + w = *(unsigned int *)src; + src += 4; + iowrite16(w >> 0, port); + iowrite16(w >> 16, port); + } + + if (count) { + iowrite16(*(unsigned short *)src, port); + } +} + +void outsw(unsigned long port, const void *src, unsigned long count) +{ + iowrite16_rep(ioport_map(port, 2), src, count); +} + +EXPORT_SYMBOL(iowrite16_rep); +EXPORT_SYMBOL(outsw); + + +/* + * Like insl but in the opposite direction. This is used by the IDE + * driver to write disk sectors. Works with any alignment in SRC. + * Performance is important, but the interfaces seems to be slow: + * just using the inlined version of the outl() breaks things. + */ +void iowrite32_rep(void __iomem *port, const void *src, unsigned long count) +{ + if (unlikely((unsigned long)src & 0x3)) { + while (count--) { + struct S { int x __attribute__((packed)); }; + iowrite32(((struct S *)src)->x, port); + src += 4; + } + } else { + /* Buffer 32-bit aligned. */ + while (count--) { + iowrite32(*(unsigned int *)src, port); + src += 4; + } + } +} + +void outsl(unsigned long port, const void *src, unsigned long count) +{ + iowrite32_rep(ioport_map(port, 4), src, count); +} + +EXPORT_SYMBOL(iowrite32_rep); +EXPORT_SYMBOL(outsl); + + +/* + * Copy data from IO memory space to "real" memory space. + * This needs to be optimized. + */ +void memcpy_fromio(void *to, const volatile void __iomem *from, long count) +{ + /* Optimize co-aligned transfers. Everything else gets handled + a byte at a time. */ + + if (count >= 8 && ((u64)to & 7) == ((u64)from & 7)) { + count -= 8; + do { + *(u64 *)to = __raw_readq(from); + count -= 8; + to += 8; + from += 8; + } while (count >= 0); + count += 8; + } + + if (count >= 4 && ((u64)to & 3) == ((u64)from & 3)) { + count -= 4; + do { + *(u32 *)to = __raw_readl(from); + count -= 4; + to += 4; + from += 4; + } while (count >= 0); + count += 4; + } + + if (count >= 2 && ((u64)to & 1) == ((u64)from & 1)) { + count -= 2; + do { + *(u16 *)to = __raw_readw(from); + count -= 2; + to += 2; + from += 2; + } while (count >= 0); + count += 2; + } + + while (count > 0) { + *(u8 *) to = __raw_readb(from); + count--; + to++; + from++; + } + mb(); +} + +EXPORT_SYMBOL(memcpy_fromio); + + +/* + * Copy data from "real" memory space to IO memory space. + * This needs to be optimized. + */ +void memcpy_toio(volatile void __iomem *to, const void *from, long count) +{ + /* Optimize co-aligned transfers. Everything else gets handled + a byte at a time. */ + /* FIXME -- align FROM. */ + + if (count >= 8 && ((u64)to & 7) == ((u64)from & 7)) { + count -= 8; + do { + __raw_writeq(*(const u64 *)from, to); + count -= 8; + to += 8; + from += 8; + } while (count >= 0); + count += 8; + } + + if (count >= 4 && ((u64)to & 3) == ((u64)from & 3)) { + count -= 4; + do { + __raw_writel(*(const u32 *)from, to); + count -= 4; + to += 4; + from += 4; + } while (count >= 0); + count += 4; + } + + if (count >= 2 && ((u64)to & 1) == ((u64)from & 1)) { + count -= 2; + do { + __raw_writew(*(const u16 *)from, to); + count -= 2; + to += 2; + from += 2; + } while (count >= 0); + count += 2; + } + + while (count > 0) { + __raw_writeb(*(const u8 *) from, to); + count--; + to++; + from++; + } + mb(); +} + +EXPORT_SYMBOL(memcpy_toio); + + +/* + * "memset" on IO memory space. + */ +void _memset_c_io(volatile void __iomem *to, unsigned long c, long count) +{ + /* Handle any initial odd byte */ + if (count > 0 && ((u64)to & 1)) { + __raw_writeb(c, to); + to++; + count--; + } + + /* Handle any initial odd halfword */ + if (count >= 2 && ((u64)to & 2)) { + __raw_writew(c, to); + to += 2; + count -= 2; + } + + /* Handle any initial odd word */ + if (count >= 4 && ((u64)to & 4)) { + __raw_writel(c, to); + to += 4; + count -= 4; + } + + /* Handle all full-sized quadwords: we're aligned + (or have a small count) */ + count -= 8; + if (count >= 0) { + do { + __raw_writeq(c, to); + to += 8; + count -= 8; + } while (count >= 0); + } + count += 8; + + /* The tail is word-aligned if we still have count >= 4 */ + if (count >= 4) { + __raw_writel(c, to); + to += 4; + count -= 4; + } + + /* The tail is half-word aligned if we have count >= 2 */ + if (count >= 2) { + __raw_writew(c, to); + to += 2; + count -= 2; + } + + /* And finally, one last byte.. */ + if (count) { + __raw_writeb(c, to); + } + mb(); +} + +EXPORT_SYMBOL(_memset_c_io); + +/* A version of memcpy used by the vga console routines to move data around + arbitrarily between screen and main memory. */ + +void +scr_memcpyw(u16 *d, const u16 *s, unsigned int count) +{ + const u16 __iomem *ios = (const u16 __iomem *) s; + u16 __iomem *iod = (u16 __iomem *) d; + int s_isio = __is_ioaddr(s); + int d_isio = __is_ioaddr(d); + + if (s_isio) { + if (d_isio) { + /* FIXME: Should handle unaligned ops and + operation widening. */ + + count /= 2; + while (count--) { + u16 tmp = __raw_readw(ios++); + __raw_writew(tmp, iod++); + } + } + else + memcpy_fromio(d, ios, count); + } else { + if (d_isio) + memcpy_toio(iod, s, count); + else + memcpy(d, s, count); + } +} + +EXPORT_SYMBOL(scr_memcpyw); + +void __iomem *ioport_map(unsigned long port, unsigned int size) +{ + return IO_CONCAT(__IO_PREFIX,ioportmap) (port); +} + +void ioport_unmap(void __iomem *addr) +{ +} + +EXPORT_SYMBOL(ioport_map); +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 @@ +/* + * linux/arch/alpha/kernel/irq.c + * + * Copyright (C) 1995 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/profile.h> +#include <linux/bitops.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { + [0 ... NR_IRQS-1] = { + .handler = &no_irq_type, + .lock = SPIN_LOCK_UNLOCKED + } +}; + +static void register_irq_proc(unsigned int irq); + +volatile unsigned long irq_err_count; + +/* + * Special irq handlers. + */ + +irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + return IRQ_NONE; +} + +/* + * Generic no controller code + */ + +static void no_irq_enable_disable(unsigned int irq) { } +static unsigned int no_irq_startup(unsigned int irq) { return 0; } + +static void +no_irq_ack(unsigned int irq) +{ + irq_err_count++; + printk(KERN_CRIT "Unexpected IRQ trap at vector %u\n", irq); +} + +struct hw_interrupt_type no_irq_type = { + .typename = "none", + .startup = no_irq_startup, + .shutdown = no_irq_enable_disable, + .enable = no_irq_enable_disable, + .disable = no_irq_enable_disable, + .ack = no_irq_ack, + .end = no_irq_enable_disable, +}; + +int +handle_IRQ_event(unsigned int irq, struct pt_regs *regs, + struct irqaction *action) +{ + int status = 1; /* Force the "do bottom halves" bit */ + int ret; + + do { + if (!(action->flags & SA_INTERRUPT)) + local_irq_enable(); + else + local_irq_disable(); + + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + local_irq_disable(); + + return status; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ +void inline +disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * Synchronous version of the above, making sure the IRQ is + * no longer running on any other IRQ.. + */ +void +disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + synchronize_irq(irq); +} + +void +enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk(KERN_ERR "enable_irq() unbalanced from %p\n", + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +int +setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + irq_desc_t *desc = irq_desc + irq; + + if (desc->handler == &no_irq_type) + return -ENOSYS; + + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= + ~(IRQ_DISABLED|IRQ_AUTODETECT|IRQ_WAITING|IRQ_INPROGRESS); + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + return 0; +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir[NR_IRQS]; + +#ifdef CONFIG_SMP +static struct proc_dir_entry * smp_affinity_entry[NR_IRQS]; +static char irq_user_affinity[NR_IRQS]; +static cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL }; + +static void +select_smp_affinity(int irq) +{ + static int last_cpu; + int cpu = last_cpu + 1; + + if (! irq_desc[irq].handler->set_affinity || irq_user_affinity[irq]) + return; + + while (!cpu_possible(cpu)) + cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0); + last_cpu = cpu; + + irq_affinity[irq] = cpumask_of_cpu(cpu); + irq_desc[irq].handler->set_affinity(irq, cpumask_of_cpu(cpu)); +} + +static int +irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, irq_affinity[(long)data]); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int +irq_affinity_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + cpumask_t new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = cpumask_parse(buffer, count, new_value); + + /* The special value 0 means release control of the + affinity to kernel. */ + cpus_and(new_value, new_value, cpu_online_map); + if (cpus_empty(new_value)) { + irq_user_affinity[irq] = 0; + select_smp_affinity(irq); + } + /* Do not allow disabling IRQs completely - it's a too easy + way to make the system unusable accidentally :-) At least + one online CPU still has to be targeted. */ + else { + irq_affinity[irq] = new_value; + irq_user_affinity[irq] = 1; + irq_desc[irq].handler->set_affinity(irq, new_value); + } + + return full_count; +} + +#endif /* CONFIG_SMP */ + +#define MAX_NAMELEN 10 + +static void +register_irq_proc (unsigned int irq) +{ + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + +#ifdef CONFIG_SMP + if (irq_desc[irq].handler->set_affinity) { + struct proc_dir_entry *entry; + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + + smp_affinity_entry[irq] = entry; + } +#endif +} + +void +init_irq_proc (void) +{ + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", NULL); + +#ifdef CONFIG_SMP + /* create /proc/irq/prof_cpu_mask */ + create_prof_cpu_mask(root_irq_dir); +#endif + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < ACTUAL_NR_IRQS; i++) { + if (irq_desc[i].handler == &no_irq_type) + continue; + register_irq_proc(i); + } +} + +int +request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + int retval; + struct irqaction * action; + + if (irq >= ACTUAL_NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if ((irqflags & SA_SHIRQ) && !dev_id) { + printk(KERN_ERR + "Bad boy: %s (at %p) called us without a dev_id!\n", + devname, __builtin_return_address(0)); + } +#endif + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + cpus_clear(action->mask); + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + +#ifdef CONFIG_SMP + select_smp_affinity(irq); +#endif + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +EXPORT_SYMBOL(request_irq); + +void +free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= ACTUAL_NR_IRQS) { + printk(KERN_CRIT "Trying to free IRQ%d\n", irq); + return; + } + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found - now remove it from the list of entries. */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on + another CPU. */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + kfree(action); + return; + } + printk(KERN_ERR "Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +EXPORT_SYMBOL(free_irq); + +int +show_interrupts(struct seq_file *p, void *v) +{ +#ifdef CONFIG_SMP + int j; +#endif + int i = *(loff_t *) v; + struct irqaction * action; + unsigned long flags; + +#ifdef CONFIG_SMP + if (i == 0) { + seq_puts(p, " "); + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(i)) + seq_printf(p, "CPU%d ", i); + seq_putc(p, '\n'); + } +#endif + + if (i < ACTUAL_NR_IRQS) { + spin_lock_irqsave(&irq_desc[i].lock, flags); + action = irq_desc[i].action; + if (!action) + goto unlock; + seq_printf(p, "%3d: ",i); +#ifndef CONFIG_SMP + seq_printf(p, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif + seq_printf(p, " %14s", irq_desc[i].handler->typename); + seq_printf(p, " %c%s", + (action->flags & SA_INTERRUPT)?'+':' ', + action->name); + + for (action=action->next; action; action = action->next) { + seq_printf(p, ", %c%s", + (action->flags & SA_INTERRUPT)?'+':' ', + action->name); + } + + seq_putc(p, '\n'); +unlock: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == ACTUAL_NR_IRQS) { +#ifdef CONFIG_SMP + seq_puts(p, "IPI: "); + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(i)) + seq_printf(p, "%10lu ", cpu_data[i].ipi_count); + seq_putc(p, '\n'); +#endif + seq_printf(p, "ERR: %10lu\n", irq_err_count); + } + return 0; +} + + +/* + * handle_irq handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ + +#define MAX_ILLEGAL_IRQS 16 + +void +handle_irq(int irq, struct pt_regs * regs) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + unsigned int status; + static unsigned int illegal_count=0; + + if ((unsigned) irq > ACTUAL_NR_IRQS && illegal_count < MAX_ILLEGAL_IRQS ) { + irq_err_count++; + illegal_count++; + printk(KERN_CRIT "device_interrupt: invalid interrupt %d\n", + irq); + return; + } + + irq_enter(); + kstat_cpu(cpu).irqs[irq]++; + spin_lock_irq(&desc->lock); /* mask also the higher prio events */ + desc->handler->ack(irq); + /* + * REPLAY is when Linux resends an IRQ that was dropped earlier. + * WAITING is used by probe to mark irqs that are being tested. + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + * Since we set PENDING, if another processor is handling + * a different instance of this same irq, the other processor + * will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in handle_irq + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_IRQ_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING) + || (desc->status & IRQ_LEVEL)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + irq_exit(); +} + +/* + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_WAITING" cleared and the interrupt + * disabled. + */ +unsigned long +probe_irq_on(void) +{ + int i; + irq_desc_t *desc; + unsigned long delay; + unsigned long val; + + /* Something may have generated an irq long ago and we want to + flush such a longstanding irq before considering it as spurious. */ + for (i = NR_IRQS-1; i >= 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!irq_desc[i].action) + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } + + /* Wait for longstanding interrupts to trigger. */ + for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) + /* about 20ms delay */ barrier(); + + /* enable any unassigned irqs (we must startup again here because + if a longstanding irq happened in the previous stage, it may have + masked itself) first, enable any unassigned irqs. */ + for (i = NR_IRQS-1; i >= 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; + } + spin_unlock_irq(&desc->lock); + } + + /* + * Wait for spurious interrupts to trigger + */ + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ barrier(); + + /* + * Now filter out any obviously spurious interrupts + */ + val = 0; + for (i=0; i<NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; + } + spin_unlock_irq(&desc->lock); + } + + return val; +} + +EXPORT_SYMBOL(probe_irq_on); + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ +unsigned int +probe_irq_mask(unsigned long val) +{ + int i; + unsigned int mask; + + mask = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* We only react to ISA interrupts */ + if (!(status & IRQ_WAITING)) { + if (i < 16) + mask |= 1 << i; + } + + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + + return mask & val; +} + +/* + * Get the result of the IRQ probe.. A negative result means that + * we have several candidates (but we return the lowest-numbered + * one). + */ + +int +probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; + + nr_irqs = 0; + irq_found = 0; + for (i=0; i<NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +EXPORT_SYMBOL(probe_irq_off); + +#ifdef CONFIG_SMP +void synchronize_irq(unsigned int irq) +{ + /* is there anything to synchronize with? */ + if (!irq_desc[irq].action) + return; + + while (irq_desc[irq].status & IRQ_INPROGRESS) + barrier(); +} +#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 @@ +/* + * Alpha specific irq code. + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/irq.h> +#include <linux/kernel_stat.h> + +#include <asm/machvec.h> +#include <asm/dma.h> + +#include "proto.h" +#include "irq_impl.h" + +/* Hack minimum IPL during interrupt processing for broken hardware. */ +#ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK +int __min_ipl; +#endif + +/* + * Performance counter hook. A module can override this to + * do something useful. + */ +static void +dummy_perf(unsigned long vector, struct pt_regs *regs) +{ + irq_err_count++; + printk(KERN_CRIT "Performance counter interrupt!\n"); +} + +void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf; + +/* + * The main interrupt entry point. + */ + +asmlinkage void +do_entInt(unsigned long type, unsigned long vector, + unsigned long la_ptr, struct pt_regs *regs) +{ + switch (type) { + case 0: +#ifdef CONFIG_SMP + handle_ipi(regs); + return; +#else + irq_err_count++; + printk(KERN_CRIT "Interprocessor interrupt? " + "You must be kidding!\n"); +#endif + break; + case 1: +#ifdef CONFIG_SMP + { + long cpu; + smp_percpu_timer_interrupt(regs); + cpu = smp_processor_id(); + if (cpu != boot_cpuid) { + kstat_cpu(cpu).irqs[RTC_IRQ]++; + } else { + handle_irq(RTC_IRQ, regs); + } + } +#else + handle_irq(RTC_IRQ, regs); +#endif + return; + case 2: + alpha_mv.machine_check(vector, la_ptr, regs); + return; + case 3: + alpha_mv.device_interrupt(vector, regs); + return; + case 4: + perf_irq(la_ptr, regs); + return; + default: + printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n", + type, vector); + } + printk(KERN_CRIT "PC = %016lx PS=%04lx\n", regs->pc, regs->ps); +} + +void __init +common_init_isa_dma(void) +{ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(0, DMA1_CLR_MASK_REG); + outb(0, DMA2_CLR_MASK_REG); +} + +void __init +init_IRQ(void) +{ + /* Just in case the platform init_irq() causes interrupts/mchecks + (as is the case with RAWHIDE, at least). */ + wrent(entInt, 0); + + alpha_mv.init_irq(); +} + +/* + * machine error checks + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_OS_BUGCHECK 0x008A +#define MCHK_K_PAL_BUGCHECK 0x0090 + +#ifndef CONFIG_SMP +struct mcheck_info __mcheck_info; +#endif + +void +process_mcheck_info(unsigned long vector, unsigned long la_ptr, + struct pt_regs *regs, const char *machine, + int expected) +{ + struct el_common *mchk_header; + const char *reason; + + /* + * See if the machine check is due to a badaddr() and if so, + * ignore it. + */ + +#ifdef CONFIG_VERBOSE_MCHECK + if (alpha_verbose_mcheck > 1) { + printk(KERN_CRIT "%s machine check %s\n", machine, + expected ? "expected." : "NOT expected!!!"); + } +#endif + + if (expected) { + int cpu = smp_processor_id(); + mcheck_expected(cpu) = 0; + mcheck_taken(cpu) = 1; + return; + } + + mchk_header = (struct el_common *)la_ptr; + + printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%x\n", + machine, vector, regs->pc, mchk_header->code); + + switch (mchk_header->code) { + /* Machine check reasons. Defined according to PALcode sources. */ + case 0x80: reason = "tag parity error"; break; + case 0x82: reason = "tag control parity error"; break; + case 0x84: reason = "generic hard error"; break; + case 0x86: reason = "correctable ECC error"; break; + case 0x88: reason = "uncorrectable ECC error"; break; + case 0x8A: reason = "OS-specific PAL bugcheck"; break; + case 0x90: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; + + /* System specific (these are for Alcor, at least): */ + case 0x202: reason = "system detected hard error"; break; + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x204: reason = "SIO SERR occurred on PCI bus"; break; + case 0x205: reason = "parity error detected by core logic"; break; + case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x208: reason = "MCHK_K_DCSR"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: reason = "unknown"; break; + } + + printk(KERN_CRIT "machine check type: %s%s\n", + reason, mchk_header->retry ? " (retryable)" : ""); + + dik_show_regs(regs, NULL); + +#ifdef CONFIG_VERBOSE_MCHECK + if (alpha_verbose_mcheck > 1) { + /* Dump the logout area to give all info. */ + unsigned long *ptr = (unsigned long *)la_ptr; + long i; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif /* CONFIG_VERBOSE_MCHECK */ +} + +/* + * The special RTC interrupt type. The interrupt itself was + * processed by PALcode, and comes in via entInt vector 1. + */ + +static void rtc_enable_disable(unsigned int irq) { } +static unsigned int rtc_startup(unsigned int irq) { return 0; } + +struct irqaction timer_irqaction = { + .handler = timer_interrupt, + .flags = SA_INTERRUPT, + .name = "timer", +}; + +static struct hw_interrupt_type rtc_irq_type = { + .typename = "RTC", + .startup = rtc_startup, + .shutdown = rtc_enable_disable, + .enable = rtc_enable_disable, + .disable = rtc_enable_disable, + .ack = rtc_enable_disable, + .end = rtc_enable_disable, +}; + +void __init +init_rtc_irq(void) +{ + irq_desc[RTC_IRQ].status = IRQ_DISABLED; + irq_desc[RTC_IRQ].handler = &rtc_irq_type; + setup_irq(RTC_IRQ, &timer_irqaction); +} + +/* Dummy irqactions. */ +struct irqaction isa_cascade_irqaction = { + .handler = no_action, + .name = "isa-cascade" +}; + +struct irqaction timer_cascade_irqaction = { + .handler = no_action, + .name = "timer-cascade" +}; + +struct irqaction halt_switch_irqaction = { + .handler = no_action, + .name = "halt-switch" +}; 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 @@ +/* + * linux/arch/alpha/kernel/irq_i8259.c + * + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. + * + * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c. + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/cache.h> +#include <linux/sched.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +#include <asm/io.h> + +#include "proto.h" +#include "irq_impl.h" + + +/* Note mask bit is true for DISABLED irqs. */ +static unsigned int cached_irq_mask = 0xffff; +static DEFINE_SPINLOCK(i8259_irq_lock); + +static inline void +i8259_update_irq_hw(unsigned int irq, unsigned long mask) +{ + int port = 0x21; + if (irq & 8) mask >>= 8; + if (irq & 8) port = 0xA1; + outb(mask, port); +} + +inline void +i8259a_enable_irq(unsigned int irq) +{ + spin_lock(&i8259_irq_lock); + i8259_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); + spin_unlock(&i8259_irq_lock); +} + +static inline void +__i8259a_disable_irq(unsigned int irq) +{ + i8259_update_irq_hw(irq, cached_irq_mask |= 1 << irq); +} + +void +i8259a_disable_irq(unsigned int irq) +{ + spin_lock(&i8259_irq_lock); + __i8259a_disable_irq(irq); + spin_unlock(&i8259_irq_lock); +} + +void +i8259a_mask_and_ack_irq(unsigned int irq) +{ + spin_lock(&i8259_irq_lock); + __i8259a_disable_irq(irq); + + /* Ack the interrupt making it the lowest priority. */ + if (irq >= 8) { + outb(0xE0 | (irq - 8), 0xa0); /* ack the slave */ + irq = 2; + } + outb(0xE0 | irq, 0x20); /* ack the master */ + spin_unlock(&i8259_irq_lock); +} + +unsigned int +i8259a_startup_irq(unsigned int irq) +{ + i8259a_enable_irq(irq); + return 0; /* never anything pending */ +} + +void +i8259a_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + i8259a_enable_irq(irq); +} + +struct hw_interrupt_type i8259a_irq_type = { + .typename = "XT-PIC", + .startup = i8259a_startup_irq, + .shutdown = i8259a_disable_irq, + .enable = i8259a_enable_irq, + .disable = i8259a_disable_irq, + .ack = i8259a_mask_and_ack_irq, + .end = i8259a_end_irq, +}; + +void __init +init_i8259a_irqs(void) +{ + static struct irqaction cascade = { + .handler = no_action, + .name = "cascade", + }; + + long i; + + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-2 */ + + for (i = 0; i < 16; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].handler = &i8259a_irq_type; + } + + setup_irq(2, &cascade); +} + + +#if defined(CONFIG_ALPHA_GENERIC) +# define IACK_SC alpha_mv.iack_sc +#elif defined(CONFIG_ALPHA_APECS) +# define IACK_SC APECS_IACK_SC +#elif defined(CONFIG_ALPHA_LCA) +# define IACK_SC LCA_IACK_SC +#elif defined(CONFIG_ALPHA_CIA) +# define IACK_SC CIA_IACK_SC +#elif defined(CONFIG_ALPHA_PYXIS) +# define IACK_SC PYXIS_IACK_SC +#elif defined(CONFIG_ALPHA_TITAN) +# define IACK_SC TITAN_IACK_SC +#elif defined(CONFIG_ALPHA_TSUNAMI) +# define IACK_SC TSUNAMI_IACK_SC +#elif defined(CONFIG_ALPHA_IRONGATE) +# define IACK_SC IRONGATE_IACK_SC +#endif +/* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since + sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ + +#if defined(IACK_SC) +void +isa_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + /* + * Generate a PCI interrupt acknowledge cycle. The PIC will + * respond with the interrupt vector of the highest priority + * interrupt that is pending. The PALcode sets up the + * interrupts vectors such that irq level L generates vector L. + */ + int j = *(vuip) IACK_SC; + j &= 0xff; + handle_irq(j, regs); +} +#endif + +#if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) +void +isa_no_iack_sc_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pic; + + /* + * It seems to me that the probability of two or more *device* + * interrupts occurring at almost exactly the same time is + * pretty low. So why pay the price of checking for + * additional interrupts here if the common case can be + * handled so much easier? + */ + /* + * The first read of gives you *all* interrupting lines. + * Therefore, read the mask register and and out those lines + * not enabled. Note that some documentation has 21 and a1 + * write only. This is not true. + */ + pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ + pic &= 0xFFFB; /* mask out cascade & hibits */ + + while (pic) { + int j = ffz(~pic); + pic &= pic - 1; + handle_irq(j, regs); + } +} +#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 @@ +/* + * linux/arch/alpha/kernel/irq_impl.h + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 1998, 2000 Richard Henderson + * + * This file contains declarations and inline functions for interfacing + * with the IRQ handling routines in irq.c. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/profile.h> + + +#define RTC_IRQ 8 + +extern void isa_device_interrupt(unsigned long, struct pt_regs *); +extern void isa_no_iack_sc_device_interrupt(unsigned long, struct pt_regs *); +extern void srm_device_interrupt(unsigned long, struct pt_regs *); +extern void pyxis_device_interrupt(unsigned long, struct pt_regs *); + +extern struct irqaction timer_irqaction; +extern struct irqaction isa_cascade_irqaction; +extern struct irqaction timer_cascade_irqaction; +extern struct irqaction halt_switch_irqaction; + +extern void init_srm_irqs(long, unsigned long); +extern void init_pyxis_irqs(unsigned long); +extern void init_rtc_irq(void); + +extern void common_init_isa_dma(void); + +extern void i8259a_enable_irq(unsigned int); +extern void i8259a_disable_irq(unsigned int); +extern void i8259a_mask_and_ack_irq(unsigned int); +extern unsigned int i8259a_startup_irq(unsigned int); +extern void i8259a_end_irq(unsigned int); +extern struct hw_interrupt_type i8259a_irq_type; +extern void init_i8259a_irqs(void); + +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 @@ +/* + * linux/arch/alpha/kernel/irq_pyxis.c + * + * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). + * + * IRQ Code common to all PYXIS core logic chips. + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/irq.h> + +#include <asm/io.h> +#include <asm/core_cia.h> + +#include "proto.h" +#include "irq_impl.h" + + +/* Note mask bit is true for ENABLED irqs. */ +static unsigned long cached_irq_mask; + +static inline void +pyxis_update_irq_hw(unsigned long mask) +{ + *(vulp)PYXIS_INT_MASK = mask; + mb(); + *(vulp)PYXIS_INT_MASK; +} + +static inline void +pyxis_enable_irq(unsigned int irq) +{ + pyxis_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); +} + +static void +pyxis_disable_irq(unsigned int irq) +{ + pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); +} + +static unsigned int +pyxis_startup_irq(unsigned int irq) +{ + pyxis_enable_irq(irq); + return 0; +} + +static void +pyxis_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + pyxis_enable_irq(irq); +} + +static void +pyxis_mask_and_ack_irq(unsigned int irq) +{ + unsigned long bit = 1UL << (irq - 16); + unsigned long mask = cached_irq_mask &= ~bit; + + /* Disable the interrupt. */ + *(vulp)PYXIS_INT_MASK = mask; + wmb(); + /* Ack PYXIS PCI interrupt. */ + *(vulp)PYXIS_INT_REQ = bit; + mb(); + /* Re-read to force both writes. */ + *(vulp)PYXIS_INT_MASK; +} + +static struct hw_interrupt_type pyxis_irq_type = { + .typename = "PYXIS", + .startup = pyxis_startup_irq, + .shutdown = pyxis_disable_irq, + .enable = pyxis_enable_irq, + .disable = pyxis_disable_irq, + .ack = pyxis_mask_and_ack_irq, + .end = pyxis_end_irq, +}; + +void +pyxis_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pld; + unsigned int i; + + /* Read the interrupt summary register of PYXIS */ + pld = *(vulp)PYXIS_INT_REQ; + pld &= cached_irq_mask; + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 7) + isa_device_interrupt(vector, regs); + else + handle_irq(16+i, regs); + } +} + +void __init +init_pyxis_irqs(unsigned long ignore_mask) +{ + long i; + + *(vulp)PYXIS_INT_MASK = 0; /* disable all */ + *(vulp)PYXIS_INT_REQ = -1; /* flush all */ + mb(); + + /* Send -INTA pulses to clear any pending interrupts ...*/ + *(vuip) CIA_IACK_SC; + + for (i = 16; i < 48; ++i) { + if ((ignore_mask >> i) & 1) + continue; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; + irq_desc[i].handler = &pyxis_irq_type; + } + + setup_irq(16+7, &isa_cascade_irqaction); +} 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 @@ +/* + * Handle interrupts from the SRM, assuming no additional weirdness. + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/irq.h> + +#include "proto.h" +#include "irq_impl.h" + + +/* + * Is the palcode SMP safe? In other words: can we call cserve_ena/dis + * at the same time in multiple CPUs? To be safe I added a spinlock + * but it can be removed trivially if the palcode is robust against smp. + */ +DEFINE_SPINLOCK(srm_irq_lock); + +static inline void +srm_enable_irq(unsigned int irq) +{ + spin_lock(&srm_irq_lock); + cserve_ena(irq - 16); + spin_unlock(&srm_irq_lock); +} + +static void +srm_disable_irq(unsigned int irq) +{ + spin_lock(&srm_irq_lock); + cserve_dis(irq - 16); + spin_unlock(&srm_irq_lock); +} + +static unsigned int +srm_startup_irq(unsigned int irq) +{ + srm_enable_irq(irq); + return 0; +} + +static void +srm_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + srm_enable_irq(irq); +} + +/* Handle interrupts from the SRM, assuming no additional weirdness. */ +static struct hw_interrupt_type srm_irq_type = { + .typename = "SRM", + .startup = srm_startup_irq, + .shutdown = srm_disable_irq, + .enable = srm_enable_irq, + .disable = srm_disable_irq, + .ack = srm_disable_irq, + .end = srm_end_irq, +}; + +void __init +init_srm_irqs(long max, unsigned long ignore_mask) +{ + long i; + + for (i = 16; i < max; ++i) { + if (i < 64 && ((ignore_mask >> i) & 1)) + continue; + irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; + irq_desc[i].handler = &srm_irq_type; + } +} + +void +srm_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + int irq = (vector - 0x800) >> 4; + handle_irq(irq, regs); +} 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 @@ +/* + * linux/arch/alpha/kernel/machvec.h + * + * Copyright (C) 1997, 1998 Richard Henderson + * + * This file has goodies to help simplify instantiation of machine vectors. + */ + +#include <linux/config.h> +#include <asm/pgalloc.h> + +/* Whee. These systems don't have an HAE: + IRONGATE, MARVEL, POLARIS, TSUNAMI, TITAN, WILDFIRE + Fix things up for the GENERIC kernel by defining the HAE address + to be that of the cache. Now we can read and write it as we like. ;-) */ +#define IRONGATE_HAE_ADDRESS (&alpha_mv.hae_cache) +#define MARVEL_HAE_ADDRESS (&alpha_mv.hae_cache) +#define POLARIS_HAE_ADDRESS (&alpha_mv.hae_cache) +#define TSUNAMI_HAE_ADDRESS (&alpha_mv.hae_cache) +#define TITAN_HAE_ADDRESS (&alpha_mv.hae_cache) +#define WILDFIRE_HAE_ADDRESS (&alpha_mv.hae_cache) + +#ifdef CIA_ONE_HAE_WINDOW +#define CIA_HAE_ADDRESS (&alpha_mv.hae_cache) +#endif +#ifdef MCPCIA_ONE_HAE_WINDOW +#define MCPCIA_HAE_ADDRESS (&alpha_mv.hae_cache) +#endif + +/* Only a few systems don't define IACK_SC, handling all interrupts through + the SRM console. But splitting out that one case from IO() below + seems like such a pain. Define this to get things to compile. */ +#define JENSEN_IACK_SC 1 +#define T2_IACK_SC 1 +#define WILDFIRE_IACK_SC 1 /* FIXME */ + +/* + * Some helpful macros for filling in the blanks. + */ + +#define CAT1(x,y) x##y +#define CAT(x,y) CAT1(x,y) + +#define DO_DEFAULT_RTC rtc_port: 0x70 + +#define DO_EV4_MMU \ + .max_asn = EV4_MAX_ASN, \ + .mv_switch_mm = ev4_switch_mm, \ + .mv_activate_mm = ev4_activate_mm, \ + .mv_flush_tlb_current = ev4_flush_tlb_current, \ + .mv_flush_tlb_current_page = ev4_flush_tlb_current_page + +#define DO_EV5_MMU \ + .max_asn = EV5_MAX_ASN, \ + .mv_switch_mm = ev5_switch_mm, \ + .mv_activate_mm = ev5_activate_mm, \ + .mv_flush_tlb_current = ev5_flush_tlb_current, \ + .mv_flush_tlb_current_page = ev5_flush_tlb_current_page + +#define DO_EV6_MMU \ + .max_asn = EV6_MAX_ASN, \ + .mv_switch_mm = ev5_switch_mm, \ + .mv_activate_mm = ev5_activate_mm, \ + .mv_flush_tlb_current = ev5_flush_tlb_current, \ + .mv_flush_tlb_current_page = ev5_flush_tlb_current_page + +#define DO_EV7_MMU \ + .max_asn = EV6_MAX_ASN, \ + .mv_switch_mm = ev5_switch_mm, \ + .mv_activate_mm = ev5_activate_mm, \ + .mv_flush_tlb_current = ev5_flush_tlb_current, \ + .mv_flush_tlb_current_page = ev5_flush_tlb_current_page + +#define IO_LITE(UP,low) \ + .hae_register = (unsigned long *) CAT(UP,_HAE_ADDRESS), \ + .iack_sc = CAT(UP,_IACK_SC), \ + .mv_ioread8 = CAT(low,_ioread8), \ + .mv_ioread16 = CAT(low,_ioread16), \ + .mv_ioread32 = CAT(low,_ioread32), \ + .mv_iowrite8 = CAT(low,_iowrite8), \ + .mv_iowrite16 = CAT(low,_iowrite16), \ + .mv_iowrite32 = CAT(low,_iowrite32), \ + .mv_readb = CAT(low,_readb), \ + .mv_readw = CAT(low,_readw), \ + .mv_readl = CAT(low,_readl), \ + .mv_readq = CAT(low,_readq), \ + .mv_writeb = CAT(low,_writeb), \ + .mv_writew = CAT(low,_writew), \ + .mv_writel = CAT(low,_writel), \ + .mv_writeq = CAT(low,_writeq), \ + .mv_ioportmap = CAT(low,_ioportmap), \ + .mv_ioremap = CAT(low,_ioremap), \ + .mv_iounmap = CAT(low,_iounmap), \ + .mv_is_ioaddr = CAT(low,_is_ioaddr), \ + .mv_is_mmio = CAT(low,_is_mmio) \ + +#define IO(UP,low) \ + IO_LITE(UP,low), \ + .pci_ops = &CAT(low,_pci_ops), \ + .mv_pci_tbi = CAT(low,_pci_tbi) + +#define DO_APECS_IO IO(APECS,apecs) +#define DO_CIA_IO IO(CIA,cia) +#define DO_IRONGATE_IO IO(IRONGATE,irongate) +#define DO_LCA_IO IO(LCA,lca) +#define DO_MARVEL_IO IO(MARVEL,marvel) +#define DO_MCPCIA_IO IO(MCPCIA,mcpcia) +#define DO_POLARIS_IO IO(POLARIS,polaris) +#define DO_T2_IO IO(T2,t2) +#define DO_TSUNAMI_IO IO(TSUNAMI,tsunami) +#define DO_TITAN_IO IO(TITAN,titan) +#define DO_WILDFIRE_IO IO(WILDFIRE,wildfire) + +#define DO_PYXIS_IO IO_LITE(CIA,cia_bwx), \ + .pci_ops = &cia_pci_ops, \ + .mv_pci_tbi = cia_pci_tbi + +/* + * In a GENERIC kernel, we have lots of these vectors floating about, + * all but one of which we want to go away. In a non-GENERIC kernel, + * we want only one, ever. + * + * Accomplish this in the GENERIC kernel by putting all of the vectors + * in the .init.data section where they'll go away. We'll copy the + * one we want to the real alpha_mv vector in setup_arch. + * + * Accomplish this in a non-GENERIC kernel by ifdef'ing out all but + * one of the vectors, which will not reside in .init.data. We then + * alias this one vector to alpha_mv, so no copy is needed. + * + * Upshot: set __initdata to nothing for non-GENERIC kernels. + */ + +#ifdef CONFIG_ALPHA_GENERIC +#define __initmv __initdata +#define ALIAS_MV(x) +#else +#define __initmv + +/* GCC actually has a syntax for defining aliases, but is under some + delusion that you shouldn't be able to declare it extern somewhere + else beforehand. Fine. We'll do it ourselves. */ +#if 0 +#define ALIAS_MV(system) \ + struct alpha_machine_vector alpha_mv __attribute__((alias(#system "_mv"))); +#else +#define ALIAS_MV(system) \ + asm(".global alpha_mv\nalpha_mv = " #system "_mv"); +#endif +#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 @@ +/* Kernel module help for Alpha. + Copyright (C) 2002 Richard Henderson. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt...) +#endif + +void * +module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + +void +module_free(struct module *mod, void *module_region) +{ + vfree(module_region); +} + +/* Allocate the GOT at the end of the core sections. */ + +struct got_entry { + struct got_entry *next; + Elf64_Addr r_offset; + int got_offset; +}; + +static inline void +process_reloc_for_got(Elf64_Rela *rela, + struct got_entry *chains, Elf64_Xword *poffset) +{ + unsigned long r_sym = ELF64_R_SYM (rela->r_info); + unsigned long r_type = ELF64_R_TYPE (rela->r_info); + Elf64_Addr r_offset = rela->r_offset; + struct got_entry *g; + + if (r_type != R_ALPHA_LITERAL) + return; + + for (g = chains + r_sym; g ; g = g->next) + if (g->r_offset == r_offset) { + if (g->got_offset == 0) { + g->got_offset = *poffset; + *poffset += 8; + } + goto found_entry; + } + + g = kmalloc (sizeof (*g), GFP_KERNEL); + g->next = chains[r_sym].next; + g->r_offset = r_offset; + g->got_offset = *poffset; + *poffset += 8; + chains[r_sym].next = g; + + found_entry: + /* Trick: most of the ELF64_R_TYPE field is unused. There are + 42 valid relocation types, and a 32-bit field. Co-opt the + bits above 256 to store the got offset for this reloc. */ + rela->r_info |= g->got_offset << 8; +} + +int +module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs, + char *secstrings, struct module *me) +{ + struct got_entry *chains; + Elf64_Rela *rela; + Elf64_Shdr *esechdrs, *symtab, *s, *got; + unsigned long nsyms, nrela, i; + + esechdrs = sechdrs + hdr->e_shnum; + symtab = got = NULL; + + /* Find out how large the symbol table is. Allocate one got_entry + head per symbol. Normally this will be enough, but not always. + We'll chain different offsets for the symbol down each head. */ + for (s = sechdrs; s < esechdrs; ++s) + if (s->sh_type == SHT_SYMTAB) + symtab = s; + else if (!strcmp(".got", secstrings + s->sh_name)) { + got = s; + me->arch.gotsecindex = s - sechdrs; + } + + if (!symtab) { + printk(KERN_ERR "module %s: no symbol table\n", me->name); + return -ENOEXEC; + } + if (!got) { + printk(KERN_ERR "module %s: no got section\n", me->name); + return -ENOEXEC; + } + + nsyms = symtab->sh_size / sizeof(Elf64_Sym); + chains = kmalloc(nsyms * sizeof(struct got_entry), GFP_KERNEL); + memset(chains, 0, nsyms * sizeof(struct got_entry)); + + got->sh_size = 0; + got->sh_addralign = 8; + got->sh_type = SHT_NOBITS; + + /* Examine all LITERAL relocations to find out what GOT entries + are required. This sizes the GOT section as well. */ + for (s = sechdrs; s < esechdrs; ++s) + if (s->sh_type == SHT_RELA) { + nrela = s->sh_size / sizeof(Elf64_Rela); + rela = (void *)hdr + s->sh_offset; + for (i = 0; i < nrela; ++i) + process_reloc_for_got(rela+i, chains, + &got->sh_size); + } + + /* Free the memory we allocated. */ + for (i = 0; i < nsyms; ++i) { + struct got_entry *g, *n; + for (g = chains[i].next; g ; g = n) { + n = g->next; + kfree(g); + } + } + kfree(chains); + + return 0; +} + +int +apply_relocate(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, + unsigned int relsec, struct module *me) +{ + printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); + return -ENOEXEC; +} + +int +apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *me) +{ + Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr; + unsigned long i, n = sechdrs[relsec].sh_size / sizeof(*rela); + Elf64_Sym *symtab, *sym; + void *base, *location; + unsigned long got, gp; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + base = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr; + symtab = (Elf64_Sym *)sechdrs[symindex].sh_addr; + + /* The small sections were sorted to the end of the segment. + The following should definitely cover them. */ + gp = (u64)me->module_core + me->core_size - 0x8000; + got = sechdrs[me->arch.gotsecindex].sh_addr; + + for (i = 0; i < n; i++) { + unsigned long r_sym = ELF64_R_SYM (rela[i].r_info); + unsigned long r_type = ELF64_R_TYPE (rela[i].r_info); + unsigned long r_got_offset = r_type >> 8; + unsigned long value, hi, lo; + r_type &= 0xff; + + /* This is where to make the change. */ + location = base + rela[i].r_offset; + + /* This is the symbol it is referring to. Note that all + unresolved symbols have been resolved. */ + sym = symtab + r_sym; + value = sym->st_value + rela[i].r_addend; + + switch (r_type) { + case R_ALPHA_NONE: + break; + case R_ALPHA_REFQUAD: + /* BUG() can produce misaligned relocations. */ + ((u32 *)location)[0] = value; + ((u32 *)location)[1] = value >> 32; + break; + case R_ALPHA_GPREL32: + value -= gp; + if ((int)value != value) + goto reloc_overflow; + *(u32 *)location = value; + break; + case R_ALPHA_LITERAL: + hi = got + r_got_offset; + lo = hi - gp; + if ((short)lo != lo) + goto reloc_overflow; + *(u16 *)location = lo; + *(u64 *)hi = value; + break; + case R_ALPHA_LITUSE: + break; + case R_ALPHA_GPDISP: + value = gp - (u64)location; + lo = (short)value; + hi = (int)(value - lo); + if (hi + lo != value) + goto reloc_overflow; + *(u16 *)location = hi >> 16; + *(u16 *)(location + rela[i].r_addend) = lo; + break; + case R_ALPHA_BRSGP: + /* BRSGP is only allowed to bind to local symbols. + If the section is undef, this means that the + value was resolved from somewhere else. */ + if (sym->st_shndx == SHN_UNDEF) + goto reloc_overflow; + if ((sym->st_other & STO_ALPHA_STD_GPLOAD) == + STO_ALPHA_STD_GPLOAD) + /* Omit the prologue. */ + value += 8; + /* FALLTHRU */ + case R_ALPHA_BRADDR: + value -= (u64)location + 4; + if (value & 3) + goto reloc_overflow; + value = (long)value >> 2; + if (value + (1<<21) >= 1<<22) + goto reloc_overflow; + value &= 0x1fffff; + value |= *(u32 *)location & ~0x1fffff; + *(u32 *)location = value; + break; + case R_ALPHA_HINT: + break; + case R_ALPHA_SREL32: + value -= (u64)location; + if ((int)value != value) + goto reloc_overflow; + *(u32 *)location = value; + break; + case R_ALPHA_SREL64: + value -= (u64)location; + *(u64 *)location = value; + break; + case R_ALPHA_GPRELHIGH: + value = (long)(value - gp + 0x8000) >> 16; + if ((short) value != value) + goto reloc_overflow; + *(u16 *)location = value; + break; + case R_ALPHA_GPRELLOW: + value -= gp; + *(u16 *)location = value; + break; + case R_ALPHA_GPREL16: + value -= gp; + if ((short) value != value) + goto reloc_overflow; + *(u16 *)location = value; + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %lu\n", + me->name, r_type); + return -ENOEXEC; + reloc_overflow: + if (ELF64_ST_TYPE (sym->st_info) == STT_SECTION) + printk(KERN_ERR + "module %s: Relocation overflow vs section %d\n", + me->name, sym->st_shndx); + else + printk(KERN_ERR + "module %s: Relocation overflow vs %s\n", + me->name, strtab + sym->st_name); + return -ENOEXEC; + } + } + + return 0; +} + +int +module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +void +module_arch_cleanup(struct module *mod) +{ +} 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 @@ +/* + * linux/arch/alpha/kernel/ns87312.c + */ + +#include <linux/init.h> +#include <asm/io.h> +#include "proto.h" + + +/* + * The SRM console *disables* the IDE interface, this code ensures it's + * enabled. + * + * This code bangs on a control register of the 87312 Super I/O chip + * that implements parallel port/serial ports/IDE/FDI. Depending on + * the motherboard, the Super I/O chip can be configured through a + * pair of registers that are located either at I/O ports 0x26e/0x26f + * or 0x398/0x399. Unfortunately, autodetecting which base address is + * in use works only once (right after a reset). The Super I/O chip + * has the additional quirk that configuration register data must be + * written twice (I believe this is a safety feature to prevent + * accidental modification---fun, isn't it?). + */ + +void __init +ns87312_enable_ide(long ide_base) +{ + int data; + unsigned long flags; + + local_irq_save(flags); + outb(0, ide_base); /* set the index register for reg #0 */ + data = inb(ide_base+1); /* read the current contents */ + outb(0, ide_base); /* set the index register for reg #0 */ + outb(data | 0x40, ide_base+1); /* turn on IDE */ + outb(data | 0x40, ide_base+1); /* turn on IDE, really! */ + local_irq_restore(flags); +} 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 @@ +/* + * linux/arch/alpha/kernel/osf_sys.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles some of the stranger OSF/1 system call interfaces. + * Some of the system calls expect a non-C calling standard, others have + * special parameter blocks.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/syscalls.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/utsname.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/major.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/shm.h> +#include <linux/poll.h> +#include <linux/file.h> +#include <linux/types.h> +#include <linux/ipc.h> +#include <linux/namei.h> +#include <linux/uio.h> +#include <linux/vfs.h> + +#include <asm/fpu.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/sysinfo.h> +#include <asm/hwrpb.h> +#include <asm/processor.h> + +extern int do_pipe(int *); + +/* + * Brk needs to return an error. Still support Linux's brk(0) query idiom, + * which OSF programs just shouldn't be doing. We're still not quite + * identical to OSF as we don't return 0 on success, but doing otherwise + * would require changes to libc. Hopefully this is good enough. + */ +asmlinkage unsigned long +osf_brk(unsigned long brk) +{ + unsigned long retval = sys_brk(brk); + if (brk && brk != retval) + retval = -ENOMEM; + return retval; +} + +/* + * This is pure guess-work.. + */ +asmlinkage int +osf_set_program_attributes(unsigned long text_start, unsigned long text_len, + unsigned long bss_start, unsigned long bss_len) +{ + struct mm_struct *mm; + + lock_kernel(); + mm = current->mm; + mm->end_code = bss_start + bss_len; + mm->brk = bss_start + bss_len; +#if 0 + printk("set_program_attributes(%lx %lx %lx %lx)\n", + text_start, text_len, bss_start, bss_len); +#endif + unlock_kernel(); + return 0; +} + +/* + * OSF/1 directory handling functions... + * + * The "getdents()" interface is much more sane: the "basep" stuff is + * braindamage (it can't really handle filesystems where the directory + * offset differences aren't the same as "d_reclen"). + */ +#define NAME_OFFSET offsetof (struct osf_dirent, d_name) +#define ROUND_UP(x) (((x)+3) & ~3) + +struct osf_dirent { + unsigned int d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[1]; +}; + +struct osf_dirent_callback { + struct osf_dirent __user *dirent; + long __user *basep; + unsigned int count; + int error; +}; + +static int +osf_filldir(void *__buf, const char *name, int namlen, loff_t offset, + ino_t ino, unsigned int d_type) +{ + struct osf_dirent __user *dirent; + struct osf_dirent_callback *buf = (struct osf_dirent_callback *) __buf; + unsigned int reclen = ROUND_UP(NAME_OFFSET + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail */ + if (reclen > buf->count) + return -EINVAL; + if (buf->basep) { + if (put_user(offset, buf->basep)) + return -EFAULT; + buf->basep = NULL; + } + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + if (copy_to_user(dirent->d_name, name, namlen) || + put_user(0, dirent->d_name + namlen)) + return -EFAULT; + dirent = (void __user *)dirent + reclen; + buf->dirent = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int +osf_getdirentries(unsigned int fd, struct osf_dirent __user *dirent, + unsigned int count, long __user *basep) +{ + int error; + struct file *file; + struct osf_dirent_callback buf; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.dirent = dirent; + buf.basep = basep; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, osf_filldir, &buf); + if (error < 0) + goto out_putf; + + error = buf.error; + if (count != buf.count) + error = count - buf.count; + + out_putf: + fput(file); + out: + return error; +} + +#undef ROUND_UP +#undef NAME_OFFSET + +asmlinkage unsigned long +osf_mmap(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long off) +{ + struct file *file = NULL; + unsigned long ret = -EBADF; + +#if 0 + if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED)) + printk("%s: unimplemented OSF mmap flags %04lx\n", + current->comm, flags); +#endif + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down_write(¤t->mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flags, off); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); + out: + return ret; +} + + +/* + * The OSF/1 statfs structure is much larger, but this should + * match the beginning, at least. + */ +struct osf_statfs { + short f_type; + short f_flags; + int f_fsize; + int f_bsize; + int f_blocks; + int f_bfree; + int f_bavail; + int f_files; + int f_ffree; + __kernel_fsid_t f_fsid; +}; + +static int +linux_to_osf_statfs(struct kstatfs *linux_stat, struct osf_statfs __user *osf_stat, + unsigned long bufsiz) +{ + struct osf_statfs tmp_stat; + + tmp_stat.f_type = linux_stat->f_type; + tmp_stat.f_flags = 0; /* mount flags */ + tmp_stat.f_fsize = linux_stat->f_frsize; + tmp_stat.f_bsize = linux_stat->f_bsize; + tmp_stat.f_blocks = linux_stat->f_blocks; + tmp_stat.f_bfree = linux_stat->f_bfree; + tmp_stat.f_bavail = linux_stat->f_bavail; + tmp_stat.f_files = linux_stat->f_files; + tmp_stat.f_ffree = linux_stat->f_ffree; + tmp_stat.f_fsid = linux_stat->f_fsid; + if (bufsiz > sizeof(tmp_stat)) + bufsiz = sizeof(tmp_stat); + return copy_to_user(osf_stat, &tmp_stat, bufsiz) ? -EFAULT : 0; +} + +static int +do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer, + unsigned long bufsiz) +{ + struct kstatfs linux_stat; + int error = vfs_statfs(dentry->d_inode->i_sb, &linux_stat); + if (!error) + error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz); + return error; +} + +asmlinkage int +osf_statfs(char __user *path, struct osf_statfs __user *buffer, unsigned long bufsiz) +{ + struct nameidata nd; + int retval; + + retval = user_path_walk(path, &nd); + if (!retval) { + retval = do_osf_statfs(nd.dentry, buffer, bufsiz); + path_release(&nd); + } + return retval; +} + +asmlinkage int +osf_fstatfs(unsigned long fd, struct osf_statfs __user *buffer, unsigned long bufsiz) +{ + struct file *file; + int retval; + + retval = -EBADF; + file = fget(fd); + if (file) { + retval = do_osf_statfs(file->f_dentry, buffer, bufsiz); + fput(file); + } + return retval; +} + +/* + * Uhh.. OSF/1 mount parameters aren't exactly obvious.. + * + * Although to be frank, neither are the native Linux/i386 ones.. + */ +struct ufs_args { + char __user *devname; + int flags; + uid_t exroot; +}; + +struct cdfs_args { + char __user *devname; + int flags; + uid_t exroot; + + /* This has lots more here, which Linux handles with the option block + but I'm too lazy to do the translation into ASCII. */ +}; + +struct procfs_args { + char __user *devname; + int flags; + uid_t exroot; +}; + +/* + * We can't actually handle ufs yet, so we translate UFS mounts to + * ext2fs mounts. I wouldn't mind a UFS filesystem, but the UFS + * layout is so braindead it's a major headache doing it. + * + * Just how long ago was it written? OTOH our UFS driver may be still + * unhappy with OSF UFS. [CHECKME] + */ +static int +osf_ufs_mount(char *dirname, struct ufs_args __user *args, int flags) +{ + int retval; + struct cdfs_args tmp; + char *devname; + + retval = -EFAULT; + if (copy_from_user(&tmp, args, sizeof(tmp))) + goto out; + devname = getname(tmp.devname); + retval = PTR_ERR(devname); + if (IS_ERR(devname)) + goto out; + retval = do_mount(devname, dirname, "ext2", flags, NULL); + putname(devname); + out: + return retval; +} + +static int +osf_cdfs_mount(char *dirname, struct cdfs_args __user *args, int flags) +{ + int retval; + struct cdfs_args tmp; + char *devname; + + retval = -EFAULT; + if (copy_from_user(&tmp, args, sizeof(tmp))) + goto out; + devname = getname(tmp.devname); + retval = PTR_ERR(devname); + if (IS_ERR(devname)) + goto out; + retval = do_mount(devname, dirname, "iso9660", flags, NULL); + putname(devname); + out: + return retval; +} + +static int +osf_procfs_mount(char *dirname, struct procfs_args __user *args, int flags) +{ + struct procfs_args tmp; + + if (copy_from_user(&tmp, args, sizeof(tmp))) + return -EFAULT; + + return do_mount("", dirname, "proc", flags, NULL); +} + +asmlinkage int +osf_mount(unsigned long typenr, char __user *path, int flag, void __user *data) +{ + int retval = -EINVAL; + char *name; + + lock_kernel(); + + name = getname(path); + retval = PTR_ERR(name); + if (IS_ERR(name)) + goto out; + switch (typenr) { + case 1: + retval = osf_ufs_mount(name, data, flag); + break; + case 6: + retval = osf_cdfs_mount(name, data, flag); + break; + case 9: + retval = osf_procfs_mount(name, data, flag); + break; + default: + printk("osf_mount(%ld, %x)\n", typenr, flag); + } + putname(name); + out: + unlock_kernel(); + return retval; +} + +asmlinkage int +osf_utsname(char __user *name) +{ + int error; + + down_read(&uts_sem); + error = -EFAULT; + if (copy_to_user(name + 0, system_utsname.sysname, 32)) + goto out; + if (copy_to_user(name + 32, system_utsname.nodename, 32)) + goto out; + if (copy_to_user(name + 64, system_utsname.release, 32)) + goto out; + if (copy_to_user(name + 96, system_utsname.version, 32)) + goto out; + if (copy_to_user(name + 128, system_utsname.machine, 32)) + goto out; + + error = 0; + out: + up_read(&uts_sem); + return error; +} + +asmlinkage unsigned long +sys_getpagesize(void) +{ + return PAGE_SIZE; +} + +asmlinkage unsigned long +sys_getdtablesize(void) +{ + return NR_OPEN; +} + +/* + * For compatibility with OSF/1 only. Use utsname(2) instead. + */ +asmlinkage int +osf_getdomainname(char __user *name, int namelen) +{ + unsigned len; + int i; + + if (!access_ok(VERIFY_WRITE, name, namelen)) + return -EFAULT; + + len = namelen; + if (namelen > 32) + len = 32; + + down_read(&uts_sem); + for (i = 0; i < len; ++i) { + __put_user(system_utsname.domainname[i], name + i); + if (system_utsname.domainname[i] == '\0') + break; + } + up_read(&uts_sem); + + return 0; +} + +asmlinkage long +osf_shmat(int shmid, void __user *shmaddr, int shmflg) +{ + unsigned long raddr; + long err; + + err = do_shmat(shmid, shmaddr, shmflg, &raddr); + + /* + * This works because all user-level addresses are + * non-negative longs! + */ + return err ? err : (long)raddr; +} + + +/* + * The following stuff should move into a header file should it ever + * be labeled "officially supported." Right now, there is just enough + * support to avoid applications (such as tar) printing error + * messages. The attributes are not really implemented. + */ + +/* + * Values for Property list entry flag + */ +#define PLE_PROPAGATE_ON_COPY 0x1 /* cp(1) will copy entry + by default */ +#define PLE_FLAG_MASK 0x1 /* Valid flag values */ +#define PLE_FLAG_ALL -1 /* All flag value */ + +struct proplistname_args { + unsigned int pl_mask; + unsigned int pl_numnames; + char **pl_names; +}; + +union pl_args { + struct setargs { + char __user *path; + long follow; + long nbytes; + char __user *buf; + } set; + struct fsetargs { + long fd; + long nbytes; + char __user *buf; + } fset; + struct getargs { + char __user *path; + long follow; + struct proplistname_args __user *name_args; + long nbytes; + char __user *buf; + int __user *min_buf_size; + } get; + struct fgetargs { + long fd; + struct proplistname_args __user *name_args; + long nbytes; + char __user *buf; + int __user *min_buf_size; + } fget; + struct delargs { + char __user *path; + long follow; + struct proplistname_args __user *name_args; + } del; + struct fdelargs { + long fd; + struct proplistname_args __user *name_args; + } fdel; +}; + +enum pl_code { + PL_SET = 1, PL_FSET = 2, + PL_GET = 3, PL_FGET = 4, + PL_DEL = 5, PL_FDEL = 6 +}; + +asmlinkage long +osf_proplist_syscall(enum pl_code code, union pl_args __user *args) +{ + long error; + int __user *min_buf_size_ptr; + + lock_kernel(); + switch (code) { + case PL_SET: + if (get_user(error, &args->set.nbytes)) + error = -EFAULT; + break; + case PL_FSET: + if (get_user(error, &args->fset.nbytes)) + error = -EFAULT; + break; + case PL_GET: + error = get_user(min_buf_size_ptr, &args->get.min_buf_size); + if (error) + break; + error = put_user(0, min_buf_size_ptr); + break; + case PL_FGET: + error = get_user(min_buf_size_ptr, &args->fget.min_buf_size); + if (error) + break; + error = put_user(0, min_buf_size_ptr); + break; + case PL_DEL: + case PL_FDEL: + error = 0; + break; + default: + error = -EOPNOTSUPP; + break; + }; + unlock_kernel(); + return error; +} + +asmlinkage int +osf_sigstack(struct sigstack __user *uss, struct sigstack __user *uoss) +{ + unsigned long usp = rdusp(); + unsigned long oss_sp = current->sas_ss_sp + current->sas_ss_size; + unsigned long oss_os = on_sig_stack(usp); + int error; + + if (uss) { + void __user *ss_sp; + + error = -EFAULT; + if (get_user(ss_sp, &uss->ss_sp)) + goto out; + + /* If the current stack was set with sigaltstack, don't + swap stacks while we are on it. */ + error = -EPERM; + if (current->sas_ss_sp && on_sig_stack(usp)) + goto out; + + /* Since we don't know the extent of the stack, and we don't + track onstack-ness, but rather calculate it, we must + presume a size. Ho hum this interface is lossy. */ + current->sas_ss_sp = (unsigned long)ss_sp - SIGSTKSZ; + current->sas_ss_size = SIGSTKSZ; + } + + if (uoss) { + error = -EFAULT; + if (! access_ok(VERIFY_WRITE, uoss, sizeof(*uoss)) + || __put_user(oss_sp, &uoss->ss_sp) + || __put_user(oss_os, &uoss->ss_onstack)) + goto out; + } + + error = 0; + out: + return error; +} + +asmlinkage long +osf_sysinfo(int command, char __user *buf, long count) +{ + static char * sysinfo_table[] = { + system_utsname.sysname, + system_utsname.nodename, + system_utsname.release, + system_utsname.version, + system_utsname.machine, + "alpha", /* instruction set architecture */ + "dummy", /* hardware serial number */ + "dummy", /* hardware manufacturer */ + "dummy", /* secure RPC domain */ + }; + unsigned long offset; + char *res; + long len, err = -EINVAL; + + offset = command-1; + if (offset >= sizeof(sysinfo_table)/sizeof(char *)) { + /* Digital UNIX has a few unpublished interfaces here */ + printk("sysinfo(%d)", command); + goto out; + } + + down_read(&uts_sem); + res = sysinfo_table[offset]; + len = strlen(res)+1; + if (len > count) + len = count; + if (copy_to_user(buf, res, len)) + err = -EFAULT; + else + err = 0; + up_read(&uts_sem); + out: + return err; +} + +asmlinkage unsigned long +osf_getsysinfo(unsigned long op, void __user *buffer, unsigned long nbytes, + int __user *start, void __user *arg) +{ + unsigned long w; + struct percpu_struct *cpu; + + switch (op) { + case GSI_IEEE_FP_CONTROL: + /* Return current software fp control & status bits. */ + /* Note that DU doesn't verify available space here. */ + + w = current_thread_info()->ieee_state & IEEE_SW_MASK; + w = swcr_update_status(w, rdfpcr()); + if (put_user(w, (unsigned long __user *) buffer)) + return -EFAULT; + return 0; + + case GSI_IEEE_STATE_AT_SIGNAL: + /* + * Not sure anybody will ever use this weird stuff. These + * ops can be used (under OSF/1) to set the fpcr that should + * be used when a signal handler starts executing. + */ + break; + + case GSI_UACPROC: + if (nbytes < sizeof(unsigned int)) + return -EINVAL; + w = (current_thread_info()->flags >> UAC_SHIFT) & UAC_BITMASK; + if (put_user(w, (unsigned int __user *)buffer)) + return -EFAULT; + return 1; + + case GSI_PROC_TYPE: + if (nbytes < sizeof(unsigned long)) + return -EINVAL; + cpu = (struct percpu_struct*) + ((char*)hwrpb + hwrpb->processor_offset); + w = cpu->type; + if (put_user(w, (unsigned long __user*)buffer)) + return -EFAULT; + return 1; + + case GSI_GET_HWRPB: + if (nbytes < sizeof(*hwrpb)) + return -EINVAL; + if (copy_to_user(buffer, hwrpb, nbytes) != 0) + return -EFAULT; + return 1; + + default: + break; + } + + return -EOPNOTSUPP; +} + +asmlinkage unsigned long +osf_setsysinfo(unsigned long op, void __user *buffer, unsigned long nbytes, + int __user *start, void __user *arg) +{ + switch (op) { + case SSI_IEEE_FP_CONTROL: { + unsigned long swcr, fpcr; + unsigned int *state; + + /* + * Alpha Architecture Handbook 4.7.7.3: + * To be fully IEEE compiant, we must track the current IEEE + * exception state in software, because spurrious bits can be + * set in the trap shadow of a software-complete insn. + */ + + if (get_user(swcr, (unsigned long __user *)buffer)) + return -EFAULT; + state = ¤t_thread_info()->ieee_state; + + /* Update softare trap enable bits. */ + *state = (*state & ~IEEE_SW_MASK) | (swcr & IEEE_SW_MASK); + + /* Update the real fpcr. */ + fpcr = rdfpcr() & FPCR_DYN_MASK; + fpcr |= ieee_swcr_to_fpcr(swcr); + wrfpcr(fpcr); + + return 0; + } + + case SSI_IEEE_RAISE_EXCEPTION: { + unsigned long exc, swcr, fpcr, fex; + unsigned int *state; + + if (get_user(exc, (unsigned long __user *)buffer)) + return -EFAULT; + state = ¤t_thread_info()->ieee_state; + exc &= IEEE_STATUS_MASK; + + /* Update softare trap enable bits. */ + swcr = (*state & IEEE_SW_MASK) | exc; + *state |= exc; + + /* Update the real fpcr. */ + fpcr = rdfpcr(); + fpcr |= ieee_swcr_to_fpcr(swcr); + wrfpcr(fpcr); + + /* If any exceptions set by this call, and are unmasked, + send a signal. Old exceptions are not signaled. */ + fex = (exc >> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr; + if (fex) { + siginfo_t info; + int si_code = 0; + + if (fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND; + if (fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES; + if (fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND; + if (fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF; + if (fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV; + if (fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV; + + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = si_code; + info.si_addr = NULL; /* FIXME */ + send_sig_info(SIGFPE, &info, current); + } + return 0; + } + + case SSI_IEEE_STATE_AT_SIGNAL: + case SSI_IEEE_IGNORE_STATE_AT_SIGNAL: + /* + * Not sure anybody will ever use this weird stuff. These + * ops can be used (under OSF/1) to set the fpcr that should + * be used when a signal handler starts executing. + */ + break; + + case SSI_NVPAIRS: { + unsigned long v, w, i; + unsigned int old, new; + + for (i = 0; i < nbytes; ++i) { + + if (get_user(v, 2*i + (unsigned int __user *)buffer)) + return -EFAULT; + if (get_user(w, 2*i + 1 + (unsigned int __user *)buffer)) + return -EFAULT; + switch (v) { + case SSIN_UACPROC: + again: + old = current_thread_info()->flags; + new = old & ~(UAC_BITMASK << UAC_SHIFT); + new = new | (w & UAC_BITMASK) << UAC_SHIFT; + if (cmpxchg(¤t_thread_info()->flags, + old, new) != old) + goto again; + break; + + default: + return -EOPNOTSUPP; + } + } + return 0; + } + + default: + break; + } + + return -EOPNOTSUPP; +} + +/* Translations due to the fact that OSF's time_t is an int. Which + affects all sorts of things, like timeval and itimerval. */ + +extern struct timezone sys_tz; +extern int do_adjtimex(struct timex *); + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long +get_tv32(struct timeval *o, struct timeval32 __user *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long +put_tv32(struct timeval32 __user *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + +static inline long +get_it32(struct itimerval *o, struct itimerval32 __user *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); +} + +static inline long +put_it32(struct itimerval32 __user *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); +} + +static inline void +jiffies_to_timeval32(unsigned long jiffies, struct timeval32 *value) +{ + value->tv_usec = (jiffies % HZ) * (1000000L / HZ); + value->tv_sec = jiffies / HZ; +} + +asmlinkage int +osf_gettimeofday(struct timeval32 __user *tv, struct timezone __user *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage int +osf_settimeofday(struct timeval32 __user *tv, struct timezone __user *tz) +{ + struct timespec kts; + struct timezone ktz; + + if (tv) { + if (get_tv32((struct timeval *)&kts, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(*tz))) + return -EFAULT; + } + + kts.tv_nsec *= 1000; + + return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); +} + +asmlinkage int +osf_getitimer(int which, struct itimerval32 __user *it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + return error; +} + +asmlinkage int +osf_setitimer(int which, struct itimerval32 __user *in, struct itimerval32 __user *out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + + if (put_it32(out, &kout)) + return -EFAULT; + + return 0; + +} + +asmlinkage int +osf_utimes(char __user *filename, struct timeval32 __user *tvs) +{ + struct timeval ktvs[2]; + + if (tvs) { + if (get_tv32(&ktvs[0], &tvs[0]) || + get_tv32(&ktvs[1], &tvs[1])) + return -EFAULT; + } + + return do_utimes(filename, tvs ? ktvs : NULL); +} + +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + +asmlinkage int +osf_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, + struct timeval32 __user *tvp) +{ + fd_set_bits fds; + char *bits; + size_t size; + long timeout; + int ret = -EINVAL; + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp) { + time_t sec, usec; + + if (!access_ok(VERIFY_READ, tvp, sizeof(*tvp)) + || __get_user(sec, &tvp->tv_sec) + || __get_user(usec, &tvp->tv_usec)) { + ret = -EFAULT; + goto out_nofds; + } + + if (sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + if (n < 0 || n > current->files->max_fdset) + goto out_nofds; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + if ((ret = get_fd_set(n, inp->fds_bits, fds.in)) || + (ret = get_fd_set(n, outp->fds_bits, fds.out)) || + (ret = get_fd_set(n, exp->fds_bits, fds.ex))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + /* OSF does not copy back the remaining time. */ + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + if (set_fd_set(n, inp->fds_bits, fds.res_in) || + set_fd_set(n, outp->fds_bits, fds.res_out) || + set_fd_set(n, exp->fds_bits, fds.res_ex)) + ret = -EFAULT; + + out: + kfree(bits); + out_nofds: + return ret; +} + +struct rusage32 { + struct timeval32 ru_utime; /* user time used */ + struct timeval32 ru_stime; /* system time used */ + long ru_maxrss; /* maximum resident set size */ + long ru_ixrss; /* integral shared memory size */ + long ru_idrss; /* integral unshared data size */ + long ru_isrss; /* integral unshared stack size */ + long ru_minflt; /* page reclaims */ + long ru_majflt; /* page faults */ + long ru_nswap; /* swaps */ + long ru_inblock; /* block input operations */ + long ru_oublock; /* block output operations */ + long ru_msgsnd; /* messages sent */ + long ru_msgrcv; /* messages received */ + long ru_nsignals; /* signals received */ + long ru_nvcsw; /* voluntary context switches */ + long ru_nivcsw; /* involuntary " */ +}; + +asmlinkage int +osf_getrusage(int who, struct rusage32 __user *ru) +{ + struct rusage32 r; + + if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN) + return -EINVAL; + + memset(&r, 0, sizeof(r)); + switch (who) { + case RUSAGE_SELF: + jiffies_to_timeval32(current->utime, &r.ru_utime); + jiffies_to_timeval32(current->stime, &r.ru_stime); + r.ru_minflt = current->min_flt; + r.ru_majflt = current->maj_flt; + break; + case RUSAGE_CHILDREN: + jiffies_to_timeval32(current->signal->cutime, &r.ru_utime); + jiffies_to_timeval32(current->signal->cstime, &r.ru_stime); + r.ru_minflt = current->signal->cmin_flt; + r.ru_majflt = current->signal->cmaj_flt; + break; + } + + return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; +} + +asmlinkage long +osf_wait4(pid_t pid, int __user *ustatus, int options, + struct rusage32 __user *ur) +{ + struct rusage r; + long ret, err; + mm_segment_t old_fs; + + if (!ur) + return sys_wait4(pid, ustatus, options, NULL); + + old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4(pid, ustatus, options, (struct rusage __user *) &r); + set_fs (old_fs); + + if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) + return -EFAULT; + + err = 0; + err |= __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec); + err |= __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec); + err |= __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec); + err |= __put_user(r.ru_stime.tv_usec, &ur->ru_stime.tv_usec); + err |= __put_user(r.ru_maxrss, &ur->ru_maxrss); + err |= __put_user(r.ru_ixrss, &ur->ru_ixrss); + err |= __put_user(r.ru_idrss, &ur->ru_idrss); + err |= __put_user(r.ru_isrss, &ur->ru_isrss); + err |= __put_user(r.ru_minflt, &ur->ru_minflt); + err |= __put_user(r.ru_majflt, &ur->ru_majflt); + err |= __put_user(r.ru_nswap, &ur->ru_nswap); + err |= __put_user(r.ru_inblock, &ur->ru_inblock); + err |= __put_user(r.ru_oublock, &ur->ru_oublock); + err |= __put_user(r.ru_msgsnd, &ur->ru_msgsnd); + err |= __put_user(r.ru_msgrcv, &ur->ru_msgrcv); + err |= __put_user(r.ru_nsignals, &ur->ru_nsignals); + err |= __put_user(r.ru_nvcsw, &ur->ru_nvcsw); + err |= __put_user(r.ru_nivcsw, &ur->ru_nivcsw); + + return err ? err : ret; +} + +/* + * I don't know what the parameters are: the first one + * seems to be a timeval pointer, and I suspect the second + * one is the time remaining.. Ho humm.. No documentation. + */ +asmlinkage int +osf_usleep_thread(struct timeval32 __user *sleep, struct timeval32 __user *remain) +{ + struct timeval tmp; + unsigned long ticks; + + if (get_tv32(&tmp, sleep)) + goto fault; + + ticks = tmp.tv_usec; + ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ); + ticks += tmp.tv_sec * HZ; + + current->state = TASK_INTERRUPTIBLE; + ticks = schedule_timeout(ticks); + + if (remain) { + tmp.tv_sec = ticks / HZ; + tmp.tv_usec = ticks % HZ; + if (put_tv32(remain, &tmp)) + goto fault; + } + + return 0; + fault: + return -EFAULT; +} + + +struct timex32 { + unsigned int modes; /* mode selector */ + long offset; /* time offset (usec) */ + long freq; /* frequency offset (scaled ppm) */ + long maxerror; /* maximum error (usec) */ + long esterror; /* estimated error (usec) */ + int status; /* clock command/status */ + long constant; /* pll time constant */ + long precision; /* clock precision (usec) (read only) */ + long tolerance; /* clock frequency tolerance (ppm) + * (read only) + */ + struct timeval32 time; /* (read only) */ + long tick; /* (modified) usecs between clock ticks */ + + long ppsfreq; /* pps frequency (scaled ppm) (ro) */ + long jitter; /* pps jitter (us) (ro) */ + int shift; /* interval duration (s) (shift) (ro) */ + long stabil; /* pps stability (scaled ppm) (ro) */ + long jitcnt; /* jitter limit exceeded (ro) */ + long calcnt; /* calibration intervals (ro) */ + long errcnt; /* calibration errors (ro) */ + long stbcnt; /* stability limit exceeded (ro) */ + + int :32; int :32; int :32; int :32; + int :32; int :32; int :32; int :32; + int :32; int :32; int :32; int :32; +}; + +asmlinkage int +sys_old_adjtimex(struct timex32 __user *txc_p) +{ + struct timex txc; + int ret; + + /* copy relevant bits of struct timex. */ + if (copy_from_user(&txc, txc_p, offsetof(struct timex32, time)) || + copy_from_user(&txc.tick, &txc_p->tick, sizeof(struct timex32) - + offsetof(struct timex32, time))) + return -EFAULT; + + ret = do_adjtimex(&txc); + if (ret < 0) + return ret; + + /* copy back to timex32 */ + if (copy_to_user(txc_p, &txc, offsetof(struct timex32, time)) || + (copy_to_user(&txc_p->tick, &txc.tick, sizeof(struct timex32) - + offsetof(struct timex32, tick))) || + (put_tv32(&txc_p->time, &txc.time))) + return -EFAULT; + + return ret; +} + +/* Get an address range which is currently unmapped. Similar to the + generic version except that we know how to honor ADDR_LIMIT_32BIT. */ + +static unsigned long +arch_get_unmapped_area_1(unsigned long addr, unsigned long len, + unsigned long limit) +{ + struct vm_area_struct *vma = find_vma(current->mm, addr); + + while (1) { + /* At this point: (!vma || addr < vma->vm_end). */ + if (limit - len < addr) + return -ENOMEM; + if (!vma || addr + len <= vma->vm_start) + return addr; + addr = vma->vm_end; + vma = vma->vm_next; + } +} + +unsigned long +arch_get_unmapped_area(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + unsigned long limit; + + /* "32 bit" actually means 31 bit, since pointers sign extend. */ + if (current->personality & ADDR_LIMIT_32BIT) + limit = 0x80000000; + else + limit = TASK_SIZE; + + if (len > limit) + return -ENOMEM; + + /* First, see if the given suggestion fits. + + The OSF/1 loader (/sbin/loader) relies on us returning an + address larger than the requested if one exists, which is + a terribly broken way to program. + + That said, I can see the use in being able to suggest not + merely specific addresses, but regions of memory -- perhaps + this feature should be incorporated into all ports? */ + + if (addr) { + addr = arch_get_unmapped_area_1 (PAGE_ALIGN(addr), len, limit); + if (addr != (unsigned long) -ENOMEM) + return addr; + } + + /* Next, try allocating at TASK_UNMAPPED_BASE. */ + addr = arch_get_unmapped_area_1 (PAGE_ALIGN(TASK_UNMAPPED_BASE), + len, limit); + if (addr != (unsigned long) -ENOMEM) + return addr; + + /* Finally, try allocating in low memory. */ + addr = arch_get_unmapped_area_1 (PAGE_SIZE, len, limit); + + return addr; +} + +#ifdef CONFIG_OSF4_COMPAT + +/* Clear top 32 bits of iov_len in the user's buffer for + compatibility with old versions of OSF/1 where iov_len + was defined as int. */ +static int +osf_fix_iov_len(const struct iovec __user *iov, unsigned long count) +{ + unsigned long i; + + for (i = 0 ; i < count ; i++) { + int __user *iov_len_high = (int __user *)&iov[i].iov_len + 1; + + if (put_user(0, iov_len_high)) + return -EFAULT; + } + return 0; +} + +asmlinkage ssize_t +osf_readv(unsigned long fd, const struct iovec __user * vector, unsigned long count) +{ + if (unlikely(personality(current->personality) == PER_OSF4)) + if (osf_fix_iov_len(vector, count)) + return -EFAULT; + return sys_readv(fd, vector, count); +} + +asmlinkage ssize_t +osf_writev(unsigned long fd, const struct iovec __user * vector, unsigned long count) +{ + if (unlikely(personality(current->personality) == PER_OSF4)) + if (osf_fix_iov_len(vector, count)) + return -EFAULT; + return sys_writev(fd, vector, count); +} + +#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 @@ +/* + * linux/arch/alpha/kernel/pci-noop.c + * + * Stub PCI interfaces for Jensen-specific kernels. + */ + +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/dma-mapping.h> + +#include "proto.h" + + +/* + * The PCI controller list. + */ + +struct pci_controller *hose_head, **hose_tail = &hose_head; +struct pci_controller *pci_isa_hose; + + +struct pci_controller * __init +alloc_pci_controller(void) +{ + struct pci_controller *hose; + + hose = alloc_bootmem(sizeof(*hose)); + + *hose_tail = hose; + hose_tail = &hose->next; + + return hose; +} + +struct resource * __init +alloc_resource(void) +{ + struct resource *res; + + res = alloc_bootmem(sizeof(*res)); + + return res; +} + +asmlinkage long +sys_pciconfig_iobase(long which, unsigned long bus, unsigned long dfn) +{ + struct pci_controller *hose; + + /* from hose or from bus.devfn */ + if (which & IOBASE_FROM_HOSE) { + for (hose = hose_head; hose; hose = hose->next) + if (hose->index == bus) + break; + if (!hose) + return -ENODEV; + } else { + /* Special hook for ISA access. */ + if (bus == 0 && dfn == 0) + hose = pci_isa_hose; + else + return -ENODEV; + } + + switch (which & ~IOBASE_FROM_HOSE) { + case IOBASE_HOSE: + return hose->index; + case IOBASE_SPARSE_MEM: + return hose->sparse_mem_base; + case IOBASE_DENSE_MEM: + return hose->dense_mem_base; + case IOBASE_SPARSE_IO: + return hose->sparse_io_base; + case IOBASE_DENSE_IO: + return hose->dense_io_base; + case IOBASE_ROOT_BUS: + return hose->bus->number; + } + + return -EOPNOTSUPP; +} + +asmlinkage long +sys_pciconfig_read(unsigned long bus, unsigned long dfn, + unsigned long off, unsigned long len, void *buf) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + else + return -ENODEV; +} + +asmlinkage long +sys_pciconfig_write(unsigned long bus, unsigned long dfn, + unsigned long off, unsigned long len, void *buf) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + else + return -ENODEV; +} + +/* Stubs for the routines in pci_iommu.c: */ + +void * +pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) +{ + return NULL; +} + +void +pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr, + dma_addr_t dma_addr) +{ +} + +dma_addr_t +pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, + int direction) +{ + return (dma_addr_t) 0; +} + +void +pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, + int direction) +{ +} + +int +pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, + int direction) +{ + return 0; +} + +void +pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, + int direction) +{ +} + +int +pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask) +{ + return 0; +} + +/* Generic DMA mapping functions: */ + +void * +dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp) +{ + void *ret; + + if (!dev || *dev->dma_mask >= 0xffffffffUL) + gfp &= ~GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + if (ret) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +EXPORT_SYMBOL(dma_alloc_coherent); + +int +dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + int i; + + for (i = 0; i < nents; i++ ) { + void *va; + + BUG_ON(!sg[i].page); + va = page_address(sg[i].page) + sg[i].offset; + sg_dma_address(sg + i) = (dma_addr_t)virt_to_bus(va); + sg_dma_len(sg + i) = sg[i].length; + } + + return nents; +} + +EXPORT_SYMBOL(dma_map_sg); + +int +dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + + *dev->dma_mask = mask; + + return 0; +} + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + return NULL; +} + +void pci_iounmap(struct pci_dev *dev, void __iomem * addr) +{ +} + +EXPORT_SYMBOL(pci_iomap); +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 @@ +/* + * linux/arch/alpha/kernel/pci.c + * + * Extruded from code written by + * Dave Rusling (david.rusling@reo.mts.dec.com) + * David Mosberger (davidm@cs.arizona.edu) + */ + +/* 2.3.x PCI/resources, 1999 Andrea Arcangeli <andrea@suse.de> */ + +/* + * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> + * PCI-PCI bridges cleanup + */ +#include <linux/config.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/bootmem.h> +#include <linux/module.h> +#include <linux/cache.h> +#include <linux/slab.h> +#include <asm/machvec.h> + +#include "proto.h" +#include "pci_impl.h" + + +/* + * Some string constants used by the various core logics. + */ + +const char *const pci_io_names[] = { + "PCI IO bus 0", "PCI IO bus 1", "PCI IO bus 2", "PCI IO bus 3", + "PCI IO bus 4", "PCI IO bus 5", "PCI IO bus 6", "PCI IO bus 7" +}; + +const char *const pci_mem_names[] = { + "PCI mem bus 0", "PCI mem bus 1", "PCI mem bus 2", "PCI mem bus 3", + "PCI mem bus 4", "PCI mem bus 5", "PCI mem bus 6", "PCI mem bus 7" +}; + +const char pci_hae0_name[] = "HAE0"; + +/* Indicate whether we respect the PCI setup left by console. */ +/* + * Make this long-lived so that we know when shutting down + * whether we probed only or not. + */ +int pci_probe_only; + +/* + * The PCI controller list. + */ + +struct pci_controller *hose_head, **hose_tail = &hose_head; +struct pci_controller *pci_isa_hose; + +/* + * Quirks. + */ + +static void __init +quirk_isa_bridge(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_ISA << 8; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, quirk_isa_bridge); + +static void __init +quirk_cypress(struct pci_dev *dev) +{ + /* The Notorious Cy82C693 chip. */ + + /* The Cypress IDE controller doesn't support native mode, but it + has programmable addresses of IDE command/control registers. + This violates PCI specifications, confuses the IDE subsystem and + causes resource conflicts between the primary HD_CMD register and + the floppy controller. Ugh. Fix that. */ + if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE) { + dev->resource[0].flags = 0; + dev->resource[1].flags = 0; + } + + /* The Cypress bridge responds on the PCI bus in the address range + 0xffff0000-0xffffffff (conventional x86 BIOS ROM). There is no + way to turn this off. The bridge also supports several extended + BIOS ranges (disabled after power-up), and some consoles do turn + them on. So if we use a large direct-map window, or a large SG + window, we must avoid the entire 0xfff00000-0xffffffff region. */ + else if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA) { + if (__direct_map_base + __direct_map_size >= 0xfff00000UL) + __direct_map_size = 0xfff00000UL - __direct_map_base; + else { + struct pci_controller *hose = dev->sysdata; + struct pci_iommu_arena *pci = hose->sg_pci; + if (pci && pci->dma_base + pci->size >= 0xfff00000UL) + pci->size = 0xfff00000UL - pci->dma_base; + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, quirk_cypress); + +/* Called f |