aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc64
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sparc64
downloadlinux-stericsson-2.6.12-rc2.tar.gz
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/sparc64')
-rw-r--r--arch/sparc64/Kconfig630
-rw-r--r--arch/sparc64/Kconfig.debug54
-rw-r--r--arch/sparc64/Makefile83
-rw-r--r--arch/sparc64/boot/Makefile34
-rw-r--r--arch/sparc64/boot/piggyback.c109
-rw-r--r--arch/sparc64/defconfig1951
-rw-r--r--arch/sparc64/kernel/Makefile44
-rw-r--r--arch/sparc64/kernel/auxio.c152
-rw-r--r--arch/sparc64/kernel/binfmt_aout32.c424
-rw-r--r--arch/sparc64/kernel/binfmt_elf32.c159
-rw-r--r--arch/sparc64/kernel/central.c457
-rw-r--r--arch/sparc64/kernel/chmc.c458
-rw-r--r--arch/sparc64/kernel/cpu.c124
-rw-r--r--arch/sparc64/kernel/devices.c144
-rw-r--r--arch/sparc64/kernel/dtlb_backend.S181
-rw-r--r--arch/sparc64/kernel/dtlb_base.S113
-rw-r--r--arch/sparc64/kernel/dtlb_prot.S54
-rw-r--r--arch/sparc64/kernel/ebus.c644
-rw-r--r--arch/sparc64/kernel/entry.S1919
-rw-r--r--arch/sparc64/kernel/etrap.S301
-rw-r--r--arch/sparc64/kernel/head.S782
-rw-r--r--arch/sparc64/kernel/idprom.c49
-rw-r--r--arch/sparc64/kernel/init_task.c35
-rw-r--r--arch/sparc64/kernel/ioctl32.c597
-rw-r--r--arch/sparc64/kernel/iommu_common.c231
-rw-r--r--arch/sparc64/kernel/iommu_common.h48
-rw-r--r--arch/sparc64/kernel/irq.c1269
-rw-r--r--arch/sparc64/kernel/isa.c329
-rw-r--r--arch/sparc64/kernel/itlb_base.S83
-rw-r--r--arch/sparc64/kernel/kprobes.c394
-rw-r--r--arch/sparc64/kernel/module.c209
-rw-r--r--arch/sparc64/kernel/pci.c805
-rw-r--r--arch/sparc64/kernel/pci_common.c1040
-rw-r--r--arch/sparc64/kernel/pci_impl.h49
-rw-r--r--arch/sparc64/kernel/pci_iommu.c855
-rw-r--r--arch/sparc64/kernel/pci_psycho.c1560
-rw-r--r--arch/sparc64/kernel/pci_sabre.c1702
-rw-r--r--arch/sparc64/kernel/pci_schizo.c2187
-rw-r--r--arch/sparc64/kernel/power.c150
-rw-r--r--arch/sparc64/kernel/process.c869
-rw-r--r--arch/sparc64/kernel/ptrace.c646
-rw-r--r--arch/sparc64/kernel/rtrap.S362
-rw-r--r--arch/sparc64/kernel/sbus.c1243
-rw-r--r--arch/sparc64/kernel/semaphore.c251
-rw-r--r--arch/sparc64/kernel/setup.c731
-rw-r--r--arch/sparc64/kernel/signal.c688
-rw-r--r--arch/sparc64/kernel/signal32.c1469
-rw-r--r--arch/sparc64/kernel/smp.c1244
-rw-r--r--arch/sparc64/kernel/sparc64_ksyms.c432
-rw-r--r--arch/sparc64/kernel/starfire.c123
-rw-r--r--arch/sparc64/kernel/sunos_ioctl32.c275
-rw-r--r--arch/sparc64/kernel/sys32.S327
-rw-r--r--arch/sparc64/kernel/sys_sparc.c723
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c1118
-rw-r--r--arch/sparc64/kernel/sys_sunos32.c1343
-rw-r--r--arch/sparc64/kernel/systbls.S251
-rw-r--r--arch/sparc64/kernel/time.c1195
-rw-r--r--arch/sparc64/kernel/trampoline.S368
-rw-r--r--arch/sparc64/kernel/traps.c2118
-rw-r--r--arch/sparc64/kernel/ttable.S280
-rw-r--r--arch/sparc64/kernel/unaligned.c729
-rw-r--r--arch/sparc64/kernel/us2e_cpufreq.c400
-rw-r--r--arch/sparc64/kernel/us3_cpufreq.c255
-rw-r--r--arch/sparc64/kernel/vmlinux.lds.S106
-rw-r--r--arch/sparc64/kernel/winfixup.S417
-rw-r--r--arch/sparc64/lib/Makefile20
-rw-r--r--arch/sparc64/lib/PeeCeeI.c237
-rw-r--r--arch/sparc64/lib/U1copy_from_user.S33
-rw-r--r--arch/sparc64/lib/U1copy_to_user.S33
-rw-r--r--arch/sparc64/lib/U1memcpy.S560
-rw-r--r--arch/sparc64/lib/U3copy_from_user.S22
-rw-r--r--arch/sparc64/lib/U3copy_to_user.S33
-rw-r--r--arch/sparc64/lib/U3memcpy.S422
-rw-r--r--arch/sparc64/lib/U3patch.S32
-rw-r--r--arch/sparc64/lib/VISsave.S131
-rw-r--r--arch/sparc64/lib/atomic.S139
-rw-r--r--arch/sparc64/lib/bitops.S145
-rw-r--r--arch/sparc64/lib/bzero.S158
-rw-r--r--arch/sparc64/lib/checksum.S172
-rw-r--r--arch/sparc64/lib/clear_page.S105
-rw-r--r--arch/sparc64/lib/copy_in_user.S119
-rw-r--r--arch/sparc64/lib/copy_page.S242
-rw-r--r--arch/sparc64/lib/csum_copy.S308
-rw-r--r--arch/sparc64/lib/csum_copy_from_user.S21
-rw-r--r--arch/sparc64/lib/csum_copy_to_user.S21
-rw-r--r--arch/sparc64/lib/debuglocks.c376
-rw-r--r--arch/sparc64/lib/dec_and_lock.S78
-rw-r--r--arch/sparc64/lib/delay.c49
-rw-r--r--arch/sparc64/lib/find_bit.c127
-rw-r--r--arch/sparc64/lib/iomap.c48
-rw-r--r--arch/sparc64/lib/ipcsum.S34
-rw-r--r--arch/sparc64/lib/mcount.S61
-rw-r--r--arch/sparc64/lib/memcmp.S28
-rw-r--r--arch/sparc64/lib/memmove.S31
-rw-r--r--arch/sparc64/lib/memscan.S129
-rw-r--r--arch/sparc64/lib/rwsem.S165
-rw-r--r--arch/sparc64/lib/strlen.S80
-rw-r--r--arch/sparc64/lib/strlen_user.S95
-rw-r--r--arch/sparc64/lib/strncmp.S32
-rw-r--r--arch/sparc64/lib/strncpy_from_user.S139
-rw-r--r--arch/sparc64/lib/user_fixup.c71
-rw-r--r--arch/sparc64/lib/xor.S354
-rw-r--r--arch/sparc64/math-emu/Makefile7
-rw-r--r--arch/sparc64/math-emu/math.c493
-rw-r--r--arch/sparc64/math-emu/sfp-util.h120
-rw-r--r--arch/sparc64/mm/Makefile10
-rw-r--r--arch/sparc64/mm/extable.c80
-rw-r--r--arch/sparc64/mm/fault.c527
-rw-r--r--arch/sparc64/mm/generic.c182
-rw-r--r--arch/sparc64/mm/hugetlbpage.c310
-rw-r--r--arch/sparc64/mm/init.c1769
-rw-r--r--arch/sparc64/mm/tlb.c151
-rw-r--r--arch/sparc64/mm/ultra.S583
-rw-r--r--arch/sparc64/oprofile/Kconfig23
-rw-r--r--arch/sparc64/oprofile/Makefile9
-rw-r--r--arch/sparc64/oprofile/init.c23
-rw-r--r--arch/sparc64/prom/Makefile10
-rw-r--r--arch/sparc64/prom/bootstr.c40
-rw-r--r--arch/sparc64/prom/cif.S225
-rw-r--r--arch/sparc64/prom/console.c146
-rw-r--r--arch/sparc64/prom/devops.c41
-rw-r--r--arch/sparc64/prom/init.c101
-rw-r--r--arch/sparc64/prom/map.S72
-rw-r--r--arch/sparc64/prom/memory.c152
-rw-r--r--arch/sparc64/prom/misc.c339
-rw-r--r--arch/sparc64/prom/p1275.c161
-rw-r--r--arch/sparc64/prom/printf.c47
-rw-r--r--arch/sparc64/prom/tree.c377
-rw-r--r--arch/sparc64/solaris/Makefile10
-rw-r--r--arch/sparc64/solaris/conv.h38
-rw-r--r--arch/sparc64/solaris/entry64.S218
-rw-r--r--arch/sparc64/solaris/fs.c739
-rw-r--r--arch/sparc64/solaris/ioctl.c820
-rw-r--r--arch/sparc64/solaris/ipc.c127
-rw-r--r--arch/sparc64/solaris/misc.c784
-rw-r--r--arch/sparc64/solaris/signal.c430
-rw-r--r--arch/sparc64/solaris/signal.h108
-rw-r--r--arch/sparc64/solaris/socket.c415
-rw-r--r--arch/sparc64/solaris/socksys.c211
-rw-r--r--arch/sparc64/solaris/socksys.h208
-rw-r--r--arch/sparc64/solaris/systbl.S314
-rw-r--r--arch/sparc64/solaris/timod.c959
142 files changed, 54601 insertions, 0 deletions
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
new file mode 100644
index 000000000000..fb1189641c74
--- /dev/null
+++ b/arch/sparc64/Kconfig
@@ -0,0 +1,630 @@
+# $Id: config.in,v 1.158 2002/01/24 22:14:44 davem Exp $
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+
+mainmenu "Linux/UltraSPARC Kernel Configuration"
+
+config 64BIT
+ def_bool y
+
+config MMU
+ bool
+ default y
+
+config TIME_INTERPOLATION
+ bool
+ default y
+
+choice
+ prompt "Kernel page size"
+ default SPARC64_PAGE_SIZE_8KB
+
+config SPARC64_PAGE_SIZE_8KB
+ bool "8KB"
+ help
+ This lets you select the page size of the kernel.
+
+ 8KB and 64KB work quite well, since Sparc ELF sections
+ provide for up to 64KB alignment.
+
+ Therefore, 512KB and 4MB are for expert hackers only.
+
+ If you don't know what to do, choose 8KB.
+
+config SPARC64_PAGE_SIZE_64KB
+ bool "64KB"
+
+config SPARC64_PAGE_SIZE_512KB
+ bool "512KB"
+
+config SPARC64_PAGE_SIZE_4MB
+ bool "4MB"
+
+endchoice
+
+source "init/Kconfig"
+
+config SYSVIPC_COMPAT
+ bool
+ depends on COMPAT && SYSVIPC
+ default y
+
+menu "General machine setup"
+
+config BBC_I2C
+ tristate "UltraSPARC-III bootbus i2c controller driver"
+ depends on PCI
+ help
+ The BBC devices on the UltraSPARC III have two I2C controllers. The
+ first I2C controller connects mainly to configuration PROMs (NVRAM,
+ CPU configuration, DIMM types, etc.). The second I2C controller
+ connects to environmental control devices such as fans and
+ temperature sensors. The second controller also connects to the
+ smartcard reader, if present. Say Y to enable support for these.
+
+config VT
+ bool "Virtual terminal" if EMBEDDED
+ select INPUT
+ default y
+ ---help---
+ If you say Y here, you will get support for terminal devices with
+ display and keyboard devices. These are called "virtual" because you
+ can run several virtual terminals (also called virtual consoles) on
+ one physical terminal. This is rather useful, for example one
+ virtual terminal can collect system messages and warnings, another
+ one can be used for a text-mode user session, and a third could run
+ an X session, all in parallel. Switching between virtual terminals
+ is done with certain key combinations, usually Alt-<function key>.
+
+ The setterm command ("man setterm") can be used to change the
+ properties (such as colors or beeping) of a virtual terminal. The
+ man page console_codes(4) ("man console_codes") contains the special
+ character sequences that can be used to change those properties
+ directly. The fonts used on virtual terminals can be changed with
+ the setfont ("man setfont") command and the key bindings are defined
+ with the loadkeys ("man loadkeys") command.
+
+ You need at least one virtual terminal device in order to make use
+ of your keyboard and monitor. Therefore, only people configuring an
+ embedded system would want to say N here in order to save some
+ memory; the only way to log into such a system is then via a serial
+ or network connection.
+
+ If unsure, say Y, or else you won't be able to do much with your new
+ shiny Linux system :-)
+
+config VT_CONSOLE
+ bool "Support for console on virtual terminal" if EMBEDDED
+ depends on VT
+ default y
+ ---help---
+ The system console is the device which receives all kernel messages
+ and warnings and which allows logins in single user mode. If you
+ answer Y here, a virtual terminal (the device used to interact with
+ a physical terminal) can be used as system console. This is the most
+ common mode of operations, so you should say Y here unless you want
+ the kernel messages be output only to a serial port (in which case
+ you should say Y to "Console on serial port", below).
+
+ If you do say Y here, by default the currently visible virtual
+ terminal (/dev/tty0) will be used as system console. You can change
+ that with a kernel command line option such as "console=tty3" which
+ would use the third virtual terminal as system console. (Try "man
+ bootparam" or see the documentation of your boot loader (lilo or
+ loadlin) about how to pass options to the kernel at boot time.)
+
+ If unsure, say Y.
+
+config HW_CONSOLE
+ bool
+ default y
+
+config SMP
+ bool "Symmetric multi-processing support"
+ ---help---
+ This enables support for systems with more than one CPU. If you have
+ a system with only one CPU, 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.
+
+ People using multiprocessor machines who say Y here should also say
+ Y to "Enhanced Real Time Clock Support", below. The "Advanced Power
+ Management" code will be disabled if you say Y here.
+
+ See also the <file:Documentation/smp.txt>,
+ <file:Documentation/nmi_watchdog.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 PREEMPT
+ bool "Preemptible Kernel"
+ help
+ This option reduces the latency of the kernel when reacting to
+ real-time or interactive events by allowing a low priority process to
+ be preempted even if it is in kernel mode executing a system call.
+ This allows applications to run more reliably even when the system is
+ under load.
+
+ Say Y here if you are building a kernel for a desktop, embedded
+ or real-time system. Say N if you are unsure.
+
+config NR_CPUS
+ int "Maximum number of CPUs (2-64)"
+ range 2 64
+ depends on SMP
+ default "32"
+
+source "drivers/cpufreq/Kconfig"
+
+config US3_FREQ
+ tristate "UltraSPARC-III CPU Frequency driver"
+ depends on CPU_FREQ
+ select CPU_FREQ_TABLE
+ help
+ This adds the CPUFreq driver for UltraSPARC-III processors.
+
+ For details, take a look at <file:Documentation/cpu-freq>.
+
+ If in doubt, say N.
+
+config US2E_FREQ
+ tristate "UltraSPARC-IIe CPU Frequency driver"
+ depends on CPU_FREQ
+ select CPU_FREQ_TABLE
+ help
+ This adds the CPUFreq driver for UltraSPARC-IIe processors.
+
+ For details, take a look at <file:Documentation/cpu-freq>.
+
+ If in doubt, say N.
+
+# Identify this as a Sparc64 build
+config SPARC64
+ bool
+ default y
+ help
+ SPARC is a family of RISC microprocessors designed and marketed by
+ Sun Microsystems, incorporated. This port covers the newer 64-bit
+ UltraSPARC. The UltraLinux project maintains both the SPARC32 and
+ SPARC64 ports; its web page is available at
+ <http://www.ultralinux.org/>.
+
+# Global things across all Sun machines.
+config RWSEM_GENERIC_SPINLOCK
+ bool
+
+config RWSEM_XCHGADD_ALGORITHM
+ bool
+ default y
+
+config GENERIC_CALIBRATE_DELAY
+ bool
+ default y
+
+choice
+ prompt "SPARC64 Huge TLB Page Size"
+ depends on HUGETLB_PAGE
+ default HUGETLB_PAGE_SIZE_4MB
+
+config HUGETLB_PAGE_SIZE_4MB
+ bool "4MB"
+
+config HUGETLB_PAGE_SIZE_512K
+ depends on !SPARC64_PAGE_SIZE_4MB
+ bool "512K"
+
+config HUGETLB_PAGE_SIZE_64K
+ depends on !SPARC64_PAGE_SIZE_4MB && !SPARC64_PAGE_SIZE_512K
+ bool "64K"
+
+endchoice
+
+config GENERIC_ISA_DMA
+ bool
+ default y
+
+config ISA
+ bool
+ 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 ISAPNP
+ bool
+ help
+ Say Y here if you would like support for ISA Plug and Play devices.
+ Some information is in <file:Documentation/isapnp.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called isapnp.
+
+ If unsure, say Y.
+
+config EISA
+ bool
+ ---help---
+ The Extended Industry Standard Architecture (EISA) bus was
+ developed as an open alternative to the IBM MicroChannel bus.
+
+ The EISA bus provided some of the features of the IBM MicroChannel
+ bus while maintaining backward compatibility with cards made for
+ the older ISA bus. The EISA bus saw limited use between 1988 and
+ 1995 when it was made obsolete by the PCI bus.
+
+ Say Y here if you are building a kernel for an EISA-based machine.
+
+ Otherwise, say N.
+
+config MCA
+ bool
+ help
+ MicroChannel Architecture is found in some IBM PS/2 machines and
+ laptops. It is a bus system similar to PCI or ISA. See
+ <file:Documentation/mca.txt> (and especially the web page given
+ there) before attempting to build an MCA bus kernel.
+
+config PCMCIA
+ tristate
+ ---help---
+ Say Y here if you want to attach PCMCIA- or PC-cards to your Linux
+ computer. These are credit-card size devices such as network cards,
+ modems or hard drives often used with laptops computers. There are
+ actually two varieties of these cards: the older 16 bit PCMCIA cards
+ and the newer 32 bit CardBus cards. If you want to use CardBus
+ cards, you need to say Y here and also to "CardBus support" below.
+
+ To use your PC-cards, you will need supporting software from David
+ Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+ for location). Please also read the PCMCIA-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as modules, choose M here: the
+ modules will be called pcmcia_core and ds.
+
+config SBUS
+ bool
+ default y
+
+config SBUSCHAR
+ bool
+ default y
+
+config SUN_AUXIO
+ bool
+ default y
+
+config SUN_IO
+ bool
+ default y
+
+config PCI
+ bool "PCI support"
+ 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 PCI
+
+config RTC
+ tristate
+ depends on PCI
+ default y
+ ---help---
+ If you say Y here and create a character special file /dev/rtc with
+ major number 10 and minor number 135 using mknod ("man mknod"), you
+ will get access to the real time clock (or hardware clock) built
+ into your computer.
+
+ Every PC has such a clock built in. It can be used to generate
+ signals from as low as 1Hz up to 8192Hz, and can also be used
+ as a 24 hour alarm. It reports status information via the file
+ /proc/driver/rtc and its behaviour is set by various ioctls on
+ /dev/rtc.
+
+ If you run Linux on a multiprocessor machine and said Y to
+ "Symmetric Multi Processing" above, you should say Y here to read
+ and set the RTC in an SMP compatible fashion.
+
+ If you think you have a use for such a device (such as periodic data
+ sampling), then say Y here, and read <file:Documentation/rtc.txt>
+ for details.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc.
+
+source "drivers/pci/Kconfig"
+
+config SUN_OPENPROMFS
+ tristate "Openprom tree appears in /proc/openprom"
+ help
+ If you say Y, the OpenPROM device tree will be available as a
+ virtual file system, which you can mount to /proc/openprom by "mount
+ -t openpromfs none /proc/openprom".
+
+ To compile the /proc/openprom support as a module, choose M here: the
+ module will be called openpromfs. If unsure, choose M.
+
+config SPARC32_COMPAT
+ bool "Kernel support for Linux/Sparc 32bit binary compatibility"
+ help
+ This allows you to run 32-bit binaries on your Ultra.
+ Everybody wants this; say Y.
+
+config COMPAT
+ bool
+ depends on SPARC32_COMPAT
+ default y
+
+config UID16
+ bool
+ depends on SPARC32_COMPAT
+ default y
+
+config BINFMT_ELF32
+ tristate "Kernel support for 32-bit ELF binaries"
+ depends on SPARC32_COMPAT
+ help
+ This allows you to run 32-bit Linux/ELF binaries on your Ultra.
+ Everybody wants this; say Y.
+
+config BINFMT_AOUT32
+ bool "Kernel support for 32-bit (ie. SunOS) a.out binaries"
+ depends on SPARC32_COMPAT
+ help
+ This allows you to run 32-bit a.out format binaries on your Ultra.
+ If you want to run SunOS binaries (see SunOS binary emulation below)
+ or other a.out binaries, say Y. If unsure, say N.
+
+source "fs/Kconfig.binfmt"
+
+config SUNOS_EMUL
+ bool "SunOS binary emulation"
+ depends on BINFMT_AOUT32
+ help
+ This allows you to run most SunOS binaries. If you want to do this,
+ say Y here and place appropriate files in /usr/gnemul/sunos. See
+ <http://www.ultralinux.org/faq.html> for more information. If you
+ want to run SunOS binaries on an Ultra you must also say Y to
+ "Kernel support for 32-bit a.out binaries" above.
+
+config SOLARIS_EMUL
+ tristate "Solaris binary emulation (EXPERIMENTAL)"
+ depends on SPARC32_COMPAT && EXPERIMENTAL
+ help
+ This is experimental code which will enable you to run (many)
+ Solaris binaries on your SPARC Linux machine.
+
+ To compile this code as a module, choose M here: the
+ module will be called solaris.
+
+source "drivers/parport/Kconfig"
+
+config PRINTER
+ tristate "Parallel printer support"
+ depends on PARPORT
+ ---help---
+ If you intend to attach a printer to the parallel port of your Linux
+ box (as opposed to using a serial printer; if the connector at the
+ printer has 9 or 25 holes ["female"], then it's serial), say Y.
+ Also read the Printing-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ It is possible to share one parallel port among several devices
+ (e.g. printer and ZIP drive) and it is safe to compile the
+ corresponding drivers into the kernel.
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/parport.txt>. The module will be called lp.
+
+ If you have several parallel ports, you can specify which ports to
+ use with the "lp" kernel command line option. (Try "man bootparam"
+ or see the documentation of your boot loader (lilo or loadlin) about
+ how to pass options to the kernel at boot time.) The syntax of the
+ "lp" command line option can be found in <file:drivers/char/lp.c>.
+
+ If you have more than 8 printers, you need to increase the LP_NO
+ macro in lp.c and the PARPORT_MAX macro in parport.h.
+
+config ENVCTRL
+ tristate "SUNW, envctrl support"
+ depends on PCI
+ help
+ Kernel support for temperature and fan monitoring on Sun SME
+ machines.
+
+ To compile this driver as a module, choose M here: the
+ module will be called envctrl.
+
+config DISPLAY7SEG
+ tristate "7-Segment Display support"
+ depends on PCI
+ ---help---
+ This is the driver for the 7-segment display and LED present on
+ Sun Microsystems CompactPCI models CP1400 and CP1500.
+
+ To compile this driver as a module, choose M here: the
+ module will be called display7seg.
+
+ If you do not have a CompactPCI model CP1400 or CP1500, or
+ another UltraSPARC-IIi-cEngine boardset with a 7-segment display,
+ you should say N to this option.
+
+config CMDLINE_BOOL
+ bool "Default bootloader kernel arguments"
+
+config CMDLINE
+ string "Initial kernel command string"
+ depends on CMDLINE_BOOL
+ default "console=ttyS0,9600 root=/dev/sda1"
+ help
+ Say Y here if you want to be able to pass default arguments to
+ the kernel. This will be overridden by the bootloader, if you
+ use one (such as SILO). This is most useful if you want to boot
+ a kernel from TFTP, and want default options to be available
+ with having them passed on the command line.
+
+ NOTE: This option WILL override the PROM bootargs setting!
+
+endmenu
+
+source "drivers/base/Kconfig"
+
+source "drivers/video/Kconfig"
+
+source "drivers/serial/Kconfig"
+
+source "drivers/sbus/char/Kconfig"
+
+source "drivers/mtd/Kconfig"
+
+source "drivers/block/Kconfig"
+
+source "drivers/ide/Kconfig"
+
+source "drivers/scsi/Kconfig"
+
+source "drivers/fc4/Kconfig"
+
+source "drivers/md/Kconfig"
+
+if PCI
+source "drivers/message/fusion/Kconfig"
+endif
+
+source "drivers/ieee1394/Kconfig"
+
+source "net/Kconfig"
+
+source "drivers/isdn/Kconfig"
+
+source "drivers/telephony/Kconfig"
+
+# This one must be before the filesystem configs. -DaveM
+
+menu "Unix98 PTY support"
+
+config UNIX98_PTYS
+ bool "Unix98 PTY support"
+ ---help---
+ A pseudo terminal (PTY) is a software device consisting of two
+ halves: a master and a slave. The slave device behaves identical to
+ a physical terminal; the master device is used by a process to
+ read data from and write data to the slave, thereby emulating a
+ terminal. Typical programs for the master side are telnet servers
+ and xterms.
+
+ Linux has traditionally used the BSD-like names /dev/ptyxx for
+ masters and /dev/ttyxx for slaves of pseudo terminals. This scheme
+ has a number of problems. The GNU C library glibc 2.1 and later,
+ however, supports the Unix98 naming standard: in order to acquire a
+ pseudo terminal, a process opens /dev/ptmx; the number of the pseudo
+ terminal is then made available to the process and the pseudo
+ terminal slave can be accessed as /dev/pts/<number>. What was
+ traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
+
+ The entries in /dev/pts/ are created on the fly by a virtual
+ file system; therefore, if you say Y here you should say Y to
+ "/dev/pts file system for Unix98 PTYs" as well.
+
+ If you want to say Y here, you need to have the C library glibc 2.1
+ or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*").
+ Read the instructions in <file:Documentation/Changes> pertaining to
+ pseudo terminals. It's safe to say N.
+
+config UNIX98_PTY_COUNT
+ int "Maximum number of Unix98 PTYs in use (0-2048)"
+ depends on UNIX98_PTYS
+ default "256"
+ help
+ The maximum number of Unix98 PTYs that can be used at any one time.
+ The default is 256, and should be enough for desktop systems. Server
+ machines which support incoming telnet/rlogin/ssh connections and/or
+ serve several X terminals may want to increase this: every incoming
+ connection and every xterm uses up one PTY.
+
+ When not in use, each additional set of 256 PTYs occupy
+ approximately 8 KB of kernel memory on 32-bit architectures.
+
+endmenu
+
+menu "XFree86 DRI support"
+
+config DRM
+ bool "Direct Rendering Manager (XFree86 DRI support)"
+ help
+ Kernel-level support for the Direct Rendering Infrastructure (DRI)
+ introduced in XFree86 4.0. If you say Y here, you need to select
+ the module that's right for your graphics card from the list below.
+ These modules provide support for synchronization, security, and
+ DMA transfers. Please see <http://dri.sourceforge.net/> for more
+ details. You should also select and configure AGP
+ (/dev/agpgart) support.
+
+config DRM_FFB
+ tristate "Creator/Creator3D"
+ depends on DRM && BROKEN
+ help
+ Choose this option if you have one of Sun's Creator3D-based graphics
+ and frame buffer cards. Product page at
+ <http://www.sun.com/desktop/products/Graphics/creator3d.html>.
+
+config DRM_TDFX
+ tristate "3dfx Banshee/Voodoo3+"
+ depends on DRM
+ help
+ Choose this option if you have a 3dfx Banshee or Voodoo3 (or later),
+ graphics card. If M is selected, the module will be called tdfx.
+
+config DRM_R128
+ tristate "ATI Rage 128"
+ depends on DRM
+ help
+ Choose this option if you have an ATI Rage 128 graphics card. If M
+ is selected, the module will be called r128. AGP support for
+ this card is strongly suggested (unless you have a PCI version).
+
+endmenu
+
+source "drivers/input/Kconfig"
+
+source "drivers/i2c/Kconfig"
+
+source "fs/Kconfig"
+
+source "drivers/media/Kconfig"
+
+source "sound/Kconfig"
+
+source "drivers/usb/Kconfig"
+
+source "drivers/infiniband/Kconfig"
+
+source "drivers/char/watchdog/Kconfig"
+
+source "arch/sparc64/oprofile/Kconfig"
+
+source "arch/sparc64/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/sparc64/Kconfig.debug b/arch/sparc64/Kconfig.debug
new file mode 100644
index 000000000000..cd8d39fb954d
--- /dev/null
+++ b/arch/sparc64/Kconfig.debug
@@ -0,0 +1,54 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+config DEBUG_STACK_USAGE
+ bool "Enable stack utilization instrumentation"
+ depends on DEBUG_KERNEL
+ help
+ Enables the display of the minimum amount of free stack which each
+ task has ever had available in the sysrq-T and sysrq-P debug output.
+
+ This option will slow down process creation somewhat.
+
+config KPROBES
+ bool "Kprobes"
+ depends on DEBUG_KERNEL
+ help
+ Kprobes allows you to trap at almost any kernel address and
+ execute a callback function. register_kprobe() establishes
+ a probepoint and specifies the callback. Kprobes is useful
+ for kernel debugging, non-intrusive instrumentation and testing.
+ If in doubt, say "N".
+
+config DEBUG_DCFLUSH
+ bool "D-cache flush debugging"
+ depends on DEBUG_KERNEL
+
+config STACK_DEBUG
+ depends on DEBUG_KERNEL
+ bool "Stack Overflow Detection Support"
+
+config DEBUG_BOOTMEM
+ depends on DEBUG_KERNEL
+ bool "Debug BOOTMEM initialization"
+
+# We have a custom atomic_dec_and_lock() implementation but it's not
+# compatible with spinlock debugging so we need to fall back on
+# the generic version in that case.
+config HAVE_DEC_LOCK
+ bool
+ depends on SMP && !DEBUG_SPINLOCK
+ default y
+
+config MCOUNT
+ bool
+ depends on STACK_DEBUG
+ default y
+
+config FRAME_POINTER
+ bool
+ depends on MCOUNT
+ default y
+
+endmenu
diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile
new file mode 100644
index 000000000000..43fe382da078
--- /dev/null
+++ b/arch/sparc64/Makefile
@@ -0,0 +1,83 @@
+# $Id: Makefile,v 1.52 2002/02/09 19:49:31 davem Exp $
+# sparc64/Makefile
+#
+# Makefile for the architecture dependent flags and dependencies on the
+# 64-bit Sparc.
+#
+# Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
+#
+
+CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -m64
+
+CPPFLAGS_vmlinux.lds += -Usparc
+
+CC := $(shell if $(CC) -m64 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo $(CC); else echo sparc64-linux-gcc; fi )
+
+NEW_GCC := $(call cc-option-yn, -m64 -mcmodel=medlow)
+NEW_GAS := $(shell if $(LD) -V 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
+UNDECLARED_REGS := $(shell if $(CC) -c -x assembler /dev/null -Wa,--help | grep undeclared-regs > /dev/null; then echo y; else echo n; fi; )
+INLINE_LIMIT := $(call cc-option-yn, -m64 -finline-limit=100000)
+
+export NEW_GCC
+
+ifneq ($(NEW_GAS),y)
+AS = sparc64-linux-as
+LD = sparc64-linux-ld
+NM = sparc64-linux-nm
+AR = sparc64-linux-ar
+RANLIB = sparc64-linux-ranlib
+else
+AS := $(AS) -64
+LDFLAGS := -m elf64_sparc
+endif
+
+ifneq ($(UNDECLARED_REGS),y)
+CC_UNDECL =
+else
+CC_UNDECL = -Wa,--undeclared-regs
+AS := $(AS) --undeclared-regs
+endif
+
+ifneq ($(NEW_GCC),y)
+ CFLAGS := $(CFLAGS) -pipe -mno-fpu -mtune=ultrasparc -mmedlow \
+ -ffixed-g4 -ffixed-g5 -fcall-used-g7 -Wno-sign-compare
+else
+ CFLAGS := $(CFLAGS) -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow \
+ -ffixed-g4 -ffixed-g5 -fcall-used-g7 -Wno-sign-compare \
+ $(CC_UNDECL)
+ AFLAGS += -m64 -mcpu=ultrasparc $(CC_UNDECL)
+endif
+
+ifeq ($(INLINE_LIMIT),y)
+ CFLAGS := $(CFLAGS) -finline-limit=100000
+endif
+
+ifeq ($(CONFIG_MCOUNT),y)
+ CFLAGS := $(CFLAGS) -pg
+endif
+
+head-y := arch/sparc64/kernel/head.o arch/sparc64/kernel/init_task.o
+
+core-y += arch/sparc64/kernel/ arch/sparc64/mm/
+core-$(CONFIG_SOLARIS_EMUL) += arch/sparc64/solaris/
+core-y += arch/sparc64/math-emu/
+libs-y += arch/sparc64/prom/ arch/sparc64/lib/
+
+# FIXME: is drivers- right?
+drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
+
+boot := arch/sparc64/boot
+
+image tftpboot.img vmlinux.aout: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+archclean:
+ $(Q)$(MAKE) $(clean)=$(boot)
+
+define archhelp
+ echo '* vmlinux - Standard sparc64 kernel'
+ echo ' vmlinux.aout - a.out kernel for sparc64'
+ echo ' tftpboot.img - Image prepared for tftp'
+endef
+
diff --git a/arch/sparc64/boot/Makefile b/arch/sparc64/boot/Makefile
new file mode 100644
index 000000000000..6968a6da57da
--- /dev/null
+++ b/arch/sparc64/boot/Makefile
@@ -0,0 +1,34 @@
+# $Id: Makefile,v 1.4 1997/12/15 20:08:56 ecd Exp $
+# Makefile for the Sparc64 boot stuff.
+#
+# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+
+ROOT_IMG := /usr/src/root.img
+ELFTOAOUT := elftoaout
+
+hostprogs-y := piggyback
+targets := image tftpboot.img vmlinux.aout
+
+quiet_cmd_elftoaout = ELF2AOUT $@
+ cmd_elftoaout = $(ELFTOAOUT) vmlinux -o $@
+quiet_cmd_piggy = PIGGY $@
+ cmd_piggy = $(obj)/piggyback $@ System.map $(ROOT_IMG)
+quiet_cmd_strip = STRIP $@
+ cmd_strip = $(STRIP) -R .comment -R .note -K sun4u_init -K _end -K _start vmlinux -o $@
+
+
+# Actual linking
+$(obj)/image: vmlinux FORCE
+ $(call if_changed,strip)
+ @echo ' kernel: $@ is ready'
+
+$(obj)/tftpboot.img: vmlinux $(obj)/piggyback System.map $(ROOT_IMG) FORCE
+ $(call if_changed,elftoaout)
+ $(call if_changed,piggy)
+ @echo ' kernel: $@ is ready'
+
+$(obj)/vmlinux.aout: vmlinux FORCE
+ $(call if_changed,elftoaout)
+ @echo ' kernel: $@ is ready'
+
diff --git a/arch/sparc64/boot/piggyback.c b/arch/sparc64/boot/piggyback.c
new file mode 100644
index 000000000000..36f907408c60
--- /dev/null
+++ b/arch/sparc64/boot/piggyback.c
@@ -0,0 +1,109 @@
+/* $Id: piggyback.c,v 1.2 2000/09/19 14:34:39 anton Exp $
+ Simple utility to make a single-image install kernel with initial ramdisk
+ for Sparc64 tftpbooting without need to set up nfs.
+
+ Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly
+ usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */
+
+void die(char *str)
+{
+ perror (str);
+ exit(1);
+}
+
+int main(int argc,char **argv)
+{
+ char buffer [1024], *q, *r;
+ unsigned int i, j, k, start, end, offset;
+ FILE *map;
+ struct stat s;
+ int image, tail;
+
+ if (stat (argv[3], &s) < 0) die (argv[3]);
+ map = fopen (argv[2], "r");
+ if (!map) die(argv[2]);
+ while (fgets (buffer, 1024, map)) {
+ if (!strcmp (buffer + 19, "_start\n"))
+ start = strtoul (buffer + 8, NULL, 16);
+ else if (!strcmp (buffer + 19, "_end\n"))
+ end = strtoul (buffer + 8, NULL, 16);
+ }
+ fclose (map);
+ if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]);
+ if (read(image,buffer,512) != 512) die(argv[1]);
+ if (!memcmp (buffer, "\177ELF", 4)) {
+ unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28));
+
+ i = p[1] + *(unsigned int *)(buffer + 24) - p[2];
+ if (lseek(image,i,0) < 0) die("lseek");
+ if (read(image,buffer,512) != 512) die(argv[1]);
+ j = 0;
+ } else if (*(unsigned int *)buffer == 0x01030107) {
+ i = j = 32;
+ } else {
+ fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n");
+ exit(1);
+ }
+ k = i;
+ if (j == 32 && buffer[40] == 'H' && buffer[41] == 'd' && buffer[42] == 'r' && buffer[43] == 'S') {
+ offset = 40 + 10;
+ } else {
+ i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512;
+ if (lseek(image,i,0) < 0) die("lseek");
+ if (read(image,buffer,1024) != 1024) die(argv[1]);
+ for (q = buffer, r = q + 512; q < r; q += 4) {
+ if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S')
+ break;
+ }
+ if (q == r) {
+ fprintf (stderr, "Couldn't find headers signature in the kernel.\n");
+ exit(1);
+ }
+ offset = i + (q - buffer) + 10;
+ }
+ if (lseek(image, offset, 0) < 0) die ("lseek");
+ *(unsigned *)buffer = 0;
+ *(unsigned *)(buffer + 4) = 0x01000000;
+ *(unsigned *)(buffer + 8) = ((end + 32 + 8191) & ~8191);
+ *(unsigned *)(buffer + 12) = s.st_size;
+ if (write(image,buffer+2,14) != 14) die (argv[1]);
+ if (lseek(image, 4, 0) < 0) die ("lseek");
+ *(unsigned *)buffer = ((end + 32 + 8191) & ~8191) - (start & ~0x3fffffUL) + s.st_size;
+ *(unsigned *)(buffer + 4) = 0;
+ *(unsigned *)(buffer + 8) = 0;
+ if (write(image,buffer,12) != 12) die (argv[1]);
+ if (lseek(image, k - start + ((end + 32 + 8191) & ~8191), 0) < 0) die ("lseek");
+ if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]);
+ while ((i = read (tail,buffer,1024)) > 0)
+ if (write(image,buffer,i) != i) die (argv[1]);
+ if (close(image) < 0) die("close");
+ if (close(tail) < 0) die("close");
+ return 0;
+}
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
new file mode 100644
index 000000000000..46a6ad60a8f5
--- /dev/null
+++ b/arch/sparc64/defconfig
@@ -0,0 +1,1951 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11
+# Sun Mar 6 20:47:29 2005
+#
+CONFIG_64BIT=y
+CONFIG_MMU=y
+CONFIG_TIME_INTERPOLATION=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_LOCK_KERNEL=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=15
+CONFIG_HOTPLUG=y
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_OBSOLETE_MODPARM=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+CONFIG_SYSVIPC_COMPAT=y
+
+#
+# General machine setup
+#
+CONFIG_BBC_I2C=m
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_SMP=y
+# CONFIG_PREEMPT is not set
+CONFIG_NR_CPUS=4
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_US3_FREQ=m
+CONFIG_US2E_FREQ=m
+CONFIG_SPARC64=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HUGETLB_PAGE_SIZE_4MB=y
+# CONFIG_HUGETLB_PAGE_SIZE_512K is not set
+# CONFIG_HUGETLB_PAGE_SIZE_64K is not set
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_SBUS=y
+CONFIG_SBUSCHAR=y
+CONFIG_SUN_AUXIO=y
+CONFIG_SUN_IO=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_RTC=y
+# CONFIG_PCI_LEGACY_PROC is not set
+# CONFIG_PCI_NAMES is not set
+CONFIG_SUN_OPENPROMFS=m
+CONFIG_SPARC32_COMPAT=y
+CONFIG_COMPAT=y
+CONFIG_UID16=y
+CONFIG_BINFMT_ELF32=y
+# CONFIG_BINFMT_AOUT32 is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=m
+CONFIG_SOLARIS_EMUL=m
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PARPORT_PC_FIFO=y
+# CONFIG_PARPORT_PC_SUPERIO is not set
+# CONFIG_PARPORT_SUNBPP is not set
+# CONFIG_PARPORT_OTHER is not set
+CONFIG_PARPORT_1284=y
+CONFIG_PRINTER=m
+CONFIG_ENVCTRL=m
+CONFIG_DISPLAY7SEG=m
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+CONFIG_FW_LOADER=m
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Graphics support
+#
+CONFIG_FB=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+# CONFIG_FB_CIRRUS is not set
+CONFIG_FB_PM2=y
+# CONFIG_FB_PM2_FIFO_DISCONNECT is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_BW2 is not set
+# CONFIG_FB_CG3 is not set
+CONFIG_FB_CG6=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON_OLD is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+CONFIG_FB_ATY=y
+CONFIG_FB_ATY_CT=y
+# CONFIG_FB_ATY_GENERIC_LCD is not set
+# CONFIG_FB_ATY_XL_INIT is not set
+CONFIG_FB_ATY_GX=y
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+CONFIG_FB_SBUS=y
+CONFIG_FB_FFB=y
+# CONFIG_FB_TCX is not set
+# CONFIG_FB_CG14 is not set
+# CONFIG_FB_P9100 is not set
+# CONFIG_FB_LEO is not set
+# CONFIG_FB_PCI is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+# CONFIG_PROM_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FONT_SUN8x16=y
+# CONFIG_FONT_SUN12x22 is not set
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_SUN_CLUT224=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Serial drivers
+#
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SUNCORE=y
+CONFIG_SERIAL_SUNZILOG=y
+CONFIG_SERIAL_SUNZILOG_CONSOLE=y
+CONFIG_SERIAL_SUNSU=y
+CONFIG_SERIAL_SUNSU_CONSOLE=y
+CONFIG_SERIAL_SUNSAB=m
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+
+#
+# Misc Linux/SPARC drivers
+#
+CONFIG_SUN_OPENPROMIO=m
+CONFIG_SUN_MOSTEK_RTC=y
+CONFIG_OBP_FLASH=m
+# CONFIG_SUN_BPP is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_PARIDE 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_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_SX8=m
+CONFIG_BLK_DEV_UB=m
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+CONFIG_CDROM_PKTCDVD_WCACHE=y
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_ATA_OVER_ETH=m
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+CONFIG_IDE=y
+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 is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_GENERIC is not set
+CONFIG_BLK_DEV_OPTI621=m
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_IDEDMA_ONLYDISK=y
+# CONFIG_BLK_DEV_AEC62XX is not set
+CONFIG_BLK_DEV_ALI15X3=y
+# CONFIG_WDC_ALI15X3 is not set
+CONFIG_BLK_DEV_AMD74XX=m
+CONFIG_BLK_DEV_CMD64X=m
+CONFIG_BLK_DEV_TRIFLEX=m
+CONFIG_BLK_DEV_CY82C693=m
+CONFIG_BLK_DEV_CS5520=m
+CONFIG_BLK_DEV_CS5530=m
+CONFIG_BLK_DEV_HPT34X=m
+# CONFIG_HPT34X_AUTODMA is not set
+CONFIG_BLK_DEV_HPT366=m
+CONFIG_BLK_DEV_SC1200=m
+CONFIG_BLK_DEV_PIIX=m
+CONFIG_BLK_DEV_NS87415=m
+CONFIG_BLK_DEV_PDC202XX_OLD=m
+# CONFIG_PDC202XX_BURST is not set
+CONFIG_BLK_DEV_PDC202XX_NEW=m
+# CONFIG_PDC202XX_FORCE is not set
+CONFIG_BLK_DEV_SVWKS=m
+CONFIG_BLK_DEV_SIIMAGE=m
+CONFIG_BLK_DEV_SLC90E66=m
+CONFIG_BLK_DEV_TRM290=m
+CONFIG_BLK_DEV_VIA82CXXX=m
+# CONFIG_IDE_ARM 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=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_ISCSI_ATTRS=m
+
+#
+# SCSI low-level drivers
+#
+CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_SCSI_3W_9XXX=m
+CONFIG_SCSI_ACARD=m
+CONFIG_SCSI_AACRAID=m
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+CONFIG_SCSI_AIC79XX=m
+CONFIG_AIC79XX_CMDS_PER_DEVICE=32
+CONFIG_AIC79XX_RESET_DELAY_MS=15000
+# CONFIG_AIC79XX_BUILD_FIRMWARE is not set
+# CONFIG_AIC79XX_ENABLE_RD_STRM is not set
+# CONFIG_AIC79XX_DEBUG_ENABLE is not set
+CONFIG_AIC79XX_DEBUG_MASK=0
+# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+CONFIG_SCSI_SATA=y
+CONFIG_SCSI_SATA_AHCI=m
+CONFIG_SCSI_SATA_SVW=m
+CONFIG_SCSI_ATA_PIIX=m
+CONFIG_SCSI_SATA_NV=m
+CONFIG_SCSI_SATA_PROMISE=m
+CONFIG_SCSI_SATA_QSTOR=m
+CONFIG_SCSI_SATA_SX4=m
+CONFIG_SCSI_SATA_SIL=m
+CONFIG_SCSI_SATA_SIS=m
+CONFIG_SCSI_SATA_ULI=m
+CONFIG_SCSI_SATA_VIA=m
+CONFIG_SCSI_SATA_VITESSE=m
+CONFIG_SCSI_DMX3191D=m
+CONFIG_SCSI_EATA_PIO=m
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+CONFIG_SCSI_IPS=m
+CONFIG_SCSI_INITIO=m
+CONFIG_SCSI_INIA100=m
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_IPR is not set
+CONFIG_SCSI_QLOGIC_ISP=m
+CONFIG_SCSI_QLOGIC_FC=y
+CONFIG_SCSI_QLOGIC_FC_FIRMWARE=y
+# CONFIG_SCSI_QLOGIC_1280 is not set
+CONFIG_SCSI_QLOGICPTI=m
+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_DC395x=m
+# CONFIG_SCSI_DC390T is not set
+CONFIG_SCSI_DEBUG=m
+CONFIG_SCSI_SUNESP=y
+
+#
+# Fibre Channel support
+#
+CONFIG_FC4=m
+
+#
+# FC4 drivers
+#
+CONFIG_FC4_SOC=m
+CONFIG_FC4_SOCAL=m
+
+#
+# FC4 targets
+#
+CONFIG_SCSI_PLUTO=m
+CONFIG_SCSI_FCAL=m
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+CONFIG_FUSION=m
+CONFIG_FUSION_MAX_SGE=40
+CONFIG_FUSION_CTL=m
+CONFIG_FUSION_LAN=m
+
+#
+# IEEE 1394 (FireWire) support
+#
+CONFIG_IEEE1394=m
+
+#
+# Subsystem Options
+#
+# CONFIG_IEEE1394_VERBOSEDEBUG is not set
+CONFIG_IEEE1394_OUI_DB=y
+CONFIG_IEEE1394_EXTRA_CONFIG_ROMS=y
+CONFIG_IEEE1394_CONFIG_ROM_IP1394=y
+
+#
+# Device Drivers
+#
+CONFIG_IEEE1394_PCILYNX=m
+CONFIG_IEEE1394_OHCI1394=m
+
+#
+# Protocol Drivers
+#
+CONFIG_IEEE1394_VIDEO1394=m
+CONFIG_IEEE1394_SBP2=m
+# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set
+CONFIG_IEEE1394_ETH1394=m
+CONFIG_IEEE1394_DV1394=m
+CONFIG_IEEE1394_RAWIO=m
+CONFIG_IEEE1394_CMP=m
+CONFIG_IEEE1394_AMDTP=m
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+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=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_ARPD=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_TUNNEL=y
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+
+#
+# IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS transport protocol load balancing support
+#
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+
+#
+# IPVS scheduler
+#
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_CT_ACCT=y
+CONFIG_IP_NF_CONNTRACK_MARK=y
+CONFIG_IP_NF_CT_PROTO_SCTP=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+CONFIG_IP_NF_MATCH_AH_ESP=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_HELPER=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_PHYSDEV=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+CONFIG_IP_NF_MATCH_SCTP=m
+CONFIG_IP_NF_MATCH_COMMENT=m
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_SAME=m
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+CONFIG_IP_NF_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_PHYSDEV=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+
+#
+# DECnet: Netfilter Configuration
+#
+CONFIG_DECNET_NF_GRABULATOR=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_ULOG=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+CONFIG_ATM=y
+CONFIG_ATM_CLIP=y
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+CONFIG_ATM_LANE=m
+CONFIG_ATM_MPOA=m
+CONFIG_ATM_BR2684=m
+CONFIG_ATM_BR2684_IPFILTER=y
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_DECNET=m
+CONFIG_DECNET_ROUTER=y
+CONFIG_DECNET_ROUTE_FWMARK=y
+CONFIG_LLC=m
+CONFIG_LLC2=m
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_ATALK=m
+# CONFIG_DEV_APPLETALK is not set
+CONFIG_X25=m
+CONFIG_LAPB=m
+CONFIG_NET_DIVERT=y
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+# CONFIG_NET_SCH_CLK_JIFFIES is not set
+# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set
+CONFIG_NET_SCH_CLK_CPU=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_ATM=y
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_QOS=y
+CONFIG_NET_ESTIMATOR=y
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_PERF=y
+CONFIG_NET_CLS_IND=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_PEDIT=m
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+CONFIG_HAMRADIO=y
+
+#
+# Packet Radio protocols
+#
+CONFIG_AX25=m
+CONFIG_AX25_DAMA_SLAVE=y
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+
+#
+# AX.25 network device drivers
+#
+# CONFIG_BPQETHER is not set
+# CONFIG_BAYCOM_SER_FDX is not set
+# CONFIG_BAYCOM_SER_HDX is not set
+# CONFIG_BAYCOM_PAR is not set
+# CONFIG_YAM is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+CONFIG_IRDA_ULTRA=y
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+
+#
+# Old SIR device drivers
+#
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+CONFIG_SIGMATEL_FIR=m
+# CONFIG_VLSI_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_CMTP=m
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+CONFIG_BT_HCIUSB_SCO=y
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_BCSP_TXCRC=y
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+CONFIG_BONDING=m
+CONFIG_EQUALIZER=m
+CONFIG_TUN=m
+# CONFIG_ETHERTAP is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_SUNLANCE=y
+CONFIG_HAPPYMEAL=y
+CONFIG_SUNBMAC=m
+CONFIG_SUNQE=m
+CONFIG_SUNGEM=y
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+CONFIG_DE2104X=m
+CONFIG_TULIP=m
+# CONFIG_TULIP_MWI is not set
+# CONFIG_TULIP_MMIO is not set
+CONFIG_TULIP_NAPI=y
+CONFIG_TULIP_NAPI_HW_MITIGATION=y
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+# CONFIG_DM9102 is not set
+# CONFIG_HP100 is not set
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+# CONFIG_AMD8111_ETH is not set
+CONFIG_ADAPTEC_STARFIRE=m
+CONFIG_ADAPTEC_STARFIRE_NAPI=y
+CONFIG_B44=m
+CONFIG_FORCEDETH=m
+CONFIG_DGRS=m
+CONFIG_EEPRO100=m
+CONFIG_E100=m
+CONFIG_FEALNX=m
+CONFIG_NATSEMI=m
+CONFIG_NE2K_PCI=m
+# CONFIG_8139CP is not set
+CONFIG_8139TOO=m
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+CONFIG_SIS900=m
+CONFIG_EPIC100=m
+CONFIG_SUNDANCE=m
+CONFIG_SUNDANCE_MMIO=y
+CONFIG_VIA_RHINE=m
+# CONFIG_VIA_RHINE_MMIO is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+CONFIG_ACENIC=m
+# CONFIG_ACENIC_OMIT_TIGON_I is not set
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000_NAPI=y
+CONFIG_MYRI_SBUS=m
+CONFIG_NS83820=m
+CONFIG_HAMACHI=m
+CONFIG_YELLOWFIN=m
+CONFIG_R8169=m
+CONFIG_R8169_NAPI=y
+CONFIG_R8169_VLAN=y
+CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
+CONFIG_TIGON3=m
+
+#
+# Ethernet (10000 Mbit)
+#
+CONFIG_IXGB=m
+CONFIG_IXGB_NAPI=y
+CONFIG_S2IO=m
+CONFIG_S2IO_NAPI=y
+CONFIG_2BUFF_MODE=y
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+
+#
+# Obsolete Wireless cards support (pre-802.11)
+#
+# CONFIG_STRIP is not set
+
+#
+# Wireless 802.11b ISA/PCI cards support
+#
+CONFIG_HERMES=m
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_PCI_HERMES=m
+CONFIG_ATMEL=m
+CONFIG_PCI_ATMEL=m
+
+#
+# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
+#
+CONFIG_PRISM54=m
+CONFIG_NET_WIRELESS=y
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# ATM drivers
+#
+CONFIG_ATM_TCP=m
+# CONFIG_ATM_LANAI is not set
+# CONFIG_ATM_ENI is not set
+# CONFIG_ATM_FIRESTREAM is not set
+# CONFIG_ATM_ZATM is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_ATM_HORIZON is not set
+CONFIG_ATM_FORE200E_MAYBE=m
+CONFIG_ATM_FORE200E_PCA=y
+CONFIG_ATM_FORE200E_PCA_DEFAULT_FW=y
+CONFIG_ATM_FORE200E_SBA=y
+CONFIG_ATM_FORE200E_SBA_DEFAULT_FW=y
+CONFIG_ATM_FORE200E_USE_TASKLET=y
+CONFIG_ATM_FORE200E_TX_RETRY=16
+CONFIG_ATM_FORE200E_DEBUG=0
+CONFIG_ATM_FORE200E=m
+CONFIG_ATM_HE=m
+CONFIG_ATM_HE_USE_SUNI=y
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+CONFIG_SKFP=m
+CONFIG_HIPPI=y
+# CONFIG_ROADRUNNER is not set
+CONFIG_PLIP=m
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_PPPOATM=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+# CONFIG_SLIP_MODE_SLIP6 is not set
+CONFIG_NET_FC=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# ISDN subsystem
+#
+CONFIG_ISDN=m
+
+#
+# Old ISDN4Linux
+#
+# CONFIG_ISDN_I4L is not set
+
+#
+# CAPI subsystem
+#
+CONFIG_ISDN_CAPI=m
+# CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON is not set
+# CONFIG_ISDN_CAPI_MIDDLEWARE is not set
+CONFIG_ISDN_CAPI_CAPI20=m
+
+#
+# CAPI hardware drivers
+#
+
+#
+# Active AVM cards
+#
+CONFIG_CAPI_AVM=y
+CONFIG_ISDN_DRV_AVMB1_B1PCI=m
+CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y
+CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m
+CONFIG_ISDN_DRV_AVMB1_T1PCI=m
+CONFIG_ISDN_DRV_AVMB1_C4=m
+
+#
+# Active Eicon DIVA Server cards
+#
+CONFIG_CAPI_EICON=y
+CONFIG_ISDN_DIVAS=m
+CONFIG_ISDN_DIVAS_BRIPCI=y
+CONFIG_ISDN_DIVAS_PRIPCI=y
+CONFIG_ISDN_DIVAS_DIVACAPI=m
+CONFIG_ISDN_DIVAS_USERIDI=m
+CONFIG_ISDN_DIVAS_MAINT=m
+
+#
+# Telephony Support
+#
+CONFIG_PHONE=m
+CONFIG_PHONE_IXJ=m
+
+#
+# Unix98 PTY support
+#
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# XFree86 DRI support
+#
+CONFIG_DRM=y
+CONFIG_DRM_TDFX=m
+# CONFIG_DRM_R128 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=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input I/O drivers
+#
+CONFIG_GAMEPORT=m
+CONFIG_SOUND_GAMEPORT=m
+# CONFIG_GAMEPORT_NS558 is not set
+# CONFIG_GAMEPORT_L4 is not set
+# CONFIG_GAMEPORT_EMU10K1 is not set
+# CONFIG_GAMEPORT_VORTEX is not set
+# CONFIG_GAMEPORT_FM801 is not set
+CONFIG_GAMEPORT_CS461X=m
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+CONFIG_SERIO_PCIPS2=m
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIO_RAW=m
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+CONFIG_KEYBOARD_SUNKBD=y
+CONFIG_KEYBOARD_LKKBD=m
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_SERIAL=y
+CONFIG_MOUSE_VSXXXAA=m
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_SPARCSPKR=y
+# CONFIG_INPUT_UINPUT is not set
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=y
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+CONFIG_I2C_ALI1535=m
+CONFIG_I2C_ALI1563=m
+CONFIG_I2C_ALI15X3=m
+CONFIG_I2C_AMD756=m
+# CONFIG_I2C_AMD756_S4882 is not set
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_I810=m
+CONFIG_I2C_ISA=m
+CONFIG_I2C_NFORCE2=m
+CONFIG_I2C_PARPORT=m
+CONFIG_I2C_PARPORT_LIGHT=m
+CONFIG_I2C_PIIX4=m
+CONFIG_I2C_PROSAVAGE=m
+CONFIG_I2C_SAVAGE4=m
+CONFIG_SCx200_ACB=m
+CONFIG_I2C_SIS5595=m
+CONFIG_I2C_SIS630=m
+CONFIG_I2C_SIS96X=m
+CONFIG_I2C_STUB=m
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+CONFIG_I2C_VOODOO3=m
+CONFIG_I2C_PCA_ISA=m
+
+#
+# Hardware Sensors Chip support
+#
+CONFIG_I2C_SENSOR=m
+CONFIG_SENSORS_ADM1021=m
+CONFIG_SENSORS_ADM1025=m
+CONFIG_SENSORS_ADM1026=m
+CONFIG_SENSORS_ADM1031=m
+CONFIG_SENSORS_ASB100=m
+CONFIG_SENSORS_DS1621=m
+CONFIG_SENSORS_FSCHER=m
+CONFIG_SENSORS_FSCPOS=m
+CONFIG_SENSORS_GL518SM=m
+CONFIG_SENSORS_GL520SM=m
+CONFIG_SENSORS_IT87=m
+CONFIG_SENSORS_LM63=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_LM77=m
+CONFIG_SENSORS_LM78=m
+CONFIG_SENSORS_LM80=m
+CONFIG_SENSORS_LM83=m
+CONFIG_SENSORS_LM85=m
+CONFIG_SENSORS_LM87=m
+CONFIG_SENSORS_LM90=m
+CONFIG_SENSORS_MAX1619=m
+CONFIG_SENSORS_PC87360=m
+CONFIG_SENSORS_SMSC47B397=m
+CONFIG_SENSORS_SIS5595=m
+CONFIG_SENSORS_SMSC47M1=m
+CONFIG_SENSORS_VIA686A=m
+CONFIG_SENSORS_W83781D=m
+CONFIG_SENSORS_W83L785TS=m
+CONFIG_SENSORS_W83627HF=m
+
+#
+# Other I2C Chip support
+#
+CONFIG_SENSORS_EEPROM=m
+CONFIG_SENSORS_PCF8574=m
+CONFIG_SENSORS_PCF8591=m
+CONFIG_SENSORS_RTC8564=m
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+CONFIG_JFS_FS=m
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+
+#
+# XFS support
+#
+CONFIG_XFS_FS=m
+CONFIG_XFS_EXPORT=y
+# CONFIG_XFS_RT is not set
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_SECURITY=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_MINIX_FS=m
+CONFIG_ROMFS_FS=m
+# CONFIG_QUOTA is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+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=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS_XATTR=y
+# CONFIG_DEVPTS_FS_SECURITY is not set
+CONFIG_TMPFS=y
+CONFIG_TMPFS_XATTR=y
+CONFIG_TMPFS_SECURITY=y
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+CONFIG_ADFS_FS=m
+# CONFIG_ADFS_FS_RW is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_BEFS_FS=m
+# CONFIG_BEFS_DEBUG is not set
+CONFIG_BFS_FS=m
+CONFIG_EFS_FS=m
+CONFIG_CRAMFS=m
+CONFIG_VXFS_FS=m
+CONFIG_HPFS_FS=m
+CONFIG_QNX4FS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_UFS_FS_WRITE=y
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_DIRECTIO=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_RPCSEC_GSS_SPKM3=m
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+CONFIG_AFS_FS=m
+CONFIG_RXRPC=m
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_SUN_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=y
+
+#
+# Video For Linux
+#
+
+#
+# Video Adapters
+#
+CONFIG_VIDEO_BT848=m
+CONFIG_VIDEO_BWQCAM=m
+CONFIG_VIDEO_CQCAM=m
+CONFIG_VIDEO_W9966=m
+CONFIG_VIDEO_CPIA=m
+CONFIG_VIDEO_CPIA_PP=m
+CONFIG_VIDEO_CPIA_USB=m
+CONFIG_VIDEO_SAA5246A=m
+CONFIG_VIDEO_SAA5249=m
+CONFIG_TUNER_3036=m
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_SAA7134 is not set
+CONFIG_VIDEO_MXB=m
+CONFIG_VIDEO_DPC=m
+CONFIG_VIDEO_HEXIUM_ORION=m
+CONFIG_VIDEO_HEXIUM_GEMINI=m
+CONFIG_VIDEO_CX88=m
+CONFIG_VIDEO_OVCAMCHIP=m
+
+#
+# Radio Adapters
+#
+CONFIG_RADIO_GEMTEK_PCI=m
+CONFIG_RADIO_MAXIRADIO=m
+CONFIG_RADIO_MAESTRO=m
+
+#
+# Digital Video Broadcasting Devices
+#
+CONFIG_DVB=y
+CONFIG_DVB_CORE=m
+
+#
+# Supported SAA7146 based PCI Adapters
+#
+CONFIG_DVB_AV7110=m
+# CONFIG_DVB_AV7110_OSD is not set
+CONFIG_DVB_BUDGET=m
+CONFIG_DVB_BUDGET_CI=m
+CONFIG_DVB_BUDGET_AV=m
+CONFIG_DVB_BUDGET_PATCH=m
+
+#
+# Supported USB Adapters
+#
+# CONFIG_DVB_TTUSB_BUDGET is not set
+CONFIG_DVB_TTUSB_DEC=m
+CONFIG_DVB_DIBUSB=m
+# CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES is not set
+CONFIG_DVB_DIBCOM_DEBUG=y
+CONFIG_DVB_CINERGYT2=m
+# CONFIG_DVB_CINERGYT2_TUNING is not set
+
+#
+# Supported FlexCopII (B2C2) Adapters
+#
+CONFIG_DVB_B2C2_SKYSTAR=m
+CONFIG_DVB_B2C2_USB=m
+
+#
+# Supported BT878 Adapters
+#
+CONFIG_DVB_BT8XX=m
+
+#
+# Supported DVB Frontends
+#
+
+#
+# Customise DVB Frontends
+#
+
+#
+# DVB-S (satellite) frontends
+#
+CONFIG_DVB_STV0299=m
+CONFIG_DVB_CX24110=m
+CONFIG_DVB_TDA8083=m
+CONFIG_DVB_TDA80XX=m
+CONFIG_DVB_MT312=m
+CONFIG_DVB_VES1X93=m
+
+#
+# DVB-T (terrestrial) frontends
+#
+CONFIG_DVB_SP8870=m
+CONFIG_DVB_SP887X=m
+CONFIG_DVB_CX22700=m
+CONFIG_DVB_CX22702=m
+CONFIG_DVB_L64781=m
+CONFIG_DVB_TDA1004X=m
+CONFIG_DVB_NXT6000=m
+CONFIG_DVB_MT352=m
+CONFIG_DVB_DIB3000MB=m
+CONFIG_DVB_DIB3000MC=m
+
+#
+# DVB-C (cable) frontends
+#
+CONFIG_DVB_ATMEL_AT76C651=m
+CONFIG_DVB_VES1820=m
+CONFIG_DVB_TDA10021=m
+CONFIG_DVB_STV0297=m
+
+#
+# ATSC (North American/Korean Terresterial DTV) frontends
+#
+CONFIG_DVB_NXT2002=m
+CONFIG_VIDEO_SAA7146=m
+CONFIG_VIDEO_SAA7146_VV=m
+CONFIG_VIDEO_VIDEOBUF=m
+CONFIG_VIDEO_TUNER=m
+CONFIG_VIDEO_BUF=m
+CONFIG_VIDEO_BTCX=m
+CONFIG_VIDEO_IR=m
+CONFIG_VIDEO_TVEEPROM=m
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_BIT32_EMUL=m
+# CONFIG_SND_RTCTIMER is not set
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_MPU401_UART=m
+CONFIG_SND_OPL3_LIB=m
+CONFIG_SND_VX_LIB=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_VIRMIDI=m
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# PCI devices
+#
+CONFIG_SND_AC97_CODEC=m
+CONFIG_SND_ALI5451=m
+CONFIG_SND_ATIIXP=m
+CONFIG_SND_ATIIXP_MODEM=m
+CONFIG_SND_AU8810=m
+CONFIG_SND_AU8820=m
+CONFIG_SND_AU8830=m
+CONFIG_SND_AZT3328=m
+CONFIG_SND_BT87X=m
+# CONFIG_SND_BT87X_OVERCLOCK is not set
+CONFIG_SND_CS46XX=m
+# CONFIG_SND_CS46XX_NEW_DSP is not set
+CONFIG_SND_CS4281=m
+CONFIG_SND_EMU10K1=m
+CONFIG_SND_EMU10K1X=m
+CONFIG_SND_CA0106=m
+CONFIG_SND_KORG1212=m
+CONFIG_SND_MIXART=m
+CONFIG_SND_NM256=m
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_HDSP is not set
+CONFIG_SND_TRIDENT=m
+CONFIG_SND_YMFPCI=m
+CONFIG_SND_ALS4000=m
+CONFIG_SND_CMIPCI=m
+CONFIG_SND_ENS1370=m
+CONFIG_SND_ENS1371=m
+CONFIG_SND_ES1938=m
+CONFIG_SND_ES1968=m
+CONFIG_SND_MAESTRO3=m
+CONFIG_SND_FM801=m
+CONFIG_SND_FM801_TEA575X=m
+CONFIG_SND_ICE1712=m
+# CONFIG_SND_ICE1724 is not set
+CONFIG_SND_INTEL8X0=m
+CONFIG_SND_INTEL8X0M=m
+CONFIG_SND_SONICVIBES=m
+# CONFIG_SND_VIA82XX is not set
+CONFIG_SND_VIA82XX_MODEM=m
+CONFIG_SND_VX222=m
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+
+#
+# ALSA Sparc devices
+#
+CONFIG_SND_SUN_AMD7930=m
+CONFIG_SND_SUN_CS4231=m
+
+#
+# USB support
+#
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=m
+# CONFIG_USB_EHCI_SPLIT_ISO is not set
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_UHCI_HCD=m
+CONFIG_USB_SL811_HCD=m
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_AUDIO is not set
+
+#
+# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem
+#
+# CONFIG_USB_MIDI is not set
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+CONFIG_USB_STORAGE_RW_DETECT=y
+# CONFIG_USB_STORAGE_DATAFAB is not set
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_DPCM=y
+CONFIG_USB_STORAGE_HP8200e=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+CONFIG_USB_HIDINPUT=y
+# CONFIG_HID_FF is not set
+CONFIG_USB_HIDDEV=y
+# CONFIG_USB_AIPTEK is not set
+CONFIG_USB_WACOM=m
+CONFIG_USB_KBTAB=m
+# CONFIG_USB_POWERMATE is not set
+CONFIG_USB_MTOUCH=m
+CONFIG_USB_EGALAX=m
+# CONFIG_USB_XPAD is not set
+CONFIG_USB_ATI_REMOTE=m
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+
+#
+# USB Multimedia devices
+#
+# CONFIG_USB_DABUSB is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+CONFIG_USB_SN9C102=m
+# CONFIG_USB_STV680 is not set
+CONFIG_USB_W9968CF=m
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+
+#
+# USB Host-to-Host Cables
+#
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_AN2720=y
+CONFIG_USB_BELKIN=y
+CONFIG_USB_GENESYS=y
+CONFIG_USB_NET1080=y
+CONFIG_USB_PL2301=y
+CONFIG_USB_KC2190=y
+
+#
+# Intelligent USB Devices/Gadgets
+#
+CONFIG_USB_ARMLINUX=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_ZAURUS=y
+CONFIG_USB_CDCETHER=y
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_AX8817X=y
+
+#
+# USB port drivers
+#
+CONFIG_USB_USS720=m
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_VISOR is not set
+CONFIG_USB_SERIAL_IPAQ=m
+# CONFIG_USB_SERIAL_IR is not set
+CONFIG_USB_SERIAL_EDGEPORT=m
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_SAFE is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+CONFIG_USB_LED=m
+CONFIG_USB_CYTHERM=m
+CONFIG_USB_PHIDGETKIT=m
+CONFIG_USB_PHIDGETSERVO=m
+CONFIG_USB_IDMOUSE=m
+CONFIG_USB_TEST=m
+
+#
+# USB ATM/DSL drivers
+#
+CONFIG_USB_ATM=m
+CONFIG_USB_SPEEDTOUCH=m
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# InfiniBand support
+#
+CONFIG_INFINIBAND=m
+CONFIG_INFINIBAND_MTHCA=m
+# CONFIG_INFINIBAND_MTHCA_DEBUG is not set
+CONFIG_INFINIBAND_IPOIB=m
+# CONFIG_INFINIBAND_IPOIB_DEBUG is not set
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+CONFIG_SOFT_WATCHDOG=m
+CONFIG_WATCHDOG_CP1XXX=m
+CONFIG_WATCHDOG_RIO=m
+
+#
+# PCI-based Watchdog Cards
+#
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_WDTPCI=m
+CONFIG_WDT_501_PCI=y
+
+#
+# USB-based Watchdog Cards
+#
+CONFIG_USBPCWATCHDOG=m
+
+#
+# Profiling support
+#
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+CONFIG_KPROBES=y
+# CONFIG_DEBUG_DCFLUSH is not set
+# CONFIG_STACK_DEBUG is not set
+# CONFIG_DEBUG_BOOTMEM is not set
+CONFIG_HAVE_DEC_LOCK=y
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+# CONFIG_KEYS_DEBUG_PROC_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
new file mode 100644
index 000000000000..093281bdf85f
--- /dev/null
+++ b/arch/sparc64/kernel/Makefile
@@ -0,0 +1,44 @@
+# $Id: Makefile,v 1.70 2002/02/09 19:49:30 davem Exp $
+# Makefile for the linux kernel.
+#
+
+EXTRA_AFLAGS := -ansi
+EXTRA_CFLAGS := -Werror
+
+extra-y := head.o init_task.o vmlinux.lds
+
+obj-y := process.o setup.o cpu.o idprom.o \
+ traps.o devices.o auxio.o \
+ irq.o ptrace.o time.o sys_sparc.o signal.o \
+ unaligned.o central.o pci.o starfire.o semaphore.o \
+ power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o
+
+obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
+ pci_psycho.o pci_sabre.o pci_schizo.o
+obj-$(CONFIG_SMP) += smp.o trampoline.o
+obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o ioctl32.o
+obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
+obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o
+obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
+obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
+obj-$(CONFIG_KPROBES) += kprobes.o
+
+ifdef CONFIG_SUNOS_EMUL
+ obj-y += sys_sunos32.o sunos_ioctl32.o
+else
+ ifdef CONFIG_SOLARIS_EMUL
+ obj-y += sys_sunos32.o sunos_ioctl32.o
+ endif
+endif
+
+ifneq ($(NEW_GCC),y)
+ CMODEL_CFLAG := -mmedlow
+else
+ CMODEL_CFLAG := -m64 -mcmodel=medlow
+endif
+
+head.o: head.S ttable.S itlb_base.S dtlb_base.S dtlb_backend.S dtlb_prot.S \
+ etrap.S rtrap.S winfixup.S entry.S
+
+CFLAGS_ioctl32.o += -Ifs/
diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c
new file mode 100644
index 000000000000..a0716ccc2f4a
--- /dev/null
+++ b/arch/sparc64/kernel/auxio.c
@@ -0,0 +1,152 @@
+/* auxio.c: Probing for the Sparc AUXIO register at boot time.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Refactoring for unified NCR/PCIO support 2002 Eric Brower (ebrower@usa.net)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/sbus.h>
+#include <asm/ebus.h>
+#include <asm/auxio.h>
+
+/* This cannot be static, as it is referenced in entry.S */
+void __iomem *auxio_register = NULL;
+
+enum auxio_type {
+ AUXIO_TYPE_NODEV,
+ AUXIO_TYPE_SBUS,
+ AUXIO_TYPE_EBUS
+};
+
+static enum auxio_type auxio_devtype = AUXIO_TYPE_NODEV;
+static DEFINE_SPINLOCK(auxio_lock);
+
+static void __auxio_sbus_set(u8 bits_on, u8 bits_off)
+{
+ if (auxio_register) {
+ unsigned char regval;
+ unsigned long flags;
+ unsigned char newval;
+
+ spin_lock_irqsave(&auxio_lock, flags);
+
+ regval = sbus_readb(auxio_register);
+ newval = regval | bits_on;
+ newval &= ~bits_off;
+ newval &= ~AUXIO_AUX1_MASK;
+ sbus_writeb(newval, auxio_register);
+
+ spin_unlock_irqrestore(&auxio_lock, flags);
+ }
+}
+
+static void __auxio_ebus_set(u8 bits_on, u8 bits_off)
+{
+ if (auxio_register) {
+ unsigned char regval;
+ unsigned long flags;
+ unsigned char newval;
+
+ spin_lock_irqsave(&auxio_lock, flags);
+
+ regval = (u8)readl(auxio_register);
+ newval = regval | bits_on;
+ newval &= ~bits_off;
+ writel((u32)newval, auxio_register);
+
+ spin_unlock_irqrestore(&auxio_lock, flags);
+ }
+}
+
+static inline void __auxio_ebus_set_led(int on)
+{
+ (on) ? __auxio_ebus_set(AUXIO_PCIO_LED, 0) :
+ __auxio_ebus_set(0, AUXIO_PCIO_LED) ;
+}
+
+static inline void __auxio_sbus_set_led(int on)
+{
+ (on) ? __auxio_sbus_set(AUXIO_AUX1_LED, 0) :
+ __auxio_sbus_set(0, AUXIO_AUX1_LED) ;
+}
+
+void auxio_set_led(int on)
+{
+ switch(auxio_devtype) {
+ case AUXIO_TYPE_SBUS:
+ __auxio_sbus_set_led(on);
+ break;
+ case AUXIO_TYPE_EBUS:
+ __auxio_ebus_set_led(on);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void __auxio_sbus_set_lte(int on)
+{
+ (on) ? __auxio_sbus_set(AUXIO_AUX1_LTE, 0) :
+ __auxio_sbus_set(0, AUXIO_AUX1_LTE) ;
+}
+
+void auxio_set_lte(int on)
+{
+ switch(auxio_devtype) {
+ case AUXIO_TYPE_SBUS:
+ __auxio_sbus_set_lte(on);
+ break;
+ case AUXIO_TYPE_EBUS:
+ /* FALL-THROUGH */
+ default:
+ break;
+ }
+}
+
+void __init auxio_probe(void)
+{
+ struct sbus_bus *sbus;
+ struct sbus_dev *sdev = NULL;
+
+ for_each_sbus(sbus) {
+ for_each_sbusdev(sdev, sbus) {
+ if(!strcmp(sdev->prom_name, "auxio"))
+ goto found_sdev;
+ }
+ }
+
+found_sdev:
+ if (sdev) {
+ auxio_devtype = AUXIO_TYPE_SBUS;
+ auxio_register = sbus_ioremap(&sdev->resource[0], 0,
+ sdev->reg_addrs[0].reg_size,
+ "auxiliaryIO");
+ }
+#ifdef CONFIG_PCI
+ else {
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev = NULL;
+
+ for_each_ebus(ebus) {
+ for_each_ebusdev(edev, ebus) {
+ if (!strcmp(edev->prom_name, "auxio"))
+ goto ebus_done;
+ }
+ }
+ ebus_done:
+ if (edev) {
+ auxio_devtype = AUXIO_TYPE_EBUS;
+ auxio_register =
+ ioremap(edev->resource[0].start, sizeof(u32));
+ }
+ }
+ auxio_set_led(AUXIO_LED_ON);
+#endif
+}
diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c
new file mode 100644
index 000000000000..b2854ef221d0
--- /dev/null
+++ b/arch/sparc64/kernel/binfmt_aout32.c
@@ -0,0 +1,424 @@
+/*
+ * linux/fs/binfmt_aout.c
+ *
+ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
+ *
+ * Hacked a bit by DaveM to make it work with 32-bit SunOS
+ * binaries on the sparc64 port.
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/slab.h>
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+
+static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs);
+static int load_aout32_library(struct file*);
+static int aout32_core_dump(long signr, struct pt_regs * regs, struct file *file);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+static struct linux_binfmt aout32_format = {
+ NULL, THIS_MODULE, load_aout32_binary, load_aout32_library, aout32_core_dump,
+ PAGE_SIZE
+};
+
+static void set_brk(unsigned long start, unsigned long end)
+{
+ start = PAGE_ALIGN(start);
+ end = PAGE_ALIGN(end);
+ if (end <= start)
+ return;
+ down_write(&current->mm->mmap_sem);
+ do_brk(start, end - start);
+ up_write(&current->mm->mmap_sem);
+}
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * macros to write out all the necessary info.
+ */
+
+static int dump_write(struct file *file, const void *addr, int nr)
+{
+ return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
+}
+
+#define DUMP_WRITE(addr, nr) \
+ if (!dump_write(file, (void *)(addr), (nr))) \
+ goto end_coredump;
+
+#define DUMP_SEEK(offset) \
+if (file->f_op->llseek) { \
+ if (file->f_op->llseek(file,(offset),0) != (offset)) \
+ goto end_coredump; \
+} else file->f_pos = (offset)
+
+/*
+ * Routine writes a core dump image in the current directory.
+ * Currently only a stub-function.
+ *
+ * Note that setuid/setgid files won't make a core-dump if the uid/gid
+ * changed due to the set[u|g]id. It's enforced by the "current->mm->dumpable"
+ * field, which also makes sure the core-dumps won't be recursive if the
+ * dumping of the process results in another error..
+ */
+
+static int aout32_core_dump(long signr, struct pt_regs *regs, struct file *file)
+{
+ mm_segment_t fs;
+ int has_dumped = 0;
+ unsigned long dump_start, dump_size;
+ struct user dump;
+# define START_DATA(u) (u.u_tsize)
+# define START_STACK(u) ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1))
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+ strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
+ dump.signal = signr;
+ dump_thread(regs, &dump);
+
+/* If the size of the dump file exceeds the rlimit, then see what would happen
+ if we wrote the stack, but not the data area. */
+ if ((dump.u_dsize+dump.u_ssize) >
+ current->signal->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_dsize = 0;
+
+/* Make sure we have enough room to write the stack and data areas. */
+ if ((dump.u_ssize) >
+ current->signal->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_ssize = 0;
+
+/* make sure we actually have a data and stack area to dump */
+ set_fs(USER_DS);
+ if (!access_ok(VERIFY_READ, (void __user *) START_DATA(dump), dump.u_dsize))
+ dump.u_dsize = 0;
+ if (!access_ok(VERIFY_READ, (void __user *) START_STACK(dump), dump.u_ssize))
+ dump.u_ssize = 0;
+
+ set_fs(KERNEL_DS);
+/* struct user */
+ DUMP_WRITE(&dump,sizeof(dump));
+/* now we start writing out the user space info */
+ set_fs(USER_DS);
+/* Dump the data area */
+ if (dump.u_dsize != 0) {
+ dump_start = START_DATA(dump);
+ dump_size = dump.u_dsize;
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Now prepare to dump the stack area */
+ if (dump.u_ssize != 0) {
+ dump_start = START_STACK(dump);
+ dump_size = dump.u_ssize;
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Finally dump the task struct. Not be used by gdb, but could be useful */
+ set_fs(KERNEL_DS);
+ DUMP_WRITE(current,sizeof(*current));
+end_coredump:
+ set_fs(fs);
+ return has_dumped;
+}
+
+/*
+ * create_aout32_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+
+static u32 __user *create_aout32_tables(char __user *p, struct linux_binprm *bprm)
+{
+ u32 __user *argv;
+ u32 __user *envp;
+ u32 __user *sp;
+ int argc = bprm->argc;
+ int envc = bprm->envc;
+
+ sp = (u32 __user *)((-(unsigned long)sizeof(char *))&(unsigned long)p);
+
+ /* This imposes the proper stack alignment for a new process. */
+ sp = (u32 __user *) (((unsigned long) sp) & ~7);
+ if ((envc+argc+3)&1)
+ --sp;
+
+ sp -= envc+1;
+ envp = sp;
+ sp -= argc+1;
+ argv = sp;
+ put_user(argc,--sp);
+ current->mm->arg_start = (unsigned long) p;
+ while (argc-->0) {
+ char c;
+ put_user(((u32)(unsigned long)(p)),argv++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,argv);
+ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
+ while (envc-->0) {
+ char c;
+ put_user(((u32)(unsigned long)(p)),envp++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,envp);
+ current->mm->env_end = (unsigned long) p;
+ return sp;
+}
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ struct exec ex;
+ unsigned long error;
+ unsigned long fd_offset;
+ unsigned long rlim;
+ unsigned long orig_thr_flags;
+ int retval;
+
+ ex = *((struct exec *) bprm->buf); /* exec-header */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
+ N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
+ N_TRSIZE(ex) || N_DRSIZE(ex) ||
+ bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+
+ fd_offset = N_TXTOFF(ex);
+
+ /* Check initial limits. This avoids letting people circumvent
+ * size limits imposed on them by creating programs with large
+ * arrays in the data or bss.
+ */
+ rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
+ if (rlim >= RLIM_INFINITY)
+ rlim = ~0;
+ if (ex.a_data + ex.a_bss > rlim)
+ return -ENOMEM;
+
+ /* Flush all traces of the currently running executable */
+ retval = flush_old_exec(bprm);
+ if (retval)
+ return retval;
+
+ /* OK, This is the point of no return */
+ set_personality(PER_SUNOS);
+
+ current->mm->end_code = ex.a_text +
+ (current->mm->start_code = N_TXTADDR(ex));
+ current->mm->end_data = ex.a_data +
+ (current->mm->start_data = N_DATADDR(ex));
+ current->mm->brk = ex.a_bss +
+ (current->mm->start_brk = N_BSSADDR(ex));
+
+ set_mm_counter(current->mm, rss, 0);
+ current->mm->mmap = NULL;
+ compute_creds(bprm);
+ current->flags &= ~PF_FORKNOEXEC;
+ if (N_MAGIC(ex) == NMAGIC) {
+ loff_t pos = fd_offset;
+ /* Fuck me plenty... */
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(N_TXTADDR(ex), ex.a_text);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex),
+ ex.a_text, &pos);
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(N_DATADDR(ex), ex.a_data);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file, (char __user *)N_DATADDR(ex),
+ ex.a_data, &pos);
+ goto beyond_if;
+ }
+
+ if (N_MAGIC(ex) == OMAGIC) {
+ loff_t pos = fd_offset;
+ down_write(&current->mm->mmap_sem);
+ do_brk(N_TXTADDR(ex) & PAGE_MASK,
+ ex.a_text+ex.a_data + PAGE_SIZE - 1);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex),
+ ex.a_text+ex.a_data, &pos);
+ } else {
+ static unsigned long error_time;
+ if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
+ (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time) > 5*HZ)
+ {
+ printk(KERN_NOTICE "executable not page aligned\n");
+ error_time = jiffies;
+ }
+
+ if (!bprm->file->f_op->mmap) {
+ loff_t pos = fd_offset;
+ down_write(&current->mm->mmap_sem);
+ do_brk(0, ex.a_text+ex.a_data);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file,
+ (char __user *)N_TXTADDR(ex),
+ ex.a_text+ex.a_data, &pos);
+ goto beyond_if;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset);
+ up_write(&current->mm->mmap_sem);
+
+ if (error != N_TXTADDR(ex)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset + ex.a_text);
+ up_write(&current->mm->mmap_sem);
+ if (error != N_DATADDR(ex)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+ }
+beyond_if:
+ set_binfmt(&aout32_format);
+
+ set_brk(current->mm->start_brk, current->mm->brk);
+
+ /* Make sure STACK_TOP returns the right thing. */
+ orig_thr_flags = current_thread_info()->flags;
+ current_thread_info()->flags |= _TIF_32BIT;
+
+ retval = setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
+ if (retval < 0) {
+ current_thread_info()->flags = orig_thr_flags;
+
+ /* Someone check-me: is this error path enough? */
+ send_sig(SIGKILL, current, 0);
+ return retval;
+ }
+
+ current->mm->start_stack =
+ (unsigned long) create_aout32_tables((char __user *)bprm->p, bprm);
+ if (!(orig_thr_flags & _TIF_32BIT)) {
+ unsigned long pgd_cache = get_pgd_cache(current->mm->pgd);
+
+ __asm__ __volatile__("stxa\t%0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (pgd_cache),
+ "r" (TSB_REG), "i" (ASI_DMMU));
+ }
+ start_thread32(regs, ex.a_entry, current->mm->start_stack);
+ if (current->ptrace & PT_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ return 0;
+}
+
+/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */
+static int load_aout32_library(struct file *file)
+{
+ struct inode * inode;
+ unsigned long bss, start_addr, len;
+ unsigned long error;
+ int retval;
+ struct exec ex;
+
+ inode = file->f_dentry->d_inode;
+
+ retval = -ENOEXEC;
+ error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
+ if (error != sizeof(ex))
+ goto out;
+
+ /* We come in here for the regular a.out style of shared libraries */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
+ N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
+ inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ goto out;
+ }
+
+ if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
+ (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+ printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
+ goto out;
+ }
+
+ if (N_FLAGS(ex))
+ goto out;
+
+ /* For QMAGIC, the starting address is 0x20 into the page. We mask
+ this off to get the starting address for the page */
+
+ start_addr = ex.a_entry & 0xfffff000;
+
+ /* Now use mmap to map the library into memory. */
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+ N_TXTOFF(ex));
+ up_write(&current->mm->mmap_sem);
+ retval = error;
+ if (error != start_addr)
+ goto out;
+
+ len = PAGE_ALIGN(ex.a_text + ex.a_data);
+ bss = ex.a_text + ex.a_data + ex.a_bss;
+ if (bss > len) {
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(start_addr + len, bss - len);
+ up_write(&current->mm->mmap_sem);
+ retval = error;
+ if (error != start_addr + len)
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+}
+
+static int __init init_aout32_binfmt(void)
+{
+ return register_binfmt(&aout32_format);
+}
+
+static void __exit exit_aout32_binfmt(void)
+{
+ unregister_binfmt(&aout32_format);
+}
+
+module_init(init_aout32_binfmt);
+module_exit(exit_aout32_binfmt);
diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c
new file mode 100644
index 000000000000..a1a12d2aa353
--- /dev/null
+++ b/arch/sparc64/kernel/binfmt_elf32.c
@@ -0,0 +1,159 @@
+/*
+ * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#define ELF_ARCH EM_SPARC
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2MSB;
+
+/* For the most part we present code dumps in the format
+ * Solaris does.
+ */
+typedef unsigned int elf_greg_t;
+#define ELF_NGREG 38
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+/* Format is:
+ * G0 --> G7
+ * O0 --> O7
+ * L0 --> L7
+ * I0 --> I7
+ * PSR, PC, nPC, Y, WIM, TBR
+ */
+#include <asm/psrcompat.h>
+#define ELF_CORE_COPY_REGS(__elf_regs, __pt_regs) \
+do { unsigned int *dest = &(__elf_regs[0]); \
+ struct pt_regs *src = (__pt_regs); \
+ unsigned int __user *sp; \
+ int i; \
+ for(i = 0; i < 16; i++) \
+ dest[i] = (unsigned int) src->u_regs[i];\
+ /* Don't try this at home kids... */ \
+ sp = (unsigned int __user *) (src->u_regs[14] & \
+ 0x00000000fffffffc); \
+ for(i = 0; i < 16; i++) \
+ __get_user(dest[i+16], &sp[i]); \
+ dest[32] = tstate_to_psr(src->tstate); \
+ dest[33] = (unsigned int) src->tpc; \
+ dest[34] = (unsigned int) src->tnpc; \
+ dest[35] = src->y; \
+ dest[36] = dest[37] = 0; /* XXX */ \
+} while(0);
+
+typedef struct {
+ union {
+ unsigned int pr_regs[32];
+ unsigned long pr_dregs[16];
+ } pr_fr;
+ unsigned int __unused;
+ unsigned int pr_fsr;
+ unsigned char pr_qcnt;
+ unsigned char pr_q_entrysize;
+ unsigned char pr_en;
+ unsigned int pr_q[64];
+} elf_fpregset_t;
+
+/* UltraSparc extensions. Still unused, but will be eventually. */
+typedef struct {
+ unsigned int pr_type;
+ unsigned int pr_align;
+ union {
+ struct {
+ union {
+ unsigned int pr_regs[32];
+ unsigned long pr_dregs[16];
+ long double pr_qregs[8];
+ } pr_xfr;
+ } pr_v8p;
+ unsigned int pr_xfsr;
+ unsigned int pr_fprs;
+ unsigned int pr_xg[8];
+ unsigned int pr_xo[8];
+ unsigned long pr_tstate;
+ unsigned int pr_filler[8];
+ } pr_un;
+} elf_xregset_t;
+
+#define elf_check_arch(x) (((x)->e_machine == EM_SPARC) || ((x)->e_machine == EM_SPARC32PLUS))
+
+#define ELF_ET_DYN_BASE 0x70000000
+
+
+#include <asm/processor.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/elfcore.h>
+#include <linux/compat.h>
+
+#define elf_prstatus elf_prstatus32
+struct elf_prstatus32
+{
+ struct elf_siginfo pr_info; /* Info associated with signal */
+ short pr_cursig; /* Current signal */
+ unsigned int pr_sigpend; /* Set of pending signals */
+ unsigned int pr_sighold; /* Set of held signals */
+ pid_t pr_pid;
+ pid_t pr_ppid;
+ pid_t pr_pgrp;
+ pid_t pr_sid;
+ struct compat_timeval pr_utime; /* User time */
+ struct compat_timeval pr_stime; /* System time */
+ struct compat_timeval pr_cutime; /* Cumulative user time */
+ struct compat_timeval pr_cstime; /* Cumulative system time */
+ elf_gregset_t pr_reg; /* GP registers */
+ int pr_fpvalid; /* True if math co-processor being used. */
+};
+
+#define elf_prpsinfo elf_prpsinfo32
+struct elf_prpsinfo32
+{
+ char pr_state; /* numeric process state */
+ char pr_sname; /* char for pr_state */
+ char pr_zomb; /* zombie */
+ char pr_nice; /* nice val */
+ unsigned int pr_flag; /* flags */
+ u16 pr_uid;
+ u16 pr_gid;
+ pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid;
+ /* Lots missing */
+ char pr_fname[16]; /* filename of executable */
+ char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
+};
+
+#include <linux/highuid.h>
+
+#undef NEW_TO_OLD_UID
+#undef NEW_TO_OLD_GID
+#define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid)
+#define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid)
+
+#include <linux/time.h>
+
+#undef cputime_to_timeval
+#define cputime_to_timeval cputime_to_compat_timeval
+static __inline__ void
+cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
+{
+ unsigned long jiffies = cputime_to_jiffies(cputime);
+ value->tv_usec = (jiffies % HZ) * (1000000L / HZ);
+ value->tv_sec = jiffies / HZ;
+}
+
+#define elf_addr_t u32
+#undef start_thread
+#define start_thread start_thread32
+#define init_elf_binfmt init_elf32_binfmt
+
+MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit SparcLinux binaries on the Ultra");
+MODULE_AUTHOR("Eric Youngdale, David S. Miller, Jakub Jelinek");
+
+#undef MODULE_DESCRIPTION
+#undef MODULE_AUTHOR
+
+#undef TASK_SIZE
+#define TASK_SIZE 0xf0000000
+
+#include "../../../fs/binfmt_elf.c"
diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c
new file mode 100644
index 000000000000..3d184a784968
--- /dev/null
+++ b/arch/sparc64/kernel/central.c
@@ -0,0 +1,457 @@
+/* $Id: central.c,v 1.15 2001/12/19 00:29:51 davem Exp $
+ * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
+ *
+ * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/page.h>
+#include <asm/fhc.h>
+#include <asm/starfire.h>
+
+struct linux_central *central_bus = NULL;
+struct linux_fhc *fhc_list = NULL;
+
+#define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child)
+
+static void central_probe_failure(int line)
+{
+ prom_printf("CENTRAL: Critical device probe failure at central.c:%d\n",
+ line);
+ prom_halt();
+}
+
+static void central_ranges_init(int cnode, struct linux_central *central)
+{
+ int success;
+
+ central->num_central_ranges = 0;
+ success = prom_getproperty(central->prom_node, "ranges",
+ (char *) central->central_ranges,
+ sizeof (central->central_ranges));
+ if (success != -1)
+ central->num_central_ranges = (success/sizeof(struct linux_prom_ranges));
+}
+
+static void fhc_ranges_init(int fnode, struct linux_fhc *fhc)
+{
+ int success;
+
+ fhc->num_fhc_ranges = 0;
+ success = prom_getproperty(fhc->prom_node, "ranges",
+ (char *) fhc->fhc_ranges,
+ sizeof (fhc->fhc_ranges));
+ if (success != -1)
+ fhc->num_fhc_ranges = (success/sizeof(struct linux_prom_ranges));
+}
+
+/* Range application routines are exported to various drivers,
+ * so do not __init this.
+ */
+static void adjust_regs(struct linux_prom_registers *regp, int nregs,
+ struct linux_prom_ranges *rangep, int nranges)
+{
+ int regc, rngc;
+
+ for (regc = 0; regc < nregs; regc++) {
+ for (rngc = 0; rngc < nranges; rngc++)
+ if (regp[regc].which_io == rangep[rngc].ot_child_space)
+ break; /* Fount it */
+ if (rngc == nranges) /* oops */
+ central_probe_failure(__LINE__);
+ regp[regc].which_io = rangep[rngc].ot_parent_space;
+ regp[regc].phys_addr -= rangep[rngc].ot_child_base;
+ regp[regc].phys_addr += rangep[rngc].ot_parent_base;
+ }
+}
+
+/* Apply probed fhc ranges to registers passed, if no ranges return. */
+void apply_fhc_ranges(struct linux_fhc *fhc,
+ struct linux_prom_registers *regs,
+ int nregs)
+{
+ if (fhc->num_fhc_ranges)
+ adjust_regs(regs, nregs, fhc->fhc_ranges,
+ fhc->num_fhc_ranges);
+}
+
+/* Apply probed central ranges to registers passed, if no ranges return. */
+void apply_central_ranges(struct linux_central *central,
+ struct linux_prom_registers *regs, int nregs)
+{
+ if (central->num_central_ranges)
+ adjust_regs(regs, nregs, central->central_ranges,
+ central->num_central_ranges);
+}
+
+void * __init central_alloc_bootmem(unsigned long size)
+{
+ void *ret;
+
+ ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
+ if (ret != NULL)
+ memset(ret, 0, size);
+
+ return ret;
+}
+
+static unsigned long prom_reg_to_paddr(struct linux_prom_registers *r)
+{
+ unsigned long ret = ((unsigned long) r->which_io) << 32;
+
+ return ret | (unsigned long) r->phys_addr;
+}
+
+static void probe_other_fhcs(void)
+{
+ struct linux_prom64_registers fpregs[6];
+ char namebuf[128];
+ int node;
+
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "fhc");
+ if (node == 0)
+ central_probe_failure(__LINE__);
+ while (node) {
+ struct linux_fhc *fhc;
+ int board;
+ u32 tmp;
+
+ fhc = (struct linux_fhc *)
+ central_alloc_bootmem(sizeof(struct linux_fhc));
+ if (fhc == NULL)
+ central_probe_failure(__LINE__);
+
+ /* Link it into the FHC chain. */
+ fhc->next = fhc_list;
+ fhc_list = fhc;
+
+ /* Toplevel FHCs have no parent. */
+ fhc->parent = NULL;
+
+ fhc->prom_node = node;
+ prom_getstring(node, "name", namebuf, sizeof(namebuf));
+ strcpy(fhc->prom_name, namebuf);
+ fhc_ranges_init(node, fhc);
+
+ /* Non-central FHC's have 64-bit OBP format registers. */
+ if (prom_getproperty(node, "reg",
+ (char *)&fpregs[0], sizeof(fpregs)) == -1)
+ central_probe_failure(__LINE__);
+
+ /* Only central FHC needs special ranges applied. */
+ fhc->fhc_regs.pregs = fpregs[0].phys_addr;
+ fhc->fhc_regs.ireg = fpregs[1].phys_addr;
+ fhc->fhc_regs.ffregs = fpregs[2].phys_addr;
+ fhc->fhc_regs.sregs = fpregs[3].phys_addr;
+ fhc->fhc_regs.uregs = fpregs[4].phys_addr;
+ fhc->fhc_regs.tregs = fpregs[5].phys_addr;
+
+ board = prom_getintdefault(node, "board#", -1);
+ fhc->board = board;
+
+ tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL);
+ if ((tmp & FHC_JTAG_CTRL_MENAB) != 0)
+ fhc->jtag_master = 1;
+ else
+ fhc->jtag_master = 0;
+
+ tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID);
+ printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n",
+ board,
+ (tmp & FHC_ID_VERS) >> 28,
+ (tmp & FHC_ID_PARTID) >> 12,
+ (tmp & FHC_ID_MANUF) >> 1,
+ (fhc->jtag_master ? "(JTAG Master)" : ""));
+
+ /* This bit must be set in all non-central FHC's in
+ * the system. When it is clear, this identifies
+ * the central board.
+ */
+ tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ tmp |= FHC_CONTROL_IXIST;
+ upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+
+ /* Look for the next FHC. */
+ node = prom_getsibling(node);
+ if (node == 0)
+ break;
+ node = prom_searchsiblings(node, "fhc");
+ if (node == 0)
+ break;
+ }
+}
+
+static void probe_clock_board(struct linux_central *central,
+ struct linux_fhc *fhc,
+ int cnode, int fnode)
+{
+ struct linux_prom_registers cregs[3];
+ int clknode, nslots, tmp, nregs;
+
+ clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board");
+ if (clknode == 0 || clknode == -1)
+ central_probe_failure(__LINE__);
+
+ nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs));
+ if (nregs == -1)
+ central_probe_failure(__LINE__);
+
+ nregs /= sizeof(struct linux_prom_registers);
+ apply_fhc_ranges(fhc, &cregs[0], nregs);
+ apply_central_ranges(central, &cregs[0], nregs);
+ central->cfreg = prom_reg_to_paddr(&cregs[0]);
+ central->clkregs = prom_reg_to_paddr(&cregs[1]);
+
+ if (nregs == 2)
+ central->clkver = 0UL;
+ else
+ central->clkver = prom_reg_to_paddr(&cregs[2]);
+
+ tmp = upa_readb(central->clkregs + CLOCK_STAT1);
+ tmp &= 0xc0;
+ switch(tmp) {
+ case 0x40:
+ nslots = 16;
+ break;
+ case 0xc0:
+ nslots = 8;
+ break;
+ case 0x80:
+ if (central->clkver != 0UL &&
+ upa_readb(central->clkver) != 0) {
+ if ((upa_readb(central->clkver) & 0x80) != 0)
+ nslots = 4;
+ else
+ nslots = 5;
+ break;
+ }
+ default:
+ nslots = 4;
+ break;
+ };
+ central->slots = nslots;
+ printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n",
+ central->slots, upa_readb(central->cfreg),
+ (central->clkver ? upa_readb(central->clkver) : 0x00));
+}
+
+static void ZAP(unsigned long iclr, unsigned long imap)
+{
+ u32 imap_tmp;
+
+ upa_writel(0, iclr);
+ upa_readl(iclr);
+ imap_tmp = upa_readl(imap);
+ imap_tmp &= ~(0x80000000);
+ upa_writel(imap_tmp, imap);
+ upa_readl(imap);
+}
+
+static void init_all_fhc_hw(void)
+{
+ struct linux_fhc *fhc;
+
+ for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) {
+ u32 tmp;
+
+ /* Clear all of the interrupt mapping registers
+ * just in case OBP left them in a foul state.
+ */
+ ZAP(fhc->fhc_regs.ffregs + FHC_FFREGS_ICLR,
+ fhc->fhc_regs.ffregs + FHC_FFREGS_IMAP);
+ ZAP(fhc->fhc_regs.sregs + FHC_SREGS_ICLR,
+ fhc->fhc_regs.sregs + FHC_SREGS_IMAP);
+ ZAP(fhc->fhc_regs.uregs + FHC_UREGS_ICLR,
+ fhc->fhc_regs.uregs + FHC_UREGS_IMAP);
+ ZAP(fhc->fhc_regs.tregs + FHC_TREGS_ICLR,
+ fhc->fhc_regs.tregs + FHC_TREGS_IMAP);
+
+ /* Setup FHC control register. */
+ tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+
+ /* All non-central boards have this bit set. */
+ if (! IS_CENTRAL_FHC(fhc))
+ tmp |= FHC_CONTROL_IXIST;
+
+ /* For all FHCs, clear the firmware synchronization
+ * line and both low power mode enables.
+ */
+ tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF |
+ FHC_CONTROL_SLINE);
+
+ upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ }
+
+}
+
+void central_probe(void)
+{
+ struct linux_prom_registers fpregs[6];
+ struct linux_fhc *fhc;
+ char namebuf[128];
+ int cnode, fnode, err;
+
+ cnode = prom_finddevice("/central");
+ if (cnode == 0 || cnode == -1) {
+ if (this_is_starfire)
+ starfire_cpu_setup();
+ return;
+ }
+
+ /* Ok we got one, grab some memory for software state. */
+ central_bus = (struct linux_central *)
+ central_alloc_bootmem(sizeof(struct linux_central));
+ if (central_bus == NULL)
+ central_probe_failure(__LINE__);
+
+ fhc = (struct linux_fhc *)
+ central_alloc_bootmem(sizeof(struct linux_fhc));
+ if (fhc == NULL)
+ central_probe_failure(__LINE__);
+
+ /* First init central. */
+ central_bus->child = fhc;
+ central_bus->prom_node = cnode;
+
+ prom_getstring(cnode, "name", namebuf, sizeof(namebuf));
+ strcpy(central_bus->prom_name, namebuf);
+
+ central_ranges_init(cnode, central_bus);
+
+ /* And then central's FHC. */
+ fhc->next = fhc_list;
+ fhc_list = fhc;
+
+ fhc->parent = central_bus;
+ fnode = prom_searchsiblings(prom_getchild(cnode), "fhc");
+ if (fnode == 0 || fnode == -1)
+ central_probe_failure(__LINE__);
+
+ fhc->prom_node = fnode;
+ prom_getstring(fnode, "name", namebuf, sizeof(namebuf));
+ strcpy(fhc->prom_name, namebuf);
+
+ fhc_ranges_init(fnode, fhc);
+
+ /* Now, map in FHC register set. */
+ if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1)
+ central_probe_failure(__LINE__);
+
+ apply_central_ranges(central_bus, &fpregs[0], 6);
+
+ fhc->fhc_regs.pregs = prom_reg_to_paddr(&fpregs[0]);
+ fhc->fhc_regs.ireg = prom_reg_to_paddr(&fpregs[1]);
+ fhc->fhc_regs.ffregs = prom_reg_to_paddr(&fpregs[2]);
+ fhc->fhc_regs.sregs = prom_reg_to_paddr(&fpregs[3]);
+ fhc->fhc_regs.uregs = prom_reg_to_paddr(&fpregs[4]);
+ fhc->fhc_regs.tregs = prom_reg_to_paddr(&fpregs[5]);
+
+ /* Obtain board number from board status register, Central's
+ * FHC lacks "board#" property.
+ */
+ err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_BSR);
+ fhc->board = (((err >> 16) & 0x01) |
+ ((err >> 12) & 0x0e));
+
+ fhc->jtag_master = 0;
+
+ /* Attach the clock board registers for CENTRAL. */
+ probe_clock_board(central_bus, fhc, cnode, fnode);
+
+ err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID);
+ printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n",
+ fhc->board,
+ ((err & FHC_ID_VERS) >> 28),
+ ((err & FHC_ID_PARTID) >> 12),
+ ((err & FHC_ID_MANUF) >> 1));
+
+ probe_other_fhcs();
+
+ init_all_fhc_hw();
+}
+
+static __inline__ void fhc_ledblink(struct linux_fhc *fhc, int on)
+{
+ u32 tmp;
+
+ tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+
+ /* NOTE: reverse logic on this bit */
+ if (on)
+ tmp &= ~(FHC_CONTROL_RLED);
+ else
+ tmp |= FHC_CONTROL_RLED;
+ tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE);
+
+ upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+}
+
+static __inline__ void central_ledblink(struct linux_central *central, int on)
+{
+ u8 tmp;
+
+ tmp = upa_readb(central->clkregs + CLOCK_CTRL);
+
+ /* NOTE: reverse logic on this bit */
+ if (on)
+ tmp &= ~(CLOCK_CTRL_RLED);
+ else
+ tmp |= CLOCK_CTRL_RLED;
+
+ upa_writeb(tmp, central->clkregs + CLOCK_CTRL);
+ upa_readb(central->clkregs + CLOCK_CTRL);
+}
+
+static struct timer_list sftimer;
+static int led_state;
+
+static void sunfire_timer(unsigned long __ignored)
+{
+ struct linux_fhc *fhc;
+
+ central_ledblink(central_bus, led_state);
+ for (fhc = fhc_list; fhc != NULL; fhc = fhc->next)
+ if (! IS_CENTRAL_FHC(fhc))
+ fhc_ledblink(fhc, led_state);
+ led_state = ! led_state;
+ sftimer.expires = jiffies + (HZ >> 1);
+ add_timer(&sftimer);
+}
+
+/* After PCI/SBUS busses have been probed, this is called to perform
+ * final initialization of all FireHose Controllers in the system.
+ */
+void firetruck_init(void)
+{
+ struct linux_central *central = central_bus;
+ u8 ctrl;
+
+ /* No central bus, nothing to do. */
+ if (central == NULL)
+ return;
+
+ /* OBP leaves it on, turn it off so clock board timer LED
+ * is in sync with FHC ones.
+ */
+ ctrl = upa_readb(central->clkregs + CLOCK_CTRL);
+ ctrl &= ~(CLOCK_CTRL_RLED);
+ upa_writeb(ctrl, central->clkregs + CLOCK_CTRL);
+
+ led_state = 0;
+ init_timer(&sftimer);
+ sftimer.data = 0;
+ sftimer.function = &sunfire_timer;
+ sftimer.expires = jiffies + (HZ >> 1);
+ add_timer(&sftimer);
+}
diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c
new file mode 100644
index 000000000000..97cf912f0853
--- /dev/null
+++ b/arch/sparc64/kernel/chmc.c
@@ -0,0 +1,458 @@
+/* $Id: chmc.c,v 1.4 2002/01/08 16:00:14 davem Exp $
+ * memctrlr.c: Driver for UltraSPARC-III memory controller.
+ *
+ * Copyright (C) 2001 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/spitfire.h>
+#include <asm/chmctrl.h>
+#include <asm/oplib.h>
+#include <asm/io.h>
+
+#define CHMCTRL_NDGRPS 2
+#define CHMCTRL_NDIMMS 4
+
+#define DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
+
+/* OBP memory-layout property format. */
+struct obp_map {
+ unsigned char dimm_map[144];
+ unsigned char pin_map[576];
+};
+
+#define DIMM_LABEL_SZ 8
+
+struct obp_mem_layout {
+ /* One max 8-byte string label per DIMM. Usually
+ * this matches the label on the motherboard where
+ * that DIMM resides.
+ */
+ char dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ];
+
+ /* If symmetric use map[0], else it is
+ * asymmetric and map[1] should be used.
+ */
+ char symmetric;
+
+ struct obp_map map[2];
+};
+
+#define CHMCTRL_NBANKS 4
+
+struct bank_info {
+ struct mctrl_info *mp;
+ int bank_id;
+
+ u64 raw_reg;
+ int valid;
+ int uk;
+ int um;
+ int lk;
+ int lm;
+ int interleave;
+ unsigned long base;
+ unsigned long size;
+};
+
+struct mctrl_info {
+ struct list_head list;
+ int portid;
+ int index;
+
+ struct obp_mem_layout layout_prop;
+ int layout_size;
+
+ void __iomem *regs;
+
+ u64 timing_control1;
+ u64 timing_control2;
+ u64 timing_control3;
+ u64 timing_control4;
+ u64 memaddr_control;
+
+ struct bank_info logical_banks[CHMCTRL_NBANKS];
+};
+
+static LIST_HEAD(mctrl_list);
+
+/* Does BANK decode PHYS_ADDR? */
+static int bank_match(struct bank_info *bp, unsigned long phys_addr)
+{
+ unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT;
+ unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT;
+
+ /* Bank must be enabled to match. */
+ if (bp->valid == 0)
+ return 0;
+
+ /* Would BANK match upper bits? */
+ upper_bits ^= bp->um; /* What bits are different? */
+ upper_bits = ~upper_bits; /* Invert. */
+ upper_bits |= bp->uk; /* What bits don't matter for matching? */
+ upper_bits = ~upper_bits; /* Invert. */
+
+ if (upper_bits)
+ return 0;
+
+ /* Would BANK match lower bits? */
+ lower_bits ^= bp->lm; /* What bits are different? */
+ lower_bits = ~lower_bits; /* Invert. */
+ lower_bits |= bp->lk; /* What bits don't matter for matching? */
+ lower_bits = ~lower_bits; /* Invert. */
+
+ if (lower_bits)
+ return 0;
+
+ /* I always knew you'd be the one. */
+ return 1;
+}
+
+/* Given PHYS_ADDR, search memory controller banks for a match. */
+static struct bank_info *find_bank(unsigned long phys_addr)
+{
+ struct list_head *mctrl_head = &mctrl_list;
+ struct list_head *mctrl_entry = mctrl_head->next;
+
+ for (;;) {
+ struct mctrl_info *mp =
+ list_entry(mctrl_entry, struct mctrl_info, list);
+ int bank_no;
+
+ if (mctrl_entry == mctrl_head)
+ break;
+ mctrl_entry = mctrl_entry->next;
+
+ for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) {
+ struct bank_info *bp;
+
+ bp = &mp->logical_banks[bank_no];
+ if (bank_match(bp, phys_addr))
+ return bp;
+ }
+ }
+
+ return NULL;
+}
+
+/* This is the main purpose of this driver. */
+#define SYNDROME_MIN -1
+#define SYNDROME_MAX 144
+int chmc_getunumber(int syndrome_code,
+ unsigned long phys_addr,
+ char *buf, int buflen)
+{
+ struct bank_info *bp;
+ struct obp_mem_layout *prop;
+ int bank_in_controller, first_dimm;
+
+ bp = find_bank(phys_addr);
+ if (bp == NULL ||
+ syndrome_code < SYNDROME_MIN ||
+ syndrome_code > SYNDROME_MAX) {
+ buf[0] = '?';
+ buf[1] = '?';
+ buf[2] = '?';
+ buf[3] = '\0';
+ return 0;
+ }
+
+ prop = &bp->mp->layout_prop;
+ bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1);
+ first_dimm = (bank_in_controller & (CHMCTRL_NDGRPS - 1));
+ first_dimm *= CHMCTRL_NDIMMS;
+
+ if (syndrome_code != SYNDROME_MIN) {
+ struct obp_map *map;
+ int qword, where_in_line, where, map_index, map_offset;
+ unsigned int map_val;
+
+ /* Yaay, single bit error so we can figure out
+ * the exact dimm.
+ */
+ if (prop->symmetric)
+ map = &prop->map[0];
+ else
+ map = &prop->map[1];
+
+ /* Covert syndrome code into the way the bits are
+ * positioned on the bus.
+ */
+ if (syndrome_code < 144 - 16)
+ syndrome_code += 16;
+ else if (syndrome_code < 144)
+ syndrome_code -= (144 - 7);
+ else if (syndrome_code < (144 + 3))
+ syndrome_code -= (144 + 3 - 4);
+ else
+ syndrome_code -= 144 + 3;
+
+ /* All this magic has to do with how a cache line
+ * comes over the wire on Safari. A 64-bit line
+ * comes over in 4 quadword cycles, each of which
+ * transmit ECC/MTAG info as well as the actual
+ * data. 144 bits per quadword, 576 total.
+ */
+#define LINE_SIZE 64
+#define LINE_ADDR_MSK (LINE_SIZE - 1)
+#define QW_PER_LINE 4
+#define QW_BYTES (LINE_SIZE / QW_PER_LINE)
+#define QW_BITS 144
+#define LAST_BIT (576 - 1)
+
+ qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES;
+ where_in_line = ((3 - qword) * QW_BITS) + syndrome_code;
+ where = (LAST_BIT - where_in_line);
+ map_index = where >> 2;
+ map_offset = where & 0x3;
+ map_val = map->dimm_map[map_index];
+ map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1));
+
+ sprintf(buf, "%s, pin %3d",
+ prop->dimm_labels[first_dimm + map_val],
+ map->pin_map[where_in_line]);
+ } else {
+ int dimm;
+
+ /* Multi-bit error, we just dump out all the
+ * dimm labels associated with this bank.
+ */
+ for (dimm = 0; dimm < CHMCTRL_NDIMMS; dimm++) {
+ sprintf(buf, "%s ",
+ prop->dimm_labels[first_dimm + dimm]);
+ buf += strlen(buf);
+ }
+ }
+ return 0;
+}
+
+/* Accessing the registers is slightly complicated. If you want
+ * to get at the memory controller which is on the same processor
+ * the code is executing, you must use special ASI load/store else
+ * you go through the global mapping.
+ */
+static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset)
+{
+ unsigned long ret;
+ int this_cpu = get_cpu();
+
+ if (mp->portid == this_cpu) {
+ __asm__ __volatile__("ldxa [%1] %2, %0"
+ : "=r" (ret)
+ : "r" (offset), "i" (ASI_MCU_CTRL_REG));
+ } else {
+ __asm__ __volatile__("ldxa [%1] %2, %0"
+ : "=r" (ret)
+ : "r" (mp->regs + offset),
+ "i" (ASI_PHYS_BYPASS_EC_E));
+ }
+ put_cpu();
+
+ return ret;
+}
+
+#if 0 /* currently unused */
+static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val)
+{
+ if (mp->portid == smp_processor_id()) {
+ __asm__ __volatile__("stxa %0, [%1] %2"
+ : : "r" (val),
+ "r" (offset), "i" (ASI_MCU_CTRL_REG));
+ } else {
+ __asm__ __volatile__("ldxa %0, [%1] %2"
+ : : "r" (val),
+ "r" (mp->regs + offset),
+ "i" (ASI_PHYS_BYPASS_EC_E));
+ }
+}
+#endif
+
+static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val)
+{
+ struct bank_info *p = &mp->logical_banks[which_bank];
+
+ p->mp = mp;
+ p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank;
+ p->raw_reg = val;
+ p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT;
+ p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT;
+ p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT;
+ p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT;
+ p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT;
+
+ p->base = (p->um);
+ p->base &= ~(p->uk);
+ p->base <<= PA_UPPER_BITS_SHIFT;
+
+ switch(p->lk) {
+ case 0xf:
+ default:
+ p->interleave = 1;
+ break;
+
+ case 0xe:
+ p->interleave = 2;
+ break;
+
+ case 0xc:
+ p->interleave = 4;
+ break;
+
+ case 0x8:
+ p->interleave = 8;
+ break;
+
+ case 0x0:
+ p->interleave = 16;
+ break;
+ };
+
+ /* UK[10] is reserved, and UK[11] is not set for the SDRAM
+ * bank size definition.
+ */
+ p->size = (((unsigned long)p->uk &
+ ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT;
+ p->size /= p->interleave;
+}
+
+static void fetch_decode_regs(struct mctrl_info *mp)
+{
+ if (mp->layout_size == 0)
+ return;
+
+ interpret_one_decode_reg(mp, 0,
+ read_mcreg(mp, CHMCTRL_DECODE1));
+ interpret_one_decode_reg(mp, 1,
+ read_mcreg(mp, CHMCTRL_DECODE2));
+ interpret_one_decode_reg(mp, 2,
+ read_mcreg(mp, CHMCTRL_DECODE3));
+ interpret_one_decode_reg(mp, 3,
+ read_mcreg(mp, CHMCTRL_DECODE4));
+}
+
+static int init_one_mctrl(int node, int index)
+{
+ struct mctrl_info *mp = kmalloc(sizeof(*mp), GFP_KERNEL);
+ int portid = prom_getintdefault(node, "portid", -1);
+ struct linux_prom64_registers p_reg_prop;
+ int t;
+
+ if (!mp)
+ return -1;
+ memset(mp, 0, sizeof(*mp));
+ if (portid == -1)
+ goto fail;
+
+ mp->portid = portid;
+ mp->layout_size = prom_getproplen(node, "memory-layout");
+ if (mp->layout_size < 0)
+ mp->layout_size = 0;
+ if (mp->layout_size > sizeof(mp->layout_prop))
+ goto fail;
+
+ if (mp->layout_size > 0)
+ prom_getproperty(node, "memory-layout",
+ (char *) &mp->layout_prop,
+ mp->layout_size);
+
+ t = prom_getproperty(node, "reg",
+ (char *) &p_reg_prop,
+ sizeof(p_reg_prop));
+ if (t < 0 || p_reg_prop.reg_size != 0x48)
+ goto fail;
+
+ mp->regs = ioremap(p_reg_prop.phys_addr, p_reg_prop.reg_size);
+ if (mp->regs == NULL)
+ goto fail;
+
+ if (mp->layout_size != 0UL) {
+ mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1);
+ mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2);
+ mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3);
+ mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4);
+ mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL);
+ }
+
+ fetch_decode_regs(mp);
+
+ mp->index = index;
+
+ list_add(&mp->list, &mctrl_list);
+
+ /* Report the device. */
+ printk(KERN_INFO "chmc%d: US3 memory controller at %p [%s]\n",
+ mp->index,
+ mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE"));
+
+ return 0;
+
+fail:
+ if (mp) {
+ if (mp->regs != NULL)
+ iounmap(mp->regs);
+ kfree(mp);
+ }
+ return -1;
+}
+
+static int __init probe_for_string(char *name, int index)
+{
+ int node = prom_getchild(prom_root_node);
+
+ while ((node = prom_searchsiblings(node, name)) != 0) {
+ int ret = init_one_mctrl(node, index);
+
+ if (!ret)
+ index++;
+
+ node = prom_getsibling(node);
+ if (!node)
+ break;
+ }
+
+ return index;
+}
+
+static int __init chmc_init(void)
+{
+ int index;
+
+ /* This driver is only for cheetah platforms. */
+ if (tlb_type != cheetah && tlb_type != cheetah_plus)
+ return -ENODEV;
+
+ index = probe_for_string("memory-controller", 0);
+ index = probe_for_string("mc-us3", index);
+
+ return 0;
+}
+
+static void __exit chmc_cleanup(void)
+{
+ struct list_head *head = &mctrl_list;
+ struct list_head *tmp = head->next;
+
+ for (;;) {
+ struct mctrl_info *p =
+ list_entry(tmp, struct mctrl_info, list);
+ if (tmp == head)
+ break;
+ tmp = tmp->next;
+
+ list_del(&p->list);
+ iounmap(p->regs);
+ kfree(p);
+ }
+}
+
+module_init(chmc_init);
+module_exit(chmc_cleanup);
diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c
new file mode 100644
index 000000000000..48756958116b
--- /dev/null
+++ b/arch/sparc64/kernel/cpu.c
@@ -0,0 +1,124 @@
+/* cpu.c: Dinky routines to look for the kind of Sparc cpu
+ * we are on.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <asm/asi.h>
+#include <asm/system.h>
+#include <asm/fpumacro.h>
+#include <asm/cpudata.h>
+
+DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 };
+
+struct cpu_iu_info {
+ short manuf;
+ short impl;
+ char* cpu_name; /* should be enough I hope... */
+};
+
+struct cpu_fp_info {
+ short manuf;
+ short impl;
+ char fpu_vers;
+ char* fp_name;
+};
+
+struct cpu_fp_info linux_sparc_fpu[] = {
+ { 0x17, 0x10, 0, "UltraSparc I integrated FPU"},
+ { 0x22, 0x10, 0, "UltraSparc I integrated FPU"},
+ { 0x17, 0x11, 0, "UltraSparc II integrated FPU"},
+ { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"},
+ { 0x17, 0x13, 0, "UltraSparc IIe integrated FPU"},
+ { 0x3e, 0x14, 0, "UltraSparc III integrated FPU"},
+ { 0x3e, 0x15, 0, "UltraSparc III+ integrated FPU"},
+ { 0x3e, 0x16, 0, "UltraSparc IIIi integrated FPU"},
+ { 0x3e, 0x18, 0, "UltraSparc IV integrated FPU"},
+};
+
+#define NSPARCFPU (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
+
+struct cpu_iu_info linux_sparc_chips[] = {
+ { 0x17, 0x10, "TI UltraSparc I (SpitFire)"},
+ { 0x22, 0x10, "TI UltraSparc I (SpitFire)"},
+ { 0x17, 0x11, "TI UltraSparc II (BlackBird)"},
+ { 0x17, 0x12, "TI UltraSparc IIi (Sabre)"},
+ { 0x17, 0x13, "TI UltraSparc IIe (Hummingbird)"},
+ { 0x3e, 0x14, "TI UltraSparc III (Cheetah)"},
+ { 0x3e, 0x15, "TI UltraSparc III+ (Cheetah+)"},
+ { 0x3e, 0x16, "TI UltraSparc IIIi (Jalapeno)"},
+ { 0x3e, 0x18, "TI UltraSparc IV (Jaguar)"},
+};
+
+#define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
+
+char *sparc_cpu_type = "cpu-oops";
+char *sparc_fpu_type = "fpu-oops";
+
+unsigned int fsr_storage;
+
+void __init cpu_probe(void)
+{
+ unsigned long ver, fpu_vers, manuf, impl, fprs;
+ int i;
+
+ fprs = fprs_read();
+ fprs_write(FPRS_FEF);
+ __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]"
+ : "=&r" (ver)
+ : "r" (&fpu_vers));
+ fprs_write(fprs);
+
+ manuf = ((ver >> 48) & 0xffff);
+ impl = ((ver >> 32) & 0xffff);
+
+ fpu_vers = ((fpu_vers >> 17) & 0x7);
+
+retry:
+ for (i = 0; i < NSPARCCHIPS; i++) {
+ if (linux_sparc_chips[i].manuf == manuf) {
+ if (linux_sparc_chips[i].impl == impl) {
+ sparc_cpu_type =
+ linux_sparc_chips[i].cpu_name;
+ break;
+ }
+ }
+ }
+
+ if (i == NSPARCCHIPS) {
+ /* Maybe it is a cheetah+ derivative, report it as cheetah+
+ * in that case until we learn the real names.
+ */
+ if (manuf == 0x3e &&
+ impl > 0x15) {
+ impl = 0x15;
+ goto retry;
+ } else {
+ printk("DEBUG: manuf[%lx] impl[%lx]\n",
+ manuf, impl);
+ }
+ sparc_cpu_type = "Unknown CPU";
+ }
+
+ for (i = 0; i < NSPARCFPU; i++) {
+ if (linux_sparc_fpu[i].manuf == manuf &&
+ linux_sparc_fpu[i].impl == impl) {
+ if (linux_sparc_fpu[i].fpu_vers == fpu_vers) {
+ sparc_fpu_type =
+ linux_sparc_fpu[i].fp_name;
+ break;
+ }
+ }
+ }
+
+ if (i == NSPARCFPU) {
+ printk("DEBUG: manuf[%lx] impl[%lx] fsr.vers[%lx]\n",
+ manuf, impl, fpu_vers);
+ sparc_fpu_type = "Unknown FPU";
+ }
+}
diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c
new file mode 100644
index 000000000000..d710274e516b
--- /dev/null
+++ b/arch/sparc64/kernel/devices.c
@@ -0,0 +1,144 @@
+/* devices.c: Initial scan of the prom device tree for important
+ * Sparc device nodes which we need to find.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/threads.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+
+#include <asm/page.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/smp.h>
+#include <asm/spitfire.h>
+#include <asm/timer.h>
+#include <asm/cpudata.h>
+
+/* Used to synchronize acceses to NatSemi SUPER I/O chip configure
+ * operations in asm/ns87303.h
+ */
+DEFINE_SPINLOCK(ns87303_lock);
+
+extern void cpu_probe(void);
+extern void central_probe(void);
+
+static char *cpu_mid_prop(void)
+{
+ if (tlb_type == spitfire)
+ return "upa-portid";
+ return "portid";
+}
+
+static int check_cpu_node(int nd, int *cur_inst,
+ int (*compare)(int, int, void *), void *compare_arg,
+ int *prom_node, int *mid)
+{
+ char node_str[128];
+
+ prom_getstring(nd, "device_type", node_str, sizeof(node_str));
+ if (strcmp(node_str, "cpu"))
+ return -ENODEV;
+
+ if (!compare(nd, *cur_inst, compare_arg)) {
+ if (prom_node)
+ *prom_node = nd;
+ if (mid)
+ *mid = prom_getintdefault(nd, cpu_mid_prop(), 0);
+ return 0;
+ }
+
+ (*cur_inst)++;
+
+ return -ENODEV;
+}
+
+static int __cpu_find_by(int (*compare)(int, int, void *), void *compare_arg,
+ int *prom_node, int *mid)
+{
+ int nd, cur_inst, err;
+
+ nd = prom_root_node;
+ cur_inst = 0;
+
+ err = check_cpu_node(nd, &cur_inst,
+ compare, compare_arg,
+ prom_node, mid);
+ if (err == 0)
+ return 0;
+
+ nd = prom_getchild(nd);
+ while ((nd = prom_getsibling(nd)) != 0) {
+ err = check_cpu_node(nd, &cur_inst,
+ compare, compare_arg,
+ prom_node, mid);
+ if (err == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int cpu_instance_compare(int nd, int instance, void *_arg)
+{
+ int desired_instance = (int) (long) _arg;
+
+ if (instance == desired_instance)
+ return 0;
+ return -ENODEV;
+}
+
+int cpu_find_by_instance(int instance, int *prom_node, int *mid)
+{
+ return __cpu_find_by(cpu_instance_compare, (void *)(long)instance,
+ prom_node, mid);
+}
+
+static int cpu_mid_compare(int nd, int instance, void *_arg)
+{
+ int desired_mid = (int) (long) _arg;
+ int this_mid;
+
+ this_mid = prom_getintdefault(nd, cpu_mid_prop(), 0);
+ if (this_mid == desired_mid)
+ return 0;
+ return -ENODEV;
+}
+
+int cpu_find_by_mid(int mid, int *prom_node)
+{
+ return __cpu_find_by(cpu_mid_compare, (void *)(long)mid,
+ prom_node, NULL);
+}
+
+void __init device_scan(void)
+{
+ /* FIX ME FAST... -DaveM */
+ ioport_resource.end = 0xffffffffffffffffUL;
+
+ prom_printf("Booting Linux...\n");
+
+#ifndef CONFIG_SMP
+ {
+ int err, cpu_node;
+ err = cpu_find_by_instance(0, &cpu_node, NULL);
+ if (err) {
+ prom_printf("No cpu nodes, cannot continue\n");
+ prom_halt();
+ }
+ cpu_data(0).clock_tick = prom_getintdefault(cpu_node,
+ "clock-frequency",
+ 0);
+ }
+#endif
+
+ central_probe();
+
+ cpu_probe();
+}
diff --git a/arch/sparc64/kernel/dtlb_backend.S b/arch/sparc64/kernel/dtlb_backend.S
new file mode 100644
index 000000000000..b73a3c858770
--- /dev/null
+++ b/arch/sparc64/kernel/dtlb_backend.S
@@ -0,0 +1,181 @@
+/* $Id: dtlb_backend.S,v 1.16 2001/10/09 04:02:11 davem Exp $
+ * dtlb_backend.S: Back end to DTLB miss replacement strategy.
+ * This is included directly into the trap table.
+ *
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+
+#if PAGE_SHIFT == 13
+#define SZ_BITS _PAGE_SZ8K
+#elif PAGE_SHIFT == 16
+#define SZ_BITS _PAGE_SZ64K
+#elif PAGE_SHIFT == 19
+#define SZ_BITS _PAGE_SZ512K
+#elif PAGE_SHIFT == 22
+#define SZ_BITS _PAGE_SZ4M
+#endif
+
+#define VALID_SZ_BITS (_PAGE_VALID | SZ_BITS)
+
+#define VPTE_BITS (_PAGE_CP | _PAGE_CV | _PAGE_P )
+#define VPTE_SHIFT (PAGE_SHIFT - 3)
+
+/* Ways we can get here:
+ *
+ * 1) Nucleus loads and stores to/from PA-->VA direct mappings at tl>1.
+ * 2) Nucleus loads and stores to/from user/kernel window save areas.
+ * 3) VPTE misses from dtlb_base and itlb_base.
+ *
+ * We need to extract out the PMD and PGDIR indexes from the
+ * linear virtual page table access address. The PTE index
+ * is at the bottom, but we are not concerned with it. Bits
+ * 0 to 2 are clear since each PTE is 8 bytes in size. Each
+ * PMD and PGDIR entry are 4 bytes in size. Thus, this
+ * address looks something like:
+ *
+ * |---------------------------------------------------------------|
+ * | ... | PGDIR index | PMD index | PTE index | |
+ * |---------------------------------------------------------------|
+ * 63 F E D C B A 3 2 0 <- bit nr
+ *
+ * The variable bits above are defined as:
+ * A --> 3 + (PAGE_SHIFT - log2(8))
+ * --> 3 + (PAGE_SHIFT - 3) - 1
+ * (ie. this is "bit 3" + PAGE_SIZE - size of PTE entry in bits - 1)
+ * B --> A + 1
+ * C --> B + (PAGE_SHIFT - log2(4))
+ * --> B + (PAGE_SHIFT - 2) - 1
+ * (ie. this is "bit B" + PAGE_SIZE - size of PMD entry in bits - 1)
+ * D --> C + 1
+ * E --> D + (PAGE_SHIFT - log2(4))
+ * --> D + (PAGE_SHIFT - 2) - 1
+ * (ie. this is "bit D" + PAGE_SIZE - size of PGDIR entry in bits - 1)
+ * F --> E + 1
+ *
+ * (Note how "B" always evalutes to PAGE_SHIFT, all the other constants
+ * cancel out.)
+ *
+ * For 8K PAGE_SIZE (thus, PAGE_SHIFT of 13) the bit numbers are:
+ * A --> 12
+ * B --> 13
+ * C --> 23
+ * D --> 24
+ * E --> 34
+ * F --> 35
+ *
+ * For 64K PAGE_SIZE (thus, PAGE_SHIFT of 16) the bit numbers are:
+ * A --> 15
+ * B --> 16
+ * C --> 29
+ * D --> 30
+ * E --> 43
+ * F --> 44
+ *
+ * Because bits both above and below each PGDIR and PMD index need to
+ * be masked out, and the index can be as long as 14 bits (when using a
+ * 64K PAGE_SIZE, and thus a PAGE_SHIFT of 16), we need 3 instructions
+ * to extract each index out.
+ *
+ * Shifts do not pair very well on UltraSPARC-I, II, IIi, and IIe, so
+ * we try to avoid using them for the entire operation. We could setup
+ * a mask anywhere from bit 31 down to bit 10 using the sethi instruction.
+ *
+ * We need a mask covering bits B --> C and one covering D --> E.
+ * For 8K PAGE_SIZE these masks are 0x00ffe000 and 0x7ff000000.
+ * For 64K PAGE_SIZE these masks are 0x3fff0000 and 0xfffc0000000.
+ * The second in each set cannot be loaded with a single sethi
+ * instruction, because the upper bits are past bit 32. We would
+ * need to use a sethi + a shift.
+ *
+ * For the time being, we use 2 shifts and a simple "and" mask.
+ * We shift left to clear the bits above the index, we shift down
+ * to clear the bits below the index (sans the log2(4 or 8) bits)
+ * and a mask to clear the log2(4 or 8) bits. We need therefore
+ * define 4 shift counts, all of which are relative to PAGE_SHIFT.
+ *
+ * Although unsupportable for other reasons, this does mean that
+ * 512K and 4MB page sizes would be generaally supported by the
+ * kernel. (ELF binaries would break with > 64K PAGE_SIZE since
+ * the sections are only aligned that strongly).
+ *
+ * The operations performed for extraction are thus:
+ *
+ * ((X << FOO_SHIFT_LEFT) >> FOO_SHIFT_RIGHT) & ~0x3
+ *
+ */
+
+#define A (3 + (PAGE_SHIFT - 3) - 1)
+#define B (A + 1)
+#define C (B + (PAGE_SHIFT - 2) - 1)
+#define D (C + 1)
+#define E (D + (PAGE_SHIFT - 2) - 1)
+#define F (E + 1)
+
+#define PMD_SHIFT_LEFT (64 - D)
+#define PMD_SHIFT_RIGHT (64 - (D - B) - 2)
+#define PGDIR_SHIFT_LEFT (64 - F)
+#define PGDIR_SHIFT_RIGHT (64 - (F - D) - 2)
+#define LOW_MASK_BITS 0x3
+
+/* TLB1 ** ICACHE line 1: tl1 DTLB and quick VPTE miss */
+ ldxa [%g1 + %g1] ASI_DMMU, %g4 ! Get TAG_ACCESS
+ add %g3, %g3, %g5 ! Compute VPTE base
+ cmp %g4, %g5 ! VPTE miss?
+ bgeu,pt %xcc, 1f ! Continue here
+ andcc %g4, TAG_CONTEXT_BITS, %g5 ! tl0 miss Nucleus test
+ ba,a,pt %xcc, from_tl1_trap ! Fall to tl0 miss
+1: sllx %g6, VPTE_SHIFT, %g4 ! Position TAG_ACCESS
+ or %g4, %g5, %g4 ! Prepare TAG_ACCESS
+
+/* TLB1 ** ICACHE line 2: Quick VPTE miss */
+ mov TSB_REG, %g1 ! Grab TSB reg
+ ldxa [%g1] ASI_DMMU, %g5 ! Doing PGD caching?
+ sllx %g6, PMD_SHIFT_LEFT, %g1 ! Position PMD offset
+ be,pn %xcc, sparc64_vpte_nucleus ! Is it from Nucleus?
+ srlx %g1, PMD_SHIFT_RIGHT, %g1 ! Mask PMD offset bits
+ brnz,pt %g5, sparc64_vpte_continue ! Yep, go like smoke
+ andn %g1, LOW_MASK_BITS, %g1 ! Final PMD mask
+ sllx %g6, PGDIR_SHIFT_LEFT, %g5 ! Position PGD offset
+
+/* TLB1 ** ICACHE line 3: Quick VPTE miss */
+ srlx %g5, PGDIR_SHIFT_RIGHT, %g5 ! Mask PGD offset bits
+ andn %g5, LOW_MASK_BITS, %g5 ! Final PGD mask
+ lduwa [%g7 + %g5] ASI_PHYS_USE_EC, %g5! Load PGD
+ brz,pn %g5, vpte_noent ! Valid?
+sparc64_kpte_continue:
+ sllx %g5, 11, %g5 ! Shift into place
+sparc64_vpte_continue:
+ lduwa [%g5 + %g1] ASI_PHYS_USE_EC, %g5! Load PMD
+ sllx %g5, 11, %g5 ! Shift into place
+ brz,pn %g5, vpte_noent ! Valid?
+
+/* TLB1 ** ICACHE line 4: Quick VPTE miss */
+ mov (VALID_SZ_BITS >> 61), %g1 ! upper vpte into %g1
+ sllx %g1, 61, %g1 ! finish calc
+ or %g5, VPTE_BITS, %g5 ! Prepare VPTE data
+ or %g5, %g1, %g5 ! ...
+ mov TLB_SFSR, %g1 ! Restore %g1 value
+ stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Load VPTE into TLB
+ stxa %g4, [%g1 + %g1] ASI_DMMU ! Restore previous TAG_ACCESS
+ retry ! Load PTE once again
+
+#undef SZ_BITS
+#undef VALID_SZ_BITS
+#undef VPTE_SHIFT
+#undef VPTE_BITS
+#undef A
+#undef B
+#undef C
+#undef D
+#undef E
+#undef F
+#undef PMD_SHIFT_LEFT
+#undef PMD_SHIFT_RIGHT
+#undef PGDIR_SHIFT_LEFT
+#undef PGDIR_SHIFT_RIGHT
+#undef LOW_MASK_BITS
+
diff --git a/arch/sparc64/kernel/dtlb_base.S b/arch/sparc64/kernel/dtlb_base.S
new file mode 100644
index 000000000000..ded2fed23fcc
--- /dev/null
+++ b/arch/sparc64/kernel/dtlb_base.S
@@ -0,0 +1,113 @@
+/* $Id: dtlb_base.S,v 1.17 2001/10/11 22:33:52 davem Exp $
+ * dtlb_base.S: Front end to DTLB miss replacement strategy.
+ * This is included directly into the trap table.
+ *
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+
+/* %g1 TLB_SFSR (%g1 + %g1 == TLB_TAG_ACCESS)
+ * %g2 (KERN_HIGHBITS | KERN_LOWBITS)
+ * %g3 VPTE base (0xfffffffe00000000) Spitfire/Blackbird (44-bit VA space)
+ * (0xffe0000000000000) Cheetah (64-bit VA space)
+ * %g7 __pa(current->mm->pgd)
+ *
+ * The VPTE base value is completely magic, but note that
+ * few places in the kernel other than these TLB miss
+ * handlers know anything about the VPTE mechanism or
+ * how it works (see VPTE_SIZE, TASK_SIZE and PTRS_PER_PGD).
+ * Consider the 44-bit VADDR Ultra-I/II case as an example:
+ *
+ * VA[0 : (1<<43)] produce VPTE index [%g3 : 0]
+ * VA[0 : -(1<<43)] produce VPTE index [%g3-(1<<(43-PAGE_SHIFT+3)) : %g3]
+ *
+ * For Cheetah's 64-bit VADDR space this is:
+ *
+ * VA[0 : (1<<63)] produce VPTE index [%g3 : 0]
+ * VA[0 : -(1<<63)] produce VPTE index [%g3-(1<<(63-PAGE_SHIFT+3)) : %g3]
+ *
+ * If you're paying attention you'll notice that this means half of
+ * the VPTE table is above %g3 and half is below, low VA addresses
+ * map progressively upwards from %g3, and high VA addresses map
+ * progressively upwards towards %g3. This trick was needed to make
+ * the same 8 instruction handler work both for Spitfire/Blackbird's
+ * peculiar VA space hole configuration and the full 64-bit VA space
+ * one of Cheetah at the same time.
+ */
+
+/* Ways we can get here:
+ *
+ * 1) Nucleus loads and stores to/from PA-->VA direct mappings.
+ * 2) Nucleus loads and stores to/from vmalloc() areas.
+ * 3) User loads and stores.
+ * 4) User space accesses by nucleus at tl0
+ */
+
+#if PAGE_SHIFT == 13
+/*
+ * To compute vpte offset, we need to do ((addr >> 13) << 3),
+ * which can be optimized to (addr >> 10) if bits 10/11/12 can
+ * be guaranteed to be 0 ... mmu_context.h does guarantee this
+ * by only using 10 bits in the hwcontext value.
+ */
+#define CREATE_VPTE_OFFSET1(r1, r2)
+#define CREATE_VPTE_OFFSET2(r1, r2) \
+ srax r1, 10, r2
+#define CREATE_VPTE_NOP nop
+#else
+#define CREATE_VPTE_OFFSET1(r1, r2) \
+ srax r1, PAGE_SHIFT, r2
+#define CREATE_VPTE_OFFSET2(r1, r2) \
+ sllx r2, 3, r2
+#define CREATE_VPTE_NOP
+#endif
+
+/* DTLB ** ICACHE line 1: Quick user TLB misses */
+ ldxa [%g1 + %g1] ASI_DMMU, %g4 ! Get TAG_ACCESS
+ andcc %g4, TAG_CONTEXT_BITS, %g0 ! From Nucleus?
+from_tl1_trap:
+ rdpr %tl, %g5 ! For TL==3 test
+ CREATE_VPTE_OFFSET1(%g4, %g6) ! Create VPTE offset
+ be,pn %xcc, 3f ! Yep, special processing
+ CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset
+ cmp %g5, 4 ! Last trap level?
+ be,pn %xcc, longpath ! Yep, cannot risk VPTE miss
+ nop ! delay slot
+
+/* DTLB ** ICACHE line 2: User finish + quick kernel TLB misses */
+ ldxa [%g3 + %g6] ASI_S, %g5 ! Load VPTE
+1: brgez,pn %g5, longpath ! Invalid, branch out
+ nop ! Delay-slot
+9: stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
+ retry ! Trap return
+3: brlz,pt %g4, 9b ! Kernel virtual map?
+ xor %g2, %g4, %g5 ! Finish bit twiddles
+ ba,a,pt %xcc, kvmap ! Yep, go check for obp/vmalloc
+
+/* DTLB ** ICACHE line 3: winfixups+real_faults */
+longpath:
+ rdpr %pstate, %g5 ! Move into alternate globals
+ wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
+ rdpr %tl, %g4 ! See where we came from.
+ cmp %g4, 1 ! Is etrap/rtrap window fault?
+ mov TLB_TAG_ACCESS, %g4 ! Prepare for fault processing
+ ldxa [%g4] ASI_DMMU, %g5 ! Load faulting VA page
+ be,pt %xcc, sparc64_realfault_common ! Jump to normal fault handling
+ mov FAULT_CODE_DTLB, %g4 ! It was read from DTLB
+
+/* DTLB ** ICACHE line 4: Unused... */
+ ba,a,pt %xcc, winfix_trampoline ! Call window fixup code
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ CREATE_VPTE_NOP
+
+#undef CREATE_VPTE_OFFSET1
+#undef CREATE_VPTE_OFFSET2
+#undef CREATE_VPTE_NOP
diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S
new file mode 100644
index 000000000000..d848bb7374bb
--- /dev/null
+++ b/arch/sparc64/kernel/dtlb_prot.S
@@ -0,0 +1,54 @@
+/* $Id: dtlb_prot.S,v 1.22 2001/04/11 23:40:32 davem Exp $
+ * dtlb_prot.S: DTLB protection trap strategy.
+ * This is included directly into the trap table.
+ *
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+/* Ways we can get here:
+ *
+ * [TL == 0] 1) User stores to readonly pages.
+ * [TL == 0] 2) Nucleus stores to user readonly pages.
+ * [TL > 0] 3) Nucleus stores to user readonly stack frame.
+ */
+
+/* PROT ** ICACHE line 1: User DTLB protection trap */
+ stxa %g0, [%g1] ASI_DMMU ! Clear SFSR FaultValid bit
+ membar #Sync ! Synchronize ASI stores
+ rdpr %pstate, %g5 ! Move into alternate globals
+ wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
+ rdpr %tl, %g1 ! Need to do a winfixup?
+ cmp %g1, 1 ! Trap level >1?
+ mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr
+ nop
+
+/* PROT ** ICACHE line 2: More real fault processing */
+ bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup
+ ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5
+ ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault
+ mov FAULT_CODE_DTLB | FAULT_CODE_WRITE, %g4
+ nop
+ nop
+ nop
+ nop
+
+/* PROT ** ICACHE line 3: Unused... */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+/* PROT ** ICACHE line 4: Unused... */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
new file mode 100644
index 000000000000..6ffbeb701940
--- /dev/null
+++ b/arch/sparc64/kernel/ebus.c
@@ -0,0 +1,644 @@
+/* $Id: ebus.c,v 1.64 2001/11/08 04:41:33 davem Exp $
+ * ebus.c: PCI to EBus bridge device.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pbm.h>
+#include <asm/ebus.h>
+#include <asm/oplib.h>
+#include <asm/bpp.h>
+#include <asm/irq.h>
+
+/* EBUS dma library. */
+
+#define EBDMA_CSR 0x00UL /* Control/Status */
+#define EBDMA_ADDR 0x04UL /* DMA Address */
+#define EBDMA_COUNT 0x08UL /* DMA Count */
+
+#define EBDMA_CSR_INT_PEND 0x00000001
+#define EBDMA_CSR_ERR_PEND 0x00000002
+#define EBDMA_CSR_DRAIN 0x00000004
+#define EBDMA_CSR_INT_EN 0x00000010
+#define EBDMA_CSR_RESET 0x00000080
+#define EBDMA_CSR_WRITE 0x00000100
+#define EBDMA_CSR_EN_DMA 0x00000200
+#define EBDMA_CSR_CYC_PEND 0x00000400
+#define EBDMA_CSR_DIAG_RD_DONE 0x00000800
+#define EBDMA_CSR_DIAG_WR_DONE 0x00001000
+#define EBDMA_CSR_EN_CNT 0x00002000
+#define EBDMA_CSR_TC 0x00004000
+#define EBDMA_CSR_DIS_CSR_DRN 0x00010000
+#define EBDMA_CSR_BURST_SZ_MASK 0x000c0000
+#define EBDMA_CSR_BURST_SZ_1 0x00080000
+#define EBDMA_CSR_BURST_SZ_4 0x00000000
+#define EBDMA_CSR_BURST_SZ_8 0x00040000
+#define EBDMA_CSR_BURST_SZ_16 0x000c0000
+#define EBDMA_CSR_DIAG_EN 0x00100000
+#define EBDMA_CSR_DIS_ERR_PEND 0x00400000
+#define EBDMA_CSR_TCI_DIS 0x00800000
+#define EBDMA_CSR_EN_NEXT 0x01000000
+#define EBDMA_CSR_DMA_ON 0x02000000
+#define EBDMA_CSR_A_LOADED 0x04000000
+#define EBDMA_CSR_NA_LOADED 0x08000000
+#define EBDMA_CSR_DEV_ID_MASK 0xf0000000
+
+#define EBUS_DMA_RESET_TIMEOUT 10000
+
+static void __ebus_dma_reset(struct ebus_dma_info *p, int no_drain)
+{
+ int i;
+ u32 val = 0;
+
+ writel(EBDMA_CSR_RESET, p->regs + EBDMA_CSR);
+ udelay(1);
+
+ if (no_drain)
+ return;
+
+ for (i = EBUS_DMA_RESET_TIMEOUT; i > 0; i--) {
+ val = readl(p->regs + EBDMA_CSR);
+
+ if (!(val & (EBDMA_CSR_DRAIN | EBDMA_CSR_CYC_PEND)))
+ break;
+ udelay(10);
+ }
+}
+
+static irqreturn_t ebus_dma_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct ebus_dma_info *p = dev_id;
+ unsigned long flags;
+ u32 csr = 0;
+
+ spin_lock_irqsave(&p->lock, flags);
+ csr = readl(p->regs + EBDMA_CSR);
+ writel(csr, p->regs + EBDMA_CSR);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ if (csr & EBDMA_CSR_ERR_PEND) {
+ printk(KERN_CRIT "ebus_dma(%s): DMA error!\n", p->name);
+ p->callback(p, EBUS_DMA_EVENT_ERROR, p->client_cookie);
+ return IRQ_HANDLED;
+ } else if (csr & EBDMA_CSR_INT_PEND) {
+ p->callback(p,
+ (csr & EBDMA_CSR_TC) ?
+ EBUS_DMA_EVENT_DMA : EBUS_DMA_EVENT_DEVICE,
+ p->client_cookie);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+
+}
+
+int ebus_dma_register(struct ebus_dma_info *p)
+{
+ u32 csr;
+
+ if (!p->regs)
+ return -EINVAL;
+ if (p->flags & ~(EBUS_DMA_FLAG_USE_EBDMA_HANDLER |
+ EBUS_DMA_FLAG_TCI_DISABLE))
+ return -EINVAL;
+ if ((p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) && !p->callback)
+ return -EINVAL;
+ if (!strlen(p->name))
+ return -EINVAL;
+
+ __ebus_dma_reset(p, 1);
+
+ csr = EBDMA_CSR_BURST_SZ_16 | EBDMA_CSR_EN_CNT;
+
+ if (p->flags & EBUS_DMA_FLAG_TCI_DISABLE)
+ csr |= EBDMA_CSR_TCI_DIS;
+
+ writel(csr, p->regs + EBDMA_CSR);
+
+ return 0;
+}
+EXPORT_SYMBOL(ebus_dma_register);
+
+int ebus_dma_irq_enable(struct ebus_dma_info *p, int on)
+{
+ unsigned long flags;
+ u32 csr;
+
+ if (on) {
+ if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {
+ if (request_irq(p->irq, ebus_dma_irq, SA_SHIRQ, p->name, p))
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&p->lock, flags);
+ csr = readl(p->regs + EBDMA_CSR);
+ csr |= EBDMA_CSR_INT_EN;
+ writel(csr, p->regs + EBDMA_CSR);
+ spin_unlock_irqrestore(&p->lock, flags);
+ } else {
+ spin_lock_irqsave(&p->lock, flags);
+ csr = readl(p->regs + EBDMA_CSR);
+ csr &= ~EBDMA_CSR_INT_EN;
+ writel(csr, p->regs + EBDMA_CSR);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {
+ free_irq(p->irq, p);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ebus_dma_irq_enable);
+
+void ebus_dma_unregister(struct ebus_dma_info *p)
+{
+ unsigned long flags;
+ u32 csr;
+ int irq_on = 0;
+
+ spin_lock_irqsave(&p->lock, flags);
+ csr = readl(p->regs + EBDMA_CSR);
+ if (csr & EBDMA_CSR_INT_EN) {
+ csr &= ~EBDMA_CSR_INT_EN;
+ writel(csr, p->regs + EBDMA_CSR);
+ irq_on = 1;
+ }
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ if (irq_on)
+ free_irq(p->irq, p);
+}
+EXPORT_SYMBOL(ebus_dma_unregister);
+
+int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, size_t len)
+{
+ unsigned long flags;
+ u32 csr;
+ int err;
+
+ if (len >= (1 << 24))
+ return -EINVAL;
+
+ spin_lock_irqsave(&p->lock, flags);
+ csr = readl(p->regs + EBDMA_CSR);
+ err = -EINVAL;
+ if (!(csr & EBDMA_CSR_EN_DMA))
+ goto out;
+ err = -EBUSY;
+ if (csr & EBDMA_CSR_NA_LOADED)
+ goto out;
+
+ writel(len, p->regs + EBDMA_COUNT);
+ writel(bus_addr, p->regs + EBDMA_ADDR);
+ err = 0;
+
+out:
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return err;
+}
+EXPORT_SYMBOL(ebus_dma_request);
+
+void ebus_dma_prepare(struct ebus_dma_info *p, int write)
+{
+ unsigned long flags;
+ u32 csr;
+
+ spin_lock_irqsave(&p->lock, flags);
+ __ebus_dma_reset(p, 0);
+
+ csr = (EBDMA_CSR_INT_EN |
+ EBDMA_CSR_EN_CNT |
+ EBDMA_CSR_BURST_SZ_16 |
+ EBDMA_CSR_EN_NEXT);
+
+ if (write)
+ csr |= EBDMA_CSR_WRITE;
+ if (p->flags & EBUS_DMA_FLAG_TCI_DISABLE)
+ csr |= EBDMA_CSR_TCI_DIS;
+
+ writel(csr, p->regs + EBDMA_CSR);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+EXPORT_SYMBOL(ebus_dma_prepare);
+
+unsigned int ebus_dma_residue(struct ebus_dma_info *p)
+{
+ return readl(p->regs + EBDMA_COUNT);
+}
+EXPORT_SYMBOL(ebus_dma_residue);
+
+unsigned int ebus_dma_addr(struct ebus_dma_info *p)
+{
+ return readl(p->regs + EBDMA_ADDR);
+}
+EXPORT_SYMBOL(ebus_dma_addr);
+
+void ebus_dma_enable(struct ebus_dma_info *p, int on)
+{
+ unsigned long flags;
+ u32 orig_csr, csr;
+
+ spin_lock_irqsave(&p->lock, flags);
+ orig_csr = csr = readl(p->regs + EBDMA_CSR);
+ if (on)
+ csr |= EBDMA_CSR_EN_DMA;
+ else
+ csr &= ~EBDMA_CSR_EN_DMA;
+ if ((orig_csr & EBDMA_CSR_EN_DMA) !=
+ (csr & EBDMA_CSR_EN_DMA))
+ writel(csr, p->regs + EBDMA_CSR);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+EXPORT_SYMBOL(ebus_dma_enable);
+
+struct linux_ebus *ebus_chain = NULL;
+
+#ifdef CONFIG_SUN_AUXIO
+extern void auxio_probe(void);
+#endif
+
+static inline void *ebus_alloc(size_t size)
+{
+ void *mem;
+
+ mem = kmalloc(size, GFP_ATOMIC);
+ if (!mem)
+ panic("ebus_alloc: out of memory");
+ memset((char *)mem, 0, size);
+ return mem;
+}
+
+static void __init ebus_ranges_init(struct linux_ebus *ebus)
+{
+ int success;
+
+ ebus->num_ebus_ranges = 0;
+ success = prom_getproperty(ebus->prom_node, "ranges",
+ (char *)ebus->ebus_ranges,
+ sizeof(ebus->ebus_ranges));
+ if (success != -1)
+ ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges));
+}
+
+static void __init ebus_intmap_init(struct linux_ebus *ebus)
+{
+ int success;
+
+ ebus->num_ebus_intmap = 0;
+ success = prom_getproperty(ebus->prom_node, "interrupt-map",
+ (char *)ebus->ebus_intmap,
+ sizeof(ebus->ebus_intmap));
+ if (success == -1)
+ return;
+
+ ebus->num_ebus_intmap = (success/sizeof(struct linux_prom_ebus_intmap));
+
+ success = prom_getproperty(ebus->prom_node, "interrupt-map-mask",
+ (char *)&ebus->ebus_intmask,
+ sizeof(ebus->ebus_intmask));
+ if (success == -1) {
+ prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__);
+ prom_halt();
+ }
+}
+
+int __init ebus_intmap_match(struct linux_ebus *ebus,
+ struct linux_prom_registers *reg,
+ int *interrupt)
+{
+ unsigned int hi, lo, irq;
+ int i;
+
+ if (!ebus->num_ebus_intmap)
+ return 0;
+
+ hi = reg->which_io & ebus->ebus_intmask.phys_hi;
+ lo = reg->phys_addr & ebus->ebus_intmask.phys_lo;
+ irq = *interrupt & ebus->ebus_intmask.interrupt;
+ for (i = 0; i < ebus->num_ebus_intmap; i++) {
+ if ((ebus->ebus_intmap[i].phys_hi == hi) &&
+ (ebus->ebus_intmap[i].phys_lo == lo) &&
+ (ebus->ebus_intmap[i].interrupt == irq)) {
+ *interrupt = ebus->ebus_intmap[i].cinterrupt;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void __init fill_ebus_child(int node, struct linux_prom_registers *preg,
+ struct linux_ebus_child *dev, int non_standard_regs)
+{
+ int regs[PROMREG_MAX];
+ int irqs[PROMREG_MAX];
+ int i, len;
+
+ dev->prom_node = node;
+ prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name));
+ printk(" (%s)", dev->prom_name);
+
+ len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+ dev->num_addrs = len / sizeof(regs[0]);
+
+ if (non_standard_regs) {
+ /* This is to handle reg properties which are not
+ * in the parent relative format. One example are
+ * children of the i2c device on CompactPCI systems.
+ *
+ * So, for such devices we just record the property
+ * raw in the child resources.
+ */
+ for (i = 0; i < dev->num_addrs; i++)
+ dev->resource[i].start = regs[i];
+ } else {
+ for (i = 0; i < dev->num_addrs; i++) {
+ int rnum = regs[i];
+ if (rnum >= dev->parent->num_addrs) {
+ prom_printf("UGH: property for %s was %d, need < %d\n",
+ dev->prom_name, len, dev->parent->num_addrs);
+ panic(__FUNCTION__);
+ }
+ dev->resource[i].start = dev->parent->resource[i].start;
+ dev->resource[i].end = dev->parent->resource[i].end;
+ dev->resource[i].flags = IORESOURCE_MEM;
+ dev->resource[i].name = dev->prom_name;
+ }
+ }
+
+ for (i = 0; i < PROMINTR_MAX; i++)
+ dev->irqs[i] = PCI_IRQ_NONE;
+
+ len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
+ if ((len == -1) || (len == 0)) {
+ dev->num_irqs = 0;
+ /*
+ * Oh, well, some PROMs don't export interrupts
+ * property to children of EBus devices...
+ *
+ * Be smart about PS/2 keyboard and mouse.
+ */
+ if (!strcmp(dev->parent->prom_name, "8042")) {
+ if (!strcmp(dev->prom_name, "kb_ps2")) {
+ dev->num_irqs = 1;
+ dev->irqs[0] = dev->parent->irqs[0];
+ } else {
+ dev->num_irqs = 1;
+ dev->irqs[0] = dev->parent->irqs[1];
+ }
+ }
+ } else {
+ dev->num_irqs = len / sizeof(irqs[0]);
+ for (i = 0; i < dev->num_irqs; i++) {
+ struct pci_pbm_info *pbm = dev->bus->parent;
+ struct pci_controller_info *p = pbm->parent;
+
+ if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) {
+ dev->irqs[i] = p->irq_build(pbm,
+ dev->bus->self,
+ irqs[i]);
+ } else {
+ /* If we get a bogus interrupt property, just
+ * record the raw value instead of punting.
+ */
+ dev->irqs[i] = irqs[i];
+ }
+ }
+ }
+}
+
+static int __init child_regs_nonstandard(struct linux_ebus_device *dev)
+{
+ if (!strcmp(dev->prom_name, "i2c") ||
+ !strcmp(dev->prom_name, "SUNW,lombus"))
+ return 1;
+ return 0;
+}
+
+void __init fill_ebus_device(int node, struct linux_ebus_device *dev)
+{
+ struct linux_prom_registers regs[PROMREG_MAX];
+ struct linux_ebus_child *child;
+ int irqs[PROMINTR_MAX];
+ int i, n, len;
+
+ dev->prom_node = node;
+ prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name));
+ printk(" [%s", dev->prom_name);
+
+ len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+ if (len == -1) {
+ dev->num_addrs = 0;
+ goto probe_interrupts;
+ }
+
+ if (len % sizeof(struct linux_prom_registers)) {
+ prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
+ dev->prom_name, len,
+ (int)sizeof(struct linux_prom_registers));
+ prom_halt();
+ }
+ dev->num_addrs = len / sizeof(struct linux_prom_registers);
+
+ for (i = 0; i < dev->num_addrs; i++) {
+ /* XXX Learn how to interpret ebus ranges... -DaveM */
+ if (regs[i].which_io >= 0x10)
+ n = (regs[i].which_io - 0x10) >> 2;
+ else
+ n = regs[i].which_io;
+
+ dev->resource[i].start = dev->bus->self->resource[n].start;
+ dev->resource[i].start += (unsigned long)regs[i].phys_addr;
+ dev->resource[i].end =
+ (dev->resource[i].start + (unsigned long)regs[i].reg_size - 1UL);
+ dev->resource[i].flags = IORESOURCE_MEM;
+ dev->resource[i].name = dev->prom_name;
+ request_resource(&dev->bus->self->resource[n],
+ &dev->resource[i]);
+ }
+
+probe_interrupts:
+ for (i = 0; i < PROMINTR_MAX; i++)
+ dev->irqs[i] = PCI_IRQ_NONE;
+
+ len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
+ if ((len == -1) || (len == 0)) {
+ dev->num_irqs = 0;
+ } else {
+ dev->num_irqs = len / sizeof(irqs[0]);
+ for (i = 0; i < dev->num_irqs; i++) {
+ struct pci_pbm_info *pbm = dev->bus->parent;
+ struct pci_controller_info *p = pbm->parent;
+
+ if (ebus_intmap_match(dev->bus, &regs[0], &irqs[i]) != -1) {
+ dev->irqs[i] = p->irq_build(pbm,
+ dev->bus->self,
+ irqs[i]);
+ } else {
+ /* If we get a bogus interrupt property, just
+ * record the raw value instead of punting.
+ */
+ dev->irqs[i] = irqs[i];
+ }
+ }
+ }
+
+ if ((node = prom_getchild(node))) {
+ printk(" ->");
+ dev->children = ebus_alloc(sizeof(struct linux_ebus_child));
+
+ child = dev->children;
+ child->next = NULL;
+ child->parent = dev;
+ child->bus = dev->bus;
+ fill_ebus_child(node, &regs[0],
+ child, child_regs_nonstandard(dev));
+
+ while ((node = prom_getsibling(node)) != 0) {
+ child->next = ebus_alloc(sizeof(struct linux_ebus_child));
+
+ child = child->next;
+ child->next = NULL;
+ child->parent = dev;
+ child->bus = dev->bus;
+ fill_ebus_child(node, &regs[0],
+ child, child_regs_nonstandard(dev));
+ }
+ }
+ printk("]");
+}
+
+static struct pci_dev *find_next_ebus(struct pci_dev *start, int *is_rio_p)
+{
+ struct pci_dev *pdev = start;
+
+ do {
+ pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_ANY_ID, pdev);
+ if (pdev &&
+ (pdev->device == PCI_DEVICE_ID_SUN_EBUS ||
+ pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS))
+ break;
+ } while (pdev != NULL);
+
+ if (pdev && (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS))
+ *is_rio_p = 1;
+ else
+ *is_rio_p = 0;
+
+ return pdev;
+}
+
+void __init ebus_init(void)
+{
+ struct pci_pbm_info *pbm;
+ struct linux_ebus_device *dev;
+ struct linux_ebus *ebus;
+ struct pci_dev *pdev;
+ struct pcidev_cookie *cookie;
+ int nd, ebusnd, is_rio;
+ int num_ebus = 0;
+
+ pdev = find_next_ebus(NULL, &is_rio);
+ if (!pdev) {
+ printk("ebus: No EBus's found.\n");
+ return;
+ }
+
+ cookie = pdev->sysdata;
+ ebusnd = cookie->prom_node;
+
+ ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus));
+ ebus->next = NULL;
+ ebus->is_rio = is_rio;
+
+ while (ebusnd) {
+ /* SUNW,pci-qfe uses four empty ebuses on it.
+ I think we should not consider them here,
+ as they have half of the properties this
+ code expects and once we do PCI hot-plug,
+ we'd have to tweak with the ebus_chain
+ in the runtime after initialization. -jj */
+ if (!prom_getchild (ebusnd)) {
+ pdev = find_next_ebus(pdev, &is_rio);
+ if (!pdev) {
+ if (ebus == ebus_chain) {
+ ebus_chain = NULL;
+ printk("ebus: No EBus's found.\n");
+ return;
+ }
+ break;
+ }
+ ebus->is_rio = is_rio;
+ cookie = pdev->sysdata;
+ ebusnd = cookie->prom_node;
+ continue;
+ }
+ printk("ebus%d:", num_ebus);
+
+ prom_getstring(ebusnd, "name", ebus->prom_name, sizeof(ebus->prom_name));
+ ebus->index = num_ebus;
+ ebus->prom_node = ebusnd;
+ ebus->self = pdev;
+ ebus->parent = pbm = cookie->pbm;
+
+ ebus_ranges_init(ebus);
+ ebus_intmap_init(ebus);
+
+ nd = prom_getchild(ebusnd);
+ if (!nd)
+ goto next_ebus;
+
+ ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device));
+
+ dev = ebus->devices;
+ dev->next = NULL;
+ dev->children = NULL;
+ dev->bus = ebus;
+ fill_ebus_device(nd, dev);
+
+ while ((nd = prom_getsibling(nd)) != 0) {
+ dev->next = ebus_alloc(sizeof(struct linux_ebus_device));
+
+ dev = dev->next;
+ dev->next = NULL;
+ dev->children = NULL;
+ dev->bus = ebus;
+ fill_ebus_device(nd, dev);
+ }
+
+ next_ebus:
+ printk("\n");
+
+ pdev = find_next_ebus(pdev, &is_rio);
+ if (!pdev)
+ break;
+
+ cookie = pdev->sysdata;
+ ebusnd = cookie->prom_node;
+
+ ebus->next = ebus_alloc(sizeof(struct linux_ebus));
+ ebus = ebus->next;
+ ebus->next = NULL;
+ ebus->is_rio = is_rio;
+ ++num_ebus;
+ }
+
+#ifdef CONFIG_SUN_AUXIO
+ auxio_probe();
+#endif
+}
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S
new file mode 100644
index 000000000000..a47f2d0b1a29
--- /dev/null
+++ b/arch/sparc64/kernel/entry.S
@@ -0,0 +1,1919 @@
+/* $Id: entry.S,v 1.144 2002/02/09 19:49:30 davem Exp $
+ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points.
+ *
+ * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996,98,99 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/smp.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/visasm.h>
+#include <asm/estate.h>
+#include <asm/auxio.h>
+
+/* #define SYSCALL_TRACING 1 */
+
+#define curptr g6
+
+#define NR_SYSCALLS 284 /* Each OS is different... */
+
+ .text
+ .align 32
+
+ .globl sparc64_vpte_patchme1
+ .globl sparc64_vpte_patchme2
+/*
+ * On a second level vpte miss, check whether the original fault is to the OBP
+ * range (note that this is only possible for instruction miss, data misses to
+ * obp range do not use vpte). If so, go back directly to the faulting address.
+ * This is because we want to read the tpc, otherwise we have no way of knowing
+ * the 8k aligned faulting address if we are using >8k kernel pagesize. This
+ * also ensures no vpte range addresses are dropped into tlb while obp is
+ * executing (see inherit_locked_prom_mappings() rant).
+ */
+sparc64_vpte_nucleus:
+ /* Load 0xf0000000, which is LOW_OBP_ADDRESS. */
+ mov 0xf, %g5
+ sllx %g5, 28, %g5
+
+ /* Is addr >= LOW_OBP_ADDRESS? */
+ cmp %g4, %g5
+ blu,pn %xcc, sparc64_vpte_patchme1
+ mov 0x1, %g5
+
+ /* Load 0x100000000, which is HI_OBP_ADDRESS. */
+ sllx %g5, 32, %g5
+
+ /* Is addr < HI_OBP_ADDRESS? */
+ cmp %g4, %g5
+ blu,pn %xcc, obp_iaddr_patch
+ nop
+
+ /* These two instructions are patched by paginig_init(). */
+sparc64_vpte_patchme1:
+ sethi %hi(0), %g5
+sparc64_vpte_patchme2:
+ or %g5, %lo(0), %g5
+
+ /* With kernel PGD in %g5, branch back into dtlb_backend. */
+ ba,pt %xcc, sparc64_kpte_continue
+ andn %g1, 0x3, %g1 /* Finish PMD offset adjustment. */
+
+vpte_noent:
+ /* Restore previous TAG_ACCESS, %g5 is zero, and we will
+ * skip over the trap instruction so that the top level
+ * TLB miss handler will thing this %g5 value is just an
+ * invalid PTE, thus branching to full fault processing.
+ */
+ mov TLB_SFSR, %g1
+ stxa %g4, [%g1 + %g1] ASI_DMMU
+ done
+
+ .globl obp_iaddr_patch
+obp_iaddr_patch:
+ /* These two instructions patched by inherit_prom_mappings(). */
+ sethi %hi(0), %g5
+ or %g5, %lo(0), %g5
+
+ /* Behave as if we are at TL0. */
+ wrpr %g0, 1, %tl
+ rdpr %tpc, %g4 /* Find original faulting iaddr */
+ srlx %g4, 13, %g4 /* Throw out context bits */
+ sllx %g4, 13, %g4 /* g4 has vpn + ctx0 now */
+
+ /* Restore previous TAG_ACCESS. */
+ mov TLB_SFSR, %g1
+ stxa %g4, [%g1 + %g1] ASI_IMMU
+
+ /* Get PMD offset. */
+ srlx %g4, 23, %g6
+ and %g6, 0x7ff, %g6
+ sllx %g6, 2, %g6
+
+ /* Load PMD, is it valid? */
+ lduwa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
+ brz,pn %g5, longpath
+ sllx %g5, 11, %g5
+
+ /* Get PTE offset. */
+ srlx %g4, 13, %g6
+ and %g6, 0x3ff, %g6
+ sllx %g6, 3, %g6
+
+ /* Load PTE. */
+ ldxa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
+ brgez,pn %g5, longpath
+ nop
+
+ /* TLB load and return from trap. */
+ stxa %g5, [%g0] ASI_ITLB_DATA_IN
+ retry
+
+ .globl obp_daddr_patch
+obp_daddr_patch:
+ /* These two instructions patched by inherit_prom_mappings(). */
+ sethi %hi(0), %g5
+ or %g5, %lo(0), %g5
+
+ /* Get PMD offset. */
+ srlx %g4, 23, %g6
+ and %g6, 0x7ff, %g6
+ sllx %g6, 2, %g6
+
+ /* Load PMD, is it valid? */
+ lduwa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
+ brz,pn %g5, longpath
+ sllx %g5, 11, %g5
+
+ /* Get PTE offset. */
+ srlx %g4, 13, %g6
+ and %g6, 0x3ff, %g6
+ sllx %g6, 3, %g6
+
+ /* Load PTE. */
+ ldxa [%g5 + %g6] ASI_PHYS_USE_EC, %g5
+ brgez,pn %g5, longpath
+ nop
+
+ /* TLB load and return from trap. */
+ stxa %g5, [%g0] ASI_DTLB_DATA_IN
+ retry
+
+/*
+ * On a first level data miss, check whether this is to the OBP range (note
+ * that such accesses can be made by prom, as well as by kernel using
+ * prom_getproperty on "address"), and if so, do not use vpte access ...
+ * rather, use information saved during inherit_prom_mappings() using 8k
+ * pagesize.
+ */
+kvmap:
+ /* Load 0xf0000000, which is LOW_OBP_ADDRESS. */
+ mov 0xf, %g5
+ sllx %g5, 28, %g5
+
+ /* Is addr >= LOW_OBP_ADDRESS? */
+ cmp %g4, %g5
+ blu,pn %xcc, vmalloc_addr
+ mov 0x1, %g5
+
+ /* Load 0x100000000, which is HI_OBP_ADDRESS. */
+ sllx %g5, 32, %g5
+
+ /* Is addr < HI_OBP_ADDRESS? */
+ cmp %g4, %g5
+ blu,pn %xcc, obp_daddr_patch
+ nop
+
+vmalloc_addr:
+ /* If we get here, a vmalloc addr accessed, load kernel VPTE. */
+ ldxa [%g3 + %g6] ASI_N, %g5
+ brgez,pn %g5, longpath
+ nop
+
+ /* PTE is valid, load into TLB and return from trap. */
+ stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
+ retry
+
+ /* This is trivial with the new code... */
+ .globl do_fpdis
+do_fpdis:
+ sethi %hi(TSTATE_PEF), %g4 ! IEU0
+ rdpr %tstate, %g5
+ andcc %g5, %g4, %g0
+ be,pt %xcc, 1f
+ nop
+ rd %fprs, %g5
+ andcc %g5, FPRS_FEF, %g0
+ be,pt %xcc, 1f
+ nop
+
+ /* Legal state when DCR_IFPOE is set in Cheetah %dcr. */
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ add %g0, %g0, %g0
+ ba,a,pt %xcc, rtrap_clr_l6
+
+1: ldub [%g6 + TI_FPSAVED], %g5 ! Load Group
+ wr %g0, FPRS_FEF, %fprs ! LSU Group+4bubbles
+ andcc %g5, FPRS_FEF, %g0 ! IEU1 Group
+ be,a,pt %icc, 1f ! CTI
+ clr %g7 ! IEU0
+ ldx [%g6 + TI_GSR], %g7 ! Load Group
+1: andcc %g5, FPRS_DL, %g0 ! IEU1
+ bne,pn %icc, 2f ! CTI
+ fzero %f0 ! FPA
+ andcc %g5, FPRS_DU, %g0 ! IEU1 Group
+ bne,pn %icc, 1f ! CTI
+ fzero %f2 ! FPA
+ faddd %f0, %f2, %f4
+ fmuld %f0, %f2, %f6
+ faddd %f0, %f2, %f8
+ fmuld %f0, %f2, %f10
+ faddd %f0, %f2, %f12
+ fmuld %f0, %f2, %f14
+ faddd %f0, %f2, %f16
+ fmuld %f0, %f2, %f18
+ faddd %f0, %f2, %f20
+ fmuld %f0, %f2, %f22
+ faddd %f0, %f2, %f24
+ fmuld %f0, %f2, %f26
+ faddd %f0, %f2, %f28
+ fmuld %f0, %f2, %f30
+ faddd %f0, %f2, %f32
+ fmuld %f0, %f2, %f34
+ faddd %f0, %f2, %f36
+ fmuld %f0, %f2, %f38
+ faddd %f0, %f2, %f40
+ fmuld %f0, %f2, %f42
+ faddd %f0, %f2, %f44
+ fmuld %f0, %f2, %f46
+ faddd %f0, %f2, %f48
+ fmuld %f0, %f2, %f50
+ faddd %f0, %f2, %f52
+ fmuld %f0, %f2, %f54
+ faddd %f0, %f2, %f56
+ fmuld %f0, %f2, %f58
+ b,pt %xcc, fpdis_exit2
+ faddd %f0, %f2, %f60
+1: mov SECONDARY_CONTEXT, %g3
+ add %g6, TI_FPREGS + 0x80, %g1
+ faddd %f0, %f2, %f4
+ fmuld %f0, %f2, %f6
+ ldxa [%g3] ASI_DMMU, %g5
+cplus_fptrap_insn_1:
+ sethi %hi(0), %g2
+ stxa %g2, [%g3] ASI_DMMU
+ membar #Sync
+ add %g6, TI_FPREGS + 0xc0, %g2
+ faddd %f0, %f2, %f8
+ fmuld %f0, %f2, %f10
+ ldda [%g1] ASI_BLK_S, %f32 ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ ldda [%g2] ASI_BLK_S, %f48
+ faddd %f0, %f2, %f12
+ fmuld %f0, %f2, %f14
+ faddd %f0, %f2, %f16
+ fmuld %f0, %f2, %f18
+ faddd %f0, %f2, %f20
+ fmuld %f0, %f2, %f22
+ faddd %f0, %f2, %f24
+ fmuld %f0, %f2, %f26
+ faddd %f0, %f2, %f28
+ fmuld %f0, %f2, %f30
+ b,pt %xcc, fpdis_exit
+ membar #Sync
+2: andcc %g5, FPRS_DU, %g0
+ bne,pt %icc, 3f
+ fzero %f32
+ mov SECONDARY_CONTEXT, %g3
+ fzero %f34
+ ldxa [%g3] ASI_DMMU, %g5
+ add %g6, TI_FPREGS, %g1
+cplus_fptrap_insn_2:
+ sethi %hi(0), %g2
+ stxa %g2, [%g3] ASI_DMMU
+ membar #Sync
+ add %g6, TI_FPREGS + 0x40, %g2
+ faddd %f32, %f34, %f36
+ fmuld %f32, %f34, %f38
+ ldda [%g1] ASI_BLK_S, %f0 ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ ldda [%g2] ASI_BLK_S, %f16
+ faddd %f32, %f34, %f40
+ fmuld %f32, %f34, %f42
+ faddd %f32, %f34, %f44
+ fmuld %f32, %f34, %f46
+ faddd %f32, %f34, %f48
+ fmuld %f32, %f34, %f50
+ faddd %f32, %f34, %f52
+ fmuld %f32, %f34, %f54
+ faddd %f32, %f34, %f56
+ fmuld %f32, %f34, %f58
+ faddd %f32, %f34, %f60
+ fmuld %f32, %f34, %f62
+ ba,pt %xcc, fpdis_exit
+ membar #Sync
+3: mov SECONDARY_CONTEXT, %g3
+ add %g6, TI_FPREGS, %g1
+ ldxa [%g3] ASI_DMMU, %g5
+cplus_fptrap_insn_3:
+ sethi %hi(0), %g2
+ stxa %g2, [%g3] ASI_DMMU
+ membar #Sync
+ mov 0x40, %g2
+ ldda [%g1] ASI_BLK_S, %f0 ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ ldda [%g1 + %g2] ASI_BLK_S, %f16
+ add %g1, 0x80, %g1
+ ldda [%g1] ASI_BLK_S, %f32
+ ldda [%g1 + %g2] ASI_BLK_S, %f48
+ membar #Sync
+fpdis_exit:
+ stxa %g5, [%g3] ASI_DMMU
+ membar #Sync
+fpdis_exit2:
+ wr %g7, 0, %gsr
+ ldx [%g6 + TI_XFSR], %fsr
+ rdpr %tstate, %g3
+ or %g3, %g4, %g3 ! anal...
+ wrpr %g3, %tstate
+ wr %g0, FPRS_FEF, %fprs ! clean DU/DL bits
+ retry
+
+ .align 32
+fp_other_bounce:
+ call do_fpother
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl do_fpother_check_fitos
+ .align 32
+do_fpother_check_fitos:
+ sethi %hi(fp_other_bounce - 4), %g7
+ or %g7, %lo(fp_other_bounce - 4), %g7
+
+ /* NOTE: Need to preserve %g7 until we fully commit
+ * to the fitos fixup.
+ */
+ stx %fsr, [%g6 + TI_XFSR]
+ rdpr %tstate, %g3
+ andcc %g3, TSTATE_PRIV, %g0
+ bne,pn %xcc, do_fptrap_after_fsr
+ nop
+ ldx [%g6 + TI_XFSR], %g3
+ srlx %g3, 14, %g1
+ and %g1, 7, %g1
+ cmp %g1, 2 ! Unfinished FP-OP
+ bne,pn %xcc, do_fptrap_after_fsr
+ sethi %hi(1 << 23), %g1 ! Inexact
+ andcc %g3, %g1, %g0
+ bne,pn %xcc, do_fptrap_after_fsr
+ rdpr %tpc, %g1
+ lduwa [%g1] ASI_AIUP, %g3 ! This cannot ever fail
+#define FITOS_MASK 0xc1f83fe0
+#define FITOS_COMPARE 0x81a01880
+ sethi %hi(FITOS_MASK), %g1
+ or %g1, %lo(FITOS_MASK), %g1
+ and %g3, %g1, %g1
+ sethi %hi(FITOS_COMPARE), %g2
+ or %g2, %lo(FITOS_COMPARE), %g2
+ cmp %g1, %g2
+ bne,pn %xcc, do_fptrap_after_fsr
+ nop
+ std %f62, [%g6 + TI_FPREGS + (62 * 4)]
+ sethi %hi(fitos_table_1), %g1
+ and %g3, 0x1f, %g2
+ or %g1, %lo(fitos_table_1), %g1
+ sllx %g2, 2, %g2
+ jmpl %g1 + %g2, %g0
+ ba,pt %xcc, fitos_emul_continue
+
+fitos_table_1:
+ fitod %f0, %f62
+ fitod %f1, %f62
+ fitod %f2, %f62
+ fitod %f3, %f62
+ fitod %f4, %f62
+ fitod %f5, %f62
+ fitod %f6, %f62
+ fitod %f7, %f62
+ fitod %f8, %f62
+ fitod %f9, %f62
+ fitod %f10, %f62
+ fitod %f11, %f62
+ fitod %f12, %f62
+ fitod %f13, %f62
+ fitod %f14, %f62
+ fitod %f15, %f62
+ fitod %f16, %f62
+ fitod %f17, %f62
+ fitod %f18, %f62
+ fitod %f19, %f62
+ fitod %f20, %f62
+ fitod %f21, %f62
+ fitod %f22, %f62
+ fitod %f23, %f62
+ fitod %f24, %f62
+ fitod %f25, %f62
+ fitod %f26, %f62
+ fitod %f27, %f62
+ fitod %f28, %f62
+ fitod %f29, %f62
+ fitod %f30, %f62
+ fitod %f31, %f62
+
+fitos_emul_continue:
+ sethi %hi(fitos_table_2), %g1
+ srl %g3, 25, %g2
+ or %g1, %lo(fitos_table_2), %g1
+ and %g2, 0x1f, %g2
+ sllx %g2, 2, %g2
+ jmpl %g1 + %g2, %g0
+ ba,pt %xcc, fitos_emul_fini
+
+fitos_table_2:
+ fdtos %f62, %f0
+ fdtos %f62, %f1
+ fdtos %f62, %f2
+ fdtos %f62, %f3
+ fdtos %f62, %f4
+ fdtos %f62, %f5
+ fdtos %f62, %f6
+ fdtos %f62, %f7
+ fdtos %f62, %f8
+ fdtos %f62, %f9
+ fdtos %f62, %f10
+ fdtos %f62, %f11
+ fdtos %f62, %f12
+ fdtos %f62, %f13
+ fdtos %f62, %f14
+ fdtos %f62, %f15
+ fdtos %f62, %f16
+ fdtos %f62, %f17
+ fdtos %f62, %f18
+ fdtos %f62, %f19
+ fdtos %f62, %f20
+ fdtos %f62, %f21
+ fdtos %f62, %f22
+ fdtos %f62, %f23
+ fdtos %f62, %f24
+ fdtos %f62, %f25
+ fdtos %f62, %f26
+ fdtos %f62, %f27
+ fdtos %f62, %f28
+ fdtos %f62, %f29
+ fdtos %f62, %f30
+ fdtos %f62, %f31
+
+fitos_emul_fini:
+ ldd [%g6 + TI_FPREGS + (62 * 4)], %f62
+ done
+
+ .globl do_fptrap
+ .align 32
+do_fptrap:
+ stx %fsr, [%g6 + TI_XFSR]
+do_fptrap_after_fsr:
+ ldub [%g6 + TI_FPSAVED], %g3
+ rd %fprs, %g1
+ or %g3, %g1, %g3
+ stb %g3, [%g6 + TI_FPSAVED]
+ rd %gsr, %g3
+ stx %g3, [%g6 + TI_GSR]
+ mov SECONDARY_CONTEXT, %g3
+ ldxa [%g3] ASI_DMMU, %g5
+cplus_fptrap_insn_4:
+ sethi %hi(0), %g2
+ stxa %g2, [%g3] ASI_DMMU
+ membar #Sync
+ add %g6, TI_FPREGS, %g2
+ andcc %g1, FPRS_DL, %g0
+ be,pn %icc, 4f
+ mov 0x40, %g3
+ stda %f0, [%g2] ASI_BLK_S
+ stda %f16, [%g2 + %g3] ASI_BLK_S
+ andcc %g1, FPRS_DU, %g0
+ be,pn %icc, 5f
+4: add %g2, 128, %g2
+ stda %f32, [%g2] ASI_BLK_S
+ stda %f48, [%g2 + %g3] ASI_BLK_S
+5: mov SECONDARY_CONTEXT, %g1
+ membar #Sync
+ stxa %g5, [%g1] ASI_DMMU
+ membar #Sync
+ ba,pt %xcc, etrap
+ wr %g0, 0, %fprs
+
+cplus_fptrap_1:
+ sethi %hi(CTX_CHEETAH_PLUS_CTX0), %g2
+
+ .globl cheetah_plus_patch_fpdis
+cheetah_plus_patch_fpdis:
+ /* We configure the dTLB512_0 for 4MB pages and the
+ * dTLB512_1 for 8K pages when in context zero.
+ */
+ sethi %hi(cplus_fptrap_1), %o0
+ lduw [%o0 + %lo(cplus_fptrap_1)], %o1
+
+ set cplus_fptrap_insn_1, %o2
+ stw %o1, [%o2]
+ flush %o2
+ set cplus_fptrap_insn_2, %o2
+ stw %o1, [%o2]
+ flush %o2
+ set cplus_fptrap_insn_3, %o2
+ stw %o1, [%o2]
+ flush %o2
+ set cplus_fptrap_insn_4, %o2
+ stw %o1, [%o2]
+ flush %o2
+
+ retl
+ nop
+
+ /* The registers for cross calls will be:
+ *
+ * DATA 0: [low 32-bits] Address of function to call, jmp to this
+ * [high 32-bits] MMU Context Argument 0, place in %g5
+ * DATA 1: Address Argument 1, place in %g6
+ * DATA 2: Address Argument 2, place in %g7
+ *
+ * With this method we can do most of the cross-call tlb/cache
+ * flushing very quickly.
+ *
+ * Current CPU's IRQ worklist table is locked into %g1,
+ * don't touch.
+ */
+ .text
+ .align 32
+ .globl do_ivec
+do_ivec:
+ mov 0x40, %g3
+ ldxa [%g3 + %g0] ASI_INTR_R, %g3
+ sethi %hi(KERNBASE), %g4
+ cmp %g3, %g4
+ bgeu,pn %xcc, do_ivec_xcall
+ srlx %g3, 32, %g5
+ stxa %g0, [%g0] ASI_INTR_RECEIVE
+ membar #Sync
+
+ sethi %hi(ivector_table), %g2
+ sllx %g3, 5, %g3
+ or %g2, %lo(ivector_table), %g2
+ add %g2, %g3, %g3
+ ldx [%g3 + 0x08], %g2 /* irq_info */
+ ldub [%g3 + 0x04], %g4 /* pil */
+ brz,pn %g2, do_ivec_spurious
+ mov 1, %g2
+
+ sllx %g2, %g4, %g2
+ sllx %g4, 2, %g4
+ lduw [%g6 + %g4], %g5 /* g5 = irq_work(cpu, pil) */
+ stw %g5, [%g3 + 0x00] /* bucket->irq_chain = g5 */
+ stw %g3, [%g6 + %g4] /* irq_work(cpu, pil) = bucket */
+ wr %g2, 0x0, %set_softint
+ retry
+do_ivec_xcall:
+ mov 0x50, %g1
+
+ ldxa [%g1 + %g0] ASI_INTR_R, %g1
+ srl %g3, 0, %g3
+ mov 0x60, %g7
+ ldxa [%g7 + %g0] ASI_INTR_R, %g7
+ stxa %g0, [%g0] ASI_INTR_RECEIVE
+ membar #Sync
+ ba,pt %xcc, 1f
+ nop
+
+ .align 32
+1: jmpl %g3, %g0
+ nop
+
+do_ivec_spurious:
+ stw %g3, [%g6 + 0x00] /* irq_work(cpu, 0) = bucket */
+ rdpr %pstate, %g5
+
+ wrpr %g5, PSTATE_IG | PSTATE_AG, %pstate
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ call catch_disabled_ivec
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl save_alternate_globals
+save_alternate_globals: /* %o0 = save_area */
+ rdpr %pstate, %o5
+ andn %o5, PSTATE_IE, %o1
+ wrpr %o1, PSTATE_AG, %pstate
+ stx %g0, [%o0 + 0x00]
+ stx %g1, [%o0 + 0x08]
+ stx %g2, [%o0 + 0x10]
+ stx %g3, [%o0 + 0x18]
+ stx %g4, [%o0 + 0x20]
+ stx %g5, [%o0 + 0x28]
+ stx %g6, [%o0 + 0x30]
+ stx %g7, [%o0 + 0x38]
+ wrpr %o1, PSTATE_IG, %pstate
+ stx %g0, [%o0 + 0x40]
+ stx %g1, [%o0 + 0x48]
+ stx %g2, [%o0 + 0x50]
+ stx %g3, [%o0 + 0x58]
+ stx %g4, [%o0 + 0x60]
+ stx %g5, [%o0 + 0x68]
+ stx %g6, [%o0 + 0x70]
+ stx %g7, [%o0 + 0x78]
+ wrpr %o1, PSTATE_MG, %pstate
+ stx %g0, [%o0 + 0x80]
+ stx %g1, [%o0 + 0x88]
+ stx %g2, [%o0 + 0x90]
+ stx %g3, [%o0 + 0x98]
+ stx %g4, [%o0 + 0xa0]
+ stx %g5, [%o0 + 0xa8]
+ stx %g6, [%o0 + 0xb0]
+ stx %g7, [%o0 + 0xb8]
+ wrpr %o5, 0x0, %pstate
+ retl
+ nop
+
+ .globl restore_alternate_globals
+restore_alternate_globals: /* %o0 = save_area */
+ rdpr %pstate, %o5
+ andn %o5, PSTATE_IE, %o1
+ wrpr %o1, PSTATE_AG, %pstate
+ ldx [%o0 + 0x00], %g0
+ ldx [%o0 + 0x08], %g1
+ ldx [%o0 + 0x10], %g2
+ ldx [%o0 + 0x18], %g3
+ ldx [%o0 + 0x20], %g4
+ ldx [%o0 + 0x28], %g5
+ ldx [%o0 + 0x30], %g6
+ ldx [%o0 + 0x38], %g7
+ wrpr %o1, PSTATE_IG, %pstate
+ ldx [%o0 + 0x40], %g0
+ ldx [%o0 + 0x48], %g1
+ ldx [%o0 + 0x50], %g2
+ ldx [%o0 + 0x58], %g3
+ ldx [%o0 + 0x60], %g4
+ ldx [%o0 + 0x68], %g5
+ ldx [%o0 + 0x70], %g6
+ ldx [%o0 + 0x78], %g7
+ wrpr %o1, PSTATE_MG, %pstate
+ ldx [%o0 + 0x80], %g0
+ ldx [%o0 + 0x88], %g1
+ ldx [%o0 + 0x90], %g2
+ ldx [%o0 + 0x98], %g3
+ ldx [%o0 + 0xa0], %g4
+ ldx [%o0 + 0xa8], %g5
+ ldx [%o0 + 0xb0], %g6
+ ldx [%o0 + 0xb8], %g7
+ wrpr %o5, 0x0, %pstate
+ retl
+ nop
+
+ .globl getcc, setcc
+getcc:
+ ldx [%o0 + PT_V9_TSTATE], %o1
+ srlx %o1, 32, %o1
+ and %o1, 0xf, %o1
+ retl
+ stx %o1, [%o0 + PT_V9_G1]
+setcc:
+ ldx [%o0 + PT_V9_TSTATE], %o1
+ ldx [%o0 + PT_V9_G1], %o2
+ or %g0, %ulo(TSTATE_ICC), %o3
+ sllx %o3, 32, %o3
+ andn %o1, %o3, %o1
+ sllx %o2, 32, %o2
+ and %o2, %o3, %o2
+ or %o1, %o2, %o1
+ retl
+ stx %o1, [%o0 + PT_V9_TSTATE]
+
+ .globl utrap, utrap_ill
+utrap: brz,pn %g1, etrap
+ nop
+ save %sp, -128, %sp
+ rdpr %tstate, %l6
+ rdpr %cwp, %l7
+ andn %l6, TSTATE_CWP, %l6
+ wrpr %l6, %l7, %tstate
+ rdpr %tpc, %l6
+ rdpr %tnpc, %l7
+ wrpr %g1, 0, %tnpc
+ done
+utrap_ill:
+ call bad_trap
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+#ifdef CONFIG_BLK_DEV_FD
+ .globl floppy_hardint
+floppy_hardint:
+ wr %g0, (1 << 11), %clear_softint
+ sethi %hi(doing_pdma), %g1
+ ld [%g1 + %lo(doing_pdma)], %g2
+ brz,pn %g2, floppy_dosoftint
+ sethi %hi(fdc_status), %g3
+ ldx [%g3 + %lo(fdc_status)], %g3
+ sethi %hi(pdma_vaddr), %g5
+ ldx [%g5 + %lo(pdma_vaddr)], %g4
+ sethi %hi(pdma_size), %g5
+ ldx [%g5 + %lo(pdma_size)], %g5
+
+next_byte:
+ lduba [%g3] ASI_PHYS_BYPASS_EC_E, %g7
+ andcc %g7, 0x80, %g0
+ be,pn %icc, floppy_fifo_emptied
+ andcc %g7, 0x20, %g0
+ be,pn %icc, floppy_overrun
+ andcc %g7, 0x40, %g0
+ be,pn %icc, floppy_write
+ sub %g5, 1, %g5
+
+ inc %g3
+ lduba [%g3] ASI_PHYS_BYPASS_EC_E, %g7
+ dec %g3
+ orcc %g0, %g5, %g0
+ stb %g7, [%g4]
+ bne,pn %xcc, next_byte
+ add %g4, 1, %g4
+
+ b,pt %xcc, floppy_tdone
+ nop
+
+floppy_write:
+ ldub [%g4], %g7
+ orcc %g0, %g5, %g0
+ inc %g3
+ stba %g7, [%g3] ASI_PHYS_BYPASS_EC_E
+ dec %g3
+ bne,pn %xcc, next_byte
+ add %g4, 1, %g4
+
+floppy_tdone:
+ sethi %hi(pdma_vaddr), %g1
+ stx %g4, [%g1 + %lo(pdma_vaddr)]
+ sethi %hi(pdma_size), %g1
+ stx %g5, [%g1 + %lo(pdma_size)]
+ sethi %hi(auxio_register), %g1
+ ldx [%g1 + %lo(auxio_register)], %g7
+ lduba [%g7] ASI_PHYS_BYPASS_EC_E, %g5
+ or %g5, AUXIO_AUX1_FTCNT, %g5
+/* andn %g5, AUXIO_AUX1_MASK, %g5 */
+ stba %g5, [%g7] ASI_PHYS_BYPASS_EC_E
+ andn %g5, AUXIO_AUX1_FTCNT, %g5
+/* andn %g5, AUXIO_AUX1_MASK, %g5 */
+
+ nop; nop; nop; nop; nop; nop;
+ nop; nop; nop; nop; nop; nop;
+
+ stba %g5, [%g7] ASI_PHYS_BYPASS_EC_E
+ sethi %hi(doing_pdma), %g1
+ b,pt %xcc, floppy_dosoftint
+ st %g0, [%g1 + %lo(doing_pdma)]
+
+floppy_fifo_emptied:
+ sethi %hi(pdma_vaddr), %g1
+ stx %g4, [%g1 + %lo(pdma_vaddr)]
+ sethi %hi(pdma_size), %g1
+ stx %g5, [%g1 + %lo(pdma_size)]
+ sethi %hi(irq_action), %g1
+ or %g1, %lo(irq_action), %g1
+ ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq]
+ ldx [%g3 + 0x08], %g4 ! action->flags>>48==ino
+ sethi %hi(ivector_table), %g3
+ srlx %g4, 48, %g4
+ or %g3, %lo(ivector_table), %g3
+ sllx %g4, 5, %g4
+ ldx [%g3 + %g4], %g4 ! &ivector_table[ino]
+ ldx [%g4 + 0x10], %g4 ! bucket->iclr
+ stwa %g0, [%g4] ASI_PHYS_BYPASS_EC_E ! ICLR_IDLE
+ membar #Sync ! probably not needed...
+ retry
+
+floppy_overrun:
+ sethi %hi(pdma_vaddr), %g1
+ stx %g4, [%g1 + %lo(pdma_vaddr)]
+ sethi %hi(pdma_size), %g1
+ stx %g5, [%g1 + %lo(pdma_size)]
+ sethi %hi(doing_pdma), %g1
+ st %g0, [%g1 + %lo(doing_pdma)]
+
+floppy_dosoftint:
+ rdpr %pil, %g2
+ wrpr %g0, 15, %pil
+ sethi %hi(109f), %g7
+ b,pt %xcc, etrap_irq
+109: or %g7, %lo(109b), %g7
+
+ mov 11, %o0
+ mov 0, %o1
+ call sparc_floppy_irq
+ add %sp, PTREGS_OFF, %o2
+
+ b,pt %xcc, rtrap_irq
+ nop
+
+#endif /* CONFIG_BLK_DEV_FD */
+
+ /* XXX Here is stuff we still need to write... -DaveM XXX */
+ .globl netbsd_syscall
+netbsd_syscall:
+ retl
+ nop
+
+ /* These next few routines must be sure to clear the
+ * SFSR FaultValid bit so that the fast tlb data protection
+ * handler does not flush the wrong context and lock up the
+ * box.
+ */
+ .globl __do_data_access_exception
+ .globl __do_data_access_exception_tl1
+__do_data_access_exception_tl1:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g5
+ ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
+ ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
+ stxa %g0, [%g3] ASI_DMMU ! Clear SFSR.FaultValid bit
+ membar #Sync
+ ba,pt %xcc, winfix_dax
+ rdpr %tpc, %g3
+__do_data_access_exception:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g5
+ ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
+ ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
+ stxa %g0, [%g3] ASI_DMMU ! Clear SFSR.FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call data_access_exception
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl __do_instruction_access_exception
+ .globl __do_instruction_access_exception_tl1
+__do_instruction_access_exception_tl1:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g5
+ ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
+ ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
+ stxa %g0, [%g3] ASI_IMMU ! Clear FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etraptl1
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call instruction_access_exception_tl1
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+__do_instruction_access_exception:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g5
+ ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
+ ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
+ stxa %g0, [%g3] ASI_IMMU ! Clear FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call instruction_access_exception
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ /* This is the trap handler entry point for ECC correctable
+ * errors. They are corrected, but we listen for the trap
+ * so that the event can be logged.
+ *
+ * Disrupting errors are either:
+ * 1) single-bit ECC errors during UDB reads to system
+ * memory
+ * 2) data parity errors during write-back events
+ *
+ * As far as I can make out from the manual, the CEE trap
+ * is only for correctable errors during memory read
+ * accesses by the front-end of the processor.
+ *
+ * The code below is only for trap level 1 CEE events,
+ * as it is the only situation where we can safely record
+ * and log. For trap level >1 we just clear the CE bit
+ * in the AFSR and return.
+ */
+
+ /* Our trap handling infrastructure allows us to preserve
+ * two 64-bit values during etrap for arguments to
+ * subsequent C code. Therefore we encode the information
+ * as follows:
+ *
+ * value 1) Full 64-bits of AFAR
+ * value 2) Low 33-bits of AFSR, then bits 33-->42
+ * are UDBL error status and bits 43-->52
+ * are UDBH error status
+ */
+ .align 64
+ .globl cee_trap
+cee_trap:
+ ldxa [%g0] ASI_AFSR, %g1 ! Read AFSR
+ ldxa [%g0] ASI_AFAR, %g2 ! Read AFAR
+ sllx %g1, 31, %g1 ! Clear reserved bits
+ srlx %g1, 31, %g1 ! in AFSR
+
+ /* NOTE: UltraSparc-I/II have high and low UDB error
+ * registers, corresponding to the two UDB units
+ * present on those chips. UltraSparc-IIi only
+ * has a single UDB, called "SDB" in the manual.
+ * For IIi the upper UDB register always reads
+ * as zero so for our purposes things will just
+ * work with the checks below.
+ */
+ ldxa [%g0] ASI_UDBL_ERROR_R, %g3 ! Read UDB-Low error status
+ andcc %g3, (1 << 8), %g4 ! Check CE bit
+ sllx %g3, (64 - 10), %g3 ! Clear reserved bits
+ srlx %g3, (64 - 10), %g3 ! in UDB-Low error status
+
+ sllx %g3, (33 + 0), %g3 ! Shift up to encoding area
+ or %g1, %g3, %g1 ! Or it in
+ be,pn %xcc, 1f ! Branch if CE bit was clear
+ nop
+ stxa %g4, [%g0] ASI_UDB_ERROR_W ! Clear CE sticky bit in UDBL
+ membar #Sync ! Synchronize ASI stores
+1: mov 0x18, %g5 ! Addr of UDB-High error status
+ ldxa [%g5] ASI_UDBH_ERROR_R, %g3 ! Read it
+
+ andcc %g3, (1 << 8), %g4 ! Check CE bit
+ sllx %g3, (64 - 10), %g3 ! Clear reserved bits
+ srlx %g3, (64 - 10), %g3 ! in UDB-High error status
+ sllx %g3, (33 + 10), %g3 ! Shift up to encoding area
+ or %g1, %g3, %g1 ! Or it in
+ be,pn %xcc, 1f ! Branch if CE bit was clear
+ nop
+ nop
+
+ stxa %g4, [%g5] ASI_UDB_ERROR_W ! Clear CE sticky bit in UDBH
+ membar #Sync ! Synchronize ASI stores
+1: mov 1, %g5 ! AFSR CE bit is
+ sllx %g5, 20, %g5 ! bit 20
+ stxa %g5, [%g0] ASI_AFSR ! Clear CE sticky bit in AFSR
+ membar #Sync ! Synchronize ASI stores
+ sllx %g2, (64 - 41), %g2 ! Clear reserved bits
+ srlx %g2, (64 - 41), %g2 ! in latched AFAR
+
+ andn %g2, 0x0f, %g2 ! Finish resv bit clearing
+ mov %g1, %g4 ! Move AFSR+UDB* into save reg
+ mov %g2, %g5 ! Move AFAR into save reg
+ rdpr %pil, %g2
+ wrpr %g0, 15, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+ mov %l4, %o0
+
+ mov %l5, %o1
+ call cee_log
+ add %sp, PTREGS_OFF, %o2
+ ba,a,pt %xcc, rtrap_irq
+
+ /* Capture I/D/E-cache state into per-cpu error scoreboard.
+ *
+ * %g1: (TL>=0) ? 1 : 0
+ * %g2: scratch
+ * %g3: scratch
+ * %g4: AFSR
+ * %g5: AFAR
+ * %g6: current thread ptr
+ * %g7: scratch
+ */
+#define CHEETAH_LOG_ERROR \
+ /* Put "TL1" software bit into AFSR. */ \
+ and %g1, 0x1, %g1; \
+ sllx %g1, 63, %g2; \
+ or %g4, %g2, %g4; \
+ /* Get log entry pointer for this cpu at this trap level. */ \
+ BRANCH_IF_JALAPENO(g2,g3,50f) \
+ ldxa [%g0] ASI_SAFARI_CONFIG, %g2; \
+ srlx %g2, 17, %g2; \
+ ba,pt %xcc, 60f; \
+ and %g2, 0x3ff, %g2; \
+50: ldxa [%g0] ASI_JBUS_CONFIG, %g2; \
+ srlx %g2, 17, %g2; \
+ and %g2, 0x1f, %g2; \
+60: sllx %g2, 9, %g2; \
+ sethi %hi(cheetah_error_log), %g3; \
+ ldx [%g3 + %lo(cheetah_error_log)], %g3; \
+ brz,pn %g3, 80f; \
+ nop; \
+ add %g3, %g2, %g3; \
+ sllx %g1, 8, %g1; \
+ add %g3, %g1, %g1; \
+ /* %g1 holds pointer to the top of the logging scoreboard */ \
+ ldx [%g1 + 0x0], %g7; \
+ cmp %g7, -1; \
+ bne,pn %xcc, 80f; \
+ nop; \
+ stx %g4, [%g1 + 0x0]; \
+ stx %g5, [%g1 + 0x8]; \
+ add %g1, 0x10, %g1; \
+ /* %g1 now points to D-cache logging area */ \
+ set 0x3ff8, %g2; /* DC_addr mask */ \
+ and %g5, %g2, %g2; /* DC_addr bits of AFAR */ \
+ srlx %g5, 12, %g3; \
+ or %g3, 1, %g3; /* PHYS tag + valid */ \
+10: ldxa [%g2] ASI_DCACHE_TAG, %g7; \
+ cmp %g3, %g7; /* TAG match? */ \
+ bne,pt %xcc, 13f; \
+ nop; \
+ /* Yep, what we want, capture state. */ \
+ stx %g2, [%g1 + 0x20]; \
+ stx %g7, [%g1 + 0x28]; \
+ /* A membar Sync is required before and after utag access. */ \
+ membar #Sync; \
+ ldxa [%g2] ASI_DCACHE_UTAG, %g7; \
+ membar #Sync; \
+ stx %g7, [%g1 + 0x30]; \
+ ldxa [%g2] ASI_DCACHE_SNOOP_TAG, %g7; \
+ stx %g7, [%g1 + 0x38]; \
+ clr %g3; \
+12: ldxa [%g2 + %g3] ASI_DCACHE_DATA, %g7; \
+ stx %g7, [%g1]; \
+ add %g3, (1 << 5), %g3; \
+ cmp %g3, (4 << 5); \
+ bl,pt %xcc, 12b; \
+ add %g1, 0x8, %g1; \
+ ba,pt %xcc, 20f; \
+ add %g1, 0x20, %g1; \
+13: sethi %hi(1 << 14), %g7; \
+ add %g2, %g7, %g2; \
+ srlx %g2, 14, %g7; \
+ cmp %g7, 4; \
+ bl,pt %xcc, 10b; \
+ nop; \
+ add %g1, 0x40, %g1; \
+20: /* %g1 now points to I-cache logging area */ \
+ set 0x1fe0, %g2; /* IC_addr mask */ \
+ and %g5, %g2, %g2; /* IC_addr bits of AFAR */ \
+ sllx %g2, 1, %g2; /* IC_addr[13:6]==VA[12:5] */ \
+ srlx %g5, (13 - 8), %g3; /* Make PTAG */ \
+ andn %g3, 0xff, %g3; /* Mask off undefined bits */ \
+21: ldxa [%g2] ASI_IC_TAG, %g7; \
+ andn %g7, 0xff, %g7; \
+ cmp %g3, %g7; \
+ bne,pt %xcc, 23f; \
+ nop; \
+ /* Yep, what we want, capture state. */ \
+ stx %g2, [%g1 + 0x40]; \
+ stx %g7, [%g1 + 0x48]; \
+ add %g2, (1 << 3), %g2; \
+ ldxa [%g2] ASI_IC_TAG, %g7; \
+ add %g2, (1 << 3), %g2; \
+ stx %g7, [%g1 + 0x50]; \
+ ldxa [%g2] ASI_IC_TAG, %g7; \
+ add %g2, (1 << 3), %g2; \
+ stx %g7, [%g1 + 0x60]; \
+ ldxa [%g2] ASI_IC_TAG, %g7; \
+ stx %g7, [%g1 + 0x68]; \
+ sub %g2, (3 << 3), %g2; \
+ ldxa [%g2] ASI_IC_STAG, %g7; \
+ stx %g7, [%g1 + 0x58]; \
+ clr %g3; \
+ srlx %g2, 2, %g2; \
+22: ldxa [%g2 + %g3] ASI_IC_INSTR, %g7; \
+ stx %g7, [%g1]; \
+ add %g3, (1 << 3), %g3; \
+ cmp %g3, (8 << 3); \
+ bl,pt %xcc, 22b; \
+ add %g1, 0x8, %g1; \
+ ba,pt %xcc, 30f; \
+ add %g1, 0x30, %g1; \
+23: sethi %hi(1 << 14), %g7; \
+ add %g2, %g7, %g2; \
+ srlx %g2, 14, %g7; \
+ cmp %g7, 4; \
+ bl,pt %xcc, 21b; \
+ nop; \
+ add %g1, 0x70, %g1; \
+30: /* %g1 now points to E-cache logging area */ \
+ andn %g5, (32 - 1), %g2; /* E-cache subblock */ \
+ stx %g2, [%g1 + 0x20]; \
+ ldxa [%g2] ASI_EC_TAG_DATA, %g7; \
+ stx %g7, [%g1 + 0x28]; \
+ ldxa [%g2] ASI_EC_R, %g0; \
+ clr %g3; \
+31: ldxa [%g3] ASI_EC_DATA, %g7; \
+ stx %g7, [%g1 + %g3]; \
+ add %g3, 0x8, %g3; \
+ cmp %g3, 0x20; \
+ bl,pt %xcc, 31b; \
+ nop; \
+80: /* DONE */
+
+ /* These get patched into the trap table at boot time
+ * once we know we have a cheetah processor.
+ */
+ .globl cheetah_fecc_trap_vector, cheetah_fecc_trap_vector_tl1
+cheetah_fecc_trap_vector:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_DC | DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_fast_ecc), %g2
+ jmpl %g2 + %lo(cheetah_fast_ecc), %g0
+ mov 0, %g1
+cheetah_fecc_trap_vector_tl1:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_DC | DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_fast_ecc), %g2
+ jmpl %g2 + %lo(cheetah_fast_ecc), %g0
+ mov 1, %g1
+ .globl cheetah_cee_trap_vector, cheetah_cee_trap_vector_tl1
+cheetah_cee_trap_vector:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_cee), %g2
+ jmpl %g2 + %lo(cheetah_cee), %g0
+ mov 0, %g1
+cheetah_cee_trap_vector_tl1:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_cee), %g2
+ jmpl %g2 + %lo(cheetah_cee), %g0
+ mov 1, %g1
+ .globl cheetah_deferred_trap_vector, cheetah_deferred_trap_vector_tl1
+cheetah_deferred_trap_vector:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1;
+ andn %g1, DCU_DC | DCU_IC, %g1;
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG;
+ membar #Sync;
+ sethi %hi(cheetah_deferred_trap), %g2
+ jmpl %g2 + %lo(cheetah_deferred_trap), %g0
+ mov 0, %g1
+cheetah_deferred_trap_vector_tl1:
+ membar #Sync;
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1;
+ andn %g1, DCU_DC | DCU_IC, %g1;
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG;
+ membar #Sync;
+ sethi %hi(cheetah_deferred_trap), %g2
+ jmpl %g2 + %lo(cheetah_deferred_trap), %g0
+ mov 1, %g1
+
+ /* Cheetah+ specific traps. These are for the new I/D cache parity
+ * error traps. The first argument to cheetah_plus_parity_handler
+ * is encoded as follows:
+ *
+ * Bit0: 0=dcache,1=icache
+ * Bit1: 0=recoverable,1=unrecoverable
+ */
+ .globl cheetah_plus_dcpe_trap_vector, cheetah_plus_dcpe_trap_vector_tl1
+cheetah_plus_dcpe_trap_vector:
+ membar #Sync
+ sethi %hi(do_cheetah_plus_data_parity), %g7
+ jmpl %g7 + %lo(do_cheetah_plus_data_parity), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+
+do_cheetah_plus_data_parity:
+ ba,pt %xcc, etrap
+ rd %pc, %g7
+ mov 0x0, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,pt %xcc, rtrap
+ clr %l6
+
+cheetah_plus_dcpe_trap_vector_tl1:
+ membar #Sync
+ wrpr PSTATE_IG | PSTATE_PEF | PSTATE_PRIV, %pstate
+ sethi %hi(do_dcpe_tl1), %g3
+ jmpl %g3 + %lo(do_dcpe_tl1), %g0
+ nop
+ nop
+ nop
+ nop
+
+ .globl cheetah_plus_icpe_trap_vector, cheetah_plus_icpe_trap_vector_tl1
+cheetah_plus_icpe_trap_vector:
+ membar #Sync
+ sethi %hi(do_cheetah_plus_insn_parity), %g7
+ jmpl %g7 + %lo(do_cheetah_plus_insn_parity), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+
+do_cheetah_plus_insn_parity:
+ ba,pt %xcc, etrap
+ rd %pc, %g7
+ mov 0x1, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,pt %xcc, rtrap
+ clr %l6
+
+cheetah_plus_icpe_trap_vector_tl1:
+ membar #Sync
+ wrpr PSTATE_IG | PSTATE_PEF | PSTATE_PRIV, %pstate
+ sethi %hi(do_icpe_tl1), %g3
+ jmpl %g3 + %lo(do_icpe_tl1), %g0
+ nop
+ nop
+ nop
+ nop
+
+ /* If we take one of these traps when tl >= 1, then we
+ * jump to interrupt globals. If some trap level above us
+ * was also using interrupt globals, we cannot recover.
+ * We may use all interrupt global registers except %g6.
+ */
+ .globl do_dcpe_tl1, do_icpe_tl1
+do_dcpe_tl1:
+ rdpr %tl, %g1 ! Save original trap level
+ mov 1, %g2 ! Setup TSTATE checking loop
+ sethi %hi(TSTATE_IG), %g3 ! TSTATE mask bit
+1: wrpr %g2, %tl ! Set trap level to check
+ rdpr %tstate, %g4 ! Read TSTATE for this level
+ andcc %g4, %g3, %g0 ! Interrupt globals in use?
+ bne,a,pn %xcc, do_dcpe_tl1_fatal ! Yep, irrecoverable
+ wrpr %g1, %tl ! Restore original trap level
+ add %g2, 1, %g2 ! Next trap level
+ cmp %g2, %g1 ! Hit them all yet?
+ ble,pt %icc, 1b ! Not yet
+ nop
+ wrpr %g1, %tl ! Restore original trap level
+do_dcpe_tl1_nonfatal: /* Ok we may use interrupt globals safely. */
+ /* Reset D-cache parity */
+ sethi %hi(1 << 16), %g1 ! D-cache size
+ mov (1 << 5), %g2 ! D-cache line size
+ sub %g1, %g2, %g1 ! Move down 1 cacheline
+1: srl %g1, 14, %g3 ! Compute UTAG
+ membar #Sync
+ stxa %g3, [%g1] ASI_DCACHE_UTAG
+ membar #Sync
+ sub %g2, 8, %g3 ! 64-bit data word within line
+2: membar #Sync
+ stxa %g0, [%g1 + %g3] ASI_DCACHE_DATA
+ membar #Sync
+ subcc %g3, 8, %g3 ! Next 64-bit data word
+ bge,pt %icc, 2b
+ nop
+ subcc %g1, %g2, %g1 ! Next cacheline
+ bge,pt %icc, 1b
+ nop
+ ba,pt %xcc, dcpe_icpe_tl1_common
+ nop
+
+do_dcpe_tl1_fatal:
+ sethi %hi(1f), %g7
+ ba,pt %xcc, etraptl1
+1: or %g7, %lo(1b), %g7
+ mov 0x2, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,pt %xcc, rtrap
+ clr %l6
+
+do_icpe_tl1:
+ rdpr %tl, %g1 ! Save original trap level
+ mov 1, %g2 ! Setup TSTATE checking loop
+ sethi %hi(TSTATE_IG), %g3 ! TSTATE mask bit
+1: wrpr %g2, %tl ! Set trap level to check
+ rdpr %tstate, %g4 ! Read TSTATE for this level
+ andcc %g4, %g3, %g0 ! Interrupt globals in use?
+ bne,a,pn %xcc, do_icpe_tl1_fatal ! Yep, irrecoverable
+ wrpr %g1, %tl ! Restore original trap level
+ add %g2, 1, %g2 ! Next trap level
+ cmp %g2, %g1 ! Hit them all yet?
+ ble,pt %icc, 1b ! Not yet
+ nop
+ wrpr %g1, %tl ! Restore original trap level
+do_icpe_tl1_nonfatal: /* Ok we may use interrupt globals safely. */
+ /* Flush I-cache */
+ sethi %hi(1 << 15), %g1 ! I-cache size
+ mov (1 << 5), %g2 ! I-cache line size
+ sub %g1, %g2, %g1
+1: or %g1, (2 << 3), %g3
+ stxa %g0, [%g3] ASI_IC_TAG
+ membar #Sync
+ subcc %g1, %g2, %g1
+ bge,pt %icc, 1b
+ nop
+ ba,pt %xcc, dcpe_icpe_tl1_common
+ nop
+
+do_icpe_tl1_fatal:
+ sethi %hi(1f), %g7
+ ba,pt %xcc, etraptl1
+1: or %g7, %lo(1b), %g7
+ mov 0x3, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,pt %xcc, rtrap
+ clr %l6
+
+dcpe_icpe_tl1_common:
+ /* Flush D-cache, re-enable D/I caches in DCU and finally
+ * retry the trapping instruction.
+ */
+ sethi %hi(1 << 16), %g1 ! D-cache size
+ mov (1 << 5), %g2 ! D-cache line size
+ sub %g1, %g2, %g1
+1: stxa %g0, [%g1] ASI_DCACHE_TAG
+ membar #Sync
+ subcc %g1, %g2, %g1
+ bge,pt %icc, 1b
+ nop
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ or %g1, (DCU_DC | DCU_IC), %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ retry
+
+ /* Cheetah FECC trap handling, we get here from tl{0,1}_fecc
+ * in the trap table. That code has done a memory barrier
+ * and has disabled both the I-cache and D-cache in the DCU
+ * control register. The I-cache is disabled so that we may
+ * capture the corrupted cache line, and the D-cache is disabled
+ * because corrupt data may have been placed there and we don't
+ * want to reference it.
+ *
+ * %g1 is one if this trap occurred at %tl >= 1.
+ *
+ * Next, we turn off error reporting so that we don't recurse.
+ */
+ .globl cheetah_fast_ecc
+cheetah_fast_ecc:
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
+ andn %g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
+ stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Fetch and clear AFSR/AFAR */
+ ldxa [%g0] ASI_AFSR, %g4
+ ldxa [%g0] ASI_AFAR, %g5
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ CHEETAH_LOG_ERROR
+
+ rdpr %pil, %g2
+ wrpr %g0, 15, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call cheetah_fecc_handler
+ add %sp, PTREGS_OFF, %o0
+ ba,a,pt %xcc, rtrap_irq
+
+ /* Our caller has disabled I-cache and performed membar Sync. */
+ .globl cheetah_cee
+cheetah_cee:
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
+ andn %g2, ESTATE_ERROR_CEEN, %g2
+ stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Fetch and clear AFSR/AFAR */
+ ldxa [%g0] ASI_AFSR, %g4
+ ldxa [%g0] ASI_AFAR, %g5
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ CHEETAH_LOG_ERROR
+
+ rdpr %pil, %g2
+ wrpr %g0, 15, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call cheetah_cee_handler
+ add %sp, PTREGS_OFF, %o0
+ ba,a,pt %xcc, rtrap_irq
+
+ /* Our caller has disabled I-cache+D-cache and performed membar Sync. */
+ .globl cheetah_deferred_trap
+cheetah_deferred_trap:
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
+ andn %g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
+ stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Fetch and clear AFSR/AFAR */
+ ldxa [%g0] ASI_AFSR, %g4
+ ldxa [%g0] ASI_AFAR, %g5
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ CHEETAH_LOG_ERROR
+
+ rdpr %pil, %g2
+ wrpr %g0, 15, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call cheetah_deferred_handler
+ add %sp, PTREGS_OFF, %o0
+ ba,a,pt %xcc, rtrap_irq
+
+ .globl __do_privact
+__do_privact:
+ mov TLB_SFSR, %g3
+ stxa %g0, [%g3] ASI_DMMU ! Clear FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ call do_privact
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl do_mna
+do_mna:
+ rdpr %tl, %g3
+ cmp %g3, 1
+
+ /* Setup %g4/%g5 now as they are used in the
+ * winfixup code.
+ */
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g4
+ ldxa [%g4] ASI_DMMU, %g4
+ ldxa [%g3] ASI_DMMU, %g5
+ stxa %g0, [%g3] ASI_DMMU ! Clear FaultValid bit
+ membar #Sync
+ bgu,pn %icc, winfix_mna
+ rdpr %tpc, %g3
+
+1: sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call mem_address_unaligned
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl do_lddfmna
+do_lddfmna:
+ sethi %hi(109f), %g7
+ mov TLB_SFSR, %g4
+ ldxa [%g4] ASI_DMMU, %g5
+ stxa %g0, [%g4] ASI_DMMU ! Clear FaultValid bit
+ membar #Sync
+ mov DMMU_SFAR, %g4
+ ldxa [%g4] ASI_DMMU, %g4
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call handle_lddfmna
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl do_stdfmna
+do_stdfmna:
+ sethi %hi(109f), %g7
+ mov TLB_SFSR, %g4
+ ldxa [%g4] ASI_DMMU, %g5
+ stxa %g0, [%g4] ASI_DMMU ! Clear FaultValid bit
+ membar #Sync
+ mov DMMU_SFAR, %g4
+ ldxa [%g4] ASI_DMMU, %g4
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call handle_stdfmna
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .globl breakpoint_trap
+breakpoint_trap:
+ call sparc_breakpoint
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+
+#if defined(CONFIG_SUNOS_EMUL) || defined(CONFIG_SOLARIS_EMUL) || \
+ defined(CONFIG_SOLARIS_EMUL_MODULE)
+ /* SunOS uses syscall zero as the 'indirect syscall' it looks
+ * like indir_syscall(scall_num, arg0, arg1, arg2...); etc.
+ * This is complete brain damage.
+ */
+ .globl sunos_indir
+sunos_indir:
+ srl %o0, 0, %o0
+ mov %o7, %l4
+ cmp %o0, NR_SYSCALLS
+ blu,a,pt %icc, 1f
+ sll %o0, 0x2, %o0
+ sethi %hi(sunos_nosys), %l6
+ b,pt %xcc, 2f
+ or %l6, %lo(sunos_nosys), %l6
+1: sethi %hi(sunos_sys_table), %l7
+ or %l7, %lo(sunos_sys_table), %l7
+ lduw [%l7 + %o0], %l6
+2: mov %o1, %o0
+ mov %o2, %o1
+ mov %o3, %o2
+ mov %o4, %o3
+ mov %o5, %o4
+ call %l6
+ mov %l4, %o7
+
+ .globl sunos_getpid
+sunos_getpid:
+ call sys_getppid
+ nop
+ call sys_getpid
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I1]
+ b,pt %xcc, ret_sys_call
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I0]
+
+ /* SunOS getuid() returns uid in %o0 and euid in %o1 */
+ .globl sunos_getuid
+sunos_getuid:
+ call sys32_geteuid16
+ nop
+ call sys32_getuid16
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I1]
+ b,pt %xcc, ret_sys_call
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I0]
+
+ /* SunOS getgid() returns gid in %o0 and egid in %o1 */
+ .globl sunos_getgid
+sunos_getgid:
+ call sys32_getegid16
+ nop
+ call sys32_getgid16
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I1]
+ b,pt %xcc, ret_sys_call
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I0]
+#endif
+
+ /* SunOS's execv() call only specifies the argv argument, the
+ * environment settings are the same as the calling processes.
+ */
+ .globl sunos_execv
+sys_execve:
+ sethi %hi(sparc_execve), %g1
+ ba,pt %xcc, execve_merge
+ or %g1, %lo(sparc_execve), %g1
+#ifdef CONFIG_COMPAT
+ .globl sys_execve
+sunos_execv:
+ stx %g0, [%sp + PTREGS_OFF + PT_V9_I2]
+ .globl sys32_execve
+sys32_execve:
+ sethi %hi(sparc32_execve), %g1
+ or %g1, %lo(sparc32_execve), %g1
+#endif
+execve_merge:
+ flushw
+ jmpl %g1, %g0
+ add %sp, PTREGS_OFF, %o0
+
+ .globl sys_pipe, sys_sigpause, sys_nis_syscall
+ .globl sys_sigsuspend, sys_rt_sigsuspend
+ .globl sys_rt_sigreturn
+ .globl sys_ptrace
+ .globl sys_sigaltstack
+ .align 32
+sys_pipe: ba,pt %xcc, sparc_pipe
+ add %sp, PTREGS_OFF, %o0
+sys_nis_syscall:ba,pt %xcc, c_sys_nis_syscall
+ add %sp, PTREGS_OFF, %o0
+sys_memory_ordering:
+ ba,pt %xcc, sparc_memory_ordering
+ add %sp, PTREGS_OFF, %o1
+sys_sigaltstack:ba,pt %xcc, do_sigaltstack
+ add %i6, STACK_BIAS, %o2
+#ifdef CONFIG_COMPAT
+ .globl sys32_sigstack
+sys32_sigstack: ba,pt %xcc, do_sys32_sigstack
+ mov %i6, %o2
+ .globl sys32_sigaltstack
+sys32_sigaltstack:
+ ba,pt %xcc, do_sys32_sigaltstack
+ mov %i6, %o2
+#endif
+ .align 32
+sys_sigsuspend: add %sp, PTREGS_OFF, %o0
+ call do_sigsuspend
+ add %o7, 1f-.-4, %o7
+ nop
+sys_rt_sigsuspend: /* NOTE: %o0,%o1 have a correct value already */
+ add %sp, PTREGS_OFF, %o2
+ call do_rt_sigsuspend
+ add %o7, 1f-.-4, %o7
+ nop
+#ifdef CONFIG_COMPAT
+ .globl sys32_rt_sigsuspend
+sys32_rt_sigsuspend: /* NOTE: %o0,%o1 have a correct value already */
+ srl %o0, 0, %o0
+ add %sp, PTREGS_OFF, %o2
+ call do_rt_sigsuspend32
+ add %o7, 1f-.-4, %o7
+#endif
+ /* NOTE: %o0 has a correct value already */
+sys_sigpause: add %sp, PTREGS_OFF, %o1
+ call do_sigpause
+ add %o7, 1f-.-4, %o7
+ nop
+#ifdef CONFIG_COMPAT
+ .globl sys32_sigreturn
+sys32_sigreturn:
+ add %sp, PTREGS_OFF, %o0
+ call do_sigreturn32
+ add %o7, 1f-.-4, %o7
+ nop
+#endif
+sys_rt_sigreturn:
+ add %sp, PTREGS_OFF, %o0
+ call do_rt_sigreturn
+ add %o7, 1f-.-4, %o7
+ nop
+#ifdef CONFIG_COMPAT
+ .globl sys32_rt_sigreturn
+sys32_rt_sigreturn:
+ add %sp, PTREGS_OFF, %o0
+ call do_rt_sigreturn32
+ add %o7, 1f-.-4, %o7
+ nop
+#endif
+sys_ptrace: add %sp, PTREGS_OFF, %o0
+ call do_ptrace
+ add %o7, 1f-.-4, %o7
+ nop
+ .align 32
+1: ldx [%curptr + TI_FLAGS], %l5
+ andcc %l5, _TIF_SYSCALL_TRACE, %g0
+ be,pt %icc, rtrap
+ clr %l6
+ call syscall_trace
+ nop
+
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ /* This is how fork() was meant to be done, 8 instruction entry.
+ *
+ * I questioned the following code briefly, let me clear things
+ * up so you must not reason on it like I did.
+ *
+ * Know the fork_kpsr etc. we use in the sparc32 port? We don't
+ * need it here because the only piece of window state we copy to
+ * the child is the CWP register. Even if the parent sleeps,
+ * we are safe because we stuck it into pt_regs of the parent
+ * so it will not change.
+ *
+ * XXX This raises the question, whether we can do the same on
+ * XXX sparc32 to get rid of fork_kpsr _and_ fork_kwim. The
+ * XXX answer is yes. We stick fork_kpsr in UREG_G0 and
+ * XXX fork_kwim in UREG_G1 (global registers are considered
+ * XXX volatile across a system call in the sparc ABI I think
+ * XXX if it isn't we can use regs->y instead, anyone who depends
+ * XXX upon the Y register being preserved across a fork deserves
+ * XXX to lose).
+ *
+ * In fact we should take advantage of that fact for other things
+ * during system calls...
+ */
+ .globl sys_fork, sys_vfork, sys_clone, sparc_exit
+ .globl ret_from_syscall
+ .align 32
+sys_vfork: /* Under Linux, vfork and fork are just special cases of clone. */
+ sethi %hi(0x4000 | 0x0100 | SIGCHLD), %o0
+ or %o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0
+ ba,pt %xcc, sys_clone
+sys_fork: clr %o1
+ mov SIGCHLD, %o0
+sys_clone: flushw
+ movrz %o1, %fp, %o1
+ mov 0, %o3
+ ba,pt %xcc, sparc_do_fork
+ add %sp, PTREGS_OFF, %o2
+ret_from_syscall:
+ /* Clear SPARC_FLAG_NEWCHILD, switch_to leaves thread.flags in
+ * %o7 for us. Check performance counter stuff too.
+ */
+ andn %o7, _TIF_NEWCHILD, %l0
+ stx %l0, [%g6 + TI_FLAGS]
+ call schedule_tail
+ mov %g7, %o0
+ andcc %l0, _TIF_PERFCTR, %g0
+ be,pt %icc, 1f
+ nop
+ ldx [%g6 + TI_PCR], %o7
+ wr %g0, %o7, %pcr
+
+ /* Blackbird errata workaround. See commentary in
+ * smp.c:smp_percpu_timer_interrupt() for more
+ * information.
+ */
+ ba,pt %xcc, 99f
+ nop
+ .align 64
+99: wr %g0, %g0, %pic
+ rd %pic, %g0
+
+1: b,pt %xcc, ret_sys_call
+ ldx [%sp + PTREGS_OFF + PT_V9_I0], %o0
+sparc_exit: wrpr %g0, (PSTATE_RMO | PSTATE_PEF | PSTATE_PRIV), %pstate
+ rdpr %otherwin, %g1
+ rdpr %cansave, %g3
+ add %g3, %g1, %g3
+ wrpr %g3, 0x0, %cansave
+ wrpr %g0, 0x0, %otherwin
+ wrpr %g0, (PSTATE_RMO | PSTATE_PEF | PSTATE_PRIV | PSTATE_IE), %pstate
+ ba,pt %xcc, sys_exit
+ stb %g0, [%g6 + TI_WSAVED]
+
+linux_sparc_ni_syscall:
+ sethi %hi(sys_ni_syscall), %l7
+ b,pt %xcc, 4f
+ or %l7, %lo(sys_ni_syscall), %l7
+
+linux_syscall_trace32:
+ call syscall_trace
+ nop
+ srl %i0, 0, %o0
+ mov %i4, %o4
+ srl %i1, 0, %o1
+ srl %i2, 0, %o2
+ b,pt %xcc, 2f
+ srl %i3, 0, %o3
+
+linux_syscall_trace:
+ call syscall_trace
+ nop
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ mov %i3, %o3
+ b,pt %xcc, 2f
+ mov %i4, %o4
+
+
+ /* Linux 32-bit and SunOS system calls enter here... */
+ .align 32
+ .globl linux_sparc_syscall32
+linux_sparc_syscall32:
+ /* Direct access to user regs, much faster. */
+ cmp %g1, NR_SYSCALLS ! IEU1 Group
+ bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI
+ srl %i0, 0, %o0 ! IEU0
+ sll %g1, 2, %l4 ! IEU0 Group
+#ifdef SYSCALL_TRACING
+ call syscall_trace_entry
+ add %sp, PTREGS_OFF, %o0
+ srl %i0, 0, %o0
+#endif
+ srl %i4, 0, %o4 ! IEU1
+ lduw [%l7 + %l4], %l7 ! Load
+ srl %i1, 0, %o1 ! IEU0 Group
+ ldx [%curptr + TI_FLAGS], %l0 ! Load
+
+ srl %i5, 0, %o5 ! IEU1
+ srl %i2, 0, %o2 ! IEU0 Group
+ andcc %l0, _TIF_SYSCALL_TRACE, %g0 ! IEU0 Group
+ bne,pn %icc, linux_syscall_trace32 ! CTI
+ mov %i0, %l5 ! IEU1
+ call %l7 ! CTI Group brk forced
+ srl %i3, 0, %o3 ! IEU0
+ ba,a,pt %xcc, 3f
+
+ /* Linux native and SunOS system calls enter here... */
+ .align 32
+ .globl linux_sparc_syscall, ret_sys_call
+linux_sparc_syscall:
+ /* Direct access to user regs, much faster. */
+ cmp %g1, NR_SYSCALLS ! IEU1 Group
+ bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI
+ mov %i0, %o0 ! IEU0
+ sll %g1, 2, %l4 ! IEU0 Group
+#ifdef SYSCALL_TRACING
+ call syscall_trace_entry
+ add %sp, PTREGS_OFF, %o0
+ mov %i0, %o0
+#endif
+ mov %i1, %o1 ! IEU1
+ lduw [%l7 + %l4], %l7 ! Load
+4: mov %i2, %o2 ! IEU0 Group
+ ldx [%curptr + TI_FLAGS], %l0 ! Load
+
+ mov %i3, %o3 ! IEU1
+ mov %i4, %o4 ! IEU0 Group
+ andcc %l0, _TIF_SYSCALL_TRACE, %g0 ! IEU1 Group+1 bubble
+ bne,pn %icc, linux_syscall_trace ! CTI Group
+ mov %i0, %l5 ! IEU0
+2: call %l7 ! CTI Group brk forced
+ mov %i5, %o5 ! IEU0
+ nop
+
+3: stx %o0, [%sp + PTREGS_OFF + PT_V9_I0]
+ret_sys_call:
+#ifdef SYSCALL_TRACING
+ mov %o0, %o1
+ call syscall_trace_exit
+ add %sp, PTREGS_OFF, %o0
+ mov %o1, %o0
+#endif
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %g3
+ ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %l1 ! pc = npc
+ sra %o0, 0, %o0
+ mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2
+ sllx %g2, 32, %g2
+
+ /* Check if force_successful_syscall_return()
+ * was invoked.
+ */
+ ldx [%curptr + TI_FLAGS], %l0
+ andcc %l0, _TIF_SYSCALL_SUCCESS, %g0
+ be,pt %icc, 1f
+ andn %l0, _TIF_SYSCALL_SUCCESS, %l0
+ ba,pt %xcc, 80f
+ stx %l0, [%curptr + TI_FLAGS]
+
+1:
+ cmp %o0, -ERESTART_RESTARTBLOCK
+ bgeu,pn %xcc, 1f
+ andcc %l0, _TIF_SYSCALL_TRACE, %l6
+80:
+ /* System call success, clear Carry condition code. */
+ andn %g3, %g2, %g3
+ stx %g3, [%sp + PTREGS_OFF + PT_V9_TSTATE]
+ bne,pn %icc, linux_syscall_trace2
+ add %l1, 0x4, %l2 ! npc = npc+4
+ stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC]
+ ba,pt %xcc, rtrap_clr_l6
+ stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]
+
+1:
+ /* System call failure, set Carry condition code.
+ * Also, get abs(errno) to return to the process.
+ */
+ andcc %l0, _TIF_SYSCALL_TRACE, %l6
+ sub %g0, %o0, %o0
+ or %g3, %g2, %g3
+ stx %o0, [%sp + PTREGS_OFF + PT_V9_I0]
+ mov 1, %l6
+ stx %g3, [%sp + PTREGS_OFF + PT_V9_TSTATE]
+ bne,pn %icc, linux_syscall_trace2
+ add %l1, 0x4, %l2 ! npc = npc+4
+ stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC]
+
+ b,pt %xcc, rtrap
+ stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]
+linux_syscall_trace2:
+ call syscall_trace
+ nop
+ stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC]
+ ba,pt %xcc, rtrap
+ stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]
+
+ .align 32
+ .globl __flushw_user
+__flushw_user:
+ rdpr %otherwin, %g1
+ brz,pn %g1, 2f
+ clr %g2
+1: save %sp, -128, %sp
+ rdpr %otherwin, %g1
+ brnz,pt %g1, 1b
+ add %g2, 1, %g2
+1: sub %g2, 1, %g2
+ brnz,pt %g2, 1b
+ restore %g0, %g0, %g0
+2: retl
+ nop
diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S
new file mode 100644
index 000000000000..50d2af1d98ae
--- /dev/null
+++ b/arch/sparc64/kernel/etrap.S
@@ -0,0 +1,301 @@
+/* $Id: etrap.S,v 1.46 2002/02/09 19:49:30 davem Exp $
+ * etrap.S: Preparing for entry into the kernel on Sparc V9.
+ *
+ * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <linux/config.h>
+
+#include <asm/asi.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spitfire.h>
+#include <asm/head.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+
+#define TASK_REGOFF (THREAD_SIZE-TRACEREG_SZ-STACKFRAME_SZ)
+#define ETRAP_PSTATE1 (PSTATE_RMO | PSTATE_PRIV)
+#define ETRAP_PSTATE2 \
+ (PSTATE_RMO | PSTATE_PEF | PSTATE_PRIV | PSTATE_IE)
+
+/*
+ * On entry, %g7 is return address - 0x4.
+ * %g4 and %g5 will be preserved %l4 and %l5 respectively.
+ */
+
+ .text
+ .align 64
+ .globl etrap, etrap_irq, etraptl1
+etrap: rdpr %pil, %g2
+etrap_irq:
+ rdpr %tstate, %g1
+ sllx %g2, 20, %g3
+ andcc %g1, TSTATE_PRIV, %g0
+ or %g1, %g3, %g1
+ bne,pn %xcc, 1f
+ sub %sp, STACKFRAME_SZ+TRACEREG_SZ-STACK_BIAS, %g2
+ wrpr %g0, 7, %cleanwin
+
+ sethi %hi(TASK_REGOFF), %g2
+ sethi %hi(TSTATE_PEF), %g3
+ or %g2, %lo(TASK_REGOFF), %g2
+ and %g1, %g3, %g3
+ brnz,pn %g3, 1f
+ add %g6, %g2, %g2
+ wr %g0, 0, %fprs
+1: rdpr %tpc, %g3
+
+ stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TSTATE]
+ rdpr %tnpc, %g1
+ stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC]
+ rd %y, %g3
+ stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC]
+ st %g3, [%g2 + STACKFRAME_SZ + PT_V9_Y]
+ save %g2, -STACK_BIAS, %sp ! Ordering here is critical
+ mov %g6, %l6
+
+ bne,pn %xcc, 3f
+ mov PRIMARY_CONTEXT, %l4
+ rdpr %canrestore, %g3
+ rdpr %wstate, %g2
+ wrpr %g0, 0, %canrestore
+ sll %g2, 3, %g2
+ mov 1, %l5
+ stb %l5, [%l6 + TI_FPDEPTH]
+
+ wrpr %g3, 0, %otherwin
+ wrpr %g2, 0, %wstate
+cplus_etrap_insn_1:
+ sethi %hi(0), %g3
+ sllx %g3, 32, %g3
+cplus_etrap_insn_2:
+ sethi %hi(0), %g2
+ or %g3, %g2, %g3
+ stxa %g3, [%l4] ASI_DMMU
+ flush %l6
+ wr %g0, ASI_AIUS, %asi
+2: wrpr %g0, 0x0, %tl
+ mov %g4, %l4
+ mov %g5, %l5
+
+ mov %g7, %l2
+ wrpr %g0, ETRAP_PSTATE1, %pstate
+ stx %g1, [%sp + PTREGS_OFF + PT_V9_G1]
+ stx %g2, [%sp + PTREGS_OFF + PT_V9_G2]
+ stx %g3, [%sp + PTREGS_OFF + PT_V9_G3]
+ stx %g4, [%sp + PTREGS_OFF + PT_V9_G4]
+ stx %g5, [%sp + PTREGS_OFF + PT_V9_G5]
+ stx %g6, [%sp + PTREGS_OFF + PT_V9_G6]
+
+ stx %g7, [%sp + PTREGS_OFF + PT_V9_G7]
+ stx %i0, [%sp + PTREGS_OFF + PT_V9_I0]
+ stx %i1, [%sp + PTREGS_OFF + PT_V9_I1]
+ stx %i2, [%sp + PTREGS_OFF + PT_V9_I2]
+ stx %i3, [%sp + PTREGS_OFF + PT_V9_I3]
+ stx %i4, [%sp + PTREGS_OFF + PT_V9_I4]
+ stx %i5, [%sp + PTREGS_OFF + PT_V9_I5]
+
+ stx %i6, [%sp + PTREGS_OFF + PT_V9_I6]
+ stx %i7, [%sp + PTREGS_OFF + PT_V9_I7]
+ wrpr %g0, ETRAP_PSTATE2, %pstate
+ mov %l6, %g6
+#ifdef CONFIG_SMP
+ mov TSB_REG, %g3
+ ldxa [%g3] ASI_IMMU, %g5
+#endif
+ jmpl %l2 + 0x4, %g0
+ ldx [%g6 + TI_TASK], %g4
+
+3: ldub [%l6 + TI_FPDEPTH], %l5
+ add %l6, TI_FPSAVED + 1, %l4
+ srl %l5, 1, %l3
+ add %l5, 2, %l5
+ stb %l5, [%l6 + TI_FPDEPTH]
+ ba,pt %xcc, 2b
+ stb %g0, [%l4 + %l3]
+ nop
+
+etraptl1: /* Save tstate/tpc/tnpc of TL 1-->4 and the tl register itself.
+ * We place this right after pt_regs on the trap stack.
+ * The layout is:
+ * 0x00 TL1's TSTATE
+ * 0x08 TL1's TPC
+ * 0x10 TL1's TNPC
+ * 0x18 TL1's TT
+ * ...
+ * 0x58 TL4's TT
+ * 0x60 TL
+ */
+ sub %sp, ((4 * 8) * 4) + 8, %g2
+ rdpr %tl, %g1
+
+ wrpr %g0, 1, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x00]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x08]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x10]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x18]
+
+ wrpr %g0, 2, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x20]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x28]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x30]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x38]
+
+ wrpr %g0, 3, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x40]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x48]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x50]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x58]
+
+ wrpr %g0, 4, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x60]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x68]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x70]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x78]
+
+ wrpr %g1, %tl
+ stx %g1, [%g2 + STACK_BIAS + 0x80]
+
+ rdpr %tstate, %g1
+ sub %g2, STACKFRAME_SZ + TRACEREG_SZ - STACK_BIAS, %g2
+ ba,pt %xcc, 1b
+ andcc %g1, TSTATE_PRIV, %g0
+
+ .align 64
+ .globl scetrap
+scetrap: rdpr %pil, %g2
+ rdpr %tstate, %g1
+ sllx %g2, 20, %g3
+ andcc %g1, TSTATE_PRIV, %g0
+ or %g1, %g3, %g1
+ bne,pn %xcc, 1f
+ sub %sp, (STACKFRAME_SZ+TRACEREG_SZ-STACK_BIAS), %g2
+ wrpr %g0, 7, %cleanwin
+
+ sllx %g1, 51, %g3
+ sethi %hi(TASK_REGOFF), %g2
+ or %g2, %lo(TASK_REGOFF), %g2
+ brlz,pn %g3, 1f
+ add %g6, %g2, %g2
+ wr %g0, 0, %fprs
+1: rdpr %tpc, %g3
+ stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TSTATE]
+
+ rdpr %tnpc, %g1
+ stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC]
+ stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC]
+ save %g2, -STACK_BIAS, %sp ! Ordering here is critical
+ mov %g6, %l6
+ bne,pn %xcc, 2f
+ mov ASI_P, %l7
+ rdpr %canrestore, %g3
+
+ rdpr %wstate, %g2
+ wrpr %g0, 0, %canrestore
+ sll %g2, 3, %g2
+ mov PRIMARY_CONTEXT, %l4
+ wrpr %g3, 0, %otherwin
+ wrpr %g2, 0, %wstate
+cplus_etrap_insn_3:
+ sethi %hi(0), %g3
+ sllx %g3, 32, %g3
+cplus_etrap_insn_4:
+ sethi %hi(0), %g2
+ or %g3, %g2, %g3
+ stxa %g3, [%l4] ASI_DMMU
+ flush %l6
+
+ mov ASI_AIUS, %l7
+2: mov %g4, %l4
+ mov %g5, %l5
+ add %g7, 0x4, %l2
+ wrpr %g0, ETRAP_PSTATE1, %pstate
+ stx %g1, [%sp + PTREGS_OFF + PT_V9_G1]
+ stx %g2, [%sp + PTREGS_OFF + PT_V9_G2]
+ sllx %l7, 24, %l7
+
+ stx %g3, [%sp + PTREGS_OFF + PT_V9_G3]
+ rdpr %cwp, %l0
+ stx %g4, [%sp + PTREGS_OFF + PT_V9_G4]
+ stx %g5, [%sp + PTREGS_OFF + PT_V9_G5]
+ stx %g6, [%sp + PTREGS_OFF + PT_V9_G6]
+ stx %g7, [%sp + PTREGS_OFF + PT_V9_G7]
+ or %l7, %l0, %l7
+ sethi %hi(TSTATE_RMO | TSTATE_PEF), %l0
+
+ or %l7, %l0, %l7
+ wrpr %l2, %tnpc
+ wrpr %l7, (TSTATE_PRIV | TSTATE_IE), %tstate
+ stx %i0, [%sp + PTREGS_OFF + PT_V9_I0]
+ stx %i1, [%sp + PTREGS_OFF + PT_V9_I1]
+ stx %i2, [%sp + PTREGS_OFF + PT_V9_I2]
+ stx %i3, [%sp + PTREGS_OFF + PT_V9_I3]
+ stx %i4, [%sp + PTREGS_OFF + PT_V9_I4]
+
+ stx %i5, [%sp + PTREGS_OFF + PT_V9_I5]
+ stx %i6, [%sp + PTREGS_OFF + PT_V9_I6]
+ mov %l6, %g6
+ stx %i7, [%sp + PTREGS_OFF + PT_V9_I7]
+#ifdef CONFIG_SMP
+ mov TSB_REG, %g3
+ ldxa [%g3] ASI_IMMU, %g5
+#endif
+ ldx [%g6 + TI_TASK], %g4
+ done
+
+#undef TASK_REGOFF
+#undef ETRAP_PSTATE1
+
+cplus_einsn_1:
+ sethi %uhi(CTX_CHEETAH_PLUS_NUC), %g3
+cplus_einsn_2:
+ sethi %hi(CTX_CHEETAH_PLUS_CTX0), %g2
+
+ .globl cheetah_plus_patch_etrap
+cheetah_plus_patch_etrap:
+ /* We configure the dTLB512_0 for 4MB pages and the
+ * dTLB512_1 for 8K pages when in context zero.
+ */
+ sethi %hi(cplus_einsn_1), %o0
+ sethi %hi(cplus_etrap_insn_1), %o2
+ lduw [%o0 + %lo(cplus_einsn_1)], %o1
+ or %o2, %lo(cplus_etrap_insn_1), %o2
+ stw %o1, [%o2]
+ flush %o2
+ sethi %hi(cplus_etrap_insn_3), %o2
+ or %o2, %lo(cplus_etrap_insn_3), %o2
+ stw %o1, [%o2]
+ flush %o2
+
+ sethi %hi(cplus_einsn_2), %o0
+ sethi %hi(cplus_etrap_insn_2), %o2
+ lduw [%o0 + %lo(cplus_einsn_2)], %o1
+ or %o2, %lo(cplus_etrap_insn_2), %o2
+ stw %o1, [%o2]
+ flush %o2
+ sethi %hi(cplus_etrap_insn_4), %o2
+ or %o2, %lo(cplus_etrap_insn_4), %o2
+ stw %o1, [%o2]
+ flush %o2
+
+ retl
+ nop
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
new file mode 100644
index 000000000000..8104a56ca2d8
--- /dev/null
+++ b/arch/sparc64/kernel/head.S
@@ -0,0 +1,782 @@
+/* $Id: head.S,v 1.87 2002/02/09 19:49:31 davem Exp $
+ * head.S: Initial boot code for the Sparc64 port of Linux.
+ *
+ * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <asm/thread_info.h>
+#include <asm/asi.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/spitfire.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/errno.h>
+#include <asm/signal.h>
+#include <asm/processor.h>
+#include <asm/lsu.h>
+#include <asm/dcr.h>
+#include <asm/dcu.h>
+#include <asm/head.h>
+#include <asm/ttable.h>
+#include <asm/mmu.h>
+
+/* This section from from _start to sparc64_boot_end should fit into
+ * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space
+ * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to
+ * 0x0000.0000.0040.6000 and empty_bad_page, which is from
+ * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000.
+ */
+
+ .text
+ .globl start, _start, stext, _stext
+_start:
+start:
+_stext:
+stext:
+bootup_user_stack:
+! 0x0000000000404000
+ b sparc64_boot
+ flushw /* Flush register file. */
+
+/* This stuff has to be in sync with SILO and other potential boot loaders
+ * Fields should be kept upward compatible and whenever any change is made,
+ * HdrS version should be incremented.
+ */
+ .global root_flags, ram_flags, root_dev
+ .global sparc_ramdisk_image, sparc_ramdisk_size
+ .global sparc_ramdisk_image64
+
+ .ascii "HdrS"
+ .word LINUX_VERSION_CODE
+
+ /* History:
+ *
+ * 0x0300 : Supports being located at other than 0x4000
+ * 0x0202 : Supports kernel params string
+ * 0x0201 : Supports reboot_command
+ */
+ .half 0x0301 /* HdrS version */
+
+root_flags:
+ .half 1
+root_dev:
+ .half 0
+ram_flags:
+ .half 0
+sparc_ramdisk_image:
+ .word 0
+sparc_ramdisk_size:
+ .word 0
+ .xword reboot_command
+ .xword bootstr_info
+sparc_ramdisk_image64:
+ .xword 0
+ .word _end
+
+ /* We must be careful, 32-bit OpenBOOT will get confused if it
+ * tries to save away a register window to a 64-bit kernel
+ * stack address. Flush all windows, disable interrupts,
+ * remap if necessary, jump onto kernel trap table, then kernel
+ * stack, or else we die.
+ *
+ * PROM entry point is on %o4
+ */
+sparc64_boot:
+ BRANCH_IF_CHEETAH_BASE(g1,g7,cheetah_boot)
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,cheetah_plus_boot)
+ ba,pt %xcc, spitfire_boot
+ nop
+
+cheetah_plus_boot:
+ /* Preserve OBP chosen DCU and DCR register settings. */
+ ba,pt %xcc, cheetah_generic_boot
+ nop
+
+cheetah_boot:
+ mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
+ wr %g1, %asr18
+
+ sethi %uhi(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g7
+ or %g7, %ulo(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g7
+ sllx %g7, 32, %g7
+ or %g7, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g7
+ stxa %g7, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+
+cheetah_generic_boot:
+ mov TSB_EXTENSION_P, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ stxa %g0, [%g3] ASI_IMMU
+ membar #Sync
+
+ mov TSB_EXTENSION_S, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ membar #Sync
+
+ mov TSB_EXTENSION_N, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ stxa %g0, [%g3] ASI_IMMU
+ membar #Sync
+
+ wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
+ wr %g0, 0, %fprs
+
+ /* Just like for Spitfire, we probe itlb-2 for a mapping which
+ * matches our current %pc. We take the physical address in
+ * that mapping and use it to make our own.
+ */
+
+ /* %g5 holds the tlb data */
+ sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
+ sllx %g5, 32, %g5
+ or %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5
+
+ /* Put PADDR tlb data mask into %g3. */
+ sethi %uhi(_PAGE_PADDR), %g3
+ or %g3, %ulo(_PAGE_PADDR), %g3
+ sllx %g3, 32, %g3
+ sethi %hi(_PAGE_PADDR), %g7
+ or %g7, %lo(_PAGE_PADDR), %g7
+ or %g3, %g7, %g3
+
+ set 2 << 16, %l0 /* TLB entry walker. */
+ set 0x1fff, %l2 /* Page mask. */
+ rd %pc, %l3
+ andn %l3, %l2, %g2 /* vaddr comparator */
+
+1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1
+ membar #Sync
+ andn %g1, %l2, %g1
+ cmp %g1, %g2
+ be,pn %xcc, cheetah_got_tlbentry
+ nop
+ and %l0, (127 << 3), %g1
+ cmp %g1, (127 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ /* Search the small TLB. OBP never maps us like that but
+ * newer SILO can.
+ */
+ clr %l0
+
+1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1
+ membar #Sync
+ andn %g1, %l2, %g1
+ cmp %g1, %g2
+ be,pn %xcc, cheetah_got_tlbentry
+ nop
+ cmp %l0, (15 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ /* BUG() if we get here... */
+ ta 0x5
+
+cheetah_got_tlbentry:
+ ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g0
+ ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1
+ membar #Sync
+ and %g1, %g3, %g1
+ set 0x5fff, %l0
+ andn %g1, %l0, %g1
+ or %g5, %g1, %g5
+
+ /* Clear out any KERNBASE area entries. */
+ set 2 << 16, %l0
+ sethi %hi(KERNBASE), %g3
+ sethi %hi(KERNBASE<<1), %g7
+ mov TLB_TAG_ACCESS, %l7
+
+ /* First, check ITLB */
+1: ldxa [%l0] ASI_ITLB_TAG_READ, %g1
+ membar #Sync
+ andn %g1, %l2, %g1
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_IMMU
+ membar #Sync
+ stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+
+2: and %l0, (127 << 3), %g1
+ cmp %g1, (127 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ /* Next, check DTLB */
+ set 2 << 16, %l0
+1: ldxa [%l0] ASI_DTLB_TAG_READ, %g1
+ membar #Sync
+ andn %g1, %l2, %g1
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_DMMU
+ membar #Sync
+ stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+
+2: and %l0, (511 << 3), %g1
+ cmp %g1, (511 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ /* On Cheetah+, have to check second DTLB. */
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,l0,2f)
+ ba,pt %xcc, 9f
+ nop
+
+2: set 3 << 16, %l0
+1: ldxa [%l0] ASI_DTLB_TAG_READ, %g1
+ membar #Sync
+ andn %g1, %l2, %g1
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_DMMU
+ membar #Sync
+ stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+
+2: and %l0, (511 << 3), %g1
+ cmp %g1, (511 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+9:
+
+ /* Now lock the TTE we created into ITLB-0 and DTLB-0,
+ * entry 15 (and maybe 14 too).
+ */
+ sethi %hi(KERNBASE), %g3
+ set (0 << 16) | (15 << 3), %g7
+ stxa %g3, [%l7] ASI_DMMU
+ membar #Sync
+ stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+ stxa %g3, [%l7] ASI_IMMU
+ membar #Sync
+ stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+ flush %g3
+ membar #Sync
+ sethi %hi(_end), %g3 /* Check for bigkernel case */
+ or %g3, %lo(_end), %g3
+ srl %g3, 23, %g3 /* Check if _end > 8M */
+ brz,pt %g3, 1f
+ sethi %hi(KERNBASE), %g3 /* Restore for fixup code below */
+ sethi %hi(0x400000), %g3
+ or %g3, %lo(0x400000), %g3
+ add %g5, %g3, %g5 /* New tte data */
+ andn %g5, (_PAGE_G), %g5
+ sethi %hi(KERNBASE+0x400000), %g3
+ or %g3, %lo(KERNBASE+0x400000), %g3
+ set (0 << 16) | (14 << 3), %g7
+ stxa %g3, [%l7] ASI_DMMU
+ membar #Sync
+ stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+ stxa %g3, [%l7] ASI_IMMU
+ membar #Sync
+ stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+ flush %g3
+ membar #Sync
+ sethi %hi(KERNBASE), %g3 /* Restore for fixup code below */
+ ba,pt %xcc, 1f
+ nop
+
+1: set sun4u_init, %g2
+ jmpl %g2 + %g0, %g0
+ nop
+
+spitfire_boot:
+ /* Typically PROM has already enabled both MMU's and both on-chip
+ * caches, but we do it here anyway just to be paranoid.
+ */
+ mov (LSU_CONTROL_IC|LSU_CONTROL_DC|LSU_CONTROL_IM|LSU_CONTROL_DM), %g1
+ stxa %g1, [%g0] ASI_LSU_CONTROL
+ membar #Sync
+
+ /*
+ * Make sure we are in privileged mode, have address masking,
+ * using the ordinary globals and have enabled floating
+ * point.
+ *
+ * Again, typically PROM has left %pil at 13 or similar, and
+ * (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE) in %pstate.
+ */
+ wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
+ wr %g0, 0, %fprs
+
+spitfire_create_mappings:
+ /* %g5 holds the tlb data */
+ sethi %uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
+ sllx %g5, 32, %g5
+ or %g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W | _PAGE_G), %g5
+
+ /* Base of physical memory cannot reliably be assumed to be
+ * at 0x0! Figure out where it happens to be. -DaveM
+ */
+
+ /* Put PADDR tlb data mask into %g3. */
+ sethi %uhi(_PAGE_PADDR_SF), %g3
+ or %g3, %ulo(_PAGE_PADDR_SF), %g3
+ sllx %g3, 32, %g3
+ sethi %hi(_PAGE_PADDR_SF), %g7
+ or %g7, %lo(_PAGE_PADDR_SF), %g7
+ or %g3, %g7, %g3
+
+ /* Walk through entire ITLB, looking for entry which maps
+ * our %pc currently, stick PADDR from there into %g5 tlb data.
+ */
+ clr %l0 /* TLB entry walker. */
+ set 0x1fff, %l2 /* Page mask. */
+ rd %pc, %l3
+ andn %l3, %l2, %g2 /* vaddr comparator */
+1:
+ /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */
+ ldxa [%l0] ASI_ITLB_TAG_READ, %g1
+ nop
+ nop
+ nop
+ andn %g1, %l2, %g1 /* Get vaddr */
+ cmp %g1, %g2
+ be,a,pn %xcc, spitfire_got_tlbentry
+ ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1
+ cmp %l0, (63 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ /* BUG() if we get here... */
+ ta 0x5
+
+spitfire_got_tlbentry:
+ /* Nops here again, perhaps Cheetah/Blackbird are better behaved... */
+ nop
+ nop
+ nop
+ and %g1, %g3, %g1 /* Mask to just get paddr bits. */
+ set 0x5fff, %l3 /* Mask offset to get phys base. */
+ andn %g1, %l3, %g1
+
+ /* NOTE: We hold on to %g1 paddr base as we need it below to lock
+ * NOTE: the PROM cif code into the TLB.
+ */
+
+ or %g5, %g1, %g5 /* Or it into TAG being built. */
+
+ clr %l0 /* TLB entry walker. */
+ sethi %hi(KERNBASE), %g3 /* 4M lower limit */
+ sethi %hi(KERNBASE<<1), %g7 /* 8M upper limit */
+ mov TLB_TAG_ACCESS, %l7
+1:
+ /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */
+ ldxa [%l0] ASI_ITLB_TAG_READ, %g1
+ nop
+ nop
+ nop
+ andn %g1, %l2, %g1 /* Get vaddr */
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_IMMU
+ stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+2:
+ cmp %l0, (63 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ nop; nop; nop
+
+ clr %l0 /* TLB entry walker. */
+1:
+ /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */
+ ldxa [%l0] ASI_DTLB_TAG_READ, %g1
+ nop
+ nop
+ nop
+ andn %g1, %l2, %g1 /* Get vaddr */
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_DMMU
+ stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+2:
+ cmp %l0, (63 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ nop; nop; nop
+
+
+ /* PROM never puts any TLB entries into the MMU with the lock bit
+ * set. So we gladly use tlb entry 63 for KERNBASE. And maybe 62 too.
+ */
+
+ sethi %hi(KERNBASE), %g3
+ mov (63 << 3), %g7
+ stxa %g3, [%l7] ASI_DMMU /* KERNBASE into TLB TAG */
+ stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */
+ membar #Sync
+ stxa %g3, [%l7] ASI_IMMU /* KERNBASE into TLB TAG */
+ stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */
+ membar #Sync
+ flush %g3
+ membar #Sync
+ sethi %hi(_end), %g3 /* Check for bigkernel case */
+ or %g3, %lo(_end), %g3
+ srl %g3, 23, %g3 /* Check if _end > 8M */
+ brz,pt %g3, 2f
+ sethi %hi(KERNBASE), %g3 /* Restore for fixup code below */
+ sethi %hi(0x400000), %g3
+ or %g3, %lo(0x400000), %g3
+ add %g5, %g3, %g5 /* New tte data */
+ andn %g5, (_PAGE_G), %g5
+ sethi %hi(KERNBASE+0x400000), %g3
+ or %g3, %lo(KERNBASE+0x400000), %g3
+ mov (62 << 3), %g7
+ stxa %g3, [%l7] ASI_DMMU
+ stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+ stxa %g3, [%l7] ASI_IMMU
+ stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+ flush %g3
+ membar #Sync
+ sethi %hi(KERNBASE), %g3 /* Restore for fixup code below */
+2: ba,pt %xcc, 1f
+ nop
+1:
+ set sun4u_init, %g2
+ jmpl %g2 + %g0, %g0
+ nop
+
+sun4u_init:
+ /* Set ctx 0 */
+ mov PRIMARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ membar #Sync
+
+ mov SECONDARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ membar #Sync
+
+ /* We are now safely (we hope) in Nucleus context (0), rewrite
+ * the KERNBASE TTE's so they no longer have the global bit set.
+ * Don't forget to setup TAG_ACCESS first 8-)
+ */
+ mov TLB_TAG_ACCESS, %g2
+ stxa %g3, [%g2] ASI_IMMU
+ stxa %g3, [%g2] ASI_DMMU
+ membar #Sync
+
+ BRANCH_IF_ANY_CHEETAH(g1,g7,cheetah_tlb_fixup)
+
+ ba,pt %xcc, spitfire_tlb_fixup
+ nop
+
+cheetah_tlb_fixup:
+ set (0 << 16) | (15 << 3), %g7
+ ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g0
+ ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1
+ andn %g1, (_PAGE_G), %g1
+ stxa %g1, [%g7] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+
+ ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g0
+ ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g1
+ andn %g1, (_PAGE_G), %g1
+ stxa %g1, [%g7] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+
+ /* Kill instruction prefetch queues. */
+ flush %g3
+ membar #Sync
+
+ mov 2, %g2 /* Set TLB type to cheetah+. */
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,1f)
+
+ mov 1, %g2 /* Set TLB type to cheetah. */
+
+1: sethi %hi(tlb_type), %g1
+ stw %g2, [%g1 + %lo(tlb_type)]
+
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,1f)
+ ba,pt %xcc, 2f
+ nop
+
+1: /* Patch context register writes to support nucleus page
+ * size correctly.
+ */
+ call cheetah_plus_patch_etrap
+ nop
+ call cheetah_plus_patch_rtrap
+ nop
+ call cheetah_plus_patch_fpdis
+ nop
+ call cheetah_plus_patch_winfixup
+ nop
+
+
+2: /* Patch copy/page operations to cheetah optimized versions. */
+ call cheetah_patch_copyops
+ nop
+ call cheetah_patch_cachetlbops
+ nop
+
+ ba,pt %xcc, tlb_fixup_done
+ nop
+
+spitfire_tlb_fixup:
+ mov (63 << 3), %g7
+ ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1
+ andn %g1, (_PAGE_G), %g1
+ stxa %g1, [%g7] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+
+ ldxa [%g7] ASI_DTLB_DATA_ACCESS, %g1
+ andn %g1, (_PAGE_G), %g1
+ stxa %g1, [%g7] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+
+ /* Kill instruction prefetch queues. */
+ flush %g3
+ membar #Sync
+
+ /* Set TLB type to spitfire. */
+ mov 0, %g2
+ sethi %hi(tlb_type), %g1
+ stw %g2, [%g1 + %lo(tlb_type)]
+
+tlb_fixup_done:
+ sethi %hi(init_thread_union), %g6
+ or %g6, %lo(init_thread_union), %g6
+ ldx [%g6 + TI_TASK], %g4
+ mov %sp, %l6
+ mov %o4, %l7
+
+#if 0 /* We don't do it like this anymore, but for historical hack value
+ * I leave this snippet here to show how crazy we can be sometimes. 8-)
+ */
+
+ /* Setup "Linux Current Register", thanks Sun 8-) */
+ wr %g0, 0x1, %pcr
+
+ /* Blackbird errata workaround. See commentary in
+ * smp.c:smp_percpu_timer_interrupt() for more
+ * information.
+ */
+ ba,pt %xcc, 99f
+ nop
+ .align 64
+99: wr %g6, %g0, %pic
+ rd %pic, %g0
+#endif
+
+ wr %g0, ASI_P, %asi
+ mov 1, %g1
+ sllx %g1, THREAD_SHIFT, %g1
+ sub %g1, (STACKFRAME_SZ + STACK_BIAS), %g1
+ add %g6, %g1, %sp
+ mov 0, %fp
+
+ /* Set per-cpu pointer initially to zero, this makes
+ * the boot-cpu use the in-kernel-image per-cpu areas
+ * before setup_per_cpu_area() is invoked.
+ */
+ clr %g5
+
+ wrpr %g0, 0, %wstate
+ wrpr %g0, 0x0, %tl
+
+ /* Clear the bss */
+ sethi %hi(__bss_start), %o0
+ or %o0, %lo(__bss_start), %o0
+ sethi %hi(_end), %o1
+ or %o1, %lo(_end), %o1
+ call __bzero
+ sub %o1, %o0, %o1
+
+ mov %l6, %o1 ! OpenPROM stack
+ call prom_init
+ mov %l7, %o0 ! OpenPROM cif handler
+
+ /* Off we go.... */
+ call start_kernel
+ nop
+ /* Not reached... */
+
+/* IMPORTANT NOTE: Whenever making changes here, check
+ * trampoline.S as well. -jj */
+ .globl setup_tba
+setup_tba: /* i0 = is_starfire */
+ save %sp, -160, %sp
+
+ rdpr %tba, %g7
+ sethi %hi(prom_tba), %o1
+ or %o1, %lo(prom_tba), %o1
+ stx %g7, [%o1]
+
+ /* Setup "Linux" globals 8-) */
+ rdpr %pstate, %o1
+ mov %g6, %o2
+ wrpr %o1, (PSTATE_AG|PSTATE_IE), %pstate
+ sethi %hi(sparc64_ttable_tl0), %g1
+ wrpr %g1, %tba
+ mov %o2, %g6
+
+ /* Set up MMU globals */
+ wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate
+
+ /* Set fixed globals used by dTLB miss handler. */
+#define KERN_HIGHBITS ((_PAGE_VALID|_PAGE_SZ4MB)^0xfffff80000000000)
+#define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
+
+ mov TSB_REG, %g1
+ stxa %g0, [%g1] ASI_DMMU
+ membar #Sync
+ stxa %g0, [%g1] ASI_IMMU
+ membar #Sync
+ mov TLB_SFSR, %g1
+ sethi %uhi(KERN_HIGHBITS), %g2
+ or %g2, %ulo(KERN_HIGHBITS), %g2
+ sllx %g2, 32, %g2
+ or %g2, KERN_LOWBITS, %g2
+
+ BRANCH_IF_ANY_CHEETAH(g3,g7,cheetah_vpte_base)
+ ba,pt %xcc, spitfire_vpte_base
+ nop
+
+cheetah_vpte_base:
+ sethi %uhi(VPTE_BASE_CHEETAH), %g3
+ or %g3, %ulo(VPTE_BASE_CHEETAH), %g3
+ ba,pt %xcc, 2f
+ sllx %g3, 32, %g3
+
+spitfire_vpte_base:
+ sethi %uhi(VPTE_BASE_SPITFIRE), %g3
+ or %g3, %ulo(VPTE_BASE_SPITFIRE), %g3
+ sllx %g3, 32, %g3
+
+2:
+ clr %g7
+#undef KERN_HIGHBITS
+#undef KERN_LOWBITS
+
+ /* Kill PROM timer */
+ sethi %hi(0x80000000), %o2
+ sllx %o2, 32, %o2
+ wr %o2, 0, %tick_cmpr
+
+ BRANCH_IF_ANY_CHEETAH(o2,o3,1f)
+
+ ba,pt %xcc, 2f
+ nop
+
+ /* Disable STICK_INT interrupts. */
+1:
+ sethi %hi(0x80000000), %o2
+ sllx %o2, 32, %o2
+ wr %o2, %asr25
+
+ /* Ok, we're done setting up all the state our trap mechanims needs,
+ * now get back into normal globals and let the PROM know what is up.
+ */
+2:
+ wrpr %g0, %g0, %wstate
+ wrpr %o1, PSTATE_IE, %pstate
+
+ call init_irqwork_curcpu
+ nop
+
+ call prom_set_trap_table
+ sethi %hi(sparc64_ttable_tl0), %o0
+
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g2,g3,1f)
+ ba,pt %xcc, 2f
+ nop
+
+1: /* Start using proper page size encodings in ctx register. */
+ sethi %uhi(CTX_CHEETAH_PLUS_NUC), %g3
+ mov PRIMARY_CONTEXT, %g1
+ sllx %g3, 32, %g3
+ sethi %hi(CTX_CHEETAH_PLUS_CTX0), %g2
+ or %g3, %g2, %g3
+ stxa %g3, [%g1] ASI_DMMU
+ membar #Sync
+
+2:
+ rdpr %pstate, %o1
+ or %o1, PSTATE_IE, %o1
+ wrpr %o1, 0, %pstate
+
+ ret
+ restore
+
+/*
+ * The following skips make sure the trap table in ttable.S is aligned
+ * on a 32K boundary as required by the v9 specs for TBA register.
+ */
+sparc64_boot_end:
+ .skip 0x2000 + _start - sparc64_boot_end
+bootup_user_stack_end:
+ .skip 0x2000
+
+#ifdef CONFIG_SBUS
+/* This is just a hack to fool make depend config.h discovering
+ strategy: As the .S files below need config.h, but
+ make depend does not find it for them, we include config.h
+ in head.S */
+#endif
+
+! 0x0000000000408000
+
+#include "ttable.S"
+#include "systbls.S"
+
+ .align 1024
+ .globl swapper_pg_dir
+swapper_pg_dir:
+ .word 0
+
+#include "etrap.S"
+#include "rtrap.S"
+#include "winfixup.S"
+#include "entry.S"
+
+ /* This is just anal retentiveness on my part... */
+ .align 16384
+
+ .data
+ .align 8
+ .globl prom_tba, tlb_type
+prom_tba: .xword 0
+tlb_type: .word 0 /* Must NOT end up in BSS */
+ .section ".fixup",#alloc,#execinstr
+ .globl __ret_efault
+__ret_efault:
+ ret
+ restore %g0, -EFAULT, %o0
+
diff --git a/arch/sparc64/kernel/idprom.c b/arch/sparc64/kernel/idprom.c
new file mode 100644
index 000000000000..3b6789e09a72
--- /dev/null
+++ b/arch/sparc64/kernel/idprom.c
@@ -0,0 +1,49 @@
+/* $Id: idprom.c,v 1.3 1999/08/31 06:54:53 davem Exp $
+ * idprom.c: Routines to load the idprom into kernel addresses and
+ * interpret the data contained within.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+
+struct idprom *idprom;
+static struct idprom idprom_buffer;
+
+/* Calculate the IDPROM checksum (xor of the data bytes). */
+static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
+{
+ unsigned char cksum, i, *ptr = (unsigned char *)idprom;
+
+ for (i = cksum = 0; i <= 0x0E; i++)
+ cksum ^= *ptr++;
+
+ return cksum;
+}
+
+/* Create a local IDPROM copy and verify integrity. */
+void __init idprom_init(void)
+{
+ prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer));
+
+ idprom = &idprom_buffer;
+
+ if (idprom->id_format != 0x01) {
+ prom_printf("IDPROM: Warning, unknown format type!\n");
+ }
+
+ if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
+ prom_printf("IDPROM: Warning, checksum failure (nvram=%x, calc=%x)!\n",
+ idprom->id_cksum, calc_idprom_cksum(idprom));
+ }
+
+ printk("Ethernet address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ idprom->id_ethaddr[0], idprom->id_ethaddr[1],
+ idprom->id_ethaddr[2], idprom->id_ethaddr[3],
+ idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
+}
diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c
new file mode 100644
index 000000000000..329b38fa5c89
--- /dev/null
+++ b/arch/sparc64/kernel/init_task.c
@@ -0,0 +1,35 @@
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init_task.h>
+#include <linux/mqueue.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/processor.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);
+
+EXPORT_SYMBOL(init_mm);
+
+/* .text section in head.S is aligned at 2 page boundary and this gets linked
+ * right after that so that the init_thread_union is aligned properly as well.
+ * We really don't need this special alignment like the Intel does, but
+ * I do it anyways for completeness.
+ */
+__asm__ (".text");
+union thread_union init_thread_union = { INIT_THREAD_INFO(init_task) };
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+EXPORT_SYMBOL(init_task);
+
+__asm__(".data");
+struct task_struct init_task = INIT_TASK(init_task);
diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c
new file mode 100644
index 000000000000..43fc3173d480
--- /dev/null
+++ b/arch/sparc64/kernel/ioctl32.c
@@ -0,0 +1,597 @@
+/* $Id: ioctl32.c,v 1.136 2002/01/14 09:49:52 davem Exp $
+ * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
+ *
+ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
+ *
+ * These routines maintain argument size conversion between 32bit and 64bit
+ * ioctls.
+ */
+
+#define INCLUDES
+#include "compat_ioctl.c"
+#include <linux/ncp_fs.h>
+#include <linux/syscalls.h>
+#include <asm/fbio.h>
+#include <asm/kbio.h>
+#include <asm/vuid_event.h>
+#include <asm/envctrl.h>
+#include <asm/display7seg.h>
+#include <asm/openpromio.h>
+#include <asm/audioio.h>
+#include <asm/watchdog.h>
+
+/* Use this to get at 32-bit user passed pointers.
+ * See sys_sparc32.c for description about it.
+ */
+#define A(__x) compat_ptr(__x)
+
+static __inline__ void *alloc_user_space(long len)
+{
+ struct pt_regs *regs = current_thread_info()->kregs;
+ unsigned long usp = regs->u_regs[UREG_I6];
+
+ if (!(test_thread_flag(TIF_32BIT)))
+ usp += STACK_BIAS;
+
+ return (void *) (usp - len);
+}
+
+#define CODE
+#include "compat_ioctl.c"
+
+struct fbcmap32 {
+ int index; /* first element (0 origin) */
+ int count;
+ u32 red;
+ u32 green;
+ u32 blue;
+};
+
+#define FBIOPUTCMAP32 _IOW('F', 3, struct fbcmap32)
+#define FBIOGETCMAP32 _IOW('F', 4, struct fbcmap32)
+
+static int fbiogetputcmap(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct fbcmap32 __user *argp = (void __user *)arg;
+ struct fbcmap __user *p = compat_alloc_user_space(sizeof(*p));
+ u32 addr;
+ int ret;
+
+ ret = copy_in_user(p, argp, 2 * sizeof(int));
+ ret |= get_user(addr, &argp->red);
+ ret |= put_user(compat_ptr(addr), &p->red);
+ ret |= get_user(addr, &argp->green);
+ ret |= put_user(compat_ptr(addr), &p->green);
+ ret |= get_user(addr, &argp->blue);
+ ret |= put_user(compat_ptr(addr), &p->blue);
+ if (ret)
+ return -EFAULT;
+ return sys_ioctl(fd, (cmd == FBIOPUTCMAP32) ? FBIOPUTCMAP_SPARC : FBIOGETCMAP_SPARC, (unsigned long)p);
+}
+
+struct fbcursor32 {
+ short set; /* what to set, choose from the list above */
+ short enable; /* cursor on/off */
+ struct fbcurpos pos; /* cursor position */
+ struct fbcurpos hot; /* cursor hot spot */
+ struct fbcmap32 cmap; /* color map info */
+ struct fbcurpos size; /* cursor bit map size */
+ u32 image; /* cursor image bits */
+ u32 mask; /* cursor mask bits */
+};
+
+#define FBIOSCURSOR32 _IOW('F', 24, struct fbcursor32)
+#define FBIOGCURSOR32 _IOW('F', 25, struct fbcursor32)
+
+static int fbiogscursor(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct fbcursor __user *p = compat_alloc_user_space(sizeof(*p));
+ struct fbcursor32 __user *argp = (void __user *)arg;
+ compat_uptr_t addr;
+ int ret;
+
+ ret = copy_in_user(p, argp,
+ 2 * sizeof (short) + 2 * sizeof(struct fbcurpos));
+ ret |= copy_in_user(&p->size, &argp->size, sizeof(struct fbcurpos));
+ ret |= copy_in_user(&p->cmap, &argp->cmap, 2 * sizeof(int));
+ ret |= get_user(addr, &argp->cmap.red);
+ ret |= put_user(compat_ptr(addr), &p->cmap.red);
+ ret |= get_user(addr, &argp->cmap.green);
+ ret |= put_user(compat_ptr(addr), &p->cmap.green);
+ ret |= get_user(addr, &argp->cmap.blue);
+ ret |= put_user(compat_ptr(addr), &p->cmap.blue);
+ ret |= get_user(addr, &argp->mask);
+ ret |= put_user(compat_ptr(addr), &p->mask);
+ ret |= get_user(addr, &argp->image);
+ ret |= put_user(compat_ptr(addr), &p->image);
+ if (ret)
+ return -EFAULT;
+ return sys_ioctl (fd, FBIOSCURSOR, (unsigned long)p);
+}
+
+#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
+/* This really belongs in include/linux/drm.h -DaveM */
+#include "../../../drivers/char/drm/drm.h"
+
+typedef struct drm32_version {
+ int version_major; /* Major version */
+ int version_minor; /* Minor version */
+ int version_patchlevel;/* Patch level */
+ int name_len; /* Length of name buffer */
+ u32 name; /* Name of driver */
+ int date_len; /* Length of date buffer */
+ u32 date; /* User-space buffer to hold date */
+ int desc_len; /* Length of desc buffer */
+ u32 desc; /* User-space buffer to hold desc */
+} drm32_version_t;
+#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t)
+
+static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_version_t __user *uversion = (drm32_version_t __user *)arg;
+ drm_version_t __user *p = compat_alloc_user_space(sizeof(*p));
+ compat_uptr_t addr;
+ int n;
+ int ret;
+
+ if (clear_user(p, 3 * sizeof(int)) ||
+ get_user(n, &uversion->name_len) ||
+ put_user(n, &p->name_len) ||
+ get_user(addr, &uversion->name) ||
+ put_user(compat_ptr(addr), &p->name) ||
+ get_user(n, &uversion->date_len) ||
+ put_user(n, &p->date_len) ||
+ get_user(addr, &uversion->date) ||
+ put_user(compat_ptr(addr), &p->date) ||
+ get_user(n, &uversion->desc_len) ||
+ put_user(n, &p->desc_len) ||
+ get_user(addr, &uversion->desc) ||
+ put_user(compat_ptr(addr), &p->desc))
+ return -EFAULT;
+
+ ret = sys_ioctl(fd, DRM_IOCTL_VERSION, (unsigned long)p);
+ if (ret)
+ return ret;
+
+ if (copy_in_user(uversion, p, 3 * sizeof(int)) ||
+ get_user(n, &p->name_len) ||
+ put_user(n, &uversion->name_len) ||
+ get_user(n, &p->date_len) ||
+ put_user(n, &uversion->date_len) ||
+ get_user(n, &p->desc_len) ||
+ put_user(n, &uversion->desc_len))
+ return -EFAULT;
+
+ return 0;
+}
+
+typedef struct drm32_unique {
+ int unique_len; /* Length of unique */
+ u32 unique; /* Unique name for driver instantiation */
+} drm32_unique_t;
+#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t)
+#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t)
+
+static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_unique_t __user *uarg = (drm32_unique_t __user *)arg;
+ drm_unique_t __user *p = compat_alloc_user_space(sizeof(*p));
+ compat_uptr_t addr;
+ int n;
+ int ret;
+
+ if (get_user(n, &uarg->unique_len) ||
+ put_user(n, &p->unique_len) ||
+ get_user(addr, &uarg->unique) ||
+ put_user(compat_ptr(addr), &p->unique))
+ return -EFAULT;
+
+ if (cmd == DRM32_IOCTL_GET_UNIQUE)
+ ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)p);
+ else
+ ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)p);
+
+ if (ret)
+ return ret;
+
+ if (get_user(n, &p->unique_len) || put_user(n, &uarg->unique_len))
+ return -EFAULT;
+
+ return 0;
+}
+
+typedef struct drm32_map {
+ u32 offset; /* Requested physical address (0 for SAREA)*/
+ u32 size; /* Requested physical size (bytes) */
+ drm_map_type_t type; /* Type of memory to map */
+ drm_map_flags_t flags; /* Flags */
+ u32 handle; /* User-space: "Handle" to pass to mmap */
+ /* Kernel-space: kernel-virtual address */
+ int mtrr; /* MTRR slot used */
+ /* Private data */
+} drm32_map_t;
+#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t)
+
+static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_map_t __user *uarg = (drm32_map_t __user *) arg;
+ drm_map_t karg;
+ mm_segment_t old_fs;
+ u32 tmp;
+ int ret;
+
+ ret = get_user(karg.offset, &uarg->offset);
+ ret |= get_user(karg.size, &uarg->size);
+ ret |= get_user(karg.type, &uarg->type);
+ ret |= get_user(karg.flags, &uarg->flags);
+ ret |= get_user(tmp, &uarg->handle);
+ ret |= get_user(karg.mtrr, &uarg->mtrr);
+ if (ret)
+ return -EFAULT;
+
+ karg.handle = (void *) (unsigned long) tmp;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg);
+ set_fs(old_fs);
+
+ if (!ret) {
+ ret = put_user(karg.offset, &uarg->offset);
+ ret |= put_user(karg.size, &uarg->size);
+ ret |= put_user(karg.type, &uarg->type);
+ ret |= put_user(karg.flags, &uarg->flags);
+ tmp = (u32) (long)karg.handle;
+ ret |= put_user(tmp, &uarg->handle);
+ ret |= put_user(karg.mtrr, &uarg->mtrr);
+ if (ret)
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+typedef struct drm32_buf_info {
+ int count; /* Entries in list */
+ u32 list; /* (drm_buf_desc_t *) */
+} drm32_buf_info_t;
+#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t)
+
+static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_buf_info_t __user *uarg = (drm32_buf_info_t __user *)arg;
+ drm_buf_info_t __user *p = compat_alloc_user_space(sizeof(*p));
+ compat_uptr_t addr;
+ int n;
+ int ret;
+
+ if (get_user(n, &uarg->count) || put_user(n, &p->count) ||
+ get_user(addr, &uarg->list) || put_user(compat_ptr(addr), &p->list))
+ return -EFAULT;
+
+ ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long)p);
+ if (ret)
+ return ret;
+
+ if (get_user(n, &p->count) || put_user(n, &uarg->count))
+ return -EFAULT;
+
+ return 0;
+}
+
+typedef struct drm32_buf_free {
+ int count;
+ u32 list; /* (int *) */
+} drm32_buf_free_t;
+#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t)
+
+static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_buf_free_t __user *uarg = (drm32_buf_free_t __user *)arg;
+ drm_buf_free_t __user *p = compat_alloc_user_space(sizeof(*p));
+ compat_uptr_t addr;
+ int n;
+
+ if (get_user(n, &uarg->count) || put_user(n, &p->count) ||
+ get_user(addr, &uarg->list) || put_user(compat_ptr(addr), &p->list))
+ return -EFAULT;
+
+ return sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long)p);
+}
+
+typedef struct drm32_buf_pub {
+ int idx; /* Index into master buflist */
+ int total; /* Buffer size */
+ int used; /* Amount of buffer in use (for DMA) */
+ u32 address; /* Address of buffer (void *) */
+} drm32_buf_pub_t;
+
+typedef struct drm32_buf_map {
+ int count; /* Length of buflist */
+ u32 virtual; /* Mmaped area in user-virtual (void *) */
+ u32 list; /* Buffer information (drm_buf_pub_t *) */
+} drm32_buf_map_t;
+#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t)
+
+static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_buf_map_t __user *uarg = (drm32_buf_map_t __user *)arg;
+ drm32_buf_pub_t __user *ulist;
+ drm_buf_map_t __user *arg64;
+ drm_buf_pub_t __user *list;
+ int orig_count, ret, i;
+ int n;
+ compat_uptr_t addr;
+
+ if (get_user(orig_count, &uarg->count))
+ return -EFAULT;
+
+ arg64 = compat_alloc_user_space(sizeof(drm_buf_map_t) +
+ (size_t)orig_count * sizeof(drm_buf_pub_t));
+ list = (void __user *)(arg64 + 1);
+
+ if (put_user(orig_count, &arg64->count) ||
+ put_user(list, &arg64->list) ||
+ get_user(addr, &uarg->virtual) ||
+ put_user(compat_ptr(addr), &arg64->virtual) ||
+ get_user(addr, &uarg->list))
+ return -EFAULT;
+
+ ulist = compat_ptr(addr);
+
+ for (i = 0; i < orig_count; i++) {
+ if (get_user(n, &ulist[i].idx) ||
+ put_user(n, &list[i].idx) ||
+ get_user(n, &ulist[i].total) ||
+ put_user(n, &list[i].total) ||
+ get_user(n, &ulist[i].used) ||
+ put_user(n, &list[i].used) ||
+ get_user(addr, &ulist[i].address) ||
+ put_user(compat_ptr(addr), &list[i].address))
+ return -EFAULT;
+ }
+
+ ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) arg64);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < orig_count; i++) {
+ void __user *p;
+ if (get_user(n, &list[i].idx) ||
+ put_user(n, &ulist[i].idx) ||
+ get_user(n, &list[i].total) ||
+ put_user(n, &ulist[i].total) ||
+ get_user(n, &list[i].used) ||
+ put_user(n, &ulist[i].used) ||
+ get_user(p, &list[i].address) ||
+ put_user((unsigned long)p, &ulist[i].address))
+ return -EFAULT;
+ }
+
+ if (get_user(n, &arg64->count) || put_user(n, &uarg->count))
+ return -EFAULT;
+
+ return 0;
+}
+
+typedef struct drm32_dma {
+ /* Indices here refer to the offset into
+ buflist in drm_buf_get_t. */
+ int context; /* Context handle */
+ int send_count; /* Number of buffers to send */
+ u32 send_indices; /* List of handles to buffers (int *) */
+ u32 send_sizes; /* Lengths of data to send (int *) */
+ drm_dma_flags_t flags; /* Flags */
+ int request_count; /* Number of buffers requested */
+ int request_size; /* Desired size for buffers */
+ u32 request_indices; /* Buffer information (int *) */
+ u32 request_sizes; /* (int *) */
+ int granted_count; /* Number of buffers granted */
+} drm32_dma_t;
+#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t)
+
+/* RED PEN The DRM layer blindly dereferences the send/request
+ * index/size arrays even though they are userland
+ * pointers. -DaveM
+ */
+static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_dma_t __user *uarg = (drm32_dma_t __user *) arg;
+ drm_dma_t __user *p = compat_alloc_user_space(sizeof(*p));
+ compat_uptr_t addr;
+ int ret;
+
+ if (copy_in_user(p, uarg, 2 * sizeof(int)) ||
+ get_user(addr, &uarg->send_indices) ||
+ put_user(compat_ptr(addr), &p->send_indices) ||
+ get_user(addr, &uarg->send_sizes) ||
+ put_user(compat_ptr(addr), &p->send_sizes) ||
+ copy_in_user(&p->flags, &uarg->flags, sizeof(drm_dma_flags_t)) ||
+ copy_in_user(&p->request_count, &uarg->request_count, sizeof(int))||
+ copy_in_user(&p->request_size, &uarg->request_size, sizeof(int)) ||
+ get_user(addr, &uarg->request_indices) ||
+ put_user(compat_ptr(addr), &p->request_indices) ||
+ get_user(addr, &uarg->request_sizes) ||
+ put_user(compat_ptr(addr), &p->request_sizes) ||
+ copy_in_user(&p->granted_count, &uarg->granted_count, sizeof(int)))
+ return -EFAULT;
+
+ ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long)p);
+ if (ret)
+ return ret;
+
+ if (copy_in_user(uarg, p, 2 * sizeof(int)) ||
+ copy_in_user(&uarg->flags, &p->flags, sizeof(drm_dma_flags_t)) ||
+ copy_in_user(&uarg->request_count, &p->request_count, sizeof(int))||
+ copy_in_user(&uarg->request_size, &p->request_size, sizeof(int)) ||
+ copy_in_user(&uarg->granted_count, &p->granted_count, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+}
+
+typedef struct drm32_ctx_res {
+ int count;
+ u32 contexts; /* (drm_ctx_t *) */
+} drm32_ctx_res_t;
+#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t)
+
+static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ drm32_ctx_res_t __user *uarg = (drm32_ctx_res_t __user *) arg;
+ drm_ctx_res_t __user *p = compat_alloc_user_space(sizeof(*p));
+ compat_uptr_t addr;
+ int ret;
+
+ if (copy_in_user(p, uarg, sizeof(int)) ||
+ get_user(addr, &uarg->contexts) ||
+ put_user(compat_ptr(addr), &p->contexts))
+ return -EFAULT;
+
+ ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long)p);
+ if (ret)
+ return ret;
+
+ if (copy_in_user(uarg, p, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+}
+
+#endif
+
+typedef int (* ioctl32_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
+
+#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL((cmd),sys_ioctl)
+#define HANDLE_IOCTL(cmd,handler) { (cmd), (ioctl32_handler_t)(handler), NULL },
+#define IOCTL_TABLE_START \
+ struct ioctl_trans ioctl_start[] = {
+#define IOCTL_TABLE_END \
+ };
+
+IOCTL_TABLE_START
+#include <linux/compat_ioctl.h>
+#define DECLARES
+#include "compat_ioctl.c"
+COMPATIBLE_IOCTL(TIOCSTART)
+COMPATIBLE_IOCTL(TIOCSTOP)
+COMPATIBLE_IOCTL(TIOCSLTC)
+COMPATIBLE_IOCTL(FBIOGTYPE)
+COMPATIBLE_IOCTL(FBIOSATTR)
+COMPATIBLE_IOCTL(FBIOGATTR)
+COMPATIBLE_IOCTL(FBIOSVIDEO)
+COMPATIBLE_IOCTL(FBIOGVIDEO)
+COMPATIBLE_IOCTL(FBIOGCURSOR32) /* This is not implemented yet. Later it should be converted... */
+COMPATIBLE_IOCTL(FBIOSCURPOS)
+COMPATIBLE_IOCTL(FBIOGCURPOS)
+COMPATIBLE_IOCTL(FBIOGCURMAX)
+/* Little k */
+COMPATIBLE_IOCTL(KIOCTYPE)
+COMPATIBLE_IOCTL(KIOCLAYOUT)
+COMPATIBLE_IOCTL(KIOCGTRANS)
+COMPATIBLE_IOCTL(KIOCTRANS)
+COMPATIBLE_IOCTL(KIOCCMD)
+COMPATIBLE_IOCTL(KIOCSDIRECT)
+COMPATIBLE_IOCTL(KIOCSLED)
+COMPATIBLE_IOCTL(KIOCGLED)
+COMPATIBLE_IOCTL(KIOCSRATE)
+COMPATIBLE_IOCTL(KIOCGRATE)
+COMPATIBLE_IOCTL(VUIDSFORMAT)
+COMPATIBLE_IOCTL(VUIDGFORMAT)
+/* Little v, the video4linux ioctls */
+COMPATIBLE_IOCTL(_IOR('p', 20, int[7])) /* RTCGET */
+COMPATIBLE_IOCTL(_IOW('p', 21, int[7])) /* RTCSET */
+COMPATIBLE_IOCTL(ENVCTRL_RD_WARNING_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_SHUTDOWN_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_FAN_STATUS)
+COMPATIBLE_IOCTL(ENVCTRL_RD_VOLTAGE_STATUS)
+COMPATIBLE_IOCTL(ENVCTRL_RD_SCSI_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_ETHERNET_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_MTHRBD_TEMPERATURE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_CPU_VOLTAGE)
+COMPATIBLE_IOCTL(ENVCTRL_RD_GLOBALADDRESS)
+/* COMPATIBLE_IOCTL(D7SIOCRD) same value as ENVCTRL_RD_VOLTAGE_STATUS */
+COMPATIBLE_IOCTL(D7SIOCWR)
+COMPATIBLE_IOCTL(D7SIOCTM)
+/* OPENPROMIO, SunOS/Solaris only, the NetBSD one's have
+ * embedded pointers in the arg which we'd need to clean up...
+ */
+COMPATIBLE_IOCTL(OPROMGETOPT)
+COMPATIBLE_IOCTL(OPROMSETOPT)
+COMPATIBLE_IOCTL(OPROMNXTOPT)
+COMPATIBLE_IOCTL(OPROMSETOPT2)
+COMPATIBLE_IOCTL(OPROMNEXT)
+COMPATIBLE_IOCTL(OPROMCHILD)
+COMPATIBLE_IOCTL(OPROMGETPROP)
+COMPATIBLE_IOCTL(OPROMNXTPROP)
+COMPATIBLE_IOCTL(OPROMU2P)
+COMPATIBLE_IOCTL(OPROMGETCONS)
+COMPATIBLE_IOCTL(OPROMGETFBNAME)
+COMPATIBLE_IOCTL(OPROMGETBOOTARGS)
+COMPATIBLE_IOCTL(OPROMSETCUR)
+COMPATIBLE_IOCTL(OPROMPCI2NODE)
+COMPATIBLE_IOCTL(OPROMPATH2NODE)
+/* Big L */
+COMPATIBLE_IOCTL(LOOP_SET_STATUS64)
+COMPATIBLE_IOCTL(LOOP_GET_STATUS64)
+/* Big A */
+COMPATIBLE_IOCTL(AUDIO_GETINFO)
+COMPATIBLE_IOCTL(AUDIO_SETINFO)
+COMPATIBLE_IOCTL(AUDIO_DRAIN)
+COMPATIBLE_IOCTL(AUDIO_GETDEV)
+COMPATIBLE_IOCTL(AUDIO_GETDEV_SUNOS)
+COMPATIBLE_IOCTL(AUDIO_FLUSH)
+COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI)
+#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
+COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC)
+COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID)
+COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC)
+COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK)
+COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK)
+COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL)
+COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS)
+COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS)
+COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX)
+COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX)
+COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX)
+COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX)
+COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX)
+COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX)
+COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW)
+COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW)
+COMPATIBLE_IOCTL(DRM_IOCTL_LOCK)
+COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK)
+COMPATIBLE_IOCTL(DRM_IOCTL_FINISH)
+#endif /* DRM */
+COMPATIBLE_IOCTL(WIOCSTART)
+COMPATIBLE_IOCTL(WIOCSTOP)
+COMPATIBLE_IOCTL(WIOCGSTAT)
+/* And these ioctls need translation */
+/* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */
+HANDLE_IOCTL(FBIOPUTCMAP32, fbiogetputcmap)
+HANDLE_IOCTL(FBIOGETCMAP32, fbiogetputcmap)
+HANDLE_IOCTL(FBIOSCURSOR32, fbiogscursor)
+#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE)
+HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version)
+HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique)
+HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique)
+HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap)
+HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs)
+HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs)
+HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs)
+HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma)
+HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx)
+#endif /* DRM */
+#if 0
+HANDLE_IOCTL(RTC32_IRQP_READ, do_rtc_ioctl)
+HANDLE_IOCTL(RTC32_IRQP_SET, do_rtc_ioctl)
+HANDLE_IOCTL(RTC32_EPOCH_READ, do_rtc_ioctl)
+HANDLE_IOCTL(RTC32_EPOCH_SET, do_rtc_ioctl)
+#endif
+/* take care of sizeof(sizeof()) breakage */
+IOCTL_TABLE_END
+
+int ioctl_table_size = ARRAY_SIZE(ioctl_start);
diff --git a/arch/sparc64/kernel/iommu_common.c b/arch/sparc64/kernel/iommu_common.c
new file mode 100644
index 000000000000..12c93a3eee26
--- /dev/null
+++ b/arch/sparc64/kernel/iommu_common.c
@@ -0,0 +1,231 @@
+/* $Id: iommu_common.c,v 1.9 2001/12/17 07:05:09 davem Exp $
+ * iommu_common.c: UltraSparc SBUS/PCI common iommu code.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include "iommu_common.h"
+
+/* You are _strongly_ advised to enable the following debugging code
+ * any time you make changes to the sg code below, run it for a while
+ * with filesystems mounted read-only before buying the farm... -DaveM
+ */
+
+#ifdef VERIFY_SG
+static int verify_lengths(struct scatterlist *sg, int nents, int npages)
+{
+ int sg_len, dma_len;
+ int i, pgcount;
+
+ sg_len = 0;
+ for (i = 0; i < nents; i++)
+ sg_len += sg[i].length;
+
+ dma_len = 0;
+ for (i = 0; i < nents && sg[i].dma_length; i++)
+ dma_len += sg[i].dma_length;
+
+ if (sg_len != dma_len) {
+ printk("verify_lengths: Error, different, sg[%d] dma[%d]\n",
+ sg_len, dma_len);
+ return -1;
+ }
+
+ pgcount = 0;
+ for (i = 0; i < nents && sg[i].dma_length; i++) {
+ unsigned long start, end;
+
+ start = sg[i].dma_address;
+ start = start & IO_PAGE_MASK;
+
+ end = sg[i].dma_address + sg[i].dma_length;
+ end = (end + (IO_PAGE_SIZE - 1)) & IO_PAGE_MASK;
+
+ pgcount += ((end - start) >> IO_PAGE_SHIFT);
+ }
+
+ if (pgcount != npages) {
+ printk("verify_lengths: Error, page count wrong, "
+ "npages[%d] pgcount[%d]\n",
+ npages, pgcount);
+ return -1;
+ }
+
+ /* This test passes... */
+ return 0;
+}
+
+static int verify_one_map(struct scatterlist *dma_sg, struct scatterlist **__sg, int nents, iopte_t **__iopte)
+{
+ struct scatterlist *sg = *__sg;
+ iopte_t *iopte = *__iopte;
+ u32 dlen = dma_sg->dma_length;
+ u32 daddr;
+ unsigned int sglen;
+ unsigned long sgaddr;
+
+ daddr = dma_sg->dma_address;
+ sglen = sg->length;
+ sgaddr = (unsigned long) (page_address(sg->page) + sg->offset);
+ while (dlen > 0) {
+ unsigned long paddr;
+
+ /* SG and DMA_SG must begin at the same sub-page boundary. */
+ if ((sgaddr & ~IO_PAGE_MASK) != (daddr & ~IO_PAGE_MASK)) {
+ printk("verify_one_map: Wrong start offset "
+ "sg[%08lx] dma[%08x]\n",
+ sgaddr, daddr);
+ nents = -1;
+ goto out;
+ }
+
+ /* Verify the IOPTE points to the right page. */
+ paddr = iopte_val(*iopte) & IOPTE_PAGE;
+ if ((paddr + PAGE_OFFSET) != (sgaddr & IO_PAGE_MASK)) {
+ printk("verify_one_map: IOPTE[%08lx] maps the "
+ "wrong page, should be [%08lx]\n",
+ iopte_val(*iopte), (sgaddr & IO_PAGE_MASK) - PAGE_OFFSET);
+ nents = -1;
+ goto out;
+ }
+
+ /* If this SG crosses a page, adjust to that next page
+ * boundary and loop.
+ */
+ if ((sgaddr & IO_PAGE_MASK) ^ ((sgaddr + sglen - 1) & IO_PAGE_MASK)) {
+ unsigned long next_page, diff;
+
+ next_page = (sgaddr + IO_PAGE_SIZE) & IO_PAGE_MASK;
+ diff = next_page - sgaddr;
+ sgaddr += diff;
+ daddr += diff;
+ sglen -= diff;
+ dlen -= diff;
+ if (dlen > 0)
+ iopte++;
+ continue;
+ }
+
+ /* SG wholly consumed within this page. */
+ daddr += sglen;
+ dlen -= sglen;
+
+ if (dlen > 0 && ((daddr & ~IO_PAGE_MASK) == 0))
+ iopte++;
+
+ sg++;
+ if (--nents <= 0)
+ break;
+ sgaddr = (unsigned long) (page_address(sg->page) + sg->offset);
+ sglen = sg->length;
+ }
+ if (dlen < 0) {
+ /* Transfer overrun, big problems. */
+ printk("verify_one_map: Transfer overrun by %d bytes.\n",
+ -dlen);
+ nents = -1;
+ } else {
+ /* Advance to next dma_sg implies that the next iopte will
+ * begin it.
+ */
+ iopte++;
+ }
+
+out:
+ *__sg = sg;
+ *__iopte = iopte;
+ return nents;
+}
+
+static int verify_maps(struct scatterlist *sg, int nents, iopte_t *iopte)
+{
+ struct scatterlist *dma_sg = sg;
+ struct scatterlist *orig_dma_sg = dma_sg;
+ int orig_nents = nents;
+
+ for (;;) {
+ nents = verify_one_map(dma_sg, &sg, nents, &iopte);
+ if (nents <= 0)
+ break;
+ dma_sg++;
+ if (dma_sg->dma_length == 0)
+ break;
+ }
+
+ if (nents > 0) {
+ printk("verify_maps: dma maps consumed by some sgs remain (%d)\n",
+ nents);
+ return -1;
+ }
+
+ if (nents < 0) {
+ printk("verify_maps: Error, messed up mappings, "
+ "at sg %d dma_sg %d\n",
+ (int) (orig_nents + nents), (int) (dma_sg - orig_dma_sg));
+ return -1;
+ }
+
+ /* This test passes... */
+ return 0;
+}
+
+void verify_sglist(struct scatterlist *sg, int nents, iopte_t *iopte, int npages)
+{
+ if (verify_lengths(sg, nents, npages) < 0 ||
+ verify_maps(sg, nents, iopte) < 0) {
+ int i;
+
+ printk("verify_sglist: Crap, messed up mappings, dumping, iodma at ");
+ printk("%016lx.\n", sg->dma_address & IO_PAGE_MASK);
+
+ for (i = 0; i < nents; i++) {
+ printk("sg(%d): page_addr(%p) off(%x) length(%x) "
+ "dma_address[%016lx] dma_length[%016lx]\n",
+ i,
+ page_address(sg[i].page), sg[i].offset,
+ sg[i].length,
+ sg[i].dma_address, sg[i].dma_length);
+ }
+ }
+
+ /* Seems to be ok */
+}
+#endif
+
+unsigned long prepare_sg(struct scatterlist *sg, int nents)
+{
+ struct scatterlist *dma_sg = sg;
+ unsigned long prev;
+ u32 dent_addr, dent_len;
+
+ prev = (unsigned long) (page_address(sg->page) + sg->offset);
+ prev += (unsigned long) (dent_len = sg->length);
+ dent_addr = (u32) ((unsigned long)(page_address(sg->page) + sg->offset)
+ & (IO_PAGE_SIZE - 1UL));
+ while (--nents) {
+ unsigned long addr;
+
+ sg++;
+ addr = (unsigned long) (page_address(sg->page) + sg->offset);
+ if (! VCONTIG(prev, addr)) {
+ dma_sg->dma_address = dent_addr;
+ dma_sg->dma_length = dent_len;
+ dma_sg++;
+
+ dent_addr = ((dent_addr +
+ dent_len +
+ (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT);
+ dent_addr <<= IO_PAGE_SHIFT;
+ dent_addr += addr & (IO_PAGE_SIZE - 1UL);
+ dent_len = 0;
+ }
+ dent_len += sg->length;
+ prev = addr + sg->length;
+ }
+ dma_sg->dma_address = dent_addr;
+ dma_sg->dma_length = dent_len;
+
+ return ((unsigned long) dent_addr +
+ (unsigned long) dent_len +
+ (IO_PAGE_SIZE - 1UL)) >> IO_PAGE_SHIFT;
+}
diff --git a/arch/sparc64/kernel/iommu_common.h b/arch/sparc64/kernel/iommu_common.h
new file mode 100644
index 000000000000..ad791014419c
--- /dev/null
+++ b/arch/sparc64/kernel/iommu_common.h
@@ -0,0 +1,48 @@
+/* $Id: iommu_common.h,v 1.5 2001/12/11 09:41:01 davem Exp $
+ * iommu_common.h: UltraSparc SBUS/PCI common iommu declarations.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/iommu.h>
+#include <asm/scatterlist.h>
+
+/*
+ * These give mapping size of each iommu pte/tlb.
+ */
+#define IO_PAGE_SHIFT 13
+#define IO_PAGE_SIZE (1UL << IO_PAGE_SHIFT)
+#define IO_PAGE_MASK (~(IO_PAGE_SIZE-1))
+#define IO_PAGE_ALIGN(addr) (((addr)+IO_PAGE_SIZE-1)&IO_PAGE_MASK)
+
+#define IO_TSB_ENTRIES (128*1024)
+#define IO_TSB_SIZE (IO_TSB_ENTRIES * 8)
+
+/*
+ * This is the hardwired shift in the iotlb tag/data parts.
+ */
+#define IOMMU_PAGE_SHIFT 13
+
+/* You are _strongly_ advised to enable the following debugging code
+ * any time you make changes to the sg code below, run it for a while
+ * with filesystems mounted read-only before buying the farm... -DaveM
+ */
+#undef VERIFY_SG
+
+#ifdef VERIFY_SG
+extern void verify_sglist(struct scatterlist *sg, int nents, iopte_t *iopte, int npages);
+#endif
+
+/* Two addresses are "virtually contiguous" if and only if:
+ * 1) They are equal, or...
+ * 2) They are both on a page boundary
+ */
+#define VCONTIG(__X, __Y) (((__X) == (__Y)) || \
+ (((__X) | (__Y)) << (64UL - PAGE_SHIFT)) == 0UL)
+
+extern unsigned long prepare_sg(struct scatterlist *sg, int nents);
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
new file mode 100644
index 000000000000..a38cb5036df0
--- /dev/null
+++ b/arch/sparc64/kernel/irq.c
@@ -0,0 +1,1269 @@
+/* $Id: irq.c,v 1.114 2002/01/11 08:45:38 davem Exp $
+ * irq.c: UltraSparc IRQ handling/init/registry.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/sbus.h>
+#include <asm/iommu.h>
+#include <asm/upa.h>
+#include <asm/oplib.h>
+#include <asm/timer.h>
+#include <asm/smp.h>
+#include <asm/starfire.h>
+#include <asm/uaccess.h>
+#include <asm/cache.h>
+#include <asm/cpudata.h>
+
+#ifdef CONFIG_SMP
+static void distribute_irqs(void);
+#endif
+
+/* UPA nodes send interrupt packet to UltraSparc with first data reg
+ * value low 5 (7 on Starfire) bits holding the IRQ identifier being
+ * delivered. We must translate this into a non-vector IRQ so we can
+ * set the softint on this cpu.
+ *
+ * To make processing these packets efficient and race free we use
+ * an array of irq buckets below. The interrupt vector handler in
+ * entry.S feeds incoming packets into per-cpu pil-indexed lists.
+ * The IVEC handler does not need to act atomically, the PIL dispatch
+ * code uses CAS to get an atomic snapshot of the list and clear it
+ * at the same time.
+ */
+
+struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BYTES)));
+
+/* This has to be in the main kernel image, it cannot be
+ * turned into per-cpu data. The reason is that the main
+ * kernel image is locked into the TLB and this structure
+ * is accessed from the vectored interrupt trap handler. If
+ * access to this structure takes a TLB miss it could cause
+ * the 5-level sparc v9 trap stack to overflow.
+ */
+struct irq_work_struct {
+ unsigned int irq_worklists[16];
+};
+struct irq_work_struct __irq_work[NR_CPUS];
+#define irq_work(__cpu, __pil) &(__irq_work[(__cpu)].irq_worklists[(__pil)])
+
+#ifdef CONFIG_PCI
+/* This is a table of physical addresses used to deal with IBF_DMA_SYNC.
+ * It is used for PCI only to synchronize DMA transfers with IRQ delivery
+ * for devices behind busses other than APB on Sabre systems.
+ *
+ * Currently these physical addresses are just config space accesses
+ * to the command register for that device.
+ */
+unsigned long pci_dma_wsync;
+unsigned long dma_sync_reg_table[256];
+unsigned char dma_sync_reg_table_entry = 0;
+#endif
+
+/* This is based upon code in the 32-bit Sparc kernel written mostly by
+ * David Redman (djhr@tadpole.co.uk).
+ */
+#define MAX_STATIC_ALLOC 4
+static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+static int static_irq_count;
+
+/* This is exported so that fast IRQ handlers can get at it... -DaveM */
+struct irqaction *irq_action[NR_IRQS+1] = {
+ NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
+};
+
+/* This only synchronizes entities which modify IRQ handler
+ * state and some selected user-level spots that want to
+ * read things in the table. IRQ handler processing orders
+ * its' accesses such that no locking is needed.
+ */
+static DEFINE_SPINLOCK(irq_action_lock);
+
+static void register_irq_proc (unsigned int irq);
+
+/*
+ * Upper 2b of irqaction->flags holds the ino.
+ * irqaction->mask holds the smp affinity information.
+ */
+#define put_ino_in_irqaction(action, irq) \
+ action->flags &= 0xffffffffffffUL; \
+ if (__bucket(irq) == &pil0_dummy_bucket) \
+ action->flags |= 0xdeadUL << 48; \
+ else \
+ action->flags |= __irq_ino(irq) << 48;
+#define get_ino_in_irqaction(action) (action->flags >> 48)
+
+#define put_smpaff_in_irqaction(action, smpaff) (action)->mask = (smpaff)
+#define get_smpaff_in_irqaction(action) ((action)->mask)
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ unsigned long flags;
+ int i = *(loff_t *) v;
+ struct irqaction *action;
+#ifdef CONFIG_SMP
+ int j;
+#endif
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+ if (i <= NR_IRQS) {
+ if (!(action = *(i + irq_action)))
+ goto out_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))
+ continue;
+ seq_printf(p, "%10u ",
+ kstat_cpu(j).irqs[i]);
+ }
+#endif
+ seq_printf(p, " %s:%lx", action->name,
+ get_ino_in_irqaction(action));
+ for (action = action->next; action; action = action->next) {
+ seq_printf(p, ", %s:%lx", action->name,
+ get_ino_in_irqaction(action));
+ }
+ seq_putc(p, '\n');
+ }
+out_unlock:
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+
+ return 0;
+}
+
+/* Now these are always passed a true fully specified sun4u INO. */
+void enable_irq(unsigned int irq)
+{
+ struct ino_bucket *bucket = __bucket(irq);
+ unsigned long imap;
+ unsigned long tid;
+
+ imap = bucket->imap;
+ if (imap == 0UL)
+ return;
+
+ preempt_disable();
+
+ if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ unsigned long ver;
+
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32) == 0x003e0016) {
+ /* We set it to our JBUS ID. */
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (tid)
+ : "i" (ASI_JBUS_CONFIG));
+ tid = ((tid & (0x1fUL<<17)) << 9);
+ tid &= IMAP_TID_JBUS;
+ } else {
+ /* We set it to our Safari AID. */
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (tid)
+ : "i" (ASI_SAFARI_CONFIG));
+ tid = ((tid & (0x3ffUL<<17)) << 9);
+ tid &= IMAP_AID_SAFARI;
+ }
+ } else if (this_is_starfire == 0) {
+ /* We set it to our UPA MID. */
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (tid)
+ : "i" (ASI_UPA_CONFIG));
+ tid = ((tid & UPA_CONFIG_MID) << 9);
+ tid &= IMAP_TID_UPA;
+ } else {
+ tid = (starfire_translate(imap, smp_processor_id()) << 26);
+ tid &= IMAP_TID_UPA;
+ }
+
+ /* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product
+ * of this SYSIO's preconfigured IGN in the SYSIO Control
+ * Register, the hardware just mirrors that value here.
+ * However for Graphics and UPA Slave devices the full
+ * IMAP_INR field can be set by the programmer here.
+ *
+ * Things like FFB can now be handled via the new IRQ mechanism.
+ */
+ upa_writel(tid | IMAP_VALID, imap);
+
+ preempt_enable();
+}
+
+/* This now gets passed true ino's as well. */
+void disable_irq(unsigned int irq)
+{
+ struct ino_bucket *bucket = __bucket(irq);
+ unsigned long imap;
+
+ imap = bucket->imap;
+ if (imap != 0UL) {
+ u32 tmp;
+
+ /* NOTE: We do not want to futz with the IRQ clear registers
+ * and move the state to IDLE, the SCSI code does call
+ * disable_irq() to assure atomicity in the queue cmd
+ * SCSI adapter driver code. Thus we'd lose interrupts.
+ */
+ tmp = upa_readl(imap);
+ tmp &= ~IMAP_VALID;
+ upa_writel(tmp, imap);
+ }
+}
+
+/* The timer is the one "weird" interrupt which is generated by
+ * the CPU %tick register and not by some normal vectored interrupt
+ * source. To handle this special case, we use this dummy INO bucket.
+ */
+static struct ino_bucket pil0_dummy_bucket = {
+ 0, /* irq_chain */
+ 0, /* pil */
+ 0, /* pending */
+ 0, /* flags */
+ 0, /* __unused */
+ NULL, /* irq_info */
+ 0UL, /* iclr */
+ 0UL, /* imap */
+};
+
+unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap)
+{
+ struct ino_bucket *bucket;
+ int ino;
+
+ if (pil == 0) {
+ if (iclr != 0UL || imap != 0UL) {
+ prom_printf("Invalid dummy bucket for PIL0 (%lx:%lx)\n",
+ iclr, imap);
+ prom_halt();
+ }
+ return __irq(&pil0_dummy_bucket);
+ }
+
+ /* RULE: Both must be specified in all other cases. */
+ if (iclr == 0UL || imap == 0UL) {
+ prom_printf("Invalid build_irq %d %d %016lx %016lx\n",
+ pil, inofixup, iclr, imap);
+ prom_halt();
+ }
+
+ ino = (upa_readl(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
+ if (ino > NUM_IVECS) {
+ prom_printf("Invalid INO %04x (%d:%d:%016lx:%016lx)\n",
+ ino, pil, inofixup, iclr, imap);
+ prom_halt();
+ }
+
+ /* Ok, looks good, set it up. Don't touch the irq_chain or
+ * the pending flag.
+ */
+ bucket = &ivector_table[ino];
+ if ((bucket->flags & IBF_ACTIVE) ||
+ (bucket->irq_info != NULL)) {
+ /* This is a gross fatal error if it happens here. */
+ prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n");
+ prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n",
+ ino, pil, inofixup, iclr, imap);
+ prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n",
+ bucket->pil, bucket->iclr, bucket->imap);
+ prom_printf("IRQ: Cannot continue, halting...\n");
+ prom_halt();
+ }
+ bucket->imap = imap;
+ bucket->iclr = iclr;
+ bucket->pil = pil;
+ bucket->flags = 0;
+
+ bucket->irq_info = NULL;
+
+ return __irq(bucket);
+}
+
+static void atomic_bucket_insert(struct ino_bucket *bucket)
+{
+ unsigned long pstate;
+ unsigned int *ent;
+
+ __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+ __asm__ __volatile__("wrpr %0, %1, %%pstate"
+ : : "r" (pstate), "i" (PSTATE_IE));
+ ent = irq_work(smp_processor_id(), bucket->pil);
+ bucket->irq_chain = *ent;
+ *ent = __irq(bucket);
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
+}
+
+int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags, const char *name, void *dev_id)
+{
+ struct irqaction *action, *tmp = NULL;
+ struct ino_bucket *bucket = __bucket(irq);
+ unsigned long flags;
+ int pending = 0;
+
+ if ((bucket != &pil0_dummy_bucket) &&
+ (bucket < &ivector_table[0] ||
+ bucket >= &ivector_table[NUM_IVECS])) {
+ unsigned int *caller;
+
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+ printk(KERN_CRIT "request_irq: Old style IRQ registry attempt "
+ "from %p, irq %08x.\n", caller, irq);
+ return -EINVAL;
+ }
+ if (!handler)
+ return -EINVAL;
+
+ if ((bucket != &pil0_dummy_bucket) && (irqflags & SA_SAMPLE_RANDOM)) {
+ /*
+ * This function might sleep, we want to call it first,
+ * outside of the atomic block. In SA_STATIC_ALLOC case,
+ * random driver's kmalloc will fail, but it is safe.
+ * If already initialized, random driver will not reinit.
+ * 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);
+ }
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ action = *(bucket->pil + irq_action);
+ if (action) {
+ if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ))
+ for (tmp = action; tmp->next; tmp = tmp->next)
+ ;
+ else {
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return -EBUSY;
+ }
+ action = NULL; /* Or else! */
+ }
+
+ /* If this is flagged as statically allocated then we use our
+ * private struct which is never freed.
+ */
+ if (irqflags & SA_STATIC_ALLOC) {
+ if (static_irq_count < MAX_STATIC_ALLOC)
+ action = &static_irqaction[static_irq_count++];
+ else
+ printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed "
+ "using kmalloc\n", irq, name);
+ }
+ if (action == NULL)
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+ GFP_ATOMIC);
+
+ if (!action) {
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return -ENOMEM;
+ }
+
+ if (bucket == &pil0_dummy_bucket) {
+ bucket->irq_info = action;
+ bucket->flags |= IBF_ACTIVE;
+ } else {
+ if ((bucket->flags & IBF_ACTIVE) != 0) {
+ void *orig = bucket->irq_info;
+ void **vector = NULL;
+
+ if ((bucket->flags & IBF_PCI) == 0) {
+ printk("IRQ: Trying to share non-PCI bucket.\n");
+ goto free_and_ebusy;
+ }
+ if ((bucket->flags & IBF_MULTI) == 0) {
+ vector = kmalloc(sizeof(void *) * 4, GFP_ATOMIC);
+ if (vector == NULL)
+ goto free_and_enomem;
+
+ /* We might have slept. */
+ if ((bucket->flags & IBF_MULTI) != 0) {
+ int ent;
+
+ kfree(vector);
+ vector = (void **)bucket->irq_info;
+ for(ent = 0; ent < 4; ent++) {
+ if (vector[ent] == NULL) {
+ vector[ent] = action;
+ break;
+ }
+ }
+ if (ent == 4)
+ goto free_and_ebusy;
+ } else {
+ vector[0] = orig;
+ vector[1] = action;
+ vector[2] = NULL;
+ vector[3] = NULL;
+ bucket->irq_info = vector;
+ bucket->flags |= IBF_MULTI;
+ }
+ } else {
+ int ent;
+
+ vector = (void **)orig;
+ for (ent = 0; ent < 4; ent++) {
+ if (vector[ent] == NULL) {
+ vector[ent] = action;
+ break;
+ }
+ }
+ if (ent == 4)
+ goto free_and_ebusy;
+ }
+ } else {
+ bucket->irq_info = action;
+ bucket->flags |= IBF_ACTIVE;
+ }
+ pending = bucket->pending;
+ if (pending)
+ bucket->pending = 0;
+ }
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->name = name;
+ action->next = NULL;
+ action->dev_id = dev_id;
+ put_ino_in_irqaction(action, irq);
+ put_smpaff_in_irqaction(action, CPU_MASK_NONE);
+
+ if (tmp)
+ tmp->next = action;
+ else
+ *(bucket->pil + irq_action) = action;
+
+ enable_irq(irq);
+
+ /* We ate the IVEC already, this makes sure it does not get lost. */
+ if (pending) {
+ atomic_bucket_insert(bucket);
+ set_softint(1 << bucket->pil);
+ }
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ if ((bucket != &pil0_dummy_bucket) && (!(irqflags & SA_STATIC_ALLOC)))
+ register_irq_proc(__irq_ino(irq));
+
+#ifdef CONFIG_SMP
+ distribute_irqs();
+#endif
+ return 0;
+
+free_and_ebusy:
+ kfree(action);
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return -EBUSY;
+
+free_and_enomem:
+ kfree(action);
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return -ENOMEM;
+}
+
+EXPORT_SYMBOL(request_irq);
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction *action;
+ struct irqaction *tmp = NULL;
+ unsigned long flags;
+ struct ino_bucket *bucket = __bucket(irq), *bp;
+
+ if ((bucket != &pil0_dummy_bucket) &&
+ (bucket < &ivector_table[0] ||
+ bucket >= &ivector_table[NUM_IVECS])) {
+ unsigned int *caller;
+
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+ printk(KERN_CRIT "free_irq: Old style IRQ removal attempt "
+ "from %p, irq %08x.\n", caller, irq);
+ return;
+ }
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ action = *(bucket->pil + irq_action);
+ if (!action->handler) {
+ printk("Freeing free IRQ %d\n", bucket->pil);
+ return;
+ }
+ if (dev_id) {
+ for ( ; action; action = action->next) {
+ if (action->dev_id == dev_id)
+ break;
+ tmp = action;
+ }
+ if (!action) {
+ printk("Trying to free free shared IRQ %d\n", bucket->pil);
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return;
+ }
+ } else if (action->flags & SA_SHIRQ) {
+ printk("Trying to free shared IRQ %d with NULL device ID\n", bucket->pil);
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return;
+ }
+
+ if (action->flags & SA_STATIC_ALLOC) {
+ printk("Attempt to free statically allocated IRQ %d (%s)\n",
+ bucket->pil, action->name);
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return;
+ }
+
+ if (action && tmp)
+ tmp->next = action->next;
+ else
+ *(bucket->pil + irq_action) = action->next;
+
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+
+ synchronize_irq(irq);
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ if (bucket != &pil0_dummy_bucket) {
+ unsigned long imap = bucket->imap;
+ void **vector, *orig;
+ int ent;
+
+ orig = bucket->irq_info;
+ vector = (void **)orig;
+
+ if ((bucket->flags & IBF_MULTI) != 0) {
+ int other = 0;
+ void *orphan = NULL;
+ for (ent = 0; ent < 4; ent++) {
+ if (vector[ent] == action)
+ vector[ent] = NULL;
+ else if (vector[ent] != NULL) {
+ orphan = vector[ent];
+ other++;
+ }
+ }
+
+ /* Only free when no other shared irq
+ * uses this bucket.
+ */
+ if (other) {
+ if (other == 1) {
+ /* Convert back to non-shared bucket. */
+ bucket->irq_info = orphan;
+ bucket->flags &= ~(IBF_MULTI);
+ kfree(vector);
+ }
+ goto out;
+ }
+ } else {
+ bucket->irq_info = NULL;
+ }
+
+ /* This unique interrupt source is now inactive. */
+ bucket->flags &= ~IBF_ACTIVE;
+
+ /* See if any other buckets share this bucket's IMAP
+ * and are still active.
+ */
+ for (ent = 0; ent < NUM_IVECS; ent++) {
+ bp = &ivector_table[ent];
+ if (bp != bucket &&
+ bp->imap == imap &&
+ (bp->flags & IBF_ACTIVE) != 0)
+ break;
+ }
+
+ /* Only disable when no other sub-irq levels of
+ * the same IMAP are active.
+ */
+ if (ent == NUM_IVECS)
+ disable_irq(irq);
+ }
+
+out:
+ kfree(action);
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+}
+
+EXPORT_SYMBOL(free_irq);
+
+#ifdef CONFIG_SMP
+void synchronize_irq(unsigned int irq)
+{
+ struct ino_bucket *bucket = __bucket(irq);
+
+#if 0
+ /* The following is how I wish I could implement this.
+ * Unfortunately the ICLR registers are read-only, you can
+ * only write ICLR_foo values to them. To get the current
+ * IRQ status you would need to get at the IRQ diag registers
+ * in the PCI/SBUS controller and the layout of those vary
+ * from one controller to the next, sigh... -DaveM
+ */
+ unsigned long iclr = bucket->iclr;
+
+ while (1) {
+ u32 tmp = upa_readl(iclr);
+
+ if (tmp == ICLR_TRANSMIT ||
+ tmp == ICLR_PENDING) {
+ cpu_relax();
+ continue;
+ }
+ break;
+ }
+#else
+ /* So we have to do this with a INPROGRESS bit just like x86. */
+ while (bucket->flags & IBF_INPROGRESS)
+ cpu_relax();
+#endif
+}
+#endif /* CONFIG_SMP */
+
+void catch_disabled_ivec(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+ struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0));
+
+ /* We can actually see this on Ultra/PCI PCI cards, which are bridges
+ * to other devices. Here a single IMAP enabled potentially multiple
+ * unique interrupt sources (which each do have a unique ICLR register.
+ *
+ * So what we do is just register that the IVEC arrived, when registered
+ * for real the request_irq() code will check the bit and signal
+ * a local CPU interrupt for it.
+ */
+#if 0
+ printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n",
+ bucket - &ivector_table[0], regs->tpc);
+#endif
+ *irq_work(cpu, 0) = 0;
+ bucket->pending = 1;
+}
+
+/* Tune this... */
+#define FORWARD_VOLUME 12
+
+#ifdef CONFIG_SMP
+
+static inline void redirect_intr(int cpu, struct ino_bucket *bp)
+{
+ /* Ok, here is what is going on:
+ * 1) Retargeting IRQs on Starfire is very
+ * expensive so just forget about it on them.
+ * 2) Moving around very high priority interrupts
+ * is a losing game.
+ * 3) If the current cpu is idle, interrupts are
+ * useful work, so keep them here. But do not
+ * pass to our neighbour if he is not very idle.
+ * 4) If sysadmin explicitly asks for directed intrs,
+ * Just Do It.
+ */
+ struct irqaction *ap = bp->irq_info;
+ cpumask_t cpu_mask;
+ unsigned int buddy, ticks;
+
+ cpu_mask = get_smpaff_in_irqaction(ap);
+ cpus_and(cpu_mask, cpu_mask, cpu_online_map);
+ if (cpus_empty(cpu_mask))
+ cpu_mask = cpu_online_map;
+
+ if (this_is_starfire != 0 ||
+ bp->pil >= 10 || current->pid == 0)
+ goto out;
+
+ /* 'cpu' is the MID (ie. UPAID), calculate the MID
+ * of our buddy.
+ */
+ buddy = cpu + 1;
+ if (buddy >= NR_CPUS)
+ buddy = 0;
+
+ ticks = 0;
+ while (!cpu_isset(buddy, cpu_mask)) {
+ if (++buddy >= NR_CPUS)
+ buddy = 0;
+ if (++ticks > NR_CPUS) {
+ put_smpaff_in_irqaction(ap, CPU_MASK_NONE);
+ goto out;
+ }
+ }
+
+ if (buddy == cpu)
+ goto out;
+
+ /* Voo-doo programming. */
+ if (cpu_data(buddy).idle_volume < FORWARD_VOLUME)
+ goto out;
+
+ /* This just so happens to be correct on Cheetah
+ * at the moment.
+ */
+ buddy <<= 26;
+
+ /* Push it to our buddy. */
+ upa_writel(buddy | IMAP_VALID, bp->imap);
+
+out:
+ return;
+}
+
+#endif
+
+void handler_irq(int irq, struct pt_regs *regs)
+{
+ struct ino_bucket *bp, *nbp;
+ int cpu = smp_processor_id();
+
+#ifndef CONFIG_SMP
+ /*
+ * Check for TICK_INT on level 14 softint.
+ */
+ {
+ unsigned long clr_mask = 1 << irq;
+ unsigned long tick_mask = tick_ops->softint_mask;
+
+ if ((irq == 14) && (get_softint() & tick_mask)) {
+ irq = 0;
+ clr_mask = tick_mask;
+ }
+ clear_softint(clr_mask);
+ }
+#else
+ int should_forward = 1;
+
+ clear_softint(1 << irq);
+#endif
+
+ irq_enter();
+ kstat_this_cpu.irqs[irq]++;
+
+ /* Sliiiick... */
+#ifndef CONFIG_SMP
+ bp = ((irq != 0) ?
+ __bucket(xchg32(irq_work(cpu, irq), 0)) :
+ &pil0_dummy_bucket);
+#else
+ bp = __bucket(xchg32(irq_work(cpu, irq), 0));
+#endif
+ for ( ; bp != NULL; bp = nbp) {
+ unsigned char flags = bp->flags;
+ unsigned char random = 0;
+
+ nbp = __bucket(bp->irq_chain);
+ bp->irq_chain = 0;
+
+ bp->flags |= IBF_INPROGRESS;
+
+ if ((flags & IBF_ACTIVE) != 0) {
+#ifdef CONFIG_PCI
+ if ((flags & IBF_DMA_SYNC) != 0) {
+ upa_readl(dma_sync_reg_table[bp->synctab_ent]);
+ upa_readq(pci_dma_wsync);
+ }
+#endif
+ if ((flags & IBF_MULTI) == 0) {
+ struct irqaction *ap = bp->irq_info;
+ int ret;
+
+ ret = ap->handler(__irq(bp), ap->dev_id, regs);
+ if (ret == IRQ_HANDLED)
+ random |= ap->flags;
+ } else {
+ void **vector = (void **)bp->irq_info;
+ int ent;
+ for (ent = 0; ent < 4; ent++) {
+ struct irqaction *ap = vector[ent];
+ if (ap != NULL) {
+ int ret;
+
+ ret = ap->handler(__irq(bp),
+ ap->dev_id,
+ regs);
+ if (ret == IRQ_HANDLED)
+ random |= ap->flags;
+ }
+ }
+ }
+ /* Only the dummy bucket lacks IMAP/ICLR. */
+ if (bp->pil != 0) {
+#ifdef CONFIG_SMP
+ if (should_forward) {
+ redirect_intr(cpu, bp);
+ should_forward = 0;
+ }
+#endif
+ upa_writel(ICLR_IDLE, bp->iclr);
+
+ /* Test and add entropy */
+ if (random & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ }
+ } else
+ bp->pending = 1;
+
+ bp->flags &= ~IBF_INPROGRESS;
+ }
+ irq_exit();
+}
+
+#ifdef CONFIG_BLK_DEV_FD
+extern void floppy_interrupt(int irq, void *dev_cookie, struct pt_regs *regs);
+
+void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs)
+{
+ struct irqaction *action = *(irq + irq_action);
+ struct ino_bucket *bucket;
+ int cpu = smp_processor_id();
+
+ irq_enter();
+ kstat_this_cpu.irqs[irq]++;
+
+ *(irq_work(cpu, irq)) = 0;
+ bucket = get_ino_in_irqaction(action) + ivector_table;
+
+ bucket->flags |= IBF_INPROGRESS;
+
+ floppy_interrupt(irq, dev_cookie, regs);
+ upa_writel(ICLR_IDLE, bucket->iclr);
+
+ bucket->flags &= ~IBF_INPROGRESS;
+
+ irq_exit();
+}
+#endif
+
+/* The following assumes that the branch lies before the place we
+ * are branching to. This is the case for a trap vector...
+ * You have been warned.
+ */
+#define SPARC_BRANCH(dest_addr, inst_addr) \
+ (0x10800000 | ((((dest_addr)-(inst_addr))>>2)&0x3fffff))
+
+#define SPARC_NOP (0x01000000)
+
+static void install_fast_irq(unsigned int cpu_irq,
+ irqreturn_t (*handler)(int, void *, struct pt_regs *))
+{
+ extern unsigned long sparc64_ttable_tl0;
+ unsigned long ttent = (unsigned long) &sparc64_ttable_tl0;
+ unsigned int *insns;
+
+ ttent += 0x820;
+ ttent += (cpu_irq - 1) << 5;
+ insns = (unsigned int *) ttent;
+ insns[0] = SPARC_BRANCH(((unsigned long) handler),
+ ((unsigned long)&insns[0]));
+ insns[1] = SPARC_NOP;
+ __asm__ __volatile__("membar #StoreStore; flush %0" : : "r" (ttent));
+}
+
+int request_fast_irq(unsigned int irq,
+ irqreturn_t (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags, const char *name, void *dev_id)
+{
+ struct irqaction *action;
+ struct ino_bucket *bucket = __bucket(irq);
+ unsigned long flags;
+
+ /* No pil0 dummy buckets allowed here. */
+ if (bucket < &ivector_table[0] ||
+ bucket >= &ivector_table[NUM_IVECS]) {
+ unsigned int *caller;
+
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+ printk(KERN_CRIT "request_fast_irq: Old style IRQ registry attempt "
+ "from %p, irq %08x.\n", caller, irq);
+ return -EINVAL;
+ }
+
+ if (!handler)
+ return -EINVAL;
+
+ if ((bucket->pil == 0) || (bucket->pil == 14)) {
+ printk("request_fast_irq: Trying to register shared IRQ 0 or 14.\n");
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ action = *(bucket->pil + irq_action);
+ if (action) {
+ if (action->flags & SA_SHIRQ)
+ panic("Trying to register fast irq when already shared.\n");
+ if (irqflags & SA_SHIRQ)
+ panic("Trying to register fast irq as shared.\n");
+ printk("request_fast_irq: Trying to register yet already owned.\n");
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return -EBUSY;
+ }
+
+ /*
+ * We do not check for SA_SAMPLE_RANDOM in this path. Neither do we
+ * support smp intr affinity in this path.
+ */
+ if (irqflags & SA_STATIC_ALLOC) {
+ if (static_irq_count < MAX_STATIC_ALLOC)
+ action = &static_irqaction[static_irq_count++];
+ else
+ printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed "
+ "using kmalloc\n", bucket->pil, name);
+ }
+ if (action == NULL)
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+ GFP_ATOMIC);
+ if (!action) {
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return -ENOMEM;
+ }
+ install_fast_irq(bucket->pil, handler);
+
+ bucket->irq_info = action;
+ bucket->flags |= IBF_ACTIVE;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->dev_id = NULL;
+ action->name = name;
+ action->next = NULL;
+ put_ino_in_irqaction(action, irq);
+ put_smpaff_in_irqaction(action, CPU_MASK_NONE);
+
+ *(bucket->pil + irq_action) = action;
+ enable_irq(irq);
+
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+
+#ifdef CONFIG_SMP
+ distribute_irqs();
+#endif
+ return 0;
+}
+
+/* We really don't need these at all on the Sparc. We only have
+ * stubs here because they are exported to modules.
+ */
+unsigned long probe_irq_on(void)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(probe_irq_on);
+
+int probe_irq_off(unsigned long mask)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(probe_irq_off);
+
+#ifdef CONFIG_SMP
+static int retarget_one_irq(struct irqaction *p, int goal_cpu)
+{
+ struct ino_bucket *bucket = get_ino_in_irqaction(p) + ivector_table;
+ unsigned long imap = bucket->imap;
+ unsigned int tid;
+
+ while (!cpu_online(goal_cpu)) {
+ if (++goal_cpu >= NR_CPUS)
+ goal_cpu = 0;
+ }
+
+ if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ tid = goal_cpu << 26;
+ tid &= IMAP_AID_SAFARI;
+ } else if (this_is_starfire == 0) {
+ tid = goal_cpu << 26;
+ tid &= IMAP_TID_UPA;
+ } else {
+ tid = (starfire_translate(imap, goal_cpu) << 26);
+ tid &= IMAP_TID_UPA;
+ }
+ upa_writel(tid | IMAP_VALID, imap);
+
+ while (!cpu_online(goal_cpu)) {
+ if (++goal_cpu >= NR_CPUS)
+ goal_cpu = 0;
+ }
+
+ return goal_cpu;
+}
+
+/* Called from request_irq. */
+static void distribute_irqs(void)
+{
+ unsigned long flags;
+ int cpu, level;
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+ cpu = 0;
+
+ /*
+ * Skip the timer at [0], and very rare error/power intrs at [15].
+ * Also level [12], it causes problems on Ex000 systems.
+ */
+ for (level = 1; level < NR_IRQS; level++) {
+ struct irqaction *p = irq_action[level];
+ if (level == 12) continue;
+ while(p) {
+ cpu = retarget_one_irq(p, cpu);
+ p = p->next;
+ }
+ }
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+}
+#endif
+
+
+struct sun5_timer *prom_timers;
+static u64 prom_limit0, prom_limit1;
+
+static void map_prom_timers(void)
+{
+ unsigned int addr[3];
+ int tnode, err;
+
+ /* PROM timer node hangs out in the top level of device siblings... */
+ tnode = prom_finddevice("/counter-timer");
+
+ /* Assume if node is not present, PROM uses different tick mechanism
+ * which we should not care about.
+ */
+ if (tnode == 0 || tnode == -1) {
+ prom_timers = (struct sun5_timer *) 0;
+ return;
+ }
+
+ /* If PROM is really using this, it must be mapped by him. */
+ err = prom_getproperty(tnode, "address", (char *)addr, sizeof(addr));
+ if (err == -1) {
+ prom_printf("PROM does not have timer mapped, trying to continue.\n");
+ prom_timers = (struct sun5_timer *) 0;
+ return;
+ }
+ prom_timers = (struct sun5_timer *) ((unsigned long)addr[0]);
+}
+
+static void kill_prom_timer(void)
+{
+ if (!prom_timers)
+ return;
+
+ /* Save them away for later. */
+ prom_limit0 = prom_timers->limit0;
+ prom_limit1 = prom_timers->limit1;
+
+ /* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14.
+ * We turn both off here just to be paranoid.
+ */
+ prom_timers->limit0 = 0;
+ prom_timers->limit1 = 0;
+
+ /* Wheee, eat the interrupt packet too... */
+ __asm__ __volatile__(
+" mov 0x40, %%g2\n"
+" ldxa [%%g0] %0, %%g1\n"
+" ldxa [%%g2] %1, %%g1\n"
+" stxa %%g0, [%%g0] %0\n"
+" membar #Sync\n"
+ : /* no outputs */
+ : "i" (ASI_INTR_RECEIVE), "i" (ASI_INTR_R)
+ : "g1", "g2");
+}
+
+void enable_prom_timer(void)
+{
+ if (!prom_timers)
+ return;
+
+ /* Set it to whatever was there before. */
+ prom_timers->limit1 = prom_limit1;
+ prom_timers->count1 = 0;
+ prom_timers->limit0 = prom_limit0;
+ prom_timers->count0 = 0;
+}
+
+void init_irqwork_curcpu(void)
+{
+ register struct irq_work_struct *workp asm("o2");
+ register unsigned long tmp asm("o3");
+ int cpu = hard_smp_processor_id();
+
+ memset(__irq_work + cpu, 0, sizeof(*workp));
+
+ /* Make sure we are called with PSTATE_IE disabled. */
+ __asm__ __volatile__("rdpr %%pstate, %0\n\t"
+ : "=r" (tmp));
+ if (tmp & PSTATE_IE) {
+ prom_printf("BUG: init_irqwork_curcpu() called with "
+ "PSTATE_IE enabled, bailing.\n");
+ __asm__ __volatile__("mov %%i7, %0\n\t"
+ : "=r" (tmp));
+ prom_printf("BUG: Called from %lx\n", tmp);
+ prom_halt();
+ }
+
+ /* Set interrupt globals. */
+ workp = &__irq_work[cpu];
+ __asm__ __volatile__(
+ "rdpr %%pstate, %0\n\t"
+ "wrpr %0, %1, %%pstate\n\t"
+ "mov %2, %%g6\n\t"
+ "wrpr %0, 0x0, %%pstate\n\t"
+ : "=&r" (tmp)
+ : "i" (PSTATE_IG), "r" (workp));
+}
+
+/* Only invoked on boot processor. */
+void __init init_IRQ(void)
+{
+ map_prom_timers();
+ kill_prom_timer();
+ memset(&ivector_table[0], 0, sizeof(ivector_table));
+
+ /* We need to clear any IRQ's pending in the soft interrupt
+ * registers, a spurious one could be left around from the
+ * PROM timer which we just disabled.
+ */
+ clear_softint(get_softint());
+
+ /* Now that ivector table is initialized, it is safe
+ * to receive IRQ vector traps. We will normally take
+ * one or two right now, in case some device PROM used
+ * to boot us wants to speak to us. We just ignore them.
+ */
+ __asm__ __volatile__("rdpr %%pstate, %%g1\n\t"
+ "or %%g1, %0, %%g1\n\t"
+ "wrpr %%g1, 0x0, %%pstate"
+ : /* No outputs */
+ : "i" (PSTATE_IE)
+ : "g1");
+}
+
+static struct proc_dir_entry * root_irq_dir;
+static struct proc_dir_entry * irq_dir [NUM_IVECS];
+
+#ifdef CONFIG_SMP
+
+static int irq_affinity_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct ino_bucket *bp = ivector_table + (long)data;
+ struct irqaction *ap = bp->irq_info;
+ cpumask_t mask;
+ int len;
+
+ mask = get_smpaff_in_irqaction(ap);
+ if (cpus_empty(mask))
+ mask = cpu_online_map;
+
+ len = cpumask_scnprintf(page, count, mask);
+ if (count - len < 2)
+ return -EINVAL;
+ len += sprintf(page + len, "\n");
+ return len;
+}
+
+static inline void set_intr_affinity(int irq, cpumask_t hw_aff)
+{
+ struct ino_bucket *bp = ivector_table + irq;
+
+ /* Users specify affinity in terms of hw cpu ids.
+ * As soon as we do this, handler_irq() might see and take action.
+ */
+ put_smpaff_in_irqaction((struct irqaction *)bp->irq_info, hw_aff);
+
+ /* Migration is simply done by the next cpu to service this
+ * interrupt.
+ */
+}
+
+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;
+
+ err = cpumask_parse(buffer, count, new_value);
+
+ /*
+ * 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.
+ */
+ cpus_and(new_value, new_value, cpu_online_map);
+ if (cpus_empty(new_value))
+ return -EINVAL;
+
+ set_intr_affinity(irq, new_value);
+
+ return full_count;
+}
+
+#endif
+
+#define MAX_NAMELEN 10
+
+static void register_irq_proc (unsigned int irq)
+{
+ char name [MAX_NAMELEN];
+
+ if (!root_irq_dir || irq_dir[irq])
+ return;
+
+ memset(name, 0, MAX_NAMELEN);
+ sprintf(name, "%x", irq);
+
+ /* create /proc/irq/1234 */
+ irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+
+#ifdef CONFIG_SMP
+ /* XXX SMP affinity not supported on starfire yet. */
+ if (this_is_starfire == 0) {
+ 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;
+ }
+ }
+#endif
+}
+
+void init_irq_proc (void)
+{
+ /* create /proc/irq */
+ root_irq_dir = proc_mkdir("irq", NULL);
+}
+
diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c
new file mode 100644
index 000000000000..30862abee611
--- /dev/null
+++ b/arch/sparc64/kernel/isa.c
@@ -0,0 +1,329 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/oplib.h>
+#include <asm/isa.h>
+
+struct sparc_isa_bridge *isa_chain;
+
+static void __init fatal_err(const char *reason)
+{
+ prom_printf("ISA: fatal error, %s.\n", reason);
+}
+
+static void __init report_dev(struct sparc_isa_device *isa_dev, int child)
+{
+ if (child)
+ printk(" (%s)", isa_dev->prom_name);
+ else
+ printk(" [%s", isa_dev->prom_name);
+}
+
+static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev,
+ struct linux_prom_registers *pregs,
+ int pregs_size)
+{
+ unsigned long base, len;
+ int prop_len;
+
+ prop_len = prom_getproperty(isa_dev->prom_node, "reg",
+ (char *) pregs, pregs_size);
+
+ if (prop_len <= 0)
+ return;
+
+ /* Only the first one is interesting. */
+ len = pregs[0].reg_size;
+ base = (((unsigned long)pregs[0].which_io << 32) |
+ (unsigned long)pregs[0].phys_addr);
+ base += isa_dev->bus->parent->io_space.start;
+
+ isa_dev->resource.start = base;
+ isa_dev->resource.end = (base + len - 1UL);
+ isa_dev->resource.flags = IORESOURCE_IO;
+ isa_dev->resource.name = isa_dev->prom_name;
+
+ request_resource(&isa_dev->bus->parent->io_space,
+ &isa_dev->resource);
+}
+
+/* I can't believe they didn't put a real INO in the isa device
+ * interrupts property. The whole point of the OBP properties
+ * is to shield the kernel from IRQ routing details.
+ *
+ * The P1275 standard for ISA devices seems to also have been
+ * totally ignored.
+ *
+ * On later systems, an interrupt-map and interrupt-map-mask scheme
+ * akin to EBUS is used.
+ */
+static struct {
+ int obp_irq;
+ int pci_ino;
+} grover_irq_table[] = {
+ { 1, 0x00 }, /* dma, unknown ino at this point */
+ { 2, 0x27 }, /* floppy */
+ { 3, 0x22 }, /* parallel */
+ { 4, 0x2b }, /* serial */
+ { 5, 0x25 }, /* acpi power management */
+
+ { 0, 0x00 } /* end of table */
+};
+
+static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev,
+ struct sparc_isa_bridge *isa_br,
+ int *interrupt,
+ struct linux_prom_registers *pregs)
+{
+ unsigned int hi, lo, irq;
+ int i;
+
+ hi = pregs->which_io & isa_br->isa_intmask.phys_hi;
+ lo = pregs->phys_addr & isa_br->isa_intmask.phys_lo;
+ irq = *interrupt & isa_br->isa_intmask.interrupt;
+ for (i = 0; i < isa_br->num_isa_intmap; i++) {
+ if ((isa_br->isa_intmap[i].phys_hi == hi) &&
+ (isa_br->isa_intmap[i].phys_lo == lo) &&
+ (isa_br->isa_intmap[i].interrupt == irq)) {
+ *interrupt = isa_br->isa_intmap[i].cinterrupt;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev,
+ struct linux_prom_registers *pregs)
+{
+ int irq_prop;
+
+ irq_prop = prom_getintdefault(isa_dev->prom_node,
+ "interrupts", -1);
+ if (irq_prop <= 0) {
+ goto no_irq;
+ } else {
+ struct pci_controller_info *pcic;
+ struct pci_pbm_info *pbm;
+ int i;
+
+ if (isa_dev->bus->num_isa_intmap) {
+ if (!isa_dev_get_irq_using_imap(isa_dev,
+ isa_dev->bus,
+ &irq_prop,
+ pregs))
+ goto route_irq;
+ }
+
+ for (i = 0; grover_irq_table[i].obp_irq != 0; i++) {
+ if (grover_irq_table[i].obp_irq == irq_prop) {
+ int ino = grover_irq_table[i].pci_ino;
+
+ if (ino == 0)
+ goto no_irq;
+
+ irq_prop = ino;
+ goto route_irq;
+ }
+ }
+ goto no_irq;
+
+route_irq:
+ pbm = isa_dev->bus->parent;
+ pcic = pbm->parent;
+ isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop);
+ return;
+ }
+
+no_irq:
+ isa_dev->irq = PCI_IRQ_NONE;
+}
+
+static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev)
+{
+ int node = prom_getchild(parent_isa_dev->prom_node);
+
+ if (node == 0)
+ return;
+
+ printk(" ->");
+ while (node != 0) {
+ struct linux_prom_registers regs[PROMREG_MAX];
+ struct sparc_isa_device *isa_dev;
+ int prop_len;
+
+ isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
+ if (!isa_dev) {
+ fatal_err("cannot allocate child isa_dev");
+ prom_halt();
+ }
+
+ memset(isa_dev, 0, sizeof(*isa_dev));
+
+ /* Link it in to parent. */
+ isa_dev->next = parent_isa_dev->child;
+ parent_isa_dev->child = isa_dev;
+
+ isa_dev->bus = parent_isa_dev->bus;
+ isa_dev->prom_node = node;
+ prop_len = prom_getproperty(node, "name",
+ (char *) isa_dev->prom_name,
+ sizeof(isa_dev->prom_name));
+ if (prop_len <= 0) {
+ fatal_err("cannot get child isa_dev OBP node name");
+ prom_halt();
+ }
+
+ prop_len = prom_getproperty(node, "compatible",
+ (char *) isa_dev->compatible,
+ sizeof(isa_dev->compatible));
+
+ /* Not having this is OK. */
+ if (prop_len <= 0)
+ isa_dev->compatible[0] = '\0';
+
+ isa_dev_get_resource(isa_dev, regs, sizeof(regs));
+ isa_dev_get_irq(isa_dev, regs);
+
+ report_dev(isa_dev, 1);
+
+ node = prom_getsibling(node);
+ }
+}
+
+static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br)
+{
+ int node = prom_getchild(isa_br->prom_node);
+
+ while (node != 0) {
+ struct linux_prom_registers regs[PROMREG_MAX];
+ struct sparc_isa_device *isa_dev;
+ int prop_len;
+
+ isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
+ if (!isa_dev) {
+ fatal_err("cannot allocate isa_dev");
+ prom_halt();
+ }
+
+ memset(isa_dev, 0, sizeof(*isa_dev));
+
+ /* Link it in. */
+ isa_dev->next = NULL;
+ if (isa_br->devices == NULL) {
+ isa_br->devices = isa_dev;
+ } else {
+ struct sparc_isa_device *tmp = isa_br->devices;
+
+ while (tmp->next)
+ tmp = tmp->next;
+
+ tmp->next = isa_dev;
+ }
+
+ isa_dev->bus = isa_br;
+ isa_dev->prom_node = node;
+ prop_len = prom_getproperty(node, "name",
+ (char *) isa_dev->prom_name,
+ sizeof(isa_dev->prom_name));
+ if (prop_len <= 0) {
+ fatal_err("cannot get isa_dev OBP node name");
+ prom_halt();
+ }
+
+ prop_len = prom_getproperty(node, "compatible",
+ (char *) isa_dev->compatible,
+ sizeof(isa_dev->compatible));
+
+ /* Not having this is OK. */
+ if (prop_len <= 0)
+ isa_dev->compatible[0] = '\0';
+
+ isa_dev_get_resource(isa_dev, regs, sizeof(regs));
+ isa_dev_get_irq(isa_dev, regs);
+
+ report_dev(isa_dev, 0);
+
+ isa_fill_children(isa_dev);
+
+ printk("]");
+
+ node = prom_getsibling(node);
+ }
+}
+
+void __init isa_init(void)
+{
+ struct pci_dev *pdev;
+ unsigned short vendor, device;
+ int index = 0;
+
+ vendor = PCI_VENDOR_ID_AL;
+ device = PCI_DEVICE_ID_AL_M1533;
+
+ pdev = NULL;
+ while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
+ struct pcidev_cookie *pdev_cookie;
+ struct pci_pbm_info *pbm;
+ struct sparc_isa_bridge *isa_br;
+ int prop_len;
+
+ pdev_cookie = pdev->sysdata;
+ if (!pdev_cookie) {
+ printk("ISA: Warning, ISA bridge ignored due to "
+ "lack of OBP data.\n");
+ continue;
+ }
+ pbm = pdev_cookie->pbm;
+
+ isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL);
+ if (!isa_br) {
+ fatal_err("cannot allocate sparc_isa_bridge");
+ prom_halt();
+ }
+
+ memset(isa_br, 0, sizeof(*isa_br));
+
+ /* Link it in. */
+ isa_br->next = isa_chain;
+ isa_chain = isa_br;
+
+ isa_br->parent = pbm;
+ isa_br->self = pdev;
+ isa_br->index = index++;
+ isa_br->prom_node = pdev_cookie->prom_node;
+ strncpy(isa_br->prom_name, pdev_cookie->prom_name,
+ sizeof(isa_br->prom_name));
+
+ prop_len = prom_getproperty(isa_br->prom_node,
+ "ranges",
+ (char *) isa_br->isa_ranges,
+ sizeof(isa_br->isa_ranges));
+ if (prop_len <= 0)
+ isa_br->num_isa_ranges = 0;
+ else
+ isa_br->num_isa_ranges =
+ (prop_len / sizeof(struct linux_prom_isa_ranges));
+
+ prop_len = prom_getproperty(isa_br->prom_node,
+ "interrupt-map",
+ (char *) isa_br->isa_intmap,
+ sizeof(isa_br->isa_intmap));
+ if (prop_len <= 0)
+ isa_br->num_isa_intmap = 0;
+ else
+ isa_br->num_isa_intmap =
+ (prop_len / sizeof(struct linux_prom_isa_intmap));
+
+ prop_len = prom_getproperty(isa_br->prom_node,
+ "interrupt-map-mask",
+ (char *) &(isa_br->isa_intmask),
+ sizeof(isa_br->isa_intmask));
+
+ printk("isa%d:", isa_br->index);
+
+ isa_fill_devices(isa_br);
+
+ printk("\n");
+ }
+}
diff --git a/arch/sparc64/kernel/itlb_base.S b/arch/sparc64/kernel/itlb_base.S
new file mode 100644
index 000000000000..b5e32dfa4fbc
--- /dev/null
+++ b/arch/sparc64/kernel/itlb_base.S
@@ -0,0 +1,83 @@
+/* $Id: itlb_base.S,v 1.12 2002/02/09 19:49:30 davem Exp $
+ * itlb_base.S: Front end to ITLB miss replacement strategy.
+ * This is included directly into the trap table.
+ *
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#if PAGE_SHIFT == 13
+/*
+ * To compute vpte offset, we need to do ((addr >> 13) << 3),
+ * which can be optimized to (addr >> 10) if bits 10/11/12 can
+ * be guaranteed to be 0 ... mmu_context.h does guarantee this
+ * by only using 10 bits in the hwcontext value.
+ */
+#define CREATE_VPTE_OFFSET1(r1, r2) \
+ srax r1, 10, r2
+#define CREATE_VPTE_OFFSET2(r1, r2)
+#define CREATE_VPTE_NOP nop
+#else /* PAGE_SHIFT */
+#define CREATE_VPTE_OFFSET1(r1, r2) \
+ srax r1, PAGE_SHIFT, r2
+#define CREATE_VPTE_OFFSET2(r1, r2) \
+ sllx r2, 3, r2
+#define CREATE_VPTE_NOP
+#endif /* PAGE_SHIFT */
+
+
+/* Ways we can get here:
+ *
+ * 1) Nucleus instruction misses from module code.
+ * 2) All user instruction misses.
+ *
+ * All real page faults merge their code paths to the
+ * sparc64_realfault_common label below.
+ */
+
+/* ITLB ** ICACHE line 1: Quick user TLB misses */
+ ldxa [%g1 + %g1] ASI_IMMU, %g4 ! Get TAG_ACCESS
+ CREATE_VPTE_OFFSET1(%g4, %g6) ! Create VPTE offset
+ CREATE_VPTE_OFFSET2(%g4, %g6) ! Create VPTE offset
+ ldxa [%g3 + %g6] ASI_P, %g5 ! Load VPTE
+1: brgez,pn %g5, 3f ! Not valid, branch out
+ sethi %hi(_PAGE_EXEC), %g4 ! Delay-slot
+ andcc %g5, %g4, %g0 ! Executable?
+ be,pn %xcc, 3f ! Nope, branch.
+ nop ! Delay-slot
+2: stxa %g5, [%g0] ASI_ITLB_DATA_IN ! Load PTE into TLB
+ retry ! Trap return
+3: rdpr %pstate, %g4 ! Move into alternate globals
+
+/* ITLB ** ICACHE line 2: Real faults */
+ wrpr %g4, PSTATE_AG|PSTATE_MG, %pstate
+ rdpr %tpc, %g5 ! And load faulting VA
+ mov FAULT_CODE_ITLB, %g4 ! It was read from ITLB
+sparc64_realfault_common: ! Called by TL0 dtlb_miss too
+ stb %g4, [%g6 + TI_FAULT_CODE]
+ stx %g5, [%g6 + TI_FAULT_ADDR]
+ ba,pt %xcc, etrap ! Save state
+1: rd %pc, %g7 ! ...
+ nop
+
+/* ITLB ** ICACHE line 3: Finish faults + window fixups */
+ call do_sparc64_fault ! Call fault handler
+ add %sp, PTREGS_OFF, %o0! Compute pt_regs arg
+ ba,pt %xcc, rtrap_clr_l6 ! Restore cpu state
+ nop
+winfix_trampoline:
+ rdpr %tpc, %g3 ! Prepare winfixup TNPC
+ or %g3, 0x7c, %g3 ! Compute offset to branch
+ wrpr %g3, %tnpc ! Write it into TNPC
+ done ! Do it to it
+
+/* ITLB ** ICACHE line 4: Unused... */
+ nop
+ nop
+ nop
+ nop
+ CREATE_VPTE_NOP
+
+#undef CREATE_VPTE_OFFSET1
+#undef CREATE_VPTE_OFFSET2
+#undef CREATE_VPTE_NOP
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c
new file mode 100644
index 000000000000..7066d7ba667a
--- /dev/null
+++ b/arch/sparc64/kernel/kprobes.c
@@ -0,0 +1,394 @@
+/* arch/sparc64/kernel/kprobes.c
+ *
+ * Copyright (C) 2004 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include <asm/kdebug.h>
+#include <asm/signal.h>
+
+/* We do not have hardware single-stepping on sparc64.
+ * So we implement software single-stepping with breakpoint
+ * traps. The top-level scheme is similar to that used
+ * in the x86 kprobes implementation.
+ *
+ * In the kprobe->ainsn.insn[] array we store the original
+ * instruction at index zero and a break instruction at
+ * index one.
+ *
+ * When we hit a kprobe we:
+ * - Run the pre-handler
+ * - Remember "regs->tnpc" and interrupt level stored in
+ * "regs->tstate" so we can restore them later
+ * - Disable PIL interrupts
+ * - Set regs->tpc to point to kprobe->ainsn.insn[0]
+ * - Set regs->tnpc to point to kprobe->ainsn.insn[1]
+ * - Mark that we are actively in a kprobe
+ *
+ * At this point we wait for the second breakpoint at
+ * kprobe->ainsn.insn[1] to hit. When it does we:
+ * - Run the post-handler
+ * - Set regs->tpc to "remembered" regs->tnpc stored above,
+ * restore the PIL interrupt level in "regs->tstate" as well
+ * - Make any adjustments necessary to regs->tnpc in order
+ * to handle relative branches correctly. See below.
+ * - Mark that we are no longer actively in a kprobe.
+ */
+
+int arch_prepare_kprobe(struct kprobe *p)
+{
+ return 0;
+}
+
+void arch_copy_kprobe(struct kprobe *p)
+{
+ p->ainsn.insn[0] = *p->addr;
+ p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2;
+}
+
+void arch_remove_kprobe(struct kprobe *p)
+{
+}
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE 0x00000001
+#define KPROBE_HIT_SS 0x00000002
+
+static struct kprobe *current_kprobe;
+static unsigned long current_kprobe_orig_tnpc;
+static unsigned long current_kprobe_orig_tstate_pil;
+static unsigned int kprobe_status;
+
+static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ current_kprobe_orig_tnpc = regs->tnpc;
+ current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
+ regs->tstate |= TSTATE_PIL;
+
+ /*single step inline, if it a breakpoint instruction*/
+ if (p->opcode == BREAKPOINT_INSTRUCTION) {
+ regs->tpc = (unsigned long) p->addr;
+ regs->tnpc = current_kprobe_orig_tnpc;
+ } else {
+ regs->tpc = (unsigned long) &p->ainsn.insn[0];
+ regs->tnpc = (unsigned long) &p->ainsn.insn[1];
+ }
+}
+
+static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
+{
+ *p->addr = p->opcode;
+ flushi(p->addr);
+
+ regs->tpc = (unsigned long) p->addr;
+ regs->tnpc = current_kprobe_orig_tnpc;
+ regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
+ current_kprobe_orig_tstate_pil);
+}
+
+static int kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p;
+ void *addr = (void *) regs->tpc;
+ int ret = 0;
+
+ preempt_disable();
+
+ if (kprobe_running()) {
+ /* We *are* holding lock here, so this is safe.
+ * Disarm the probe we just hit, and ignore it.
+ */
+ p = get_kprobe(addr);
+ if (p) {
+ if (kprobe_status == KPROBE_HIT_SS) {
+ regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
+ current_kprobe_orig_tstate_pil);
+ unlock_kprobes();
+ goto no_kprobe;
+ }
+ disarm_kprobe(p, regs);
+ ret = 1;
+ } else {
+ p = current_kprobe;
+ if (p->break_handler && p->break_handler(p, regs))
+ goto ss_probe;
+ }
+ /* If it's not ours, can't be delete race, (we hold lock). */
+ goto no_kprobe;
+ }
+
+ lock_kprobes();
+ p = get_kprobe(addr);
+ if (!p) {
+ unlock_kprobes();
+ if (*(u32 *)addr != BREAKPOINT_INSTRUCTION) {
+ /*
+ * The breakpoint instruction was removed right
+ * after we hit it. Another cpu has removed
+ * either a probepoint or a debugger breakpoint
+ * at this address. In either case, no further
+ * handling of this interrupt is appropriate.
+ */
+ ret = 1;
+ }
+ /* Not one of ours: let kernel handle it */
+ goto no_kprobe;
+ }
+
+ kprobe_status = KPROBE_HIT_ACTIVE;
+ current_kprobe = p;
+ if (p->pre_handler && p->pre_handler(p, regs))
+ return 1;
+
+ss_probe:
+ prepare_singlestep(p, regs);
+ kprobe_status = KPROBE_HIT_SS;
+ return 1;
+
+no_kprobe:
+ preempt_enable_no_resched();
+ return ret;
+}
+
+/* If INSN is a relative control transfer instruction,
+ * return the corrected branch destination value.
+ *
+ * The original INSN location was REAL_PC, it actually
+ * executed at PC and produced destination address NPC.
+ */
+static unsigned long relbranch_fixup(u32 insn, unsigned long real_pc,
+ unsigned long pc, unsigned long npc)
+{
+ /* Branch not taken, no mods necessary. */
+ if (npc == pc + 0x4UL)
+ return real_pc + 0x4UL;
+
+ /* The three cases are call, branch w/prediction,
+ * and traditional branch.
+ */
+ if ((insn & 0xc0000000) == 0x40000000 ||
+ (insn & 0xc1c00000) == 0x00400000 ||
+ (insn & 0xc1c00000) == 0x00800000) {
+ /* The instruction did all the work for us
+ * already, just apply the offset to the correct
+ * instruction location.
+ */
+ return (real_pc + (npc - pc));
+ }
+
+ return real_pc + 0x4UL;
+}
+
+/* If INSN is an instruction which writes it's PC location
+ * into a destination register, fix that up.
+ */
+static void retpc_fixup(struct pt_regs *regs, u32 insn, unsigned long real_pc)
+{
+ unsigned long *slot = NULL;
+
+ /* Simplest cast is call, which always uses %o7 */
+ if ((insn & 0xc0000000) == 0x40000000) {
+ slot = &regs->u_regs[UREG_I7];
+ }
+
+ /* Jmpl encodes the register inside of the opcode */
+ if ((insn & 0xc1f80000) == 0x81c00000) {
+ unsigned long rd = ((insn >> 25) & 0x1f);
+
+ if (rd <= 15) {
+ slot = &regs->u_regs[rd];
+ } else {
+ /* Hard case, it goes onto the stack. */
+ flushw_all();
+
+ rd -= 16;
+ slot = (unsigned long *)
+ (regs->u_regs[UREG_FP] + STACK_BIAS);
+ slot += rd;
+ }
+ }
+ if (slot != NULL)
+ *slot = real_pc;
+}
+
+/*
+ * Called after single-stepping. p->addr is the address of the
+ * instruction whose first byte has been replaced by the breakpoint
+ * instruction. To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction. The address of this
+ * copy is p->ainsn.insn.
+ *
+ * This function prepares to return from the post-single-step
+ * breakpoint trap.
+ */
+static void resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+ u32 insn = p->ainsn.insn[0];
+
+ regs->tpc = current_kprobe_orig_tnpc;
+ regs->tnpc = relbranch_fixup(insn,
+ (unsigned long) p->addr,
+ (unsigned long) &p->ainsn.insn[0],
+ regs->tnpc);
+ retpc_fixup(regs, insn, (unsigned long) p->addr);
+
+ regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
+ current_kprobe_orig_tstate_pil);
+}
+
+static inline int post_kprobe_handler(struct pt_regs *regs)
+{
+ if (!kprobe_running())
+ return 0;
+
+ if (current_kprobe->post_handler)
+ current_kprobe->post_handler(current_kprobe, regs, 0);
+
+ resume_execution(current_kprobe, regs);
+
+ unlock_kprobes();
+ preempt_enable_no_resched();
+
+ return 1;
+}
+
+/* Interrupts disabled, kprobe_lock held. */
+static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ if (current_kprobe->fault_handler
+ && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
+ return 1;
+
+ if (kprobe_status & KPROBE_HIT_SS) {
+ resume_execution(current_kprobe, regs);
+
+ unlock_kprobes();
+ preempt_enable_no_resched();
+ }
+ return 0;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ struct die_args *args = (struct die_args *)data;
+ switch (val) {
+ case DIE_DEBUG:
+ if (kprobe_handler(args->regs))
+ return NOTIFY_STOP;
+ break;
+ case DIE_DEBUG_2:
+ if (post_kprobe_handler(args->regs))
+ return NOTIFY_STOP;
+ break;
+ case DIE_GPF:
+ if (kprobe_running() &&
+ kprobe_fault_handler(args->regs, args->trapnr))
+ return NOTIFY_STOP;
+ break;
+ case DIE_PAGE_FAULT:
+ if (kprobe_running() &&
+ kprobe_fault_handler(args->regs, args->trapnr))
+ return NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+asmlinkage void kprobe_trap(unsigned long trap_level, struct pt_regs *regs)
+{
+ BUG_ON(trap_level != 0x170 && trap_level != 0x171);
+
+ if (user_mode(regs)) {
+ local_irq_enable();
+ bad_trap(regs, trap_level);
+ return;
+ }
+
+ /* trap_level == 0x170 --> ta 0x70
+ * trap_level == 0x171 --> ta 0x71
+ */
+ if (notify_die((trap_level == 0x170) ? DIE_DEBUG : DIE_DEBUG_2,
+ (trap_level == 0x170) ? "debug" : "debug_2",
+ regs, 0, trap_level, SIGTRAP) != NOTIFY_STOP)
+ bad_trap(regs, trap_level);
+}
+
+/* Jprobes support. */
+static struct pt_regs jprobe_saved_regs;
+static struct pt_regs *jprobe_saved_regs_location;
+static struct sparc_stackf jprobe_saved_stack;
+
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+ jprobe_saved_regs_location = regs;
+ memcpy(&jprobe_saved_regs, regs, sizeof(*regs));
+
+ /* Save a whole stack frame, this gets arguments
+ * pushed onto the stack after using up all the
+ * arg registers.
+ */
+ memcpy(&jprobe_saved_stack,
+ (char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
+ sizeof(jprobe_saved_stack));
+
+ regs->tpc = (unsigned long) jp->entry;
+ regs->tnpc = ((unsigned long) jp->entry) + 0x4UL;
+ regs->tstate |= TSTATE_PIL;
+
+ return 1;
+}
+
+void jprobe_return(void)
+{
+ preempt_enable_no_resched();
+ __asm__ __volatile__(
+ ".globl jprobe_return_trap_instruction\n"
+"jprobe_return_trap_instruction:\n\t"
+ "ta 0x70");
+}
+
+extern void jprobe_return_trap_instruction(void);
+
+extern void __show_regs(struct pt_regs * regs);
+
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ u32 *addr = (u32 *) regs->tpc;
+
+ if (addr == (u32 *) jprobe_return_trap_instruction) {
+ if (jprobe_saved_regs_location != regs) {
+ printk("JPROBE: Current regs (%p) does not match "
+ "saved regs (%p).\n",
+ regs, jprobe_saved_regs_location);
+ printk("JPROBE: Saved registers\n");
+ __show_regs(jprobe_saved_regs_location);
+ printk("JPROBE: Current registers\n");
+ __show_regs(regs);
+ BUG();
+ }
+ /* Restore old register state. Do pt_regs
+ * first so that UREG_FP is the original one for
+ * the stack frame restore.
+ */
+ memcpy(regs, &jprobe_saved_regs, sizeof(*regs));
+
+ memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
+ &jprobe_saved_stack,
+ sizeof(jprobe_saved_stack));
+
+ return 1;
+ }
+ return 0;
+}
diff --git a/arch/sparc64/kernel/module.c b/arch/sparc64/kernel/module.c
new file mode 100644
index 000000000000..6c83e372f75d
--- /dev/null
+++ b/arch/sparc64/kernel/module.c
@@ -0,0 +1,209 @@
+/* Kernel module help for sparc64.
+ *
+ * Copyright (C) 2001 Rusty Russell.
+ * Copyright (C) 2002 David S. Miller.
+ */
+
+#include <linux/moduleloader.h>
+#include <linux/kernel.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include <asm/processor.h>
+#include <asm/spitfire.h>
+
+static void *module_map(unsigned long size)
+{
+ struct vm_struct *area;
+
+ size = PAGE_ALIGN(size);
+ if (!size || size > MODULES_LEN)
+ return NULL;
+
+ area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
+ if (!area)
+ return NULL;
+
+ return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL);
+}
+
+void *module_alloc(unsigned long size)
+{
+ void *ret;
+
+ /* We handle the zero case fine, unlike vmalloc */
+ if (size == 0)
+ return NULL;
+
+ ret = module_map(size);
+ if (!ret)
+ ret = ERR_PTR(-ENOMEM);
+ else
+ memset(ret, 0, size);
+
+ return ret;
+}
+
+/* Free memory returned from module_core_alloc/module_init_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+ vfree(module_region);
+ /* FIXME: If module_region == mod->init_region, trim exception
+ table entries. */
+}
+
+/* Make generic code ignore STT_REGISTER dummy undefined symbols. */
+int module_frob_arch_sections(Elf_Ehdr *hdr,
+ Elf_Shdr *sechdrs,
+ char *secstrings,
+ struct module *mod)
+{
+ unsigned int symidx;
+ Elf64_Sym *sym;
+ const char *strtab;
+ int i;
+
+ for (symidx = 0; sechdrs[symidx].sh_type != SHT_SYMTAB; symidx++) {
+ if (symidx == hdr->e_shnum-1) {
+ printk("%s: no symtab found.\n", mod->name);
+ return -ENOEXEC;
+ }
+ }
+ sym = (Elf64_Sym *)sechdrs[symidx].sh_addr;
+ strtab = (char *)sechdrs[sechdrs[symidx].sh_link].sh_addr;
+
+ for (i = 1; i < sechdrs[symidx].sh_size / sizeof(Elf_Sym); i++) {
+ if (sym[i].st_shndx == SHN_UNDEF &&
+ ELF64_ST_TYPE(sym[i].st_info) == STT_REGISTER)
+ sym[i].st_shndx = SHN_ABS;
+ }
+ 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: non-ADD 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)
+{
+ unsigned int i;
+ Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+ Elf64_Sym *sym;
+ u8 *location;
+ u32 *loc32;
+
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+ Elf64_Addr v;
+
+ /* This is where to make the change */
+ location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rel[i].r_offset;
+ loc32 = (u32 *) location;
+
+ BUG_ON(((u64)location >> (u64)32) != (u64)0);
+
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
+ + ELF64_R_SYM(rel[i].r_info);
+ v = sym->st_value + rel[i].r_addend;
+
+ switch (ELF64_R_TYPE(rel[i].r_info) & 0xff) {
+ case R_SPARC_64:
+ location[0] = v >> 56;
+ location[1] = v >> 48;
+ location[2] = v >> 40;
+ location[3] = v >> 32;
+ location[4] = v >> 24;
+ location[5] = v >> 16;
+ location[6] = v >> 8;
+ location[7] = v >> 0;
+ break;
+
+ case R_SPARC_32:
+ location[0] = v >> 24;
+ location[1] = v >> 16;
+ location[2] = v >> 8;
+ location[3] = v >> 0;
+ break;
+
+ case R_SPARC_WDISP30:
+ v -= (Elf64_Addr) location;
+ *loc32 = (*loc32 & ~0x3fffffff) |
+ ((v >> 2) & 0x3fffffff);
+ break;
+
+ case R_SPARC_WDISP22:
+ v -= (Elf64_Addr) location;
+ *loc32 = (*loc32 & ~0x3fffff) |
+ ((v >> 2) & 0x3fffff);
+ break;
+
+ case R_SPARC_WDISP19:
+ v -= (Elf64_Addr) location;
+ *loc32 = (*loc32 & ~0x7ffff) |
+ ((v >> 2) & 0x7ffff);
+ break;
+
+ case R_SPARC_LO10:
+ *loc32 = (*loc32 & ~0x3ff) | (v & 0x3ff);
+ break;
+
+ case R_SPARC_HI22:
+ *loc32 = (*loc32 & ~0x3fffff) |
+ ((v >> 10) & 0x3fffff);
+ break;
+
+ case R_SPARC_OLO10:
+ *loc32 = (*loc32 & ~0x1fff) |
+ (((v & 0x3ff) +
+ (ELF64_R_TYPE(rel[i].r_info) >> 8))
+ & 0x1fff);
+ break;
+
+ default:
+ printk(KERN_ERR "module %s: Unknown relocation: %x\n",
+ me->name,
+ (int) (ELF64_R_TYPE(rel[i].r_info) & 0xff));
+ return -ENOEXEC;
+ };
+ }
+ return 0;
+}
+
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ /* Cheetah's I-cache is fully coherent. */
+ if (tlb_type == spitfire) {
+ unsigned long va;
+
+ flushw_all();
+ for (va = 0; va < (PAGE_SIZE << 1); va += 32)
+ spitfire_put_icache_tag(va, 0x0);
+ __asm__ __volatile__("flush %g6");
+ }
+
+ return 0;
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+}
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
new file mode 100644
index 000000000000..bba140d98b1b
--- /dev/null
+++ b/arch/sparc64/kernel/pci.c
@@ -0,0 +1,805 @@
+/* $Id: pci.c,v 1.39 2002/01/05 01:13:43 davem Exp $
+ * pci.c: UltraSparc PCI controller support.
+ *
+ * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/capability.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/pbm.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/ebus.h>
+#include <asm/isa.h>
+
+unsigned long pci_memspace_mask = 0xffffffffUL;
+
+#ifndef CONFIG_PCI
+/* A "nop" PCI implementation. */
+asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn,
+ unsigned long off, unsigned long len,
+ unsigned char *buf)
+{
+ return 0;
+}
+asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn,
+ unsigned long off, unsigned long len,
+ unsigned char *buf)
+{
+ return 0;
+}
+#else
+
+/* List of all PCI controllers found in the system. */
+struct pci_controller_info *pci_controller_root = NULL;
+
+/* Each PCI controller found gets a unique index. */
+int pci_num_controllers = 0;
+
+/* At boot time the user can give the kernel a command
+ * line option which controls if and how PCI devices
+ * are reordered at PCI bus probing time.
+ */
+int pci_device_reorder = 0;
+
+volatile int pci_poke_in_progress;
+volatile int pci_poke_cpu = -1;
+volatile int pci_poke_faulted;
+
+static DEFINE_SPINLOCK(pci_poke_lock);
+
+void pci_config_read8(u8 *addr, u8 *ret)
+{
+ unsigned long flags;
+ u8 byte;
+
+ spin_lock_irqsave(&pci_poke_lock, flags);
+ pci_poke_cpu = smp_processor_id();
+ pci_poke_in_progress = 1;
+ pci_poke_faulted = 0;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "lduba [%1] %2, %0\n\t"
+ "membar #Sync"
+ : "=r" (byte)
+ : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+ pci_poke_in_progress = 0;
+ pci_poke_cpu = -1;
+ if (!pci_poke_faulted)
+ *ret = byte;
+ spin_unlock_irqrestore(&pci_poke_lock, flags);
+}
+
+void pci_config_read16(u16 *addr, u16 *ret)
+{
+ unsigned long flags;
+ u16 word;
+
+ spin_lock_irqsave(&pci_poke_lock, flags);
+ pci_poke_cpu = smp_processor_id();
+ pci_poke_in_progress = 1;
+ pci_poke_faulted = 0;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "lduha [%1] %2, %0\n\t"
+ "membar #Sync"
+ : "=r" (word)
+ : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+ pci_poke_in_progress = 0;
+ pci_poke_cpu = -1;
+ if (!pci_poke_faulted)
+ *ret = word;
+ spin_unlock_irqrestore(&pci_poke_lock, flags);
+}
+
+void pci_config_read32(u32 *addr, u32 *ret)
+{
+ unsigned long flags;
+ u32 dword;
+
+ spin_lock_irqsave(&pci_poke_lock, flags);
+ pci_poke_cpu = smp_processor_id();
+ pci_poke_in_progress = 1;
+ pci_poke_faulted = 0;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "lduwa [%1] %2, %0\n\t"
+ "membar #Sync"
+ : "=r" (dword)
+ : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+ pci_poke_in_progress = 0;
+ pci_poke_cpu = -1;
+ if (!pci_poke_faulted)
+ *ret = dword;
+ spin_unlock_irqrestore(&pci_poke_lock, flags);
+}
+
+void pci_config_write8(u8 *addr, u8 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pci_poke_lock, flags);
+ pci_poke_cpu = smp_processor_id();
+ pci_poke_in_progress = 1;
+ pci_poke_faulted = 0;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stba %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+ pci_poke_in_progress = 0;
+ pci_poke_cpu = -1;
+ spin_unlock_irqrestore(&pci_poke_lock, flags);
+}
+
+void pci_config_write16(u16 *addr, u16 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pci_poke_lock, flags);
+ pci_poke_cpu = smp_processor_id();
+ pci_poke_in_progress = 1;
+ pci_poke_faulted = 0;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stha %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+ pci_poke_in_progress = 0;
+ pci_poke_cpu = -1;
+ spin_unlock_irqrestore(&pci_poke_lock, flags);
+}
+
+void pci_config_write32(u32 *addr, u32 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pci_poke_lock, flags);
+ pci_poke_cpu = smp_processor_id();
+ pci_poke_in_progress = 1;
+ pci_poke_faulted = 0;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stwa %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+ pci_poke_in_progress = 0;
+ pci_poke_cpu = -1;
+ spin_unlock_irqrestore(&pci_poke_lock, flags);
+}
+
+/* Probe for all PCI controllers in the system. */
+extern void sabre_init(int, char *);
+extern void psycho_init(int, char *);
+extern void schizo_init(int, char *);
+extern void schizo_plus_init(int, char *);
+extern void tomatillo_init(int, char *);
+
+static struct {
+ char *model_name;
+ void (*init)(int, char *);
+} pci_controller_table[] __initdata = {
+ { "SUNW,sabre", sabre_init },
+ { "pci108e,a000", sabre_init },
+ { "pci108e,a001", sabre_init },
+ { "SUNW,psycho", psycho_init },
+ { "pci108e,8000", psycho_init },
+ { "SUNW,schizo", schizo_init },
+ { "pci108e,8001", schizo_init },
+ { "SUNW,schizo+", schizo_plus_init },
+ { "pci108e,8002", schizo_plus_init },
+ { "SUNW,tomatillo", tomatillo_init },
+ { "pci108e,a801", tomatillo_init },
+};
+#define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \
+ sizeof(pci_controller_table[0]))
+
+static int __init pci_controller_init(char *model_name, int namelen, int node)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) {
+ if (!strncmp(model_name,
+ pci_controller_table[i].model_name,
+ namelen)) {
+ pci_controller_table[i].init(node, model_name);
+ return 1;
+ }
+ }
+ printk("PCI: Warning unknown controller, model name [%s]\n",
+ model_name);
+ printk("PCI: Ignoring controller...\n");
+
+ return 0;
+}
+
+static int __init pci_is_controller(char *model_name, int namelen, int node)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) {
+ if (!strncmp(model_name,
+ pci_controller_table[i].model_name,
+ namelen)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int __init pci_controller_scan(int (*handler)(char *, int, int))
+{
+ char namebuf[64];
+ int node;
+ int count = 0;
+
+ node = prom_getchild(prom_root_node);
+ while ((node = prom_searchsiblings(node, "pci")) != 0) {
+ int len;
+
+ if ((len = prom_getproperty(node, "model", namebuf, sizeof(namebuf))) > 0 ||
+ (len = prom_getproperty(node, "compatible", namebuf, sizeof(namebuf))) > 0) {
+ int item_len = 0;
+
+ /* Our value may be a multi-valued string in the
+ * case of some compatible properties. For sanity,
+ * only try the first one. */
+
+ while (namebuf[item_len] && len) {
+ len--;
+ item_len++;
+ }
+
+ if (handler(namebuf, item_len, node))
+ count++;
+ }
+
+ node = prom_getsibling(node);
+ if (!node)
+ break;
+ }
+
+ return count;
+}
+
+
+/* Is there some PCI controller in the system? */
+int __init pcic_present(void)
+{
+ return pci_controller_scan(pci_is_controller);
+}
+
+/* Find each controller in the system, attach and initialize
+ * software state structure for each and link into the
+ * pci_controller_root. Setup the controller enough such
+ * that bus scanning can be done.
+ */
+static void __init pci_controller_probe(void)
+{
+ printk("PCI: Probing for controllers.\n");
+
+ pci_controller_scan(pci_controller_init);
+}
+
+static void __init pci_scan_each_controller_bus(void)
+{
+ struct pci_controller_info *p;
+
+ for (p = pci_controller_root; p; p = p->next)
+ p->scan_bus(p);
+}
+
+/* Reorder the pci_dev chain, so that onboard devices come first
+ * and then come the pluggable cards.
+ */
+static void __init pci_reorder_devs(void)
+{
+ struct list_head *pci_onboard = &pci_devices;
+ struct list_head *walk = pci_onboard->next;
+
+ while (walk != pci_onboard) {
+ struct pci_dev *pdev = pci_dev_g(walk);
+ struct list_head *walk_next = walk->next;
+
+ if (pdev->irq && (__irq_ino(pdev->irq) & 0x20)) {
+ list_del(walk);
+ list_add(walk, pci_onboard);
+ }
+
+ walk = walk_next;
+ }
+}
+
+extern void clock_probe(void);
+extern void power_init(void);
+
+static int __init pcibios_init(void)
+{
+ pci_controller_probe();
+ if (pci_controller_root == NULL)
+ return 0;
+
+ pci_scan_each_controller_bus();
+
+ if (pci_device_reorder)
+ pci_reorder_devs();
+
+ isa_init();
+ ebus_init();
+ clock_probe();
+ power_init();
+
+ return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+void pcibios_fixup_bus(struct pci_bus *pbus)
+{
+ struct pci_pbm_info *pbm = pbus->sysdata;
+
+ /* Generic PCI bus probing sets these to point at
+ * &io{port,mem}_resouce which is wrong for us.
+ */
+ pbus->resource[0] = &pbm->io_space;
+ pbus->resource[1] = &pbm->mem_space;
+}
+
+int pci_claim_resource(struct pci_dev *pdev, int resource)
+{
+ struct pci_pbm_info *pbm = pdev->bus->sysdata;
+ struct resource *res = &pdev->resource[resource];
+ struct resource *root;
+
+ if (!pbm)
+ return -EINVAL;
+
+ if (res->flags & IORESOURCE_IO)
+ root = &pbm->io_space;
+ else
+ root = &pbm->mem_space;
+
+ pbm->parent->resource_adjust(pdev, res, root);
+
+ return request_resource(root, res);
+}
+
+/*
+ * Given the PCI bus a device resides on, try to
+ * find an acceptable resource allocation for a
+ * specific device resource..
+ */
+static int pci_assign_bus_resource(const struct pci_bus *bus,
+ struct pci_dev *dev,
+ struct resource *res,
+ unsigned long size,
+ unsigned long min,
+ int resno)
+{
+ unsigned int type_mask;
+ int i;
+
+ type_mask = IORESOURCE_IO | IORESOURCE_MEM;
+ for (i = 0 ; i < 4; i++) {
+ struct resource *r = bus->resource[i];
+ if (!r)
+ continue;
+
+ /* type_mask must match */
+ if ((res->flags ^ r->flags) & type_mask)
+ continue;
+
+ /* Ok, try it out.. */
+ if (allocate_resource(r, res, size, min, -1, size, NULL, NULL) < 0)
+ continue;
+
+ /* PCI config space updated by caller. */
+ return 0;
+ }
+ return -EBUSY;
+}
+
+int pci_assign_resource(struct pci_dev *pdev, int resource)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm = pcp->pbm;
+ struct resource *res = &pdev->resource[resource];
+ unsigned long min, size;
+ int err;
+
+ if (res->flags & IORESOURCE_IO)
+ min = pbm->io_space.start + 0x400UL;
+ else
+ min = pbm->mem_space.start;
+
+ size = res->end - res->start + 1;
+
+ err = pci_assign_bus_resource(pdev->bus, pdev, res, size, min, resource);
+
+ if (err < 0) {
+ printk("PCI: Failed to allocate resource %d for %s\n",
+ resource, pci_name(pdev));
+ } else {
+ /* Update PCI config space. */
+ pbm->parent->base_address_update(pdev, resource);
+ }
+
+ return err;
+}
+
+/* Sort resources by alignment */
+void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *r;
+ struct resource_list *list, *tmp;
+ unsigned long r_align;
+
+ r = &dev->resource[i];
+ r_align = r->end - r->start;
+
+ if (!(r->flags) || r->parent)
+ continue;
+ if (!r_align) {
+ printk(KERN_WARNING "PCI: Ignore bogus resource %d "
+ "[%lx:%lx] of %s\n",
+ i, r->start, r->end, pci_name(dev));
+ continue;
+ }
+ r_align = (i < PCI_BRIDGE_RESOURCES) ? r_align + 1 : r->start;
+ for (list = head; ; list = list->next) {
+ unsigned long align = 0;
+ struct resource_list *ln = list->next;
+ int idx;
+
+ if (ln) {
+ idx = ln->res - &ln->dev->resource[0];
+ align = (idx < PCI_BRIDGE_RESOURCES) ?
+ ln->res->end - ln->res->start + 1 :
+ ln->res->start;
+ }
+ if (r_align > align) {
+ tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ panic("pdev_sort_resources(): "
+ "kmalloc() failed!\n");
+ tmp->next = ln;
+ tmp->res = r;
+ tmp->dev = dev;
+ list->next = tmp;
+ break;
+ }
+ }
+ }
+}
+
+void pcibios_update_irq(struct pci_dev *pdev, int irq)
+{
+}
+
+void pcibios_align_resource(void *data, struct resource *res,
+ unsigned long size, unsigned long align)
+{
+}
+
+int pcibios_enable_device(struct pci_dev *pdev, int mask)
+{
+ return 0;
+}
+
+void pcibios_resource_to_bus(struct pci_dev *pdev, struct pci_bus_region *region,
+ struct resource *res)
+{
+ struct pci_pbm_info *pbm = pdev->bus->sysdata;
+ struct resource zero_res, *root;
+
+ zero_res.start = 0;
+ zero_res.end = 0;
+ zero_res.flags = res->flags;
+
+ if (res->flags & IORESOURCE_IO)
+ root = &pbm->io_space;
+ else
+ root = &pbm->mem_space;
+
+ pbm->parent->resource_adjust(pdev, &zero_res, root);
+
+ region->start = res->start - zero_res.start;
+ region->end = res->end - zero_res.start;
+}
+
+void pcibios_bus_to_resource(struct pci_dev *pdev, struct resource *res,
+ struct pci_bus_region *region)
+{
+ struct pci_pbm_info *pbm = pdev->bus->sysdata;
+ struct resource *root;
+
+ res->start = region->start;
+ res->end = region->end;
+
+ if (res->flags & IORESOURCE_IO)
+ root = &pbm->io_space;
+ else
+ root = &pbm->mem_space;
+
+ pbm->parent->resource_adjust(pdev, res, root);
+}
+
+char * __init pcibios_setup(char *str)
+{
+ if (!strcmp(str, "onboardfirst")) {
+ pci_device_reorder = 1;
+ return NULL;
+ }
+ if (!strcmp(str, "noreorder")) {
+ pci_device_reorder = 0;
+ return NULL;
+ }
+ return str;
+}
+
+/* Platform support for /proc/bus/pci/X/Y mmap()s. */
+
+/* If the user uses a host-bridge as the PCI device, he may use
+ * this to perform a raw mmap() of the I/O or MEM space behind
+ * that controller.
+ *
+ * This can be useful for execution of x86 PCI bios initialization code
+ * on a PCI card, like the xfree86 int10 stuff does.
+ */
+static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm;
+ struct pci_controller_info *p;
+ unsigned long space_size, user_offset, user_size;
+
+ if (!pcp)
+ return -ENXIO;
+ pbm = pcp->pbm;
+ if (!pbm)
+ return -ENXIO;
+
+ p = pbm->parent;
+ if (p->pbms_same_domain) {
+ unsigned long lowest, highest;
+
+ lowest = ~0UL; highest = 0UL;
+ if (mmap_state == pci_mmap_io) {
+ if (p->pbm_A.io_space.flags) {
+ lowest = p->pbm_A.io_space.start;
+ highest = p->pbm_A.io_space.end + 1;
+ }
+ if (p->pbm_B.io_space.flags) {
+ if (lowest > p->pbm_B.io_space.start)
+ lowest = p->pbm_B.io_space.start;
+ if (highest < p->pbm_B.io_space.end + 1)
+ highest = p->pbm_B.io_space.end + 1;
+ }
+ space_size = highest - lowest;
+ } else {
+ if (p->pbm_A.mem_space.flags) {
+ lowest = p->pbm_A.mem_space.start;
+ highest = p->pbm_A.mem_space.end + 1;
+ }
+ if (p->pbm_B.mem_space.flags) {
+ if (lowest > p->pbm_B.mem_space.start)
+ lowest = p->pbm_B.mem_space.start;
+ if (highest < p->pbm_B.mem_space.end + 1)
+ highest = p->pbm_B.mem_space.end + 1;
+ }
+ space_size = highest - lowest;
+ }
+ } else {
+ if (mmap_state == pci_mmap_io) {
+ space_size = (pbm->io_space.end -
+ pbm->io_space.start) + 1;
+ } else {
+ space_size = (pbm->mem_space.end -
+ pbm->mem_space.start) + 1;
+ }
+ }
+
+ /* Make sure the request is in range. */
+ user_offset = vma->vm_pgoff << PAGE_SHIFT;
+ user_size = vma->vm_end - vma->vm_start;
+
+ if (user_offset >= space_size ||
+ (user_offset + user_size) > space_size)
+ return -EINVAL;
+
+ if (p->pbms_same_domain) {
+ unsigned long lowest = ~0UL;
+
+ if (mmap_state == pci_mmap_io) {
+ if (p->pbm_A.io_space.flags)
+ lowest = p->pbm_A.io_space.start;
+ if (p->pbm_B.io_space.flags &&
+ lowest > p->pbm_B.io_space.start)
+ lowest = p->pbm_B.io_space.start;
+ } else {
+ if (p->pbm_A.mem_space.flags)
+ lowest = p->pbm_A.mem_space.start;
+ if (p->pbm_B.mem_space.flags &&
+ lowest > p->pbm_B.mem_space.start)
+ lowest = p->pbm_B.mem_space.start;
+ }
+ vma->vm_pgoff = (lowest + user_offset) >> PAGE_SHIFT;
+ } else {
+ if (mmap_state == pci_mmap_io) {
+ vma->vm_pgoff = (pbm->io_space.start +
+ user_offset) >> PAGE_SHIFT;
+ } else {
+ vma->vm_pgoff = (pbm->mem_space.start +
+ user_offset) >> PAGE_SHIFT;
+ }
+ }
+
+ return 0;
+}
+
+/* Adjust vm_pgoff of VMA such that it is the physical page offset corresponding
+ * to the 32-bit pci bus offset for DEV requested by the user.
+ *
+ * Basically, the user finds the base address for his device which he wishes
+ * to mmap. They read the 32-bit value from the config space base register,
+ * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
+ * offset parameter of mmap on /proc/bus/pci/XXX for that device.
+ *
+ * Returns negative error code on failure, zero on success.
+ */
+static int __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ unsigned long user_offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long user32 = user_offset & pci_memspace_mask;
+ unsigned long largest_base, this_base, addr32;
+ int i;
+
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+ return __pci_mmap_make_offset_bus(dev, vma, mmap_state);
+
+ /* Figure out which base address this is for. */
+ largest_base = 0UL;
+ for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+ struct resource *rp = &dev->resource[i];
+
+ /* Active? */
+ if (!rp->flags)
+ continue;
+
+ /* Same type? */
+ if (i == PCI_ROM_RESOURCE) {
+ if (mmap_state != pci_mmap_mem)
+ continue;
+ } else {
+ if ((mmap_state == pci_mmap_io &&
+ (rp->flags & IORESOURCE_IO) == 0) ||
+ (mmap_state == pci_mmap_mem &&
+ (rp->flags & IORESOURCE_MEM) == 0))
+ continue;
+ }
+
+ this_base = rp->start;
+
+ addr32 = (this_base & PAGE_MASK) & pci_memspace_mask;
+
+ if (mmap_state == pci_mmap_io)
+ addr32 &= 0xffffff;
+
+ if (addr32 <= user32 && this_base > largest_base)
+ largest_base = this_base;
+ }
+
+ if (largest_base == 0UL)
+ return -EINVAL;
+
+ /* Now construct the final physical address. */
+ if (mmap_state == pci_mmap_io)
+ vma->vm_pgoff = (((largest_base & ~0xffffffUL) | user32) >> PAGE_SHIFT);
+ else
+ vma->vm_pgoff = (((largest_base & ~(pci_memspace_mask)) | user32) >> PAGE_SHIFT);
+
+ return 0;
+}
+
+/* Set vm_flags of VMA, as appropriate for this architecture, for a pci device
+ * mapping.
+ */
+static void __pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ vma->vm_flags |= (VM_IO | VM_RESERVED);
+}
+
+/* Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
+ * device mapping.
+ */
+static void __pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state)
+{
+ /* Our io_remap_page_range/io_remap_pfn_range takes care of this,
+ do nothing. */
+}
+
+/* Perform the actual remap of the pages for a PCI device mapping, as appropriate
+ * for this architecture. The region in the process to map is described by vm_start
+ * and vm_end members of VMA, the base physical address is found in vm_pgoff.
+ * The pci device structure is provided so that architectures may make mapping
+ * decisions on a per-device or per-bus basis.
+ *
+ * Returns a negative error code on failure, zero on success.
+ */
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state,
+ int write_combine)
+{
+ int ret;
+
+ ret = __pci_mmap_make_offset(dev, vma, mmap_state);
+ if (ret < 0)
+ return ret;
+
+ __pci_mmap_set_flags(dev, vma, mmap_state);
+ __pci_mmap_set_pgprot(dev, vma, mmap_state);
+
+ ret = io_remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ if (ret)
+ return ret;
+
+ vma->vm_flags |= VM_IO;
+ return 0;
+}
+
+/* Return the domain nuber for this pci bus */
+
+int pci_domain_nr(struct pci_bus *pbus)
+{
+ struct pci_pbm_info *pbm = pbus->sysdata;
+ int ret;
+
+ if (pbm == NULL || pbm->parent == NULL) {
+ ret = -ENXIO;
+ } else {
+ struct pci_controller_info *p = pbm->parent;
+
+ ret = p->index;
+ if (p->pbms_same_domain == 0)
+ ret = ((ret << 1) +
+ ((pbm == &pbm->parent->pbm_B) ? 1 : 0));
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pci_domain_nr);
+
+int pcibios_prep_mwi(struct pci_dev *dev)
+{
+ /* We set correct PCI_CACHE_LINE_SIZE register values for every
+ * device probed on this platform. So there is nothing to check
+ * and this always succeeds.
+ */
+ return 0;
+}
+
+#endif /* !(CONFIG_PCI) */
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
new file mode 100644
index 000000000000..58310aacea28
--- /dev/null
+++ b/arch/sparc64/kernel/pci_common.c
@@ -0,0 +1,1040 @@
+/* $Id: pci_common.c,v 1.29 2002/02/01 00:56:03 davem Exp $
+ * pci_common.c: PCI controller common support.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <asm/pbm.h>
+
+/* Fix self device of BUS and hook it into BUS->self.
+ * The pci_scan_bus does not do this for the host bridge.
+ */
+void __init pci_fixup_host_bridge_self(struct pci_bus *pbus)
+{
+ struct pci_dev *pdev;
+
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
+ if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) {
+ pbus->self = pdev;
+ return;
+ }
+ }
+
+ prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n");
+ prom_halt();
+}
+
+/* Find the OBP PROM device tree node for a PCI device.
+ * Return zero if not found.
+ */
+static int __init find_device_prom_node(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev,
+ int bus_prom_node,
+ struct linux_prom_pci_registers *pregs,
+ int *nregs)
+{
+ int node;
+
+ /*
+ * Return the PBM's PROM node in case we are it's PCI device,
+ * as the PBM's reg property is different to standard PCI reg
+ * properties. We would delete this device entry otherwise,
+ * which confuses XFree86's device probing...
+ */
+ if ((pdev->bus->number == pbm->pci_bus->number) && (pdev->devfn == 0) &&
+ (pdev->vendor == PCI_VENDOR_ID_SUN) &&
+ (pdev->device == PCI_DEVICE_ID_SUN_PBM ||
+ pdev->device == PCI_DEVICE_ID_SUN_SCHIZO ||
+ pdev->device == PCI_DEVICE_ID_SUN_TOMATILLO ||
+ pdev->device == PCI_DEVICE_ID_SUN_SABRE ||
+ pdev->device == PCI_DEVICE_ID_SUN_HUMMINGBIRD)) {
+ *nregs = 0;
+ return bus_prom_node;
+ }
+
+ node = prom_getchild(bus_prom_node);
+ while (node != 0) {
+ int err = prom_getproperty(node, "reg",
+ (char *)pregs,
+ sizeof(*pregs) * PROMREG_MAX);
+ if (err == 0 || err == -1)
+ goto do_next_sibling;
+ if (((pregs[0].phys_hi >> 8) & 0xff) == pdev->devfn) {
+ *nregs = err / sizeof(*pregs);
+ return node;
+ }
+
+ do_next_sibling:
+ node = prom_getsibling(node);
+ }
+ return 0;
+}
+
+/* Older versions of OBP on PCI systems encode 64-bit MEM
+ * space assignments incorrectly, this fixes them up. We also
+ * take the opportunity here to hide other kinds of bogus
+ * assignments.
+ */
+static void __init fixup_obp_assignments(struct pci_dev *pdev,
+ struct pcidev_cookie *pcp)
+{
+ int i;
+
+ if (pdev->vendor == PCI_VENDOR_ID_AL &&
+ (pdev->device == PCI_DEVICE_ID_AL_M7101 ||
+ pdev->device == PCI_DEVICE_ID_AL_M1533)) {
+ int i;
+
+ /* Zap all of the normal resources, they are
+ * meaningless and generate bogus resource collision
+ * messages. This is OpenBoot's ill-fated attempt to
+ * represent the implicit resources that these devices
+ * have.
+ */
+ pcp->num_prom_assignments = 0;
+ for (i = 0; i < 6; i++) {
+ pdev->resource[i].start =
+ pdev->resource[i].end =
+ pdev->resource[i].flags = 0;
+ }
+ pdev->resource[PCI_ROM_RESOURCE].start =
+ pdev->resource[PCI_ROM_RESOURCE].end =
+ pdev->resource[PCI_ROM_RESOURCE].flags = 0;
+ return;
+ }
+
+ for (i = 0; i < pcp->num_prom_assignments; i++) {
+ struct linux_prom_pci_registers *ap;
+ int space;
+
+ ap = &pcp->prom_assignments[i];
+ space = ap->phys_hi >> 24;
+ if ((space & 0x3) == 2 &&
+ (space & 0x4) != 0) {
+ ap->phys_hi &= ~(0x7 << 24);
+ ap->phys_hi |= 0x3 << 24;
+ }
+ }
+}
+
+/* Fill in the PCI device cookie sysdata for the given
+ * PCI device. This cookie is the means by which one
+ * can get to OBP and PCI controller specific information
+ * for a PCI device.
+ */
+static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev,
+ int bus_prom_node)
+{
+ struct linux_prom_pci_registers pregs[PROMREG_MAX];
+ struct pcidev_cookie *pcp;
+ int device_prom_node, nregs, err;
+
+ device_prom_node = find_device_prom_node(pbm, pdev, bus_prom_node,
+ pregs, &nregs);
+ if (device_prom_node == 0) {
+ /* If it is not in the OBP device tree then
+ * there must be a damn good reason for it.
+ *
+ * So what we do is delete the device from the
+ * PCI device tree completely. This scenario
+ * is seen, for example, on CP1500 for the
+ * second EBUS/HappyMeal pair if the external
+ * connector for it is not present.
+ */
+ pci_remove_bus_device(pdev);
+ return;
+ }
+
+ pcp = kmalloc(sizeof(*pcp), GFP_ATOMIC);
+ if (pcp == NULL) {
+ prom_printf("PCI_COOKIE: Fatal malloc error, aborting...\n");
+ prom_halt();
+ }
+ pcp->pbm = pbm;
+ pcp->prom_node = device_prom_node;
+ memcpy(pcp->prom_regs, pregs, sizeof(pcp->prom_regs));
+ pcp->num_prom_regs = nregs;
+ err = prom_getproperty(device_prom_node, "name",
+ pcp->prom_name, sizeof(pcp->prom_name));
+ if (err > 0)
+ pcp->prom_name[err] = 0;
+ else
+ pcp->prom_name[0] = 0;
+
+ err = prom_getproperty(device_prom_node,
+ "assigned-addresses",
+ (char *)pcp->prom_assignments,
+ sizeof(pcp->prom_assignments));
+ if (err == 0 || err == -1)
+ pcp->num_prom_assignments = 0;
+ else
+ pcp->num_prom_assignments =
+ (err / sizeof(pcp->prom_assignments[0]));
+
+ if (strcmp(pcp->prom_name, "ebus") == 0) {
+ struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX];
+ int iter;
+
+ /* EBUS is special... */
+ err = prom_getproperty(device_prom_node, "ranges",
+ (char *)&erng[0], sizeof(erng));
+ if (err == 0 || err == -1) {
+ prom_printf("EBUS: Fatal error, no range property\n");
+ prom_halt();
+ }
+ err = (err / sizeof(erng[0]));
+ for(iter = 0; iter < err; iter++) {
+ struct linux_prom_ebus_ranges *ep = &erng[iter];
+ struct linux_prom_pci_registers *ap;
+
+ ap = &pcp->prom_assignments[iter];
+
+ ap->phys_hi = ep->parent_phys_hi;
+ ap->phys_mid = ep->parent_phys_mid;
+ ap->phys_lo = ep->parent_phys_lo;
+ ap->size_hi = 0;
+ ap->size_lo = ep->size;
+ }
+ pcp->num_prom_assignments = err;
+ }
+
+ fixup_obp_assignments(pdev, pcp);
+
+ pdev->sysdata = pcp;
+}
+
+void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus,
+ struct pci_pbm_info *pbm,
+ int prom_node)
+{
+ struct pci_dev *pdev, *pdev_next;
+ struct pci_bus *this_pbus, *pbus_next;
+
+ /* This must be _safe because the cookie fillin
+ routine can delete devices from the tree. */
+ list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list)
+ pdev_cookie_fillin(pbm, pdev, prom_node);
+
+ list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) {
+ struct pcidev_cookie *pcp = this_pbus->self->sysdata;
+
+ pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node);
+ }
+}
+
+static void __init bad_assignment(struct pci_dev *pdev,
+ struct linux_prom_pci_registers *ap,
+ struct resource *res,
+ int do_prom_halt)
+{
+ prom_printf("PCI: Bogus PROM assignment. BUS[%02x] DEVFN[%x]\n",
+ pdev->bus->number, pdev->devfn);
+ if (ap)
+ prom_printf("PCI: phys[%08x:%08x:%08x] size[%08x:%08x]\n",
+ ap->phys_hi, ap->phys_mid, ap->phys_lo,
+ ap->size_hi, ap->size_lo);
+ if (res)
+ prom_printf("PCI: RES[%016lx-->%016lx:(%lx)]\n",
+ res->start, res->end, res->flags);
+ prom_printf("Please email this information to davem@redhat.com\n");
+ if (do_prom_halt)
+ prom_halt();
+}
+
+static struct resource *
+__init get_root_resource(struct linux_prom_pci_registers *ap,
+ struct pci_pbm_info *pbm)
+{
+ int space = (ap->phys_hi >> 24) & 3;
+
+ switch (space) {
+ case 0:
+ /* Configuration space, silently ignore it. */
+ return NULL;
+
+ case 1:
+ /* 16-bit IO space */
+ return &pbm->io_space;
+
+ case 2:
+ /* 32-bit MEM space */
+ return &pbm->mem_space;
+
+ case 3:
+ /* 64-bit MEM space, these are allocated out of
+ * the 32-bit mem_space range for the PBM, ie.
+ * we just zero out the upper 32-bits.
+ */
+ return &pbm->mem_space;
+
+ default:
+ printk("PCI: What is resource space %x? "
+ "Tell davem@redhat.com about it!\n", space);
+ return NULL;
+ };
+}
+
+static struct resource *
+__init get_device_resource(struct linux_prom_pci_registers *ap,
+ struct pci_dev *pdev)
+{
+ struct resource *res;
+ int breg = (ap->phys_hi & 0xff);
+
+ switch (breg) {
+ case PCI_ROM_ADDRESS:
+ /* Unfortunately I have seen several cases where
+ * buggy FCODE uses a space value of '1' (I/O space)
+ * in the register property for the ROM address
+ * so disable this sanity check for now.
+ */
+#if 0
+ {
+ int space = (ap->phys_hi >> 24) & 3;
+
+ /* It had better be MEM space. */
+ if (space != 2)
+ bad_assignment(pdev, ap, NULL, 0);
+ }
+#endif
+ res = &pdev->resource[PCI_ROM_RESOURCE];
+ break;
+
+ case PCI_BASE_ADDRESS_0:
+ case PCI_BASE_ADDRESS_1:
+ case PCI_BASE_ADDRESS_2:
+ case PCI_BASE_ADDRESS_3:
+ case PCI_BASE_ADDRESS_4:
+ case PCI_BASE_ADDRESS_5:
+ res = &pdev->resource[(breg - PCI_BASE_ADDRESS_0) / 4];
+ break;
+
+ default:
+ bad_assignment(pdev, ap, NULL, 0);
+ res = NULL;
+ break;
+ };
+
+ return res;
+}
+
+static int __init pdev_resource_collisions_expected(struct pci_dev *pdev)
+{
+ if (pdev->vendor != PCI_VENDOR_ID_SUN)
+ return 0;
+
+ if (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS ||
+ pdev->device == PCI_DEVICE_ID_SUN_RIO_1394 ||
+ pdev->device == PCI_DEVICE_ID_SUN_RIO_USB)
+ return 1;
+
+ return 0;
+}
+
+static void __init pdev_record_assignments(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ int i;
+
+ for (i = 0; i < pcp->num_prom_assignments; i++) {
+ struct linux_prom_pci_registers *ap;
+ struct resource *root, *res;
+
+ /* The format of this property is specified in
+ * the PCI Bus Binding to IEEE1275-1994.
+ */
+ ap = &pcp->prom_assignments[i];
+ root = get_root_resource(ap, pbm);
+ res = get_device_resource(ap, pdev);
+ if (root == NULL || res == NULL ||
+ res->flags == 0)
+ continue;
+
+ /* Ok we know which resource this PROM assignment is
+ * for, sanity check it.
+ */
+ if ((res->start & 0xffffffffUL) != ap->phys_lo)
+ bad_assignment(pdev, ap, res, 1);
+
+ /* If it is a 64-bit MEM space assignment, verify that
+ * the resource is too and that the upper 32-bits match.
+ */
+ if (((ap->phys_hi >> 24) & 3) == 3) {
+ if (((res->flags & IORESOURCE_MEM) == 0) ||
+ ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
+ != PCI_BASE_ADDRESS_MEM_TYPE_64))
+ bad_assignment(pdev, ap, res, 1);
+ if ((res->start >> 32) != ap->phys_mid)
+ bad_assignment(pdev, ap, res, 1);
+
+ /* PBM cannot generate cpu initiated PIOs
+ * to the full 64-bit space. Therefore the
+ * upper 32-bits better be zero. If it is
+ * not, just skip it and we will assign it
+ * properly ourselves.
+ */
+ if ((res->start >> 32) != 0UL) {
+ printk(KERN_ERR "PCI: OBP assigns out of range MEM address "
+ "%016lx for region %ld on device %s\n",
+ res->start, (res - &pdev->resource[0]), pci_name(pdev));
+ continue;
+ }
+ }
+
+ /* Adjust the resource into the physical address space
+ * of this PBM.
+ */
+ pbm->parent->resource_adjust(pdev, res, root);
+
+ if (request_resource(root, res) < 0) {
+ /* OK, there is some conflict. But this is fine
+ * since we'll reassign it in the fixup pass.
+ *
+ * We notify the user that OBP made an error if it
+ * is a case we don't expect.
+ */
+ if (!pdev_resource_collisions_expected(pdev)) {
+ printk(KERN_ERR "PCI: Address space collision on region %ld "
+ "[%016lx:%016lx] of device %s\n",
+ (res - &pdev->resource[0]),
+ res->start, res->end,
+ pci_name(pdev));
+ }
+ }
+ }
+}
+
+void __init pci_record_assignments(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *dev;
+ struct pci_bus *bus;
+
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_record_assignments(pbm, dev);
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_record_assignments(pbm, bus);
+}
+
+/* Return non-zero if PDEV has implicit I/O resources even
+ * though it may not have an I/O base address register
+ * active.
+ */
+static int __init has_implicit_io(struct pci_dev *pdev)
+{
+ int class = pdev->class >> 8;
+
+ if (class == PCI_CLASS_NOT_DEFINED ||
+ class == PCI_CLASS_NOT_DEFINED_VGA ||
+ class == PCI_CLASS_STORAGE_IDE ||
+ (pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
+ return 1;
+
+ return 0;
+}
+
+static void __init pdev_assign_unassigned(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev)
+{
+ u32 reg;
+ u16 cmd;
+ int i, io_seen, mem_seen;
+
+ io_seen = mem_seen = 0;
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *root, *res;
+ unsigned long size, min, max, align;
+
+ res = &pdev->resource[i];
+
+ if (res->flags & IORESOURCE_IO)
+ io_seen++;
+ else if (res->flags & IORESOURCE_MEM)
+ mem_seen++;
+
+ /* If it is already assigned or the resource does
+ * not exist, there is nothing to do.
+ */
+ if (res->parent != NULL || res->flags == 0UL)
+ continue;
+
+ /* Determine the root we allocate from. */
+ if (res->flags & IORESOURCE_IO) {
+ root = &pbm->io_space;
+ min = root->start + 0x400UL;
+ max = root->end;
+ } else {
+ root = &pbm->mem_space;
+ min = root->start;
+ max = min + 0x80000000UL;
+ }
+
+ size = res->end - res->start;
+ align = size + 1;
+ if (allocate_resource(root, res, size + 1, min, max, align, NULL, NULL) < 0) {
+ /* uh oh */
+ prom_printf("PCI: Failed to allocate resource %d for %s\n",
+ i, pci_name(pdev));
+ prom_halt();
+ }
+
+ /* Update PCI config space. */
+ pbm->parent->base_address_update(pdev, i);
+ }
+
+ /* Special case, disable the ROM. Several devices
+ * act funny (ie. do not respond to memory space writes)
+ * when it is left enabled. A good example are Qlogic,ISP
+ * adapters.
+ */
+ pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &reg);
+ reg &= ~PCI_ROM_ADDRESS_ENABLE;
+ pci_write_config_dword(pdev, PCI_ROM_ADDRESS, reg);
+
+ /* If we saw I/O or MEM resources, enable appropriate
+ * bits in PCI command register.
+ */
+ if (io_seen || mem_seen) {
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (io_seen || has_implicit_io(pdev))
+ cmd |= PCI_COMMAND_IO;
+ if (mem_seen)
+ cmd |= PCI_COMMAND_MEMORY;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ }
+
+ /* If this is a PCI bridge or an IDE controller,
+ * enable bus mastering. In the former case also
+ * set the cache line size correctly.
+ */
+ if (((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI) ||
+ (((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) &&
+ ((pdev->class & 0x80) != 0))) {
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_PCI)
+ pci_write_config_byte(pdev,
+ PCI_CACHE_LINE_SIZE,
+ (64 / sizeof(u32)));
+ }
+}
+
+void __init pci_assign_unassigned(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *dev;
+ struct pci_bus *bus;
+
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_assign_unassigned(pbm, dev);
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_assign_unassigned(pbm, bus);
+}
+
+static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt)
+{
+ struct linux_prom_pci_intmap bridge_local_intmap[PROM_PCIIMAP_MAX], *intmap;
+ struct linux_prom_pci_intmask bridge_local_intmask, *intmask;
+ struct pcidev_cookie *dev_pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm = dev_pcp->pbm;
+ struct linux_prom_pci_registers *pregs = dev_pcp->prom_regs;
+ unsigned int hi, mid, lo, irq;
+ int i, num_intmap, map_slot;
+
+ intmap = &pbm->pbm_intmap[0];
+ intmask = &pbm->pbm_intmask;
+ num_intmap = pbm->num_pbm_intmap;
+ map_slot = 0;
+
+ /* If we are underneath a PCI bridge, use PROM register
+ * property of the parent bridge which is closest to
+ * the PBM.
+ *
+ * However if that parent bridge has interrupt map/mask
+ * properties of its own we use the PROM register property
+ * of the next child device on the path to PDEV.
+ *
+ * In detail the two cases are (note that the 'X' below is the
+ * 'next child on the path to PDEV' mentioned above):
+ *
+ * 1) PBM --> PCI bus lacking int{map,mask} --> X ... PDEV
+ *
+ * Here we use regs of 'PCI bus' device.
+ *
+ * 2) PBM --> PCI bus with int{map,mask} --> X ... PDEV
+ *
+ * Here we use regs of 'X'. Note that X can be PDEV.
+ */
+ if (pdev->bus->number != pbm->pci_first_busno) {
+ struct pcidev_cookie *bus_pcp, *regs_pcp;
+ struct pci_dev *bus_dev, *regs_dev;
+ int plen;
+
+ bus_dev = pdev->bus->self;
+ regs_dev = pdev;
+
+ while (bus_dev->bus &&
+ bus_dev->bus->number != pbm->pci_first_busno) {
+ regs_dev = bus_dev;
+ bus_dev = bus_dev->bus->self;
+ }
+
+ regs_pcp = regs_dev->sysdata;
+ pregs = regs_pcp->prom_regs;
+
+ bus_pcp = bus_dev->sysdata;
+
+ /* But if the PCI bridge has it's own interrupt map
+ * and mask properties, use that and the regs of the
+ * PCI entity at the next level down on the path to the
+ * device.
+ */
+ plen = prom_getproperty(bus_pcp->prom_node, "interrupt-map",
+ (char *) &bridge_local_intmap[0],
+ sizeof(bridge_local_intmap));
+ if (plen != -1) {
+ intmap = &bridge_local_intmap[0];
+ num_intmap = plen / sizeof(struct linux_prom_pci_intmap);
+ plen = prom_getproperty(bus_pcp->prom_node,
+ "interrupt-map-mask",
+ (char *) &bridge_local_intmask,
+ sizeof(bridge_local_intmask));
+ if (plen == -1) {
+ printk("pci_intmap_match: Warning! Bridge has intmap "
+ "but no intmask.\n");
+ printk("pci_intmap_match: Trying to recover.\n");
+ return 0;
+ }
+
+ if (pdev->bus->self != bus_dev)
+ map_slot = 1;
+ } else {
+ pregs = bus_pcp->prom_regs;
+ map_slot = 1;
+ }
+ }
+
+ if (map_slot) {
+ *interrupt = ((*interrupt
+ - 1
+ + PCI_SLOT(pdev->devfn)) & 0x3) + 1;
+ }
+
+ hi = pregs->phys_hi & intmask->phys_hi;
+ mid = pregs->phys_mid & intmask->phys_mid;
+ lo = pregs->phys_lo & intmask->phys_lo;
+ irq = *interrupt & intmask->interrupt;
+
+ for (i = 0; i < num_intmap; i++) {
+ if (intmap[i].phys_hi == hi &&
+ intmap[i].phys_mid == mid &&
+ intmap[i].phys_lo == lo &&
+ intmap[i].interrupt == irq) {
+ *interrupt = intmap[i].cinterrupt;
+ printk("PCI-IRQ: Routing bus[%2x] slot[%2x] map[%d] to INO[%02x]\n",
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ map_slot, *interrupt);
+ return 1;
+ }
+ }
+
+ /* We will run this code even if pbm->num_pbm_intmap is zero, just so
+ * we can apply the slot mapping to the PROM interrupt property value.
+ * So do not spit out these warnings in that case.
+ */
+ if (num_intmap != 0) {
+ /* Print it both to OBP console and kernel one so that if bootup
+ * hangs here the user has the information to report.
+ */
+ prom_printf("pci_intmap_match: bus %02x, devfn %02x: ",
+ pdev->bus->number, pdev->devfn);
+ prom_printf("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n",
+ pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt);
+ prom_printf("Please email this information to davem@redhat.com\n");
+
+ printk("pci_intmap_match: bus %02x, devfn %02x: ",
+ pdev->bus->number, pdev->devfn);
+ printk("IRQ [%08x.%08x.%08x.%08x] not found in interrupt-map\n",
+ pregs->phys_hi, pregs->phys_mid, pregs->phys_lo, *interrupt);
+ printk("Please email this information to davem@redhat.com\n");
+ }
+
+ return 0;
+}
+
+static void __init pdev_fixup_irq(struct pci_dev *pdev)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm = pcp->pbm;
+ struct pci_controller_info *p = pbm->parent;
+ unsigned int portid = pbm->portid;
+ unsigned int prom_irq;
+ int prom_node = pcp->prom_node;
+ int err;
+
+ /* If this is an empty EBUS device, sometimes OBP fails to
+ * give it a valid fully specified interrupts property.
+ * The EBUS hooked up to SunHME on PCI I/O boards of
+ * Ex000 systems is one such case.
+ *
+ * The interrupt is not important so just ignore it.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_SUN &&
+ pdev->device == PCI_DEVICE_ID_SUN_EBUS &&
+ !prom_getchild(prom_node)) {
+ pdev->irq = 0;
+ return;
+ }
+
+ err = prom_getproperty(prom_node, "interrupts",
+ (char *)&prom_irq, sizeof(prom_irq));
+ if (err == 0 || err == -1) {
+ pdev->irq = 0;
+ return;
+ }
+
+ /* Fully specified already? */
+ if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) {
+ pdev->irq = p->irq_build(pbm, pdev, prom_irq);
+ goto have_irq;
+ }
+
+ /* An onboard device? (bit 5 set) */
+ if ((prom_irq & PCI_IRQ_INO) & 0x20) {
+ pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq));
+ goto have_irq;
+ }
+
+ /* Can we find a matching entry in the interrupt-map? */
+ if (pci_intmap_match(pdev, &prom_irq)) {
+ pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq);
+ goto have_irq;
+ }
+
+ /* Ok, we have to do it the hard way. */
+ {
+ unsigned int bus, slot, line;
+
+ bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0;
+
+ /* If we have a legal interrupt property, use it as
+ * the IRQ line.
+ */
+ if (prom_irq > 0 && prom_irq < 5) {
+ line = ((prom_irq - 1) & 3);
+ } else {
+ u8 pci_irq_line;
+
+ /* Else just directly consult PCI config space. */
+ pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line);
+ line = ((pci_irq_line - 1) & 3);
+ }
+
+ /* Now figure out the slot.
+ *
+ * Basically, device number zero on the top-level bus is
+ * always the PCI host controller. Slot 0 is then device 1.
+ * PBM A supports two external slots (0 and 1), and PBM B
+ * supports 4 external slots (0, 1, 2, and 3). On-board PCI
+ * devices are wired to device numbers outside of these
+ * ranges. -DaveM
+ */
+ if (pdev->bus->number == pbm->pci_first_busno) {
+ slot = PCI_SLOT(pdev->devfn) - pbm->pci_first_slot;
+ } else {
+ struct pci_dev *bus_dev;
+
+ /* Underneath a bridge, use slot number of parent
+ * bridge which is closest to the PBM.
+ */
+ bus_dev = pdev->bus->self;
+ while (bus_dev->bus &&
+ bus_dev->bus->number != pbm->pci_first_busno)
+ bus_dev = bus_dev->bus->self;
+
+ slot = PCI_SLOT(bus_dev->devfn) - pbm->pci_first_slot;
+ }
+ slot = slot << 2;
+
+ pdev->irq = p->irq_build(pbm, pdev,
+ ((portid << 6) & PCI_IRQ_IGN) |
+ (bus | slot | line));
+ }
+
+have_irq:
+ pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
+ pdev->irq & PCI_IRQ_INO);
+}
+
+void __init pci_fixup_irq(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *dev;
+ struct pci_bus *bus;
+
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_fixup_irq(dev);
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_fixup_irq(pbm, bus);
+}
+
+static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz)
+{
+ u16 cmd;
+ u8 hdr_type, min_gnt, ltimer;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ /* Read it back, if the mastering bit did not
+ * get set, the device does not support bus
+ * mastering so we have nothing to do here.
+ */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if ((cmd & PCI_COMMAND_MASTER) == 0)
+ return;
+
+ /* Set correct cache line size, 64-byte on all
+ * Sparc64 PCI systems. Note that the value is
+ * measured in 32-bit words.
+ */
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+ 64 / sizeof(u32));
+
+ pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type);
+ hdr_type &= ~0x80;
+ if (hdr_type != PCI_HEADER_TYPE_NORMAL)
+ return;
+
+ /* If the latency timer is already programmed with a non-zero
+ * value, assume whoever set it (OBP or whoever) knows what
+ * they are doing.
+ */
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &ltimer);
+ if (ltimer != 0)
+ return;
+
+ /* XXX Since I'm tipping off the min grant value to
+ * XXX choose a suitable latency timer value, I also
+ * XXX considered making use of the max latency value
+ * XXX as well. Unfortunately I've seen too many bogusly
+ * XXX low settings for it to the point where it lacks
+ * XXX any usefulness. In one case, an ethernet card
+ * XXX claimed a min grant of 10 and a max latency of 5.
+ * XXX Now, if I had two such cards on the same bus I
+ * XXX could not set the desired burst period (calculated
+ * XXX from min grant) without violating the max latency
+ * XXX bound. Duh...
+ * XXX
+ * XXX I blame dumb PC bios implementors for stuff like
+ * XXX this, most of them don't even try to do something
+ * XXX sensible with latency timer values and just set some
+ * XXX default value (usually 32) into every device.
+ */
+
+ pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt);
+
+ if (min_gnt == 0) {
+ /* If no min_gnt setting then use a default
+ * value.
+ */
+ if (is_66mhz)
+ ltimer = 16;
+ else
+ ltimer = 32;
+ } else {
+ int shift_factor;
+
+ if (is_66mhz)
+ shift_factor = 2;
+ else
+ shift_factor = 3;
+
+ /* Use a default value when the min_gnt value
+ * is erroneously high.
+ */
+ if (((unsigned int) min_gnt << shift_factor) > 512 ||
+ ((min_gnt << shift_factor) & 0xff) == 0) {
+ ltimer = 8 << shift_factor;
+ } else {
+ ltimer = min_gnt << shift_factor;
+ }
+ }
+
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ltimer);
+}
+
+void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *pdev;
+ int all_are_66mhz;
+ u16 status;
+
+ if (pbm->is_66mhz_capable == 0) {
+ all_are_66mhz = 0;
+ goto out;
+ }
+
+ all_are_66mhz = 1;
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
+ pci_read_config_word(pdev, PCI_STATUS, &status);
+ if (!(status & PCI_STATUS_66MHZ)) {
+ all_are_66mhz = 0;
+ break;
+ }
+ }
+out:
+ pbm->all_devs_66mhz = all_are_66mhz;
+
+ printk("PCI%d(PBM%c): Bus running at %dMHz\n",
+ pbm->parent->index,
+ (pbm == &pbm->parent->pbm_A) ? 'A' : 'B',
+ (all_are_66mhz ? 66 : 33));
+}
+
+void pci_setup_busmastering(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *dev;
+ struct pci_bus *bus;
+ int is_66mhz;
+
+ is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz;
+
+ list_for_each_entry(dev, &pbus->devices, bus_list)
+ pdev_setup_busmastering(dev, is_66mhz);
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_setup_busmastering(pbm, bus);
+}
+
+void pci_register_legacy_regions(struct resource *io_res,
+ struct resource *mem_res)
+{
+ struct resource *p;
+
+ /* VGA Video RAM. */
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return;
+
+ memset(p, 0, sizeof(*p));
+ p->name = "Video RAM area";
+ p->start = mem_res->start + 0xa0000UL;
+ p->end = p->start + 0x1ffffUL;
+ p->flags = IORESOURCE_BUSY;
+ request_resource(mem_res, p);
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return;
+
+ memset(p, 0, sizeof(*p));
+ p->name = "System ROM";
+ p->start = mem_res->start + 0xf0000UL;
+ p->end = p->start + 0xffffUL;
+ p->flags = IORESOURCE_BUSY;
+ request_resource(mem_res, p);
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return;
+
+ memset(p, 0, sizeof(*p));
+ p->name = "Video ROM";
+ p->start = mem_res->start + 0xc0000UL;
+ p->end = p->start + 0x7fffUL;
+ p->flags = IORESOURCE_BUSY;
+ request_resource(mem_res, p);
+}
+
+/* Generic helper routines for PCI error reporting. */
+void pci_scan_for_target_abort(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *pdev;
+ struct pci_bus *bus;
+
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
+ u16 status, error_bits;
+
+ pci_read_config_word(pdev, PCI_STATUS, &status);
+ error_bits =
+ (status & (PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT));
+ if (error_bits) {
+ pci_write_config_word(pdev, PCI_STATUS, error_bits);
+ printk("PCI%d(PBM%c): Device [%s] saw Target Abort [%016x]\n",
+ p->index, ((pbm == &p->pbm_A) ? 'A' : 'B'),
+ pci_name(pdev), status);
+ }
+ }
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_scan_for_target_abort(p, pbm, bus);
+}
+
+void pci_scan_for_master_abort(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *pdev;
+ struct pci_bus *bus;
+
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
+ u16 status, error_bits;
+
+ pci_read_config_word(pdev, PCI_STATUS, &status);
+ error_bits =
+ (status & (PCI_STATUS_REC_MASTER_ABORT));
+ if (error_bits) {
+ pci_write_config_word(pdev, PCI_STATUS, error_bits);
+ printk("PCI%d(PBM%c): Device [%s] received Master Abort [%016x]\n",
+ p->index, ((pbm == &p->pbm_A) ? 'A' : 'B'),
+ pci_name(pdev), status);
+ }
+ }
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_scan_for_master_abort(p, pbm, bus);
+}
+
+void pci_scan_for_parity_error(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct pci_dev *pdev;
+ struct pci_bus *bus;
+
+ list_for_each_entry(pdev, &pbus->devices, bus_list) {
+ u16 status, error_bits;
+
+ pci_read_config_word(pdev, PCI_STATUS, &status);
+ error_bits =
+ (status & (PCI_STATUS_PARITY |
+ PCI_STATUS_DETECTED_PARITY));
+ if (error_bits) {
+ pci_write_config_word(pdev, PCI_STATUS, error_bits);
+ printk("PCI%d(PBM%c): Device [%s] saw Parity Error [%016x]\n",
+ p->index, ((pbm == &p->pbm_A) ? 'A' : 'B'),
+ pci_name(pdev), status);
+ }
+ }
+
+ list_for_each_entry(bus, &pbus->children, node)
+ pci_scan_for_parity_error(p, pbm, bus);
+}
diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h
new file mode 100644
index 000000000000..6c3205962544
--- /dev/null
+++ b/arch/sparc64/kernel/pci_impl.h
@@ -0,0 +1,49 @@
+/* $Id: pci_impl.h,v 1.9 2001/06/13 06:34:30 davem Exp $
+ * pci_impl.h: Helper definitions for PCI controller support.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ */
+
+#ifndef PCI_IMPL_H
+#define PCI_IMPL_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+extern struct pci_controller_info *pci_controller_root;
+
+extern int pci_num_controllers;
+
+/* PCI bus scanning and fixup support. */
+extern void pci_fixup_host_bridge_self(struct pci_bus *pbus);
+extern void pci_fill_in_pbm_cookies(struct pci_bus *pbus,
+ struct pci_pbm_info *pbm,
+ int prom_node);
+extern void pci_record_assignments(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
+extern void pci_assign_unassigned(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
+extern void pci_fixup_irq(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
+extern void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
+extern void pci_setup_busmastering(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
+extern void pci_register_legacy_regions(struct resource *io_res,
+ struct resource *mem_res);
+
+/* Error reporting support. */
+extern void pci_scan_for_target_abort(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *);
+extern void pci_scan_for_master_abort(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *);
+extern void pci_scan_for_parity_error(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *);
+
+/* Configuration space access. */
+extern void pci_config_read8(u8 *addr, u8 *ret);
+extern void pci_config_read16(u16 *addr, u16 *ret);
+extern void pci_config_read32(u32 *addr, u32 *ret);
+extern void pci_config_write8(u8 *addr, u8 val);
+extern void pci_config_write16(u16 *addr, u16 val);
+extern void pci_config_write32(u32 *addr, u32 val);
+
+#endif /* !(PCI_IMPL_H) */
diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c
new file mode 100644
index 000000000000..292983413ae2
--- /dev/null
+++ b/arch/sparc64/kernel/pci_iommu.c
@@ -0,0 +1,855 @@
+/* $Id: pci_iommu.c,v 1.17 2001/12/17 07:05:09 davem Exp $
+ * pci_iommu.c: UltraSparc PCI controller IOM/STC support.
+ *
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2000 Jakub Jelinek (jakub@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/pbm.h>
+
+#include "iommu_common.h"
+
+#define PCI_STC_CTXMATCH_ADDR(STC, CTX) \
+ ((STC)->strbuf_ctxmatch_base + ((CTX) << 3))
+
+/* Accessing IOMMU and Streaming Buffer registers.
+ * REG parameter is a physical address. All registers
+ * are 64-bits in size.
+ */
+#define pci_iommu_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define pci_iommu_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E))
+
+/* Must be invoked under the IOMMU lock. */
+static void __iommu_flushall(struct pci_iommu *iommu)
+{
+ unsigned long tag;
+ int entry;
+
+ tag = iommu->iommu_flush + (0xa580UL - 0x0210UL);
+ for (entry = 0; entry < 16; entry++) {
+ pci_iommu_write(tag, 0);
+ tag += 8;
+ }
+
+ /* Ensure completion of previous PIO writes. */
+ (void) pci_iommu_read(iommu->write_complete_reg);
+
+ /* Now update everyone's flush point. */
+ for (entry = 0; entry < PBM_NCLUSTERS; entry++) {
+ iommu->alloc_info[entry].flush =
+ iommu->alloc_info[entry].next;
+ }
+}
+
+#define IOPTE_CONSISTENT(CTX) \
+ (IOPTE_VALID | IOPTE_CACHE | \
+ (((CTX) << 47) & IOPTE_CONTEXT))
+
+#define IOPTE_STREAMING(CTX) \
+ (IOPTE_CONSISTENT(CTX) | IOPTE_STBUF)
+
+/* Existing mappings are never marked invalid, instead they
+ * are pointed to a dummy page.
+ */
+#define IOPTE_IS_DUMMY(iommu, iopte) \
+ ((iopte_val(*iopte) & IOPTE_PAGE) == (iommu)->dummy_page_pa)
+
+static void inline iopte_make_dummy(struct pci_iommu *iommu, iopte_t *iopte)
+{
+ unsigned long val = iopte_val(*iopte);
+
+ val &= ~IOPTE_PAGE;
+ val |= iommu->dummy_page_pa;
+
+ iopte_val(*iopte) = val;
+}
+
+void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize)
+{
+ int i;
+
+ tsbsize /= sizeof(iopte_t);
+
+ for (i = 0; i < tsbsize; i++)
+ iopte_make_dummy(iommu, &iommu->page_table[i]);
+}
+
+static iopte_t *alloc_streaming_cluster(struct pci_iommu *iommu, unsigned long npages)
+{
+ iopte_t *iopte, *limit, *first;
+ unsigned long cnum, ent, flush_point;
+
+ cnum = 0;
+ while ((1UL << cnum) < npages)
+ cnum++;
+ iopte = (iommu->page_table +
+ (cnum << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
+
+ if (cnum == 0)
+ limit = (iommu->page_table +
+ iommu->lowest_consistent_map);
+ else
+ limit = (iopte +
+ (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
+
+ iopte += ((ent = iommu->alloc_info[cnum].next) << cnum);
+ flush_point = iommu->alloc_info[cnum].flush;
+
+ first = iopte;
+ for (;;) {
+ if (IOPTE_IS_DUMMY(iommu, iopte)) {
+ if ((iopte + (1 << cnum)) >= limit)
+ ent = 0;
+ else
+ ent = ent + 1;
+ iommu->alloc_info[cnum].next = ent;
+ if (ent == flush_point)
+ __iommu_flushall(iommu);
+ break;
+ }
+ iopte += (1 << cnum);
+ ent++;
+ if (iopte >= limit) {
+ iopte = (iommu->page_table +
+ (cnum <<
+ (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
+ ent = 0;
+ }
+ if (ent == flush_point)
+ __iommu_flushall(iommu);
+ if (iopte == first)
+ goto bad;
+ }
+
+ /* I've got your streaming cluster right here buddy boy... */
+ return iopte;
+
+bad:
+ printk(KERN_EMERG "pci_iommu: alloc_streaming_cluster of npages(%ld) failed!\n",
+ npages);
+ return NULL;
+}
+
+static void free_streaming_cluster(struct pci_iommu *iommu, dma_addr_t base,
+ unsigned long npages, unsigned long ctx)
+{
+ unsigned long cnum, ent;
+
+ cnum = 0;
+ while ((1UL << cnum) < npages)
+ cnum++;
+
+ ent = (base << (32 - IO_PAGE_SHIFT + PBM_LOGCLUSTERS - iommu->page_table_sz_bits))
+ >> (32 + PBM_LOGCLUSTERS + cnum - iommu->page_table_sz_bits);
+
+ /* If the global flush might not have caught this entry,
+ * adjust the flush point such that we will flush before
+ * ever trying to reuse it.
+ */
+#define between(X,Y,Z) (((Z) - (Y)) >= ((X) - (Y)))
+ if (between(ent, iommu->alloc_info[cnum].next, iommu->alloc_info[cnum].flush))
+ iommu->alloc_info[cnum].flush = ent;
+#undef between
+}
+
+/* We allocate consistent mappings from the end of cluster zero. */
+static iopte_t *alloc_consistent_cluster(struct pci_iommu *iommu, unsigned long npages)
+{
+ iopte_t *iopte;
+
+ iopte = iommu->page_table + (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS));
+ while (iopte > iommu->page_table) {
+ iopte--;
+ if (IOPTE_IS_DUMMY(iommu, iopte)) {
+ unsigned long tmp = npages;
+
+ while (--tmp) {
+ iopte--;
+ if (!IOPTE_IS_DUMMY(iommu, iopte))
+ break;
+ }
+ if (tmp == 0) {
+ u32 entry = (iopte - iommu->page_table);
+
+ if (entry < iommu->lowest_consistent_map)
+ iommu->lowest_consistent_map = entry;
+ return iopte;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Allocate and map kernel buffer of size SIZE using consistent mode
+ * DMA for PCI device PDEV. Return non-NULL cpu-side address if
+ * successful and set *DMA_ADDRP to the PCI side dma address.
+ */
+void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ iopte_t *iopte;
+ unsigned long flags, order, first_page, ctx;
+ void *ret;
+ int npages;
+
+ size = IO_PAGE_ALIGN(size);
+ order = get_order(size);
+ if (order >= 10)
+ return NULL;
+
+ first_page = __get_free_pages(GFP_ATOMIC, order);
+ if (first_page == 0UL)
+ return NULL;
+ memset((char *)first_page, 0, PAGE_SIZE << order);
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+ iopte = alloc_consistent_cluster(iommu, size >> IO_PAGE_SHIFT);
+ if (iopte == NULL) {
+ spin_unlock_irqrestore(&iommu->lock, flags);
+ free_pages(first_page, order);
+ return NULL;
+ }
+
+ *dma_addrp = (iommu->page_table_map_base +
+ ((iopte - iommu->page_table) << IO_PAGE_SHIFT));
+ ret = (void *) first_page;
+ npages = size >> IO_PAGE_SHIFT;
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = iommu->iommu_cur_ctx++;
+ first_page = __pa(first_page);
+ while (npages--) {
+ iopte_val(*iopte) = (IOPTE_CONSISTENT(ctx) |
+ IOPTE_WRITE |
+ (first_page & IOPTE_PAGE));
+ iopte++;
+ first_page += IO_PAGE_SIZE;
+ }
+
+ {
+ int i;
+ u32 daddr = *dma_addrp;
+
+ npages = size >> IO_PAGE_SHIFT;
+ for (i = 0; i < npages; i++) {
+ pci_iommu_write(iommu->iommu_flush, daddr);
+ daddr += IO_PAGE_SIZE;
+ }
+ }
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ return ret;
+}
+
+/* Free and unmap a consistent DMA translation. */
+void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ iopte_t *iopte;
+ unsigned long flags, order, npages, i, ctx;
+
+ npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ iopte = iommu->page_table +
+ ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ if ((iopte - iommu->page_table) ==
+ iommu->lowest_consistent_map) {
+ iopte_t *walk = iopte + npages;
+ iopte_t *limit;
+
+ limit = (iommu->page_table +
+ (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)));
+ while (walk < limit) {
+ if (!IOPTE_IS_DUMMY(iommu, walk))
+ break;
+ walk++;
+ }
+ iommu->lowest_consistent_map =
+ (walk - iommu->page_table);
+ }
+
+ /* Data for consistent mappings cannot enter the streaming
+ * buffers, so we only need to update the TSB. We flush
+ * the IOMMU here as well to prevent conflicts with the
+ * streaming mapping deferred tlb flush scheme.
+ */
+
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = (iopte_val(*iopte) & IOPTE_CONTEXT) >> 47UL;
+
+ for (i = 0; i < npages; i++, iopte++)
+ iopte_make_dummy(iommu, iopte);
+
+ if (iommu->iommu_ctxflush) {
+ pci_iommu_write(iommu->iommu_ctxflush, ctx);
+ } else {
+ for (i = 0; i < npages; i++) {
+ u32 daddr = dvma + (i << IO_PAGE_SHIFT);
+
+ pci_iommu_write(iommu->iommu_flush, daddr);
+ }
+ }
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ order = get_order(size);
+ if (order < 10)
+ free_pages((unsigned long)cpu, order);
+}
+
+/* Map a single buffer at PTR of SZ bytes for PCI DMA
+ * in streaming mode.
+ */
+dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ struct pci_strbuf *strbuf;
+ iopte_t *base;
+ unsigned long flags, npages, oaddr;
+ unsigned long i, base_paddr, ctx;
+ u32 bus_addr, ret;
+ unsigned long iopte_protection;
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ strbuf = &pcp->pbm->stc;
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
+ oaddr = (unsigned long)ptr;
+ npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK);
+ npages >>= IO_PAGE_SHIFT;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ base = alloc_streaming_cluster(iommu, npages);
+ if (base == NULL)
+ goto bad;
+ bus_addr = (iommu->page_table_map_base +
+ ((base - iommu->page_table) << IO_PAGE_SHIFT));
+ ret = bus_addr | (oaddr & ~IO_PAGE_MASK);
+ base_paddr = __pa(oaddr & IO_PAGE_MASK);
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = iommu->iommu_cur_ctx++;
+ if (strbuf->strbuf_enabled)
+ iopte_protection = IOPTE_STREAMING(ctx);
+ else
+ iopte_protection = IOPTE_CONSISTENT(ctx);
+ if (direction != PCI_DMA_TODEVICE)
+ iopte_protection |= IOPTE_WRITE;
+
+ for (i = 0; i < npages; i++, base++, base_paddr += IO_PAGE_SIZE)
+ iopte_val(*base) = iopte_protection | base_paddr;
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ return ret;
+
+bad:
+ spin_unlock_irqrestore(&iommu->lock, flags);
+ return PCI_DMA_ERROR_CODE;
+}
+
+/* Unmap a single streaming mode DMA translation. */
+void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ struct pci_strbuf *strbuf;
+ iopte_t *base;
+ unsigned long flags, npages, i, ctx;
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ strbuf = &pcp->pbm->stc;
+
+ npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
+ npages >>= IO_PAGE_SHIFT;
+ base = iommu->page_table +
+ ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
+#ifdef DEBUG_PCI_IOMMU
+ if (IOPTE_IS_DUMMY(iommu, base))
+ printk("pci_unmap_single called on non-mapped region %08x,%08x from %016lx\n",
+ bus_addr, sz, __builtin_return_address(0));
+#endif
+ bus_addr &= IO_PAGE_MASK;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ /* Record the context, if any. */
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = (iopte_val(*base) & IOPTE_CONTEXT) >> 47UL;
+
+ /* Step 1: Kick data out of streaming buffers if necessary. */
+ if (strbuf->strbuf_enabled) {
+ u32 vaddr = bus_addr;
+
+ PCI_STC_FLUSHFLAG_INIT(strbuf);
+ if (strbuf->strbuf_ctxflush &&
+ iommu->iommu_ctxflush) {
+ unsigned long matchreg, flushreg;
+
+ flushreg = strbuf->strbuf_ctxflush;
+ matchreg = PCI_STC_CTXMATCH_ADDR(strbuf, ctx);
+ do {
+ pci_iommu_write(flushreg, ctx);
+ } while(((long)pci_iommu_read(matchreg)) < 0L);
+ } else {
+ for (i = 0; i < npages; i++, vaddr += IO_PAGE_SIZE)
+ pci_iommu_write(strbuf->strbuf_pflush, vaddr);
+ }
+
+ pci_iommu_write(strbuf->strbuf_fsync, strbuf->strbuf_flushflag_pa);
+ (void) pci_iommu_read(iommu->write_complete_reg);
+ while (!PCI_STC_FLUSHFLAG_SET(strbuf))
+ membar("#LoadLoad");
+ }
+
+ /* Step 2: Clear out first TSB entry. */
+ iopte_make_dummy(iommu, base);
+
+ free_streaming_cluster(iommu, bus_addr - iommu->page_table_map_base,
+ npages, ctx);
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+#define SG_ENT_PHYS_ADDRESS(SG) \
+ (__pa(page_address((SG)->page)) + (SG)->offset)
+
+static inline void fill_sg(iopte_t *iopte, struct scatterlist *sg,
+ int nused, int nelems, unsigned long iopte_protection)
+{
+ struct scatterlist *dma_sg = sg;
+ struct scatterlist *sg_end = sg + nelems;
+ int i;
+
+ for (i = 0; i < nused; i++) {
+ unsigned long pteval = ~0UL;
+ u32 dma_npages;
+
+ dma_npages = ((dma_sg->dma_address & (IO_PAGE_SIZE - 1UL)) +
+ dma_sg->dma_length +
+ ((IO_PAGE_SIZE - 1UL))) >> IO_PAGE_SHIFT;
+ do {
+ unsigned long offset;
+ signed int len;
+
+ /* If we are here, we know we have at least one
+ * more page to map. So walk forward until we
+ * hit a page crossing, and begin creating new
+ * mappings from that spot.
+ */
+ for (;;) {
+ unsigned long tmp;
+
+ tmp = SG_ENT_PHYS_ADDRESS(sg);
+ len = sg->length;
+ if (((tmp ^ pteval) >> IO_PAGE_SHIFT) != 0UL) {
+ pteval = tmp & IO_PAGE_MASK;
+ offset = tmp & (IO_PAGE_SIZE - 1UL);
+ break;
+ }
+ if (((tmp ^ (tmp + len - 1UL)) >> IO_PAGE_SHIFT) != 0UL) {
+ pteval = (tmp + IO_PAGE_SIZE) & IO_PAGE_MASK;
+ offset = 0UL;
+ len -= (IO_PAGE_SIZE - (tmp & (IO_PAGE_SIZE - 1UL)));
+ break;
+ }
+ sg++;
+ }
+
+ pteval = iopte_protection | (pteval & IOPTE_PAGE);
+ while (len > 0) {
+ *iopte++ = __iopte(pteval);
+ pteval += IO_PAGE_SIZE;
+ len -= (IO_PAGE_SIZE - offset);
+ offset = 0;
+ dma_npages--;
+ }
+
+ pteval = (pteval & IOPTE_PAGE) + len;
+ sg++;
+
+ /* Skip over any tail mappings we've fully mapped,
+ * adjusting pteval along the way. Stop when we
+ * detect a page crossing event.
+ */
+ while (sg < sg_end &&
+ (pteval << (64 - IO_PAGE_SHIFT)) != 0UL &&
+ (pteval == SG_ENT_PHYS_ADDRESS(sg)) &&
+ ((pteval ^
+ (SG_ENT_PHYS_ADDRESS(sg) + sg->length - 1UL)) >> IO_PAGE_SHIFT) == 0UL) {
+ pteval += sg->length;
+ sg++;
+ }
+ if ((pteval << (64 - IO_PAGE_SHIFT)) == 0UL)
+ pteval = ~0UL;
+ } while (dma_npages != 0);
+ dma_sg++;
+ }
+}
+
+/* Map a set of buffers described by SGLIST with NELEMS array
+ * elements in streaming mode for PCI DMA.
+ * When making changes here, inspect the assembly output. I was having
+ * hard time to kepp this routine out of using stack slots for holding variables.
+ */
+int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ struct pci_strbuf *strbuf;
+ unsigned long flags, ctx, npages, iopte_protection;
+ iopte_t *base;
+ u32 dma_base;
+ struct scatterlist *sgtmp;
+ int used;
+
+ /* Fast path single entry scatterlists. */
+ if (nelems == 1) {
+ sglist->dma_address =
+ pci_map_single(pdev,
+ (page_address(sglist->page) + sglist->offset),
+ sglist->length, direction);
+ sglist->dma_length = sglist->length;
+ return 1;
+ }
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ strbuf = &pcp->pbm->stc;
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
+ /* Step 1: Prepare scatter list. */
+
+ npages = prepare_sg(sglist, nelems);
+
+ /* Step 2: Allocate a cluster. */
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ base = alloc_streaming_cluster(iommu, npages);
+ if (base == NULL)
+ goto bad;
+ dma_base = iommu->page_table_map_base + ((base - iommu->page_table) << IO_PAGE_SHIFT);
+
+ /* Step 3: Normalize DMA addresses. */
+ used = nelems;
+
+ sgtmp = sglist;
+ while (used && sgtmp->dma_length) {
+ sgtmp->dma_address += dma_base;
+ sgtmp++;
+ used--;
+ }
+ used = nelems - used;
+
+ /* Step 4: Choose a context if necessary. */
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = iommu->iommu_cur_ctx++;
+
+ /* Step 5: Create the mappings. */
+ if (strbuf->strbuf_enabled)
+ iopte_protection = IOPTE_STREAMING(ctx);
+ else
+ iopte_protection = IOPTE_CONSISTENT(ctx);
+ if (direction != PCI_DMA_TODEVICE)
+ iopte_protection |= IOPTE_WRITE;
+ fill_sg (base, sglist, used, nelems, iopte_protection);
+#ifdef VERIFY_SG
+ verify_sglist(sglist, nelems, base, npages);
+#endif
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ return used;
+
+bad:
+ spin_unlock_irqrestore(&iommu->lock, flags);
+ return PCI_DMA_ERROR_CODE;
+}
+
+/* Unmap a set of streaming mode DMA translations. */
+void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ struct pci_strbuf *strbuf;
+ iopte_t *base;
+ unsigned long flags, ctx, i, npages;
+ u32 bus_addr;
+
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ strbuf = &pcp->pbm->stc;
+
+ bus_addr = sglist->dma_address & IO_PAGE_MASK;
+
+ for (i = 1; i < nelems; i++)
+ if (sglist[i].dma_length == 0)
+ break;
+ i--;
+ npages = (IO_PAGE_ALIGN(sglist[i].dma_address + sglist[i].dma_length) - bus_addr) >> IO_PAGE_SHIFT;
+
+ base = iommu->page_table +
+ ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
+
+#ifdef DEBUG_PCI_IOMMU
+ if (IOPTE_IS_DUMMY(iommu, base))
+ printk("pci_unmap_sg called on non-mapped region %016lx,%d from %016lx\n", sglist->dma_address, nelems, __builtin_return_address(0));
+#endif
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ /* Record the context, if any. */
+ ctx = 0;
+ if (iommu->iommu_ctxflush)
+ ctx = (iopte_val(*base) & IOPTE_CONTEXT) >> 47UL;
+
+ /* Step 1: Kick data out of streaming buffers if necessary. */
+ if (strbuf->strbuf_enabled) {
+ u32 vaddr = (u32) bus_addr;
+
+ PCI_STC_FLUSHFLAG_INIT(strbuf);
+ if (strbuf->strbuf_ctxflush &&
+ iommu->iommu_ctxflush) {
+ unsigned long matchreg, flushreg;
+
+ flushreg = strbuf->strbuf_ctxflush;
+ matchreg = PCI_STC_CTXMATCH_ADDR(strbuf, ctx);
+ do {
+ pci_iommu_write(flushreg, ctx);
+ } while(((long)pci_iommu_read(matchreg)) < 0L);
+ } else {
+ for (i = 0; i < npages; i++, vaddr += IO_PAGE_SIZE)
+ pci_iommu_write(strbuf->strbuf_pflush, vaddr);
+ }
+
+ pci_iommu_write(strbuf->strbuf_fsync, strbuf->strbuf_flushflag_pa);
+ (void) pci_iommu_read(iommu->write_complete_reg);
+ while (!PCI_STC_FLUSHFLAG_SET(strbuf))
+ membar("#LoadLoad");
+ }
+
+ /* Step 2: Clear out first TSB entry. */
+ iopte_make_dummy(iommu, base);
+
+ free_streaming_cluster(iommu, bus_addr - iommu->page_table_map_base,
+ npages, ctx);
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+/* Make physical memory consistent for a single
+ * streaming mode DMA translation after a transfer.
+ */
+void pci_dma_sync_single_for_cpu(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ struct pci_strbuf *strbuf;
+ unsigned long flags, ctx, npages;
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ strbuf = &pcp->pbm->stc;
+
+ if (!strbuf->strbuf_enabled)
+ return;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK);
+ npages >>= IO_PAGE_SHIFT;
+ bus_addr &= IO_PAGE_MASK;
+
+ /* Step 1: Record the context, if any. */
+ ctx = 0;
+ if (iommu->iommu_ctxflush &&
+ strbuf->strbuf_ctxflush) {
+ iopte_t *iopte;
+
+ iopte = iommu->page_table +
+ ((bus_addr - iommu->page_table_map_base)>>IO_PAGE_SHIFT);
+ ctx = (iopte_val(*iopte) & IOPTE_CONTEXT) >> 47UL;
+ }
+
+ /* Step 2: Kick data out of streaming buffers. */
+ PCI_STC_FLUSHFLAG_INIT(strbuf);
+ if (iommu->iommu_ctxflush &&
+ strbuf->strbuf_ctxflush) {
+ unsigned long matchreg, flushreg;
+
+ flushreg = strbuf->strbuf_ctxflush;
+ matchreg = PCI_STC_CTXMATCH_ADDR(strbuf, ctx);
+ do {
+ pci_iommu_write(flushreg, ctx);
+ } while(((long)pci_iommu_read(matchreg)) < 0L);
+ } else {
+ unsigned long i;
+
+ for (i = 0; i < npages; i++, bus_addr += IO_PAGE_SIZE)
+ pci_iommu_write(strbuf->strbuf_pflush, bus_addr);
+ }
+
+ /* Step 3: Perform flush synchronization sequence. */
+ pci_iommu_write(strbuf->strbuf_fsync, strbuf->strbuf_flushflag_pa);
+ (void) pci_iommu_read(iommu->write_complete_reg);
+ while (!PCI_STC_FLUSHFLAG_SET(strbuf))
+ membar("#LoadLoad");
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+/* Make physical memory consistent for a set of streaming
+ * mode DMA translations after a transfer.
+ */
+void pci_dma_sync_sg_for_cpu(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
+{
+ struct pcidev_cookie *pcp;
+ struct pci_iommu *iommu;
+ struct pci_strbuf *strbuf;
+ unsigned long flags, ctx;
+
+ pcp = pdev->sysdata;
+ iommu = pcp->pbm->iommu;
+ strbuf = &pcp->pbm->stc;
+
+ if (!strbuf->strbuf_enabled)
+ return;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ /* Step 1: Record the context, if any. */
+ ctx = 0;
+ if (iommu->iommu_ctxflush &&
+ strbuf->strbuf_ctxflush) {
+ iopte_t *iopte;
+
+ iopte = iommu->page_table +
+ ((sglist[0].dma_address - iommu->page_table_map_base) >> IO_PAGE_SHIFT);
+ ctx = (iopte_val(*iopte) & IOPTE_CONTEXT) >> 47UL;
+ }
+
+ /* Step 2: Kick data out of streaming buffers. */
+ PCI_STC_FLUSHFLAG_INIT(strbuf);
+ if (iommu->iommu_ctxflush &&
+ strbuf->strbuf_ctxflush) {
+ unsigned long matchreg, flushreg;
+
+ flushreg = strbuf->strbuf_ctxflush;
+ matchreg = PCI_STC_CTXMATCH_ADDR(strbuf, ctx);
+ do {
+ pci_iommu_write(flushreg, ctx);
+ } while (((long)pci_iommu_read(matchreg)) < 0L);
+ } else {
+ unsigned long i, npages;
+ u32 bus_addr;
+
+ bus_addr = sglist[0].dma_address & IO_PAGE_MASK;
+
+ for(i = 1; i < nelems; i++)
+ if (!sglist[i].dma_length)
+ break;
+ i--;
+ npages = (IO_PAGE_ALIGN(sglist[i].dma_address + sglist[i].dma_length) - bus_addr) >> IO_PAGE_SHIFT;
+ for (i = 0; i < npages; i++, bus_addr += IO_PAGE_SIZE)
+ pci_iommu_write(strbuf->strbuf_pflush, bus_addr);
+ }
+
+ /* Step 3: Perform flush synchronization sequence. */
+ pci_iommu_write(strbuf->strbuf_fsync, strbuf->strbuf_flushflag_pa);
+ (void) pci_iommu_read(iommu->write_complete_reg);
+ while (!PCI_STC_FLUSHFLAG_SET(strbuf))
+ membar("#LoadLoad");
+
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+static void ali_sound_dma_hack(struct pci_dev *pdev, int set_bit)
+{
+ struct pci_dev *ali_isa_bridge;
+ u8 val;
+
+ /* ALI sound chips generate 31-bits of DMA, a special register
+ * determines what bit 31 is emitted as.
+ */
+ ali_isa_bridge = pci_get_device(PCI_VENDOR_ID_AL,
+ PCI_DEVICE_ID_AL_M1533,
+ NULL);
+
+ pci_read_config_byte(ali_isa_bridge, 0x7e, &val);
+ if (set_bit)
+ val |= 0x01;
+ else
+ val &= ~0x01;
+ pci_write_config_byte(ali_isa_bridge, 0x7e, val);
+ pci_dev_put(ali_isa_bridge);
+}
+
+int pci_dma_supported(struct pci_dev *pdev, u64 device_mask)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ u64 dma_addr_mask;
+
+ if (pdev == NULL) {
+ dma_addr_mask = 0xffffffff;
+ } else {
+ struct pci_iommu *iommu = pcp->pbm->iommu;
+
+ dma_addr_mask = iommu->dma_addr_mask;
+
+ if (pdev->vendor == PCI_VENDOR_ID_AL &&
+ pdev->device == PCI_DEVICE_ID_AL_M5451 &&
+ device_mask == 0x7fffffff) {
+ ali_sound_dma_hack(pdev,
+ (dma_addr_mask & 0x80000000) != 0);
+ return 1;
+ }
+ }
+
+ if (device_mask >= (1UL << 32UL))
+ return 0;
+
+ return (device_mask & dma_addr_mask) == dma_addr_mask;
+}
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
new file mode 100644
index 000000000000..3567fa879e1f
--- /dev/null
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -0,0 +1,1560 @@
+/* $Id: pci_psycho.c,v 1.33 2002/02/01 00:58:33 davem Exp $
+ * pci_psycho.c: PSYCHO/U2P specific PCI controller support.
+ *
+ * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <asm/pbm.h>
+#include <asm/iommu.h>
+#include <asm/irq.h>
+#include <asm/starfire.h>
+
+#include "pci_impl.h"
+#include "iommu_common.h"
+
+/* All PSYCHO registers are 64-bits. The following accessor
+ * routines are how they are accessed. The REG parameter
+ * is a physical address.
+ */
+#define psycho_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define psycho_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory")
+
+/* Misc. PSYCHO PCI controller register offsets and definitions. */
+#define PSYCHO_CONTROL 0x0010UL
+#define PSYCHO_CONTROL_IMPL 0xf000000000000000UL /* Implementation of this PSYCHO*/
+#define PSYCHO_CONTROL_VER 0x0f00000000000000UL /* Version of this PSYCHO */
+#define PSYCHO_CONTROL_MID 0x00f8000000000000UL /* UPA Module ID of PSYCHO */
+#define PSYCHO_CONTROL_IGN 0x0007c00000000000UL /* Interrupt Group Number */
+#define PSYCHO_CONTROL_RESV 0x00003ffffffffff0UL /* Reserved */
+#define PSYCHO_CONTROL_APCKEN 0x0000000000000008UL /* Address Parity Check Enable */
+#define PSYCHO_CONTROL_APERR 0x0000000000000004UL /* Incoming System Addr Parerr */
+#define PSYCHO_CONTROL_IAP 0x0000000000000002UL /* Invert UPA Parity */
+#define PSYCHO_CONTROL_MODE 0x0000000000000001UL /* PSYCHO clock mode */
+#define PSYCHO_PCIA_CTRL 0x2000UL
+#define PSYCHO_PCIB_CTRL 0x4000UL
+#define PSYCHO_PCICTRL_RESV1 0xfffffff000000000UL /* Reserved */
+#define PSYCHO_PCICTRL_SBH_ERR 0x0000000800000000UL /* Streaming byte hole error */
+#define PSYCHO_PCICTRL_SERR 0x0000000400000000UL /* SERR signal asserted */
+#define PSYCHO_PCICTRL_SPEED 0x0000000200000000UL /* PCI speed (1 is U2P clock) */
+#define PSYCHO_PCICTRL_RESV2 0x00000001ffc00000UL /* Reserved */
+#define PSYCHO_PCICTRL_ARB_PARK 0x0000000000200000UL /* PCI arbitration parking */
+#define PSYCHO_PCICTRL_RESV3 0x00000000001ff800UL /* Reserved */
+#define PSYCHO_PCICTRL_SBH_INT 0x0000000000000400UL /* Streaming byte hole int enab */
+#define PSYCHO_PCICTRL_WEN 0x0000000000000200UL /* Power Mgmt Wake Enable */
+#define PSYCHO_PCICTRL_EEN 0x0000000000000100UL /* PCI Error Interrupt Enable */
+#define PSYCHO_PCICTRL_RESV4 0x00000000000000c0UL /* Reserved */
+#define PSYCHO_PCICTRL_AEN 0x000000000000003fUL /* PCI DVMA Arbitration Enable */
+
+/* U2P Programmer's Manual, page 13-55, configuration space
+ * address format:
+ *
+ * 32 24 23 16 15 11 10 8 7 2 1 0
+ * ---------------------------------------------------------
+ * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 |
+ * ---------------------------------------------------------
+ */
+#define PSYCHO_CONFIG_BASE(PBM) \
+ ((PBM)->config_space | (1UL << 24))
+#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+static void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned int devfn,
+ int where)
+{
+ if (!pbm)
+ return NULL;
+ return (void *)
+ (PSYCHO_CONFIG_BASE(pbm) |
+ PSYCHO_CONFIG_ENCODE(bus, devfn, where));
+}
+
+static int psycho_out_of_range(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned char devfn)
+{
+ return ((pbm->parent == 0) ||
+ ((pbm == &pbm->parent->pbm_B) &&
+ (bus == pbm->pci_first_busno) &&
+ PCI_SLOT(devfn) > 8) ||
+ ((pbm == &pbm->parent->pbm_A) &&
+ (bus == pbm->pci_first_busno) &&
+ PCI_SLOT(devfn) > 8));
+}
+
+/* PSYCHO PCI configuration space accessors. */
+
+static int psycho_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ struct pci_pbm_info *pbm = bus_dev->sysdata;
+ unsigned char bus = bus_dev->number;
+ u32 *addr;
+ u16 tmp16;
+ u8 tmp8;
+
+ switch (size) {
+ case 1:
+ *value = 0xff;
+ break;
+ case 2:
+ *value = 0xffff;
+ break;
+ case 4:
+ *value = 0xffffffff;
+ break;
+ }
+
+ addr = psycho_pci_config_mkaddr(pbm, bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (psycho_out_of_range(pbm, bus, devfn))
+ return PCIBIOS_SUCCESSFUL;
+ switch (size) {
+ case 1:
+ pci_config_read8((u8 *)addr, &tmp8);
+ *value = (u32) tmp8;
+ break;
+
+ case 2:
+ if (where & 0x01) {
+ printk("pci_read_config_word: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_read16((u16 *)addr, &tmp16);
+ *value = (u32) tmp16;
+ break;
+
+ case 4:
+ if (where & 0x03) {
+ printk("pci_read_config_dword: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_read32(addr, value);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int psycho_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ struct pci_pbm_info *pbm = bus_dev->sysdata;
+ unsigned char bus = bus_dev->number;
+ u32 *addr;
+
+ addr = psycho_pci_config_mkaddr(pbm, bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (psycho_out_of_range(pbm, bus, devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ switch (size) {
+ case 1:
+ pci_config_write8((u8 *)addr, value);
+ break;
+
+ case 2:
+ if (where & 0x01) {
+ printk("pci_write_config_word: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_write16((u16 *)addr, value);
+ break;
+
+ case 4:
+ if (where & 0x03) {
+ printk("pci_write_config_dword: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_write32(addr, value);
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops psycho_ops = {
+ .read = psycho_read_pci_cfg,
+ .write = psycho_write_pci_cfg,
+};
+
+/* PSYCHO interrupt mapping support. */
+#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
+#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
+static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
+}
+
+#define PSYCHO_IMAP_SCSI 0x1000UL
+#define PSYCHO_IMAP_ETH 0x1008UL
+#define PSYCHO_IMAP_BPP 0x1010UL
+#define PSYCHO_IMAP_AU_REC 0x1018UL
+#define PSYCHO_IMAP_AU_PLAY 0x1020UL
+#define PSYCHO_IMAP_PFAIL 0x1028UL
+#define PSYCHO_IMAP_KMS 0x1030UL
+#define PSYCHO_IMAP_FLPY 0x1038UL
+#define PSYCHO_IMAP_SHW 0x1040UL
+#define PSYCHO_IMAP_KBD 0x1048UL
+#define PSYCHO_IMAP_MS 0x1050UL
+#define PSYCHO_IMAP_SER 0x1058UL
+#define PSYCHO_IMAP_TIM0 0x1060UL
+#define PSYCHO_IMAP_TIM1 0x1068UL
+#define PSYCHO_IMAP_UE 0x1070UL
+#define PSYCHO_IMAP_CE 0x1078UL
+#define PSYCHO_IMAP_A_ERR 0x1080UL
+#define PSYCHO_IMAP_B_ERR 0x1088UL
+#define PSYCHO_IMAP_PMGMT 0x1090UL
+#define PSYCHO_IMAP_GFX 0x1098UL
+#define PSYCHO_IMAP_EUPA 0x10a0UL
+
+static unsigned long __onboard_imap_off[] = {
+/*0x20*/ PSYCHO_IMAP_SCSI,
+/*0x21*/ PSYCHO_IMAP_ETH,
+/*0x22*/ PSYCHO_IMAP_BPP,
+/*0x23*/ PSYCHO_IMAP_AU_REC,
+/*0x24*/ PSYCHO_IMAP_AU_PLAY,
+/*0x25*/ PSYCHO_IMAP_PFAIL,
+/*0x26*/ PSYCHO_IMAP_KMS,
+/*0x27*/ PSYCHO_IMAP_FLPY,
+/*0x28*/ PSYCHO_IMAP_SHW,
+/*0x29*/ PSYCHO_IMAP_KBD,
+/*0x2a*/ PSYCHO_IMAP_MS,
+/*0x2b*/ PSYCHO_IMAP_SER,
+/*0x2c*/ PSYCHO_IMAP_TIM0,
+/*0x2d*/ PSYCHO_IMAP_TIM1,
+/*0x2e*/ PSYCHO_IMAP_UE,
+/*0x2f*/ PSYCHO_IMAP_CE,
+/*0x30*/ PSYCHO_IMAP_A_ERR,
+/*0x31*/ PSYCHO_IMAP_B_ERR,
+/*0x32*/ PSYCHO_IMAP_PMGMT
+};
+#define PSYCHO_ONBOARD_IRQ_BASE 0x20
+#define PSYCHO_ONBOARD_IRQ_LAST 0x32
+#define psycho_onboard_imap_offset(__ino) \
+ __onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE]
+
+#define PSYCHO_ICLR_A_SLOT0 0x1400UL
+#define PSYCHO_ICLR_SCSI 0x1800UL
+
+#define psycho_iclr_offset(ino) \
+ ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+/* PCI PSYCHO INO number to Sparc PIL level. */
+static unsigned char psycho_pil_table[] = {
+/*0x00*/0, 0, 0, 0, /* PCI A slot 0 Int A, B, C, D */
+/*0x04*/0, 0, 0, 0, /* PCI A slot 1 Int A, B, C, D */
+/*0x08*/0, 0, 0, 0, /* PCI A slot 2 Int A, B, C, D */
+/*0x0c*/0, 0, 0, 0, /* PCI A slot 3 Int A, B, C, D */
+/*0x10*/0, 0, 0, 0, /* PCI B slot 0 Int A, B, C, D */
+/*0x14*/0, 0, 0, 0, /* PCI B slot 1 Int A, B, C, D */
+/*0x18*/0, 0, 0, 0, /* PCI B slot 2 Int A, B, C, D */
+/*0x1c*/0, 0, 0, 0, /* PCI B slot 3 Int A, B, C, D */
+/*0x20*/4, /* SCSI */
+/*0x21*/5, /* Ethernet */
+/*0x22*/8, /* Parallel Port */
+/*0x23*/13, /* Audio Record */
+/*0x24*/14, /* Audio Playback */
+/*0x25*/15, /* PowerFail */
+/*0x26*/4, /* second SCSI */
+/*0x27*/11, /* Floppy */
+/*0x28*/4, /* Spare Hardware */
+/*0x29*/9, /* Keyboard */
+/*0x2a*/4, /* Mouse */
+/*0x2b*/12, /* Serial */
+/*0x2c*/10, /* Timer 0 */
+/*0x2d*/11, /* Timer 1 */
+/*0x2e*/15, /* Uncorrectable ECC */
+/*0x2f*/15, /* Correctable ECC */
+/*0x30*/15, /* PCI Bus A Error */
+/*0x31*/15, /* PCI Bus B Error */
+/*0x32*/15, /* Power Management */
+};
+
+static int __init psycho_ino_to_pil(struct pci_dev *pdev, unsigned int ino)
+{
+ int ret;
+
+ ret = psycho_pil_table[ino];
+ if (ret == 0 && pdev == NULL) {
+ ret = 4;
+ } else if (ret == 0) {
+ switch ((pdev->class >> 16) & 0xff) {
+ case PCI_BASE_CLASS_STORAGE:
+ ret = 4;
+ break;
+
+ case PCI_BASE_CLASS_NETWORK:
+ ret = 6;
+ break;
+
+ case PCI_BASE_CLASS_DISPLAY:
+ ret = 9;
+ break;
+
+ case PCI_BASE_CLASS_MULTIMEDIA:
+ case PCI_BASE_CLASS_MEMORY:
+ case PCI_BASE_CLASS_BRIDGE:
+ case PCI_BASE_CLASS_SERIAL:
+ ret = 10;
+ break;
+
+ default:
+ ret = 4;
+ break;
+ };
+ }
+
+ return ret;
+}
+
+static unsigned int __init psycho_irq_build(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev,
+ unsigned int ino)
+{
+ struct ino_bucket *bucket;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int pil, inofixup = 0;
+
+ ino &= PCI_IRQ_INO;
+ if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = psycho_pcislot_imap_offset(ino);
+ } else {
+ /* Onboard device */
+ if (ino > PSYCHO_ONBOARD_IRQ_LAST) {
+ prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ }
+ imap_off = psycho_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ pil = psycho_ino_to_pil(pdev, ino);
+
+ if (PIL_RESERVED(pil))
+ BUG();
+
+ imap = pbm->controller_regs + imap_off;
+ imap += 4;
+
+ iclr_off = psycho_iclr_offset(ino);
+ iclr = pbm->controller_regs + iclr_off;
+ iclr += 4;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ bucket = __bucket(build_irq(pil, inofixup, iclr, imap));
+ bucket->flags |= IBF_PCI;
+
+ return __irq(bucket);
+}
+
+/* PSYCHO error handling support. */
+enum psycho_error_type {
+ UE_ERR, CE_ERR, PCI_ERR
+};
+
+/* Helper function of IOMMU error checking, which checks out
+ * the state of the streaming buffers. The IOMMU lock is
+ * held when this is called.
+ *
+ * For the PCI error case we know which PBM (and thus which
+ * streaming buffer) caused the error, but for the uncorrectable
+ * error case we do not. So we always check both streaming caches.
+ */
+#define PSYCHO_STRBUF_CONTROL_A 0x2800UL
+#define PSYCHO_STRBUF_CONTROL_B 0x4800UL
+#define PSYCHO_STRBUF_CTRL_LPTR 0x00000000000000f0UL /* LRU Lock Pointer */
+#define PSYCHO_STRBUF_CTRL_LENAB 0x0000000000000008UL /* LRU Lock Enable */
+#define PSYCHO_STRBUF_CTRL_RRDIS 0x0000000000000004UL /* Rerun Disable */
+#define PSYCHO_STRBUF_CTRL_DENAB 0x0000000000000002UL /* Diagnostic Mode Enable */
+#define PSYCHO_STRBUF_CTRL_ENAB 0x0000000000000001UL /* Streaming Buffer Enable */
+#define PSYCHO_STRBUF_FLUSH_A 0x2808UL
+#define PSYCHO_STRBUF_FLUSH_B 0x4808UL
+#define PSYCHO_STRBUF_FSYNC_A 0x2810UL
+#define PSYCHO_STRBUF_FSYNC_B 0x4810UL
+#define PSYCHO_STC_DATA_A 0xb000UL
+#define PSYCHO_STC_DATA_B 0xc000UL
+#define PSYCHO_STC_ERR_A 0xb400UL
+#define PSYCHO_STC_ERR_B 0xc400UL
+#define PSYCHO_STCERR_WRITE 0x0000000000000002UL /* Write Error */
+#define PSYCHO_STCERR_READ 0x0000000000000001UL /* Read Error */
+#define PSYCHO_STC_TAG_A 0xb800UL
+#define PSYCHO_STC_TAG_B 0xc800UL
+#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL /* Physical Page Number */
+#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL /* Virtual Page Number */
+#define PSYCHO_STCTAG_VALID 0x0000000000000002UL /* Valid */
+#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL /* Writable */
+#define PSYCHO_STC_LINE_A 0xb900UL
+#define PSYCHO_STC_LINE_B 0xc900UL
+#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL /* LRU Index */
+#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL /* Dirty Data Start Pointer */
+#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL /* Line Address */
+#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL /* Dirty Data End Pointer */
+#define PSYCHO_STCLINE_VALID 0x0000000000000002UL /* Valid */
+#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL /* Fetch Outstanding / Flush Necessary */
+
+static DEFINE_SPINLOCK(stc_buf_lock);
+static unsigned long stc_error_buf[128];
+static unsigned long stc_tag_buf[16];
+static unsigned long stc_line_buf[16];
+
+static void __psycho_check_one_stc(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm,
+ int is_pbm_a)
+{
+ struct pci_strbuf *strbuf = &pbm->stc;
+ unsigned long regbase = p->pbm_A.controller_regs;
+ unsigned long err_base, tag_base, line_base;
+ u64 control;
+ int i;
+
+ if (is_pbm_a) {
+ err_base = regbase + PSYCHO_STC_ERR_A;
+ tag_base = regbase + PSYCHO_STC_TAG_A;
+ line_base = regbase + PSYCHO_STC_LINE_A;
+ } else {
+ err_base = regbase + PSYCHO_STC_ERR_B;
+ tag_base = regbase + PSYCHO_STC_TAG_B;
+ line_base = regbase + PSYCHO_STC_LINE_B;
+ }
+
+ spin_lock(&stc_buf_lock);
+
+ /* This is __REALLY__ dangerous. When we put the
+ * streaming buffer into diagnostic mode to probe
+ * it's tags and error status, we _must_ clear all
+ * of the line tag valid bits before re-enabling
+ * the streaming buffer. If any dirty data lives
+ * in the STC when we do this, we will end up
+ * invalidating it before it has a chance to reach
+ * main memory.
+ */
+ control = psycho_read(strbuf->strbuf_control);
+ psycho_write(strbuf->strbuf_control,
+ (control | PSYCHO_STRBUF_CTRL_DENAB));
+ for (i = 0; i < 128; i++) {
+ unsigned long val;
+
+ val = psycho_read(err_base + (i * 8UL));
+ psycho_write(err_base + (i * 8UL), 0UL);
+ stc_error_buf[i] = val;
+ }
+ for (i = 0; i < 16; i++) {
+ stc_tag_buf[i] = psycho_read(tag_base + (i * 8UL));
+ stc_line_buf[i] = psycho_read(line_base + (i * 8UL));
+ psycho_write(tag_base + (i * 8UL), 0UL);
+ psycho_write(line_base + (i * 8UL), 0UL);
+ }
+
+ /* OK, state is logged, exit diagnostic mode. */
+ psycho_write(strbuf->strbuf_control, control);
+
+ for (i = 0; i < 16; i++) {
+ int j, saw_error, first, last;
+
+ saw_error = 0;
+ first = i * 8;
+ last = first + 8;
+ for (j = first; j < last; j++) {
+ unsigned long errval = stc_error_buf[j];
+ if (errval != 0) {
+ saw_error++;
+ printk("PSYCHO%d(PBM%c): STC_ERR(%d)[wr(%d)rd(%d)]\n",
+ p->index,
+ (is_pbm_a ? 'A' : 'B'),
+ j,
+ (errval & PSYCHO_STCERR_WRITE) ? 1 : 0,
+ (errval & PSYCHO_STCERR_READ) ? 1 : 0);
+ }
+ }
+ if (saw_error != 0) {
+ unsigned long tagval = stc_tag_buf[i];
+ unsigned long lineval = stc_line_buf[i];
+ printk("PSYCHO%d(PBM%c): STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)W(%d)]\n",
+ p->index,
+ (is_pbm_a ? 'A' : 'B'),
+ i,
+ ((tagval & PSYCHO_STCTAG_PPN) >> 19UL),
+ (tagval & PSYCHO_STCTAG_VPN),
+ ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0),
+ ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0));
+ printk("PSYCHO%d(PBM%c): STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)"
+ "V(%d)FOFN(%d)]\n",
+ p->index,
+ (is_pbm_a ? 'A' : 'B'),
+ i,
+ ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL),
+ ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL),
+ ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL),
+ ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL),
+ ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0),
+ ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0));
+ }
+ }
+
+ spin_unlock(&stc_buf_lock);
+}
+
+static void __psycho_check_stc_error(struct pci_controller_info *p,
+ unsigned long afsr,
+ unsigned long afar,
+ enum psycho_error_type type)
+{
+ struct pci_pbm_info *pbm;
+
+ pbm = &p->pbm_A;
+ if (pbm->stc.strbuf_enabled)
+ __psycho_check_one_stc(p, pbm, 1);
+
+ pbm = &p->pbm_B;
+ if (pbm->stc.strbuf_enabled)
+ __psycho_check_one_stc(p, pbm, 0);
+}
+
+/* When an Uncorrectable Error or a PCI Error happens, we
+ * interrogate the IOMMU state to see if it is the cause.
+ */
+#define PSYCHO_IOMMU_CONTROL 0x0200UL
+#define PSYCHO_IOMMU_CTRL_RESV 0xfffffffff9000000UL /* Reserved */
+#define PSYCHO_IOMMU_CTRL_XLTESTAT 0x0000000006000000UL /* Translation Error Status */
+#define PSYCHO_IOMMU_CTRL_XLTEERR 0x0000000001000000UL /* Translation Error encountered */
+#define PSYCHO_IOMMU_CTRL_LCKEN 0x0000000000800000UL /* Enable translation locking */
+#define PSYCHO_IOMMU_CTRL_LCKPTR 0x0000000000780000UL /* Translation lock pointer */
+#define PSYCHO_IOMMU_CTRL_TSBSZ 0x0000000000070000UL /* TSB Size */
+#define PSYCHO_IOMMU_TSBSZ_1K 0x0000000000000000UL /* TSB Table 1024 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_2K 0x0000000000010000UL /* TSB Table 2048 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_4K 0x0000000000020000UL /* TSB Table 4096 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_8K 0x0000000000030000UL /* TSB Table 8192 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_16K 0x0000000000040000UL /* TSB Table 16k 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_32K 0x0000000000050000UL /* TSB Table 32k 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_64K 0x0000000000060000UL /* TSB Table 64k 8-byte entries */
+#define PSYCHO_IOMMU_TSBSZ_128K 0x0000000000070000UL /* TSB Table 128k 8-byte entries */
+#define PSYCHO_IOMMU_CTRL_RESV2 0x000000000000fff8UL /* Reserved */
+#define PSYCHO_IOMMU_CTRL_TBWSZ 0x0000000000000004UL /* Assumed page size, 0=8k 1=64k */
+#define PSYCHO_IOMMU_CTRL_DENAB 0x0000000000000002UL /* Diagnostic mode enable */
+#define PSYCHO_IOMMU_CTRL_ENAB 0x0000000000000001UL /* IOMMU Enable */
+#define PSYCHO_IOMMU_TSBBASE 0x0208UL
+#define PSYCHO_IOMMU_FLUSH 0x0210UL
+#define PSYCHO_IOMMU_TAG 0xa580UL
+#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL)
+#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL)
+#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL)
+#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL)
+#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL)
+#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL
+#define PSYCHO_IOMMU_DATA 0xa600UL
+#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL)
+#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL)
+#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL
+static void psycho_check_iommu_error(struct pci_controller_info *p,
+ unsigned long afsr,
+ unsigned long afar,
+ enum psycho_error_type type)
+{
+ struct pci_iommu *iommu = p->pbm_A.iommu;
+ unsigned long iommu_tag[16];
+ unsigned long iommu_data[16];
+ unsigned long flags;
+ u64 control;
+ int i;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+ control = psycho_read(iommu->iommu_control);
+ if (control & PSYCHO_IOMMU_CTRL_XLTEERR) {
+ char *type_string;
+
+ /* Clear the error encountered bit. */
+ control &= ~PSYCHO_IOMMU_CTRL_XLTEERR;
+ psycho_write(iommu->iommu_control, control);
+
+ switch((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
+ case 0:
+ type_string = "Protection Error";
+ break;
+ case 1:
+ type_string = "Invalid Error";
+ break;
+ case 2:
+ type_string = "TimeOut Error";
+ break;
+ case 3:
+ default:
+ type_string = "ECC Error";
+ break;
+ };
+ printk("PSYCHO%d: IOMMU Error, type[%s]\n",
+ p->index, type_string);
+
+ /* Put the IOMMU into diagnostic mode and probe
+ * it's TLB for entries with error status.
+ *
+ * It is very possible for another DVMA to occur
+ * while we do this probe, and corrupt the system
+ * further. But we are so screwed at this point
+ * that we are likely to crash hard anyways, so
+ * get as much diagnostic information to the
+ * console as we can.
+ */
+ psycho_write(iommu->iommu_control,
+ control | PSYCHO_IOMMU_CTRL_DENAB);
+ for (i = 0; i < 16; i++) {
+ unsigned long base = p->pbm_A.controller_regs;
+
+ iommu_tag[i] =
+ psycho_read(base + PSYCHO_IOMMU_TAG + (i * 8UL));
+ iommu_data[i] =
+ psycho_read(base + PSYCHO_IOMMU_DATA + (i * 8UL));
+
+ /* Now clear out the entry. */
+ psycho_write(base + PSYCHO_IOMMU_TAG + (i * 8UL), 0);
+ psycho_write(base + PSYCHO_IOMMU_DATA + (i * 8UL), 0);
+ }
+
+ /* Leave diagnostic mode. */
+ psycho_write(iommu->iommu_control, control);
+
+ for (i = 0; i < 16; i++) {
+ unsigned long tag, data;
+
+ tag = iommu_tag[i];
+ if (!(tag & PSYCHO_IOMMU_TAG_ERR))
+ continue;
+
+ data = iommu_data[i];
+ switch((tag & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) {
+ case 0:
+ type_string = "Protection Error";
+ break;
+ case 1:
+ type_string = "Invalid Error";
+ break;
+ case 2:
+ type_string = "TimeOut Error";
+ break;
+ case 3:
+ default:
+ type_string = "ECC Error";
+ break;
+ };
+ printk("PSYCHO%d: IOMMU TAG(%d)[error(%s) wr(%d) str(%d) sz(%dK) vpg(%08lx)]\n",
+ p->index, i, type_string,
+ ((tag & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0),
+ ((tag & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0),
+ ((tag & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8),
+ (tag & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT);
+ printk("PSYCHO%d: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n",
+ p->index, i,
+ ((data & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0),
+ ((data & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0),
+ (data & PSYCHO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT);
+ }
+ }
+ __psycho_check_stc_error(p, afsr, afar, type);
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+/* Uncorrectable Errors. Cause of the error and the address are
+ * recorded in the UE_AFSR and UE_AFAR of PSYCHO. They are errors
+ * relating to UPA interface transactions.
+ */
+#define PSYCHO_UE_AFSR 0x0030UL
+#define PSYCHO_UEAFSR_PPIO 0x8000000000000000UL /* Primary PIO is cause */
+#define PSYCHO_UEAFSR_PDRD 0x4000000000000000UL /* Primary DVMA read is cause */
+#define PSYCHO_UEAFSR_PDWR 0x2000000000000000UL /* Primary DVMA write is cause */
+#define PSYCHO_UEAFSR_SPIO 0x1000000000000000UL /* Secondary PIO is cause */
+#define PSYCHO_UEAFSR_SDRD 0x0800000000000000UL /* Secondary DVMA read is cause */
+#define PSYCHO_UEAFSR_SDWR 0x0400000000000000UL /* Secondary DVMA write is cause*/
+#define PSYCHO_UEAFSR_RESV1 0x03ff000000000000UL /* Reserved */
+#define PSYCHO_UEAFSR_BMSK 0x0000ffff00000000UL /* Bytemask of failed transfer */
+#define PSYCHO_UEAFSR_DOFF 0x00000000e0000000UL /* Doubleword Offset */
+#define PSYCHO_UEAFSR_MID 0x000000001f000000UL /* UPA MID causing the fault */
+#define PSYCHO_UEAFSR_BLK 0x0000000000800000UL /* Trans was block operation */
+#define PSYCHO_UEAFSR_RESV2 0x00000000007fffffUL /* Reserved */
+#define PSYCHO_UE_AFAR 0x0038UL
+
+static irqreturn_t psycho_ue_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg = p->pbm_A.controller_regs + PSYCHO_UE_AFSR;
+ unsigned long afar_reg = p->pbm_A.controller_regs + PSYCHO_UE_AFAR;
+ unsigned long afsr, afar, error_bits;
+ int reported;
+
+ /* Latch uncorrectable error status. */
+ afar = psycho_read(afar_reg);
+ afsr = psycho_read(afsr_reg);
+
+ /* Clear the primary/secondary error status bits. */
+ error_bits = afsr &
+ (PSYCHO_UEAFSR_PPIO | PSYCHO_UEAFSR_PDRD | PSYCHO_UEAFSR_PDWR |
+ PSYCHO_UEAFSR_SPIO | PSYCHO_UEAFSR_SDRD | PSYCHO_UEAFSR_SDWR);
+ if (!error_bits)
+ return IRQ_NONE;
+ psycho_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("PSYCHO%d: Uncorrectable Error, primary error type[%s]\n",
+ p->index,
+ (((error_bits & PSYCHO_UEAFSR_PPIO) ?
+ "PIO" :
+ ((error_bits & PSYCHO_UEAFSR_PDRD) ?
+ "DMA Read" :
+ ((error_bits & PSYCHO_UEAFSR_PDWR) ?
+ "DMA Write" : "???")))));
+ printk("PSYCHO%d: bytemask[%04lx] dword_offset[%lx] UPA_MID[%02lx] was_block(%d)\n",
+ p->index,
+ (afsr & PSYCHO_UEAFSR_BMSK) >> 32UL,
+ (afsr & PSYCHO_UEAFSR_DOFF) >> 29UL,
+ (afsr & PSYCHO_UEAFSR_MID) >> 24UL,
+ ((afsr & PSYCHO_UEAFSR_BLK) ? 1 : 0));
+ printk("PSYCHO%d: UE AFAR [%016lx]\n", p->index, afar);
+ printk("PSYCHO%d: UE Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & PSYCHO_UEAFSR_SPIO) {
+ reported++;
+ printk("(PIO)");
+ }
+ if (afsr & PSYCHO_UEAFSR_SDRD) {
+ reported++;
+ printk("(DMA Read)");
+ }
+ if (afsr & PSYCHO_UEAFSR_SDWR) {
+ reported++;
+ printk("(DMA Write)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ /* Interrogate IOMMU for error status. */
+ psycho_check_iommu_error(p, afsr, afar, UE_ERR);
+
+ return IRQ_HANDLED;
+}
+
+/* Correctable Errors. */
+#define PSYCHO_CE_AFSR 0x0040UL
+#define PSYCHO_CEAFSR_PPIO 0x8000000000000000UL /* Primary PIO is cause */
+#define PSYCHO_CEAFSR_PDRD 0x4000000000000000UL /* Primary DVMA read is cause */
+#define PSYCHO_CEAFSR_PDWR 0x2000000000000000UL /* Primary DVMA write is cause */
+#define PSYCHO_CEAFSR_SPIO 0x1000000000000000UL /* Secondary PIO is cause */
+#define PSYCHO_CEAFSR_SDRD 0x0800000000000000UL /* Secondary DVMA read is cause */
+#define PSYCHO_CEAFSR_SDWR 0x0400000000000000UL /* Secondary DVMA write is cause*/
+#define PSYCHO_CEAFSR_RESV1 0x0300000000000000UL /* Reserved */
+#define PSYCHO_CEAFSR_ESYND 0x00ff000000000000UL /* Syndrome Bits */
+#define PSYCHO_CEAFSR_BMSK 0x0000ffff00000000UL /* Bytemask of failed transfer */
+#define PSYCHO_CEAFSR_DOFF 0x00000000e0000000UL /* Double Offset */
+#define PSYCHO_CEAFSR_MID 0x000000001f000000UL /* UPA MID causing the fault */
+#define PSYCHO_CEAFSR_BLK 0x0000000000800000UL /* Trans was block operation */
+#define PSYCHO_CEAFSR_RESV2 0x00000000007fffffUL /* Reserved */
+#define PSYCHO_CE_AFAR 0x0040UL
+
+static irqreturn_t psycho_ce_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg = p->pbm_A.controller_regs + PSYCHO_CE_AFSR;
+ unsigned long afar_reg = p->pbm_A.controller_regs + PSYCHO_CE_AFAR;
+ unsigned long afsr, afar, error_bits;
+ int reported;
+
+ /* Latch error status. */
+ afar = psycho_read(afar_reg);
+ afsr = psycho_read(afsr_reg);
+
+ /* Clear primary/secondary error status bits. */
+ error_bits = afsr &
+ (PSYCHO_CEAFSR_PPIO | PSYCHO_CEAFSR_PDRD | PSYCHO_CEAFSR_PDWR |
+ PSYCHO_CEAFSR_SPIO | PSYCHO_CEAFSR_SDRD | PSYCHO_CEAFSR_SDWR);
+ if (!error_bits)
+ return IRQ_NONE;
+ psycho_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("PSYCHO%d: Correctable Error, primary error type[%s]\n",
+ p->index,
+ (((error_bits & PSYCHO_CEAFSR_PPIO) ?
+ "PIO" :
+ ((error_bits & PSYCHO_CEAFSR_PDRD) ?
+ "DMA Read" :
+ ((error_bits & PSYCHO_CEAFSR_PDWR) ?
+ "DMA Write" : "???")))));
+
+ /* XXX Use syndrome and afar to print out module string just like
+ * XXX UDB CE trap handler does... -DaveM
+ */
+ printk("PSYCHO%d: syndrome[%02lx] bytemask[%04lx] dword_offset[%lx] "
+ "UPA_MID[%02lx] was_block(%d)\n",
+ p->index,
+ (afsr & PSYCHO_CEAFSR_ESYND) >> 48UL,
+ (afsr & PSYCHO_CEAFSR_BMSK) >> 32UL,
+ (afsr & PSYCHO_CEAFSR_DOFF) >> 29UL,
+ (afsr & PSYCHO_CEAFSR_MID) >> 24UL,
+ ((afsr & PSYCHO_CEAFSR_BLK) ? 1 : 0));
+ printk("PSYCHO%d: CE AFAR [%016lx]\n", p->index, afar);
+ printk("PSYCHO%d: CE Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & PSYCHO_CEAFSR_SPIO) {
+ reported++;
+ printk("(PIO)");
+ }
+ if (afsr & PSYCHO_CEAFSR_SDRD) {
+ reported++;
+ printk("(DMA Read)");
+ }
+ if (afsr & PSYCHO_CEAFSR_SDWR) {
+ reported++;
+ printk("(DMA Write)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ return IRQ_HANDLED;
+}
+
+/* PCI Errors. They are signalled by the PCI bus module since they
+ * are associated with a specific bus segment.
+ */
+#define PSYCHO_PCI_AFSR_A 0x2010UL
+#define PSYCHO_PCI_AFSR_B 0x4010UL
+#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL /* Primary Master Abort Error */
+#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL /* Primary Target Abort Error */
+#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL /* Primary Excessive Retries */
+#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL /* Primary Parity Error */
+#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL /* Secondary Master Abort Error */
+#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL /* Secondary Target Abort Error */
+#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL /* Secondary Excessive Retries */
+#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL /* Secondary Parity Error */
+#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL /* Reserved */
+#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL /* Bytemask of failed transfer */
+#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL /* Trans was block operation */
+#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL /* Reserved */
+#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL /* MID causing the error */
+#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL /* Reserved */
+#define PSYCHO_PCI_AFAR_A 0x2018UL
+#define PSYCHO_PCI_AFAR_B 0x4018UL
+
+static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm, int is_pbm_a)
+{
+ unsigned long csr_reg, csr, csr_error_bits;
+ irqreturn_t ret = IRQ_NONE;
+ u16 stat;
+
+ if (is_pbm_a) {
+ csr_reg = pbm->controller_regs + PSYCHO_PCIA_CTRL;
+ } else {
+ csr_reg = pbm->controller_regs + PSYCHO_PCIB_CTRL;
+ }
+ csr = psycho_read(csr_reg);
+ csr_error_bits =
+ csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR);
+ if (csr_error_bits) {
+ /* Clear the errors. */
+ psycho_write(csr_reg, csr);
+
+ /* Log 'em. */
+ if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR)
+ printk("%s: PCI streaming byte hole error asserted.\n",
+ pbm->name);
+ if (csr_error_bits & PSYCHO_PCICTRL_SERR)
+ printk("%s: PCI SERR signal asserted.\n", pbm->name);
+ ret = IRQ_HANDLED;
+ }
+ pci_read_config_word(pbm->pci_bus->self, PCI_STATUS, &stat);
+ if (stat & (PCI_STATUS_PARITY |
+ PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT |
+ PCI_STATUS_REC_MASTER_ABORT |
+ PCI_STATUS_SIG_SYSTEM_ERROR)) {
+ printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
+ pbm->name, stat);
+ pci_write_config_word(pbm->pci_bus->self, PCI_STATUS, 0xffff);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+
+static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_pbm_info *pbm = dev_id;
+ struct pci_controller_info *p = pbm->parent;
+ unsigned long afsr_reg, afar_reg;
+ unsigned long afsr, afar, error_bits;
+ int is_pbm_a, reported;
+
+ is_pbm_a = (pbm == &pbm->parent->pbm_A);
+ if (is_pbm_a) {
+ afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_A;
+ afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_A;
+ } else {
+ afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_B;
+ afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_B;
+ }
+
+ /* Latch error status. */
+ afar = psycho_read(afar_reg);
+ afsr = psycho_read(afsr_reg);
+
+ /* Clear primary/secondary error status bits. */
+ error_bits = afsr &
+ (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA |
+ PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR |
+ PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA |
+ PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR);
+ if (!error_bits)
+ return psycho_pcierr_intr_other(pbm, is_pbm_a);
+ psycho_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("PSYCHO%d(PBM%c): PCI Error, primary error type[%s]\n",
+ p->index, (is_pbm_a ? 'A' : 'B'),
+ (((error_bits & PSYCHO_PCIAFSR_PMA) ?
+ "Master Abort" :
+ ((error_bits & PSYCHO_PCIAFSR_PTA) ?
+ "Target Abort" :
+ ((error_bits & PSYCHO_PCIAFSR_PRTRY) ?
+ "Excessive Retries" :
+ ((error_bits & PSYCHO_PCIAFSR_PPERR) ?
+ "Parity Error" : "???"))))));
+ printk("PSYCHO%d(PBM%c): bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n",
+ p->index, (is_pbm_a ? 'A' : 'B'),
+ (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL,
+ (afsr & PSYCHO_PCIAFSR_MID) >> 25UL,
+ (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0);
+ printk("PSYCHO%d(PBM%c): PCI AFAR [%016lx]\n",
+ p->index, (is_pbm_a ? 'A' : 'B'), afar);
+ printk("PSYCHO%d(PBM%c): PCI Secondary errors [",
+ p->index, (is_pbm_a ? 'A' : 'B'));
+ reported = 0;
+ if (afsr & PSYCHO_PCIAFSR_SMA) {
+ reported++;
+ printk("(Master Abort)");
+ }
+ if (afsr & PSYCHO_PCIAFSR_STA) {
+ reported++;
+ printk("(Target Abort)");
+ }
+ if (afsr & PSYCHO_PCIAFSR_SRTRY) {
+ reported++;
+ printk("(Excessive Retries)");
+ }
+ if (afsr & PSYCHO_PCIAFSR_SPERR) {
+ reported++;
+ printk("(Parity Error)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ /* For the error types shown, scan PBM's PCI bus for devices
+ * which have logged that error type.
+ */
+
+ /* If we see a Target Abort, this could be the result of an
+ * IOMMU translation error of some sort. It is extremely
+ * useful to log this information as usually it indicates
+ * a bug in the IOMMU support code or a PCI device driver.
+ */
+ if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) {
+ psycho_check_iommu_error(p, afsr, afar, PCI_ERR);
+ pci_scan_for_target_abort(p, pbm, pbm->pci_bus);
+ }
+ if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA))
+ pci_scan_for_master_abort(p, pbm, pbm->pci_bus);
+
+ /* For excessive retries, PSYCHO/PBM will abort the device
+ * and there is no way to specifically check for excessive
+ * retries in the config space status registers. So what
+ * we hope is that we'll catch it via the master/target
+ * abort events.
+ */
+
+ if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR))
+ pci_scan_for_parity_error(p, pbm, pbm->pci_bus);
+
+ return IRQ_HANDLED;
+}
+
+/* XXX What about PowerFail/PowerManagement??? -DaveM */
+#define PSYCHO_ECC_CTRL 0x0020
+#define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */
+#define PSYCHO_ECCCTRL_UE 0x4000000000000000UL /* Enable UE Interrupts */
+#define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */
+#define PSYCHO_UE_INO 0x2e
+#define PSYCHO_CE_INO 0x2f
+#define PSYCHO_PCIERR_A_INO 0x30
+#define PSYCHO_PCIERR_B_INO 0x31
+static void __init psycho_register_error_handlers(struct pci_controller_info *p)
+{
+ struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */
+ unsigned long base = p->pbm_A.controller_regs;
+ unsigned int irq, portid = pbm->portid;
+ u64 tmp;
+
+ /* Build IRQs and register handlers. */
+ irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_UE_INO);
+ if (request_irq(irq, psycho_ue_intr,
+ SA_SHIRQ, "PSYCHO UE", p) < 0) {
+ prom_printf("PSYCHO%d: Cannot register UE interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_CE_INO);
+ if (request_irq(irq, psycho_ce_intr,
+ SA_SHIRQ, "PSYCHO CE", p) < 0) {
+ prom_printf("PSYCHO%d: Cannot register CE interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ pbm = &p->pbm_A;
+ irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_A_INO);
+ if (request_irq(irq, psycho_pcierr_intr,
+ SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_A) < 0) {
+ prom_printf("PSYCHO%d(PBMA): Cannot register PciERR interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ pbm = &p->pbm_B;
+ irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_B_INO);
+ if (request_irq(irq, psycho_pcierr_intr,
+ SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_B) < 0) {
+ prom_printf("PSYCHO%d(PBMB): Cannot register PciERR interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ /* Enable UE and CE interrupts for controller. */
+ psycho_write(base + PSYCHO_ECC_CTRL,
+ (PSYCHO_ECCCTRL_EE |
+ PSYCHO_ECCCTRL_UE |
+ PSYCHO_ECCCTRL_CE));
+
+ /* Enable PCI Error interrupts and clear error
+ * bits for each PBM.
+ */
+ tmp = psycho_read(base + PSYCHO_PCIA_CTRL);
+ tmp |= (PSYCHO_PCICTRL_SERR |
+ PSYCHO_PCICTRL_SBH_ERR |
+ PSYCHO_PCICTRL_EEN);
+ tmp &= ~(PSYCHO_PCICTRL_SBH_INT);
+ psycho_write(base + PSYCHO_PCIA_CTRL, tmp);
+
+ tmp = psycho_read(base + PSYCHO_PCIB_CTRL);
+ tmp |= (PSYCHO_PCICTRL_SERR |
+ PSYCHO_PCICTRL_SBH_ERR |
+ PSYCHO_PCICTRL_EEN);
+ tmp &= ~(PSYCHO_PCICTRL_SBH_INT);
+ psycho_write(base + PSYCHO_PCIB_CTRL, tmp);
+}
+
+/* PSYCHO boot time probing and initialization. */
+static void __init psycho_resource_adjust(struct pci_dev *pdev,
+ struct resource *res,
+ struct resource *root)
+{
+ res->start += root->start;
+ res->end += root->start;
+}
+
+static void __init psycho_base_address_update(struct pci_dev *pdev, int resource)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm = pcp->pbm;
+ struct resource *res, *root;
+ u32 reg;
+ int where, size, is_64bit;
+
+ res = &pdev->resource[resource];
+ if (resource < 6) {
+ where = PCI_BASE_ADDRESS_0 + (resource * 4);
+ } else if (resource == PCI_ROM_RESOURCE) {
+ where = pdev->rom_base_reg;
+ } else {
+ /* Somebody might have asked allocation of a non-standard resource */
+ return;
+ }
+
+ is_64bit = 0;
+ if (res->flags & IORESOURCE_IO)
+ root = &pbm->io_space;
+ else {
+ root = &pbm->mem_space;
+ if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
+ == PCI_BASE_ADDRESS_MEM_TYPE_64)
+ is_64bit = 1;
+ }
+
+ size = res->end - res->start;
+ pci_read_config_dword(pdev, where, &reg);
+ reg = ((reg & size) |
+ (((u32)(res->start - root->start)) & ~size));
+ if (resource == PCI_ROM_RESOURCE) {
+ reg |= PCI_ROM_ADDRESS_ENABLE;
+ res->flags |= IORESOURCE_ROM_ENABLE;
+ }
+ pci_write_config_dword(pdev, where, reg);
+
+ /* This knows that the upper 32-bits of the address
+ * must be zero. Our PCI common layer enforces this.
+ */
+ if (is_64bit)
+ pci_write_config_dword(pdev, where + 4, 0);
+}
+
+static void __init pbm_config_busmastering(struct pci_pbm_info *pbm)
+{
+ u8 *addr;
+
+ /* Set cache-line size to 64 bytes, this is actually
+ * a nop but I do it for completeness.
+ */
+ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
+ 0, PCI_CACHE_LINE_SIZE);
+ pci_config_write8(addr, 64 / sizeof(u32));
+
+ /* Set PBM latency timer to 64 PCI clocks. */
+ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
+ 0, PCI_LATENCY_TIMER);
+ pci_config_write8(addr, 64);
+}
+
+static void __init pbm_scan_bus(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm)
+{
+ struct pcidev_cookie *cookie = kmalloc(sizeof(*cookie), GFP_KERNEL);
+
+ if (!cookie) {
+ prom_printf("PSYCHO: Critical allocation failure.\n");
+ prom_halt();
+ }
+
+ /* All we care about is the PBM. */
+ memset(cookie, 0, sizeof(*cookie));
+ cookie->pbm = pbm;
+
+ pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno,
+ p->pci_ops,
+ pbm);
+ pci_fixup_host_bridge_self(pbm->pci_bus);
+ pbm->pci_bus->self->sysdata = cookie;
+
+ pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
+ pci_record_assignments(pbm, pbm->pci_bus);
+ pci_assign_unassigned(pbm, pbm->pci_bus);
+ pci_fixup_irq(pbm, pbm->pci_bus);
+ pci_determine_66mhz_disposition(pbm, pbm->pci_bus);
+ pci_setup_busmastering(pbm, pbm->pci_bus);
+}
+
+static void __init psycho_scan_bus(struct pci_controller_info *p)
+{
+ pbm_config_busmastering(&p->pbm_B);
+ p->pbm_B.is_66mhz_capable = 0;
+ pbm_config_busmastering(&p->pbm_A);
+ p->pbm_A.is_66mhz_capable = 1;
+ pbm_scan_bus(p, &p->pbm_B);
+ pbm_scan_bus(p, &p->pbm_A);
+
+ /* After the PCI bus scan is complete, we can register
+ * the error interrupt handlers.
+ */
+ psycho_register_error_handlers(p);
+}
+
+static void __init psycho_iommu_init(struct pci_controller_info *p)
+{
+ struct pci_iommu *iommu = p->pbm_A.iommu;
+ unsigned long tsbbase, i;
+ u64 control;
+
+ /* Setup initial software IOMMU state. */
+ spin_lock_init(&iommu->lock);
+ iommu->iommu_cur_ctx = 0;
+
+ /* Register addresses. */
+ iommu->iommu_control = p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL;
+ iommu->iommu_tsbbase = p->pbm_A.controller_regs + PSYCHO_IOMMU_TSBBASE;
+ iommu->iommu_flush = p->pbm_A.controller_regs + PSYCHO_IOMMU_FLUSH;
+ /* PSYCHO's IOMMU lacks ctx flushing. */
+ iommu->iommu_ctxflush = 0;
+
+ /* We use the main control register of PSYCHO as the write
+ * completion register.
+ */
+ iommu->write_complete_reg = p->pbm_A.controller_regs + PSYCHO_CONTROL;
+
+ /*
+ * Invalidate TLB Entries.
+ */
+ control = psycho_read(p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL);
+ control |= PSYCHO_IOMMU_CTRL_DENAB;
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL, control);
+ for(i = 0; i < 16; i++) {
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_TAG + (i * 8UL), 0);
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_DATA + (i * 8UL), 0);
+ }
+
+ /* Leave diag mode enabled for full-flushing done
+ * in pci_iommu.c
+ */
+
+ iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0);
+ if (!iommu->dummy_page) {
+ prom_printf("PSYCHO_IOMMU: Error, gfp(dummy_page) failed.\n");
+ prom_halt();
+ }
+ memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
+ iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
+
+ /* Using assumed page size 8K with 128K entries we need 1MB iommu page
+ * table (128K ioptes * 8 bytes per iopte). This is
+ * page order 7 on UltraSparc.
+ */
+ tsbbase = __get_free_pages(GFP_KERNEL, get_order(IO_TSB_SIZE));
+ if (!tsbbase) {
+ prom_printf("PSYCHO_IOMMU: Error, gfp(tsb) failed.\n");
+ prom_halt();
+ }
+ iommu->page_table = (iopte_t *)tsbbase;
+ iommu->page_table_sz_bits = 17;
+ iommu->page_table_map_base = 0xc0000000;
+ iommu->dma_addr_mask = 0xffffffff;
+ pci_iommu_table_init(iommu, IO_TSB_SIZE);
+
+ /* We start with no consistent mappings. */
+ iommu->lowest_consistent_map =
+ 1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS);
+
+ for (i = 0; i < PBM_NCLUSTERS; i++) {
+ iommu->alloc_info[i].flush = 0;
+ iommu->alloc_info[i].next = 0;
+ }
+
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_TSBBASE, __pa(tsbbase));
+
+ control = psycho_read(p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL);
+ control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ);
+ control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB);
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IOMMU_CONTROL, control);
+
+ /* If necessary, hook us up for starfire IRQ translations. */
+ if(this_is_starfire)
+ p->starfire_cookie = starfire_hookup(p->pbm_A.portid);
+ else
+ p->starfire_cookie = NULL;
+}
+
+#define PSYCHO_IRQ_RETRY 0x1a00UL
+#define PSYCHO_PCIA_DIAG 0x2020UL
+#define PSYCHO_PCIB_DIAG 0x4020UL
+#define PSYCHO_PCIDIAG_RESV 0xffffffffffffff80UL /* Reserved */
+#define PSYCHO_PCIDIAG_DRETRY 0x0000000000000040UL /* Disable retry limit */
+#define PSYCHO_PCIDIAG_DISYNC 0x0000000000000020UL /* Disable DMA wr / irq sync */
+#define PSYCHO_PCIDIAG_DDWSYNC 0x0000000000000010UL /* Disable DMA wr / PIO rd sync */
+#define PSYCHO_PCIDIAG_IDDPAR 0x0000000000000008UL /* Invert DMA data parity */
+#define PSYCHO_PCIDIAG_IPDPAR 0x0000000000000004UL /* Invert PIO data parity */
+#define PSYCHO_PCIDIAG_IPAPAR 0x0000000000000002UL /* Invert PIO address parity */
+#define PSYCHO_PCIDIAG_LPBACK 0x0000000000000001UL /* Enable loopback mode */
+
+static void psycho_controller_hwinit(struct pci_controller_info *p)
+{
+ u64 tmp;
+
+ /* PROM sets the IRQ retry value too low, increase it. */
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_IRQ_RETRY, 0xff);
+
+ /* Enable arbiter for all PCI slots. */
+ tmp = psycho_read(p->pbm_A.controller_regs + PSYCHO_PCIA_CTRL);
+ tmp |= PSYCHO_PCICTRL_AEN;
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_PCIA_CTRL, tmp);
+
+ tmp = psycho_read(p->pbm_A.controller_regs + PSYCHO_PCIB_CTRL);
+ tmp |= PSYCHO_PCICTRL_AEN;
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_PCIB_CTRL, tmp);
+
+ /* Disable DMA write / PIO read synchronization on
+ * both PCI bus segments.
+ * [ U2P Erratum 1243770, STP2223BGA data sheet ]
+ */
+ tmp = psycho_read(p->pbm_A.controller_regs + PSYCHO_PCIA_DIAG);
+ tmp |= PSYCHO_PCIDIAG_DDWSYNC;
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_PCIA_DIAG, tmp);
+
+ tmp = psycho_read(p->pbm_A.controller_regs + PSYCHO_PCIB_DIAG);
+ tmp |= PSYCHO_PCIDIAG_DDWSYNC;
+ psycho_write(p->pbm_A.controller_regs + PSYCHO_PCIB_DIAG, tmp);
+}
+
+static void __init pbm_register_toplevel_resources(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm)
+{
+ char *name = pbm->name;
+
+ sprintf(name, "PSYCHO%d PBM%c",
+ p->index,
+ (pbm == &p->pbm_A ? 'A' : 'B'));
+ pbm->io_space.name = pbm->mem_space.name = name;
+
+ request_resource(&ioport_resource, &pbm->io_space);
+ request_resource(&iomem_resource, &pbm->mem_space);
+ pci_register_legacy_regions(&pbm->io_space,
+ &pbm->mem_space);
+}
+
+static void psycho_pbm_strbuf_init(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm,
+ int is_pbm_a)
+{
+ unsigned long base = pbm->controller_regs;
+ u64 control;
+
+ if (is_pbm_a) {
+ pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_A;
+ pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_A;
+ pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_A;
+ } else {
+ pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_B;
+ pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_B;
+ pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_B;
+ }
+ /* PSYCHO's streaming buffer lacks ctx flushing. */
+ pbm->stc.strbuf_ctxflush = 0;
+ pbm->stc.strbuf_ctxmatch_base = 0;
+
+ pbm->stc.strbuf_flushflag = (volatile unsigned long *)
+ ((((unsigned long)&pbm->stc.__flushflag_buf[0])
+ + 63UL)
+ & ~63UL);
+ pbm->stc.strbuf_flushflag_pa = (unsigned long)
+ __pa(pbm->stc.strbuf_flushflag);
+
+ /* Enable the streaming buffer. We have to be careful
+ * just in case OBP left it with LRU locking enabled.
+ *
+ * It is possible to control if PBM will be rerun on
+ * line misses. Currently I just retain whatever setting
+ * OBP left us with. All checks so far show it having
+ * a value of zero.
+ */
+#undef PSYCHO_STRBUF_RERUN_ENABLE
+#undef PSYCHO_STRBUF_RERUN_DISABLE
+ control = psycho_read(pbm->stc.strbuf_control);
+ control |= PSYCHO_STRBUF_CTRL_ENAB;
+ control &= ~(PSYCHO_STRBUF_CTRL_LENAB | PSYCHO_STRBUF_CTRL_LPTR);
+#ifdef PSYCHO_STRBUF_RERUN_ENABLE
+ control &= ~(PSYCHO_STRBUF_CTRL_RRDIS);
+#else
+#ifdef PSYCHO_STRBUF_RERUN_DISABLE
+ control |= PSYCHO_STRBUF_CTRL_RRDIS;
+#endif
+#endif
+ psycho_write(pbm->stc.strbuf_control, control);
+
+ pbm->stc.strbuf_enabled = 1;
+}
+
+#define PSYCHO_IOSPACE_A 0x002000000UL
+#define PSYCHO_IOSPACE_B 0x002010000UL
+#define PSYCHO_IOSPACE_SIZE 0x00000ffffUL
+#define PSYCHO_MEMSPACE_A 0x100000000UL
+#define PSYCHO_MEMSPACE_B 0x180000000UL
+#define PSYCHO_MEMSPACE_SIZE 0x07fffffffUL
+
+static void psycho_pbm_init(struct pci_controller_info *p,
+ int prom_node, int is_pbm_a)
+{
+ unsigned int busrange[2];
+ struct pci_pbm_info *pbm;
+ int err;
+
+ if (is_pbm_a) {
+ pbm = &p->pbm_A;
+ pbm->pci_first_slot = 1;
+ pbm->io_space.start = pbm->controller_regs + PSYCHO_IOSPACE_A;
+ pbm->mem_space.start = pbm->controller_regs + PSYCHO_MEMSPACE_A;
+ } else {
+ pbm = &p->pbm_B;
+ pbm->pci_first_slot = 2;
+ pbm->io_space.start = pbm->controller_regs + PSYCHO_IOSPACE_B;
+ pbm->mem_space.start = pbm->controller_regs + PSYCHO_MEMSPACE_B;
+ }
+
+ pbm->chip_type = PBM_CHIP_TYPE_PSYCHO;
+ pbm->chip_version =
+ prom_getintdefault(prom_node, "version#", 0);
+ pbm->chip_revision =
+ prom_getintdefault(prom_node, "module-revision#", 0);
+
+ pbm->io_space.end = pbm->io_space.start + PSYCHO_IOSPACE_SIZE;
+ pbm->io_space.flags = IORESOURCE_IO;
+ pbm->mem_space.end = pbm->mem_space.start + PSYCHO_MEMSPACE_SIZE;
+ pbm->mem_space.flags = IORESOURCE_MEM;
+ pbm_register_toplevel_resources(p, pbm);
+
+ pbm->parent = p;
+ pbm->prom_node = prom_node;
+ prom_getstring(prom_node, "name",
+ pbm->prom_name,
+ sizeof(pbm->prom_name));
+
+ err = prom_getproperty(prom_node, "ranges",
+ (char *)pbm->pbm_ranges,
+ sizeof(pbm->pbm_ranges));
+ if (err != -1)
+ pbm->num_pbm_ranges =
+ (err / sizeof(struct linux_prom_pci_ranges));
+ else
+ pbm->num_pbm_ranges = 0;
+
+ err = prom_getproperty(prom_node, "interrupt-map",
+ (char *)pbm->pbm_intmap,
+ sizeof(pbm->pbm_intmap));
+ if (err != -1) {
+ pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap));
+ err = prom_getproperty(prom_node, "interrupt-map-mask",
+ (char *)&pbm->pbm_intmask,
+ sizeof(pbm->pbm_intmask));
+ if (err == -1) {
+ prom_printf("PSYCHO-PBM: Fatal error, no "
+ "interrupt-map-mask.\n");
+ prom_halt();
+ }
+ } else {
+ pbm->num_pbm_intmap = 0;
+ memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask));
+ }
+
+ err = prom_getproperty(prom_node, "bus-range",
+ (char *)&busrange[0],
+ sizeof(busrange));
+ if (err == 0 || err == -1) {
+ prom_printf("PSYCHO-PBM: Fatal error, no bus-range.\n");
+ prom_halt();
+ }
+ pbm->pci_first_busno = busrange[0];
+ pbm->pci_last_busno = busrange[1];
+
+ psycho_pbm_strbuf_init(p, pbm, is_pbm_a);
+}
+
+#define PSYCHO_CONFIGSPACE 0x001000000UL
+
+void __init psycho_init(int node, char *model_name)
+{
+ struct linux_prom64_registers pr_regs[3];
+ struct pci_controller_info *p;
+ struct pci_iommu *iommu;
+ u32 upa_portid;
+ int is_pbm_a, err;
+
+ upa_portid = prom_getintdefault(node, "upa-portid", 0xff);
+
+ for(p = pci_controller_root; p; p = p->next) {
+ if (p->pbm_A.portid == upa_portid) {
+ is_pbm_a = (p->pbm_A.prom_node == 0);
+ psycho_pbm_init(p, node, is_pbm_a);
+ return;
+ }
+ }
+
+ p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
+ if (!p) {
+ prom_printf("PSYCHO: Fatal memory allocation error.\n");
+ prom_halt();
+ }
+ memset(p, 0, sizeof(*p));
+ iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
+ if (!iommu) {
+ prom_printf("PSYCHO: Fatal memory allocation error.\n");
+ prom_halt();
+ }
+ memset(iommu, 0, sizeof(*iommu));
+ p->pbm_A.iommu = p->pbm_B.iommu = iommu;
+
+ p->next = pci_controller_root;
+ pci_controller_root = p;
+
+ p->pbm_A.portid = upa_portid;
+ p->pbm_B.portid = upa_portid;
+ p->index = pci_num_controllers++;
+ p->pbms_same_domain = 0;
+ p->scan_bus = psycho_scan_bus;
+ p->irq_build = psycho_irq_build;
+ p->base_address_update = psycho_base_address_update;
+ p->resource_adjust = psycho_resource_adjust;
+ p->pci_ops = &psycho_ops;
+
+ err = prom_getproperty(node, "reg",
+ (char *)&pr_regs[0],
+ sizeof(pr_regs));
+ if (err == 0 || err == -1) {
+ prom_printf("PSYCHO: Fatal error, no reg property.\n");
+ prom_halt();
+ }
+
+ p->pbm_A.controller_regs = pr_regs[2].phys_addr;
+ p->pbm_B.controller_regs = pr_regs[2].phys_addr;
+ printk("PCI: Found PSYCHO, control regs at %016lx\n",
+ p->pbm_A.controller_regs);
+
+ p->pbm_A.config_space = p->pbm_B.config_space =
+ (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE);
+ printk("PSYCHO: Shared PCI config space at %016lx\n",
+ p->pbm_A.config_space);
+
+ /*
+ * Psycho's PCI MEM space is mapped to a 2GB aligned area, so
+ * we need to adjust our MEM space mask.
+ */
+ pci_memspace_mask = 0x7fffffffUL;
+
+ psycho_controller_hwinit(p);
+
+ psycho_iommu_init(p);
+
+ is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
+ psycho_pbm_init(p, node, is_pbm_a);
+}
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
new file mode 100644
index 000000000000..5525d1ec4af8
--- /dev/null
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -0,0 +1,1702 @@
+/* $Id: pci_sabre.c,v 1.42 2002/01/23 11:27:32 davem Exp $
+ * pci_sabre.c: Sabre specific PCI controller support.
+ *
+ * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1998, 1999 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <asm/apb.h>
+#include <asm/pbm.h>
+#include <asm/iommu.h>
+#include <asm/irq.h>
+#include <asm/smp.h>
+#include <asm/oplib.h>
+
+#include "pci_impl.h"
+#include "iommu_common.h"
+
+/* All SABRE registers are 64-bits. The following accessor
+ * routines are how they are accessed. The REG parameter
+ * is a physical address.
+ */
+#define sabre_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define sabre_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory")
+
+/* SABRE PCI controller register offsets and definitions. */
+#define SABRE_UE_AFSR 0x0030UL
+#define SABRE_UEAFSR_PDRD 0x4000000000000000UL /* Primary PCI DMA Read */
+#define SABRE_UEAFSR_PDWR 0x2000000000000000UL /* Primary PCI DMA Write */
+#define SABRE_UEAFSR_SDRD 0x0800000000000000UL /* Secondary PCI DMA Read */
+#define SABRE_UEAFSR_SDWR 0x0400000000000000UL /* Secondary PCI DMA Write */
+#define SABRE_UEAFSR_SDTE 0x0200000000000000UL /* Secondary DMA Translation Error */
+#define SABRE_UEAFSR_PDTE 0x0100000000000000UL /* Primary DMA Translation Error */
+#define SABRE_UEAFSR_BMSK 0x0000ffff00000000UL /* Bytemask */
+#define SABRE_UEAFSR_OFF 0x00000000e0000000UL /* Offset (AFAR bits [5:3] */
+#define SABRE_UEAFSR_BLK 0x0000000000800000UL /* Was block operation */
+#define SABRE_UECE_AFAR 0x0038UL
+#define SABRE_CE_AFSR 0x0040UL
+#define SABRE_CEAFSR_PDRD 0x4000000000000000UL /* Primary PCI DMA Read */
+#define SABRE_CEAFSR_PDWR 0x2000000000000000UL /* Primary PCI DMA Write */
+#define SABRE_CEAFSR_SDRD 0x0800000000000000UL /* Secondary PCI DMA Read */
+#define SABRE_CEAFSR_SDWR 0x0400000000000000UL /* Secondary PCI DMA Write */
+#define SABRE_CEAFSR_ESYND 0x00ff000000000000UL /* ECC Syndrome */
+#define SABRE_CEAFSR_BMSK 0x0000ffff00000000UL /* Bytemask */
+#define SABRE_CEAFSR_OFF 0x00000000e0000000UL /* Offset */
+#define SABRE_CEAFSR_BLK 0x0000000000800000UL /* Was block operation */
+#define SABRE_UECE_AFAR_ALIAS 0x0048UL /* Aliases to 0x0038 */
+#define SABRE_IOMMU_CONTROL 0x0200UL
+#define SABRE_IOMMUCTRL_ERRSTS 0x0000000006000000UL /* Error status bits */
+#define SABRE_IOMMUCTRL_ERR 0x0000000001000000UL /* Error present in IOTLB */
+#define SABRE_IOMMUCTRL_LCKEN 0x0000000000800000UL /* IOTLB lock enable */
+#define SABRE_IOMMUCTRL_LCKPTR 0x0000000000780000UL /* IOTLB lock pointer */
+#define SABRE_IOMMUCTRL_TSBSZ 0x0000000000070000UL /* TSB Size */
+#define SABRE_IOMMU_TSBSZ_1K 0x0000000000000000
+#define SABRE_IOMMU_TSBSZ_2K 0x0000000000010000
+#define SABRE_IOMMU_TSBSZ_4K 0x0000000000020000
+#define SABRE_IOMMU_TSBSZ_8K 0x0000000000030000
+#define SABRE_IOMMU_TSBSZ_16K 0x0000000000040000
+#define SABRE_IOMMU_TSBSZ_32K 0x0000000000050000
+#define SABRE_IOMMU_TSBSZ_64K 0x0000000000060000
+#define SABRE_IOMMU_TSBSZ_128K 0x0000000000070000
+#define SABRE_IOMMUCTRL_TBWSZ 0x0000000000000004UL /* TSB assumed page size */
+#define SABRE_IOMMUCTRL_DENAB 0x0000000000000002UL /* Diagnostic Mode Enable */
+#define SABRE_IOMMUCTRL_ENAB 0x0000000000000001UL /* IOMMU Enable */
+#define SABRE_IOMMU_TSBBASE 0x0208UL
+#define SABRE_IOMMU_FLUSH 0x0210UL
+#define SABRE_IMAP_A_SLOT0 0x0c00UL
+#define SABRE_IMAP_B_SLOT0 0x0c20UL
+#define SABRE_IMAP_SCSI 0x1000UL
+#define SABRE_IMAP_ETH 0x1008UL
+#define SABRE_IMAP_BPP 0x1010UL
+#define SABRE_IMAP_AU_REC 0x1018UL
+#define SABRE_IMAP_AU_PLAY 0x1020UL
+#define SABRE_IMAP_PFAIL 0x1028UL
+#define SABRE_IMAP_KMS 0x1030UL
+#define SABRE_IMAP_FLPY 0x1038UL
+#define SABRE_IMAP_SHW 0x1040UL
+#define SABRE_IMAP_KBD 0x1048UL
+#define SABRE_IMAP_MS 0x1050UL
+#define SABRE_IMAP_SER 0x1058UL
+#define SABRE_IMAP_UE 0x1070UL
+#define SABRE_IMAP_CE 0x1078UL
+#define SABRE_IMAP_PCIERR 0x1080UL
+#define SABRE_IMAP_GFX 0x1098UL
+#define SABRE_IMAP_EUPA 0x10a0UL
+#define SABRE_ICLR_A_SLOT0 0x1400UL
+#define SABRE_ICLR_B_SLOT0 0x1480UL
+#define SABRE_ICLR_SCSI 0x1800UL
+#define SABRE_ICLR_ETH 0x1808UL
+#define SABRE_ICLR_BPP 0x1810UL
+#define SABRE_ICLR_AU_REC 0x1818UL
+#define SABRE_ICLR_AU_PLAY 0x1820UL
+#define SABRE_ICLR_PFAIL 0x1828UL
+#define SABRE_ICLR_KMS 0x1830UL
+#define SABRE_ICLR_FLPY 0x1838UL
+#define SABRE_ICLR_SHW 0x1840UL
+#define SABRE_ICLR_KBD 0x1848UL
+#define SABRE_ICLR_MS 0x1850UL
+#define SABRE_ICLR_SER 0x1858UL
+#define SABRE_ICLR_UE 0x1870UL
+#define SABRE_ICLR_CE 0x1878UL
+#define SABRE_ICLR_PCIERR 0x1880UL
+#define SABRE_WRSYNC 0x1c20UL
+#define SABRE_PCICTRL 0x2000UL
+#define SABRE_PCICTRL_MRLEN 0x0000001000000000UL /* Use MemoryReadLine for block loads/stores */
+#define SABRE_PCICTRL_SERR 0x0000000400000000UL /* Set when SERR asserted on PCI bus */
+#define SABRE_PCICTRL_ARBPARK 0x0000000000200000UL /* Bus Parking 0=Ultra-IIi 1=prev-bus-owner */
+#define SABRE_PCICTRL_CPUPRIO 0x0000000000100000UL /* Ultra-IIi granted every other bus cycle */
+#define SABRE_PCICTRL_ARBPRIO 0x00000000000f0000UL /* Slot which is granted every other bus cycle */
+#define SABRE_PCICTRL_ERREN 0x0000000000000100UL /* PCI Error Interrupt Enable */
+#define SABRE_PCICTRL_RTRYWE 0x0000000000000080UL /* DMA Flow Control 0=wait-if-possible 1=retry */
+#define SABRE_PCICTRL_AEN 0x000000000000000fUL /* Slot PCI arbitration enables */
+#define SABRE_PIOAFSR 0x2010UL
+#define SABRE_PIOAFSR_PMA 0x8000000000000000UL /* Primary Master Abort */
+#define SABRE_PIOAFSR_PTA 0x4000000000000000UL /* Primary Target Abort */
+#define SABRE_PIOAFSR_PRTRY 0x2000000000000000UL /* Primary Excessive Retries */
+#define SABRE_PIOAFSR_PPERR 0x1000000000000000UL /* Primary Parity Error */
+#define SABRE_PIOAFSR_SMA 0x0800000000000000UL /* Secondary Master Abort */
+#define SABRE_PIOAFSR_STA 0x0400000000000000UL /* Secondary Target Abort */
+#define SABRE_PIOAFSR_SRTRY 0x0200000000000000UL /* Secondary Excessive Retries */
+#define SABRE_PIOAFSR_SPERR 0x0100000000000000UL /* Secondary Parity Error */
+#define SABRE_PIOAFSR_BMSK 0x0000ffff00000000UL /* Byte Mask */
+#define SABRE_PIOAFSR_BLK 0x0000000080000000UL /* Was Block Operation */
+#define SABRE_PIOAFAR 0x2018UL
+#define SABRE_PCIDIAG 0x2020UL
+#define SABRE_PCIDIAG_DRTRY 0x0000000000000040UL /* Disable PIO Retry Limit */
+#define SABRE_PCIDIAG_IPAPAR 0x0000000000000008UL /* Invert PIO Address Parity */
+#define SABRE_PCIDIAG_IPDPAR 0x0000000000000004UL /* Invert PIO Data Parity */
+#define SABRE_PCIDIAG_IDDPAR 0x0000000000000002UL /* Invert DMA Data Parity */
+#define SABRE_PCIDIAG_ELPBK 0x0000000000000001UL /* Loopback Enable - not supported */
+#define SABRE_PCITASR 0x2028UL
+#define SABRE_PCITASR_EF 0x0000000000000080UL /* Respond to 0xe0000000-0xffffffff */
+#define SABRE_PCITASR_CD 0x0000000000000040UL /* Respond to 0xc0000000-0xdfffffff */
+#define SABRE_PCITASR_AB 0x0000000000000020UL /* Respond to 0xa0000000-0xbfffffff */
+#define SABRE_PCITASR_89 0x0000000000000010UL /* Respond to 0x80000000-0x9fffffff */
+#define SABRE_PCITASR_67 0x0000000000000008UL /* Respond to 0x60000000-0x7fffffff */
+#define SABRE_PCITASR_45 0x0000000000000004UL /* Respond to 0x40000000-0x5fffffff */
+#define SABRE_PCITASR_23 0x0000000000000002UL /* Respond to 0x20000000-0x3fffffff */
+#define SABRE_PCITASR_01 0x0000000000000001UL /* Respond to 0x00000000-0x1fffffff */
+#define SABRE_PIOBUF_DIAG 0x5000UL
+#define SABRE_DMABUF_DIAGLO 0x5100UL
+#define SABRE_DMABUF_DIAGHI 0x51c0UL
+#define SABRE_IMAP_GFX_ALIAS 0x6000UL /* Aliases to 0x1098 */
+#define SABRE_IMAP_EUPA_ALIAS 0x8000UL /* Aliases to 0x10a0 */
+#define SABRE_IOMMU_VADIAG 0xa400UL
+#define SABRE_IOMMU_TCDIAG 0xa408UL
+#define SABRE_IOMMU_TAG 0xa580UL
+#define SABRE_IOMMUTAG_ERRSTS 0x0000000001800000UL /* Error status bits */
+#define SABRE_IOMMUTAG_ERR 0x0000000000400000UL /* Error present */
+#define SABRE_IOMMUTAG_WRITE 0x0000000000200000UL /* Page is writable */
+#define SABRE_IOMMUTAG_STREAM 0x0000000000100000UL /* Streamable bit - unused */
+#define SABRE_IOMMUTAG_SIZE 0x0000000000080000UL /* 0=8k 1=16k */
+#define SABRE_IOMMUTAG_VPN 0x000000000007ffffUL /* Virtual Page Number [31:13] */
+#define SABRE_IOMMU_DATA 0xa600UL
+#define SABRE_IOMMUDATA_VALID 0x0000000040000000UL /* Valid */
+#define SABRE_IOMMUDATA_USED 0x0000000020000000UL /* Used (for LRU algorithm) */
+#define SABRE_IOMMUDATA_CACHE 0x0000000010000000UL /* Cacheable */
+#define SABRE_IOMMUDATA_PPN 0x00000000001fffffUL /* Physical Page Number [33:13] */
+#define SABRE_PCI_IRQSTATE 0xa800UL
+#define SABRE_OBIO_IRQSTATE 0xa808UL
+#define SABRE_FFBCFG 0xf000UL
+#define SABRE_FFBCFG_SPRQS 0x000000000f000000 /* Slave P_RQST queue size */
+#define SABRE_FFBCFG_ONEREAD 0x0000000000004000 /* Slave supports one outstanding read */
+#define SABRE_MCCTRL0 0xf010UL
+#define SABRE_MCCTRL0_RENAB 0x0000000080000000 /* Refresh Enable */
+#define SABRE_MCCTRL0_EENAB 0x0000000010000000 /* Enable all ECC functions */
+#define SABRE_MCCTRL0_11BIT 0x0000000000001000 /* Enable 11-bit column addressing */
+#define SABRE_MCCTRL0_DPP 0x0000000000000f00 /* DIMM Pair Present Bits */
+#define SABRE_MCCTRL0_RINTVL 0x00000000000000ff /* Refresh Interval */
+#define SABRE_MCCTRL1 0xf018UL
+#define SABRE_MCCTRL1_AMDC 0x0000000038000000 /* Advance Memdata Clock */
+#define SABRE_MCCTRL1_ARDC 0x0000000007000000 /* Advance DRAM Read Data Clock */
+#define SABRE_MCCTRL1_CSR 0x0000000000e00000 /* CAS to RAS delay for CBR refresh */
+#define SABRE_MCCTRL1_CASRW 0x00000000001c0000 /* CAS length for read/write */
+#define SABRE_MCCTRL1_RCD 0x0000000000038000 /* RAS to CAS delay */
+#define SABRE_MCCTRL1_CP 0x0000000000007000 /* CAS Precharge */
+#define SABRE_MCCTRL1_RP 0x0000000000000e00 /* RAS Precharge */
+#define SABRE_MCCTRL1_RAS 0x00000000000001c0 /* Length of RAS for refresh */
+#define SABRE_MCCTRL1_CASRW2 0x0000000000000038 /* Must be same as CASRW */
+#define SABRE_MCCTRL1_RSC 0x0000000000000007 /* RAS after CAS hold time */
+#define SABRE_RESETCTRL 0xf020UL
+
+#define SABRE_CONFIGSPACE 0x001000000UL
+#define SABRE_IOSPACE 0x002000000UL
+#define SABRE_IOSPACE_SIZE 0x000ffffffUL
+#define SABRE_MEMSPACE 0x100000000UL
+#define SABRE_MEMSPACE_SIZE 0x07fffffffUL
+
+/* UltraSparc-IIi Programmer's Manual, page 325, PCI
+ * configuration space address format:
+ *
+ * 32 24 23 16 15 11 10 8 7 2 1 0
+ * ---------------------------------------------------------
+ * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 |
+ * ---------------------------------------------------------
+ */
+#define SABRE_CONFIG_BASE(PBM) \
+ ((PBM)->config_space | (1UL << 24))
+#define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+static int hummingbird_p;
+static struct pci_bus *sabre_root_bus;
+
+static void *sabre_pci_config_mkaddr(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned int devfn,
+ int where)
+{
+ if (!pbm)
+ return NULL;
+ return (void *)
+ (SABRE_CONFIG_BASE(pbm) |
+ SABRE_CONFIG_ENCODE(bus, devfn, where));
+}
+
+static int sabre_out_of_range(unsigned char devfn)
+{
+ if (hummingbird_p)
+ return 0;
+
+ return (((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) ||
+ ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) ||
+ (PCI_SLOT(devfn) > 1));
+}
+
+static int __sabre_out_of_range(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned char devfn)
+{
+ if (hummingbird_p)
+ return 0;
+
+ return ((pbm->parent == 0) ||
+ ((pbm == &pbm->parent->pbm_B) &&
+ (bus == pbm->pci_first_busno) &&
+ PCI_SLOT(devfn) > 8) ||
+ ((pbm == &pbm->parent->pbm_A) &&
+ (bus == pbm->pci_first_busno) &&
+ PCI_SLOT(devfn) > 8));
+}
+
+static int __sabre_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ struct pci_pbm_info *pbm = bus_dev->sysdata;
+ unsigned char bus = bus_dev->number;
+ u32 *addr;
+ u16 tmp16;
+ u8 tmp8;
+
+ switch (size) {
+ case 1:
+ *value = 0xff;
+ break;
+ case 2:
+ *value = 0xffff;
+ break;
+ case 4:
+ *value = 0xffffffff;
+ break;
+ }
+
+ addr = sabre_pci_config_mkaddr(pbm, bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (__sabre_out_of_range(pbm, bus, devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ switch (size) {
+ case 1:
+ pci_config_read8((u8 *) addr, &tmp8);
+ *value = tmp8;
+ break;
+
+ case 2:
+ if (where & 0x01) {
+ printk("pci_read_config_word: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_read16((u16 *) addr, &tmp16);
+ *value = tmp16;
+ break;
+
+ case 4:
+ if (where & 0x03) {
+ printk("pci_read_config_dword: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_read32(addr, value);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int sabre_read_pci_cfg(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ if (!bus->number && sabre_out_of_range(devfn)) {
+ switch (size) {
+ case 1:
+ *value = 0xff;
+ break;
+ case 2:
+ *value = 0xffff;
+ break;
+ case 4:
+ *value = 0xffffffff;
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (bus->number || PCI_SLOT(devfn))
+ return __sabre_read_pci_cfg(bus, devfn, where, size, value);
+
+ /* When accessing PCI config space of the PCI controller itself (bus
+ * 0, device slot 0, function 0) there are restrictions. Each
+ * register must be accessed as it's natural size. Thus, for example
+ * the Vendor ID must be accessed as a 16-bit quantity.
+ */
+
+ switch (size) {
+ case 1:
+ if (where < 8) {
+ u32 tmp32;
+ u16 tmp16;
+
+ __sabre_read_pci_cfg(bus, devfn, where & ~1, 2, &tmp32);
+ tmp16 = (u16) tmp32;
+ if (where & 1)
+ *value = tmp16 >> 8;
+ else
+ *value = tmp16 & 0xff;
+ } else
+ return __sabre_read_pci_cfg(bus, devfn, where, 1, value);
+ break;
+
+ case 2:
+ if (where < 8)
+ return __sabre_read_pci_cfg(bus, devfn, where, 2, value);
+ else {
+ u32 tmp32;
+ u8 tmp8;
+
+ __sabre_read_pci_cfg(bus, devfn, where, 1, &tmp32);
+ tmp8 = (u8) tmp32;
+ *value = tmp8;
+ __sabre_read_pci_cfg(bus, devfn, where + 1, 1, &tmp32);
+ tmp8 = (u8) tmp32;
+ *value |= tmp8 << 8;
+ }
+ break;
+
+ case 4: {
+ u32 tmp32;
+ u16 tmp16;
+
+ sabre_read_pci_cfg(bus, devfn, where, 2, &tmp32);
+ tmp16 = (u16) tmp32;
+ *value = tmp16;
+ sabre_read_pci_cfg(bus, devfn, where + 2, 2, &tmp32);
+ tmp16 = (u16) tmp32;
+ *value |= tmp16 << 16;
+ break;
+ }
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int __sabre_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ struct pci_pbm_info *pbm = bus_dev->sysdata;
+ unsigned char bus = bus_dev->number;
+ u32 *addr;
+
+ addr = sabre_pci_config_mkaddr(pbm, bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (__sabre_out_of_range(pbm, bus, devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ switch (size) {
+ case 1:
+ pci_config_write8((u8 *) addr, value);
+ break;
+
+ case 2:
+ if (where & 0x01) {
+ printk("pci_write_config_word: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_write16((u16 *) addr, value);
+ break;
+
+ case 4:
+ if (where & 0x03) {
+ printk("pci_write_config_dword: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_write32(addr, value);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int sabre_write_pci_cfg(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ if (bus->number)
+ return __sabre_write_pci_cfg(bus, devfn, where, size, value);
+
+ if (sabre_out_of_range(devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ switch (size) {
+ case 1:
+ if (where < 8) {
+ u32 tmp32;
+ u16 tmp16;
+
+ __sabre_read_pci_cfg(bus, devfn, where & ~1, 2, &tmp32);
+ tmp16 = (u16) tmp32;
+ if (where & 1) {
+ value &= 0x00ff;
+ value |= tmp16 << 8;
+ } else {
+ value &= 0xff00;
+ value |= tmp16;
+ }
+ tmp32 = (u32) tmp16;
+ return __sabre_write_pci_cfg(bus, devfn, where & ~1, 2, tmp32);
+ } else
+ return __sabre_write_pci_cfg(bus, devfn, where, 1, value);
+ break;
+ case 2:
+ if (where < 8)
+ return __sabre_write_pci_cfg(bus, devfn, where, 2, value);
+ else {
+ __sabre_write_pci_cfg(bus, devfn, where, 1, value & 0xff);
+ __sabre_write_pci_cfg(bus, devfn, where + 1, 1, value >> 8);
+ }
+ break;
+ case 4:
+ sabre_write_pci_cfg(bus, devfn, where, 2, value & 0xffff);
+ sabre_write_pci_cfg(bus, devfn, where + 2, 2, value >> 16);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops sabre_ops = {
+ .read = sabre_read_pci_cfg,
+ .write = sabre_write_pci_cfg,
+};
+
+static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return SABRE_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return SABRE_IMAP_B_SLOT0 + (slot * 8);
+}
+
+static unsigned long __onboard_imap_off[] = {
+/*0x20*/ SABRE_IMAP_SCSI,
+/*0x21*/ SABRE_IMAP_ETH,
+/*0x22*/ SABRE_IMAP_BPP,
+/*0x23*/ SABRE_IMAP_AU_REC,
+/*0x24*/ SABRE_IMAP_AU_PLAY,
+/*0x25*/ SABRE_IMAP_PFAIL,
+/*0x26*/ SABRE_IMAP_KMS,
+/*0x27*/ SABRE_IMAP_FLPY,
+/*0x28*/ SABRE_IMAP_SHW,
+/*0x29*/ SABRE_IMAP_KBD,
+/*0x2a*/ SABRE_IMAP_MS,
+/*0x2b*/ SABRE_IMAP_SER,
+/*0x2c*/ 0 /* reserved */,
+/*0x2d*/ 0 /* reserved */,
+/*0x2e*/ SABRE_IMAP_UE,
+/*0x2f*/ SABRE_IMAP_CE,
+/*0x30*/ SABRE_IMAP_PCIERR,
+};
+#define SABRE_ONBOARD_IRQ_BASE 0x20
+#define SABRE_ONBOARD_IRQ_LAST 0x30
+#define sabre_onboard_imap_offset(__ino) \
+ __onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE]
+
+#define sabre_iclr_offset(ino) \
+ ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+/* PCI SABRE INO number to Sparc PIL level. */
+static unsigned char sabre_pil_table[] = {
+/*0x00*/0, 0, 0, 0, /* PCI A slot 0 Int A, B, C, D */
+/*0x04*/0, 0, 0, 0, /* PCI A slot 1 Int A, B, C, D */
+/*0x08*/0, 0, 0, 0, /* PCI A slot 2 Int A, B, C, D */
+/*0x0c*/0, 0, 0, 0, /* PCI A slot 3 Int A, B, C, D */
+/*0x10*/0, 0, 0, 0, /* PCI B slot 0 Int A, B, C, D */
+/*0x14*/0, 0, 0, 0, /* PCI B slot 1 Int A, B, C, D */
+/*0x18*/0, 0, 0, 0, /* PCI B slot 2 Int A, B, C, D */
+/*0x1c*/0, 0, 0, 0, /* PCI B slot 3 Int A, B, C, D */
+/*0x20*/4, /* SCSI */
+/*0x21*/5, /* Ethernet */
+/*0x22*/8, /* Parallel Port */
+/*0x23*/13, /* Audio Record */
+/*0x24*/14, /* Audio Playback */
+/*0x25*/15, /* PowerFail */
+/*0x26*/4, /* second SCSI */
+/*0x27*/11, /* Floppy */
+/*0x28*/4, /* Spare Hardware */
+/*0x29*/9, /* Keyboard */
+/*0x2a*/4, /* Mouse */
+/*0x2b*/12, /* Serial */
+/*0x2c*/10, /* Timer 0 */
+/*0x2d*/11, /* Timer 1 */
+/*0x2e*/15, /* Uncorrectable ECC */
+/*0x2f*/15, /* Correctable ECC */
+/*0x30*/15, /* PCI Bus A Error */
+/*0x31*/15, /* PCI Bus B Error */
+/*0x32*/15, /* Power Management */
+};
+
+static int __init sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino)
+{
+ int ret;
+
+ if (pdev &&
+ pdev->vendor == PCI_VENDOR_ID_SUN &&
+ pdev->device == PCI_DEVICE_ID_SUN_RIO_USB)
+ return 9;
+
+ ret = sabre_pil_table[ino];
+ if (ret == 0 && pdev == NULL) {
+ ret = 4;
+ } else if (ret == 0) {
+ switch ((pdev->class >> 16) & 0xff) {
+ case PCI_BASE_CLASS_STORAGE:
+ ret = 4;
+ break;
+
+ case PCI_BASE_CLASS_NETWORK:
+ ret = 6;
+ break;
+
+ case PCI_BASE_CLASS_DISPLAY:
+ ret = 9;
+ break;
+
+ case PCI_BASE_CLASS_MULTIMEDIA:
+ case PCI_BASE_CLASS_MEMORY:
+ case PCI_BASE_CLASS_BRIDGE:
+ case PCI_BASE_CLASS_SERIAL:
+ ret = 10;
+ break;
+
+ default:
+ ret = 4;
+ break;
+ };
+ }
+ return ret;
+}
+
+static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev,
+ unsigned int ino)
+{
+ struct ino_bucket *bucket;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int pil, inofixup = 0;
+
+ ino &= PCI_IRQ_INO;
+ if (ino < SABRE_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = sabre_pcislot_imap_offset(ino);
+ } else {
+ /* onboard device */
+ if (ino > SABRE_ONBOARD_IRQ_LAST) {
+ prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ }
+ imap_off = sabre_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ pil = sabre_ino_to_pil(pdev, ino);
+
+ if (PIL_RESERVED(pil))
+ BUG();
+
+ imap = pbm->controller_regs + imap_off;
+ imap += 4;
+
+ iclr_off = sabre_iclr_offset(ino);
+ iclr = pbm->controller_regs + iclr_off;
+ iclr += 4;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ bucket = __bucket(build_irq(pil, inofixup, iclr, imap));
+ bucket->flags |= IBF_PCI;
+
+ if (pdev) {
+ struct pcidev_cookie *pcp = pdev->sysdata;
+
+ /* When a device lives behind a bridge deeper in the
+ * PCI bus topology than APB, a special sequence must
+ * run to make sure all pending DMA transfers at the
+ * time of IRQ delivery are visible in the coherency
+ * domain by the cpu. This sequence is to perform
+ * a read on the far side of the non-APB bridge, then
+ * perform a read of Sabre's DMA write-sync register.
+ *
+ * Currently, the PCI_CONFIG register for the device
+ * is used for this read from the far side of the bridge.
+ */
+ if (pdev->bus->number != pcp->pbm->pci_first_busno) {
+ bucket->flags |= IBF_DMA_SYNC;
+ bucket->synctab_ent = dma_sync_reg_table_entry++;
+ dma_sync_reg_table[bucket->synctab_ent] =
+ (unsigned long) sabre_pci_config_mkaddr(
+ pcp->pbm,
+ pdev->bus->number, pdev->devfn, PCI_COMMAND);
+ }
+ }
+ return __irq(bucket);
+}
+
+/* SABRE error handling support. */
+static void sabre_check_iommu_error(struct pci_controller_info *p,
+ unsigned long afsr,
+ unsigned long afar)
+{
+ struct pci_iommu *iommu = p->pbm_A.iommu;
+ unsigned long iommu_tag[16];
+ unsigned long iommu_data[16];
+ unsigned long flags;
+ u64 control;
+ int i;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+ control = sabre_read(iommu->iommu_control);
+ if (control & SABRE_IOMMUCTRL_ERR) {
+ char *type_string;
+
+ /* Clear the error encountered bit.
+ * NOTE: On Sabre this is write 1 to clear,
+ * which is different from Psycho.
+ */
+ sabre_write(iommu->iommu_control, control);
+ switch((control & SABRE_IOMMUCTRL_ERRSTS) >> 25UL) {
+ case 1:
+ type_string = "Invalid Error";
+ break;
+ case 3:
+ type_string = "ECC Error";
+ break;
+ default:
+ type_string = "Unknown";
+ break;
+ };
+ printk("SABRE%d: IOMMU Error, type[%s]\n",
+ p->index, type_string);
+
+ /* Enter diagnostic mode and probe for error'd
+ * entries in the IOTLB.
+ */
+ control &= ~(SABRE_IOMMUCTRL_ERRSTS | SABRE_IOMMUCTRL_ERR);
+ sabre_write(iommu->iommu_control,
+ (control | SABRE_IOMMUCTRL_DENAB));
+ for (i = 0; i < 16; i++) {
+ unsigned long base = p->pbm_A.controller_regs;
+
+ iommu_tag[i] =
+ sabre_read(base + SABRE_IOMMU_TAG + (i * 8UL));
+ iommu_data[i] =
+ sabre_read(base + SABRE_IOMMU_DATA + (i * 8UL));
+ sabre_write(base + SABRE_IOMMU_TAG + (i * 8UL), 0);
+ sabre_write(base + SABRE_IOMMU_DATA + (i * 8UL), 0);
+ }
+ sabre_write(iommu->iommu_control, control);
+
+ for (i = 0; i < 16; i++) {
+ unsigned long tag, data;
+
+ tag = iommu_tag[i];
+ if (!(tag & SABRE_IOMMUTAG_ERR))
+ continue;
+
+ data = iommu_data[i];
+ switch((tag & SABRE_IOMMUTAG_ERRSTS) >> 23UL) {
+ case 1:
+ type_string = "Invalid Error";
+ break;
+ case 3:
+ type_string = "ECC Error";
+ break;
+ default:
+ type_string = "Unknown";
+ break;
+ };
+ printk("SABRE%d: IOMMU TAG(%d)[RAW(%016lx)error(%s)wr(%d)sz(%dK)vpg(%08lx)]\n",
+ p->index, i, tag, type_string,
+ ((tag & SABRE_IOMMUTAG_WRITE) ? 1 : 0),
+ ((tag & SABRE_IOMMUTAG_SIZE) ? 64 : 8),
+ ((tag & SABRE_IOMMUTAG_VPN) << IOMMU_PAGE_SHIFT));
+ printk("SABRE%d: IOMMU DATA(%d)[RAW(%016lx)valid(%d)used(%d)cache(%d)ppg(%016lx)\n",
+ p->index, i, data,
+ ((data & SABRE_IOMMUDATA_VALID) ? 1 : 0),
+ ((data & SABRE_IOMMUDATA_USED) ? 1 : 0),
+ ((data & SABRE_IOMMUDATA_CACHE) ? 1 : 0),
+ ((data & SABRE_IOMMUDATA_PPN) << IOMMU_PAGE_SHIFT));
+ }
+ }
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+static irqreturn_t sabre_ue_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg = p->pbm_A.controller_regs + SABRE_UE_AFSR;
+ unsigned long afar_reg = p->pbm_A.controller_regs + SABRE_UECE_AFAR;
+ unsigned long afsr, afar, error_bits;
+ int reported;
+
+ /* Latch uncorrectable error status. */
+ afar = sabre_read(afar_reg);
+ afsr = sabre_read(afsr_reg);
+
+ /* Clear the primary/secondary error status bits. */
+ error_bits = afsr &
+ (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR |
+ SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR |
+ SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE);
+ if (!error_bits)
+ return IRQ_NONE;
+ sabre_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("SABRE%d: Uncorrectable Error, primary error type[%s%s]\n",
+ p->index,
+ ((error_bits & SABRE_UEAFSR_PDRD) ?
+ "DMA Read" :
+ ((error_bits & SABRE_UEAFSR_PDWR) ?
+ "DMA Write" : "???")),
+ ((error_bits & SABRE_UEAFSR_PDTE) ?
+ ":Translation Error" : ""));
+ printk("SABRE%d: bytemask[%04lx] dword_offset[%lx] was_block(%d)\n",
+ p->index,
+ (afsr & SABRE_UEAFSR_BMSK) >> 32UL,
+ (afsr & SABRE_UEAFSR_OFF) >> 29UL,
+ ((afsr & SABRE_UEAFSR_BLK) ? 1 : 0));
+ printk("SABRE%d: UE AFAR [%016lx]\n", p->index, afar);
+ printk("SABRE%d: UE Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & SABRE_UEAFSR_SDRD) {
+ reported++;
+ printk("(DMA Read)");
+ }
+ if (afsr & SABRE_UEAFSR_SDWR) {
+ reported++;
+ printk("(DMA Write)");
+ }
+ if (afsr & SABRE_UEAFSR_SDTE) {
+ reported++;
+ printk("(Translation Error)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ /* Interrogate IOMMU for error status. */
+ sabre_check_iommu_error(p, afsr, afar);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sabre_ce_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg = p->pbm_A.controller_regs + SABRE_CE_AFSR;
+ unsigned long afar_reg = p->pbm_A.controller_regs + SABRE_UECE_AFAR;
+ unsigned long afsr, afar, error_bits;
+ int reported;
+
+ /* Latch error status. */
+ afar = sabre_read(afar_reg);
+ afsr = sabre_read(afsr_reg);
+
+ /* Clear primary/secondary error status bits. */
+ error_bits = afsr &
+ (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR |
+ SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR);
+ if (!error_bits)
+ return IRQ_NONE;
+ sabre_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("SABRE%d: Correctable Error, primary error type[%s]\n",
+ p->index,
+ ((error_bits & SABRE_CEAFSR_PDRD) ?
+ "DMA Read" :
+ ((error_bits & SABRE_CEAFSR_PDWR) ?
+ "DMA Write" : "???")));
+
+ /* XXX Use syndrome and afar to print out module string just like
+ * XXX UDB CE trap handler does... -DaveM
+ */
+ printk("SABRE%d: syndrome[%02lx] bytemask[%04lx] dword_offset[%lx] "
+ "was_block(%d)\n",
+ p->index,
+ (afsr & SABRE_CEAFSR_ESYND) >> 48UL,
+ (afsr & SABRE_CEAFSR_BMSK) >> 32UL,
+ (afsr & SABRE_CEAFSR_OFF) >> 29UL,
+ ((afsr & SABRE_CEAFSR_BLK) ? 1 : 0));
+ printk("SABRE%d: CE AFAR [%016lx]\n", p->index, afar);
+ printk("SABRE%d: CE Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & SABRE_CEAFSR_SDRD) {
+ reported++;
+ printk("(DMA Read)");
+ }
+ if (afsr & SABRE_CEAFSR_SDWR) {
+ reported++;
+ printk("(DMA Write)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sabre_pcierr_intr_other(struct pci_controller_info *p)
+{
+ unsigned long csr_reg, csr, csr_error_bits;
+ irqreturn_t ret = IRQ_NONE;
+ u16 stat;
+
+ csr_reg = p->pbm_A.controller_regs + SABRE_PCICTRL;
+ csr = sabre_read(csr_reg);
+ csr_error_bits =
+ csr & SABRE_PCICTRL_SERR;
+ if (csr_error_bits) {
+ /* Clear the errors. */
+ sabre_write(csr_reg, csr);
+
+ /* Log 'em. */
+ if (csr_error_bits & SABRE_PCICTRL_SERR)
+ printk("SABRE%d: PCI SERR signal asserted.\n",
+ p->index);
+ ret = IRQ_HANDLED;
+ }
+ pci_read_config_word(sabre_root_bus->self,
+ PCI_STATUS, &stat);
+ if (stat & (PCI_STATUS_PARITY |
+ PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT |
+ PCI_STATUS_REC_MASTER_ABORT |
+ PCI_STATUS_SIG_SYSTEM_ERROR)) {
+ printk("SABRE%d: PCI bus error, PCI_STATUS[%04x]\n",
+ p->index, stat);
+ pci_write_config_word(sabre_root_bus->self,
+ PCI_STATUS, 0xffff);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+
+static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg, afar_reg;
+ unsigned long afsr, afar, error_bits;
+ int reported;
+
+ afsr_reg = p->pbm_A.controller_regs + SABRE_PIOAFSR;
+ afar_reg = p->pbm_A.controller_regs + SABRE_PIOAFAR;
+
+ /* Latch error status. */
+ afar = sabre_read(afar_reg);
+ afsr = sabre_read(afsr_reg);
+
+ /* Clear primary/secondary error status bits. */
+ error_bits = afsr &
+ (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_PTA |
+ SABRE_PIOAFSR_PRTRY | SABRE_PIOAFSR_PPERR |
+ SABRE_PIOAFSR_SMA | SABRE_PIOAFSR_STA |
+ SABRE_PIOAFSR_SRTRY | SABRE_PIOAFSR_SPERR);
+ if (!error_bits)
+ return sabre_pcierr_intr_other(p);
+ sabre_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("SABRE%d: PCI Error, primary error type[%s]\n",
+ p->index,
+ (((error_bits & SABRE_PIOAFSR_PMA) ?
+ "Master Abort" :
+ ((error_bits & SABRE_PIOAFSR_PTA) ?
+ "Target Abort" :
+ ((error_bits & SABRE_PIOAFSR_PRTRY) ?
+ "Excessive Retries" :
+ ((error_bits & SABRE_PIOAFSR_PPERR) ?
+ "Parity Error" : "???"))))));
+ printk("SABRE%d: bytemask[%04lx] was_block(%d)\n",
+ p->index,
+ (afsr & SABRE_PIOAFSR_BMSK) >> 32UL,
+ (afsr & SABRE_PIOAFSR_BLK) ? 1 : 0);
+ printk("SABRE%d: PCI AFAR [%016lx]\n", p->index, afar);
+ printk("SABRE%d: PCI Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & SABRE_PIOAFSR_SMA) {
+ reported++;
+ printk("(Master Abort)");
+ }
+ if (afsr & SABRE_PIOAFSR_STA) {
+ reported++;
+ printk("(Target Abort)");
+ }
+ if (afsr & SABRE_PIOAFSR_SRTRY) {
+ reported++;
+ printk("(Excessive Retries)");
+ }
+ if (afsr & SABRE_PIOAFSR_SPERR) {
+ reported++;
+ printk("(Parity Error)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ /* For the error types shown, scan both PCI buses for devices
+ * which have logged that error type.
+ */
+
+ /* If we see a Target Abort, this could be the result of an
+ * IOMMU translation error of some sort. It is extremely
+ * useful to log this information as usually it indicates
+ * a bug in the IOMMU support code or a PCI device driver.
+ */
+ if (error_bits & (SABRE_PIOAFSR_PTA | SABRE_PIOAFSR_STA)) {
+ sabre_check_iommu_error(p, afsr, afar);
+ pci_scan_for_target_abort(p, &p->pbm_A, p->pbm_A.pci_bus);
+ pci_scan_for_target_abort(p, &p->pbm_B, p->pbm_B.pci_bus);
+ }
+ if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA)) {
+ pci_scan_for_master_abort(p, &p->pbm_A, p->pbm_A.pci_bus);
+ pci_scan_for_master_abort(p, &p->pbm_B, p->pbm_B.pci_bus);
+ }
+ /* For excessive retries, SABRE/PBM will abort the device
+ * and there is no way to specifically check for excessive
+ * retries in the config space status registers. So what
+ * we hope is that we'll catch it via the master/target
+ * abort events.
+ */
+
+ if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR)) {
+ pci_scan_for_parity_error(p, &p->pbm_A, p->pbm_A.pci_bus);
+ pci_scan_for_parity_error(p, &p->pbm_B, p->pbm_B.pci_bus);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* XXX What about PowerFail/PowerManagement??? -DaveM */
+#define SABRE_UE_INO 0x2e
+#define SABRE_CE_INO 0x2f
+#define SABRE_PCIERR_INO 0x30
+static void __init sabre_register_error_handlers(struct pci_controller_info *p)
+{
+ struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */
+ unsigned long base = pbm->controller_regs;
+ unsigned long irq, portid = pbm->portid;
+ u64 tmp;
+
+ /* We clear the error bits in the appropriate AFSR before
+ * registering the handler so that we don't get spurious
+ * interrupts.
+ */
+ sabre_write(base + SABRE_UE_AFSR,
+ (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR |
+ SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR |
+ SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE));
+ irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_UE_INO);
+ if (request_irq(irq, sabre_ue_intr,
+ SA_SHIRQ, "SABRE UE", p) < 0) {
+ prom_printf("SABRE%d: Cannot register UE interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ sabre_write(base + SABRE_CE_AFSR,
+ (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR |
+ SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR));
+ irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_CE_INO);
+ if (request_irq(irq, sabre_ce_intr,
+ SA_SHIRQ, "SABRE CE", p) < 0) {
+ prom_printf("SABRE%d: Cannot register CE interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_PCIERR_INO);
+ if (request_irq(irq, sabre_pcierr_intr,
+ SA_SHIRQ, "SABRE PCIERR", p) < 0) {
+ prom_printf("SABRE%d: Cannot register PciERR interrupt.\n",
+ p->index);
+ prom_halt();
+ }
+
+ tmp = sabre_read(base + SABRE_PCICTRL);
+ tmp |= SABRE_PCICTRL_ERREN;
+ sabre_write(base + SABRE_PCICTRL, tmp);
+}
+
+static void __init sabre_resource_adjust(struct pci_dev *pdev,
+ struct resource *res,
+ struct resource *root)
+{
+ struct pci_pbm_info *pbm = pdev->bus->sysdata;
+ unsigned long base;
+
+ if (res->flags & IORESOURCE_IO)
+ base = pbm->controller_regs + SABRE_IOSPACE;
+ else
+ base = pbm->controller_regs + SABRE_MEMSPACE;
+
+ res->start += base;
+ res->end += base;
+}
+
+static void __init sabre_base_address_update(struct pci_dev *pdev, int resource)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ struct pci_pbm_info *pbm = pcp->pbm;
+ struct resource *res;
+ unsigned long base;
+ u32 reg;
+ int where, size, is_64bit;
+
+ res = &pdev->resource[resource];
+ if (resource < 6) {
+ where = PCI_BASE_ADDRESS_0 + (resource * 4);
+ } else if (resource == PCI_ROM_RESOURCE) {
+ where = pdev->rom_base_reg;
+ } else {
+ /* Somebody might have asked allocation of a non-standard resource */
+ return;
+ }
+
+ is_64bit = 0;
+ if (res->flags & IORESOURCE_IO)
+ base = pbm->controller_regs + SABRE_IOSPACE;
+ else {
+ base = pbm->controller_regs + SABRE_MEMSPACE;
+ if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
+ == PCI_BASE_ADDRESS_MEM_TYPE_64)
+ is_64bit = 1;
+ }
+
+ size = res->end - res->start;
+ pci_read_config_dword(pdev, where, &reg);
+ reg = ((reg & size) |
+ (((u32)(res->start - base)) & ~size));
+ if (resource == PCI_ROM_RESOURCE) {
+ reg |= PCI_ROM_ADDRESS_ENABLE;
+ res->flags |= IORESOURCE_ROM_ENABLE;
+ }
+ pci_write_config_dword(pdev, where, reg);
+
+ /* This knows that the upper 32-bits of the address
+ * must be zero. Our PCI common layer enforces this.
+ */
+ if (is_64bit)
+ pci_write_config_dword(pdev, where + 4, 0);
+}
+
+static void __init apb_init(struct pci_controller_info *p, struct pci_bus *sabre_bus)
+{
+ struct pci_dev *pdev;
+
+ list_for_each_entry(pdev, &sabre_bus->devices, bus_list) {
+
+ if (pdev->vendor == PCI_VENDOR_ID_SUN &&
+ pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
+ u32 word32;
+ u16 word16;
+
+ sabre_read_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_COMMAND, 2, &word32);
+ word16 = (u16) word32;
+ word16 |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_IO;
+ word32 = (u32) word16;
+ sabre_write_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_COMMAND, 2, word32);
+
+ /* Status register bits are "write 1 to clear". */
+ sabre_write_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_STATUS, 2, 0xffff);
+ sabre_write_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_SEC_STATUS, 2, 0xffff);
+
+ /* Use a primary/seconday latency timer value
+ * of 64.
+ */
+ sabre_write_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_LATENCY_TIMER, 1, 64);
+ sabre_write_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_SEC_LATENCY_TIMER, 1, 64);
+
+ /* Enable reporting/forwarding of master aborts,
+ * parity, and SERR.
+ */
+ sabre_write_pci_cfg(pdev->bus, pdev->devfn,
+ PCI_BRIDGE_CONTROL, 1,
+ (PCI_BRIDGE_CTL_PARITY |
+ PCI_BRIDGE_CTL_SERR |
+ PCI_BRIDGE_CTL_MASTER_ABORT));
+ }
+ }
+}
+
+static struct pcidev_cookie *alloc_bridge_cookie(struct pci_pbm_info *pbm)
+{
+ struct pcidev_cookie *cookie = kmalloc(sizeof(*cookie), GFP_KERNEL);
+
+ if (!cookie) {
+ prom_printf("SABRE: Critical allocation failure.\n");
+ prom_halt();
+ }
+
+ /* All we care about is the PBM. */
+ memset(cookie, 0, sizeof(*cookie));
+ cookie->pbm = pbm;
+
+ return cookie;
+}
+
+static void __init sabre_scan_bus(struct pci_controller_info *p)
+{
+ static int once;
+ struct pci_bus *sabre_bus, *pbus;
+ struct pci_pbm_info *pbm;
+ struct pcidev_cookie *cookie;
+ int sabres_scanned;
+
+ /* The APB bridge speaks to the Sabre host PCI bridge
+ * at 66Mhz, but the front side of APB runs at 33Mhz
+ * for both segments.
+ */
+ p->pbm_A.is_66mhz_capable = 0;
+ p->pbm_B.is_66mhz_capable = 0;
+
+ /* This driver has not been verified to handle
+ * multiple SABREs yet, so trap this.
+ *
+ * Also note that the SABRE host bridge is hardwired
+ * to live at bus 0.
+ */
+ if (once != 0) {
+ prom_printf("SABRE: Multiple controllers unsupported.\n");
+ prom_halt();
+ }
+ once++;
+
+ cookie = alloc_bridge_cookie(&p->pbm_A);
+
+ sabre_bus = pci_scan_bus(p->pci_first_busno,
+ p->pci_ops,
+ &p->pbm_A);
+ pci_fixup_host_bridge_self(sabre_bus);
+ sabre_bus->self->sysdata = cookie;
+
+ sabre_root_bus = sabre_bus;
+
+ apb_init(p, sabre_bus);
+
+ sabres_scanned = 0;
+
+ list_for_each_entry(pbus, &sabre_bus->children, node) {
+
+ if (pbus->number == p->pbm_A.pci_first_busno) {
+ pbm = &p->pbm_A;
+ } else if (pbus->number == p->pbm_B.pci_first_busno) {
+ pbm = &p->pbm_B;
+ } else
+ continue;
+
+ cookie = alloc_bridge_cookie(pbm);
+ pbus->self->sysdata = cookie;
+
+ sabres_scanned++;
+
+ pbus->sysdata = pbm;
+ pbm->pci_bus = pbus;
+ pci_fill_in_pbm_cookies(pbus, pbm, pbm->prom_node);
+ pci_record_assignments(pbm, pbus);
+ pci_assign_unassigned(pbm, pbus);
+ pci_fixup_irq(pbm, pbus);
+ pci_determine_66mhz_disposition(pbm, pbus);
+ pci_setup_busmastering(pbm, pbus);
+ }
+
+ if (!sabres_scanned) {
+ /* Hummingbird, no APBs. */
+ pbm = &p->pbm_A;
+ sabre_bus->sysdata = pbm;
+ pbm->pci_bus = sabre_bus;
+ pci_fill_in_pbm_cookies(sabre_bus, pbm, pbm->prom_node);
+ pci_record_assignments(pbm, sabre_bus);
+ pci_assign_unassigned(pbm, sabre_bus);
+ pci_fixup_irq(pbm, sabre_bus);
+ pci_determine_66mhz_disposition(pbm, sabre_bus);
+ pci_setup_busmastering(pbm, sabre_bus);
+ }
+
+ sabre_register_error_handlers(p);
+}
+
+static void __init sabre_iommu_init(struct pci_controller_info *p,
+ int tsbsize, unsigned long dvma_offset,
+ u32 dma_mask)
+{
+ struct pci_iommu *iommu = p->pbm_A.iommu;
+ unsigned long tsbbase, i, order;
+ u64 control;
+
+ /* Setup initial software IOMMU state. */
+ spin_lock_init(&iommu->lock);
+ iommu->iommu_cur_ctx = 0;
+
+ /* Register addresses. */
+ iommu->iommu_control = p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL;
+ iommu->iommu_tsbbase = p->pbm_A.controller_regs + SABRE_IOMMU_TSBBASE;
+ iommu->iommu_flush = p->pbm_A.controller_regs + SABRE_IOMMU_FLUSH;
+ iommu->write_complete_reg = p->pbm_A.controller_regs + SABRE_WRSYNC;
+ /* Sabre's IOMMU lacks ctx flushing. */
+ iommu->iommu_ctxflush = 0;
+
+ /* Invalidate TLB Entries. */
+ control = sabre_read(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL);
+ control |= SABRE_IOMMUCTRL_DENAB;
+ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL, control);
+
+ for(i = 0; i < 16; i++) {
+ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_TAG + (i * 8UL), 0);
+ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_DATA + (i * 8UL), 0);
+ }
+
+ /* Leave diag mode enabled for full-flushing done
+ * in pci_iommu.c
+ */
+
+ iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0);
+ if (!iommu->dummy_page) {
+ prom_printf("PSYCHO_IOMMU: Error, gfp(dummy_page) failed.\n");
+ prom_halt();
+ }
+ memset((void *)iommu->dummy_page, 0, PAGE_SIZE);
+ iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page);
+
+ tsbbase = __get_free_pages(GFP_KERNEL, order = get_order(tsbsize * 1024 * 8));
+ if (!tsbbase) {
+ prom_printf("SABRE_IOMMU: Error, gfp(tsb) failed.\n");
+ prom_halt();
+ }
+ iommu->page_table = (iopte_t *)tsbbase;
+ iommu->page_table_map_base = dvma_offset;
+ iommu->dma_addr_mask = dma_mask;
+ pci_iommu_table_init(iommu, PAGE_SIZE << order);
+
+ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_TSBBASE, __pa(tsbbase));
+
+ control = sabre_read(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL);
+ control &= ~(SABRE_IOMMUCTRL_TSBSZ | SABRE_IOMMUCTRL_TBWSZ);
+ control |= SABRE_IOMMUCTRL_ENAB;
+ switch(tsbsize) {
+ case 64:
+ control |= SABRE_IOMMU_TSBSZ_64K;
+ iommu->page_table_sz_bits = 16;
+ break;
+ case 128:
+ control |= SABRE_IOMMU_TSBSZ_128K;
+ iommu->page_table_sz_bits = 17;
+ break;
+ default:
+ prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
+ prom_halt();
+ break;
+ }
+ sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_CONTROL, control);
+
+ /* We start with no consistent mappings. */
+ iommu->lowest_consistent_map =
+ 1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS);
+
+ for (i = 0; i < PBM_NCLUSTERS; i++) {
+ iommu->alloc_info[i].flush = 0;
+ iommu->alloc_info[i].next = 0;
+ }
+}
+
+static void __init pbm_register_toplevel_resources(struct pci_controller_info *p,
+ struct pci_pbm_info *pbm)
+{
+ char *name = pbm->name;
+ unsigned long ibase = p->pbm_A.controller_regs + SABRE_IOSPACE;
+ unsigned long mbase = p->pbm_A.controller_regs + SABRE_MEMSPACE;
+ unsigned int devfn;
+ unsigned long first, last, i;
+ u8 *addr, map;
+
+ sprintf(name, "SABRE%d PBM%c",
+ p->index,
+ (pbm == &p->pbm_A ? 'A' : 'B'));
+ pbm->io_space.name = pbm->mem_space.name = name;
+
+ devfn = PCI_DEVFN(1, (pbm == &p->pbm_A) ? 0 : 1);
+ addr = sabre_pci_config_mkaddr(pbm, 0, devfn, APB_IO_ADDRESS_MAP);
+ map = 0;
+ pci_config_read8(addr, &map);
+
+ first = 8;
+ last = 0;
+ for (i = 0; i < 8; i++) {
+ if ((map & (1 << i)) != 0) {
+ if (first > i)
+ first = i;
+ if (last < i)
+ last = i;
+ }
+ }
+ pbm->io_space.start = ibase + (first << 21UL);
+ pbm->io_space.end = ibase + (last << 21UL) + ((1 << 21UL) - 1);
+ pbm->io_space.flags = IORESOURCE_IO;
+
+ addr = sabre_pci_config_mkaddr(pbm, 0, devfn, APB_MEM_ADDRESS_MAP);
+ map = 0;
+ pci_config_read8(addr, &map);
+
+ first = 8;
+ last = 0;
+ for (i = 0; i < 8; i++) {
+ if ((map & (1 << i)) != 0) {
+ if (first > i)
+ first = i;
+ if (last < i)
+ last = i;
+ }
+ }
+ pbm->mem_space.start = mbase + (first << 29UL);
+ pbm->mem_space.end = mbase + (last << 29UL) + ((1 << 29UL) - 1);
+ pbm->mem_space.flags = IORESOURCE_MEM;
+
+ if (request_resource(&ioport_resource, &pbm->io_space) < 0) {
+ prom_printf("Cannot register PBM-%c's IO space.\n",
+ (pbm == &p->pbm_A ? 'A' : 'B'));
+ prom_halt();
+ }
+ if (request_resource(&iomem_resource, &pbm->mem_space) < 0) {
+ prom_printf("Cannot register PBM-%c's MEM space.\n",
+ (pbm == &p->pbm_A ? 'A' : 'B'));
+ prom_halt();
+ }
+
+ /* Register legacy regions if this PBM covers that area. */
+ if (pbm->io_space.start == ibase &&
+ pbm->mem_space.start == mbase)
+ pci_register_legacy_regions(&pbm->io_space,
+ &pbm->mem_space);
+}
+
+static void __init sabre_pbm_init(struct pci_controller_info *p, int sabre_node, u32 dma_begin)
+{
+ struct pci_pbm_info *pbm;
+ char namebuf[128];
+ u32 busrange[2];
+ int node, simbas_found;
+
+ simbas_found = 0;
+ node = prom_getchild(sabre_node);
+ while ((node = prom_searchsiblings(node, "pci")) != 0) {
+ int err;
+
+ err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
+ if ((err <= 0) || strncmp(namebuf, "SUNW,simba", err))
+ goto next_pci;
+
+ err = prom_getproperty(node, "bus-range",
+ (char *)&busrange[0], sizeof(busrange));
+ if (err == 0 || err == -1) {
+ prom_printf("APB: Error, cannot get PCI bus-range.\n");
+ prom_halt();
+ }
+
+ simbas_found++;
+ if (busrange[0] == 1)
+ pbm = &p->pbm_B;
+ else
+ pbm = &p->pbm_A;
+ pbm->chip_type = PBM_CHIP_TYPE_SABRE;
+ pbm->parent = p;
+ pbm->prom_node = node;
+ pbm->pci_first_slot = 1;
+ pbm->pci_first_busno = busrange[0];
+ pbm->pci_last_busno = busrange[1];
+
+ prom_getstring(node, "name", pbm->prom_name, sizeof(pbm->prom_name));
+ err = prom_getproperty(node, "ranges",
+ (char *)pbm->pbm_ranges,
+ sizeof(pbm->pbm_ranges));
+ if (err != -1)
+ pbm->num_pbm_ranges =
+ (err / sizeof(struct linux_prom_pci_ranges));
+ else
+ pbm->num_pbm_ranges = 0;
+
+ err = prom_getproperty(node, "interrupt-map",
+ (char *)pbm->pbm_intmap,
+ sizeof(pbm->pbm_intmap));
+ if (err != -1) {
+ pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap));
+ err = prom_getproperty(node, "interrupt-map-mask",
+ (char *)&pbm->pbm_intmask,
+ sizeof(pbm->pbm_intmask));
+ if (err == -1) {
+ prom_printf("APB: Fatal error, no interrupt-map-mask.\n");
+ prom_halt();
+ }
+ } else {
+ pbm->num_pbm_intmap = 0;
+ memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask));
+ }
+
+ pbm_register_toplevel_resources(p, pbm);
+
+ next_pci:
+ node = prom_getsibling(node);
+ if (!node)
+ break;
+ }
+ if (simbas_found == 0) {
+ int err;
+
+ /* No APBs underneath, probably this is a hummingbird
+ * system.
+ */
+ pbm = &p->pbm_A;
+ pbm->parent = p;
+ pbm->prom_node = sabre_node;
+ pbm->pci_first_busno = p->pci_first_busno;
+ pbm->pci_last_busno = p->pci_last_busno;
+
+ prom_getstring(sabre_node, "name", pbm->prom_name, sizeof(pbm->prom_name));
+ err = prom_getproperty(sabre_node, "ranges",
+ (char *) pbm->pbm_ranges,
+ sizeof(pbm->pbm_ranges));
+ if (err != -1)
+ pbm->num_pbm_ranges =
+ (err / sizeof(struct linux_prom_pci_ranges));
+ else
+ pbm->num_pbm_ranges = 0;
+
+ err = prom_getproperty(sabre_node, "interrupt-map",
+ (char *) pbm->pbm_intmap,
+ sizeof(pbm->pbm_intmap));
+
+ if (err != -1) {
+ pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap));
+ err = prom_getproperty(sabre_node, "interrupt-map-mask",
+ (char *)&pbm->pbm_intmask,
+ sizeof(pbm->pbm_intmask));
+ if (err == -1) {
+ prom_printf("Hummingbird: Fatal error, no interrupt-map-mask.\n");
+ prom_halt();
+ }
+ } else {
+ pbm->num_pbm_intmap = 0;
+ memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask));
+ }
+
+
+ sprintf(pbm->name, "SABRE%d PBM%c", p->index,
+ (pbm == &p->pbm_A ? 'A' : 'B'));
+ pbm->io_space.name = pbm->mem_space.name = pbm->name;
+
+ /* Hack up top-level resources. */
+ pbm->io_space.start = p->pbm_A.controller_regs + SABRE_IOSPACE;
+ pbm->io_space.end = pbm->io_space.start + (1UL << 24) - 1UL;
+ pbm->io_space.flags = IORESOURCE_IO;
+
+ pbm->mem_space.start = p->pbm_A.controller_regs + SABRE_MEMSPACE;
+ pbm->mem_space.end = pbm->mem_space.start + (unsigned long)dma_begin - 1UL;
+ pbm->mem_space.flags = IORESOURCE_MEM;
+
+ if (request_resource(&ioport_resource, &pbm->io_space) < 0) {
+ prom_printf("Cannot register Hummingbird's IO space.\n");
+ prom_halt();
+ }
+ if (request_resource(&iomem_resource, &pbm->mem_space) < 0) {
+ prom_printf("Cannot register Hummingbird's MEM space.\n");
+ prom_halt();
+ }
+
+ pci_register_legacy_regions(&pbm->io_space,
+ &pbm->mem_space);
+ }
+}
+
+void __init sabre_init(int pnode, char *model_name)
+{
+ struct linux_prom64_registers pr_regs[2];
+ struct pci_controller_info *p;
+ struct pci_iommu *iommu;
+ int tsbsize, err;
+ u32 busrange[2];
+ u32 vdma[2];
+ u32 upa_portid, dma_mask;
+ u64 clear_irq;
+
+ hummingbird_p = 0;
+ if (!strcmp(model_name, "pci108e,a001"))
+ hummingbird_p = 1;
+ else if (!strcmp(model_name, "SUNW,sabre")) {
+ char compat[64];
+
+ if (prom_getproperty(pnode, "compatible",
+ compat, sizeof(compat)) > 0 &&
+ !strcmp(compat, "pci108e,a001")) {
+ hummingbird_p = 1;
+ } else {
+ int cpu_node;
+
+ /* Of course, Sun has to encode things a thousand
+ * different ways, inconsistently.
+ */
+ cpu_find_by_instance(0, &cpu_node, NULL);
+ if (prom_getproperty(cpu_node, "name",
+ compat, sizeof(compat)) > 0 &&
+ !strcmp(compat, "SUNW,UltraSPARC-IIe"))
+ hummingbird_p = 1;
+ }
+ }
+
+ p = kmalloc(sizeof(*p), GFP_ATOMIC);
+ if (!p) {
+ prom_printf("SABRE: Error, kmalloc(pci_controller_info) failed.\n");
+ prom_halt();
+ }
+ memset(p, 0, sizeof(*p));
+
+ iommu = kmalloc(sizeof(*iommu), GFP_ATOMIC);
+ if (!iommu) {
+ prom_printf("SABRE: Error, kmalloc(pci_iommu) failed.\n");
+ prom_halt();
+ }
+ memset(iommu, 0, sizeof(*iommu));
+ p->pbm_A.iommu = p->pbm_B.iommu = iommu;
+
+ upa_portid = prom_getintdefault(pnode, "upa-portid", 0xff);
+
+ p->next = pci_controller_root;
+ pci_controller_root = p;
+
+ p->pbm_A.portid = upa_portid;
+ p->pbm_B.portid = upa_portid;
+ p->index = pci_num_controllers++;
+ p->pbms_same_domain = 1;
+ p->scan_bus = sabre_scan_bus;
+ p->irq_build = sabre_irq_build;
+ p->base_address_update = sabre_base_address_update;
+ p->resource_adjust = sabre_resource_adjust;
+ p->pci_ops = &sabre_ops;
+
+ /*
+ * Map in SABRE register set and report the presence of this SABRE.
+ */
+ err = prom_getproperty(pnode, "reg",
+ (char *)&pr_regs[0], sizeof(pr_regs));
+ if(err == 0 || err == -1) {
+ prom_printf("SABRE: Error, cannot get U2P registers "
+ "from PROM.\n");
+ prom_halt();
+ }
+
+ /*
+ * First REG in property is base of entire SABRE register space.
+ */
+ p->pbm_A.controller_regs = pr_regs[0].phys_addr;
+ p->pbm_B.controller_regs = pr_regs[0].phys_addr;
+ pci_dma_wsync = p->pbm_A.controller_regs + SABRE_WRSYNC;
+
+ printk("PCI: Found SABRE, main regs at %016lx, wsync at %016lx\n",
+ p->pbm_A.controller_regs, pci_dma_wsync);
+
+ /* Clear interrupts */
+
+ /* PCI first */
+ for (clear_irq = SABRE_ICLR_A_SLOT0; clear_irq < SABRE_ICLR_B_SLOT0 + 0x80; clear_irq += 8)
+ sabre_write(p->pbm_A.controller_regs + clear_irq, 0x0UL);
+
+ /* Then OBIO */
+ for (clear_irq = SABRE_ICLR_SCSI; clear_irq < SABRE_ICLR_SCSI + 0x80; clear_irq += 8)
+ sabre_write(p->pbm_A.controller_regs + clear_irq, 0x0UL);
+
+ /* Error interrupts are enabled later after the bus scan. */
+ sabre_write(p->pbm_A.controller_regs + SABRE_PCICTRL,
+ (SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR |
+ SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN));
+
+ /* Now map in PCI config space for entire SABRE. */
+ p->pbm_A.config_space = p->pbm_B.config_space =
+ (p->pbm_A.controller_regs + SABRE_CONFIGSPACE);
+ printk("SABRE: Shared PCI config space at %016lx\n",
+ p->pbm_A.config_space);
+
+ err = prom_getproperty(pnode, "virtual-dma",
+ (char *)&vdma[0], sizeof(vdma));
+ if(err == 0 || err == -1) {
+ prom_printf("SABRE: Error, cannot get virtual-dma property "
+ "from PROM.\n");
+ prom_halt();
+ }
+
+ dma_mask = vdma[0];
+ switch(vdma[1]) {
+ case 0x20000000:
+ dma_mask |= 0x1fffffff;
+ tsbsize = 64;
+ break;
+ case 0x40000000:
+ dma_mask |= 0x3fffffff;
+ tsbsize = 128;
+ break;
+
+ case 0x80000000:
+ dma_mask |= 0x7fffffff;
+ tsbsize = 128;
+ break;
+ default:
+ prom_printf("SABRE: strange virtual-dma size.\n");
+ prom_halt();
+ }
+
+ sabre_iommu_init(p, tsbsize, vdma[0], dma_mask);
+
+ printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+
+ err = prom_getproperty(pnode, "bus-range",
+ (char *)&busrange[0], sizeof(busrange));
+ if(err == 0 || err == -1) {
+ prom_printf("SABRE: Error, cannot get PCI bus-range "
+ " from PROM.\n");
+ prom_halt();
+ }
+
+ p->pci_first_busno = busrange[0];
+ p->pci_last_busno = busrange[1];
+
+ /*
+ * Look for APB underneath.
+ */
+ sabre_pbm_init(p, pnode, vdma[0]);
+}
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
new file mode 100644
index 000000000000..e93fcadc3722
--- /dev/null
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -0,0 +1,2187 @@
+/* $Id: pci_schizo.c,v 1.24 2002/01/23 11:27:32 davem Exp $
+ * pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support.
+ *
+ * Copyright (C) 2001, 2002, 2003 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <asm/pbm.h>
+#include <asm/iommu.h>
+#include <asm/irq.h>
+#include <asm/upa.h>
+
+#include "pci_impl.h"
+#include "iommu_common.h"
+
+/* All SCHIZO registers are 64-bits. The following accessor
+ * routines are how they are accessed. The REG parameter
+ * is a physical address.
+ */
+#define schizo_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define schizo_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory")
+
+/* This is a convention that at least Excalibur and Merlin
+ * follow. I suppose the SCHIZO used in Starcat and friends
+ * will do similar.
+ *
+ * The only way I could see this changing is if the newlink
+ * block requires more space in Schizo's address space than
+ * they predicted, thus requiring an address space reorg when
+ * the newer Schizo is taped out.
+ */
+
+/* Streaming buffer control register. */
+#define SCHIZO_STRBUF_CTRL_LPTR 0x00000000000000f0UL /* LRU Lock Pointer */
+#define SCHIZO_STRBUF_CTRL_LENAB 0x0000000000000008UL /* LRU Lock Enable */
+#define SCHIZO_STRBUF_CTRL_RRDIS 0x0000000000000004UL /* Rerun Disable */
+#define SCHIZO_STRBUF_CTRL_DENAB 0x0000000000000002UL /* Diagnostic Mode Enable */
+#define SCHIZO_STRBUF_CTRL_ENAB 0x0000000000000001UL /* Streaming Buffer Enable */
+
+/* IOMMU control register. */
+#define SCHIZO_IOMMU_CTRL_RESV 0xfffffffff9000000UL /* Reserved */
+#define SCHIZO_IOMMU_CTRL_XLTESTAT 0x0000000006000000UL /* Translation Error Status */
+#define SCHIZO_IOMMU_CTRL_XLTEERR 0x0000000001000000UL /* Translation Error encountered */
+#define SCHIZO_IOMMU_CTRL_LCKEN 0x0000000000800000UL /* Enable translation locking */
+#define SCHIZO_IOMMU_CTRL_LCKPTR 0x0000000000780000UL /* Translation lock pointer */
+#define SCHIZO_IOMMU_CTRL_TSBSZ 0x0000000000070000UL /* TSB Size */
+#define SCHIZO_IOMMU_TSBSZ_1K 0x0000000000000000UL /* TSB Table 1024 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_2K 0x0000000000010000UL /* TSB Table 2048 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_4K 0x0000000000020000UL /* TSB Table 4096 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_8K 0x0000000000030000UL /* TSB Table 8192 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_16K 0x0000000000040000UL /* TSB Table 16k 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_32K 0x0000000000050000UL /* TSB Table 32k 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_64K 0x0000000000060000UL /* TSB Table 64k 8-byte entries */
+#define SCHIZO_IOMMU_TSBSZ_128K 0x0000000000070000UL /* TSB Table 128k 8-byte entries */
+#define SCHIZO_IOMMU_CTRL_RESV2 0x000000000000fff8UL /* Reserved */
+#define SCHIZO_IOMMU_CTRL_TBWSZ 0x0000000000000004UL /* Assumed page size, 0=8k 1=64k */
+#define SCHIZO_IOMMU_CTRL_DENAB 0x0000000000000002UL /* Diagnostic mode enable */
+#define SCHIZO_IOMMU_CTRL_ENAB 0x0000000000000001UL /* IOMMU Enable */
+
+/* Schizo config space address format is nearly identical to
+ * that of PSYCHO:
+ *
+ * 32 24 23 16 15 11 10 8 7 2 1 0
+ * ---------------------------------------------------------
+ * |0 0 0 0 0 0 0 0 0| bus | device | function | reg | 0 0 |
+ * ---------------------------------------------------------
+ */
+#define SCHIZO_CONFIG_BASE(PBM) ((PBM)->config_space)
+#define SCHIZO_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+static void *schizo_pci_config_mkaddr(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned int devfn,
+ int where)
+{
+ if (!pbm)
+ return NULL;
+ bus -= pbm->pci_first_busno;
+ return (void *)
+ (SCHIZO_CONFIG_BASE(pbm) |
+ SCHIZO_CONFIG_ENCODE(bus, devfn, where));
+}
+
+/* Just make sure the bus number is in range. */
+static int schizo_out_of_range(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned char devfn)
+{
+ if (bus < pbm->pci_first_busno ||
+ bus > pbm->pci_last_busno)
+ return 1;
+ return 0;
+}
+
+/* SCHIZO PCI configuration space accessors. */
+
+static int schizo_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ struct pci_pbm_info *pbm = bus_dev->sysdata;
+ unsigned char bus = bus_dev->number;
+ u32 *addr;
+ u16 tmp16;
+ u8 tmp8;
+
+ switch (size) {
+ case 1:
+ *value = 0xff;
+ break;
+ case 2:
+ *value = 0xffff;
+ break;
+ case 4:
+ *value = 0xffffffff;
+ break;
+ }
+
+ addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (schizo_out_of_range(pbm, bus, devfn))
+ return PCIBIOS_SUCCESSFUL;
+ switch (size) {
+ case 1:
+ pci_config_read8((u8 *)addr, &tmp8);
+ *value = tmp8;
+ break;
+
+ case 2:
+ if (where & 0x01) {
+ printk("pci_read_config_word: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_read16((u16 *)addr, &tmp16);
+ *value = tmp16;
+ break;
+
+ case 4:
+ if (where & 0x03) {
+ printk("pci_read_config_dword: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_read32(addr, value);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int schizo_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ struct pci_pbm_info *pbm = bus_dev->sysdata;
+ unsigned char bus = bus_dev->number;
+ u32 *addr;
+
+ addr = schizo_pci_config_mkaddr(pbm, bus, devfn, where);
+ if (!addr)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (schizo_out_of_range(pbm, bus, devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ switch (size) {
+ case 1:
+ pci_config_write8((u8 *)addr, value);
+ break;
+
+ case 2:
+ if (where & 0x01) {
+ printk("pci_write_config_word: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+ pci_config_write16((u16 *)addr, value);
+ break;
+
+ case 4:
+ if (where & 0x03) {
+ printk("pci_write_config_dword: misaligned reg [%x]\n",
+ where);
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ pci_config_write32(addr, value);
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops schizo_ops = {
+ .read = schizo_read_pci_cfg,
+ .write = schizo_write_pci_cfg,
+};
+
+/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
+ * imap/iclr registers are per-PBM.
+ */
+#define SCHIZO_IMAP_BASE 0x1000UL
+#define SCHIZO_ICLR_BASE 0x1400UL
+
+static unsigned long schizo_imap_offset(unsigned long ino)
+{
+ return SCHIZO_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_iclr_offset(unsigned long ino)
+{
+ return SCHIZO_ICLR_BASE + (ino * 8UL);
+}
+
+/* PCI SCHIZO INO number to Sparc PIL level. This table only matters for
+ * INOs which will not have an associated PCI device struct, ie. onboard
+ * EBUS devices and PCI controller internal error interrupts.
+ */
+static unsigned char schizo_pil_table[] = {
+/*0x00*/0, 0, 0, 0, /* PCI slot 0 Int A, B, C, D */
+/*0x04*/0, 0, 0, 0, /* PCI slot 1 Int A, B, C, D */
+/*0x08*/0, 0, 0, 0, /* PCI slot 2 Int A, B, C, D */
+/*0x0c*/0, 0, 0, 0, /* PCI slot 3 Int A, B, C, D */
+/*0x10*/0, 0, 0, 0, /* PCI slot 4 Int A, B, C, D */
+/*0x14*/0, 0, 0, 0, /* PCI slot 5 Int A, B, C, D */
+/*0x18*/4, /* SCSI */
+/*0x19*/4, /* second SCSI */
+/*0x1a*/0, /* UNKNOWN */
+/*0x1b*/0, /* UNKNOWN */
+/*0x1c*/8, /* Parallel */
+/*0x1d*/5, /* Ethernet */
+/*0x1e*/8, /* Firewire-1394 */
+/*0x1f*/9, /* USB */
+/*0x20*/13, /* Audio Record */
+/*0x21*/14, /* Audio Playback */
+/*0x22*/12, /* Serial */
+/*0x23*/4, /* EBUS I2C */
+/*0x24*/10, /* RTC Clock */
+/*0x25*/11, /* Floppy */
+/*0x26*/0, /* UNKNOWN */
+/*0x27*/0, /* UNKNOWN */
+/*0x28*/0, /* UNKNOWN */
+/*0x29*/0, /* UNKNOWN */
+/*0x2a*/10, /* UPA 1 */
+/*0x2b*/10, /* UPA 2 */
+/*0x2c*/0, /* UNKNOWN */
+/*0x2d*/0, /* UNKNOWN */
+/*0x2e*/0, /* UNKNOWN */
+/*0x2f*/0, /* UNKNOWN */
+/*0x30*/15, /* Uncorrectable ECC */
+/*0x31*/15, /* Correctable ECC */
+/*0x32*/15, /* PCI Bus A Error */
+/*0x33*/15, /* PCI Bus B Error */
+/*0x34*/15, /* Safari Bus Error */
+/*0x35*/0, /* Reserved */
+/*0x36*/0, /* Reserved */
+/*0x37*/0, /* Reserved */
+/*0x38*/0, /* Reserved for NewLink */
+/*0x39*/0, /* Reserved for NewLink */
+/*0x3a*/0, /* Reserved for NewLink */
+/*0x3b*/0, /* Reserved for NewLink */
+/*0x3c*/0, /* Reserved for NewLink */
+/*0x3d*/0, /* Reserved for NewLink */
+/*0x3e*/0, /* Reserved for NewLink */
+/*0x3f*/0, /* Reserved for NewLink */
+};
+
+static int __init schizo_ino_to_pil(struct pci_dev *pdev, unsigned int ino)
+{
+ int ret;
+
+ if (pdev &&
+ pdev->vendor == PCI_VENDOR_ID_SUN &&
+ pdev->device == PCI_DEVICE_ID_SUN_RIO_USB)
+ return 9;
+
+ ret = schizo_pil_table[ino];
+ if (ret == 0 && pdev == NULL) {
+ ret = 4;
+ } else if (ret == 0) {
+ switch ((pdev->class >> 16) & 0xff) {
+ case PCI_BASE_CLASS_STORAGE:
+ ret = 4;
+ break;
+
+ case PCI_BASE_CLASS_NETWORK:
+ ret = 6;
+ break;
+
+ case PCI_BASE_CLASS_DISPLAY:
+ ret = 9;
+ break;
+
+ case PCI_BASE_CLASS_MULTIMEDIA:
+ case PCI_BASE_CLASS_MEMORY:
+ case PCI_BASE_CLASS_BRIDGE:
+ case PCI_BASE_CLASS_SERIAL:
+ ret = 10;
+ break;
+
+ default:
+ ret = 4;
+ break;
+ };
+ }
+
+ return ret;
+}
+
+static unsigned int schizo_irq_build(struct pci_pbm_info *pbm,
+ struct pci_dev *pdev,
+ unsigned int ino)
+{
+ struct ino_bucket *bucket;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int pil, ign_fixup;
+
+ ino &= PCI_IRQ_INO;
+ imap_off = schizo_imap_offset(ino);
+
+ /* Now build the IRQ bucket. */
+ pil = schizo_ino_to_pil(pdev, ino);
+
+ if (PIL_RESERVED(pil))
+ BUG();
+
+ imap = pbm->pbm_regs + imap_off;
+ imap += 4;
+
+ iclr_off = schizo_iclr_offset(ino);
+ iclr = pbm->pbm_regs + iclr_off;
+ iclr += 4;
+
+ /* On Schizo, no inofixup occurs. This is because each
+ * INO has it's own IMAP register. On Psycho and Sabre
+ * there is only one IMAP register for each PCI slot even
+ * though four different INOs can be generated by each
+ * PCI slot.
+ *
+ * But, for JBUS variants (essentially, Tomatillo), we have
+ * to fixup the lowest bit of the interrupt group number.
+ */
+ ign_fixup = 0;
+ if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) {
+ if (pbm->portid & 1)
+ ign_fixup = (1 << 6);
+ }
+
+ bucket = __bucket(build_irq(pil, ign_fixup, iclr, imap));
+ bucket->flags |= IBF_PCI;
+
+ return __irq(bucket);
+}
+
+/* SCHIZO error handling support. */
+enum schizo_error_type {
+ UE_ERR, CE_ERR, PCI_ERR, SAFARI_ERR
+};
+
+static DEFINE_SPINLOCK(stc_buf_lock);
+static unsigned long stc_error_buf[128];
+static unsigned long stc_tag_buf[16];
+static unsigned long stc_line_buf[16];
+
+#define SCHIZO_UE_INO 0x30 /* Uncorrectable ECC error */
+#define SCHIZO_CE_INO 0x31 /* Correctable ECC error */
+#define SCHIZO_PCIERR_A_INO 0x32 /* PBM A PCI bus error */
+#define SCHIZO_PCIERR_B_INO 0x33 /* PBM B PCI bus error */
+#define SCHIZO_SERR_INO 0x34 /* Safari interface error */
+
+struct pci_pbm_info *pbm_for_ino(struct pci_controller_info *p, u32 ino)
+{
+ ino &= IMAP_INO;
+ if (p->pbm_A.ino_bitmap & (1UL << ino))
+ return &p->pbm_A;
+ if (p->pbm_B.ino_bitmap & (1UL << ino))
+ return &p->pbm_B;
+
+ printk("PCI%d: No ino_bitmap entry for ino[%x], bitmaps "
+ "PBM_A[%016lx] PBM_B[%016lx]",
+ p->index, ino,
+ p->pbm_A.ino_bitmap,
+ p->pbm_B.ino_bitmap);
+ printk("PCI%d: Using PBM_A, report this problem immediately.\n",
+ p->index);
+
+ return &p->pbm_A;
+}
+
+static void schizo_clear_other_err_intr(struct pci_controller_info *p, int irq)
+{
+ struct pci_pbm_info *pbm;
+ struct ino_bucket *bucket;
+ unsigned long iclr;
+
+ /* Do not clear the interrupt for the other PCI bus.
+ *
+ * This "ACK both PBM IRQs" only needs to be performed
+ * for chip-wide error interrupts.
+ */
+ if ((irq & IMAP_INO) == SCHIZO_PCIERR_A_INO ||
+ (irq & IMAP_INO) == SCHIZO_PCIERR_B_INO)
+ return;
+
+ pbm = pbm_for_ino(p, irq);
+ if (pbm == &p->pbm_A)
+ pbm = &p->pbm_B;
+ else
+ pbm = &p->pbm_A;
+
+ irq = schizo_irq_build(pbm, NULL,
+ (pbm->portid << 6) | (irq & IMAP_INO));
+ bucket = __bucket(irq);
+ iclr = bucket->iclr;
+
+ upa_writel(ICLR_IDLE, iclr);
+}
+
+#define SCHIZO_STC_ERR 0xb800UL /* --> 0xba00 */
+#define SCHIZO_STC_TAG 0xba00UL /* --> 0xba80 */
+#define SCHIZO_STC_LINE 0xbb00UL /* --> 0xbb80 */
+
+#define SCHIZO_STCERR_WRITE 0x2UL
+#define SCHIZO_STCERR_READ 0x1UL
+
+#define SCHIZO_STCTAG_PPN 0x3fffffff00000000UL
+#define SCHIZO_STCTAG_VPN 0x00000000ffffe000UL
+#define SCHIZO_STCTAG_VALID 0x8000000000000000UL
+#define SCHIZO_STCTAG_READ 0x4000000000000000UL
+
+#define SCHIZO_STCLINE_LINDX 0x0000000007800000UL
+#define SCHIZO_STCLINE_SPTR 0x000000000007e000UL
+#define SCHIZO_STCLINE_LADDR 0x0000000000001fc0UL
+#define SCHIZO_STCLINE_EPTR 0x000000000000003fUL
+#define SCHIZO_STCLINE_VALID 0x0000000000600000UL
+#define SCHIZO_STCLINE_FOFN 0x0000000000180000UL
+
+static void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm,
+ enum schizo_error_type type)
+{
+ struct pci_strbuf *strbuf = &pbm->stc;
+ unsigned long regbase = pbm->pbm_regs;
+ unsigned long err_base, tag_base, line_base;
+ u64 control;
+ int i;
+
+ err_base = regbase + SCHIZO_STC_ERR;
+ tag_base = regbase + SCHIZO_STC_TAG;
+ line_base = regbase + SCHIZO_STC_LINE;
+
+ spin_lock(&stc_buf_lock);
+
+ /* This is __REALLY__ dangerous. When we put the
+ * streaming buffer into diagnostic mode to probe
+ * it's tags and error status, we _must_ clear all
+ * of the line tag valid bits before re-enabling
+ * the streaming buffer. If any dirty data lives
+ * in the STC when we do this, we will end up
+ * invalidating it before it has a chance to reach
+ * main memory.
+ */
+ control = schizo_read(strbuf->strbuf_control);
+ schizo_write(strbuf->strbuf_control,
+ (control | SCHIZO_STRBUF_CTRL_DENAB));
+ for (i = 0; i < 128; i++) {
+ unsigned long val;
+
+ val = schizo_read(err_base + (i * 8UL));
+ schizo_write(err_base + (i * 8UL), 0UL);
+ stc_error_buf[i] = val;
+ }
+ for (i = 0; i < 16; i++) {
+ stc_tag_buf[i] = schizo_read(tag_base + (i * 8UL));
+ stc_line_buf[i] = schizo_read(line_base + (i * 8UL));
+ schizo_write(tag_base + (i * 8UL), 0UL);
+ schizo_write(line_base + (i * 8UL), 0UL);
+ }
+
+ /* OK, state is logged, exit diagnostic mode. */
+ schizo_write(strbuf->strbuf_control, control);
+
+ for (i = 0; i < 16; i++) {
+ int j, saw_error, first, last;
+
+ saw_error = 0;
+ first = i * 8;
+ last = first + 8;
+ for (j = first; j < last; j++) {
+ unsigned long errval = stc_error_buf[j];
+ if (errval != 0) {
+ saw_error++;
+ printk("%s: STC_ERR(%d)[wr(%d)rd(%d)]\n",
+ pbm->name,
+ j,
+ (errval & SCHIZO_STCERR_WRITE) ? 1 : 0,
+ (errval & SCHIZO_STCERR_READ) ? 1 : 0);
+ }
+ }
+ if (saw_error != 0) {
+ unsigned long tagval = stc_tag_buf[i];
+ unsigned long lineval = stc_line_buf[i];
+ printk("%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)R(%d)]\n",
+ pbm->name,
+ i,
+ ((tagval & SCHIZO_STCTAG_PPN) >> 19UL),
+ (tagval & SCHIZO_STCTAG_VPN),
+ ((tagval & SCHIZO_STCTAG_VALID) ? 1 : 0),
+ ((tagval & SCHIZO_STCTAG_READ) ? 1 : 0));
+
+ /* XXX Should spit out per-bank error information... -DaveM */
+ printk("%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)"
+ "V(%d)FOFN(%d)]\n",
+ pbm->name,
+ i,
+ ((lineval & SCHIZO_STCLINE_LINDX) >> 23UL),
+ ((lineval & SCHIZO_STCLINE_SPTR) >> 13UL),
+ ((lineval & SCHIZO_STCLINE_LADDR) >> 6UL),
+ ((lineval & SCHIZO_STCLINE_EPTR) >> 0UL),
+ ((lineval & SCHIZO_STCLINE_VALID) ? 1 : 0),
+ ((lineval & SCHIZO_STCLINE_FOFN) ? 1 : 0));
+ }
+ }
+
+ spin_unlock(&stc_buf_lock);
+}
+
+/* IOMMU is per-PBM in Schizo, so interrogate both for anonymous
+ * controller level errors.
+ */
+
+#define SCHIZO_IOMMU_TAG 0xa580UL
+#define SCHIZO_IOMMU_DATA 0xa600UL
+
+#define SCHIZO_IOMMU_TAG_CTXT 0x0000001ffe000000UL
+#define SCHIZO_IOMMU_TAG_ERRSTS 0x0000000001800000UL
+#define SCHIZO_IOMMU_TAG_ERR 0x0000000000400000UL
+#define SCHIZO_IOMMU_TAG_WRITE 0x0000000000200000UL
+#define SCHIZO_IOMMU_TAG_STREAM 0x0000000000100000UL
+#define SCHIZO_IOMMU_TAG_SIZE 0x0000000000080000UL
+#define SCHIZO_IOMMU_TAG_VPAGE 0x000000000007ffffUL
+
+#define SCHIZO_IOMMU_DATA_VALID 0x0000000100000000UL
+#define SCHIZO_IOMMU_DATA_CACHE 0x0000000040000000UL
+#define SCHIZO_IOMMU_DATA_PPAGE 0x000000003fffffffUL
+
+static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm,
+ enum schizo_error_type type)
+{
+ struct pci_iommu *iommu = pbm->iommu;
+ unsigned long iommu_tag[16];
+ unsigned long iommu_data[16];
+ unsigned long flags;
+ u64 control;
+ int i;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+ control = schizo_read(iommu->iommu_control);
+ if (control & SCHIZO_IOMMU_CTRL_XLTEERR) {
+ unsigned long base;
+ char *type_string;
+
+ /* Clear the error encountered bit. */
+ control &= ~SCHIZO_IOMMU_CTRL_XLTEERR;
+ schizo_write(iommu->iommu_control, control);
+
+ switch((control & SCHIZO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
+ case 0:
+ type_string = "Protection Error";
+ break;
+ case 1:
+ type_string = "Invalid Error";
+ break;
+ case 2:
+ type_string = "TimeOut Error";
+ break;
+ case 3:
+ default:
+ type_string = "ECC Error";
+ break;
+ };
+ printk("%s: IOMMU Error, type[%s]\n",
+ pbm->name, type_string);
+
+ /* Put the IOMMU into diagnostic mode and probe
+ * it's TLB for entries with error status.
+ *
+ * It is very possible for another DVMA to occur
+ * while we do this probe, and corrupt the system
+ * further. But we are so screwed at this point
+ * that we are likely to crash hard anyways, so
+ * get as much diagnostic information to the
+ * console as we can.
+ */
+ schizo_write(iommu->iommu_control,
+ control | SCHIZO_IOMMU_CTRL_DENAB);
+
+ base = pbm->pbm_regs;
+
+ for (i = 0; i < 16; i++) {
+ iommu_tag[i] =
+ schizo_read(base + SCHIZO_IOMMU_TAG + (i * 8UL));
+ iommu_data[i] =
+ schizo_read(base + SCHIZO_IOMMU_DATA + (i * 8UL));
+
+ /* Now clear out the entry. */
+ schizo_write(base + SCHIZO_IOMMU_TAG + (i * 8UL), 0);
+ schizo_write(base + SCHIZO_IOMMU_DATA + (i * 8UL), 0);
+ }
+
+ /* Leave diagnostic mode. */
+ schizo_write(iommu->iommu_control, control);
+
+ for (i = 0; i < 16; i++) {
+ unsigned long tag, data;
+
+ tag = iommu_tag[i];
+ if (!(tag & SCHIZO_IOMMU_TAG_ERR))
+ continue;
+
+ data = iommu_data[i];
+ switch((tag & SCHIZO_IOMMU_TAG_ERRSTS) >> 23UL) {
+ case 0:
+ type_string = "Protection Error";
+ break;
+ case 1:
+ type_string = "Invalid Error";
+ break;
+ case 2:
+ type_string = "TimeOut Error";
+ break;
+ case 3:
+ default:
+ type_string = "ECC Error";
+ break;
+ };
+ printk("%s: IOMMU TAG(%d)[error(%s) ctx(%x) wr(%d) str(%d) "
+ "sz(%dK) vpg(%08lx)]\n",
+ pbm->name, i, type_string,
+ (int)((tag & SCHIZO_IOMMU_TAG_CTXT) >> 25UL),
+ ((tag & SCHIZO_IOMMU_TAG_WRITE) ? 1 : 0),
+ ((tag & SCHIZO_IOMMU_TAG_STREAM) ? 1 : 0),
+ ((tag & SCHIZO_IOMMU_TAG_SIZE) ? 64 : 8),
+ (tag & SCHIZO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT);
+ printk("%s: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n",
+ pbm->name, i,
+ ((data & SCHIZO_IOMMU_DATA_VALID) ? 1 : 0),
+ ((data & SCHIZO_IOMMU_DATA_CACHE) ? 1 : 0),
+ (data & SCHIZO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT);
+ }
+ }
+ if (pbm->stc.strbuf_enabled)
+ __schizo_check_stc_error_pbm(pbm, type);
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+static void schizo_check_iommu_error(struct pci_controller_info *p,
+ enum schizo_error_type type)
+{
+ schizo_check_iommu_error_pbm(&p->pbm_A, type);
+ schizo_check_iommu_error_pbm(&p->pbm_B, type);
+}
+
+/* Uncorrectable ECC error status gathering. */
+#define SCHIZO_UE_AFSR 0x10030UL
+#define SCHIZO_UE_AFAR 0x10038UL
+
+#define SCHIZO_UEAFSR_PPIO 0x8000000000000000UL /* Safari */
+#define SCHIZO_UEAFSR_PDRD 0x4000000000000000UL /* Safari/Tomatillo */
+#define SCHIZO_UEAFSR_PDWR 0x2000000000000000UL /* Safari */
+#define SCHIZO_UEAFSR_SPIO 0x1000000000000000UL /* Safari */
+#define SCHIZO_UEAFSR_SDMA 0x0800000000000000UL /* Safari/Tomatillo */
+#define SCHIZO_UEAFSR_ERRPNDG 0x0300000000000000UL /* Safari */
+#define SCHIZO_UEAFSR_BMSK 0x000003ff00000000UL /* Safari */
+#define SCHIZO_UEAFSR_QOFF 0x00000000c0000000UL /* Safari/Tomatillo */
+#define SCHIZO_UEAFSR_AID 0x000000001f000000UL /* Safari/Tomatillo */
+#define SCHIZO_UEAFSR_PARTIAL 0x0000000000800000UL /* Safari */
+#define SCHIZO_UEAFSR_OWNEDIN 0x0000000000400000UL /* Safari */
+#define SCHIZO_UEAFSR_MTAGSYND 0x00000000000f0000UL /* Safari */
+#define SCHIZO_UEAFSR_MTAG 0x000000000000e000UL /* Safari */
+#define SCHIZO_UEAFSR_ECCSYND 0x00000000000001ffUL /* Safari */
+
+static irqreturn_t schizo_ue_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg = p->pbm_B.controller_regs + SCHIZO_UE_AFSR;
+ unsigned long afar_reg = p->pbm_B.controller_regs + SCHIZO_UE_AFAR;
+ unsigned long afsr, afar, error_bits;
+ int reported, limit;
+
+ /* Latch uncorrectable error status. */
+ afar = schizo_read(afar_reg);
+
+ /* If either of the error pending bits are set in the
+ * AFSR, the error status is being actively updated by
+ * the hardware and we must re-read to get a clean value.
+ */
+ limit = 1000;
+ do {
+ afsr = schizo_read(afsr_reg);
+ } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit);
+
+ /* Clear the primary/secondary error status bits. */
+ error_bits = afsr &
+ (SCHIZO_UEAFSR_PPIO | SCHIZO_UEAFSR_PDRD | SCHIZO_UEAFSR_PDWR |
+ SCHIZO_UEAFSR_SPIO | SCHIZO_UEAFSR_SDMA);
+ if (!error_bits)
+ return IRQ_NONE;
+ schizo_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("PCI%d: Uncorrectable Error, primary error type[%s]\n",
+ p->index,
+ (((error_bits & SCHIZO_UEAFSR_PPIO) ?
+ "PIO" :
+ ((error_bits & SCHIZO_UEAFSR_PDRD) ?
+ "DMA Read" :
+ ((error_bits & SCHIZO_UEAFSR_PDWR) ?
+ "DMA Write" : "???")))));
+ printk("PCI%d: bytemask[%04lx] qword_offset[%lx] SAFARI_AID[%02lx]\n",
+ p->index,
+ (afsr & SCHIZO_UEAFSR_BMSK) >> 32UL,
+ (afsr & SCHIZO_UEAFSR_QOFF) >> 30UL,
+ (afsr & SCHIZO_UEAFSR_AID) >> 24UL);
+ printk("PCI%d: partial[%d] owned_in[%d] mtag[%lx] mtag_synd[%lx] ecc_sync[%lx]\n",
+ p->index,
+ (afsr & SCHIZO_UEAFSR_PARTIAL) ? 1 : 0,
+ (afsr & SCHIZO_UEAFSR_OWNEDIN) ? 1 : 0,
+ (afsr & SCHIZO_UEAFSR_MTAG) >> 13UL,
+ (afsr & SCHIZO_UEAFSR_MTAGSYND) >> 16UL,
+ (afsr & SCHIZO_UEAFSR_ECCSYND) >> 0UL);
+ printk("PCI%d: UE AFAR [%016lx]\n", p->index, afar);
+ printk("PCI%d: UE Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & SCHIZO_UEAFSR_SPIO) {
+ reported++;
+ printk("(PIO)");
+ }
+ if (afsr & SCHIZO_UEAFSR_SDMA) {
+ reported++;
+ printk("(DMA)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ /* Interrogate IOMMU for error status. */
+ schizo_check_iommu_error(p, UE_ERR);
+
+ schizo_clear_other_err_intr(p, irq);
+
+ return IRQ_HANDLED;
+}
+
+#define SCHIZO_CE_AFSR 0x10040UL
+#define SCHIZO_CE_AFAR 0x10048UL
+
+#define SCHIZO_CEAFSR_PPIO 0x8000000000000000UL
+#define SCHIZO_CEAFSR_PDRD 0x4000000000000000UL
+#define SCHIZO_CEAFSR_PDWR 0x2000000000000000UL
+#define SCHIZO_CEAFSR_SPIO 0x1000000000000000UL
+#define SCHIZO_CEAFSR_SDMA 0x0800000000000000UL
+#define SCHIZO_CEAFSR_ERRPNDG 0x0300000000000000UL
+#define SCHIZO_CEAFSR_BMSK 0x000003ff00000000UL
+#define SCHIZO_CEAFSR_QOFF 0x00000000c0000000UL
+#define SCHIZO_CEAFSR_AID 0x000000001f000000UL
+#define SCHIZO_CEAFSR_PARTIAL 0x0000000000800000UL
+#define SCHIZO_CEAFSR_OWNEDIN 0x0000000000400000UL
+#define SCHIZO_CEAFSR_MTAGSYND 0x00000000000f0000UL
+#define SCHIZO_CEAFSR_MTAG 0x000000000000e000UL
+#define SCHIZO_CEAFSR_ECCSYND 0x00000000000001ffUL
+
+static irqreturn_t schizo_ce_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pci_controller_info *p = dev_id;
+ unsigned long afsr_reg = p->pbm_B.controller_regs + SCHIZO_CE_AFSR;
+ unsigned long afar_reg = p->pbm_B.controller_regs + SCHIZO_CE_AFAR;
+ unsigned long afsr, afar, error_bits;
+ int reported, limit;
+
+ /* Latch error status. */
+ afar = schizo_read(afar_reg);
+
+ /* If either of the error pending bits are set in the
+ * AFSR, the error status is being actively updated by
+ * the hardware and we must re-read to get a clean value.
+ */
+ limit = 1000;
+ do {
+ afsr = schizo_read(afsr_reg);
+ } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit);
+
+ /* Clear primary/secondary error status bits. */
+ error_bits = afsr &
+ (SCHIZO_CEAFSR_PPIO | SCHIZO_CEAFSR_PDRD | SCHIZO_CEAFSR_PDWR |
+ SCHIZO_CEAFSR_SPIO | SCHIZO_CEAFSR_SDMA);
+ if (!error_bits)
+ return IRQ_NONE;
+ schizo_write(afsr_reg, error_bits);
+
+ /* Log the error. */
+ printk("PCI%d: Correctable Error, primary error type[%s]\n",
+ p->index,
+ (((error_bits & SCHIZO_CEAFSR_PPIO) ?
+ "PIO" :
+ ((error_bits & SCHIZO_CEAFSR_PDRD) ?
+ "DMA Read" :
+ ((error_bits & SCHIZO_CEAFSR_PDWR) ?
+ "DMA Write" : "???")))));
+
+ /* XXX Use syndrome and afar to print out module string just like
+ * XXX UDB CE trap handler does... -DaveM
+ */
+ printk("PCI%d: bytemask[%04lx] qword_offset[%lx] SAFARI_AID[%02lx]\n",
+ p->index,
+ (afsr & SCHIZO_UEAFSR_BMSK) >> 32UL,
+ (afsr & SCHIZO_UEAFSR_QOFF) >> 30UL,
+ (afsr & SCHIZO_UEAFSR_AID) >> 24UL);
+ printk("PCI%d: partial[%d] owned_in[%d] mtag[%lx] mtag_synd[%lx] ecc_sync[%lx]\n",
+ p->index,
+ (afsr & SCHIZO_UEAFSR_PARTIAL) ? 1 : 0,
+ (afsr & SCHIZO_UEAFSR_OWNEDIN) ? 1 : 0,
+ (afsr & SCHIZO_UEAFSR_MTAG) >> 13UL,
+ (afsr & SCHIZO_UEAFSR_MTAGSYND) >> 16UL,
+ (afsr & SCHIZO_UEAFSR_ECCSYND) >> 0UL);
+ printk("PCI%d: CE AFAR [%016lx]\n", p->index, afar);
+ printk("PCI%d: CE Secondary errors [", p->index);
+ reported = 0;
+ if (afsr & SCHIZO_CEAFSR_SPIO) {
+ reported++;
+ printk("(PIO)");
+ }
+ if (afsr & SCHIZO_CEAFSR_SDMA) {
+ reported++;
+ printk("(DMA)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ schizo_clear_other_err_intr(p, irq);
+
+ return IRQ_HANDLED;
+}
+
+#define SCHIZO_PCI_AFSR 0x2010UL
+#define SCHIZO_PCI_AFAR 0x2018UL
+
+#define SCHIZO_PCIAFSR_PMA 0x8000000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_PTA 0x4000000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_PRTRY 0x2000000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_PPERR 0x1000000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_PTTO 0x0800000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_PUNUS 0x0400000000000000UL /* Schizo */
+#define SCHIZO_PCIAFSR_SMA 0x0200000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_STA 0x0100000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_SRTRY 0x0080000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_SPERR 0x0040000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_STTO 0x0020000000000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_SUNUS 0x0010000000000000UL /* Schizo */
+#define SCHIZO_PCIAFSR_BMSK 0x000003ff00000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_BLK 0x0000000080000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_CFG 0x0000000040000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_MEM 0x0000000020000000UL /* Schizo/Tomatillo */
+#define SCHIZO_PCIAFSR_IO 0x0000000010000000UL /* Schizo/Tomatillo */
+
+#define SCHIZO_PCI_CTRL (0x2000UL)
+#define SCHIZO_PCICTRL_BUS_UNUS (1UL << 63UL) /* Safari */
+#define SCHIZO_PCICTRL_ARB_PRIO (0x1ff << 52UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_ESLCK (1UL << 51UL) /* Safari */
+#define SCHIZO_PCICTRL_ERRSLOT (7UL << 48UL) /* Safari */
+#define SCHIZO_PCICTRL_TTO_ERR (1UL << 38UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_RTRY_ERR (1UL << 37UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_DTO_ERR (1UL << 36UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_SBH_ERR (1UL << 35UL) /* Safari */
+#define SCHIZO_PCICTRL_SERR (1UL << 34UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_PCISPD (1UL << 33UL) /* Safari */
+#define SCHIZO_PCICTRL_MRM_PREF (1UL << 30UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_RDO_PREF (1UL << 29UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_RDL_PREF (1UL << 28UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_PTO (3UL << 24UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_PTO_SHIFT 24UL
+#define SCHIZO_PCICTRL_TRWSW (7UL << 21UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_F_TGT_A (1UL << 20UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_S_DTO_INT (1UL << 19UL) /* Safari */
+#define SCHIZO_PCICTRL_F_TGT_RT (1UL << 19UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_SBH_INT (1UL << 18UL) /* Safari */
+#define SCHIZO_PCICTRL_T_DTO_INT (1UL << 18UL) /* Tomatillo */
+#define SCHIZO_PCICTRL_EEN (1UL << 17UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_PARK (1UL << 16UL) /* Safari/Tomatillo */
+#define SCHIZO_PCICTRL_PCIRST (1UL << 8UL) /* Safari */
+#define SCHIZO_PCICTRL_ARB_S (0x3fUL << 0UL) /* Safari */
+#define SCHIZO_PCICTRL_ARB_T (0xffUL << 0UL) /* Tomatillo */
+
+static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
+{
+ unsigned long csr_reg, csr, csr_error_bits;
+ irqreturn_t ret = IRQ_NONE;
+ u16 stat;
+
+ csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
+ csr = schizo_read(csr_reg);
+ csr_error_bits =
+ csr & (SCHIZO_PCICTRL_BUS_UNUS |
+ SCHIZO_PCICTRL_TTO_ERR |
+ SCHIZO_PCICTRL_RTRY_ERR |
+ SCHIZO_PCICTRL_DTO_ERR |
+ SCHIZO_PCICTRL_SBH_ERR |
+ SCHIZO_PCICTRL_SERR);
+ if (csr_error_bits) {
+ /* Clear the errors. */
+ schizo_write(csr_reg, csr);
+
+ /* Log 'em. */
+ if (csr_error_bits & SCHIZO_PCICTRL_BUS_UNUS)
+ printk("%s: Bus unusable error asserted.\n",
+ pbm->name);
+ if (csr_error_bits & SCHIZO_PCICTRL_TTO_ERR)
+ printk("%s: PCI TRDY# timeout error asserted.\n",
+ pbm->name);
+ if (csr_error_bits & SCHIZO_PCICTRL_RTRY_ERR)
+ printk("%s: PCI excessiv