summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
commitdcec8a13bf565e47942a1751a9cec21bec5648fe (patch)
tree548b69625b18cc2e88c3e68d0923be546c9ebb03 /arch
parent2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff)
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash. o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'arch')
-rw-r--r--arch/alpha/Makefile34
-rw-r--r--arch/alpha/boot/bootp.c53
-rw-r--r--arch/alpha/config.in63
-rw-r--r--arch/alpha/defconfig7
-rw-r--r--arch/alpha/kernel/Makefile18
-rw-r--r--arch/alpha/kernel/alpha_ksyms.c6
-rw-r--r--arch/alpha/kernel/apecs.c68
-rw-r--r--arch/alpha/kernel/bios32.c861
-rw-r--r--arch/alpha/kernel/cia.c150
-rw-r--r--arch/alpha/kernel/entry.S70
-rw-r--r--arch/alpha/kernel/head.S21
-rw-r--r--arch/alpha/kernel/irq.c851
-rw-r--r--arch/alpha/kernel/lca.c51
-rw-r--r--arch/alpha/kernel/mcpcia.c977
-rw-r--r--arch/alpha/kernel/process.c62
-rw-r--r--arch/alpha/kernel/pyxis.c240
-rw-r--r--arch/alpha/kernel/setup.c103
-rw-r--r--arch/alpha/kernel/smc37c669.c6
-rw-r--r--arch/alpha/kernel/smc37c93x.c2
-rw-r--r--arch/alpha/kernel/smp.c1097
-rw-r--r--arch/alpha/kernel/t2.c229
-rw-r--r--arch/alpha/kernel/time.c10
-rw-r--r--arch/alpha/kernel/tsunami.c503
-rw-r--r--arch/alpha/lib/checksum.c21
-rw-r--r--arch/alpha/lib/copy_user.S53
-rw-r--r--arch/alpha/lib/csum_partial_copy.c6
-rw-r--r--arch/alpha/lib/memcpy.c23
-rw-r--r--arch/alpha/math-emu/ieee-math.c30
-rw-r--r--arch/alpha/mm/fault.c55
-rw-r--r--arch/alpha/mm/init.c59
-rw-r--r--arch/arm/Makefile61
-rw-r--r--arch/arm/boot/Makefile5
-rw-r--r--arch/arm/boot/compressed/Makefile2
-rw-r--r--arch/arm/config.in48
-rw-r--r--arch/arm/defconfig16
-rw-r--r--arch/arm/kernel/Makefile16
-rw-r--r--arch/arm/kernel/armksyms.c28
-rw-r--r--arch/arm/kernel/bios32.c144
-rw-r--r--arch/arm/kernel/calls.S6
-rw-r--r--arch/arm/kernel/dma-a5k.c79
-rw-r--r--arch/arm/kernel/dma-arc.c115
-rw-r--r--arch/arm/kernel/dma-dummy.c45
-rw-r--r--arch/arm/kernel/dma-rpc.c342
-rw-r--r--arch/arm/kernel/dma.c312
-rw-r--r--arch/arm/kernel/dma.h70
-rw-r--r--arch/arm/kernel/ecard.c100
-rw-r--r--arch/arm/kernel/entry-armv.S329
-rw-r--r--arch/arm/kernel/entry-common.S208
-rw-r--r--arch/arm/kernel/head-armo.S2
-rw-r--r--arch/arm/kernel/head-armv.S159
-rw-r--r--arch/arm/kernel/irq.c51
-rw-r--r--arch/arm/kernel/leds-ebsa285.c44
-rw-r--r--arch/arm/kernel/process.c4
-rw-r--r--arch/arm/kernel/ptrace.c14
-rw-r--r--arch/arm/kernel/setup-ebsa110.c71
-rw-r--r--arch/arm/kernel/setup.c22
-rw-r--r--arch/arm/kernel/time.c8
-rw-r--r--arch/arm/kernel/traps.c54
-rw-r--r--arch/arm/lib/Makefile27
-rw-r--r--arch/arm/lib/extractconstants.pl46
-rw-r--r--arch/arm/lib/getconsdata.c61
-rw-r--r--arch/arm/lib/io-acorn.S4
-rw-r--r--arch/arm/lib/io-ebsa285.S109
-rw-r--r--arch/arm/lib/ll_char_wr.S25
-rw-r--r--arch/arm/lib/memcpy.S11
-rw-r--r--arch/arm/lib/uaccess.S35
-rw-r--r--arch/arm/mm/Makefile16
-rw-r--r--arch/arm/mm/fault-armo.c44
-rw-r--r--arch/arm/mm/fault-armv.c121
-rw-r--r--arch/arm/mm/init.c6
-rw-r--r--arch/arm/mm/mm-arc.c1
-rw-r--r--arch/arm/mm/mm-ebsa110.c19
-rw-r--r--arch/arm/mm/mm-ebsa285.c97
-rw-r--r--arch/arm/mm/mm-nexuspci.c23
-rw-r--r--arch/arm/mm/mm-rpc.c65
-rw-r--r--arch/arm/mm/proc-arm6,7.S6
-rw-r--r--arch/arm/mm/proc-sa110.S10
-rw-r--r--arch/arm/vmlinux.lds11
-rw-r--r--arch/i386/Makefile17
-rw-r--r--arch/i386/boot/compressed/head.S8
-rw-r--r--arch/i386/config.in18
-rw-r--r--arch/i386/defconfig3
-rw-r--r--arch/i386/kernel/Makefile9
-rw-r--r--arch/i386/kernel/bios32.c448
-rw-r--r--arch/i386/kernel/entry.S33
-rw-r--r--arch/i386/kernel/head.S31
-rw-r--r--arch/i386/kernel/i386_ksyms.c10
-rw-r--r--arch/i386/kernel/io_apic.c127
-rw-r--r--arch/i386/kernel/ioport.c2
-rw-r--r--arch/i386/kernel/irq.c415
-rw-r--r--arch/i386/kernel/irq.h13
-rw-r--r--arch/i386/kernel/ldt.c26
-rw-r--r--arch/i386/kernel/mca.c30
-rw-r--r--arch/i386/kernel/mtrr.c1229
-rw-r--r--arch/i386/kernel/process.c85
-rw-r--r--arch/i386/kernel/signal.c10
-rw-r--r--arch/i386/kernel/smp.c70
-rw-r--r--arch/i386/kernel/traps.c6
-rw-r--r--arch/i386/kernel/vm86.c2
-rw-r--r--arch/i386/lib/checksum.c13
-rw-r--r--arch/i386/math-emu/fpu_system.h2
-rw-r--r--arch/i386/math-emu/poly.h2
-rw-r--r--arch/i386/mm/init.c86
-rw-r--r--arch/i386/vmlinux.lds3
-rw-r--r--arch/m68k/amiga/amiga_ksyms.c1
-rw-r--r--arch/m68k/atari/atari_ksyms.c1
-rw-r--r--arch/m68k/config.in11
-rw-r--r--arch/m68k/mac/ksyms.c1
-rw-r--r--arch/mips/config.in11
-rw-r--r--arch/mips/defconfig2
-rw-r--r--arch/mips/jazz/hw-access.c16
-rw-r--r--arch/mips/kernel/irixelf.c4
-rw-r--r--arch/mips/kernel/irq.c10
-rw-r--r--arch/mips/kernel/pci.c53
-rw-r--r--arch/mips/kernel/syscalls.h7
-rw-r--r--arch/mips/lib/csum_partial_copy.S11
-rw-r--r--arch/mips/lib/ide-std.c4
-rw-r--r--arch/mips/sgi/kernel/indy_int.c10
-rw-r--r--arch/mips/sgi/kernel/setup.c17
-rw-r--r--arch/mips/sni/hw-access.c17
-rw-r--r--arch/mips/sni/pci.c3
-rw-r--r--arch/ppc/8xx_io/.cvsignore2
-rw-r--r--arch/ppc/8xx_io/Makefile13
-rw-r--r--arch/ppc/8xx_io/commproc.c222
-rw-r--r--arch/ppc/8xx_io/commproc.h464
-rw-r--r--arch/ppc/8xx_io/enet.c912
-rw-r--r--arch/ppc/8xx_io/uart.c2530
-rw-r--r--arch/ppc/Makefile41
-rw-r--r--arch/ppc/boot/Makefile131
-rw-r--r--arch/ppc/boot/head.S182
-rw-r--r--arch/ppc/boot/mbxtty.c118
-rw-r--r--arch/ppc/boot/misc.c121
-rw-r--r--arch/ppc/boot/vreset.c173
-rw-r--r--arch/ppc/chrp_defconfig321
-rw-r--r--arch/ppc/chrpboot/main.c10
-rw-r--r--arch/ppc/chrpboot/misc.S8
-rw-r--r--arch/ppc/coffboot/main.c11
-rw-r--r--arch/ppc/coffboot/misc.S13
-rw-r--r--arch/ppc/common_defconfig331
-rw-r--r--arch/ppc/config.in75
-rw-r--r--arch/ppc/defconfig177
-rw-r--r--arch/ppc/kernel/Makefile34
-rw-r--r--arch/ppc/kernel/align.c16
-rw-r--r--arch/ppc/kernel/chrp_pci.c22
-rw-r--r--arch/ppc/kernel/chrp_setup.c103
-rw-r--r--arch/ppc/kernel/chrp_time.c9
-rw-r--r--arch/ppc/kernel/head.S818
-rw-r--r--arch/ppc/kernel/idle.c205
-rw-r--r--arch/ppc/kernel/irq.c334
-rw-r--r--arch/ppc/kernel/mbx_pci.c254
-rw-r--r--arch/ppc/kernel/mbx_setup.c169
-rw-r--r--arch/ppc/kernel/misc.S59
-rw-r--r--arch/ppc/kernel/mk_defs.c4
-rw-r--r--arch/ppc/kernel/pci.c116
-rw-r--r--arch/ppc/kernel/pmac_pci.c65
-rw-r--r--arch/ppc/kernel/pmac_setup.c160
-rw-r--r--arch/ppc/kernel/pmac_support.c26
-rw-r--r--arch/ppc/kernel/pmac_time.c46
-rw-r--r--arch/ppc/kernel/ppc-stub.c705
-rw-r--r--arch/ppc/kernel/ppc_htab.c15
-rw-r--r--arch/ppc/kernel/ppc_ksyms.c22
-rw-r--r--arch/ppc/kernel/prep_pci.c23
-rw-r--r--arch/ppc/kernel/prep_setup.c18
-rw-r--r--arch/ppc/kernel/prep_time.c2
-rw-r--r--arch/ppc/kernel/process.c110
-rw-r--r--arch/ppc/kernel/prom.c353
-rw-r--r--arch/ppc/kernel/ptrace.c11
-rw-r--r--arch/ppc/kernel/residual.c2
-rw-r--r--arch/ppc/kernel/setup.c395
-rw-r--r--arch/ppc/kernel/signal.c14
-rw-r--r--arch/ppc/kernel/smp.c189
-rw-r--r--arch/ppc/kernel/softemu8xx.c96
-rw-r--r--arch/ppc/kernel/time.c196
-rw-r--r--arch/ppc/kernel/time.h7
-rw-r--r--arch/ppc/kernel/traps.c93
-rw-r--r--arch/ppc/lib/locks.c160
-rw-r--r--arch/ppc/mbx_defconfig196
-rw-r--r--arch/ppc/mm/fault.c125
-rw-r--r--arch/ppc/mm/init.c612
-rw-r--r--arch/ppc/pmac_defconfig148
-rw-r--r--arch/ppc/prep_defconfig113
-rw-r--r--arch/sparc/Makefile29
-rw-r--r--arch/sparc/ap1000/apmmu.c364
-rw-r--r--arch/sparc/boot/Makefile8
-rw-r--r--arch/sparc/boot/btfixupprep.c345
-rw-r--r--arch/sparc/config.in24
-rw-r--r--arch/sparc/defconfig59
-rw-r--r--arch/sparc/kernel/Makefile62
-rw-r--r--arch/sparc/kernel/auxio.c6
-rw-r--r--arch/sparc/kernel/cpu.c13
-rw-r--r--arch/sparc/kernel/devices.c16
-rw-r--r--arch/sparc/kernel/entry.S204
-rw-r--r--arch/sparc/kernel/etrap.S27
-rw-r--r--arch/sparc/kernel/head.S124
-rw-r--r--arch/sparc/kernel/init_task.c6
-rw-r--r--arch/sparc/kernel/irq.c46
-rw-r--r--arch/sparc/kernel/process.c98
-rw-r--r--arch/sparc/kernel/rtrap.S5
-rw-r--r--arch/sparc/kernel/sclow.S3
-rw-r--r--arch/sparc/kernel/setup.c144
-rw-r--r--arch/sparc/kernel/signal.c127
-rw-r--r--arch/sparc/kernel/smp.c472
-rw-r--r--arch/sparc/kernel/sparc-stub.c15
-rw-r--r--arch/sparc/kernel/sparc_ksyms.c32
-rw-r--r--arch/sparc/kernel/sun4c_irq.c58
-rw-r--r--arch/sparc/kernel/sun4d_irq.c252
-rw-r--r--arch/sparc/kernel/sun4d_smp.c576
-rw-r--r--arch/sparc/kernel/sun4m_irq.c26
-rw-r--r--arch/sparc/kernel/sun4m_smp.c545
-rw-r--r--arch/sparc/kernel/sunos_ioctl.c4
-rw-r--r--arch/sparc/kernel/sys_sparc.c26
-rw-r--r--arch/sparc/kernel/sys_sunos.c147
-rw-r--r--arch/sparc/kernel/systbls.S12
-rw-r--r--arch/sparc/kernel/tadpole.c3
-rw-r--r--arch/sparc/kernel/time.c36
-rw-r--r--arch/sparc/kernel/trampoline.S91
-rw-r--r--arch/sparc/kernel/traps.c60
-rw-r--r--arch/sparc/kernel/wof.S17
-rw-r--r--arch/sparc/kernel/wuf.S19
-rw-r--r--arch/sparc/lib/Makefile32
-rw-r--r--arch/sparc/lib/atomic.S14
-rw-r--r--arch/sparc/lib/blockops.S7
-rw-r--r--arch/sparc/math-emu/.cvsignore2
-rw-r--r--arch/sparc/math-emu/Makefile37
-rw-r--r--arch/sparc/math-emu/ashldi3.S36
-rw-r--r--arch/sparc/math-emu/fabss.c6
-rw-r--r--arch/sparc/math-emu/fcmpd.c18
-rw-r--r--arch/sparc/math-emu/fcmped.c18
-rw-r--r--arch/sparc/math-emu/fcmpeq.c18
-rw-r--r--arch/sparc/math-emu/fcmpes.c18
-rw-r--r--arch/sparc/math-emu/fcmpq.c18
-rw-r--r--arch/sparc/math-emu/fcmps.c18
-rw-r--r--arch/sparc/math-emu/fdmulq.c16
-rw-r--r--arch/sparc/math-emu/fdtoq.c13
-rw-r--r--arch/sparc/math-emu/fdtos.c13
-rw-r--r--arch/sparc/math-emu/fmovs.c5
-rw-r--r--arch/sparc/math-emu/fnegs.c7
-rw-r--r--arch/sparc/math-emu/fqtod.c13
-rw-r--r--arch/sparc/math-emu/fqtos.c13
-rw-r--r--arch/sparc/math-emu/fsmuld.c16
-rw-r--r--arch/sparc/math-emu/fstod.c13
-rw-r--r--arch/sparc/math-emu/fstoq.c13
-rw-r--r--arch/sparc/math-emu/math.c416
-rw-r--r--arch/sparc/math-emu/sfp-machine.h363
-rw-r--r--arch/sparc/mm/Makefile16
-rw-r--r--arch/sparc/mm/btfixup.c334
-rw-r--r--arch/sparc/mm/fault.c4
-rw-r--r--arch/sparc/mm/hypersparc.S9
-rw-r--r--arch/sparc/mm/init.c114
-rw-r--r--arch/sparc/mm/io-unit.c152
-rw-r--r--arch/sparc/mm/iommu.c45
-rw-r--r--arch/sparc/mm/loadmmu.c136
-rw-r--r--arch/sparc/mm/nosrmmu.c50
-rw-r--r--arch/sparc/mm/nosun4c.c77
-rw-r--r--arch/sparc/mm/srmmu.c1623
-rw-r--r--arch/sparc/mm/sun4c.c629
-rw-r--r--arch/sparc/mm/turbosparc.S4
-rw-r--r--arch/sparc/mm/viking.S78
-rw-r--r--arch/sparc/prom/Makefile8
-rw-r--r--arch/sparc/prom/bootstr.c8
-rw-r--r--arch/sparc/prom/console.c26
-rw-r--r--arch/sparc/prom/devmap.c13
-rw-r--r--arch/sparc/prom/devops.c17
-rw-r--r--arch/sparc/prom/init.c16
-rw-r--r--arch/sparc/prom/memory.c22
-rw-r--r--arch/sparc/prom/misc.c21
-rw-r--r--arch/sparc/prom/mp.c21
-rw-r--r--arch/sparc/prom/ranges.c4
-rw-r--r--arch/sparc/prom/segment.c9
-rw-r--r--arch/sparc/prom/sun4prom.c161
-rw-r--r--arch/sparc/prom/tree.c30
-rw-r--r--arch/sparc/vmlinux.lds1
-rw-r--r--arch/sparc64/Makefile10
-rw-r--r--arch/sparc64/config.in25
-rw-r--r--arch/sparc64/defconfig58
-rw-r--r--arch/sparc64/kernel/Makefile58
-rw-r--r--arch/sparc64/kernel/binfmt_aout32.c10
-rw-r--r--arch/sparc64/kernel/central.c31
-rw-r--r--arch/sparc64/kernel/cpu.c6
-rw-r--r--arch/sparc64/kernel/devices.c5
-rw-r--r--arch/sparc64/kernel/dtlb_miss.S50
-rw-r--r--arch/sparc64/kernel/dtlb_prot.S24
-rw-r--r--arch/sparc64/kernel/ebus.c99
-rw-r--r--arch/sparc64/kernel/head.S19
-rw-r--r--arch/sparc64/kernel/init_task.c2
-rw-r--r--arch/sparc64/kernel/ioctl32.c166
-rw-r--r--arch/sparc64/kernel/irq.c392
-rw-r--r--arch/sparc64/kernel/itlb_miss.S36
-rw-r--r--arch/sparc64/kernel/process.c31
-rw-r--r--arch/sparc64/kernel/psycho.c1166
-rw-r--r--arch/sparc64/kernel/setup.c45
-rw-r--r--arch/sparc64/kernel/signal32.c3
-rw-r--r--arch/sparc64/kernel/smp.c196
-rw-r--r--arch/sparc64/kernel/sparc64_ksyms.c17
-rw-r--r--arch/sparc64/kernel/sunos_ioctl32.c10
-rw-r--r--arch/sparc64/kernel/sys32.S13
-rw-r--r--arch/sparc64/kernel/sys_sparc.c49
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c185
-rw-r--r--arch/sparc64/kernel/sys_sunos32.c130
-rw-r--r--arch/sparc64/kernel/systbls.S18
-rw-r--r--arch/sparc64/kernel/time.c133
-rw-r--r--arch/sparc64/kernel/trampoline.S8
-rw-r--r--arch/sparc64/kernel/traps.c103
-rw-r--r--arch/sparc64/kernel/ttable.S6
-rw-r--r--arch/sparc64/lib/VIScsumcopy.S38
-rw-r--r--arch/sparc64/math-emu/Makefile10
-rw-r--r--arch/sparc64/math-emu/double.h2
-rw-r--r--arch/sparc64/math-emu/fabsq.c13
-rw-r--r--arch/sparc64/math-emu/fcmpeq.c7
-rw-r--r--arch/sparc64/math-emu/fcmpq.c7
-rw-r--r--arch/sparc64/math-emu/fnegq.c11
-rw-r--r--arch/sparc64/math-emu/math.c27
-rw-r--r--arch/sparc64/math-emu/op-2.h33
-rw-r--r--arch/sparc64/math-emu/op-4.h235
-rw-r--r--arch/sparc64/math-emu/op-common.h52
-rw-r--r--arch/sparc64/math-emu/quad.h73
-rw-r--r--arch/sparc64/math-emu/single.h2
-rw-r--r--arch/sparc64/mm/fault.c4
-rw-r--r--arch/sparc64/mm/init.c200
-rw-r--r--arch/sparc64/mm/modutil.c32
-rw-r--r--arch/sparc64/prom/bootstr.c10
-rw-r--r--arch/sparc64/prom/init.c52
-rw-r--r--arch/sparc64/prom/ranges.c48
-rw-r--r--arch/sparc64/solaris/Makefile2
-rw-r--r--arch/sparc64/solaris/conv.h11
-rw-r--r--arch/sparc64/solaris/entry64.S54
-rw-r--r--arch/sparc64/solaris/fs.c39
-rw-r--r--arch/sparc64/solaris/ioctl.c327
-rw-r--r--arch/sparc64/solaris/misc.c36
-rw-r--r--arch/sparc64/solaris/socksys.c89
-rw-r--r--arch/sparc64/solaris/socksys.h208
-rw-r--r--arch/sparc64/solaris/systbl.S10
-rw-r--r--arch/sparc64/solaris/timod.c979
-rw-r--r--arch/sparc64/vmlinux.lds3
334 files changed, 30492 insertions, 7696 deletions
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile
index 9cd1ab3e6..ff6e422df 100644
--- a/arch/alpha/Makefile
+++ b/arch/alpha/Makefile
@@ -10,8 +10,38 @@
NM := nm -B
+#LINKFLAGS = -static -T arch/alpha/vmlinux.lds
+#CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8
+
+ifdef CONFIG_CROSSCOMPILE
+# enable this for linking under OSF/1:
+LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N
+else
+ elf=$(shell if $(LD) --help | grep elf64alpha >/dev/null; then echo yes; fi)
+ ifeq ($(elf),yes)
+# LINKFLAGS = -static -Ttext 0xfffffc0000310000 -N
LINKFLAGS = -static -T arch/alpha/vmlinux.lds
-CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8
+ else
+ LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N
+ endif
+# GNU gcc/cc1/as can use pipes instead of temporary files
+CFLAGS := $(CFLAGS) -pipe
+endif
+
+CFLAGS := $(CFLAGS) -mno-fp-regs -ffixed-8 -Wno-uninitialized
+
+# determine if we can use the BWX instructions with GAS
+$(shell rm -f ./GAS_VER)
+$(shell $(AS) --version >& ./GAS_VER)
+OLD_GAS := $(shell if cat ./GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi)
+$(shell rm -f ./GAS_VER)
+
+ifneq ($(OLD_GAS),yes)
+# if PYXIS, then enable use of BWIO space
+ ifeq ($(CONFIG_ALPHA_PYXIS),y)
+ CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED
+ endif
+endif
HEAD := arch/alpha/kernel/head.o
@@ -23,7 +53,7 @@ ifeq ($(CONFIG_MATHEMU),y)
CORE_FILES := $(CORE_FILES) arch/alpha/math-emu/math-emu.o
endif
-LIBS := arch/alpha/lib/lib.a $(LIBS) arch/alpha/lib/lib.a
+LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
diff --git a/arch/alpha/boot/bootp.c b/arch/alpha/boot/bootp.c
index 65829793e..a1dc6d818 100644
--- a/arch/alpha/boot/bootp.c
+++ b/arch/alpha/boot/bootp.c
@@ -16,6 +16,7 @@
#include <asm/console.h>
#include <asm/hwrpb.h>
#include <asm/pgtable.h>
+#include <asm/io.h>
#include <stdarg.h>
@@ -156,7 +157,8 @@ void pal_init(void)
printk("Ok (rev %lx)\n", rev);
/* remove the old virtual page-table mapping */
L1[1] = 0;
- flush_tlb_all();
+
+ tbia(); /* do it directly in case we are SMP */
}
static inline long load(unsigned long dst,
@@ -189,30 +191,59 @@ extern char _end;
void start_kernel(void)
{
- long i;
- int nbytes;
- char envval[256];
+ static long i;
+ static int nbytes;
+ /*
+ * note that this crufty stuff with static and envval and envbuf
+ * is because:
+ *
+ * 1. frequently, the stack is is short, and we don't want to overrun;
+ * 2. frequently the stack is where we are going to copy the kernel to;
+ * 3. a certain SRM console required the GET_ENV output to stack.
+ */
+ static char envval[256];
+ char envbuf[256];
printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n");
if (hwrpb.pagesize != 8192) {
- printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10);
+ printk("Expected 8kB pages, got %ldkB\n",
+ hwrpb.pagesize >> 10);
return;
}
pal_init();
nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS,
- envval, sizeof(envval));
- if (nbytes < 0) {
+ envbuf, sizeof(envbuf));
+ if (nbytes < 0 || nbytes >= sizeof(envbuf)) {
nbytes = 0;
}
- envval[nbytes] = '\0';
- strcpy((char*)ZERO_PAGE, envval);
-
- printk("Loading the kernel ...\n");
+ envbuf[nbytes] = '\0';
+ memcpy(envval, envbuf, nbytes+1);
+ printk("Loading the kernel...'%s'\n", envval);
/* NOTE: *no* callbacks or printouts from here on out!!! */
+#if 1
+ /*
+ * this is a hack, as some consoles seem to get virtual 20000000
+ * (ie where the SRM console puts the kernel bootp image) memory
+ * overlapping physical 310000 memory, which causes real problems
+ * when attempting to copy the former to the latter... :-(
+ *
+ * so, we first move the kernel virtual-to-physical way above where
+ * we physically want the kernel to end up, then copy it from there
+ * to its final resting place... ;-}
+ *
+ * sigh...
+ */
+
+ i = load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE);
+ i = load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE);
+#else
i = load(START_ADDR, KERNEL_ORIGIN, KERNEL_SIZE);
+#endif
+
+ strcpy((char*)ZERO_PAGE, envval);
runkernel();
diff --git a/arch/alpha/config.in b/arch/alpha/config.in
index 1fefe0c62..fc2546218 100644
--- a/arch/alpha/config.in
+++ b/arch/alpha/config.in
@@ -6,10 +6,11 @@ mainmenu_name "Kernel configuration of Linux for Alpha machines"
# clear all implied options (don't want default values for those):
unset CONFIG_CROSSCOMPILE CONFIG_NATIVE
-unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5
+unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 CONFIG_ALPHA_EV6
unset CONFIG_PCI CONFIG_ALPHA_EISA
unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA
unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS
+unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA
unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION
mainmenu_option next_comment
@@ -48,13 +49,16 @@ choice 'Alpha system type' \
PC164 CONFIG_ALPHA_PC164 \
LX164 CONFIG_ALPHA_LX164 \
SX164 CONFIG_ALPHA_SX164 \
+ DP264 CONFIG_ALPHA_DP264 \
Jensen CONFIG_ALPHA_JENSEN \
Noname CONFIG_ALPHA_NONAME \
+ Takara CONFIG_ALPHA_TAKARA \
Mikasa CONFIG_ALPHA_MIKASA \
Noritake CONFIG_ALPHA_NORITAKE \
Alcor CONFIG_ALPHA_ALCOR \
Miata CONFIG_ALPHA_MIATA \
Sable CONFIG_ALPHA_SABLE \
+ Rawhide CONFIG_ALPHA_RAWHIDE \
AlphaBook1 CONFIG_ALPHA_BOOK1 \
Ruffian CONFIG_ALPHA_RUFFIAN \
Platform2000 CONFIG_ALPHA_P2K" Cabriolet
@@ -78,7 +82,8 @@ then
define_bool CONFIG_ALPHA_APECS y
fi
if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
- -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" ]
+ -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" \
+ -o "$CONFIG_ALPHA_TAKARA" = "y" ]
then
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_EV5 y
@@ -86,9 +91,7 @@ then
fi
if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
then
- choice 'CPU daughtercard' \
- "Pinnacle CONFIG_ALPHA_PINNACLE \
- Primo CONFIG_ALPHA_PRIMO" Primo
+ bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO
if [ "$CONFIG_ALPHA_PRIMO" = "y" ]
then
define_bool CONFIG_ALPHA_EV5 y
@@ -102,7 +105,13 @@ fi
if [ "$CONFIG_ALPHA_SABLE" = "y" ]
then
define_bool CONFIG_PCI y
+ bool 'EV5 CPU(s) (model 5/xxx)?' CONFIG_ALPHA_GAMMA
+ if [ "$CONFIG_ALPHA_GAMMA" = "y" ]
+ then
+ define_bool CONFIG_ALPHA_EV5 y
+ else
define_bool CONFIG_ALPHA_EV4 y
+ fi
define_bool CONFIG_ALPHA_T2 y
fi
if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \
@@ -112,6 +121,18 @@ then
define_bool CONFIG_ALPHA_EV5 y
define_bool CONFIG_ALPHA_PYXIS y
fi
+if [ "$CONFIG_ALPHA_DP264" = "y" ]
+then
+ define_bool CONFIG_PCI y
+ define_bool CONFIG_ALPHA_EV6 y
+ define_bool CONFIG_ALPHA_TSUNAMI y
+fi
+if [ "$CONFIG_ALPHA_RAWHIDE" = "y" ]
+then
+ define_bool CONFIG_PCI y
+ define_bool CONFIG_ALPHA_EV5 y
+ define_bool CONFIG_ALPHA_MCPCIA y
+fi
if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
then
define_bool CONFIG_ALPHA_EV4 y
@@ -127,12 +148,19 @@ if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \
-o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \
-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \
-o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
- -o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" ]
+ -o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" \
+ -o "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_RAWHIDE" = "y" ]
then
- bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM
+ bool 'Use SRM as bootloader' CONFIG_ALPHA_SRM
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_ALPHA_SRM" = "y" ]; then
+ bool ' Use SRM PCI setup' CONFIG_ALPHA_SRM_SETUP
+ fi
+ fi
fi
if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \
- -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
+ -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" \
+ -o "$CONFIG_ALPHA_RAWHIDE" = "y" ]
then
define_bool CONFIG_ALPHA_EISA y
fi
@@ -141,9 +169,15 @@ then
define_bool CONFIG_ALPHA_AVANTI y
fi
+#bool 'Echo console messages on /dev/ttyS0 (COM1)' CONFIG_SERIAL_ECHO
+
if [ "$CONFIG_PCI" = "y" ]; then
bool 'TGA Console Support' CONFIG_TGA_CONSOLE
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+# if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then
+# bool 'VGA Console Support' CONFIG_VGA_CONSOLE
+# fi
+ bool 'PCI quirks' CONFIG_PCI_QUIRKS
+ if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
fi
bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
@@ -216,17 +250,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
fi
endmenu
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
source fs/nls/Config.in
diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig
index 1bdecf444..7d78e4b97 100644
--- a/arch/alpha/defconfig
+++ b/arch/alpha/defconfig
@@ -27,14 +27,20 @@ CONFIG_NATIVE=y
# CONFIG_ALPHA_EB64P is not set
# CONFIG_ALPHA_EB164 is not set
# CONFIG_ALPHA_PC164 is not set
+# CONFIG_ALPHA_LX164 is not set
+# CONFIG_ALPHA_SX164 is not set
+# CONFIG_ALPHA_DP264 is not set
# CONFIG_ALPHA_JENSEN is not set
# CONFIG_ALPHA_NONAME is not set
+# CONFIG_ALPHA_TAKARA is not set
# CONFIG_ALPHA_MIKASA is not set
# CONFIG_ALPHA_NORITAKE is not set
CONFIG_ALPHA_ALCOR=y
# CONFIG_ALPHA_MIATA is not set
# CONFIG_ALPHA_SABLE is not set
+# CONFIG_ALPHA_RAWHIDE is not set
# CONFIG_ALPHA_BOOK1 is not set
+# CONFIG_ALPHA_RUFFIAN is not set
# CONFIG_ALPHA_P2K is not set
CONFIG_PCI=y
CONFIG_ALPHA_EV5=y
@@ -214,7 +220,6 @@ CONFIG_DE4X5=y
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
-CONFIG_CDROM=y
#
# Filesystems
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index ba0aee556..8d09ea8c4 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -8,9 +8,9 @@
# Note 2! The CFLAGS definitions are now in the main makefile...
.S.s:
- $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
+ $(CPP) -D__ASSEMBLY__ $(AFLAGS) -traditional $< -o $*.s
.S.o:
- $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+ $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
all: kernel.o head.o
@@ -35,19 +35,29 @@ endif
ifdef CONFIG_ALPHA_T2
O_OBJS += t2.o
endif
+ifdef CONFIG_ALPHA_TSUNAMI
+O_OBJS += tsunami.o
+endif
+ifdef CONFIG_ALPHA_MCPCIA
+O_OBJS += mcpcia.o
+endif
+
ifneq ($(CONFIG_ALPHA_PC164)$(CONFIG_ALPHA_LX164),nn)
O_OBJS += smc37c93x.o
endif
-ifdef CONFIG_ALPHA_SX164
+ifneq ($(CONFIG_ALPHA_SX164)$(CONFIG_ALPHA_MIATA)$(CONFIG_ALPHA_DP264),nnn)
O_OBJS += smc37c669.o
endif
+ifdef SMP
+O_OBJS += smp.o
+endif
all: kernel.o head.o
head.o: head.s
head.s: head.S $(TOPDIR)/include/asm-alpha/system.h
- $(CPP) -traditional -o $*.s $<
+ $(CPP) -traditional $(AFLAGS) -o $*.s $<
include $(TOPDIR)/Rules.make
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c
index 132175541..c220ccdc6 100644
--- a/arch/alpha/kernel/alpha_ksyms.c
+++ b/arch/alpha/kernel/alpha_ksyms.c
@@ -27,7 +27,6 @@
#define __KERNEL_SYSCALLS__
#include <asm/unistd.h>
-extern void bcopy (const char *src, char *dst, int len);
extern struct hwrpb_struct *hwrpb;
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
@@ -134,7 +133,6 @@ EXPORT_SYMBOL(__strlen_user);
* interface isn't gonna change any time soon now, so it's OK
* to leave it out of version control.
*/
-# undef bcopy
# undef memcpy
# undef memset
EXPORT_SYMBOL_NOVERS(__divl);
@@ -147,7 +145,3 @@ EXPORT_SYMBOL_NOVERS(__remq);
EXPORT_SYMBOL_NOVERS(__remqu);
EXPORT_SYMBOL_NOVERS(memcpy);
EXPORT_SYMBOL_NOVERS(memset);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff --git a/arch/alpha/kernel/apecs.c b/arch/alpha/kernel/apecs.c
index d5e7fa2a7..2bbdf0062 100644
--- a/arch/alpha/kernel/apecs.c
+++ b/arch/alpha/kernel/apecs.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <asm/system.h>
@@ -18,13 +17,14 @@
#include <asm/hwrpb.h>
#include <asm/ptrace.h>
-/* NOTE: Herein are back-to-back mb insns. They are magic.
- A plausible explanation is that the i/o controler does not properly
- handle the system transaction. Another involves timing. Ho hum. */
+/*
+ * NOTE: Herein lie back-to-back mb instructions. They are magic.
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction. Another involves timing. Ho hum.
+ */
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
-extern int alpha_sys_type;
/*
* BIOS32-style PCI interface:
@@ -36,13 +36,16 @@ extern int alpha_sys_type;
# define DBG(args)
#endif
-#define vulp volatile unsigned long *
#define vuip volatile unsigned int *
static volatile unsigned int apecs_mcheck_expected = 0;
static volatile unsigned int apecs_mcheck_taken = 0;
-static unsigned long apecs_jd, apecs_jd1, apecs_jd2;
+static unsigned int apecs_jd, apecs_jd1, apecs_jd2;
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int APECS_DMA_WIN_BASE = APECS_DMA_WIN_BASE_DEFAULT;
+unsigned int APECS_DMA_WIN_SIZE = APECS_DMA_WIN_SIZE_DEFAULT;
+#endif /* SRM_SETUP */
/*
* Given a bus, device, and function number, compute resulting
@@ -194,7 +197,7 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1)
}
/* reset error status: */
- *(vulp)APECS_IOC_DCSR = stat0;
+ *(vuip)APECS_IOC_DCSR = stat0;
mb();
wrmces(0x7); /* reset machine check */
value = 0xffffffff;
@@ -269,7 +272,7 @@ static void conf_write(unsigned long addr, unsigned int value, unsigned char typ
}
/* reset error status: */
- *(vulp)APECS_IOC_DCSR = stat0;
+ *(vuip)APECS_IOC_DCSR = stat0;
mb();
wrmces(0x7); /* reset machine check */
}
@@ -424,6 +427,38 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end)
*(vuip)APECS_IOC_TB2R = 0;
#else /* CONFIG_ALPHA_XL */
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 1 for enabled and mapped to 0 */
+ if ((*(vuip)APECS_IOC_PB1R & (1U<<19)) && (*(vuip)APECS_IOC_TB1R == 0))
+ {
+ APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB1R & 0xfff00000U;
+ APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM1R & 0xfff00000U;
+ APECS_DMA_WIN_SIZE += 0x00100000U;
+#if 0
+ printk("apecs_init: using Window 1 settings\n");
+ printk("apecs_init: PB1R 0x%x PM1R 0x%x TB1R 0x%x\n",
+ *(vuip)APECS_IOC_PB1R,
+ *(vuip)APECS_IOC_PM1R,
+ *(vuip)APECS_IOC_TB1R);
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if ((*(vuip)APECS_IOC_PB2R & (1U<<19)) && (*(vuip)APECS_IOC_TB2R == 0))
+ {
+ APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB2R & 0xfff00000U;
+ APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM2R & 0xfff00000U;
+ APECS_DMA_WIN_SIZE += 0x00100000U;
+#if 0
+ printk("apecs_init: using Window 2 settings\n");
+ printk("apecs_init: PB2R 0x%x PM2R 0x%x TB2R 0x%x\n",
+ *(vuip)APECS_IOC_PB2R,
+ *(vuip)APECS_IOC_PM2R,
+ *(vuip)APECS_IOC_TB2R);
+#endif
+ }
+ else /* we must use our defaults... */
+#endif /* SRM_SETUP */
+ {
/*
* Set up the PCI->physical memory translation windows.
* For now, window 2 is disabled. In the future, we may
@@ -435,9 +470,11 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end)
*(vuip)APECS_IOC_PB1R = 1U<<19 | (APECS_DMA_WIN_BASE & 0xfff00000U);
*(vuip)APECS_IOC_PM1R = (APECS_DMA_WIN_SIZE - 1) & 0xfff00000U;
*(vuip)APECS_IOC_TB1R = 0;
+ }
#endif /* CONFIG_ALPHA_XL */
#ifdef CONFIG_ALPHA_CABRIOLET
+#ifdef NO_LONGER_NEEDED_I_HOPE
/*
* JAE: HACK!!! for now, hardwire if configured...
* davidm: Older miniloader versions don't set the clock frequency
@@ -461,6 +498,7 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end)
sum += *l;
hwrpb->chksum = sum;
}
+#endif /* NO_LONGER_NEEDED_I_HOPE */
#endif /* CONFIG_ALPHA_CABRIOLET */
/*
@@ -483,15 +521,15 @@ unsigned long apecs_init(unsigned long mem_start, unsigned long mem_end)
int apecs_pci_clr_err(void)
{
- apecs_jd = *(vulp)APECS_IOC_DCSR;
+ apecs_jd = *(vuip)APECS_IOC_DCSR;
if (apecs_jd & 0xffe0L) {
- apecs_jd1 = *(vulp)APECS_IOC_SEAR;
- *(vulp)APECS_IOC_DCSR = apecs_jd | 0xffe1L;
- apecs_jd = *(vulp)APECS_IOC_DCSR;
+ apecs_jd1 = *(vuip)APECS_IOC_SEAR;
+ *(vuip)APECS_IOC_DCSR = apecs_jd | 0xffe1L;
+ apecs_jd = *(vuip)APECS_IOC_DCSR;
mb();
}
- *(vulp)APECS_IOC_TBIA = APECS_IOC_TBIA;
- apecs_jd2 = *(vulp)APECS_IOC_TBIA;
+ *(vuip)APECS_IOC_TBIA = (unsigned int)APECS_IOC_TBIA;
+ apecs_jd2 = *(vuip)APECS_IOC_TBIA;
mb();
return 0;
}
diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c
index af8971834..46880f11e 100644
--- a/arch/alpha/kernel/bios32.c
+++ b/arch/alpha/kernel/bios32.c
@@ -25,6 +25,7 @@
*/
#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/tasks.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
@@ -55,7 +56,6 @@ asmlinkage int sys_pciconfig_write()
#else /* CONFIG_PCI */
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/malloc.h>
#include <linux/mm.h>
@@ -63,6 +63,8 @@ asmlinkage int sys_pciconfig_write()
#include <asm/hwrpb.h>
#include <asm/io.h>
#include <asm/uaccess.h>
+#include <asm/segment.h>
+#include <asm/system.h>
#define KB 1024
@@ -70,7 +72,9 @@ asmlinkage int sys_pciconfig_write()
#define GB (1024*MB)
#define MAJOR_REV 0
-#define MINOR_REV 3
+
+/* minor revision 4, add multi-PCI handling */
+#define MINOR_REV 4
/*
* Align VAL to ALIGN, which must be a power of two.
@@ -78,7 +82,20 @@ asmlinkage int sys_pciconfig_write()
#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
+#if defined(CONFIG_ALPHA_MCPCIA) || defined(CONFIG_ALPHA_TSUNAMI)
+/* multiple PCI bus machines */
+/* make handle from bus number */
+extern struct linux_hose_info *bus2hose[256];
+#define HANDLE(b) (((unsigned long)(bus2hose[(b)]->pci_hose_index)&3)<<32)
+#define DEV_IS_ON_PRIMARY(dev) \
+ (bus2hose[(dev)->bus->number]->pci_first_busno == (dev)->bus->number)
+#else /* MCPCIA || TSUNAMI */
+#define HANDLE(b) (0)
+#define DEV_IS_ON_PRIMARY(dev) ((dev)->bus->number == 0)
+#endif /* MCPCIA || TSUNAMI */
/*
+ * PCI_MODIFY
+ *
* Temporary internal macro. If this 0, then do not write to any of
* the PCI registers, merely read them (i.e., use configuration as
* determined by SRM). The SRM seem do be doing a less than perfect
@@ -95,7 +112,18 @@ asmlinkage int sys_pciconfig_write()
* the graphics card---there have been some rumor that the #9 BIOS
* incorrectly resets that address to 0...).
*/
+#ifdef CONFIG_ALPHA_SRM_SETUP
+#define PCI_MODIFY 0
+static struct pci_dev *irq_dev_to_reset[16];
+static unsigned char irq_to_reset[16];
+static int irq_reset_count = 0;
+static struct pci_dev *io_dev_to_reset[16];
+static unsigned char io_reg_to_reset[16];
+static unsigned int io_to_reset[16];
+static int io_reset_count = 0;
+#else /* SRM_SETUP */
#define PCI_MODIFY 1
+#endif /* SRM_SETUP */
extern struct hwrpb_struct *hwrpb;
@@ -103,9 +131,7 @@ extern struct hwrpb_struct *hwrpb;
#if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
extern int SMC93x_Init(void);
#endif
-#ifdef CONFIG_ALPHA_SX164
extern int SMC669_Init(void);
-#endif
#ifdef CONFIG_ALPHA_MIATA
static int es1888_init(void);
#endif
@@ -115,7 +141,7 @@ static int es1888_init(void);
/*
* NOTE: we can't just blindly use 64K for machines with EISA busses; they
* may also have PCI-PCI bridges present, and then we'd configure the bridge
- * incorrectly
+ * incorrectly.
*
* Also, we start at 0x8000 or 0x9000, in hopes to get all devices'
* IO space areas allocated *before* 0xC000; this is because certain
@@ -123,12 +149,17 @@ static int es1888_init(void);
* accesses to probe the bus. If a device's registers appear at 0xC000,
* it may see an INx/OUTx at that address during BIOS emulation of the
* VGA BIOS, and some cards, notably Adaptec 2940UW, take mortal offense.
+ *
+ * Note that we may need this stuff for SRM_SETUP also, since certain
+ * SRM consoles screw up and allocate I/O space addresses > 64K behind
+ * PCI-to_PCI bridges, which can't pass I/O addresses larger than 64K, AFAIK.
*/
#if defined(CONFIG_ALPHA_EISA)
-static unsigned int io_base = 0x9000; /* start above 8th slot */
+#define DEFAULT_IO_BASE 0x9000 /* start above 8th slot */
#else
-static unsigned int io_base = 0x8000;
+#define DEFAULT_IO_BASE 0x8000 /* start at 8th slot */
#endif
+static unsigned int io_base;
#if defined(CONFIG_ALPHA_XL)
/*
@@ -142,7 +173,7 @@ static unsigned int io_base = 0x8000;
* We accept the risk that a broken Myrinet card will be put into a true XL
* and thus can more easily run into the problem described below.
*/
-static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */
+#define DEFAULT_MEM_BASE (16*MB + 2*MB) /* 16M to 64M-1 is avail */
#elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS)
/*
@@ -154,7 +185,7 @@ static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */
* However, APECS and LCA have only 34 bits for physical addresses, thus
* limiting PCI bus memory addresses for SPARSE access to be less than 128Mb.
*/
-static unsigned int mem_base = 64*MB + 2*MB;
+#define DEFAULT_MEM_BASE (64*MB + 2*MB)
#else
/*
@@ -166,9 +197,10 @@ static unsigned int mem_base = 64*MB + 2*MB;
* Because CIA and PYXIS and T2 have more bits for physical addresses,
* they support an expanded range of SPARSE memory addresses.
*/
-static unsigned int mem_base = 128*MB + 16*MB;
+#define DEFAULT_MEM_BASE (128*MB + 16*MB)
#endif
+static unsigned int mem_base;
/*
* Disable PCI device DEV so that it does not respond to I/O or memory
@@ -179,7 +211,6 @@ static void disable_dev(struct pci_dev *dev)
struct pci_bus *bus;
unsigned short cmd;
-#ifdef CONFIG_ALPHA_EISA
/*
* HACK: the PCI-to-EISA bridge does not seem to identify
* itself as a bridge... :-(
@@ -189,15 +220,17 @@ static void disable_dev(struct pci_dev *dev)
DBG_DEVS(("disable_dev: ignoring PCEB...\n"));
return;
}
-#endif
-#ifdef CONFIG_ALPHA_SX164
+
+ /*
+ * we don't have code that will init the CYPRESS bridge correctly
+ * so we do the next best thing, and depend on the previous
+ * console code to do the right thing, and ignore it here... :-\
+ */
if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
- /* FIXME: We want a symbolic device name here. */
- dev->device == 0xc693) {
+ dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
DBG_DEVS(("disable_dev: ignoring CYPRESS bridge...\n"));
return;
}
-#endif
bus = dev->bus;
pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
@@ -211,16 +244,16 @@ static void disable_dev(struct pci_dev *dev)
/*
* Layout memory and I/O for a device:
*/
-#define MAX(val1, val2) ((val1) > (val2) ? val1 : val2)
+#define MAX(val1, val2) ((val1) > (val2) ? (val1) : (val2))
static void layout_dev(struct pci_dev *dev)
{
struct pci_bus *bus;
unsigned short cmd;
- unsigned int base, mask, size, reg;
+ unsigned int base, mask, size, off, idx;
unsigned int alignto;
+ unsigned long handle;
-#ifdef CONFIG_ALPHA_EISA
/*
* HACK: the PCI-to-EISA bridge does not seem to identify
* itself as a bridge... :-(
@@ -230,32 +263,40 @@ static void layout_dev(struct pci_dev *dev)
DBG_DEVS(("layout_dev: ignoring PCEB...\n"));
return;
}
-#endif
-#ifdef CONFIG_ALPHA_SX164
+
+ /*
+ * we don't have code that will init the CYPRESS bridge correctly
+ * so we do the next best thing, and depend on the previous
+ * console code to do the right thing, and ignore it here... :-\
+ */
if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
- dev->device == 0xc693) {
+ dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
DBG_DEVS(("layout_dev: ignoring CYPRESS bridge...\n"));
return;
}
-#endif
bus = dev->bus;
pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
- for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ for (idx = 0; idx <= 5; idx++) {
+ off = PCI_BASE_ADDRESS_0 + 4*idx;
/*
* Figure out how much space and of what type this
* device wants.
*/
- pcibios_write_config_dword(bus->number, dev->devfn, reg,
+ pcibios_write_config_dword(bus->number, dev->devfn, off,
0xffffffff);
- pcibios_read_config_dword(bus->number, dev->devfn, reg, &base);
+ pcibios_read_config_dword(bus->number, dev->devfn, off, &base);
if (!base) {
/* this base-address register is unused */
- dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2] = 0;
+ dev->base_address[idx] = 0;
continue;
}
+ DBG_DEVS(("layout_dev: slot %d fn %d off 0x%x base 0x%x\n",
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ off, base));
+
/*
* We've read the base address register back after
* writing all ones and so now we must decode it.
@@ -281,11 +322,13 @@ static void layout_dev(struct pci_dev *dev)
base = ALIGN(io_base, alignto);
io_base = base + size;
pcibios_write_config_dword(bus->number, dev->devfn,
- reg, base | 0x1);
- dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2]
- = base | 0x1;
- DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n",
- dev->device, base, size));
+ off, base | 0x1);
+
+ handle = HANDLE(bus->number) | base | 1;
+ dev->base_address[idx] = handle;
+
+ DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%lx (0x%x)\n",
+ dev->device, handle, size));
} else {
unsigned int type;
/*
@@ -306,7 +349,7 @@ static void layout_dev(struct pci_dev *dev)
"slot %d, function %d: \n",
PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
- reg += 4; /* skip extra 4 bytes */
+ idx++; /* skip extra 4 bytes */
continue;
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
@@ -365,10 +408,11 @@ static void layout_dev(struct pci_dev *dev)
}
mem_base = base + size;
pcibios_write_config_dword(bus->number, dev->devfn,
- reg, base);
- dev->base_address[(reg-PCI_BASE_ADDRESS_0)>>2] = base;
- DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n",
- dev->device, base, size));
+ off, base);
+ handle = HANDLE(bus->number) | base;
+ dev->base_address[idx] = handle;
+ DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%lx (0x%x)\n",
+ dev->device, handle, size));
}
}
@@ -397,16 +441,17 @@ static void layout_dev(struct pci_dev *dev)
}
-static void layout_bus(struct pci_bus *bus)
+static int layout_bus(struct pci_bus *bus)
{
unsigned int l, tio, bio, tmem, bmem;
struct pci_bus *child;
struct pci_dev *dev;
+ int found_vga = 0;
DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
if (!bus->devices && !bus->children)
- return;
+ return 0;
/*
* Align the current bases on appropriate boundaries (4K for
@@ -424,6 +469,8 @@ static void layout_bus(struct pci_bus *bus)
* devices. They'll be re-enabled only once all address
* decoders are programmed consistently.
*/
+ DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number));
+
for (dev = bus->devices; dev; dev = dev->sibling) {
if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
(dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
@@ -441,6 +488,8 @@ static void layout_bus(struct pci_bus *bus)
(dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
layout_dev(dev);
}
+ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+ found_vga = 1;
}
/*
* Recursively allocate space for all of the sub-buses:
@@ -448,7 +497,7 @@ static void layout_bus(struct pci_bus *bus)
DBG_DEVS(("layout_bus: starting bus %d children\n", bus->number));
for (child = bus->children; child; child = child->next) {
- layout_bus(child);
+ found_vga += layout_bus(child);
}
/*
* Align the current bases on 4K and 1MB boundaries:
@@ -458,6 +507,8 @@ static void layout_bus(struct pci_bus *bus)
if (bus->self) {
struct pci_dev *bridge = bus->self;
+
+ DBG_DEVS(("layout_bus: config bus %d bridge\n", bus->number));
/*
* Set up the top and bottom of the PCI I/O segment
* for this bus.
@@ -481,10 +532,13 @@ static void layout_bus(struct pci_bus *bus)
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x24, 0x0000ffff);
/*
- * Tell bridge that there is an ISA bus in the system:
+ * Tell bridge that there is an ISA bus in the system,
+ * and (possibly) a VGA as well.
*/
+ l = 0x00040000; /* ISA present */
+ if (found_vga) l |= 0x00080000; /* VGA present */
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
- 0x3c, 0x00040000);
+ 0x3c, l);
/*
* Clear status bits, enable I/O (for downstream I/O),
* turn on master enable (for upstream I/O), turn on
@@ -494,75 +548,26 @@ static void layout_bus(struct pci_bus *bus)
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x4, 0xffff0007);
}
+ DBG_DEVS(("layout_bus: bus %d finished\n", bus->number));
+ return found_vga;
}
#endif /* !PCI_MODIFY */
-/*
- * Given the vendor and device ids, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus,
- unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->vendor == vendor && dev->device == device_id) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-
-/*
- * Given the class, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->class == class_code) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-
int pcibios_present(void)
{
return 1;
}
-unsigned long pcibios_init(unsigned long mem_start,
- unsigned long mem_end)
+void __init
+pcibios_init(void)
{
printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
-
#if !PCI_MODIFY
printk("...NOT modifying existing (SRM) PCI configuration\n");
#endif
- return mem_start;
}
/*
@@ -633,6 +638,76 @@ bridge_swizzle(unsigned char pin, unsigned int slot)
return (((pin-1) + slot) % 4) + 1;
}
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* look for mis-configured devices' I/O space addresses behind bridges */
+static void check_behind_io(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->bus;
+ unsigned int reg, orig_base, new_base, found_one = 0;
+
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ /* read the current setting, check for I/O space and >= 64K */
+ pcibios_read_config_dword(bus->number, dev->devfn, reg, &orig_base);
+ if (!orig_base || !(orig_base & PCI_BASE_ADDRESS_SPACE_IO))
+ continue; /* unused or non-IO */
+ if (orig_base < 64*1024) {
+#if 1
+printk("check_behind_io: ALREADY OK! bus %d slot %d base 0x%x\n",
+ bus->number, PCI_SLOT(dev->devfn), orig_base);
+#endif
+ if (orig_base & ~1)
+ continue; /* OK! */
+ orig_base = 0x12001; /* HACK! FIXME!! */
+ }
+
+ /* HACK ALERT! for now, just subtract 32K from the
+ original address, which should give us addresses
+ in the range 0x8000 and up */
+ new_base = orig_base - 0x8000;
+#if 1
+printk("check_behind_io: ALERT! bus %d slot %d old 0x%x new 0x%x\n",
+ bus->number, PCI_SLOT(dev->devfn), orig_base, new_base);
+#endif
+ pcibios_write_config_dword(bus->number, dev->devfn,
+ reg, new_base);
+
+ io_dev_to_reset[io_reset_count] = dev;
+ io_reg_to_reset[io_reset_count] = reg;
+ io_to_reset[io_reset_count] = orig_base;
+ io_reset_count++;
+ found_one++;
+ } /* end for-loop */
+
+ /* if any were modified, gotta hack the bridge IO limits too... */
+ if (found_one) {
+ if (bus->self) {
+ struct pci_dev *bridge = bus->self;
+ unsigned int l;
+ /*
+ * Set up the top and bottom of the PCI I/O segment
+ * for this bus.
+ */
+ pcibios_read_config_dword(bridge->bus->number,
+ bridge->devfn, 0x1c, &l);
+#if 1
+printk("check_behind_io: ALERT! bus %d slot %d oldLIM 0x%x\n",
+ bus->number, PCI_SLOT(bridge->devfn), l);
+#endif
+ l = (l & 0xffff0000U) | 0xf080U; /* give it ALL */
+ pcibios_write_config_dword(bridge->bus->number,
+ bridge->devfn, 0x1c, l);
+ pcibios_write_config_dword(bridge->bus->number,
+ bridge->devfn,
+ 0x3c, 0x00040000);
+ pcibios_write_config_dword(bridge->bus->number,
+ bridge->devfn,
+ 0x4, 0xffff0007);
+ } else
+ printk("check_behind_io: WARNING! bus->self NULL\n");
+ }
+}
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+
/*
* Most evaluation boards share most of the fixup code, which is isolated
* here. This function is declared "inline" as only one platform will ever
@@ -644,7 +719,7 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot],
long ide_base)
{
- struct pci_dev *dev;
+ struct pci_dev *dev, *curr;
unsigned char pin;
unsigned char slot;
@@ -652,12 +727,18 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
* Go through all devices, fixing up irqs as we see fit:
*/
for (dev = pci_devices; dev; dev = dev->next) {
- if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE
- /* PCEB (PCI to EISA bridge) does not identify
- itself as a bridge... :-P */
- && !(dev->vendor == PCI_VENDOR_ID_INTEL &&
- dev->device == PCI_DEVICE_ID_INTEL_82375))
- || dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) {
+ if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE ||
+ dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) {
+ /*
+ * HACK: the PCI-to-EISA bridge appears not to identify
+ * itself as a bridge... :-(
+ */
+ if (dev->vendor == PCI_VENDOR_ID_INTEL &&
+ dev->device == PCI_DEVICE_ID_INTEL_82375) {
+ DBG_DEVS(("common_fixup: ignoring PCEB...\n"));
+ continue;
+ }
+
/*
* This device is not on the primary bus, we need
* to figure out which interrupt pin it will come
@@ -668,45 +749,100 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
* the inline static routine above).
*/
dev->irq = 0;
- if (dev->bus->number != 0) {
- struct pci_dev *curr = dev;
+ if (!DEV_IS_ON_PRIMARY(dev)) {
/* read the pin and do the PCI-PCI bridge
interrupt pin swizzle */
pcibios_read_config_byte(dev->bus->number,
dev->devfn,
PCI_INTERRUPT_PIN,
&pin);
- /* cope with 0 */
- if (pin == 0)
+ /* cope with 0 and illegal */
+ if (pin == 0 || pin > 4)
pin = 1;
/* follow the chain of bridges, swizzling
as we go */
+ curr = dev;
#if defined(CONFIG_ALPHA_MIATA)
+ /* check first for the built-in bridge */
+ if ((PCI_SLOT(dev->bus->self->devfn) == 8) ||
+ (PCI_SLOT(dev->bus->self->devfn) == 20)) {
slot = PCI_SLOT(dev->devfn) + 5;
DBG_DEVS(("MIATA: bus 1 slot %d pin %d"
" irq %d min_idsel %d\n",
PCI_SLOT(dev->devfn), pin,
irq_tab[slot - min_idsel][pin],
min_idsel));
+ }
+ else /* must be a card-based bridge */
+ {
+ do {
+ if ((PCI_SLOT(curr->bus->self->devfn) == 8) ||
+ (PCI_SLOT(curr->bus->self->devfn) == 20))
+ {
+ slot = PCI_SLOT(curr->devfn) + 5;
+ break;
+ }
+ /* swizzle */
+ pin = bridge_swizzle(
+ pin, PCI_SLOT(curr->devfn)) ;
+ /* move up the chain of bridges */
+ curr = curr->bus->self ;
+ /* slot of the next bridge. */
+ slot = PCI_SLOT(curr->devfn);
+ } while (curr->bus->self) ;
+ }
#elif defined(CONFIG_ALPHA_NORITAKE)
- /* WAG Alert! */
- slot = PCI_SLOT(dev->devfn) + 14;
+ /* check first for the built-in bridge */
+ if (PCI_SLOT(dev->bus->self->devfn) == 8) {
+ slot = PCI_SLOT(dev->devfn) + 15; /* WAG! */
DBG_DEVS(("NORITAKE: bus 1 slot %d pin %d"
- " irq %d min_idsel %d\n",
+ "irq %d min_idsel %ld\n",
PCI_SLOT(dev->devfn), pin,
irq_tab[slot - min_idsel][pin],
min_idsel));
-#else
+ }
+ else /* must be a card-based bridge */
+ {
do {
+ if (PCI_SLOT(curr->bus->self->devfn) == 8) {
+ slot = PCI_SLOT(curr->devfn) + 15;
+ break;
+ }
/* swizzle */
- pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn));
+ pin = bridge_swizzle(
+ pin, PCI_SLOT(curr->devfn)) ;
+ /* move up the chain of bridges */
+ curr = curr->bus->self ;
+ /* slot of the next bridge. */
+ slot = PCI_SLOT(curr->devfn);
+ } while (curr->bus->self) ;
+ }
+#else /* everyone but MIATA and NORITAKE */
+ DBG_DEVS(("common_fixup: bus %d slot %d pin %d "
+ "irq %d min_idsel %ld\n",
+ curr->bus->number,
+ PCI_SLOT(dev->devfn), pin,
+ irq_tab[slot - min_idsel][pin],
+ min_idsel));
+ do {
+ /* swizzle */
+ pin =
+ bridge_swizzle(pin, PCI_SLOT(curr->devfn));
/* move up the chain of bridges */
curr = curr->bus->self;
} while (curr->bus->self);
/* The slot is the slot of the last bridge. */
slot = PCI_SLOT(curr->devfn);
-#endif /* MIATA */
- } else {
+#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /*
+ * must make sure that SRM didn't screw up
+ * and allocate an address > 64K for I/O
+ * space behind a PCI-PCI bridge
+ */
+ check_behind_io(dev);
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+ } else { /* just a device on a primary bus */
/* work out the slot */
slot = PCI_SLOT(dev->devfn);
/* read the pin */
@@ -714,16 +850,48 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
dev->devfn,
PCI_INTERRUPT_PIN,
&pin);
+ DBG_DEVS(("common_fixup: bus %d slot %d"
+ " pin %d irq %d min_idsel %ld\n",
+ dev->bus->number, slot, pin,
+ irq_tab[slot - min_idsel][pin],
+ min_idsel));
+ /* cope with 0 and illegal */
+ if (pin == 0 || pin > 4)
+ pin = 1;
}
if (irq_tab[slot - min_idsel][pin] != -1)
dev->irq = irq_tab[slot - min_idsel][pin];
-#if PCI_MODIFY
- /* tell the device: */
- pcibios_write_config_byte(dev->bus->number,
+#ifdef CONFIG_ALPHA_RAWHIDE
+ dev->irq +=
+ 24 * bus2hose[dev->bus->number]->pci_hose_index;
+#endif /* RAWHIDE */
+#ifdef CONFIG_ALPHA_SRM
+ {
+ unsigned char irq_orig;
+ /* read the original SRM-set IRQ and tell */
+ pcibios_read_config_byte(dev->bus->number,
dev->devfn,
PCI_INTERRUPT_LINE,
- dev->irq);
-#endif
+ &irq_orig);
+ if (irq_orig != dev->irq) {
+ DBG_DEVS(("common_fixup: bus %d slot 0x%x "
+ "SRM IRQ 0x%x changed to 0x%x\n",
+ dev->bus->number,PCI_SLOT(dev->devfn),
+ irq_orig, dev->irq));
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ irq_dev_to_reset[irq_reset_count] = dev;
+ irq_to_reset[irq_reset_count] = irq_orig;
+ irq_reset_count++;
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+ }
+ }
+#endif /* SRM */
+
+ /* always tell the device, so the driver knows what is
+ * the real IRQ to use; the device does not use it.
+ */
+ pcibios_write_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_LINE, dev->irq);
DBG_DEVS(("common_fixup: bus %d slot 0x%x"
" VID 0x%x DID 0x%x\n"
@@ -737,11 +905,24 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
* if it's a VGA, enable its BIOS ROM at C0000
*/
if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
- pcibios_write_config_dword(dev->bus->number,
+ /* but if its a Cirrus 543x/544x DISABLE it, */
+ /* since enabling ROM disables the memory... */
+ if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) &&
+ (dev->device >= 0x00a0) &&
+ (dev->device <= 0x00ac)) {
+ pcibios_write_config_dword(
+ dev->bus->number,
+ dev->devfn,
+ PCI_ROM_ADDRESS,
+ 0x00000000);
+ } else {
+ pcibios_write_config_dword(
+ dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
}
+ }
/*
* if it's a SCSI, disable its BIOS ROM
*/
@@ -752,46 +933,6 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
0x0000000);
}
}
-#ifdef CONFIG_ALPHA_SX164
- /* If it the CYPRESS PCI-ISA bridge, disable IDE
- interrupt routing through PCI (ie do through PIC). */
- else if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
- dev->device == 0xc693 &&
- PCI_FUNC(dev->devfn) == 0) {
- pcibios_write_config_word(dev->bus->number,
- dev->devfn, 0x04, 0x0007);
-
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x40, 0x80);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x41, 0x80);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x42, 0x80);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x43, 0x80);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x44, 0x27);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x45, 0xe0);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x48, 0xf0);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x49, 0x40);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x4a, 0x00);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x4b, 0x80);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x4c, 0x80);
- pcibios_write_config_byte(dev->bus->number,
- dev->devfn, 0x4d, 0x70);
-
- outb(0, DMA1_RESET_REG);
- outb(0, DMA2_RESET_REG);
- outb(DMA_MODE_CASCADE, DMA2_MODE_REG);
- outb(0, DMA2_MASK_REG);
- }
-#endif /* SX164 */
}
if (ide_base) {
enable_ide(ide_base);
@@ -814,6 +955,7 @@ common_fixup(long min_idsel, long max_idsel, long irqs_per_slot,
static inline void eb66p_fixup(void)
{
static char irq_tab[5][5] __initlocaldata = {
+ /*INT INTA INTB INTC INTD */
{16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */
{16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */
{ -1, -1, -1, -1, -1}, /* IdSel 8, SIO */
@@ -825,8 +967,8 @@ static inline void eb66p_fixup(void)
/*
- * The PC164/LX164 has 19 PCI interrupts, four from each of the four PCI
- * slots, the SIO, PCI/IDE, and USB.
+ * The PC164 and LX164 have 19 PCI interrupts, four from each of the four
+ * PCI slots, the SIO, PCI/IDE, and USB.
*
* Each of the interrupts can be individually masked. This is
* accomplished by setting the appropriate bit in the mask register.
@@ -901,6 +1043,7 @@ static inline void alphapc164_fixup(void)
static inline void cabriolet_fixup(void)
{
static char irq_tab[5][5] __initlocaldata = {
+ /*INT INTA INTB INTC INTD */
{ 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */
{ 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */
{ 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */
@@ -957,6 +1100,7 @@ static inline void cabriolet_fixup(void)
static inline void eb66_and_eb64p_fixup(void)
{
static char irq_tab[5][5] __initlocaldata = {
+ /*INT INTA INTB INTC INTD */
{16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */
{16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */
{16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */
@@ -968,7 +1112,7 @@ static inline void eb66_and_eb64p_fixup(void)
/*
- * Fixup configuration for MIKASA (NORITAKE is different)
+ * Fixup configuration for MIKASA (AlphaServer 1000)
*
* Summary @ 0x536:
* Bit Meaning
@@ -1020,7 +1164,10 @@ static inline void mikasa_fixup(void)
}
/*
- * Fixup configuration for NORITAKE (MIKASA is different)
+ * Fixup configuration for NORITAKE (AlphaServer 1000A)
+ *
+ * This is also used for CORELLE (AlphaServer 800)
+ * and ALCOR Primo (AlphaStation 600A).
*
* Summary @ 0x542, summary register #1:
* Bit Meaning
@@ -1076,8 +1223,11 @@ static inline void mikasa_fixup(void)
*/
static inline void noritake_fixup(void)
{
- static char irq_tab[13][5] __initlocaldata = {
+ static char irq_tab[15][5] __initlocaldata = {
/*INT INTA INTB INTC INTD */
+ /* note: IDSELs 16, 17, and 25 are CORELLE only */
+ { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */
+ { -1, -1, -1, -1, -1}, /* IdSel 17, S3 Trio64 */
{ -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */
{ -1, -1, -1, -1, -1}, /* IdSel 19, PPB */
{ -1, -1, -1, -1, -1}, /* IdSel 20, ???? */
@@ -1085,18 +1235,20 @@ static inline void noritake_fixup(void)
{ 16+2, 16+2, 16+3, 32+2, 32+3}, /* IdSel 22, slot 0 */
{ 16+4, 16+4, 16+5, 32+4, 32+5}, /* IdSel 23, slot 1 */
{ 16+6, 16+6, 16+7, 32+6, 32+7}, /* IdSel 24, slot 2 */
- /* The following are actually on bus 1, across the bridge */
+ { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 25, slot 3 */
+ /* the following 5 are actually on PCI bus 1, which is */
+ /* across the built-in bridge of the NORITAKE only */
{ 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */
{ 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 17, slot 3 */
{16+10, 16+10, 16+11, 32+10, 32+11}, /* IdSel 18, slot 4 */
{16+12, 16+12, 16+13, 32+12, 32+13}, /* IdSel 19, slot 5 */
{16+14, 16+14, 16+15, 32+14, 32+15}, /* IdSel 20, slot 6 */
};
- common_fixup(7, 18, 5, irq_tab, 0);
+ common_fixup(5, 19, 5, irq_tab, 0);
}
/*
- * Fixup configuration for ALCOR
+ * Fixup configuration for ALCOR and XLT (XL-300/366/433)
*
* Summary @ GRU_INT_REQ:
* Bit Meaning
@@ -1126,6 +1278,7 @@ static inline void noritake_fixup(void)
* The device to slot mapping looks like:
*
* Slot Device
+ * 6 built-in TULIP (XLT only)
* 7 PCI on board slot 0
* 8 PCI on board slot 3
* 9 PCI on board slot 4
@@ -1140,8 +1293,10 @@ static inline void noritake_fixup(void)
*/
static inline void alcor_fixup(void)
{
- static char irq_tab[6][5] __initlocaldata = {
+ static char irq_tab[7][5] __initlocaldata = {
/*INT INTA INTB INTC INTD */
+ /* note: IDSEL 17 is XLT only */
+ {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */
{ 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */
{16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */
{16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */
@@ -1149,62 +1304,6 @@ static inline void alcor_fixup(void)
{ 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */
{ 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */
};
- common_fixup(7, 12, 5, irq_tab, 0);
-}
-
-/*
- * Fixup configuration for ALPHA XLT (EV5/EV56)
- *
- * Summary @ GRU_INT_REQ:
- * Bit Meaning
- * 0 Interrupt Line A from slot 2
- * 1 Interrupt Line B from slot 2
- * 2 Interrupt Line C from slot 2
- * 3 Interrupt Line D from slot 2
- * 4 Interrupt Line A from slot 1
- * 5 Interrupt line B from slot 1
- * 6 Interrupt Line C from slot 1
- * 7 Interrupt Line D from slot 1
- * 8 Interrupt Line A from slot 0
- * 9 Interrupt Line B from slot 0
- *10 Interrupt Line C from slot 0
- *11 Interrupt Line D from slot 0
- *12 NCR810 SCSI in slot 9
- *13 DC-21040 (TULIP) in slot 6
- *14-19 Reserved
- *20-23 Jumpers (interrupt)
- *24-27 Module revision
- *28-30 Reserved
- *31 EISA interrupt
- *
- * The device to slot mapping looks like:
- *
- * Slot Device
- * 6 TULIP
- * 7 PCI on board slot 0
- * 8 none
- * 9 SCSI
- * 10 PCI-ISA bridge
- * 11 PCI on board slot 2
- * 12 PCI on board slot 1
- *
- *
- * This two layered interrupt approach means that we allocate IRQ 16 and
- * above for PCI interrupts. The IRQ relates to which bit the interrupt
- * comes in on. This makes interrupt processing much easier.
- */
-static inline void xlt_fixup(void)
-{
- static char irq_tab[7][5] __initlocaldata = {
- /*INT INTA INTB INTC INTD */
- {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */
- { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */
- { -1, -1, -1, -1, -1}, /* IdSel 19, none */
- {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 20, SCSI */
- { -1, -1, -1, -1, -1}, /* IdSel 21, SIO */
- { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */
- { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */
- };
common_fixup(6, 12, 5, irq_tab, 0);
}
@@ -1262,8 +1361,6 @@ static inline void xlt_fixup(void)
* with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables
* in irq.c
*/
-
-#ifdef CONFIG_ALPHA_SABLE
static inline void sable_fixup(void)
{
static char irq_tab[9][5] __initlocaldata = {
@@ -1280,7 +1377,6 @@ static inline void sable_fixup(void)
};
common_fixup(0, 8, 5, irq_tab, 0);
}
-#endif
/*
* Fixup configuration for MIATA (EV56+PYXIS)
@@ -1362,7 +1458,8 @@ static inline void miata_fixup(void)
{ -1, -1, -1, -1, -1}, /* IdSel 21, none */
{16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 22, slot 4 */
{16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 23, slot 5 */
- /* The following are actually on bus 1, across the bridge */
+ /* The following are actually on bus 1, which is */
+ /* across the builtin PCI-PCI bridge */
{16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 24, slot 1 */
{16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 25, slot 2 */
{16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 26, slot 3 */
@@ -1373,6 +1470,7 @@ static inline void miata_fixup(void)
{ -1, -1, -1, -1, -1}, /* IdSel 31, PCI-PCI */
};
common_fixup(3, 20, 5, irq_tab, 0);
+ SMC669_Init(); /* it might be a GL (fails harmlessly if not) */
es1888_init();
}
#endif
@@ -1399,7 +1497,6 @@ static inline void miata_fixup(void)
*14 Interrupt Line B from slot 1
*15 Interrupt line B from slot 0
*16 Interrupt Line C from slot 3
-
*17 Interrupt Line C from slot 2
*18 Interrupt Line C from slot 1
*19 Interrupt Line C from slot 0
@@ -1417,7 +1514,6 @@ static inline void miata_fixup(void)
*
*/
-#ifdef CONFIG_ALPHA_SX164
static inline void sx164_fixup(void)
{
static char irq_tab[5][5] __initlocaldata = {
@@ -1428,12 +1524,154 @@ static inline void sx164_fixup(void)
{ -1, -1, -1, -1, -1}, /* IdSel 8 SIO */
{ 16+ 8, 16+ 8, 16+12, 16+16, 16+20} /* IdSel 9 slot 3 J15 */
};
-
common_fixup(5, 9, 5, irq_tab, 0);
+ SMC669_Init();
+}
+
+/*
+ * Fixup configuration for DP264 (EV6+TSUNAMI)
+ *
+ * Summary @ TSUNAMI_CSR_DIM0:
+ * Bit Meaning
+ * 0-17 Unused
+ *18 Interrupt SCSI B (Adaptec 7895 builtin)
+ *19 Interrupt SCSI A (Adaptec 7895 builtin)
+ *20 Interrupt Line D from slot 2 PCI0
+ *21 Interrupt Line C from slot 2 PCI0
+ *22 Interrupt Line B from slot 2 PCI0
+ *23 Interrupt Line A from slot 2 PCI0
+ *24 Interrupt Line D from slot 1 PCI0
+ *25 Interrupt Line C from slot 1 PCI0
+ *26 Interrupt Line B from slot 1 PCI0
+ *27 Interrupt Line A from slot 1 PCI0
+ *28 Interrupt Line D from slot 0 PCI0
+ *29 Interrupt Line C from slot 0 PCI0
+ *30 Interrupt Line B from slot 0 PCI0
+ *31 Interrupt Line A from slot 0 PCI0
+ *
+ *32 Interrupt Line D from slot 3 PCI1
+ *33 Interrupt Line C from slot 3 PCI1
+ *34 Interrupt Line B from slot 3 PCI1
+ *35 Interrupt Line A from slot 3 PCI1
+ *36 Interrupt Line D from slot 2 PCI1
+ *37 Interrupt Line C from slot 2 PCI1
+ *38 Interrupt Line B from slot 2 PCI1
+ *39 Interrupt Line A from slot 2 PCI1
+ *40 Interrupt Line D from slot 1 PCI1
+ *41 Interrupt Line C from slot 1 PCI1
+ *42 Interrupt Line B from slot 1 PCI1
+ *43 Interrupt Line A from slot 1 PCI1
+ *44 Interrupt Line D from slot 0 PCI1
+ *45 Interrupt Line C from slot 0 PCI1
+ *46 Interrupt Line B from slot 0 PCI1
+ *47 Interrupt Line A from slot 0 PCI1
+ *48-52 Unused
+ *53 PCI0 NMI (from Cypress)
+ *54 PCI0 SMI INT (from Cypress)
+ *55 PCI0 ISA Interrupt (from Cypress)
+ *56-60 Unused
+ *61 PCI1 Bus Error
+ *62 PCI0 Bus Error
+ *63 Reserved
+ *
+ * IdSel
+ * 5 Cypress Bridge I/O
+ * 6 SCSI Adaptec builtin
+ * 7 64 bit PCI option slot 0
+ * 8 64 bit PCI option slot 1
+ * 9 64 bit PCI option slot 2
+ *
+ */
+static inline void dp264_fixup(void)
+{
+ static char irq_tab[5][5] __initlocaldata = {
+ /*INT INTA INTB INTC INTD */
+ { -1, -1, -1, -1, -1}, /* IdSel 5 ISA Bridge */
+ { 16+ 2, 16+ 2, 16+ 2, 16+ 2, 16+ 2}, /* IdSel 6 SCSI builtin */
+ { 16+15, 16+15, 16+14, 16+13, 16+12}, /* IdSel 7 slot 0 */
+ { 16+11, 16+11, 16+10, 16+ 9, 16+ 8}, /* IdSel 8 slot 1 */
+ { 16+ 7, 16+ 7, 16+ 6, 16+ 5, 16+ 4} /* IdSel 9 slot 2 */
+ };
+ common_fixup(5, 9, 5, irq_tab, 0);
SMC669_Init();
}
-#endif
+
+/*
+ * Fixup configuration for RAWHIDE
+ *
+ * Summary @ MCPCIA_PCI0_INT_REQ:
+ * Bit Meaning
+ *0 Interrupt Line A from slot 2 PCI0
+ *1 Interrupt Line B from slot 2 PCI0
+ *2 Interrupt Line C from slot 2 PCI0
+ *3 Interrupt Line D from slot 2 PCI0
+ *4 Interrupt Line A from slot 3 PCI0
+ *5 Interrupt Line B from slot 3 PCI0
+ *6 Interrupt Line C from slot 3 PCI0
+ *7 Interrupt Line D from slot 3 PCI0
+ *8 Interrupt Line A from slot 4 PCI0
+ *9 Interrupt Line B from slot 4 PCI0
+ *10 Interrupt Line C from slot 4 PCI0
+ *11 Interrupt Line D from slot 4 PCI0
+ *12 Interrupt Line A from slot 5 PCI0
+ *13 Interrupt Line B from slot 5 PCI0
+ *14 Interrupt Line C from slot 5 PCI0
+ *15 Interrupt Line D from slot 5 PCI0
+ *16 EISA interrupt (PCI 0) or SCSI interrupt (PCI 1)
+ *17-23 NA
+ *
+ * IdSel
+ * 1 EISA bridge (PCI bus 0 only)
+ * 2 PCI option slot 2
+ * 3 PCI option slot 3
+ * 4 PCI option slot 4
+ * 5 PCI option slot 5
+ *
+ */
+
+static inline void rawhide_fixup(void)
+{
+ static char irq_tab[5][5] __initlocaldata = {
+ /*INT INTA INTB INTC INTD */
+ { 16+16, 16+16, 16+16, 16+16, 16+16}, /* IdSel 1 SCSI PCI 1 only */
+ { 16+ 0, 16+ 0, 16+ 1, 16+ 2, 16+ 3}, /* IdSel 2 slot 2 */
+ { 16+ 4, 16+ 4, 16+ 5, 16+ 6, 16+ 7}, /* IdSel 3 slot 3 */
+ { 16+ 8, 16+ 8, 16+ 9, 16+10, 16+11}, /* IdSel 4 slot 4 */
+ { 16+12, 16+12, 16+13, 16+14, 16+15} /* IdSel 5 slot 5 */
+ };
+ common_fixup(1, 5, 5, irq_tab, 0);
+}
+
+/*
+ * The Takara has PCI devices 1, 2, and 3 configured to slots 20,
+ * 19, and 18 respectively, in the default configuration. They can
+ * also be jumpered to slots 8, 7, and 6 respectively, which is fun
+ * because the SIO ISA bridge can also be slot 7. However, the SIO
+ * doesn't explicitly generate PCI-type interrupts, so we can
+ * assign it whatever the hell IRQ we like and it doesn't matter.
+ */
+static inline void takara_fixup(void)
+{
+ static char irq_tab[15][5] __initlocaldata = {
+ { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */
+ { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */
+ { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */
+ { -1, -1, -1, -1, -1}, /* slot 9 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 10 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 11 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 12 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 13 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 14 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 15 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 16 == nothing */
+ { -1, -1, -1, -1, -1}, /* slot 17 == nothing */
+ { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 18 == device 3 */
+ { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 19 == device 2 */
+ { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 20 == device 1 */
+ };
+ common_fixup(6, 20, 5, irq_tab, 0x26e);
+}
/*
* Fixup configuration for all boards that route the PCI interrupts
@@ -1462,6 +1700,7 @@ static inline void sio_fixup(void)
* driven at all).
*/
static const char pirq_tab[][5] __initlocaldata = {
+ /*INT A B C D */
#ifdef CONFIG_ALPHA_P2K
{ 0, 0, -1, -1, -1}, /* idsel 6 (53c810) */
{-1, -1, -1, -1, -1}, /* idsel 7 (SIO: PCI/ISA bridge) */
@@ -1497,7 +1736,7 @@ static inline void sio_fixup(void)
#if defined(CONFIG_ALPHA_BOOK1)
/* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */
- const unsigned int route_tab = 0x0e0f0a0a;
+ const unsigned int new_route_tab = 0x0e0f0a0a;
#elif defined(CONFIG_ALPHA_NONAME)
/*
@@ -1510,16 +1749,24 @@ static inline void sio_fixup(void)
* they are co-indicated when the platform type "Noname" is
* selected... :-(
*/
- const unsigned int route_tab = 0x0b0a0f09;
+ const unsigned int new_route_tab = 0x0b0a0f09;
#else
- const unsigned int route_tab = 0x0b0a090f;
+ const unsigned int new_route_tab = 0x0b0a090f;
#endif
-
- unsigned int level_bits;
+ unsigned int route_tab, old_route_tab;
+ unsigned int level_bits, old_level_bits;
unsigned char pin, slot;
int pirq;
+ pcibios_read_config_dword(0, PCI_DEVFN(7, 0), 0x60, &old_route_tab);
+ DBG_DEVS(("sio_fixup: old pirq route table: 0x%08x\n",
+ old_route_tab));
+#if PCI_MODIFY
+ route_tab = new_route_tab;
pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab);
+#else
+ route_tab = old_route_tab;
+#endif
/*
* Go through all devices, fixing up irqs as we see fit:
@@ -1576,20 +1823,33 @@ static inline void sio_fixup(void)
* if it's a VGA, enable its BIOS ROM at C0000
*/
if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
- pcibios_write_config_dword(dev->bus->number,
+ /* but if its a Cirrus 543x/544x DISABLE it, */
+ /* since enabling ROM disables the memory... */
+ if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) &&
+ (dev->device >= 0x00a0) &&
+ (dev->device <= 0x00ac)) {
+ pcibios_write_config_dword(
+ dev->bus->number,
+ dev->devfn,
+ PCI_ROM_ADDRESS,
+ 0x00000000);
+ } else {
+ pcibios_write_config_dword(
+ dev->bus->number,
dev->devfn,
PCI_ROM_ADDRESS,
0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
}
+ }
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
continue; /* for now, displays get no IRQ */
}
if (pirq < 0) {
- printk("bios32.sio_fixup: "
+ DBG_DEVS(("bios32.sio_fixup: "
"weird, device %04x:%04x coming in on"
" slot %d has no irq line!!\n",
- dev->vendor, dev->device, slot);
+ dev->vendor, dev->device, slot));
continue;
}
@@ -1653,7 +1913,12 @@ static inline void sio_fixup(void)
*
* Note: we at least preserve any level-set bits on AlphaBook1
*/
- level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff);
+ old_level_bits = inb(0x4d0) | (inb(0x4d1) << 8);
+ DBG_DEVS(("sio_fixup: old irq level bits: 0x%04x\n",
+ old_level_bits));
+ level_bits |= (old_level_bits & 0x71ff);
+ DBG_DEVS(("sio_fixup: new irq level bits: 0x%04x\n",
+ level_bits));
outb((level_bits >> 0) & 0xff, 0x4d0);
outb((level_bits >> 8) & 0xff, 0x4d1);
@@ -1685,14 +1950,38 @@ static inline void sio_fixup(void)
extern void tga_console_init(void);
#endif /* CONFIG_TGA_CONSOLE */
-unsigned long __init
-pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
+void __init
+pcibios_fixup(void)
{
+ struct pci_bus *cur;
+
+#ifdef CONFIG_ALPHA_MCPCIA
+ /* must do massive setup for multiple PCI busses here... */
+ DBG_DEVS(("pcibios_fixup: calling mcpcia_fixup()...\n"));
+ mcpcia_fixup();
+#endif /* MCPCIA */
+
+#ifdef CONFIG_ALPHA_TSUNAMI
+ /* must do massive setup for multiple PCI busses here... */
+ /* tsunami_fixup(); */
+#endif /* TSUNAMI */
+
#if PCI_MODIFY && !defined(CONFIG_ALPHA_RUFFIAN)
/*
* Scan the tree, allocating PCI memory and I/O space.
*/
- layout_bus(&pci_root);
+ /*
+ * Sigh; check_region() will need changing to accept a HANDLE,
+ * if we allocate I/O space addresses on a per-bus basis.
+ * For now, make the I/O bases unique across all busses, so
+ * that check_region() will not get confused... ;-}
+ */
+ io_base = DEFAULT_IO_BASE;
+ for (cur = &pci_root; cur; cur = cur->next) {
+ mem_base = DEFAULT_MEM_BASE;
+ DBG_DEVS(("pcibios_fixup: calling layout_bus()\n"));
+ layout_bus(cur);
+ }
#endif
/*
@@ -1713,10 +2002,8 @@ pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
eb66_and_eb64p_fixup();
#elif defined(CONFIG_ALPHA_MIKASA)
mikasa_fixup();
-#elif defined(CONFIG_ALPHA_ALCOR)
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
alcor_fixup();
-#elif defined(CONFIG_ALPHA_XLT)
- xlt_fixup();
#elif defined(CONFIG_ALPHA_SABLE)
sable_fixup();
#elif defined(CONFIG_ALPHA_MIATA)
@@ -1725,6 +2012,12 @@ pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
noritake_fixup();
#elif defined(CONFIG_ALPHA_SX164)
sx164_fixup();
+#elif defined(CONFIG_ALPHA_DP264)
+ dp264_fixup();
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+ rawhide_fixup();
+#elif defined(CONFIG_ALPHA_TAKARA)
+ takara_fixup();
#elif defined(CONFIG_ALPHA_RUFFIAN)
/* no fixup needed */
#else
@@ -1736,8 +2029,6 @@ pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
tga_console_init();
#endif
#endif
-
- return mem_start;
}
@@ -1831,6 +2122,33 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn,
return err;
}
+#if (defined(CONFIG_ALPHA_PC164) || \
+ defined(CONFIG_ALPHA_LX164) || \
+ defined(CONFIG_ALPHA_SX164) || \
+ defined(CONFIG_ALPHA_EB164) || \
+ defined(CONFIG_ALPHA_EB66P) || \
+ defined(CONFIG_ALPHA_CABRIOLET)) && defined(CONFIG_ALPHA_SRM)
+
+/*
+ on the above machines, under SRM console, we must use the CSERVE PALcode
+ routine to manage the interrupt mask for us, otherwise, the kernel/HW get
+ out of sync with what the PALcode thinks it needs to deliver/ignore
+ */
+void
+cserve_update_hw(unsigned long irq, unsigned long mask)
+{
+ extern void cserve_ena(unsigned long);
+ extern void cserve_dis(unsigned long);
+
+ if (mask & (1UL << irq))
+ /* disable */
+ cserve_dis(irq - 16);
+ else
+ /* enable */
+ cserve_ena(irq - 16);
+ return;
+}
+#endif /* (PC164 || LX164 || SX164 || EB164 || CABRIO) && SRM */
#ifdef CONFIG_ALPHA_MIATA
/*
@@ -1877,4 +2195,49 @@ es1888_init(void)
}
#endif /* CONFIG_ALPHA_MIATA */
+__initfunc(char *pcibios_setup(char *str))
+{
+ return str;
+}
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+void reset_for_srm(void)
+{
+ extern void scrreset(void);
+ struct pci_dev *dev;
+ int i;
+
+ /* reset any IRQs that we changed */
+ for (i = 0; i < irq_reset_count; i++) {
+ dev = irq_dev_to_reset[i];
+
+ pcibios_write_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_LINE, irq_to_reset[i]);
+#if 1
+ printk("reset_for_srm: bus %d slot 0x%x "
+ "SRM IRQ 0x%x changed back from 0x%x\n",
+ dev->bus->number, PCI_SLOT(dev->devfn),
+ irq_to_reset[i], dev->irq);
+#endif
+ }
+
+ /* reset any IO addresses that we changed */
+ for (i = 0; i < io_reset_count; i++) {
+ dev = io_dev_to_reset[i];
+
+ pcibios_write_config_byte(dev->bus->number, dev->devfn,
+ io_reg_to_reset[i], io_to_reset[i]);
+#if 1
+ printk("reset_for_srm: bus %d slot 0x%x "
+ "SRM IO restored to 0x%x\n",
+ dev->bus->number, PCI_SLOT(dev->devfn),
+ io_to_reset[i]);
+#endif
+}
+
+ /* reset the visible screen to the top of display memory */
+ scrreset();
+}
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+
#endif /* CONFIG_PCI */
diff --git a/arch/alpha/kernel/cia.c b/arch/alpha/kernel/cia.c
index 4bebe2732..57fae7d87 100644
--- a/arch/alpha/kernel/cia.c
+++ b/arch/alpha/kernel/cia.c
@@ -6,8 +6,8 @@
*
*/
#include <linux/kernel.h>
+#include <linux/config.h>
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/sched.h>
@@ -17,13 +17,14 @@
#include <asm/ptrace.h>
#include <asm/mmu_context.h>
-/* NOTE: Herein are back-to-back mb insns. They are magic.
- A plausible explanation is that the i/o controler does not properly
- handle the system transaction. Another involves timing. Ho hum. */
+/*
+ * NOTE: Herein lie back-to-back mb instructions. They are magic.
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction. Another involves timing. Ho hum.
+ */
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
-extern int alpha_sys_type;
/*
* Machine check reasons. Defined according to PALcode sources
@@ -56,13 +57,17 @@ extern int alpha_sys_type;
# define DBGC(args)
#endif
-#define vulp volatile unsigned long *
#define vuip volatile unsigned int *
static volatile unsigned int CIA_mcheck_expected = 0;
static volatile unsigned int CIA_mcheck_taken = 0;
static unsigned int CIA_jd;
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int CIA_DMA_WIN_BASE = CIA_DMA_WIN_BASE_DEFAULT;
+unsigned int CIA_DMA_WIN_SIZE = CIA_DMA_WIN_SIZE_DEFAULT;
+unsigned long cia_sm_base_r1, cia_sm_base_r2, cia_sm_base_r3;
+#endif /* SRM_SETUP */
/*
* Given a bus, device, and function number, compute resulting
@@ -271,7 +276,7 @@ static void conf_write(unsigned long addr, unsigned int value,
}
/* reset error status: */
- *(vulp)CIA_IOC_CIA_ERR = stat0;
+ *(vuip)CIA_IOC_CIA_ERR = stat0;
mb();
wrmces(0x7); /* reset machine check */
value = 0xffffffff;
@@ -442,6 +447,18 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end)
printk("CIA_init: CIA_STAT was 0x%x\n", temp);
temp = *(vuip)CIA_IOC_MCR; mb();
printk("CIA_init: CIA_MCR was 0x%x\n", temp);
+ temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
+ printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
+ temp = *(vuip)CIA_IOC_ERR_MASK; mb();
+ printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp);
+ temp = *((vuip)CIA_IOC_PCI_W0_BASE); mb();
+ printk("CIA_init: W0_BASE was 0x%x\n", temp);
+ temp = *((vuip)CIA_IOC_PCI_W1_BASE); mb();
+ printk("CIA_init: W1_BASE was 0x%x\n", temp);
+ temp = *((vuip)CIA_IOC_PCI_W2_BASE); mb();
+ printk("CIA_init: W2_BASE was 0x%x\n", temp);
+ temp = *((vuip)CIA_IOC_PCI_W3_BASE); mb();
+ printk("CIA_init: W3_BASE was 0x%x\n", temp);
}
#endif /* DEBUG_DUMP_REGS */
@@ -458,6 +475,70 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end)
*(vuip)CIA_IOC_CIA_CTRL = cia_tmp;
mb();
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 0 for enabled and mapped to 0 */
+ if (((*(vuip)CIA_IOC_PCI_W0_BASE & 3) == 1) &&
+ (*(vuip)CIA_IOC_PCI_T0_BASE == 0))
+ {
+ CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W0_BASE & 0xfff00000U;
+ CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W0_MASK & 0xfff00000U;
+ CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("cia_init: using Window 0 settings\n");
+ printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)CIA_IOC_PCI_W0_BASE,
+ *(vuip)CIA_IOC_PCI_W0_MASK,
+ *(vuip)CIA_IOC_PCI_T0_BASE);
+#endif
+ }
+ else /* check window 1 for enabled and mapped to 0 */
+ if (((*(vuip)CIA_IOC_PCI_W1_BASE & 3) == 1) &&
+ (*(vuip)CIA_IOC_PCI_T1_BASE == 0))
+ {
+ CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W1_BASE & 0xfff00000U;
+ CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W1_MASK & 0xfff00000U;
+ CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("cia_init: using Window 1 settings\n");
+ printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)CIA_IOC_PCI_W1_BASE,
+ *(vuip)CIA_IOC_PCI_W1_MASK,
+ *(vuip)CIA_IOC_PCI_T1_BASE);
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if (((*(vuip)CIA_IOC_PCI_W2_BASE & 3) == 1) &&
+ (*(vuip)CIA_IOC_PCI_T2_BASE == 0))
+ {
+ CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W2_BASE & 0xfff00000U;
+ CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W2_MASK & 0xfff00000U;
+ CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("cia_init: using Window 2 settings\n");
+ printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)CIA_IOC_PCI_W2_BASE,
+ *(vuip)CIA_IOC_PCI_W2_MASK,
+ *(vuip)CIA_IOC_PCI_T2_BASE);
+#endif
+ }
+ else /* check window 3 for enabled and mapped to 0 */
+ if (((*(vuip)CIA_IOC_PCI_W3_BASE & 3) == 1) &&
+ (*(vuip)CIA_IOC_PCI_T3_BASE == 0))
+ {
+ CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W3_BASE & 0xfff00000U;
+ CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W3_MASK & 0xfff00000U;
+ CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("cia_init: using Window 3 settings\n");
+ printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)CIA_IOC_PCI_W3_BASE,
+ *(vuip)CIA_IOC_PCI_W3_MASK,
+ *(vuip)CIA_IOC_PCI_T3_BASE);
+#endif
+ }
+ else /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+ {
/*
* Set up the PCI->physical memory translation windows.
* For now, windows 1,2 and 3 are disabled. In the future, we may
@@ -472,6 +553,7 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end)
*(vuip)CIA_IOC_PCI_W1_BASE = 0x0;
*(vuip)CIA_IOC_PCI_W2_BASE = 0x0;
*(vuip)CIA_IOC_PCI_W3_BASE = 0x0;
+ }
/*
* check ASN in HWRPB for validity, report if bad
@@ -483,28 +565,54 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end)
}
/*
- * Finally, clear the CIA_CFG register, which gets used
+ * Next, clear the CIA_CFG register, which gets used
* for PCI Config Space accesses. That is the way
* we want to use it, and we do not want to depend on
* what ARC or SRM might have left behind...
*/
{
-#if 0
- unsigned int cia_cfg = *(vuip)CIA_IOC_CFG; mb();
- if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg);
-#endif
- *(vuip)CIA_IOC_CFG = 0; mb();
+ unsigned int cia_cfg = *((vuip)CIA_IOC_CFG); mb();
+ if (cia_cfg) {
+ printk("CIA_init: CFG was 0x%x\n", cia_cfg);
+ *((vuip)CIA_IOC_CFG) = 0; mb();
+ }
}
-#if 0
{
- unsigned int temp;
- temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
- printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_ERR_MASK; mb();
- printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp);
- }
+ unsigned int cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM);
+ unsigned int cia_hae_io = *((vuip)CIA_IOC_HAE_IO);
+#if 0
+ printk("CIA_init: HAE_MEM was 0x%x\n", cia_hae_mem);
+ printk("CIA_init: HAE_IO was 0x%x\n", cia_hae_io);
#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /*
+ sigh... For the SRM setup, unless we know apriori what the HAE
+ contents will be, we need to setup the arbitrary region bases
+ so we can test against the range of addresses and tailor the
+ region chosen for the SPARSE memory access.
+
+ see include/asm-alpha/cia.h for the SPARSE mem read/write
+ */
+ cia_sm_base_r1 = (cia_hae_mem ) & 0xe0000000UL; /* region 1 */
+ cia_sm_base_r2 = (cia_hae_mem << 16) & 0xf8000000UL; /* region 2 */
+ cia_sm_base_r3 = (cia_hae_mem << 24) & 0xfc000000UL; /* region 3 */
+
+ /*
+ Set the HAE cache, so that setup_arch() code
+ will use the SRM setting always. Our readb/writeb
+ code in cia.h expects never to have to change
+ the contents of the HAE.
+ */
+ hae.cache = cia_hae_mem;
+#else /* SRM_SETUP */
+ *((vuip)CIA_IOC_HAE_MEM) = 0; mb();
+ cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM);
+ *((vuip)CIA_IOC_HAE_IO) = 0; mb();
+ cia_hae_io = *((vuip)CIA_IOC_HAE_IO);
+#endif /* SRM_SETUP */
+ }
+
return mem_start;
}
@@ -512,7 +620,7 @@ int cia_pci_clr_err(void)
{
CIA_jd = *(vuip)CIA_IOC_CIA_ERR;
DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd));
- *(vulp)CIA_IOC_CIA_ERR = 0x0180;
+ *(vuip)CIA_IOC_CIA_ERR = 0x0180;
mb();
return 0;
}
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index b139d5178..0bbc71926 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -4,6 +4,7 @@
* kernel entry-points
*/
+#include <linux/config.h>
#include <asm/system.h>
#define halt .long PAL_halt
@@ -48,6 +49,8 @@
* JRP - Save regs 16-18 in a special area of the stack, so that
* the palcode-provided values are available to the signal handler.
*/
+#if defined(CONFIG_ALPHA_TSUNAMI)
+/* TSUNAMI has no HAE register to save/restore */
#define SAVE_ALL \
subq $30,184,$30; \
stq $0,0($30); \
@@ -55,6 +58,55 @@
stq $2,16($30); \
stq $3,24($30); \
stq $4,32($30); \
+ stq $5,40($30); \
+ stq $6,48($30); \
+ stq $7,56($30); \
+ stq $8,64($30); \
+ stq $19,72($30); \
+ stq $20,80($30); \
+ stq $21,88($30); \
+ stq $22,96($30); \
+ stq $23,104($30); \
+ stq $24,112($30); \
+ stq $25,120($30); \
+ stq $26,128($30); \
+ stq $27,136($30); \
+ stq $28,144($30); \
+ stq $16,160($30); \
+ stq $17,168($30); \
+ stq $18,176($30)
+
+#define RESTORE_ALL \
+ ldq $0,0($30); \
+ ldq $1,8($30); \
+ ldq $2,16($30); \
+ ldq $3,24($30); \
+ ldq $4,32($30); \
+ ldq $5,40($30); \
+ ldq $6,48($30); \
+ ldq $7,56($30); \
+ ldq $8,64($30); \
+ ldq $19,72($30); \
+ ldq $20,80($30); \
+ ldq $21,88($30); \
+ ldq $22,96($30); \
+ ldq $23,104($30); \
+ ldq $24,112($30); \
+ ldq $25,120($30); \
+ ldq $26,128($30); \
+ ldq $27,136($30); \
+ ldq $28,144($30); \
+ addq $30,184,$30
+
+#else /* TSUNAMI */
+#define SAVE_ALL \
+ subq $30,184,$30; \
+ stq $0,0($30); \
+ stq $1,8($30); \
+ stq $2,16($30); \
+ stq $3,24($30); \
+ stq $4,32($30); \
+ stq $28,144($30); \
lda $2,hae; \
stq $5,40($30); \
stq $6,48($30); \
@@ -70,7 +122,6 @@
stq $25,120($30); \
stq $26,128($30); \
stq $27,136($30); \
- stq $28,144($30); \
stq $2,152($30); \
stq $16,160($30); \
stq $17,168($30); \
@@ -113,6 +164,8 @@
ldq $28,144($30); \
addq $30,184,$30
+#endif /* TSUNAMI */
+
.text
.set noat
#if defined(__linux__) && !defined(__ELF__)
@@ -508,6 +561,8 @@ sys_clone:
alpha_switch_to:
bsr $1,do_switch_stack
call_pal PAL_swpctx
+ lda $16,-2($31)
+ call_pal PAL_tbi
bsr $1,undo_switch_stack
ret $31,($26),1
.end alpha_switch_to
@@ -681,6 +736,19 @@ signal_return:
br $31,restore_all
.end entSys
+#ifdef __SMP__
+ .globl ret_from_smpfork
+.align 3
+.ent ret_from_smpfork
+ret_from_smpfork:
+ .set at
+ stq $31,scheduler_lock
+ mb /* ?????????????????? */
+ br ret_from_sys_call
+ .set noat
+.end ret_from_smpfork
+#endif /* __SMP__ */
+
.align 3
.ent reschedule
reschedule:
diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S
index 4a3ec9e7c..a6bcd616d 100644
--- a/arch/alpha/kernel/head.S
+++ b/arch/alpha/kernel/head.S
@@ -32,6 +32,27 @@ __start:
halt
.end __start
+#ifdef __SMP__
+ .align 3
+ .globl __start_cpu
+ .ent __start_cpu
+ /* on entry here from SRM console, the HWPCB of this processor */
+ /* has been loaded, and $27 contains the task pointer */
+__start_cpu:
+ /* first order of business, load the GP */
+ br $26,1f
+1: ldgp $29,0($26)
+ /* We need to get current loaded up with our first task... */
+ lda $8,0($27)
+ /* set FEN */
+ lda $16,1($31)
+ call_pal PAL_wrfen
+ /* ... and then we can start the processor. */
+ jsr $26,start_secondary
+ halt
+ .end __start_cpu
+#endif /* __SMP__ */
+
.align 3
.globl wrent
.ent wrent
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
index 41d5d5f01..bcac2da2b 100644
--- a/arch/alpha/kernel/irq.c
+++ b/arch/alpha/kernel/irq.c
@@ -30,6 +30,10 @@
#define vulp volatile unsigned long *
#define vuip volatile unsigned int *
+extern void timer_interrupt(struct pt_regs * regs);
+extern void cserve_update_hw(unsigned long, unsigned long);
+extern void handle_ipi(struct pt_regs *);
+
#define RTC_IRQ 8
#ifdef CONFIG_RTC
#define TIMER_IRQ 0 /* timer is the pit */
@@ -45,12 +49,15 @@
#if defined(CONFIG_ALPHA_P2K)
/* always mask out unused timer irq 0 and RTC irq 8 */
# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL)
-#elif defined(CONFIG_ALPHA_ALCOR)
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
/* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */
# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL)
#elif defined(CONFIG_ALPHA_RUFFIAN)
/* must leave timer irq 0 in the mask */
# define PROBE_MASK ((1UL << NR_IRQS) - 1)
+#elif NR_IRQS == 64
+ /* always mask out unused timer irq 0: */
+# define PROBE_MASK (~1UL)
#else
/* always mask out unused timer irq 0: */
# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL)
@@ -119,7 +126,7 @@ sable_update_hw(unsigned long irq, unsigned long mask)
{
/* The "irq" argument is really the mask bit number */
switch (irq) {
- default: /* 16 ... 23 */
+ case 16 ... 23:
outb(mask >> 16, 0x53d);
break;
case 8 ... 15:
@@ -135,7 +142,7 @@ static inline void
noritake_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 32 ... 47 */
+ case 32 ... 47:
outw(~(mask >> 32), 0x54c);
break;
case 16 ... 31:
@@ -155,7 +162,7 @@ static inline void
miata_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 16 ... 47 */
+ case 16 ... 47:
/* Make CERTAIN none of the bogus ints get enabled... */
*(vulp)PYXIS_INT_MASK =
~((long)mask >> 16) & ~0x4000000000000e3bUL;
@@ -178,7 +185,7 @@ static inline void
alcor_and_xlt_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 16 ... 47 */
+ case 16 ... 47:
/* On Alcor, at least, lines 20..30 are not connected and can
generate spurrious interrupts if we turn them on while IRQ
probing. So explicitly mask them out. */
@@ -202,7 +209,7 @@ static inline void
mikasa_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 16 ... 31 */
+ case 16 ... 31:
outw(~(mask >> 16), 0x536); /* note invert */
break;
case 8 ... 15: /* ISA PIC2 */
@@ -214,7 +221,7 @@ mikasa_update_hw(unsigned long irq, unsigned long mask)
}
}
-#ifdef CONFIG_ALPHA_RUFFIAN
+#if defined(CONFIG_ALPHA_RUFFIAN)
static inline void
ruffian_update_hw(unsigned long irq, unsigned long mask)
{
@@ -223,8 +230,7 @@ ruffian_update_hw(unsigned long irq, unsigned long mask)
/* Note inverted sense of mask bits: */
/* Make CERTAIN none of the bogus ints get enabled... */
*(vulp)PYXIS_INT_MASK =
- ~((long)mask >> 16) & 0x00000000ffffffbfUL;
- mb();
+ ~((long)mask >> 16) & 0x00000000ffffffbfUL; mb();
/* ... and read it back to make sure it got written. */
*(vulp)PYXIS_INT_MASK;
break;
@@ -236,20 +242,23 @@ ruffian_update_hw(unsigned long irq, unsigned long mask)
break;
}
}
-#endif
+#endif /* RUFFIAN */
-#ifdef CONFIG_ALPHA_SX164
+#if defined(CONFIG_ALPHA_SX164)
static inline void
sx164_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
case 16 ... 39:
- /* Make CERTAIN none of the bogus ints get enabled */
+#if defined(CONFIG_ALPHA_SRM)
+ cserve_update_hw(irq, mask);
+#else
+ /* make CERTAIN none of the bogus ints get enabled */
*(vulp)PYXIS_INT_MASK =
- ~((long)mask >> 16) & ~0x000000000000003bUL;
- mb();
+ ~((long)mask >> 16) & ~0x000000000000003bUL; mb();
/* ... and read it back to make sure it got written. */
*(vulp)PYXIS_INT_MASK;
+#endif /* SRM */
break;
case 8 ... 15: /* ISA PIC2 */
outb(mask >> 8, 0xA1);
@@ -258,20 +267,23 @@ sx164_update_hw(unsigned long irq, unsigned long mask)
outb(mask, 0x21);
break;
}
-}
-#endif
-/* Unlabeled mechanisms based on the number of irqs. Someone should
- probably document and name these. */
+}
+#endif /* SX164 */
+#if defined(CONFIG_ALPHA_DP264)
static inline void
-update_hw_33(unsigned long irq, unsigned long mask)
+dp264_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 16 ... 32 */
- outl(mask >> 16, 0x804);
+ case 16 ... 63:
+ /* make CERTAIN none of the bogus ints get enabled */
+ /* HACK ALERT! only CPU#0 is used currently */
+ *(vulp)TSUNAMI_CSR_DIM0 =
+ ~(mask) & ~0x0000000000000000UL; mb();
+ /* ... and read it back to make sure it got written. */
+ *(vulp)TSUNAMI_CSR_DIM0;
break;
-
case 8 ... 15: /* ISA PIC2 */
outb(mask >> 8, 0xA1);
break;
@@ -280,16 +292,24 @@ update_hw_33(unsigned long irq, unsigned long mask)
break;
}
}
+#endif /* DP264 */
+#if defined(CONFIG_ALPHA_RAWHIDE)
static inline void
-update_hw_32(unsigned long irq, unsigned long mask)
+rawhide_update_hw(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 24 ... 31 */
- outb(mask >> 24, 0x27);
+ case 16 ... 39: /* PCI bus 0 with EISA bridge */
+ *(vuip)MCPCIA_INT_MASK0(0) =
+ (~((mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb();
+ /* ... and read it back to make sure it got written. */
+ *(vuip)MCPCIA_INT_MASK0(0);
break;
- case 16 ... 23:
- outb(mask >> 16, 0x26);
+ case 40 ... 63: /* PCI bus 1 with builtin NCR810 SCSI */
+ *(vuip)MCPCIA_INT_MASK0(1) =
+ (~((mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb();
+ /* ... and read it back to make sure it got written. */
+ *(vuip)MCPCIA_INT_MASK0(1);
break;
case 8 ... 15: /* ISA PIC2 */
outb(mask >> 8, 0xA1);
@@ -299,12 +319,29 @@ update_hw_32(unsigned long irq, unsigned long mask)
break;
}
}
+#endif /* RAWHIDE */
+/*
+ * HW update code for the following platforms:
+ *
+ * CABRIOLET (AlphaPC64)
+ * EB66P
+ * EB164
+ * PC164
+ * LX164
+ */
static inline void
-update_hw_16(unsigned long irq, unsigned long mask)
+update_hw_35(unsigned long irq, unsigned long mask)
{
switch (irq) {
- default: /* 8 ... 15, ISA PIC2 */
+ case 16 ... 34:
+#if defined(CONFIG_ALPHA_SRM)
+ cserve_update_hw(irq, mask);
+#else /* SRM */
+ outl(irq_mask >> 16, 0x804);
+#endif /* SRM */
+ break;
+ case 8 ... 15: /* ISA PIC2 */
outb(mask >> 8, 0xA1);
break;
case 0 ... 7: /* ISA PIC1 */
@@ -313,42 +350,38 @@ update_hw_16(unsigned long irq, unsigned long mask)
}
}
-#if (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \
- && defined(CONFIG_ALPHA_SRM)
-/*
- * On the pc164, we cannot take over the IRQs from the SRM,
- * so we call down to do our dirty work. Too bad the SRM
- * isn't consistent across platforms otherwise we could do
- * this always.
- */
-
-extern void cserve_ena(unsigned long);
-extern void cserve_dis(unsigned long);
-
-static inline void mask_irq(unsigned long irq)
+static inline void
+update_hw_32(unsigned long irq, unsigned long mask)
{
- irq_mask |= (1UL << irq);
- cserve_dis(irq - 16);
+ switch (irq) {
+ case 24 ... 31:
+ outb(mask >> 24, 0x27);
+ break;
+ case 16 ... 23:
+ outb(mask >> 16, 0x26);
+ break;
+ case 8 ... 15: /* ISA PIC2 */
+ outb(mask >> 8, 0xA1);
+ break;
+ case 0 ... 7: /* ISA PIC1 */
+ outb(mask, 0x21);
+ break;
}
-
-static inline void unmask_irq(unsigned long irq)
-{
- irq_mask &= ~(1UL << irq);
- cserve_ena(irq - 16);
}
-/* Since we are calling down to PALcode, no need to diddle IPL. */
-void disable_irq(unsigned int irq_nr)
+static inline void
+update_hw_16(unsigned long irq, unsigned long mask)
{
- mask_irq(IRQ_TO_MASK(irq_nr));
+ switch (irq) {
+ case 8 ... 15: /* ISA PIC2 */
+ outb(mask >> 8, 0xA1);
+ break;
+ case 0 ... 7: /* ISA PIC1 */
+ outb(mask, 0x21);
+ break;
}
-
-void enable_irq(unsigned int irq_nr)
-{
- unmask_irq(IRQ_TO_MASK(irq_nr));
}
-#else
/*
* We manipulate the hardware ourselves.
*/
@@ -369,9 +402,18 @@ static void update_hw(unsigned long irq, unsigned long mask)
sx164_update_hw(irq, mask);
#elif defined(CONFIG_ALPHA_RUFFIAN)
ruffian_update_hw(irq, mask);
-#elif NR_IRQS == 33
- update_hw_33(irq, mask);
-#elif NR_IRQS == 32
+#elif defined(CONFIG_ALPHA_DP264)
+ dp264_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+ rawhide_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_CABRIOLET) || \
+ defined(CONFIG_ALPHA_EB66P) || \
+ defined(CONFIG_ALPHA_EB164) || \
+ defined(CONFIG_ALPHA_PC164) || \
+ defined(CONFIG_ALPHA_LX164)
+ update_hw_35(irq, mask);
+#elif defined(CONFIG_ALPHA_EB66) || \
+ defined(CONFIG_ALPHA_EB64P)
update_hw_32(irq, mask);
#elif NR_IRQS == 16
update_hw_16(irq, mask);
@@ -396,8 +438,7 @@ void disable_irq(unsigned int irq_nr)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
mask_irq(IRQ_TO_MASK(irq_nr));
restore_flags(flags);
}
@@ -406,12 +447,10 @@ void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
unmask_irq(IRQ_TO_MASK(irq_nr));
restore_flags(flags);
}
-#endif /* (PC164 || LX164) && SRM */
/*
* Initial irq handlers.
@@ -423,13 +462,14 @@ int get_irq_list(char *buf)
{
int i, len = 0;
struct irqaction * action;
+ int cpu = smp_processor_id();
for (i = 0; i < NR_IRQS; i++) {
action = irq_action[i];
if (!action)
continue;
len += sprintf(buf+len, "%2d: %10u %c %s",
- i, kstat.irqs[0][i],
+ i, kstat.irqs[cpu][i],
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
@@ -463,16 +503,18 @@ static inline void ack_irq(int irq)
#elif defined(CONFIG_ALPHA_RUFFIAN)
if (irq < 16) {
/* Ack PYXIS ISA interrupt. */
- *(vulp)PYXIS_INT_REQ = 1 << 7;
- mb();
+ *(vulp)PYXIS_INT_REQ = 1L << 7; mb();
+ /* ... and read it back to make sure it got written. */
+ *(vulp)PYXIS_INT_REQ;
if (irq > 7) {
outb(0x20, 0xa0);
}
outb(0x20, 0x20);
} else {
- /* Ack PYXIS interrupt. */
+ /* Ack PYXIS PCI interrupt. */
*(vulp)PYXIS_INT_REQ = (1UL << (irq - 16));
- mb();
+ /* ... and read it back to make sure it got written. */
+ *(vulp)PYXIS_INT_REQ;
}
#else
if (irq < 16) {
@@ -488,7 +530,7 @@ static inline void ack_irq(int irq)
/* on ALCOR/XLT, need to dismiss interrupt via GRU */
*(vuip)GRU_INT_CLEAR = 0x80000000; mb();
*(vuip)GRU_INT_CLEAR = 0x00000000; mb();
-#endif
+#endif /* ALCOR || XLT */
}
#endif
}
@@ -556,8 +598,7 @@ int request_irq(unsigned int irq,
action->next = NULL;
action->dev_id = dev_id;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
*p = action;
if (!shared)
@@ -585,8 +626,7 @@ void free_irq(unsigned int irq, void *dev_id)
continue;
/* Found it - now free it */
- save_flags(flags);
- cli();
+ save_and_cli(flags);
*p = action->next;
if (!irq[irq_action])
mask_irq(IRQ_TO_MASK(irq));
@@ -607,7 +647,277 @@ unsigned int local_irq_count[NR_CPUS];
unsigned int local_bh_count[NR_CPUS];
#ifdef __SMP__
-#error "Me no hablo Alpha SMP"
+/* Who has global_irq_lock. */
+unsigned char global_irq_holder = NO_PROC_ID;
+
+/* This protects IRQ's. */
+spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
+
+/* Global IRQ locking depth. */
+atomic_t global_irq_count = ATOMIC_INIT(0);
+
+/* This protects BH software state (masks, things like that). */
+atomic_t global_bh_lock = ATOMIC_INIT(0);
+atomic_t global_bh_count = ATOMIC_INIT(0);
+
+static unsigned long previous_irqholder = NO_PROC_ID;
+
+#undef INIT_STUCK
+#define INIT_STUCK 100000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, atomic_read(&global_irq_count)); stuck = INIT_STUCK; }
+
+static inline void wait_on_irq(int cpu, unsigned long where)
+{
+ int stuck = INIT_STUCK;
+ int local_count = local_irq_count[cpu];
+
+ /* Are we the only one in an interrupt context? */
+ while (local_count != atomic_read(&global_irq_count)) {
+ /*
+ * No such luck. Now we need to release the lock,
+ * _and_ release our interrupt context, because
+ * otherwise we'd have dead-locks and live-locks
+ * and other fun things.
+ */
+ atomic_sub(local_count, &global_irq_count);
+ spin_unlock(&global_irq_lock);
+
+ /*
+ * Wait for everybody else to go away and release
+ * their things before trying to get the lock again.
+ */
+ for (;;) {
+ STUCK;
+ if (atomic_read(&global_irq_count))
+ continue;
+ if (global_irq_lock.lock)
+ continue;
+ if (spin_trylock(&global_irq_lock))
+ break;
+ }
+ atomic_add(local_count, &global_irq_count);
+ }
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 10000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("get_irqlock stuck at %08lx, waiting for %08lx\n", where, previous_irqholder); stuck = INIT_STUCK;}
+
+static inline void get_irqlock(int cpu, unsigned long where)
+{
+ int stuck = INIT_STUCK;
+
+ if (!spin_trylock(&global_irq_lock)) {
+ /* do we already hold the lock? */
+ if ((unsigned char) cpu == global_irq_holder) {
+#if 0
+ printk("get_irqlock: already held at %08lx\n",
+ previous_irqholder);
+#endif
+ return;
+ }
+ /* Uhhuh.. Somebody else got it. Wait.. */
+ do {
+ do {
+ STUCK;
+ barrier();
+ } while (global_irq_lock.lock);
+ } while (!spin_trylock(&global_irq_lock));
+ }
+ /*
+ * Ok, we got the lock bit.
+ * But that's actually just the easy part.. Now
+ * we need to make sure that nobody else is running
+ * in an interrupt context.
+ */
+ wait_on_irq(cpu, where);
+
+ /*
+ * Finally.
+ */
+ global_irq_holder = cpu;
+ previous_irqholder = where;
+}
+
+void __global_cli(void)
+{
+ int cpu = smp_processor_id();
+ unsigned long where;
+
+ __asm__("mov $26, %0" : "=r" (where));
+ __cli();
+
+ if (!local_irq_count[cpu])
+ get_irqlock(smp_processor_id(), where);
+}
+
+void __global_sti(void)
+{
+ int cpu = smp_processor_id();
+
+ if (!local_irq_count[cpu])
+ release_irqlock(smp_processor_id());
+
+ __sti();
+}
+
+#if 0
+unsigned long __global_save_flags(void)
+{
+ return global_irq_holder == (unsigned char) smp_processor_id();
+}
+#endif
+
+void __global_restore_flags(unsigned long flags)
+{
+ if (flags & 1) {
+ __global_cli();
+ } else {
+ /* release_irqlock() */
+ if (global_irq_holder == smp_processor_id()) {
+ global_irq_holder = NO_PROC_ID;
+ spin_unlock(&global_irq_lock);
+ }
+ if (!(flags & 2))
+ __sti();
+ }
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 200000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;}
+
+#undef VERBOSE_IRQLOCK_DEBUGGING
+
+void irq_enter(int cpu, int irq)
+{
+#ifdef VERBOSE_IRQLOCK_DEBUGGING
+ extern void smp_show_backtrace_all_cpus(void);
+#endif
+ int stuck = INIT_STUCK;
+
+ hardirq_enter(cpu);
+ barrier();
+ while (global_irq_lock.lock) {
+ if ((unsigned char) cpu == global_irq_holder) {
+ int globl_locked = global_irq_lock.lock;
+ int globl_icount = atomic_read(&global_irq_count);
+ int local_count = local_irq_count[cpu];
+
+ /* It is very important that we load the state variables
+ * before we do the first call to printk() as printk()
+ * could end up changing them...
+ */
+
+#if 0
+ printk("CPU[%d]: BAD! Local IRQ's enabled,"
+ " global disabled interrupt\n", cpu);
+#endif
+ printk("CPU[%d]: where [%08lx] glocked[%d] gicnt[%d]"
+ " licnt[%d]\n",
+ cpu, previous_irqholder, globl_locked,
+ globl_icount, local_count);
+#ifdef VERBOSE_IRQLOCK_DEBUGGING
+ printk("Performing backtrace on all cpus,"
+ " write this down!\n");
+ smp_show_backtrace_all_cpus();
+#endif
+ break;
+ }
+ STUCK;
+ barrier();
+ }
+}
+
+void irq_exit(int cpu, int irq)
+{
+ hardirq_exit(cpu);
+ release_irqlock(cpu);
+}
+
+static void show(char * str)
+{
+#if 0
+ int i;
+ unsigned long *stack;
+#endif
+ int cpu = smp_processor_id();
+
+ printk("\n%s, CPU %d:\n", str, cpu);
+ printk("irq: %d [%d %d]\n",
+ atomic_read(&global_irq_count), local_irq_count[0],
+ local_irq_count[1]);
+ printk("bh: %d [%d %d]\n",
+ atomic_read(&global_bh_count), local_bh_count[0],
+ local_bh_count[1]);
+#if 0
+ stack = (unsigned long *) &str;
+ for (i = 40; i ; i--) {
+ unsigned long x = *++stack;
+ if (x > (unsigned long) &init_task_union &&
+ x < (unsigned long) &vsprintf) {
+ printk("<[%08lx]> ", x);
+ }
+ }
+#endif
+}
+
+#define MAXCOUNT 100000000
+
+static inline void wait_on_bh(void)
+{
+ int count = MAXCOUNT;
+ do {
+ if (!--count) {
+ show("wait_on_bh");
+ count = ~0;
+ }
+ /* nothing .. wait for the other bh's to go away */
+ } while (atomic_read(&global_bh_count) != 0);
+}
+
+/*
+ * This is called when we want to synchronize with
+ * bottom half handlers. We need to wait until
+ * no other CPU is executing any bottom half handler.
+ *
+ * Don't wait if we're already running in an interrupt
+ * context or are inside a bh handler.
+ */
+void synchronize_bh(void)
+{
+ if (atomic_read(&global_bh_count)) {
+ int cpu = smp_processor_id();
+ if (!local_irq_count[cpu] && !local_bh_count[cpu]) {
+ wait_on_bh();
+ }
+ }
+}
+
+/* There has to be a better way. */
+void synchronize_irq(void)
+{
+ int cpu = smp_processor_id();
+ int local_count = local_irq_count[cpu];
+
+ if (local_count != atomic_read(&global_irq_count)) {
+ unsigned long flags;
+
+ /* An infamously unpopular approach. */
+ save_and_cli(flags);
+ restore_flags(flags);
+ }
+}
+
#else
#define irq_enter(cpu, irq) (++local_irq_count[cpu])
#define irq_exit(cpu, irq) (--local_irq_count[cpu])
@@ -647,7 +957,7 @@ static inline void handle_irq(int irq, struct pt_regs * regs)
int cpu = smp_processor_id();
irq_enter(cpu, irq);
- kstat.irqs[0][irq] += 1;
+ kstat.irqs[cpu][irq] += 1;
if (!action) {
unexpected_irq(irq, regs);
} else {
@@ -670,8 +980,9 @@ static inline void device_interrupt(int irq, int ack, struct pt_regs * regs)
}
irq_enter(cpu, irq);
- kstat.irqs[0][irq] += 1;
+ kstat.irqs[cpu][irq] += 1;
action = irq_action[irq];
+
/*
* For normal interrupts, we mask it out, and then ACK it.
* This way another (more timing-critical) interrupt can
@@ -691,6 +1002,10 @@ static inline void device_interrupt(int irq, int ack, struct pt_regs * regs)
action = action->next;
} while (action);
unmask_irq(ack);
+ } else {
+#if 1
+ printk("device_interrupt: unexpected interrupt %d\n", irq);
+#endif
}
irq_exit(cpu, irq);
}
@@ -711,6 +1026,8 @@ static inline void isa_device_interrupt(unsigned long vector,
# define IACK_SC CIA_IACK_SC
#elif defined(CONFIG_ALPHA_PYXIS)
# define IACK_SC PYXIS_IACK_SC
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+# define IACK_SC TSUNAMI_PCI0_IACK_SC
#else
/*
* This is bogus but necessary to get it to compile
@@ -729,7 +1046,7 @@ static inline void isa_device_interrupt(unsigned long vector,
* interrupt that is pending. The PALcode sets up the
* interrupts vectors such that irq level L generates vector L.
*/
- j = *(volatile int *) IACK_SC;
+ j = *(vuip) IACK_SC;
j &= 0xff;
if (j == 7) {
if (!(inb(0x20) & 0x80)) {
@@ -775,10 +1092,9 @@ alcor_and_xlt_device_interrupt(unsigned long vector, struct pt_regs *regs)
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
- /* read the interrupt summary register of the GRU */
+ /* Read the interrupt summary register of the GRU */
pld = (*(vuip)GRU_INT_REQ) & GRU_INT_REQ_BITS;
#if 0
@@ -810,10 +1126,9 @@ cabriolet_and_eb66p_device_interrupt(unsigned long vector,
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
- /* read the interrupt summary registers */
+ /* Read the interrupt summary registers */
pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16);
#if 0
@@ -843,10 +1158,9 @@ mikasa_device_interrupt(unsigned long vector, struct pt_regs *regs)
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
- /* read the interrupt summary registers */
+ /* Read the interrupt summary registers */
pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) |
(((unsigned long) inb(0xa0)) << 8) |
((unsigned long) inb(0x20));
@@ -878,10 +1192,9 @@ eb66_and_eb64p_device_interrupt(unsigned long vector, struct pt_regs *regs)
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
- /* read the interrupt summary registers */
+ /* Read the interrupt summary registers */
pld = inb(0x26) | (inb(0x27) << 8);
/*
* Now, for every possible bit set, work through
@@ -909,30 +1222,34 @@ miata_device_interrupt(unsigned long vector, struct pt_regs *regs)
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
- /* read the interrupt summary register of PYXIS */
- pld = (*(vulp)PYXIS_INT_REQ);
+ /* Read the interrupt summary register of PYXIS */
+ pld = *(vulp)PYXIS_INT_REQ;
#if 0
printk("[0x%08lx/0x%08lx/0x%04x]", pld,
*(vulp)PYXIS_INT_MASK, inb(0x20) | (inb(0xA0) << 8));
#endif
- /* For now, AND off any bits we are not interested in. */
-#if defined(CONFIG_ALPHA_MIATA)
- /* HALT (2), timer (6), ISA Bridge (7), 21142/3 (8),
- then all the PCI slots/INTXs (12-31). */
+#ifdef CONFIG_ALPHA_MIATA
+ /*
+ * For now, AND off any bits we are not interested in:
+ * HALT (2), timer (6), ISA Bridge (7), 21142/3 (8)
+ * then all the PCI slots/INTXs (12-31).
+ */
/* Maybe HALT should only be used for SRM console boots? */
pld &= 0x00000000fffff1c4UL;
#endif
-#if defined(CONFIG_ALPHA_SX164)
- /* HALT (2), timer (6), ISA Bridge (7),
- then all the PCI slots/INTXs (8-23). */
- /* HALT should only be used for SRM console boots. */
+#ifdef CONFIG_ALPHA_SX164
+ /*
+ * For now, AND off any bits we are not interested in:
+ * HALT (2), timer (6), ISA Bridge (7)
+ * then all the PCI slots/INTXs (8-23)
+ */
+ /* Maybe HALT should only be used for SRM console boots? */
pld &= 0x0000000000ffffc0UL;
-#endif
+#endif /* SX164 */
/*
* Now for every possible bit set, work through them and call
@@ -962,10 +1279,9 @@ noritake_device_interrupt(unsigned long vector, struct pt_regs *regs)
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
- /* read the interrupt summary registers of NORITAKE */
+ /* Read the interrupt summary registers of NORITAKE */
pld = ((unsigned long) inw(0x54c) << 32) |
((unsigned long) inw(0x54a) << 16) |
((unsigned long) inb(0xa0) << 8) |
@@ -991,16 +1307,76 @@ noritake_device_interrupt(unsigned long vector, struct pt_regs *regs)
restore_flags(flags);
}
+#if defined(CONFIG_ALPHA_DP264)
+/* we have to conditionally compile this because of TSUNAMI_xxx symbols */
+static inline void dp264_device_interrupt(unsigned long vector,
+ struct pt_regs * regs)
+{
+ unsigned long pld, tmp;
+ unsigned int i;
+ unsigned long flags;
+
+ __save_and_cli(flags);
+
+ /* Read the interrupt summary register of TSUNAMI */
+ pld = (*(vulp)TSUNAMI_CSR_DIR0);
+
+#if 0
+ printk("[0x%08lx/0x%08lx/0x%04x]", pld,
+ *(vulp)TSUNAMI_CSR_DIM0,
+ inb(0x20) | (inb(0xA0) << 8));
+#endif
+
+ /*
+ * Now for every possible bit set, work through them and call
+ * the appropriate interrupt handler.
+ */
+ while (pld) {
+ i = ffz(~pld);
+ pld &= pld - 1; /* clear least bit set */
+ if (i == 55) {
+ isa_device_interrupt(vector, regs);
+ } else { /* if not timer int */
+ device_interrupt(16 + i, 16 + i, regs);
+ }
+#if 0
+ *(vulp)TSUNAMI_CSR_DIR0 = 1UL << i; mb();
+ tmp = *(vulp)TSUNAMI_CSR_DIR0;
+#endif
+ }
+ __restore_flags(flags);
+}
+#endif /* DP264 */
+
+#if defined(CONFIG_ALPHA_RAWHIDE)
+/* we have to conditionally compile this because of MCPCIA_xxx symbols */
+static inline void rawhide_device_interrupt(unsigned long vector,
+ struct pt_regs * regs)
+{
+#if 0
+ unsigned long pld;
+ unsigned int i;
+ unsigned long flags;
+
+ __save_and_cli(flags);
+
+ /* PLACEHOLDER, perhaps never used if we always do SRM */
+
+ __restore_flags(flags);
+#endif
+}
+#endif /* RAWHIDE */
+
#if defined(CONFIG_ALPHA_RUFFIAN)
static inline void
ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs)
+
{
unsigned long pld;
unsigned int i;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
/* Read the interrupt summary register of PYXIS */
pld = *(vulp)PYXIS_INT_REQ;
@@ -1010,16 +1386,16 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs)
* then all the PCI slots/INTXs (12-31)
* flash(5) :DWH:
*/
- pld &= 0x00000000ffffff9fUL;
+ pld &= 0x00000000ffffff9fUL;/* was ffff7f */
/*
* Now for every possible bit set, work through them and call
* the appropriate interrupt handler.
*/
+
while (pld) {
i = ffz(~pld);
pld &= pld - 1; /* clear least bit set */
-
if (i == 7) {
/* Copy this bit from isa_device_interrupt cause
we need to hook into int 0 for the timer. I
@@ -1041,19 +1417,57 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs)
} else {
device_interrupt(j, j, regs);
}
- } else {
+ } else { /* if not timer int */
device_interrupt(16 + i, 16 + i, regs);
}
- *(vulp)PYXIS_INT_REQ = 1UL << i;
- mb();
- *(vulp)PYXIS_INT_REQ;
+ *(vulp)PYXIS_INT_REQ = 1UL << i; mb();
+ *(vulp)PYXIS_INT_REQ; /* read to force the write */
}
-
restore_flags(flags);
}
#endif /* RUFFIAN */
+static inline void takara_device_interrupt(unsigned long vector,
+ struct pt_regs * regs)
+{
+ unsigned long flags;
+ unsigned intstatus;
+
+ save_and_cli(flags);
+
+ /*
+ * The PALcode will have passed us vectors 0x800 or 0x810,
+ * which are fairly arbitrary values and serve only to tell
+ * us whether an interrupt has come in on IRQ0 or IRQ1. If
+ * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's
+ * probably ISA, but PCI interrupts can come through IRQ0
+ * as well if the interrupt controller isn't in accelerated
+ * mode.
+ *
+ * OTOH, the accelerator thing doesn't seem to be working
+ * overly well, so what we'll do instead is try directly
+ * examining the Master Interrupt Register to see if it's a
+ * PCI interrupt, and if _not_ then we'll pass it on to the
+ * ISA handler.
+ */
+
+ intstatus = inw(0x500) & 15;
+ if (intstatus) {
+ /*
+ * This is a PCI interrupt. Check each bit and
+ * despatch an interrupt if it's set.
+ */
+ if (intstatus & 8) device_interrupt(16+3, 16+3, regs);
+ if (intstatus & 4) device_interrupt(16+2, 16+2, regs);
+ if (intstatus & 2) device_interrupt(16+1, 16+1, regs);
+ if (intstatus & 1) device_interrupt(16+0, 16+0, regs);
+ } else
+ isa_device_interrupt (vector, regs);
+
+ restore_flags(flags);
+}
+
#endif /* CONFIG_PCI */
/*
@@ -1085,9 +1499,11 @@ srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
int irq, ack;
unsigned long flags;
- save_flags(flags);
- cli();
+ __save_and_cli(flags);
+#ifdef __SMP__
+if (smp_processor_id()) printk("srm_device_interrupt on other CPU\n");
+#endif
ack = irq = (vector - 0x800) >> 4;
@@ -1131,9 +1547,9 @@ srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
#ifdef CONFIG_ALPHA_NORITAKE
/*
- * I really hate to do this, but the NORITAKE SRM console reports
- * PCI vectors *lower* than I expected from the bit numbering in
- * the documentation.
+ * I really hate to do this, too, but the NORITAKE SRM console also
+ * reports PCI vectors *lower* than I expected from the bit numbers
+ * in the documentation.
* But I really don't want to change the fixup code for allocation
* of IRQs, nor the irq_mask maintenance stuff, both of which look
* nice and clean now.
@@ -1153,9 +1569,41 @@ srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
#endif
#endif /* CONFIG_ALPHA_SABLE */
+#ifdef CONFIG_ALPHA_DP264
+ /*
+ * the DP264 SRM console reports PCI interrupts with a vector
+ * 0x100 *higher* than one might expect, as PCI IRQ 0 (ie bit 0)
+ * shows up as IRQ 16, etc, etc. We adjust it down by 16 to have
+ * it line up with the actual bit numbers from the DIM registers,
+ * which is how we manage the interrupts/mask. Sigh...
+ */
+ if (irq >= 32)
+ ack = irq = irq - 16;
+#endif /* DP264 */
+
+#ifdef CONFIG_ALPHA_RAWHIDE
+ /*
+ * the RAWHIDE SRM console reports PCI interrupts with a vector
+ * 0x80 *higher* than one might expect, as PCI IRQ 0 (ie bit 0)
+ * shows up as IRQ 24, etc, etc. We adjust it down by 8 to have
+ * it line up with the actual bit numbers from the REQ registers,
+ * which is how we manage the interrupts/mask. Sigh...
+ *
+ * also, PCI #1 interrupts are offset some more... :-(
+ */
+ if (irq == 52)
+ ack = irq = 56; /* SCSI on PCI 1 is special */
+ else {
+ if (irq >= 24) /* adjust all PCI interrupts down 8 */
+ ack = irq = irq - 8;
+ if (irq >= 48) /* adjust PCI bus 1 interrupts down another 8 */
+ ack = irq = irq - 8;
+ }
+#endif /* RAWHIDE */
+
device_interrupt(irq, ack, regs);
- restore_flags(flags);
+ __restore_flags(flags);
}
/*
@@ -1218,6 +1666,10 @@ extern void pyxis_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
extern void t2_machine_check(unsigned long vector, unsigned long la,
struct pt_regs * regs);
+extern void tsunami_machine_check(unsigned long vector, unsigned long la,
+ struct pt_regs * regs);
+extern void mcpcia_machine_check(unsigned long vector, unsigned long la,
+ struct pt_regs * regs);
static void
machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs)
@@ -1232,6 +1684,10 @@ machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs)
pyxis_machine_check(vector, la, regs);
#elif defined(CONFIG_ALPHA_T2)
t2_machine_check(vector, la, regs);
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+ tsunami_machine_check(vector, la, regs);
+#elif defined(CONFIG_ALPHA_MCPCIA)
+ mcpcia_machine_check(vector, la, regs);
#else
printk("Machine check\n");
#endif
@@ -1244,7 +1700,14 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
{
switch (type) {
case 0:
+#ifdef __SMP__
+/* irq_enter(smp_processor_id(), 0); ??????? */
+ handle_ipi(&regs);
+/* irq_exit(smp_processor_id(), 0); ??????? */
+ return;
+#else /* __SMP__ */
printk("Interprocessor interrupt? You must be kidding\n");
+#endif /* __SMP__ */
break;
case 1:
handle_irq(RTC_IRQ, &regs);
@@ -1253,23 +1716,38 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
machine_check(vector, la_ptr, &regs);
return;
case 3:
-#if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \
- defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM)
+#if defined(CONFIG_ALPHA_JENSEN) || \
+ defined(CONFIG_ALPHA_NONAME) || \
+ defined(CONFIG_ALPHA_P2K) || \
+ defined(CONFIG_ALPHA_SRM)
srm_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164)
+#elif defined(CONFIG_ALPHA_MIATA) || \
+ defined(CONFIG_ALPHA_SX164)
miata_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_NORITAKE)
noritake_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+#elif defined(CONFIG_ALPHA_ALCOR) || \
+ defined(CONFIG_ALPHA_XLT)
alcor_and_xlt_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_RUFFIAN)
- ruffian_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_CABRIOLET) || \
+ defined(CONFIG_ALPHA_EB66P) || \
+ defined(CONFIG_ALPHA_EB164) || \
+ defined(CONFIG_ALPHA_PC164) || \
+ defined(CONFIG_ALPHA_LX164)
+ cabriolet_and_eb66p_device_interrupt(vector, &regs);
#elif defined(CONFIG_ALPHA_MIKASA)
mikasa_device_interrupt(vector, &regs);
-#elif NR_IRQS == 33
- cabriolet_and_eb66p_device_interrupt(vector, &regs);
-#elif NR_IRQS == 32
+#elif defined(CONFIG_ALPHA_EB66) || \
+ defined(CONFIG_ALPHA_EB64P)
eb66_and_eb64p_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+ ruffian_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_DP264)
+ dp264_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+ rawhide_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_TAKARA)
+ takara_device_interrupt(vector, &regs);
#elif NR_IRQS == 16
isa_device_interrupt(vector, &regs);
#endif
@@ -1293,22 +1771,21 @@ static inline void sable_init_IRQ(void)
outb(0x44, 0x535); /* enable cascades in master */
}
-#ifdef CONFIG_ALPHA_SX164
+#if defined(CONFIG_ALPHA_SX164)
static inline void sx164_init_IRQ(void)
{
+#if !defined(CONFIG_ALPHA_SRM)
/* note invert on MASK bits */
*(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb();
-#if 0
- *(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb(); /* ISA/NMI HI */
- *(vulp)PYXIS_RT_COUNT = 0UL; mb(); /* clear count */
-#endif
+ *(vulp)PYXIS_INT_MASK;
+#endif /* !SRM */
enable_irq(16 + 6); /* enable timer */
enable_irq(16 + 7); /* enable ISA PIC cascade */
enable_irq(2); /* enable cascade */
}
#endif /* SX164 */
-#ifdef CONFIG_ALPHA_RUFFIAN
+#if defined(CONFIG_ALPHA_RUFFIAN)
static inline void ruffian_init_IRQ(void)
{
/* invert 6&7 for i82371 */
@@ -1343,19 +1820,19 @@ static inline void ruffian_init_IRQ(void)
}
#endif /* RUFFIAN */
-
#ifdef CONFIG_ALPHA_MIATA
static inline void miata_init_IRQ(void)
{
/* note invert on MASK bits */
*(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); /* invert */
+#if 0
+ /* these break on MiataGL so we'll try not to do it at all */
*(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb(); /* ISA/NMI HI */
*(vulp)PYXIS_RT_COUNT = 0UL; mb(); /* clear count */
- *(vulp)PYXIS_INT_REQ = 0x4000000000000000UL; mb(); /* clear upper timer */
-#if 0
- *(vulp)PYXIS_INT_ROUTE = 0UL; mb(); /* all are level */
- *(vulp)PYXIS_INT_CNFG = 0UL; mb(); /* all clear */
#endif
+ /* clear upper timer */
+ *(vulp)PYXIS_INT_REQ = 0x4000000000000000UL; mb();
+
enable_irq(16 + 2); /* enable HALT switch - SRM only? */
enable_irq(16 + 6); /* enable timer */
enable_irq(16 + 7); /* enable ISA PIC cascade */
@@ -1381,7 +1858,7 @@ static inline void alcor_and_xlt_init_IRQ(void)
enable_irq(16 + 31); /* enable (E)ISA PIC cascade */
enable_irq(2); /* enable cascade */
}
-#endif
+#endif /* ALCOR || XLT */
static inline void mikasa_init_IRQ(void)
{
@@ -1389,9 +1866,56 @@ static inline void mikasa_init_IRQ(void)
enable_irq(2); /* enable cascade */
}
-static inline void init_IRQ_33(void)
+#if defined(CONFIG_ALPHA_DP264)
+static inline void dp264_init_IRQ(void)
+{
+ /* note invert on MASK bits */
+ *(vulp)TSUNAMI_CSR_DIM0 =
+ ~(irq_mask) & ~0x0000000000000000UL; mb();
+ *(vulp)TSUNAMI_CSR_DIM0;
+ enable_irq(55); /* enable CYPRESS interrupt controller (ISA) */
+ enable_irq(2);
+}
+#endif /* DP264 */
+
+#if defined(CONFIG_ALPHA_RAWHIDE)
+static inline void rawhide_init_IRQ(void)
{
+ /* HACK ALERT! only PCI busses 0 and 1 are used currently,
+ and routing is only to CPU #1*/
+
+ *(vuip)MCPCIA_INT_MASK0(0) =
+ (~((irq_mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb();
+ /* ... and read it back to make sure it got written. */
+ *(vuip)MCPCIA_INT_MASK0(0);
+
+ *(vuip)MCPCIA_INT_MASK0(1) =
+ (~((irq_mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb();
+ /* ... and read it back to make sure it got written. */
+ *(vuip)MCPCIA_INT_MASK0(1);
+ enable_irq(2);
+}
+#endif /* RAWHIDE */
+
+static inline void takara_init_IRQ(void)
+{
+ unsigned int ctlreg = inl(0x500);
+
+ ctlreg &= ~0x8000; /* return to non-accelerated mode */
+ outw(ctlreg >> 16, 0x502);
+ outw(ctlreg & 0xFFFF, 0x500);
+ ctlreg = 0x05107c00; /* enable the PCI interrupt register */
+ printk("Setting to 0x%08x\n", ctlreg);
+ outw(ctlreg >> 16, 0x502);
+ outw(ctlreg & 0xFFFF, 0x500);
+ enable_irq(2);
+}
+
+static inline void init_IRQ_35(void)
+{
+#if !defined(CONFIG_ALPHA_SRM)
outl(irq_mask >> 16, 0x804);
+#endif /* !SRM */
enable_irq(16 + 4); /* enable SIO cascade */
enable_irq(2); /* enable cascade */
}
@@ -1413,13 +1937,20 @@ void __init
init_IRQ(void)
{
wrent(entInt, 0);
- dma_outb(0, DMA1_RESET_REG);
- dma_outb(0, DMA2_RESET_REG);
-#ifndef CONFIG_ALPHA_SX164
- dma_outb(0, DMA1_CLR_MASK_REG);
- /* We need to figure out why this fails on the SX164. */
- dma_outb(0, DMA2_CLR_MASK_REG);
-#endif
+
+/* FIXME FIXME FIXME FIXME FIXME */
+#if !defined(CONFIG_ALPHA_DP264)
+ /* we need to figure out why these fail on the DP264 */
+ outb(0, DMA1_RESET_REG);
+ outb(0, DMA2_RESET_REG);
+#endif /* !DP264 */
+/* FIXME FIXME FIXME FIXME FIXME */
+#if !defined(CONFIG_ALPHA_SX164) && !defined(CONFIG_ALPHA_DP264)
+ outb(0, DMA1_CLR_MASK_REG);
+ /* we need to figure out why this fails on the SX164 */
+ outb(0, DMA2_CLR_MASK_REG);
+#endif /* !SX164 && !DP264 */
+/* end FIXMEs */
#if defined(CONFIG_ALPHA_SABLE)
sable_init_IRQ();
@@ -1431,17 +1962,21 @@ init_IRQ(void)
noritake_init_IRQ();
#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
alcor_and_xlt_init_IRQ();
-#elif (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \
- && defined(CONFIG_ALPHA_SRM)
- /* Disable all the PCI interrupts? Otherwise, everthing was
- done by SRM already. */
#elif defined(CONFIG_ALPHA_MIKASA)
mikasa_init_IRQ();
+#elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \
+ defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) || \
+ defined(CONFIG_ALPHA_EB164)
+ init_IRQ_35();
#elif defined(CONFIG_ALPHA_RUFFIAN)
ruffian_init_IRQ();
-#elif NR_IRQS == 33
- init_IRQ_33();
-#elif NR_IRQS == 32
+#elif defined(CONFIG_ALPHA_DP264)
+ dp264_init_IRQ();
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+ rawhide_init_IRQ();
+#elif defined(CONFIG_ALPHA_TAKARA)
+ takara_init_IRQ();
+#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P)
init_IRQ_32();
#elif NR_IRQS == 16
init_IRQ_16();
diff --git a/arch/alpha/kernel/lca.c b/arch/alpha/kernel/lca.c
index 2a39a1cf9..a0b8aea8d 100644
--- a/arch/alpha/kernel/lca.c
+++ b/arch/alpha/kernel/lca.c
@@ -6,8 +6,8 @@
* bios code.
*/
#include <linux/kernel.h>
+#include <linux/config.h>
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <asm/ptrace.h>
@@ -44,6 +44,11 @@
#define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */
#define MCHK_K_DCSR 0x208 /* all but Noname */
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int LCA_DMA_WIN_BASE = LCA_DMA_WIN_BASE_DEFAULT;
+unsigned int LCA_DMA_WIN_SIZE = LCA_DMA_WIN_SIZE_DEFAULT;
+#endif /* SRM_SETUP */
+
/*
* Given a bus, device, and function number, compute resulting
* configuration space address and setup the LCA_IOC_CONF register
@@ -100,11 +105,11 @@ static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
return -1;
}
- *((vulp) LCA_IOC_CONF) = 0;
+ *(vulp)LCA_IOC_CONF = 0;
addr = (1 << (11 + device)) | (func << 8) | where;
} else {
/* type 1 configuration cycle: */
- *((vulp) LCA_IOC_CONF) = 1;
+ *(vulp)LCA_IOC_CONF = 1;
addr = (bus << 16) | (device_fn << 8) | where;
}
*pci_addr = addr;
@@ -130,7 +135,7 @@ static unsigned int conf_read(unsigned long addr)
value = *(vuip)addr;
draina();
- stat0 = *((unsigned long*)LCA_IOC_STAT0);
+ stat0 = *(vulp)LCA_IOC_STAT0;
if (stat0 & LCA_IOC_STAT0_ERR) {
code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
& LCA_IOC_STAT0_CODE_MASK);
@@ -167,7 +172,7 @@ static void conf_write(unsigned long addr, unsigned int value)
*(vuip)addr = value;
draina();
- stat0 = *((unsigned long*)LCA_IOC_STAT0);
+ stat0 = *(vulp)LCA_IOC_STAT0;
if (stat0 & LCA_IOC_STAT0_ERR) {
code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
& LCA_IOC_STAT0_CODE_MASK);
@@ -287,6 +292,40 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned long lca_init(unsigned long mem_start, unsigned long mem_end)
{
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 0 for enabled and mapped to 0 */
+ if ((*(vulp)LCA_IOC_W_BASE0 & (1UL<<33)) &&
+ (*(vulp)LCA_IOC_T_BASE0 == 0))
+ {
+ LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE0 & 0xffffffffUL;
+ LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK0 & 0xffffffffUL;
+ LCA_DMA_WIN_SIZE += 1;
+#if 1
+ printk("lca_init: using Window 0 settings\n");
+ printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+ *(vulp)LCA_IOC_W_BASE0,
+ *(vulp)LCA_IOC_W_MASK0,
+ *(vulp)LCA_IOC_T_BASE0);
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if ((*(vulp)LCA_IOC_W_BASE1 & (1UL<<33)) &&
+ (*(vulp)LCA_IOC_T_BASE1 == 0))
+ {
+ LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE1 & 0xffffffffUL;
+ LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK1 & 0xffffffffUL;
+ LCA_DMA_WIN_SIZE += 1;
+#if 1
+ printk("lca_init: using Window 1 settings\n");
+ printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+ *(vulp)LCA_IOC_W_BASE1,
+ *(vulp)LCA_IOC_W_MASK1,
+ *(vulp)LCA_IOC_T_BASE1);
+#endif
+ }
+ else /* we must use our defaults... */
+#endif /* SRM_SETUP */
+ {
/*
* Set up the PCI->physical memory translation windows.
* For now, window 1 is disabled. In the future, we may
@@ -294,9 +333,11 @@ unsigned long lca_init(unsigned long mem_start, unsigned long mem_end)
* goes at 1 GB and is 1 GB large.
*/
*(vulp)LCA_IOC_W_BASE1 = 0UL<<33;
+
*(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE;
*(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1;
*(vulp)LCA_IOC_T_BASE0 = 0;
+ }
/*
* Disable PCI parity for now. The NCR53c810 chip has
diff --git a/arch/alpha/kernel/mcpcia.c b/arch/alpha/kernel/mcpcia.c
new file mode 100644
index 000000000..6a9dab59a
--- /dev/null
+++ b/arch/alpha/kernel/mcpcia.c
@@ -0,0 +1,977 @@
+/*
+ * Code common to all MCbus-PCI adaptor chipsets
+ *
+ * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+#include <asm/delay.h>
+
+/*
+ * NOTE: Herein lie back-to-back mb instructions. They are magic.
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction. Another involves timing. Ho hum.
+ */
+
+extern struct hwrpb_struct *hwrpb;
+extern asmlinkage void wrmces(unsigned long mces);
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef CONFIG_ALPHA_MCPCIA
+
+#undef DEBUG_CFG
+
+#ifdef DEBUG_CFG
+# define DBG_CFG(args) printk args
+#else
+# define DBG_CFG(args)
+#endif
+
+#undef DEBUG_PCI
+
+#ifdef DEBUG_PCI
+# define DBG_PCI(args) printk args
+#else
+# define DBG_PCI(args)
+#endif
+
+#define DEBUG_MCHECK
+
+#ifdef DEBUG_MCHECK
+# define DBG_MCK(args) printk args
+# define DEBUG_MCHECK_DUMP
+#else
+# define DBG_MCK(args)
+#endif
+
+#define vuip volatile unsigned int *
+#define vulp volatile unsigned long *
+
+static volatile unsigned int MCPCIA_mcheck_expected[NR_CPUS];
+static volatile unsigned int MCPCIA_mcheck_taken[NR_CPUS];
+static unsigned int MCPCIA_jd[NR_CPUS];
+
+#define MCPCIA_MAX_HOSES 2
+static int mcpcia_num_hoses = 0;
+
+static int pci_probe_enabled = 0; /* disable to start */
+
+static struct linux_hose_info *mcpcia_root = NULL, *mcpcia_last_hose;
+
+struct linux_hose_info *bus2hose[256];
+
+static inline unsigned long long_align(unsigned long addr)
+{
+ return ((addr + (sizeof(unsigned long) - 1)) &
+ ~(sizeof(unsigned long) - 1));
+}
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int MCPCIA_DMA_WIN_BASE = MCPCIA_DMA_WIN_BASE_DEFAULT;
+unsigned int MCPCIA_DMA_WIN_SIZE = MCPCIA_DMA_WIN_SIZE_DEFAULT;
+unsigned long mcpcia_sm_base_r1, mcpcia_sm_base_r2, mcpcia_sm_base_r3;
+#endif /* SRM_SETUP */
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address and setup the MCPCIA_HAXR2 register
+ * accordingly. It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Type 0:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:11 Device select bit.
+ * 10:8 Function number
+ * 7:2 Register number
+ *
+ * Type 1:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:24 reserved
+ * 23:16 bus number (8 bits = 128 possible buses)
+ * 15:11 Device number (5 bits)
+ * 10:8 function number
+ * 7:2 register number
+ *
+ * Notes:
+ * The function number selects which function of a multi-function device
+ * (e.g., scsi and ethernet).
+ *
+ * The register selects a DWORD (32 bit) register offset. Hence it
+ * doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ * bits.
+ */
+
+static unsigned int
+conf_read(unsigned long addr, unsigned char type1,
+ struct linux_hose_info *hose)
+{
+ unsigned long flags;
+ unsigned long hoseno = hose->pci_hose_index;
+ unsigned int stat0, value, temp, cpu;
+
+ cpu = smp_processor_id();
+
+ save_and_cli(flags);
+
+ DBG_CFG(("conf_read(addr=0x%lx, type1=%d, hose=%d)\n",
+ addr, type1, hoseno));
+
+ /* reset status register to avoid losing errors: */
+ stat0 = *(vuip)MCPCIA_CAP_ERR(hoseno);
+ *(vuip)MCPCIA_CAP_ERR(hoseno) = stat0; mb();
+ temp = *(vuip)MCPCIA_CAP_ERR(hoseno);
+ DBG_CFG(("conf_read: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0));
+
+ mb();
+ draina();
+ MCPCIA_mcheck_expected[cpu] = 1;
+ MCPCIA_mcheck_taken[cpu] = 0;
+ mb();
+ /* access configuration space: */
+ value = *((vuip)addr);
+ mb();
+ mb(); /* magic */
+ if (MCPCIA_mcheck_taken[cpu]) {
+ MCPCIA_mcheck_taken[cpu] = 0;
+ value = 0xffffffffU;
+ mb();
+ }
+ MCPCIA_mcheck_expected[cpu] = 0;
+ mb();
+
+ DBG_CFG(("conf_read(): finished\n"));
+
+ restore_flags(flags);
+ return value;
+}
+
+
+static void
+conf_write(unsigned long addr, unsigned int value, unsigned char type1,
+ struct linux_hose_info *hose)
+{
+ unsigned long flags;
+ unsigned long hoseno = hose->pci_hose_index;
+ unsigned int stat0, temp, cpu;
+
+ cpu = smp_processor_id();
+
+ save_and_cli(flags); /* avoid getting hit by machine check */
+
+ /* reset status register to avoid losing errors: */
+ stat0 = *(vuip)MCPCIA_CAP_ERR(hoseno);
+ *(vuip)MCPCIA_CAP_ERR(hoseno) = stat0; mb();
+ temp = *(vuip)MCPCIA_CAP_ERR(hoseno);
+ DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0));
+
+ draina();
+ MCPCIA_mcheck_expected[cpu] = 1;
+ mb();
+ /* access configuration space: */
+ *((vuip)addr) = value;
+ mb();
+ mb(); /* magic */
+ temp = *(vuip)MCPCIA_CAP_ERR(hoseno); /* read to force the write */
+ MCPCIA_mcheck_expected[cpu] = 0;
+ mb();
+
+ DBG_CFG(("conf_write(): finished\n"));
+ restore_flags(flags);
+}
+
+static int mk_conf_addr(struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned long *pci_addr,
+ unsigned char *type1)
+{
+ unsigned long addr;
+
+ if (!pci_probe_enabled) /* if doing standard pci_init(), ignore */
+ return -1;
+
+ DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
+ " pci_addr=0x%p, type1=0x%p)\n",
+ bus, device_fn, where, pci_addr, type1));
+
+ /* type 1 configuration cycle for *ALL* busses */
+ *type1 = 1;
+
+ if (hose->pci_first_busno == bus)
+ bus = 0;
+ addr = (bus << 16) | (device_fn << 8) | (where);
+ addr <<= 5; /* swizzle for SPARSE */
+ addr |= hose->pci_config_space;
+
+ *pci_addr = addr;
+ DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+ return 0;
+}
+
+
+int hose_read_config_byte (struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char *value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ *value = 0xff;
+
+ if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ addr |= 0x00; /* or in length */
+
+ *value = conf_read(addr, type1, hose) >> ((where & 3) * 8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_read_config_word (struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short *value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ *value = 0xffff;
+
+ if (where & 0x1) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1)) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ addr |= 0x08; /* or in length */
+
+ *value = conf_read(addr, type1, hose) >> ((where & 3) * 8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_read_config_dword (struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned int *value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ *value = 0xffffffff;
+
+ if (where & 0x3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1)) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+ addr |= 0x18; /* or in length */
+
+ *value = conf_read(addr, type1, hose);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_write_config_byte (struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ addr |= 0x00; /* or in length */
+
+ conf_write(addr, value << ((where & 3) * 8), type1, hose);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_write_config_word (struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ addr |= 0x08; /* or in length */
+
+ conf_write(addr, value << ((where & 3) * 8), type1, hose);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_write_config_dword (struct linux_hose_info *hose,
+ unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned int value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ addr |= 0x18; /* or in length */
+
+ conf_write(addr, value << ((where & 3) * 8), type1, hose);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned char *value)
+{
+ return hose_read_config_byte(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_read_config_word (unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned short *value)
+{
+ return hose_read_config_word(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned int *value)
+{
+ return hose_read_config_dword(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned char value)
+{
+ return hose_write_config_byte(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_word (unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned short value)
+{
+ return hose_write_config_word(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned int value)
+{
+ return hose_write_config_dword(bus2hose[bus], bus, devfn, where, value);
+}
+
+unsigned long mcpcia_init(unsigned long mem_start, unsigned long mem_end)
+{
+ struct linux_hose_info *hose;
+ unsigned int mcpcia_err;
+ unsigned int pci_rev;
+ int h;
+
+ mem_start = long_align(mem_start);
+
+ for (h = 0; h < NR_CPUS; h++) {
+ MCPCIA_mcheck_expected[h] = 0;
+ MCPCIA_mcheck_taken[h] = 0;
+ }
+
+ /* first, find how many hoses we have */
+ for (h = 0; h < MCPCIA_MAX_HOSES; h++) {
+ pci_rev = *(vuip)MCPCIA_REV(h);
+#if 0
+ printk("mcpcia_init: got 0x%x for PCI_REV for hose %d\n",
+ pci_rev, h);
+#endif
+ if ((pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST) {
+ mcpcia_num_hoses++;
+
+ hose = (struct linux_hose_info *)mem_start;
+ mem_start = long_align(mem_start + sizeof(*hose));
+
+ memset(hose, 0, sizeof(*hose));
+
+ if (mcpcia_root)
+ mcpcia_last_hose->next = hose;
+ else
+ mcpcia_root = hose;
+ mcpcia_last_hose = hose;
+
+ hose->pci_io_space = MCPCIA_IO(h);
+ hose->pci_mem_space = MCPCIA_DENSE(h);
+ hose->pci_config_space = MCPCIA_CONF(h);
+ hose->pci_sparse_space = MCPCIA_SPARSE(h);
+ hose->pci_hose_index = h;
+ hose->pci_first_busno = 255;
+ hose->pci_last_busno = 0;
+ }
+ }
+
+#if 1
+ printk("mcpcia_init: found %d hoses\n", mcpcia_num_hoses);
+#endif
+
+ /* now do init for each hose */
+ for (hose = mcpcia_root; hose; hose = hose->next) {
+ h = hose->pci_hose_index;
+#if 0
+#define PRINTK printk
+PRINTK("mcpcia_init: -------- hose %d --------\n",h);
+PRINTK("mcpcia_init: MCPCIA_REV 0x%x\n", *(vuip)MCPCIA_REV(h));
+PRINTK("mcpcia_init: MCPCIA_WHOAMI 0x%x\n", *(vuip)MCPCIA_WHOAMI(h));
+PRINTK("mcpcia_init: MCPCIA_HAE_MEM 0x%x\n", *(vuip)MCPCIA_HAE_MEM(h));
+PRINTK("mcpcia_init: MCPCIA_HAE_IO 0x%x\n", *(vuip)MCPCIA_HAE_IO(h));
+PRINTK("mcpcia_init: MCPCIA_HAE_DENSE 0x%x\n", *(vuip)MCPCIA_HAE_DENSE(h));
+PRINTK("mcpcia_init: MCPCIA_INT_CTL 0x%x\n", *(vuip)MCPCIA_INT_CTL(h));
+PRINTK("mcpcia_init: MCPCIA_INT_REQ 0x%x\n", *(vuip)MCPCIA_INT_REQ(h));
+PRINTK("mcpcia_init: MCPCIA_INT_TARG 0x%x\n", *(vuip)MCPCIA_INT_TARG(h));
+PRINTK("mcpcia_init: MCPCIA_INT_ADR 0x%x\n", *(vuip)MCPCIA_INT_ADR(h));
+PRINTK("mcpcia_init: MCPCIA_INT_ADR_EXT 0x%x\n", *(vuip)MCPCIA_INT_ADR_EXT(h));
+PRINTK("mcpcia_init: MCPCIA_INT_MASK0 0x%x\n", *(vuip)MCPCIA_INT_MASK0(h));
+PRINTK("mcpcia_init: MCPCIA_INT_MASK1 0x%x\n", *(vuip)MCPCIA_INT_MASK1(h));
+PRINTK("mcpcia_init: MCPCIA_HBASE 0x%x\n", *(vuip)MCPCIA_HBASE(h));
+#endif
+
+ /*
+ * Set up error reporting. Make sure CPU_PE is OFF in the mask.
+ */
+#if 0
+ mcpcia_err = *(vuip)MCPCIA_ERR_MASK(h);
+ mcpcia_err &= ~4;
+ *(vuip)MCPCIA_ERR_MASK(h) = mcpcia_err;
+ mb();
+ mcpcia_err = *(vuip)MCPCIA_ERR_MASK;
+#endif
+
+ mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h);
+ mcpcia_err |= 0x0006; /* master/target abort */
+ *(vuip)MCPCIA_CAP_ERR(h) = mcpcia_err;
+ mb() ;
+ mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h);
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 0 for enabled and mapped to 0 */
+ if (((*(vuip)MCPCIA_W0_BASE(h) & 3) == 1) &&
+ (*(vuip)MCPCIA_T0_BASE(h) == 0) &&
+ ((*(vuip)MCPCIA_W0_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+ {
+ MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W0_BASE(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W0_MASK(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("mcpcia_init: using Window 0 settings\n");
+ printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)MCPCIA_W0_BASE(h),
+ *(vuip)MCPCIA_W0_MASK(h),
+ *(vuip)MCPCIA_T0_BASE(h));
+#endif
+ }
+ else /* check window 1 for enabled and mapped to 0 */
+ if (((*(vuip)MCPCIA_W1_BASE(h) & 3) == 1) &&
+ (*(vuip)MCPCIA_T1_BASE(h) == 0) &&
+ ((*(vuip)MCPCIA_W1_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+{
+ MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W1_BASE(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W1_MASK(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("mcpcia_init: using Window 1 settings\n");
+ printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)MCPCIA_W1_BASE(h),
+ *(vuip)MCPCIA_W1_MASK(h),
+ *(vuip)MCPCIA_T1_BASE(h));
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if (((*(vuip)MCPCIA_W2_BASE(h) & 3) == 1) &&
+ (*(vuip)MCPCIA_T2_BASE(h) == 0) &&
+ ((*(vuip)MCPCIA_W2_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+ {
+ MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W2_BASE(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W2_MASK(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("mcpcia_init: using Window 2 settings\n");
+ printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)MCPCIA_W2_BASE(h),
+ *(vuip)MCPCIA_W2_MASK(h),
+ *(vuip)MCPCIA_T2_BASE(h));
+#endif
+ }
+ else /* check window 3 for enabled and mapped to 0 */
+ if (((*(vuip)MCPCIA_W3_BASE(h) & 3) == 1) &&
+ (*(vuip)MCPCIA_T3_BASE(h) == 0) &&
+ ((*(vuip)MCPCIA_W3_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+ {
+ MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W3_BASE(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W3_MASK(h) & 0xfff00000U;
+ MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("mcpcia_init: using Window 3 settings\n");
+ printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)MCPCIA_W3_BASE(h),
+ *(vuip)MCPCIA_W3_MASK(h),
+ *(vuip)MCPCIA_T3_BASE(h));
+#endif
+ }
+ else /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+ {
+ /*
+ * Set up the PCI->physical memory translation windows.
+ * For now, windows 1,2 and 3 are disabled. In the future, we may
+ * want to use them to do scatter/gather DMA. Window 0
+ * goes at 1 GB and is 1 GB large.
+ */
+
+ *(vuip)MCPCIA_W0_BASE(h) = 1U | (MCPCIA_DMA_WIN_BASE & 0xfff00000U);
+ *(vuip)MCPCIA_W0_MASK(h) = (MCPCIA_DMA_WIN_SIZE - 1) & 0xfff00000U;
+ *(vuip)MCPCIA_T0_BASE(h) = 0;
+
+ *(vuip)MCPCIA_W1_BASE(h) = 0x0 ;
+ *(vuip)MCPCIA_W2_BASE(h) = 0x0 ;
+ *(vuip)MCPCIA_W3_BASE(h) = 0x0 ;
+
+ *(vuip)MCPCIA_HBASE(h) = 0x0 ;
+ mb();
+ }
+
+ /*
+ * check ASN in HWRPB for validity, report if bad
+ */
+ if (hwrpb->max_asn != MAX_ASN) {
+ printk("mcpcia_init: max ASN from HWRPB is bad (0x%lx)\n",
+ hwrpb->max_asn);
+ hwrpb->max_asn = MAX_ASN;
+ }
+
+#if 0
+ {
+ unsigned int mcpcia_int_ctl = *((vuip)MCPCIA_INT_CTL(h));
+ printk("mcpcia_init: INT_CTL was 0x%x\n", mcpcia_int_ctl);
+ *(vuip)MCPCIA_INT_CTL(h) = 1U; mb();
+ mcpcia_int_ctl = *(vuip)MCPCIA_INT_CTL(h);
+ }
+#endif
+
+ {
+ unsigned int mcpcia_hae_mem = *(vuip)MCPCIA_HAE_MEM(h);
+ unsigned int mcpcia_hae_io = *(vuip)MCPCIA_HAE_IO(h);
+#if 0
+ printk("mcpcia_init: HAE_MEM was 0x%x\n", mcpcia_hae_mem);
+ printk("mcpcia_init: HAE_IO was 0x%x\n", mcpcia_hae_io);
+#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /*
+ sigh... For the SRM setup, unless we know apriori what the HAE
+ contents will be, we need to setup the arbitrary region bases
+ so we can test against the range of addresses and tailor the
+ region chosen for the SPARSE memory access.
+
+ see include/asm-alpha/mcpcia.h for the SPARSE mem read/write
+ */
+ mcpcia_sm_base_r1 = (mcpcia_hae_mem ) & 0xe0000000UL;/* reg 1 */
+ mcpcia_sm_base_r2 = (mcpcia_hae_mem << 16) & 0xf8000000UL;/* reg 2 */
+ mcpcia_sm_base_r3 = (mcpcia_hae_mem << 24) & 0xfc000000UL;/* reg 3 */
+ /*
+ Set the HAE cache, so that setup_arch() code
+ will use the SRM setting always. Our readb/writeb
+ code in mcpcia.h expects never to have to change
+ the contents of the HAE.
+ */
+ hae.cache = mcpcia_hae_mem;
+#else /* SRM_SETUP */
+ *(vuip)MCPCIA_HAE_MEM(h) = 0U; mb();
+ mcpcia_hae_mem = *(vuip)MCPCIA_HAE_MEM(h);
+ *(vuip)MCPCIA_HAE_IO(h) = 0; mb();
+ mcpcia_hae_io = *(vuip)MCPCIA_HAE_IO(h);
+#endif /* SRM_SETUP */
+ }
+ } /* end for-loop on hoses */
+ return mem_start;
+}
+
+int mcpcia_pci_clr_err(int h)
+{
+ unsigned int cpu = smp_processor_id();
+
+ MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h);
+#if 0
+ DBG_MCK(("MCPCIA_pci_clr_err: MCPCIA CAP_ERR(%d) after read 0x%x\n",
+ h, MCPCIA_jd[cpu]));
+#endif
+ *(vuip)MCPCIA_CAP_ERR(h) = 0xffffffff; mb(); /* clear them all */
+ MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h);
+ return 0;
+}
+
+static void
+mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout)
+{
+ struct el_common_EV5_uncorrectable_mcheck *frame;
+ int i;
+
+ frame = &logout->procdata;
+
+ /* Print PAL fields */
+ for (i = 0; i < 24; i += 2) {
+ printk("\tpal temp[%d-%d]\t\t= %16lx %16lx\n\r",
+ i, i+1, frame->paltemp[i], frame->paltemp[i+1]);
+ }
+ for (i = 0; i < 8; i += 2) {
+ printk("\tshadow[%d-%d]\t\t= %16lx %16lx\n\r",
+ i, i+1, frame->shadow[i],
+ frame->shadow[i+1]);
+ }
+ printk("\tAddr of excepting instruction\t= %16lx\n\r",
+ frame->exc_addr);
+ printk("\tSummary of arithmetic traps\t= %16lx\n\r",
+ frame->exc_sum);
+ printk("\tException mask\t\t\t= %16lx\n\r",
+ frame->exc_mask);
+ printk("\tBase address for PALcode\t= %16lx\n\r",
+ frame->pal_base);
+ printk("\tInterrupt Status Reg\t\t= %16lx\n\r",
+ frame->isr);
+ printk("\tCURRENT SETUP OF EV5 IBOX\t= %16lx\n\r",
+ frame->icsr);
+ printk("\tI-CACHE Reg %s parity error\t= %16lx\n\r",
+ (frame->ic_perr_stat & 0x800L) ?
+ "Data" : "Tag",
+ frame->ic_perr_stat);
+ printk("\tD-CACHE error Reg\t\t= %16lx\n\r",
+ frame->dc_perr_stat);
+ if (frame->dc_perr_stat & 0x2) {
+ switch (frame->dc_perr_stat & 0x03c) {
+ case 8:
+ printk("\t\tData error in bank 1\n\r");
+ break;
+ case 4:
+ printk("\t\tData error in bank 0\n\r");
+ break;
+ case 20:
+ printk("\t\tTag error in bank 1\n\r");
+ break;
+ case 10:
+ printk("\t\tTag error in bank 0\n\r");
+ break;
+ }
+ }
+ printk("\tEffective VA\t\t\t= %16lx\n\r",
+ frame->va);
+ printk("\tReason for D-stream\t\t= %16lx\n\r",
+ frame->mm_stat);
+ printk("\tEV5 SCache address\t\t= %16lx\n\r",
+ frame->sc_addr);
+ printk("\tEV5 SCache TAG/Data parity\t= %16lx\n\r",
+ frame->sc_stat);
+ printk("\tEV5 BC_TAG_ADDR\t\t\t= %16lx\n\r",
+ frame->bc_tag_addr);
+ printk("\tEV5 EI_ADDR: Phys addr of Xfer\t= %16lx\n\r",
+ frame->ei_addr);
+ printk("\tFill Syndrome\t\t\t= %16lx\n\r",
+ frame->fill_syndrome);
+ printk("\tEI_STAT reg\t\t\t= %16lx\n\r",
+ frame->ei_stat);
+ printk("\tLD_LOCK\t\t\t\t= %16lx\n\r",
+ frame->ld_lock);
+}
+
+void mcpcia_machine_check(unsigned long type, unsigned long la_ptr,
+ struct pt_regs * regs)
+{
+#if 0
+ printk("mcpcia machine check ignored\n") ;
+#else
+ struct el_common *mchk_header;
+ struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout;
+ unsigned int cpu = smp_processor_id();
+ int h = 0;
+
+ mchk_header = (struct el_common *)la_ptr;
+ mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr;
+
+#if 0
+ DBG_MCK(("mcpcia_machine_check: type=0x%lx la_ptr=0x%lx\n",
+ type, la_ptr));
+ DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+ regs->pc, mchk_header->size, mchk_header->proc_offset,
+ mchk_header->sys_offset));
+#endif
+ /*
+ * Check if machine check is due to a badaddr() and if so,
+ * ignore the machine check.
+ */
+ mb();
+ mb(); /* magic */
+ if (MCPCIA_mcheck_expected[cpu]) {
+#if 0
+ DBG_MCK(("MCPCIA machine check expected\n"));
+#endif
+ MCPCIA_mcheck_expected[cpu] = 0;
+ MCPCIA_mcheck_taken[cpu] = 1;
+ mb();
+ mb(); /* magic */
+ draina();
+ mcpcia_pci_clr_err(h);
+ wrmces(0x7);
+ mb();
+ }
+#if 1
+ else {
+ printk("MCPCIA machine check NOT expected on CPU %d\n", cpu);
+ DBG_MCK(("mcpcia_machine_check: type=0x%lx pc=0x%lx"
+ " code=0x%lx\n",
+ type, regs->pc, mchk_header->code));
+
+ MCPCIA_mcheck_expected[cpu] = 0;
+ MCPCIA_mcheck_taken[cpu] = 1;
+ mb();
+ mb(); /* magic */
+ draina();
+ mcpcia_pci_clr_err(h);
+ wrmces(0x7);
+ mb();
+#ifdef DEBUG_MCHECK_DUMP
+ if (type == 0x620)
+ printk("MCPCIA machine check: system CORRECTABLE!\n");
+ else if (type == 0x630)
+ printk("MCPCIA machine check: processor CORRECTABLE!\n");
+ else
+ mcpcia_print_uncorrectable(mchk_logout);
+#endif /* DEBUG_MCHECK_DUMP */
+ }
+#endif
+#endif
+}
+
+/*==========================================================================*/
+
+#define PRIMARY(b) ((b)&0xff)
+#define SECONDARY(b) (((b)>>8)&0xff)
+#define SUBORDINATE(b) (((b)>>16)&0xff)
+
+static int
+hose_scan_bridges(struct linux_hose_info *hose, unsigned char bus)
+{
+ unsigned int devfn, l, class;
+ unsigned char hdr_type = 0;
+ unsigned int found = 0;
+
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) == 0) {
+ hose_read_config_byte(hose, bus, devfn,
+ PCI_HEADER_TYPE, &hdr_type);
+ } else if (!(hdr_type & 0x80)) {
+ /* not a multi-function device */
+ continue;
+ }
+
+ /* Check if there is anything here. */
+ hose_read_config_dword(hose, bus, devfn, PCI_VENDOR_ID, &l);
+ if (l == 0xffffffff || l == 0x00000000) {
+ hdr_type = 0;
+ continue;
+ }
+
+ /* See if this is a bridge device. */
+ hose_read_config_dword(hose, bus, devfn,
+ PCI_CLASS_REVISION, &class);
+
+ if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int busses;
+
+ found++;
+
+ hose_read_config_dword(hose, bus, devfn,
+ PCI_PRIMARY_BUS, &busses);
+
+DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d busses 0x%x\n",
+ hose->pci_hose_index, bus, PCI_SLOT(devfn), busses));
+ /*
+ * do something with first_busno and last_busno
+ */
+ if (hose->pci_first_busno > PRIMARY(busses)) {
+ hose->pci_first_busno = PRIMARY(busses);
+DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d change first to %d\n",
+ hose->pci_hose_index, bus, PCI_SLOT(devfn), PRIMARY(busses)));
+ }
+ if (hose->pci_last_busno < SUBORDINATE(busses)) {
+ hose->pci_last_busno = SUBORDINATE(busses);
+DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d change last to %d\n",
+ hose->pci_hose_index, bus, PCI_SLOT(devfn), SUBORDINATE(busses)));
+ }
+ /*
+ * Now scan everything underneath the bridge.
+ */
+ hose_scan_bridges(hose, SECONDARY(busses));
+ }
+ }
+ return found;
+}
+
+static void
+hose_reconfigure_bridges(struct linux_hose_info *hose, unsigned char bus)
+{
+ unsigned int devfn, l, class;
+ unsigned char hdr_type = 0;
+
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) == 0) {
+ hose_read_config_byte(hose, bus, devfn,
+ PCI_HEADER_TYPE, &hdr_type);
+ } else if (!(hdr_type & 0x80)) {
+ /* not a multi-function device */
+ continue;
+ }
+
+ /* Check if there is anything here. */
+ hose_read_config_dword(hose, bus, devfn, PCI_VENDOR_ID, &l);
+ if (l == 0xffffffff || l == 0x00000000) {
+ hdr_type = 0;
+ continue;
+ }
+
+ /* See if this is a bridge device. */
+ hose_read_config_dword(hose, bus, devfn,
+ PCI_CLASS_REVISION, &class);
+
+ if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int busses;
+
+ hose_read_config_dword(hose, bus, devfn,
+ PCI_PRIMARY_BUS, &busses);
+
+ /*
+ * First reconfigure everything underneath the bridge.
+ */
+ hose_reconfigure_bridges(hose, (busses >> 8) & 0xff);
+
+ /*
+ * Unconfigure this bridges bus numbers,
+ * pci_scan_bus() will fix this up properly.
+ */
+ busses &= 0xff000000;
+ hose_write_config_dword(hose, bus, devfn,
+ PCI_PRIMARY_BUS, busses);
+ }
+ }
+}
+
+static void mcpcia_fixup_busno(struct linux_hose_info *hose, unsigned char bus)
+{
+ unsigned int nbus;
+
+ /*
+ * First, scan for all bridge devices underneath this hose,
+ * to determine the first and last busnos.
+ */
+ if (!hose_scan_bridges(hose, 0)) {
+ /* none found, exit */
+ hose->pci_first_busno = bus;
+ hose->pci_last_busno = bus;
+ } else {
+ /*
+ * Reconfigure all bridge devices underneath this hose.
+ */
+ hose_reconfigure_bridges(hose, hose->pci_first_busno);
+ }
+
+ /*
+ * Now reconfigure the hose to it's new bus number and set up
+ * our bus2hose mapping for this hose.
+ */
+ nbus = hose->pci_last_busno - hose->pci_first_busno;
+
+ hose->pci_first_busno = bus;
+
+DBG_PCI(("mcpcia_fixup_busno: hose %d startbus %d nbus %d\n",
+ hose->pci_hose_index, bus, nbus));
+
+ do {
+ bus2hose[bus++] = hose;
+ } while (nbus-- > 0);
+}
+
+static void mcpcia_probe(struct linux_hose_info *hose,
+ unsigned long *mem_start)
+{
+ static struct pci_bus *pchain = NULL;
+ struct pci_bus *pbus = &hose->pci_bus;
+ static unsigned char busno = 0;
+
+ /* Hoses include child PCI bridges in bus-range property,
+ * but we don't scan each of those ourselves, Linux generic PCI
+ * probing code will find child bridges and link them into this
+ * hose's root PCI device hierarchy.
+ */
+
+ pbus->number = pbus->secondary = busno;
+ pbus->sysdata = hose;
+
+ mcpcia_fixup_busno(hose, busno);
+
+ pbus->subordinate = pci_scan_bus(pbus, mem_start); /* the original! */
+
+ /*
+ * Set the maximum subordinate bus of this hose.
+ */
+ hose->pci_last_busno = pbus->subordinate;
+#if 0
+ hose_write_config_byte(hose, busno, 0, 0x41, hose->pci_last_busno);
+#endif
+ busno = pbus->subordinate + 1;
+
+ /*
+ * Fixup the chain of primary PCI busses.
+ */
+ if (pchain) {
+ pchain->next = &hose->pci_bus;
+ pchain = pchain->next;
+ } else {
+ pchain = &pci_root;
+ memcpy(pchain, &hose->pci_bus, sizeof(pci_root));
+ }
+}
+
+void mcpcia_fixup(void)
+{
+ struct linux_hose_info *hose;
+
+ /* turn on Config space access finally! */
+ pci_probe_enabled = 1;
+
+ /* for each hose, probe and setup the devices on the hose */
+ for (hose = mcpcia_root; hose; hose = hose->next) {
+ mcpcia_probe(hose, &memory_start);
+ }
+}
+#endif /* CONFIG_ALPHA_MCPCIA */
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index f8146c54f..401a7af46 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -66,10 +66,49 @@ asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
struct pt_regs regs)
{
+#if !defined(CONFIG_ALPHA_TSUNAMI)
(&regs)->hae = hae;
+#endif
return 0;
}
+#ifdef __SMP__
+/* This is being executed in task 0 'user space'. */
+#define resched_needed() 1
+int cpu_idle(void *unused)
+{
+ extern volatile int smp_commenced;
+
+ current->priority = -100;
+ while (1) {
+ /*
+ * tq_scheduler currently assumes we're running in a process
+ * context (ie that we hold the kernel lock..)
+ */
+ if (tq_scheduler) {
+ lock_kernel();
+ run_task_queue(&tq_scheduler);
+ unlock_kernel();
+ }
+ /* endless idle loop with no priority at all */
+ current->counter = -100;
+ if (!smp_commenced || resched_needed()) {
+ schedule();
+ }
+ }
+}
+
+asmlinkage int sys_idle(void)
+{
+ if(current->pid != 0)
+ return -EPERM;
+
+ cpu_idle(NULL);
+ return 0;
+}
+
+#else /* __SMP__ */
+
asmlinkage int sys_idle(void)
{
int ret = -EPERM;
@@ -88,6 +127,12 @@ out:
unlock_kernel();
return ret;
}
+#endif /* __SMP__ */
+
+#if defined(CONFIG_ALPHA_SRM_SETUP)
+extern void reset_for_srm(void);
+extern unsigned long srm_hae;
+#endif
static void finish_shutdown(void)
{
@@ -96,8 +141,8 @@ static void finish_shutdown(void)
unsigned long flags;
/* i'm not sure if i really need to disable interrupts here */
- save_flags(flags);
- cli();
+ save_and_cli(flags);
+
/* reset periodic interrupt frequency */
CMOS_WRITE(0x26, RTC_FREQ_SELECT);
@@ -131,6 +176,10 @@ void machine_restart(char * __unused)
/* flags |= 0x0000000000030000UL; *//* this is "warm bootstrap" */
cpup->flags = flags;
mb();
+#if defined(CONFIG_ALPHA_SRM_SETUP)
+ reset_for_srm();
+ set_hae(srm_hae);
+#endif
#endif /* SRM */
finish_shutdown();
@@ -150,6 +199,10 @@ void machine_halt(void)
flags |= 0x0000000000040000UL; /* this is "remain halted" */
cpup->flags = flags;
mb();
+#if defined(CONFIG_ALPHA_SRM_SETUP)
+ reset_for_srm();
+ set_hae(srm_hae);
+#endif
finish_shutdown();
#endif /* SRM */
@@ -228,6 +281,7 @@ int alpha_clone(unsigned long clone_flags, unsigned long usp,
}
extern void ret_from_sys_call(void);
+extern void ret_from_smpfork(void);
/*
* Copy an alpha thread..
*
@@ -258,7 +312,11 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
stack = ((struct switch_stack *) regs) - 1;
childstack = ((struct switch_stack *) childregs) - 1;
*childstack = *stack;
+#ifdef __SMP__
+ childstack->r26 = (unsigned long) ret_from_smpfork;
+#else
childstack->r26 = (unsigned long) ret_from_sys_call;
+#endif
p->tss.usp = usp;
p->tss.ksp = (unsigned long) childstack;
p->tss.pal_flags = 1; /* set FEN, clear everything else */
diff --git a/arch/alpha/kernel/pyxis.c b/arch/alpha/kernel/pyxis.c
index b4c5d188e..de3814b66 100644
--- a/arch/alpha/kernel/pyxis.c
+++ b/arch/alpha/kernel/pyxis.c
@@ -7,7 +7,6 @@
#include <linux/config.h> /* CONFIG_ALPHA_RUFFIAN. */
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/sched.h>
@@ -17,13 +16,12 @@
#include <asm/ptrace.h>
#include <asm/mmu_context.h>
-/* NOTE: Herein are back-to-back mb insns. They are magic.
- A plausible explanation is that the i/o controler does not properly
+/* NOTE: Herein are back-to-back mb instructions. They are magic.
+ One plausible explanation is that the I/O controller does not properly
handle the system transaction. Another involves timing. Ho hum. */
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
-extern int alpha_sys_type;
/*
* BIOS32-style PCI interface:
@@ -38,6 +36,7 @@ extern int alpha_sys_type;
#define DEBUG_MCHECK
#ifdef DEBUG_MCHECK
# define DBG_MCK(args) printk args
+#define DEBUG_MCHECK_DUMP
#else
# define DBG_MCK(args)
#endif
@@ -49,6 +48,11 @@ static volatile unsigned int PYXIS_mcheck_expected = 0;
static volatile unsigned int PYXIS_mcheck_taken = 0;
static unsigned int PYXIS_jd;
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int PYXIS_DMA_WIN_BASE = PYXIS_DMA_WIN_BASE_DEFAULT;
+unsigned int PYXIS_DMA_WIN_SIZE = PYXIS_DMA_WIN_SIZE_DEFAULT;
+unsigned long pyxis_sm_base_r1, pyxis_sm_base_r2, pyxis_sm_base_r3;
+#endif /* SRM_SETUP */
/*
* Given a bus, device, and function number, compute resulting
@@ -129,24 +133,23 @@ static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
static unsigned int conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
- unsigned int stat0, value;
+ unsigned int stat0, value, temp;
unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
- save_flags(flags); /* avoid getting hit by machine check */
- cli();
+ save_and_cli(flags); /* avoid getting hit by machine check */
DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
/* reset status register to avoid losing errors: */
stat0 = *(vuip)PYXIS_ERR;
- *(vuip)PYXIS_ERR = stat0;
- mb();
+ *(vuip)PYXIS_ERR = stat0; mb();
+ temp = *(vuip)PYXIS_ERR; /* re-read to force write */
DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0));
/* if Type1 access, must set PYXIS CFG */
if (type1) {
pyxis_cfg = *(vuip)PYXIS_CFG;
- *(vuip)PYXIS_CFG = pyxis_cfg | 1;
- mb();
+ *(vuip)PYXIS_CFG = pyxis_cfg | 1; mb();
+ temp = *(vuip)PYXIS_CFG; /* re-read to force write */
DBG(("conf_read: TYPE1 access\n"));
}
@@ -166,36 +169,11 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1)
}
PYXIS_mcheck_expected = 0;
mb();
- /*
- * david.rusling@reo.mts.dec.com. This code is needed for the
- * EB64+ as it does not generate a machine check (why I don't
- * know). When we build kernels for one particular platform
- * then we can make this conditional on the type.
- */
-#if 0
- draina();
-
- /* now look for any errors */
- stat0 = *(vuip)PYXIS_IOC_PYXIS_ERR;
- DBG(("conf_read: PYXIS ERR after read 0x%x\n", stat0));
- if (stat0 & 0x8280U) { /* is any error bit set? */
- /* if not NDEV, print status */
- if (!(stat0 & 0x0080)) {
- printk("PYXIS.c:conf_read: got stat0=%x\n", stat0);
- }
-
- /* reset error status: */
- *(vulp)PYXIS_IOC_PYXIS_ERR = stat0;
- mb();
- wrmces(0x7); /* reset machine check */
- value = 0xffffffff;
- }
-#endif
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
- *(vuip)PYXIS_CFG = pyxis_cfg & ~1;
- mb();
+ *(vuip)PYXIS_CFG = pyxis_cfg & ~1; mb();
+ temp = *(vuip)PYXIS_CFG; /* re-read to force write */
}
DBG(("conf_read(): finished\n"));
@@ -209,22 +187,21 @@ static void conf_write(unsigned long addr, unsigned int value,
unsigned char type1)
{
unsigned long flags;
- unsigned int stat0;
+ unsigned int stat0, temp;
unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
- save_flags(flags); /* avoid getting hit by machine check */
- cli();
+ save_and_cli(flags); /* avoid getting hit by machine check */
/* reset status register to avoid losing errors: */
stat0 = *(vuip)PYXIS_ERR;
- *(vuip)PYXIS_ERR = stat0;
- mb();
+ *(vuip)PYXIS_ERR = stat0; mb();
+ temp = *(vuip)PYXIS_ERR; /* re-read to force write */
DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0));
/* if Type1 access, must set PYXIS CFG */
if (type1) {
pyxis_cfg = *(vuip)PYXIS_CFG;
- *(vuip)PYXIS_CFG = pyxis_cfg | 1;
- mb();
+ *(vuip)PYXIS_CFG = pyxis_cfg | 1; mb();
+ temp = *(vuip)PYXIS_CFG; /* re-read to force write */
DBG(("conf_read: TYPE1 access\n"));
}
@@ -235,13 +212,14 @@ static void conf_write(unsigned long addr, unsigned int value,
*(vuip)addr = value;
mb();
mb(); /* magic */
+ temp = *(vuip)PYXIS_ERR; /* do a PYXIS read to force the write */
PYXIS_mcheck_expected = 0;
mb();
/* if Type1 access, must reset IOC CFG so normal IO space ops work */
if (type1) {
- *(vuip)PYXIS_CFG = pyxis_cfg & ~1;
- mb();
+ *(vuip)PYXIS_CFG = pyxis_cfg & ~1; mb();
+ temp = *(vuip)PYXIS_CFG; /* re-read to force write */
}
DBG(("conf_write(): finished\n"));
@@ -367,19 +345,105 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end)
{
unsigned int pyxis_err ;
+#if 0
+printk("pyxis_init: PYXIS_ERR_MASK 0x%x\n", *(vuip)PYXIS_ERR_MASK);
+printk("pyxis_init: PYXIS_ERR 0x%x\n", *(vuip)PYXIS_ERR);
+
+printk("pyxis_init: PYXIS_INT_REQ 0x%lx\n", *(vulp)PYXIS_INT_REQ);
+printk("pyxis_init: PYXIS_INT_MASK 0x%lx\n", *(vulp)PYXIS_INT_MASK);
+printk("pyxis_init: PYXIS_INT_ROUTE 0x%lx\n", *(vulp)PYXIS_INT_ROUTE);
+printk("pyxis_init: PYXIS_INT_HILO 0x%lx\n", *(vulp)PYXIS_INT_HILO);
+printk("pyxis_init: PYXIS_INT_CNFG 0x%x\n", *(vuip)PYXIS_INT_CNFG);
+printk("pyxis_init: PYXIS_RT_COUNT 0x%lx\n", *(vulp)PYXIS_RT_COUNT);
+#endif
+
/*
- * Set up error reporting.
+ * Set up error reporting. Make sure CPU_PE is OFF in the mask.
*/
+ pyxis_err = *(vuip)PYXIS_ERR_MASK;
+ pyxis_err &= ~4;
+ *(vuip)PYXIS_ERR_MASK = pyxis_err; mb();
+ pyxis_err = *(vuip)PYXIS_ERR_MASK; /* re-read to force write */
+
pyxis_err = *(vuip)PYXIS_ERR ;
pyxis_err |= 0x180; /* master/target abort */
- *(vuip)PYXIS_ERR = pyxis_err ;
- mb() ;
- pyxis_err = *(vuip)PYXIS_ERR ;
-
-#ifdef CONFIG_ALPHA_RUFFIAN
- printk("pyxis_init: Skipping window register rewrites --"
+ *(vuip)PYXIS_ERR = pyxis_err; mb();
+ pyxis_err = *(vuip)PYXIS_ERR; /* re-read to force write */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 0 for enabled and mapped to 0 */
+ if (((*(vuip)PYXIS_W0_BASE & 3) == 1) &&
+ (*(vuip)PYXIS_T0_BASE == 0) &&
+ ((*(vuip)PYXIS_W0_MASK & 0xfff00000U) > 0x0ff00000U))
+ {
+ PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W0_BASE & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W0_MASK & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("pyxis_init: using Window 0 settings\n");
+ printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)PYXIS_W0_BASE,
+ *(vuip)PYXIS_W0_MASK,
+ *(vuip)PYXIS_T0_BASE);
+#endif
+ }
+ else /* check window 1 for enabled and mapped to 0 */
+ if (((*(vuip)PYXIS_W1_BASE & 3) == 1) &&
+ (*(vuip)PYXIS_T1_BASE == 0) &&
+ ((*(vuip)PYXIS_W1_MASK & 0xfff00000U) > 0x0ff00000U))
+{
+ PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W1_BASE & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W1_MASK & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("pyxis_init: using Window 1 settings\n");
+ printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)PYXIS_W1_BASE,
+ *(vuip)PYXIS_W1_MASK,
+ *(vuip)PYXIS_T1_BASE);
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if (((*(vuip)PYXIS_W2_BASE & 3) == 1) &&
+ (*(vuip)PYXIS_T2_BASE == 0) &&
+ ((*(vuip)PYXIS_W2_MASK & 0xfff00000U) > 0x0ff00000U))
+ {
+ PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W2_BASE & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W2_MASK & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("pyxis_init: using Window 2 settings\n");
+ printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)PYXIS_W2_BASE,
+ *(vuip)PYXIS_W2_MASK,
+ *(vuip)PYXIS_T2_BASE);
+#endif
+ }
+ else /* check window 3 for enabled and mapped to 0 */
+ if (((*(vuip)PYXIS_W3_BASE & 3) == 1) &&
+ (*(vuip)PYXIS_T3_BASE == 0) &&
+ ((*(vuip)PYXIS_W3_MASK & 0xfff00000U) > 0x0ff00000U))
+ {
+ PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W3_BASE & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W3_MASK & 0xfff00000U;
+ PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("pyxis_init: using Window 3 settings\n");
+ printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vuip)PYXIS_W3_BASE,
+ *(vuip)PYXIS_W3_MASK,
+ *(vuip)PYXIS_T3_BASE);
+#endif
+ }
+ else /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+ {
+#if defined(CONFIG_ALPHA_RUFFIAN)
+#if 1
+ printk("pyxis_init: skipping window register rewrites... "
" trust DeskStation firmware!\n");
-#else
+#endif
+#else /* RUFFIAN */
/*
* Set up the PCI->physical memory translation windows.
* For now, windows 1,2 and 3 are disabled. In the future, we may
@@ -395,7 +459,8 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end)
*(vuip)PYXIS_W2_BASE = 0x0 ;
*(vuip)PYXIS_W3_BASE = 0x0 ;
mb();
-#endif
+#endif /* RUFFIAN */
+ }
/*
* check ASN in HWRPB for validity, report if bad
@@ -407,18 +472,21 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end)
}
/*
- * Finally, clear the PYXIS_CFG register, which gets used
+ * Next, clear the PYXIS_CFG register, which gets used
* for PCI Config Space accesses. That is the way
* we want to use it, and we do not want to depend on
* what ARC or SRM might have left behind...
*/
{
- unsigned int pyxis_cfg;
+ unsigned int pyxis_cfg, temp;
pyxis_cfg = *(vuip)PYXIS_CFG; mb();
-#if 0
+ if (pyxis_cfg != 0) {
+#if 1
printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg);
#endif
*(vuip)PYXIS_CFG = 0; mb();
+ temp = *(vuip)PYXIS_CFG; /* re-read to force write */
+ }
}
{
@@ -428,10 +496,48 @@ unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end)
printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem);
printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io);
#endif
- *(vuip)PYXIS_HAE_MEM = 0; mb();
- pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM;
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /*
+ * sigh... For the SRM setup, unless we know apriori what the HAE
+ * contents will be, we need to setup the arbitrary region bases
+ * so we can test against the range of addresses and tailor the
+ * region chosen for the SPARSE memory access.
+ *
+ * see include/asm-alpha/pyxis.h for the SPARSE mem read/write
+ */
+ pyxis_sm_base_r1 = (pyxis_hae_mem ) & 0xe0000000UL;/* region 1 */
+ pyxis_sm_base_r2 = (pyxis_hae_mem << 16) & 0xf8000000UL;/* region 2 */
+ pyxis_sm_base_r3 = (pyxis_hae_mem << 24) & 0xfc000000UL;/* region 3 */
+
+ /*
+ Set the HAE cache, so that setup_arch() code
+ will use the SRM setting always. Our readb/writeb
+ code in pyxis.h expects never to have to change
+ the contents of the HAE.
+ */
+ hae.cache = pyxis_hae_mem;
+#else /* SRM_SETUP */
+ *(vuip)PYXIS_HAE_MEM = 0U; mb();
+ pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM; /* re-read to force write */
*(vuip)PYXIS_HAE_IO = 0; mb();
- pyxis_hae_io = *(vuip)PYXIS_HAE_IO;
+ pyxis_hae_io = *(vuip)PYXIS_HAE_IO; /* re-read to force write */
+#endif /* SRM_SETUP */
+ }
+
+ /*
+ * Finally, check that the PYXIS_CTRL1 has IOA_BEN set for
+ * enabling byte/word PCI bus space(s) access.
+ */
+ {
+ unsigned int ctrl1;
+ ctrl1 = *(vuip) PYXIS_CTRL1;
+ if (!(ctrl1 & 1)) {
+#if 1
+ printk("PYXIS_init: enabling byte/word PCI space\n");
+#endif
+ *(vuip) PYXIS_CTRL1 = ctrl1 | 1; mb();
+ ctrl1 = *(vuip)PYXIS_CTRL1; /* re-read to force write */
+ }
}
return mem_start;
@@ -441,9 +547,8 @@ int pyxis_pci_clr_err(void)
{
PYXIS_jd = *(vuip)PYXIS_ERR;
DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd));
- *(vuip)PYXIS_ERR = 0x0180;
- mb();
- PYXIS_jd = *(vuip)PYXIS_ERR;
+ *(vuip)PYXIS_ERR = 0x0180; mb();
+ PYXIS_jd = *(vuip)PYXIS_ERR; /* re-read to force write */
return 0;
}
@@ -486,7 +591,7 @@ void pyxis_machine_check(unsigned long vector, unsigned long la_ptr,
*/
mb();
mb(); /* magic */
- if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
+ if (PYXIS_mcheck_expected) {
DBG(("PYXIS machine check expected\n"));
PYXIS_mcheck_expected = 0;
PYXIS_mcheck_taken = 1;
@@ -502,7 +607,8 @@ void pyxis_machine_check(unsigned long vector, unsigned long la_ptr,
printk("PYXIS machine check NOT expected\n") ;
DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr));
- DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+ DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x"
+ " sysoffset 0x%x\n",
regs->pc, mchk_header->size, mchk_header->proc_offset,
mchk_header->sys_offset));
PYXIS_mcheck_expected = 0;
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index 9ce9c1f17..bcab8667f 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -22,6 +22,7 @@
#include <linux/delay.h>
#include <linux/config.h> /* CONFIG_ALPHA_LCA etc */
#include <linux/ioport.h>
+#include <linux/mc146818rtc.h>
#ifdef CONFIG_RTC
#include <linux/timex.h>
@@ -34,11 +35,24 @@
#include <asm/dma.h>
#include <asm/io.h>
+extern void setup_smp(void);
+extern char *smp_info(void);
+
+#if 1
+# define DBG_SRM(args) printk args
+#else
+# define DBG_SRM(args)
+#endif
+
struct hae hae = {
0,
(unsigned long*) HAE_ADDRESS
};
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned long srm_hae;
+#endif
+
struct hwrpb_struct *hwrpb;
unsigned char aux_device_present = 0xaa;
@@ -106,12 +120,13 @@ static void init_pit (void)
outb(LATCH & 0xff, 0x40); /* LSB */
outb(LATCH >> 8, 0x40); /* MSB */
request_region(0x40, 0x20, "timer"); /* reserve pit */
-#else
-#ifndef CONFIG_ALPHA_RUFFIAN
+#else /* RTC */
+#if !defined(CONFIG_ALPHA_RUFFIAN)
+ /* Ruffian depends on the system timer established in MILO!! */
outb(0x36, 0x43); /* counter 0: system timer */
outb(0x00, 0x40);
outb(0x00, 0x40);
-#endif
+#endif /* RUFFIAN */
request_region(0x70, 0x10, "timer"); /* reserve rtc */
#endif /* RTC */
@@ -148,9 +163,21 @@ void setup_arch(char **cmdline_p,
init_pit();
+ if ((CMOS_READ(RTC_FREQ_SELECT) & 0x3f) != 0x26) {
+ printk("setup_arch: setting RTC_FREQ to 1024/sec\n");
+ CMOS_WRITE(0x26, RTC_FREQ_SELECT);
+ }
+
hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr);
+#if !defined(CONFIG_ALPHA_TSUNAMI)
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ srm_hae = *hae.reg; /* save SRM setting for restoration */
+ DBG_SRM(("setup_arch: old HAE base: 0x%016lx\n", srm_hae));
+#endif /* SRM_SETUP */
set_hae(hae.cache); /* sync HAE register w/hae_cache */
+#endif /* !TSUNAMI */
+
wrmces(0x7); /* reset enable correctable error reports */
ROOT_DEV = to_kdev_t(0x0802); /* sda2 */
@@ -185,12 +212,54 @@ void setup_arch(char **cmdline_p,
*memory_start_p = pyxis_init(*memory_start_p, *memory_end_p);
#elif defined(CONFIG_ALPHA_T2)
*memory_start_p = t2_init(*memory_start_p, *memory_end_p);
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+ *memory_start_p = tsunami_init(*memory_start_p, *memory_end_p);
+#elif defined(CONFIG_ALPHA_MCPCIA)
+ *memory_start_p = mcpcia_init(*memory_start_p, *memory_end_p);
+#endif
+
+#ifdef __SMP__
+ setup_smp();
#endif
}
#define N(a) (sizeof(a)/sizeof(a[0]))
+/* A change was made to the HWRPB via an ECO and the following code tracks
+ * a part of the ECO. The HWRPB version must be 5 or higher or the ECO
+ * was not implemented in the console firmware. If its at rev 5 or greater
+ * we can get the platform ascii string name from the HWRPB. Thats what this
+ * function does. It checks the rev level and if the string is in the HWRPB
+ * it returns the addtess of the string ... a pointer to the platform name.
+ *
+ * Returns:
+ * - Pointer to a ascii string if its in the HWRPB
+ * - Pointer to a blank string if the data is not in the HWRPB.
+ */
+static char *
+platform_string(void)
+{
+ struct dsr_struct *dsr;
+ static char unk_system_string[] = "N/A";
+
+ /* Go to the console for the string pointer.
+ * If the rpb_vers is not 5 or greater the rpb
+ * is old and does not have this data in it.
+ */
+ if (hwrpb->revision < 5)
+ return (unk_system_string);
+ else {
+ /* The Dynamic System Recognition struct
+ * has the system platform name starting
+ * after the character count of the string.
+ */
+ dsr = ((struct dsr_struct *)
+ ((char *)hwrpb + hwrpb->dsr_offset));
+ return ((char *)dsr + (dsr->sysname_off +
+ sizeof(long)));
+ }
+}
static void
get_sysnames(long type, long variation,
@@ -222,6 +291,10 @@ get_sysnames(long type, long variation,
static char * eb66_names[] = {"EB66", "EB66+"};
static int eb66_indices[] = {0,0,1};
+ static char * rawhide_names[] = {"Dodge", "Wrangler", "Durango",
+ "Tincup", "DaVinci"};
+ static int rawhide_indices[] = {0,0,0,1,1,2,2,3,3,4,4};
+
long member;
/* Restore real CABRIO and EB66+ family names, ie EB64+ and EB66 */
@@ -249,7 +322,9 @@ get_sysnames(long type, long variation,
member = (variation >> 10) & 0x3f; /* member ID is a bit-field */
- switch (type) {
+ switch (type) { /* select by family */
+ default: /* default to variation "0" for now */
+ break;
case ST_DEC_EB164:
if (member < N(eb164_indices))
*variation_name = eb164_names[eb164_indices[member]];
@@ -266,7 +341,11 @@ get_sysnames(long type, long variation,
if (member < N(eb66_indices))
*variation_name = eb66_names[eb66_indices[member]];
break;
- }
+ case ST_DEC_RAWHIDE:
+ if (member < N(rawhide_indices))
+ *variation_name = rawhide_names[rawhide_indices[member]];
+ break;
+ } /* end family switch */
}
/*
@@ -315,7 +394,12 @@ int get_cpuinfo(char *buffer)
"max. addr. space #\t: %ld\n"
"BogoMIPS\t\t: %lu.%02lu\n"
"kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
- "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n",
+ "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
+ "platform string\t\t: %s\n"
+#ifdef __SMP__
+ "%s"
+#endif
+ ,
cpu_name, cpu->variation, cpu->revision,
(char*)cpu->serial_no,
@@ -329,5 +413,10 @@ int get_cpuinfo(char *buffer)
hwrpb->max_asn,
loops_per_sec / 500000, (loops_per_sec / 5000) % 100,
unaligned[0].count, unaligned[0].pc, unaligned[0].va,
- unaligned[1].count, unaligned[1].pc, unaligned[1].va);
+ unaligned[1].count, unaligned[1].pc, unaligned[1].va,
+ platform_string()
+#ifdef __SMP__
+ , smp_info()
+#endif
+ );
}
diff --git a/arch/alpha/kernel/smc37c669.c b/arch/alpha/kernel/smc37c669.c
index 6724372fe..bcc4ed212 100644
--- a/arch/alpha/kernel/smc37c669.c
+++ b/arch/alpha/kernel/smc37c669.c
@@ -3,8 +3,6 @@
*/
#include <linux/kernel.h>
-#include <linux/bios32.h>
-#include <linux/pci.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/init.h>
@@ -996,7 +994,7 @@ static SMC37c669_CONFIG_REGS *SMC37c669 __initdata = NULL;
** and standard ISA IRQs.
**
*/
-static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata;
+static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table __initdata = 0;
/*
** The following definition is for the default IRQ
@@ -1022,7 +1020,7 @@ __initdata =
** ISA DMA channels.
**
*/
-static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata;
+static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table __initdata = 0;
/*
** The following definition is the default DRQ
diff --git a/arch/alpha/kernel/smc37c93x.c b/arch/alpha/kernel/smc37c93x.c
index a75998d7e..b0284b032 100644
--- a/arch/alpha/kernel/smc37c93x.c
+++ b/arch/alpha/kernel/smc37c93x.c
@@ -5,8 +5,6 @@
#include <linux/config.h>
#include <linux/kernel.h>
-#include <linux/bios32.h>
-#include <linux/pci.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/init.h>
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
new file mode 100644
index 000000000..fed91a1c1
--- /dev/null
+++ b/arch/alpha/kernel/smp.c
@@ -0,0 +1,1097 @@
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+#define __KERNEL_SYSCALLS__
+#include <asm/unistd.h>
+
+struct ipi_msg_flush_tb_struct ipi_msg_flush_tb;
+
+struct cpuinfo_alpha cpu_data[NR_CPUS];
+
+/* Processor holding kernel spinlock */
+klock_info_t klock_info = { KLOCK_CLEAR, 0 };
+
+spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+unsigned int boot_cpu_id = 0;
+static int smp_activated = 0;
+
+int smp_found_config = 0; /* Have we found an SMP box */
+static int max_cpus = -1;
+
+unsigned int cpu_present_map = 0;
+
+int smp_num_cpus = 1;
+int smp_num_probed = 0; /* Internal processor count */
+
+int smp_threads_ready = 0;
+volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
+
+volatile int ipi_bits[NR_CPUS];
+
+unsigned long boot_cpu_palrev;
+
+volatile int smp_commenced = 0;
+volatile int smp_processors_ready = 0;
+
+volatile int cpu_number_map[NR_CPUS];
+volatile int cpu_logical_map[NR_CPUS];
+
+extern int cpu_idle(void *unused);
+extern void calibrate_delay(void);
+extern struct hwrpb_struct *hwrpb;
+extern struct thread_struct * original_pcb_ptr;
+extern void __start_cpu(unsigned long);
+
+static void smp_setup_percpu_timer(void);
+static void secondary_cpu_start(int, struct task_struct *);
+static void send_cpu_msg(char *, int);
+
+/* process bootcommand SMP options, like "nosmp" and "maxcpus=" */
+__initfunc(void smp_setup(char *str, int *ints))
+{
+ if (ints && ints[0] > 0)
+ max_cpus = ints[1];
+ else
+ max_cpus = 0;
+}
+
+void smp_store_cpu_info(int id)
+{
+ /* This is it on Alpha, so far. */
+ cpu_data[id].loops_per_sec = loops_per_sec;
+}
+
+void smp_commence(void)
+{
+ /* Lets the callin's below out of their loop. */
+ mb();
+ smp_commenced = 1;
+}
+
+void smp_callin(void)
+{
+ int cpuid = hard_smp_processor_id();
+
+#if 0
+ printk("CALLIN %d state 0x%lx\n", cpuid, current->state);
+#endif
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+#if 0
+ set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif
+
+ /* Get our local ticker going. */
+ smp_setup_percpu_timer();
+
+#if 0
+ calibrate_delay();
+#endif
+ smp_store_cpu_info(cpuid);
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+
+ /* Allow master to continue. */
+ set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]);
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+
+#ifdef NOT_YET
+ while(!task[cpuid] || current_set[cpuid] != task[cpuid])
+ barrier();
+#endif /* NOT_YET */
+
+#if 0
+ /* Fix idle thread fields. */
+ __asm__ __volatile__("ld [%0], %%g6\n\t"
+ : : "r" (&current_set[cpuid])
+ : "memory" /* paranoid */);
+ current->mm->mmap->vm_page_prot = PAGE_SHARED;
+ current->mm->mmap->vm_start = PAGE_OFFSET;
+ current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+#endif
+
+#ifdef HUH
+ local_flush_cache_all();
+ local_flush_tlb_all();
+#endif
+#if 0
+ __sti();
+#endif
+}
+
+asmlinkage int start_secondary(void *unused)
+{
+ extern asmlinkage void entInt(void);
+ extern void paging_init_secondary(void);
+
+ wrmces(7);
+ paging_init_secondary();
+ trap_init();
+ wrent(entInt, 0);
+
+ smp_callin();
+ while (!smp_commenced)
+ barrier();
+#if 1
+printk("start_secondary: commencing CPU %d current %p\n",
+ hard_smp_processor_id(), current);
+#endif
+ return cpu_idle(NULL);
+}
+
+/*
+ * Cycle through the processors sending START msgs to boot each.
+ */
+void smp_boot_cpus(void)
+{
+ int cpucount = 0;
+ int i, first, prev;
+
+ printk("smp_boot_cpus: Entering SMP Mode...\n");
+
+#if 0
+ __sti();
+#endif
+
+ for(i=0; i < NR_CPUS; i++) {
+ cpu_number_map[i] = -1;
+ cpu_logical_map[i] = -1;
+ prof_counter[i] = 1;
+ prof_multiplier[i] = 1;
+ ipi_bits[i] = 0;
+ }
+
+ cpu_number_map[boot_cpu_id] = 0;
+ cpu_logical_map[0] = boot_cpu_id;
+ current->processor = boot_cpu_id; /* ??? */
+ klock_info.akp = boot_cpu_id;
+
+ smp_store_cpu_info(boot_cpu_id);
+#ifdef NOT_YET
+ printk("CPU%d: ", boot_cpu_id);
+ print_cpu_info(&cpu_data[boot_cpu_id]);
+ set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif /* NOT_YET */
+ smp_setup_percpu_timer();
+#ifdef HUH
+ local_flush_cache_all();
+#endif
+ if (smp_num_probed == 1)
+ return; /* Not an MP box. */
+
+#if NOT_YET
+ /*
+ * If SMP should be disabled, then really disable it!
+ */
+ if (!max_cpus)
+ {
+ smp_found_config = 0;
+ printk(KERN_INFO "SMP mode deactivated.\n");
+ }
+#endif /* NOT_YET */
+
+ for (i = 0; i < NR_CPUS; i++) {
+
+ if (i == boot_cpu_id)
+ continue;
+
+ if (cpu_present_map & (1 << i)) {
+ struct task_struct *idle;
+ int timeout;
+
+ /* Cook up an idler for this guy. */
+ kernel_thread(start_secondary, NULL, CLONE_PID);
+ idle = task[++cpucount];
+ if (!idle)
+ panic("No idle process for CPU %d", i);
+ idle->processor = i;
+
+#if 0
+printk("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n",
+ i, idle->state, idle->flags);
+#endif
+
+ /* whirrr, whirrr, whirrrrrrrrr... */
+#ifdef HUH
+ local_flush_cache_all();
+#endif
+ secondary_cpu_start(i, idle);
+
+ /* wheee... it's going... wait for 5 secs...*/
+ for (timeout = 0; timeout < 50000; timeout++) {
+ if (cpu_callin_map[i])
+ break;
+ udelay(100);
+ }
+ if (cpu_callin_map[i]) {
+ /* Another "Red Snapper". */
+ cpu_number_map[i] = cpucount;
+ cpu_logical_map[cpucount] = i;
+ } else {
+ cpucount--;
+ printk("smp_boot_cpus: Processor %d"
+ " is stuck 0x%lx.\n", i, idle->flags);
+ }
+ }
+ if (!(cpu_callin_map[i])) {
+ cpu_present_map &= ~(1 << i);
+ cpu_number_map[i] = -1;
+ }
+ }
+#ifdef HUH
+ local_flush_cache_all();
+#endif
+ if (cpucount == 0) {
+ printk("smp_boot_cpus: ERROR - only one Processor found.\n");
+ cpu_present_map = (1 << smp_processor_id());
+ } else {
+ unsigned long bogosum = 0;
+ for (i = 0; i < NR_CPUS; i++) {
+ if (cpu_present_map & (1 << i))
+ bogosum += cpu_data[i].loops_per_sec;
+ }
+ printk("smp_boot_cpus: Total of %d Processors activated"
+ " (%lu.%02lu BogoMIPS).\n",
+ cpucount + 1,
+ (bogosum + 2500)/500000,
+ ((bogosum + 2500)/5000)%100);
+ smp_activated = 1;
+ smp_num_cpus = cpucount + 1;
+ }
+
+ /* Setup CPU list for IRQ distribution scheme. */
+ first = prev = -1;
+ for (i = 0; i < NR_CPUS; i++) {
+ if (cpu_present_map & (1 << i)) {
+ if (first == -1)
+ first = i;
+ if (prev != -1)
+ cpu_data[i].next = i;
+ prev = i;
+ }
+ }
+ cpu_data[prev].next = first;
+
+ /* Ok, they are spinning and ready to go. */
+ smp_processors_ready = 1;
+}
+
+__initfunc(void ioapic_pirq_setup(char *str, int *ints))
+{
+ /* this is prolly INTEL-specific */
+}
+
+static void smp_setup_percpu_timer(void)
+{
+ int cpu = smp_processor_id();
+
+ prof_counter[cpu] = prof_multiplier[cpu] = 1;
+#ifdef NOT_YET
+ load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+ if (cpu == boot_cpu_id)
+ enable_pil_irq(14);
+#endif
+}
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+ unsigned long user, unsigned long system,
+ int cpu);
+
+void smp_percpu_timer_interrupt(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+#ifdef NOT_YET
+ clear_profile_irq(mid_xlate[cpu]);
+#ifdef CONFIG_PROFILE
+ if(!user_mode(regs))
+ sparc_do_profile(regs->pc);
+#endif
+#endif
+
+ if (!--prof_counter[cpu]) {
+ int user = user_mode(regs);
+ if (current->pid) {
+ update_one_process(current, 1, user, !user, cpu);
+
+ if (--current->counter < 0) {
+ current->counter = 0;
+ need_resched = 1;
+ }
+
+ spin_lock(&ticker_lock);
+ if (user) {
+ if (current->priority < DEF_PRIORITY) {
+ kstat.cpu_nice++;
+ kstat.per_cpu_nice[cpu]++;
+ } else {
+ kstat.cpu_user++;
+ kstat.per_cpu_user[cpu]++;
+ }
+ } else {
+ kstat.cpu_system++;
+ kstat.per_cpu_system[cpu]++;
+ }
+ spin_unlock(&ticker_lock);
+ }
+ prof_counter[cpu] = prof_multiplier[cpu];
+ }
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+#ifdef NOT_YET
+ int i;
+ unsigned long flags;
+
+ /* Prevent level14 ticker IRQ flooding. */
+ if((!multiplier) || (lvl14_resolution / multiplier) < 500)
+ return -EINVAL;
+
+ save_and_cli(flags);
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i)) {
+ load_profile_irq(mid_xlate[i], lvl14_resolution / multip
+lier);
+ prof_multiplier[i] = multiplier;
+ }
+ }
+ restore_flags(flags);
+
+ return 0;
+
+#endif
+ return -EINVAL;
+}
+
+/* Only broken Intel needs this, thus it should not even be referenced
+ * globally...
+ */
+__initfunc(void initialize_secondary(void))
+{
+ printk("initialize_secondary: entry\n");
+}
+
+static void
+secondary_cpu_start(int cpuid, struct task_struct *idle)
+{
+ struct percpu_struct *cpu;
+ int timeout;
+
+ cpu = (struct percpu_struct *)
+ ((char*)hwrpb
+ + hwrpb->processor_offset
+ + cpuid * hwrpb->processor_size);
+
+ /* set context to idle thread this CPU will use when running */
+ /* assumption is that the idle thread is all set to go... ??? */
+ memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct));
+ cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */
+#if 0
+printk("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n",
+ cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb);
+printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n",
+ cpuid, idle->state, idle->tss.pal_flags);
+#endif
+
+ /* setup HWRPB fields that SRM uses to activate secondary CPU */
+ hwrpb->CPU_restart = __start_cpu;
+ hwrpb->CPU_restart_data = (unsigned long) idle;
+
+ /* recalculate and update the HWRPB checksum */
+ {
+ unsigned long sum, *lp1, *lp2;
+ sum = 0;
+ lp1 = (unsigned long *)hwrpb;
+ lp2 = &hwrpb->chksum;
+ while (lp1 < lp2)
+ sum += *lp1++;
+ *lp2 = sum;
+ }
+
+ /*
+ * Send a "start" command to the specified processor.
+ */
+
+ /* SRM III 3.4.1.3 */
+ cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */
+ cpu->flags &= ~1;/* turn off Bootstrap In Progress */
+ mb();
+
+ send_cpu_msg("START\r\n", cpuid);
+
+ /* now, we wait... */
+ for (timeout = 10000; !(cpu->flags & 1); timeout--) {
+ if (timeout <= 0) {
+ printk("Processor %d failed to start\n", cpuid);
+ /* needed for pset_info to work */
+#if 0
+ ipc_processor_enable(cpu_to_processor(cpunum));
+#endif
+ return;
+ }
+ udelay(1000);
+ }
+#if 0
+ printk("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid);
+#endif
+}
+
+static void
+send_cpu_msg(char *str, int cpuid)
+{
+ struct percpu_struct *cpu;
+ register char *cp1, *cp2;
+ unsigned long cpumask;
+ int timeout;
+
+
+ cpu = (struct percpu_struct *)
+ ((char*)hwrpb
+ + hwrpb->processor_offset
+ + cpuid * hwrpb->processor_size);
+
+ cpumask = (1L << cpuid);
+ for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
+ if (timeout <= 0) {
+ printk("Processor %x not ready\n", cpuid);
+ return;
+ }
+ udelay(1000);
+ }
+
+ cp1 = (char *) &cpu->ipc_buffer[1];
+ cp2 = str;
+ while (*cp2) *cp1++ = *cp2++;
+ *(unsigned int *)&cpu->ipc_buffer[0] = cp2 - str; /* hack */
+
+ /* atomic test and set */
+ set_bit(cpuid, &hwrpb->rxrdy);
+
+ for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
+ if (timeout <= 0) {
+ printk("Processor %x not ready\n", cpuid);
+ return;
+ }
+ udelay(1000);
+ }
+}
+
+/*
+ * setup_smp()
+ *
+ * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined
+ */
+__initfunc(void setup_smp(void))
+{
+ struct percpu_struct *cpubase, *cpu;
+ int i;
+
+ boot_cpu_id = hard_smp_processor_id();
+ if (boot_cpu_id != 0) {
+ printk("setup_smp: boot_cpu_id != 0 (%d).\n", boot_cpu_id);
+ }
+
+ if (hwrpb->nr_processors > 1) {
+#if 0
+printk("setup_smp: nr_processors 0x%lx\n",
+ hwrpb->nr_processors);
+#endif
+ cpubase = (struct percpu_struct *)
+ ((char*)hwrpb + hwrpb->processor_offset);
+ boot_cpu_palrev = cpubase->pal_revision;
+
+ for (i = 0; i < hwrpb->nr_processors; i++ ) {
+ cpu = (struct percpu_struct *)
+ ((char *)cpubase + i*hwrpb->processor_size);
+ if ((cpu->flags & 0x1cc) == 0x1cc) {
+ smp_num_probed++;
+ /* assume here that "whami" == index */
+ cpu_present_map |= (1 << i);
+ if (i != boot_cpu_id)
+ cpu->pal_revision = boot_cpu_palrev;
+ }
+#if 0
+printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
+ i, cpu->flags, cpu->type);
+ printk("setup_smp: CPU %d: PAL rev 0x%lx\n",
+ i, cpu->pal_revision);
+#endif
+ }
+ } else {
+ smp_num_probed = 1;
+ cpu_present_map = (1 << boot_cpu_id);
+ }
+ printk("setup_smp: %d CPUs probed, cpu_present_map 0x%x,"
+ " boot_cpu_id %d\n",
+ smp_num_probed, cpu_present_map, boot_cpu_id);
+}
+
+static void
+secondary_console_message(void)
+{
+ int mycpu, i, cnt;
+ unsigned long txrdy = hwrpb->txrdy;
+ char *cp1, *cp2, buf[80];
+ struct percpu_struct *cpu;
+
+ mycpu = hard_smp_processor_id();
+
+#if 0
+printk("secondary_console_message: TXRDY 0x%lx.\n", txrdy);
+#endif
+ for (i = 0; i < NR_CPUS; i++) {
+ if (txrdy & (1L << i)) {
+#if 0
+printk("secondary_console_message: TXRDY contains CPU %d.\n", i);
+#endif
+ cpu = (struct percpu_struct *)
+ ((char*)hwrpb
+ + hwrpb->processor_offset
+ + i * hwrpb->processor_size);
+#if 1
+ printk("secondary_console_message: on %d from %d"
+ " HALT_REASON 0x%lx FLAGS 0x%lx\n",
+ mycpu, i, cpu->halt_reason, cpu->flags);
+#endif
+ cnt = cpu->ipc_buffer[0] >> 32;
+ if (cnt <= 0 || cnt >= 80)
+ strcpy(buf,"<<< BOGUS MSG >>>");
+ else {
+ cp1 = (char *) &cpu->ipc_buffer[11];
+ cp2 = buf;
+ while (cnt--) {
+ if (*cp1 == '\r' || *cp1 == '\n') {
+ *cp2++ = ' '; cp1++;
+ } else
+ *cp2++ = *cp1++;
+ }
+ *cp2 = 0;
+ }
+#if 1
+ printk("secondary_console_message: on %d message is '%s'\n",
+ mycpu, buf);
+#endif
+ }
+ }
+ hwrpb->txrdy = 0;
+ return;
+}
+
+static int
+halt_on_panic(unsigned int this_cpu)
+{
+ halt();
+ return 0;
+}
+
+static int
+local_flush_tlb_all(unsigned int this_cpu)
+{
+ tbia();
+ clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+ mb();
+ return 0;
+}
+
+static int
+local_flush_tlb_mm(unsigned int this_cpu)
+{
+ struct mm_struct * mm = ipi_msg_flush_tb.p.flush_mm;
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current(mm);
+ clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+ mb();
+ return 0;
+}
+
+static int
+local_flush_tlb_page(unsigned int this_cpu)
+{
+ struct vm_area_struct * vma = ipi_msg_flush_tb.p.flush_vma;
+ struct mm_struct * mm = vma->vm_mm;
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current_page(mm, vma, ipi_msg_flush_tb.flush_addr);
+ clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+ mb();
+ return 0;
+}
+
+static int
+wrapper_local_flush_tlb_page(unsigned int this_cpu)
+{
+#if 0
+ int cpu = smp_processor_id();
+
+ if (cpu) {
+ printk("wrapper: ipi_msg_flush_tb.flush_addr 0x%lx [%d]\n",
+ ipi_msg_flush_tb.flush_addr, atomic_read(&global_irq_count));
+ }
+#endif
+ local_flush_tlb_page(this_cpu);
+ return 0;
+}
+
+static int
+unknown_ipi(unsigned int this_cpu)
+{
+ printk("unknown_ipi() on cpu %d: ", this_cpu);
+ return 1;
+}
+
+enum ipi_message_type {
+ CPU_STOP,
+ TLB_ALL,
+ TLB_MM,
+ TLB_PAGE,
+ TLB_RANGE
+};
+
+static int (* ipi_func[32])(unsigned int) = {
+ halt_on_panic,
+ local_flush_tlb_all,
+ local_flush_tlb_mm,
+ wrapper_local_flush_tlb_page,
+ local_flush_tlb_mm, /* a.k.a. local_flush_tlb_range */
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+ unknown_ipi, unknown_ipi, unknown_ipi
+};
+
+void
+handle_ipi(struct pt_regs *regs)
+{
+ int this_cpu = smp_processor_id();
+ volatile int * pending_ipis = &ipi_bits[this_cpu];
+ int ops;
+
+ mb();
+#if 0
+ printk("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
+ this_cpu, *pending_ipis, regs->pc);
+#endif
+ while ((ops = *pending_ipis)) {
+ int first;
+ for (first = 0; (ops & 1) == 0; ++first, ops >>= 1)
+ ; /* look for the first thing to do */
+ clear_bit(first, pending_ipis);
+ mb();
+ if ((*ipi_func[first])(this_cpu))
+ printk("%d\n", first);
+ mb();
+ }
+ if (hwrpb->txrdy)
+ secondary_console_message();
+}
+
+void
+send_ipi_message(long to_whom, enum ipi_message_type operation)
+{
+ int i;
+ unsigned int j;
+
+ for (i = 0, j = 1; i < NR_CPUS; ++i, j += j) {
+ if ((to_whom & j) == 0)
+ continue;
+ set_bit(operation, &ipi_bits[i]);
+ mb();
+ wripir(i);
+ }
+}
+
+static char smp_buf[256];
+
+char *smp_info(void)
+{
+ sprintf(smp_buf, "CPUs probed %d active %d map 0x%x AKP %d\n",
+ smp_num_probed, smp_num_cpus, cpu_present_map,
+ klock_info.akp);
+
+ return smp_buf;
+}
+
+/* wrapper for call from panic() */
+void
+smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ int me = smp_processor_id();
+
+ if (msg != MSG_STOP_CPU)
+ goto barf;
+
+ send_ipi_message(CPU_STOP, cpu_present_map ^ (1 << me));
+ return;
+barf:
+ printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+ panic("Bogon SMP message pass.");
+}
+
+void
+flush_tlb_all(void)
+{
+ unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
+ int timeout = 10000;
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_all: kernel_flag %d (cpu %d akp %d)!\n",
+ klock_info.kernel_flag, smp_processor_id(), klock_info.akp);
+ }
+#endif
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_ALL);
+ tbia();
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n",
+ smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+}
+
+void
+flush_tlb_mm(struct mm_struct *mm)
+{
+ unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
+ int timeout = 10000;
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_mm: kernel_flag %d (cpu %d akp %d)!\n",
+ klock_info.kernel_flag, smp_processor_id(), klock_info.akp);
+ }
+#endif
+ ipi_msg_flush_tb.p.flush_mm = mm;
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_MM);
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current(mm);
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n",
+ smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+}
+
+void
+flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
+{
+ int cpu = smp_processor_id();
+ unsigned int to_whom = cpu_present_map ^ (1 << cpu);
+ struct mm_struct * mm = vma->vm_mm;
+ int timeout = 10000;
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_page: kernel_flag %d (cpu %d akp %d)!\n",
+ klock_info.kernel_flag, cpu, klock_info.akp);
+ }
+#endif
+ ipi_msg_flush_tb.p.flush_vma = vma;
+ ipi_msg_flush_tb.flush_addr = addr;
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_PAGE);
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current_page(mm, vma, addr);
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_page: STUCK on CPU %d [0x%x,0x%lx,%d,%d]\n",
+ cpu, ipi_msg_flush_tb.flush_tb_mask, addr,
+ klock_info.akp, global_irq_holder);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+}
+
+void
+flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#if 0
+ flush_tlb_mm(mm);
+#else
+ unsigned int to_whom;
+ int timeout;
+ unsigned long where;
+
+ __asm__("mov $26, %0" : "=r" (where));
+
+ timeout = 10000;
+ to_whom = cpu_present_map ^ (1 << smp_processor_id());
+
+#if 1
+ if (!kernel_lock_held()) {
+ printk("flush_tlb_range: kernel_flag %d (cpu %d akp %d) @ 0x%lx\n",
+ klock_info.kernel_flag, smp_processor_id(), klock_info.akp,
+ where);
+ }
+#endif
+ ipi_msg_flush_tb.p.flush_mm = mm;
+ ipi_msg_flush_tb.flush_tb_mask = to_whom;
+ send_ipi_message(to_whom, TLB_MM);
+
+ if (mm != current->mm)
+ flush_tlb_other(mm);
+ else
+ flush_tlb_current(mm);
+
+ while (ipi_msg_flush_tb.flush_tb_mask) {
+ if (--timeout < 0) {
+ printk("flush_tlb_range: STUCK on CPU %d mask 0x%x\n",
+ smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+ ipi_msg_flush_tb.flush_tb_mask = 0;
+ break;
+ }
+ udelay(100);
+ ; /* Wait for all clear from other CPUs. */
+ }
+#endif
+}
+
+#ifdef DEBUG_KERNEL_LOCK
+void ___lock_kernel(klock_info_t *klip, int cpu, long ipl)
+{
+ long regx;
+ int stuck_lock;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck_lock = 1<<26;
+
+ __asm__ __volatile__(
+ "1: ldl_l %1,%0;"
+ " blbs %1,6f;"
+ " or %1,1,%1;"
+ " stl_c %1,%0;"
+ " beq %1,6f;"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "6: mov %5,$16;"
+ " call_pal %4;"
+ "7: ldl %1,%0;"
+ " blt %2,4b # debug\n"
+ " subl %2,1,%2 # debug\n"
+ " blbs %1,7b;"
+ " bis $31,7,$16;"
+ " call_pal %4;"
+ " br 1b\n"
+ ".previous"
+ : "=m,=m" (__dummy_lock(klip)), "=&r,=&r" (regx),
+ "=&r,=&r" (stuck_lock)
+ : "0,0" (__dummy_lock(klip)), "i,i" (PAL_swpipl),
+ "i,r" (ipl), "2,2" (stuck_lock)
+ : "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory");
+
+ if (stuck_lock < 0) {
+ printk("___kernel_lock stuck at %lx(%d) held %lx(%d)\n",
+ inline_pc, cpu, klip->pc, klip->cpu);
+ goto try_again;
+ } else {
+ klip->pc = inline_pc;
+ klip->cpu = cpu;
+ }
+}
+#endif
+
+#ifdef DEBUG_SPINLOCK
+void spin_lock(spinlock_t * lock)
+{
+ long tmp;
+ long stuck;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck = 0x10000000; /* was 4G, now 256M */
+
+ /* Use sub-sections to put the actual loop at the end
+ of this object file's text section so as to perfect
+ branch prediction. */
+ __asm__ __volatile__(
+ "1: ldq_l %0,%1\n"
+ " subq %2,1,%2\n"
+ " blbs %0,2f\n"
+ " or %0,1,%0\n"
+ " stq_c %0,%1\n"
+ " beq %0,3f\n"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "2: ldq %0,%1\n"
+ " subq %2,1,%2\n"
+ "3: blt %2,4b\n"
+ " blbs %0,2b\n"
+ " br 1b\n"
+ ".previous"
+ : "=r" (tmp),
+ "=m" (__dummy_lock(lock)),
+ "=r" (stuck)
+ : "2" (stuck));
+
+ if (stuck < 0) {
+ printk("spinlock stuck at %lx (cur=%lx, own=%lx)\n",
+ inline_pc,
+#if 0
+ lock->previous, lock->task
+#else
+ (unsigned long) current, lock->task
+#endif
+ );
+ goto try_again;
+ } else {
+ lock->previous = (unsigned long) inline_pc;
+ lock->task = (unsigned long) current;
+ }
+}
+#endif /* DEBUG_SPINLOCK */
+
+#ifdef DEBUG_RWLOCK
+void write_lock(rwlock_t * lock)
+{
+ long regx, regy;
+ int stuck_lock, stuck_reader;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck_lock = 1<<26;
+ stuck_reader = 1<<26;
+
+ __asm__ __volatile__(
+ "1: ldl_l %1,%0;"
+ " blbs %1,6f;"
+ " or %1,1,%2;"
+ " stl_c %2,%0;"
+ " beq %2,6f;"
+ " blt %1,8f;"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "6: ldl %1,%0;"
+ " blt %3,4b # debug\n"
+ " subl %3,1,%3 # debug\n"
+ " blbs %1,6b;"
+ " br 1b;"
+ "8: ldl %1,%0;"
+ " blt %4,4b # debug\n"
+ " subl %4,1,%4 # debug\n"
+ " blt %1,8b;"
+ "9: br 4b\n"
+ ".previous"
+ : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy)
+ , "=&r" (stuck_lock), "=&r" (stuck_reader)
+ : "0" (__dummy_lock(lock))
+ , "3" (stuck_lock), "4" (stuck_reader)
+ );
+
+ if (stuck_lock < 0) {
+ printk("write_lock stuck at %lx\n", inline_pc);
+ goto try_again;
+ }
+ if (stuck_reader < 0) {
+ printk("write_lock stuck on readers at %lx\n", inline_pc);
+ goto try_again;
+ }
+}
+
+void _read_lock(rwlock_t * lock)
+{
+ long regx;
+ int stuck_lock;
+ unsigned long inline_pc;
+
+ __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+ stuck_lock = 1<<26;
+
+ __asm__ __volatile__(
+ "1: ldl_l %1,%0;"
+ " blbs %1,6f;"
+ " subl %1,2,%1;"
+ " stl_c %1,%0;"
+ " beq %1,6f;"
+ "4: mb\n"
+ ".section .text2,\"ax\"\n"
+ "6: ldl %1,%0;"
+ " blt %2,4b # debug\n"
+ " subl %2,1,%2 # debug\n"
+ " blbs %1,6b;"
+ " br 1b\n"
+ ".previous"
+ : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock)
+ : "0" (__dummy_lock(lock)), "2" (stuck_lock)
+ );
+
+ if (stuck_lock < 0) {
+ printk("_read_lock stuck at %lx\n", inline_pc);
+ goto try_again;
+ }
+}
+#endif /* DEBUG_RWLOCK */
diff --git a/arch/alpha/kernel/t2.c b/arch/alpha/kernel/t2.c
index 398aaebb5..69ca71404 100644
--- a/arch/alpha/kernel/t2.c
+++ b/arch/alpha/kernel/t2.c
@@ -8,8 +8,8 @@
*
*/
#include <linux/kernel.h>
+#include <linux/config.h>
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/sched.h>
@@ -19,17 +19,14 @@
#include <asm/ptrace.h>
#include <asm/mmu_context.h>
-/* NOTE: Herein are back-to-back mb insns. They are magic.
- A plausable explanation is that the i/o controler does not properly
- handle the system transaction. Another involves timing. Ho hum. */
+/*
+ * NOTE: Herein lie back-to-back mb instructions. They are magic.
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction. Another involves timing. Ho hum.
+ */
extern struct hwrpb_struct *hwrpb;
extern asmlinkage void wrmces(unsigned long mces);
-extern asmlinkage unsigned long whami(void);
-extern int alpha_sys_type;
-
-#define CPUID whami()
-
/*
* Machine check reasons. Defined according to PALcode sources
@@ -62,10 +59,14 @@ extern int alpha_sys_type;
#define vulp volatile unsigned long *
#define vuip volatile unsigned int *
-static volatile unsigned int T2_mcheck_expected = 0;
-static volatile unsigned int T2_mcheck_taken = 0;
-static unsigned long T2_jd;
+static volatile unsigned int T2_mcheck_expected[NR_CPUS];
+static volatile unsigned int T2_mcheck_taken[NR_CPUS];
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int T2_DMA_WIN_BASE = T2_DMA_WIN_BASE_DEFAULT;
+unsigned int T2_DMA_WIN_SIZE = T2_DMA_WIN_SIZE_DEFAULT;
+unsigned long t2_sm_base;
+#endif /* SRM_SETUP */
/*
* Given a bus, device, and function number, compute resulting
@@ -145,8 +146,10 @@ static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
static unsigned int conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
- unsigned int stat0, value;
- unsigned int t2_cfg = 0; /* to keep gcc quiet */
+ unsigned int stat0, value, cpu;
+ unsigned long t2_cfg = 0; /* to keep gcc quiet */
+
+ cpu = smp_processor_id();
save_flags(flags); /* avoid getting hit by machine check */
cli();
@@ -155,43 +158,41 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1)
#if 0
/* reset status register to avoid losing errors: */
- stat0 = *(vuip)T2_IOCSR;
- *(vuip)T2_IOCSR = stat0;
+ stat0 = *(vulp)T2_IOCSR;
+ *(vulp)T2_IOCSR = stat0;
mb();
DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0));
+#endif
/* if Type1 access, must set T2 CFG */
if (type1) {
- t2_cfg = *(vuip)T2_IOC_CFG;
+ t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL;
+ *(vulp)T2_HAE_3 = 0x40000000UL | t2_cfg;
mb();
- *(vuip)T2_IOC_CFG = t2_cfg | 1;
DBG(("conf_read: TYPE1 access\n"));
}
mb();
draina();
-#endif
- T2_mcheck_expected = 1;
- T2_mcheck_taken = 0;
+ T2_mcheck_expected[cpu] = 1;
+ T2_mcheck_taken[cpu] = 0;
mb();
/* access configuration space: */
value = *(vuip)addr;
mb();
mb(); /* magic */
- if (T2_mcheck_taken) {
- T2_mcheck_taken = 0;
+ if (T2_mcheck_taken[cpu]) {
+ T2_mcheck_taken[cpu] = 0;
value = 0xffffffffU;
mb();
}
- T2_mcheck_expected = 0;
+ T2_mcheck_expected[cpu] = 0;
mb();
-#if 0
- /* if Type1 access, must reset IOC CFG so normal IO space ops work */
+ /* if Type1 access, must reset T2 CFG so normal IO space ops work */
if (type1) {
- *(vuip)T2_IOC_CFG = t2_cfg & ~1;
+ *(vulp)T2_HAE_3 = t2_cfg;
mb();
}
-#endif
DBG(("conf_read(): finished\n"));
restore_flags(flags);
@@ -203,44 +204,45 @@ static void conf_write(unsigned long addr, unsigned int value,
unsigned char type1)
{
unsigned long flags;
- unsigned int stat0;
- unsigned int t2_cfg = 0; /* to keep gcc quiet */
+ unsigned int stat0, cpu;
+ unsigned long t2_cfg = 0; /* to keep gcc quiet */
+
+ cpu = smp_processor_id();
save_flags(flags); /* avoid getting hit by machine check */
cli();
#if 0
/* reset status register to avoid losing errors: */
- stat0 = *(vuip)T2_IOCSR;
- *(vuip)T2_IOCSR = stat0;
+ stat0 = *(vulp)T2_IOCSR;
+ *(vulp)T2_IOCSR = stat0;
mb();
DBG(("conf_write: T2 ERR was 0x%x\n", stat0));
+#endif
/* if Type1 access, must set T2 CFG */
if (type1) {
- t2_cfg = *(vuip)T2_IOC_CFG;
+ t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL;
+ *(vulp)T2_HAE_3 = t2_cfg | 0x40000000UL;
mb();
- *(vuip)T2_IOC_CFG = t2_cfg | 1;
DBG(("conf_write: TYPE1 access\n"));
}
+ mb();
draina();
-#endif
- T2_mcheck_expected = 1;
+ T2_mcheck_expected[cpu] = 1;
mb();
/* access configuration space: */
*(vuip)addr = value;
mb();
mb(); /* magic */
- T2_mcheck_expected = 0;
+ T2_mcheck_expected[cpu] = 0;
mb();
-#if 0
- /* if Type1 access, must reset IOC CFG so normal IO space ops work */
+ /* if Type1 access, must reset T2 CFG so normal IO space ops work */
if (type1) {
- *(vuip)T2_IOC_CFG = t2_cfg & ~1;
+ *(vulp)T2_HAE_3 = t2_cfg;
mb();
}
-#endif
DBG(("conf_write(): finished\n"));
restore_flags(flags);
}
@@ -362,17 +364,21 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
{
- unsigned int t2_err;
- struct percpu_struct *cpu;
- int i;
+ unsigned long t2_err;
+ unsigned int i;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ T2_mcheck_expected[i] = 0;
+ T2_mcheck_taken[i] = 0;
+ }
#if 0
/*
* Set up error reporting.
*/
- t2_err = *(vuip)T2_IOCSR ;
+ t2_err = *(vulp)T2_IOCSR ;
t2_err |= (0x1 << 7) ; /* master abort */
- *(vuip)T2_IOC_T2_ERR = t2_err ;
+ *(vulp)T2_IOCSR = t2_err ;
mb() ;
#endif
@@ -388,6 +394,42 @@ unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
*(vulp)T2_TBASE2);
#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 1 for enabled and mapped to 0 */
+ if (((*(vulp)T2_WBASE1 & (3UL<<18)) == (2UL<<18)) &&
+ (*(vulp)T2_TBASE1 == 0))
+ {
+ T2_DMA_WIN_BASE = *(vulp)T2_WBASE1 & 0xfff00000UL;
+ T2_DMA_WIN_SIZE = *(vulp)T2_WMASK1 & 0xfff00000UL;
+ T2_DMA_WIN_SIZE += 0x00100000UL;
+/* DISABLE window 2!! ?? */
+#if 1
+ printk("t2_init: using Window 1 settings\n");
+ printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+ *(vulp)T2_WBASE1,
+ *(vulp)T2_WMASK1,
+ *(vulp)T2_TBASE1);
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if (((*(vulp)T2_WBASE2 & (3UL<<18)) == (2UL<<18)) &&
+ (*(vulp)T2_TBASE2 == 0))
+ {
+ T2_DMA_WIN_BASE = *(vulp)T2_WBASE2 & 0xfff00000UL;
+ T2_DMA_WIN_SIZE = *(vulp)T2_WMASK2 & 0xfff00000UL;
+ T2_DMA_WIN_SIZE += 0x00100000UL;
+/* DISABLE window 1!! ?? */
+#if 1
+ printk("t2_init: using Window 2 settings\n");
+ printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+ *(vulp)T2_WBASE2,
+ *(vulp)T2_WMASK2,
+ *(vulp)T2_TBASE2);
+#endif
+ }
+ else /* we must use our defaults... */
+#endif /* SRM_SETUP */
+ {
/*
* Set up the PCI->physical memory translation windows.
* For now, window 2 is disabled. In the future, we may
@@ -396,13 +438,13 @@ unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
*/
/* WARNING!! must correspond to the DMA_WIN params!!! */
- *(vuip)T2_WBASE1 = 0x400807ffU;
- *(vuip)T2_WMASK1 = 0x3ff00000U;
- *(vuip)T2_TBASE1 = 0;
-
- *(vuip)T2_WBASE2 = 0x0;
+ *(vulp)T2_WBASE1 = 0x400807ffU;
+ *(vulp)T2_WMASK1 = 0x3ff00000U;
+ *(vulp)T2_TBASE1 = 0;
- *(vuip)T2_HBASE = 0x0;
+ *(vulp)T2_WBASE2 = 0x0;
+ *(vulp)T2_HBASE = 0x0;
+ }
/*
* check ASN in HWRPB for validity, report if bad
@@ -420,41 +462,43 @@ unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
* what ARC or SRM might have left behind...
*/
{
-#if 0
- printk("T2_init: HAE1 was 0x%lx\n", *(vulp)T2_HAE_1);
- printk("T2_init: HAE2 was 0x%lx\n", *(vulp)T2_HAE_2);
- printk("T2_init: HAE3 was 0x%lx\n", *(vulp)T2_HAE_3);
- printk("T2_init: HAE4 was 0x%lx\n", *(vulp)T2_HAE_4);
+ unsigned long t2_hae_1 = *(vulp)T2_HAE_1;
+ unsigned long t2_hae_2 = *(vulp)T2_HAE_2;
+ unsigned long t2_hae_3 = *(vulp)T2_HAE_3;
+ unsigned long t2_hae_4 = *(vulp)T2_HAE_4;
+#if 1
+ printk("T2_init: HAE1 was 0x%lx\n", t2_hae_1);
+ printk("T2_init: HAE2 was 0x%lx\n", t2_hae_2);
+ printk("T2_init: HAE3 was 0x%lx\n", t2_hae_3);
+ printk("T2_init: HAE4 was 0x%lx\n", t2_hae_4);
#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /*
+ * sigh... For the SRM setup, unless we know apriori what the HAE
+ * contents will be, we need to setup the arbitrary region bases
+ * so we can test against the range of addresses and tailor the
+ * region chosen for the SPARSE memory access.
+ *
+ * see include/asm-alpha/t2.h for the SPARSE mem read/write
+ */
+ t2_sm_base = (t2_hae_1 << 27) & 0xf8000000UL;
+ /*
+ Set the HAE cache, so that setup_arch() code
+ will use the SRM setting always. Our readb/writeb
+ code in .h expects never to have to change
+ the contents of the HAE.
+ */
+ hae.cache = t2_hae_1;
+#else /* SRM_SETUP */
+ *(vulp)T2_HAE_1 = 0; mb();
+ *(vulp)T2_HAE_2 = 0; mb();
+ *(vulp)T2_HAE_3 = 0; mb();
#if 0
- *(vuip)T2_HAE_1 = 0; mb();
- *(vuip)T2_HAE_2 = 0; mb();
- *(vuip)T2_HAE_3 = 0; mb();
- *(vuip)T2_HAE_4 = 0; mb();
+ *(vulp)T2_HAE_4 = 0; mb(); /* do not touch this */
#endif
+#endif /* SRM_SETUP */
}
-#if 1
- if (hwrpb->nr_processors > 1) {
- printk("T2_init: nr_processors 0x%lx\n",
- hwrpb->nr_processors);
- printk("T2_init: processor_size 0x%lx\n",
- hwrpb->processor_size);
- printk("T2_init: processor_offset 0x%lx\n",
- hwrpb->processor_offset);
-
- cpu = (struct percpu_struct *)
- ((char*)hwrpb + hwrpb->processor_offset);
-
- for (i = 0; i < hwrpb->nr_processors; i++ ) {
- printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n",
- i, cpu->flags, cpu->type);
- cpu = (struct percpu_struct *)
- ((char *)cpu + hwrpb->processor_size);
- }
- }
-#endif
-
return mem_start;
}
@@ -469,17 +513,19 @@ static struct sable_cpu_csr *sable_cpu_regs[4] = {
int t2_clear_errors(void)
{
+ unsigned int cpu = smp_processor_id();
+
DBGMC(("???????? t2_clear_errors\n"));
- sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC;
+ sable_cpu_regs[cpu]->sic &= ~SIC_SEIC;
/*
* clear cpu errors
*/
- sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce;
- sable_cpu_regs[CPUID]->cbe |= sable_cpu_regs[CPUID]->cbe;
- sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue;
- sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter;
+ sable_cpu_regs[cpu]->bcce |= sable_cpu_regs[cpu]->bcce;
+ sable_cpu_regs[cpu]->cbe |= sable_cpu_regs[cpu]->cbe;
+ sable_cpu_regs[cpu]->bcue |= sable_cpu_regs[cpu]->bcue;
+ sable_cpu_regs[cpu]->dter |= sable_cpu_regs[cpu]->dter;
*(vulp)T2_CERR1 |= *(vulp)T2_CERR1;
*(vulp)T2_PERR1 |= *(vulp)T2_PERR1;
@@ -499,6 +545,7 @@ void t2_machine_check(unsigned long vector, unsigned long la_ptr,
const char * reason;
char buf[128];
long i;
+ unsigned int cpu = smp_processor_id();
DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n",
vector, la_ptr));
@@ -516,7 +563,7 @@ void t2_machine_check(unsigned long vector, unsigned long la_ptr,
DBGMC((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset,
mchk_header->elfl_sysoffset));
- DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected));
+ DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected[cpu]));
#ifdef DEBUG_DUMP
{
@@ -537,11 +584,11 @@ void t2_machine_check(unsigned long vector, unsigned long la_ptr,
*/
mb();
mb(); /* magic */
- if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
+ if (T2_mcheck_expected[cpu]) {
DBGMC(("T2 machine check expected\n"));
- T2_mcheck_taken = 1;
+ T2_mcheck_taken[cpu] = 1;
t2_clear_errors();
- T2_mcheck_expected = 0;
+ T2_mcheck_expected[cpu] = 0;
mb();
mb(); /* magic */
wrmces(rdmces()|1);/* ??? */
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index 9d8b56dc3..0456eb171 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -82,6 +82,16 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
__u32 now;
long nticks;
+#ifdef __SMP__
+ extern void smp_percpu_timer_interrupt(struct pt_regs *);
+ extern unsigned int boot_cpu_id;
+ /* when SMP, do this for *all* CPUs,
+ but only do the rest for the boot CPU */
+ smp_percpu_timer_interrupt(regs);
+ if (smp_processor_id() != boot_cpu_id)
+ return;
+#endif
+
/*
* Estimate how many ticks have passed since the last update.
* Round the result, .5 to even. When we loose ticks due to
diff --git a/arch/alpha/kernel/tsunami.c b/arch/alpha/kernel/tsunami.c
new file mode 100644
index 000000000..3d0fdde89
--- /dev/null
+++ b/arch/alpha/kernel/tsunami.c
@@ -0,0 +1,503 @@
+/*
+ * Code common to all TSUNAMI chips.
+ *
+ * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+
+/*
+ * NOTE: Herein lie back-to-back mb instructions. They are magic.
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction. Another involves timing. Ho hum.
+ */
+
+extern struct hwrpb_struct *hwrpb;
+extern asmlinkage void wrmces(unsigned long mces);
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef CONFIG_ALPHA_TSUNAMI
+
+#ifdef DEBUG
+# define DBG(args) printk args
+#else
+# define DBG(args)
+#endif
+
+#define DEBUG_MCHECK
+#ifdef DEBUG_MCHECK
+# define DBG_MCK(args) printk args
+#define DEBUG_MCHECK_DUMP
+#else
+# define DBG_MCK(args)
+#endif
+
+#define vuip volatile unsigned int *
+#define vulp volatile unsigned long *
+
+static volatile unsigned int TSUNAMI_mcheck_expected[NR_CPUS];
+static volatile unsigned int TSUNAMI_mcheck_taken[NR_CPUS];
+static unsigned int TSUNAMI_jd[NR_CPUS];
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int TSUNAMI_DMA_WIN_BASE = TSUNAMI_DMA_WIN_BASE_DEFAULT;
+unsigned int TSUNAMI_DMA_WIN_SIZE = TSUNAMI_DMA_WIN_SIZE_DEFAULT;
+#endif /* SRM_SETUP */
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address
+ * accordingly. It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Note that all config space accesses use Type 1 address format.
+ *
+ * Note also that type 1 is determined by non-zero bus number.
+ *
+ * Type 1:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:24 reserved
+ * 23:16 bus number (8 bits = 128 possible buses)
+ * 15:11 Device number (5 bits)
+ * 10:8 function number
+ * 7:2 register number
+ *
+ * Notes:
+ * The function number selects which function of a multi-function device
+ * (e.g., scsi and ethernet).
+ *
+ * The register selects a DWORD (32 bit) register offset. Hence it
+ * doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ * bits.
+ */
+static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned long *pci_addr,
+ unsigned char *type1)
+{
+ unsigned long addr;
+
+ DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
+ bus, device_fn, where, pci_addr, type1));
+
+ if (bus == 0) {
+ *type1 = 0;
+ } else {
+ /* type 1 configuration cycle: */
+ *type1 = 1;
+ }
+ addr = (bus << 16) | (device_fn << 8) | (where);
+ *pci_addr = addr;
+ DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+ return 0;
+}
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char *value)
+{
+ unsigned long addr;
+ unsigned char type1;
+ unsigned char result;
+
+ *value = 0xff;
+
+ if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ __asm__ __volatile__ (
+ "ldbu %0,%1"
+ : "=r" (result)
+ : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_CONF)));
+
+ *value = result;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short *value)
+{
+ unsigned long addr;
+ unsigned char type1;
+ unsigned short result;
+
+ *value = 0xffff;
+
+ if (where & 0x1) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (mk_conf_addr(bus, device_fn, where, &addr, &type1)) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ __asm__ __volatile__ (
+ "ldwu %0,%1"
+ : "=r" (result)
+ : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_CONF)));
+
+ *value = result;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned int *value)
+{
+ unsigned long addr;
+ unsigned char type1;
+ unsigned int result;
+
+ *value = 0xffffffff;
+ if (where & 0x3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (mk_conf_addr(bus, device_fn, where, &addr, &type1)) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ __asm__ __volatile__ (
+ "ldl %0,%1"
+ : "=r" (result)
+ : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_CONF)));
+
+ *value = result;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ __asm__ __volatile__ (
+ "stb %1,%0\n\t"
+ "mb"
+ : : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_CONF)),
+ "r" (value));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ if (where & 0x1) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ __asm__ __volatile__ (
+ "stw %1,%0\n\t"
+ "mb"
+ : : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_CONF)),
+ "r" (value));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned int value)
+{
+ unsigned long addr;
+ unsigned char type1;
+
+ if (where & 0x3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ __asm__ __volatile__ (
+ "stl %1,%0\n\t"
+ "mb"
+ : : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_CONF)),
+ "r" (value));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+unsigned long tsunami_init(unsigned long mem_start, unsigned long mem_end)
+{
+ unsigned long tsunami_err;
+ unsigned int i;
+
+#if 0
+printk("tsunami_init: CChip registers:\n");
+printk("tsunami_init: CSR_CSC 0x%lx\n", *(vulp)TSUNAMI_CSR_CSC);
+printk("tsunami_init: CSR_MTR 0x%lx\n", *(vulp)TSUNAMI_CSR_MTR);
+printk("tsunami_init: CSR_MISC 0x%lx\n", *(vulp)TSUNAMI_CSR_MISC);
+printk("tsunami_init: CSR_DIM0 0x%lx\n", *(vulp)TSUNAMI_CSR_DIM0);
+printk("tsunami_init: CSR_DIM1 0x%lx\n", *(vulp)TSUNAMI_CSR_DIM1);
+printk("tsunami_init: CSR_DIR0 0x%lx\n", *(vulp)TSUNAMI_CSR_DIR0);
+printk("tsunami_init: CSR_DIR1 0x%lx\n", *(vulp)TSUNAMI_CSR_DIR1);
+printk("tsunami_init: CSR_DRIR 0x%lx\n", *(vulp)TSUNAMI_CSR_DRIR);
+
+printk("tsunami_init: DChip registers:\n");
+printk("tsunami_init: CSR_DSC 0x%lx\n", *(vulp)TSUNAMI_CSR_DSC);
+printk("tsunami_init: CSR_STR 0x%lx\n", *(vulp)TSUNAMI_CSR_STR);
+printk("tsunami_init: CSR_DREV 0x%lx\n", *(vulp)TSUNAMI_CSR_DREV);
+
+printk("tsunami_init: PChip registers:\n");
+printk("tsunami_init: PCHIP0_WSBA0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA0);
+printk("tsunami_init: PCHIP0_WSBA1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA1);
+printk("tsunami_init: PCHIP0_WSBA2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA2);
+printk("tsunami_init: PCHIP0_WSBA3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA3);
+printk("tsunami_init: PCHIP0_WSM0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM0);
+printk("tsunami_init: PCHIP0_WSM1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM1);
+printk("tsunami_init: PCHIP0_WSM2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM2);
+printk("tsunami_init: PCHIP0_WSM3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM3);
+printk("tsunami_init: PCHIP0_TBA0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA0);
+printk("tsunami_init: PCHIP0_TBA1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA1);
+printk("tsunami_init: PCHIP0_TBA2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA2);
+printk("tsunami_init: PCHIP0_TBA3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA3);
+
+printk("tsunami_init: PCHIP0_PCTL 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PCTL);
+printk("tsunami_init: PCHIP0_PLAT 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PLAT);
+printk("tsunami_init: PCHIP0_PERROR 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PERROR);
+printk("tsunami_init: PCHIP0_PERRMASK 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PERRMASK);
+
+#endif
+
+ for (i = 0; i < NR_CPUS; i++) {
+ TSUNAMI_mcheck_expected[i] = 0;
+ TSUNAMI_mcheck_taken[i] = 0;
+ }
+#ifdef NOT_YET
+ /*
+ * Set up error reporting. Make sure CPU_PE is OFF in the mask.
+ */
+ tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERRMASK;
+ tsunami_err &= ~20;
+ *(vulp)TSUNAMI_PCHIP0_PERRMASK = tsunami_err;
+ mb();
+ tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERRMASK;
+
+ tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERROR ;
+ tsunami_err |= 0x40; /* master/target abort */
+ *(vulp)TSUNAMI_PCHIP0_PERROR = tsunami_err ;
+ mb() ;
+ tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERROR ;
+#endif /* NOT_YET */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+ /* check window 0 for enabled and mapped to 0 */
+ if (((*(vulp)TSUNAMI_PCHIP0_WSBA0 & 3) == 1) &&
+ (*(vulp)TSUNAMI_PCHIP0_TBA0 == 0) &&
+ ((*(vulp)TSUNAMI_PCHIP0_WSM0 & 0xfff00000U) > 0x0ff00000U))
+ {
+ TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA0 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM0 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("tsunami_init: using Window 0 settings\n");
+ printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vulp)TSUNAMI_PCHIP0_WSBA0,
+ *(vulp)TSUNAMI_PCHIP0_WSM0,
+ *(vulp)TSUNAMI_PCHIP0_TBA0);
+#endif
+ }
+ else /* check window 1 for enabled and mapped to 0 */
+ if (((*(vulp)TSUNAMI_PCHIP0_WSBA1 & 3) == 1) &&
+ (*(vulp)TSUNAMI_PCHIP0_TBA1 == 0) &&
+ ((*(vulp)TSUNAMI_PCHIP0_WSM1 & 0xfff00000U) > 0x0ff00000U))
+{
+ TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA1 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM1 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("tsunami_init: using Window 1 settings\n");
+ printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vulp)TSUNAMI_PCHIP0_WSBA1,
+ *(vulp)TSUNAMI_PCHIP0_WSM1,
+ *(vulp)TSUNAMI_PCHIP0_TBA1);
+#endif
+ }
+ else /* check window 2 for enabled and mapped to 0 */
+ if (((*(vulp)TSUNAMI_PCHIP0_WSBA2 & 3) == 1) &&
+ (*(vulp)TSUNAMI_PCHIP0_TSB2 == 0) &&
+ ((*(vulp)TSUNAMI_PCHIP0_WSM2 & 0xfff00000U) > 0x0ff00000U))
+ {
+ TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA2 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM2 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("tsunami_init: using Window 2 settings\n");
+ printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vulp)TSUNAMI_PCHIP0_WSBA2,
+ *(vulp)TSUNAMI_PCHIP0_WSM2,
+ *(vulp)TSUNAMI_PCHIP0_TSB2);
+#endif
+ }
+ else /* check window 3 for enabled and mapped to 0 */
+ if (((*(vulp)TSUNAMI_PCHIP0_WSBA3 & 3) == 1) &&
+ (*(vulp)TSUNAMI_PCHIP0_TBA3 == 0) &&
+ ((*(vulp)TSUNAMI_PCHIP0_WSM3 & 0xfff00000U) > 0x0ff00000U))
+ {
+ TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA3 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM3 & 0xfff00000U;
+ TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+ printk("tsunami_init: using Window 3 settings\n");
+ printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+ *(vulp)TSUNAMI_PCHIP0_WSBA3,
+ *(vulp)TSUNAMI_PCHIP0_WSM3,
+ *(vulp)TSUNAMI_PCHIP0_TBA3);
+#endif
+ }
+ else /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+ {
+ /*
+ * Set up the PCI->physical memory translation windows.
+ * For now, windows 1,2 and 3 are disabled. In the future, we may
+ * want to use them to do scatter/gather DMA. Window 0
+ * goes at 1 GB and is 1 GB large.
+ */
+
+ *(vulp)TSUNAMI_PCHIP0_WSBA0 = 1L | (TSUNAMI_DMA_WIN_BASE & 0xfff00000U);
+ *(vulp)TSUNAMI_PCHIP0_WSM0 = (TSUNAMI_DMA_WIN_SIZE - 1) & 0xfff00000UL;
+ *(vulp)TSUNAMI_PCHIP0_TBA0 = 0UL;
+
+ *(vulp)TSUNAMI_PCHIP0_WSBA1 = 0UL;
+ *(vulp)TSUNAMI_PCHIP0_WSBA2 = 0UL;
+ *(vulp)TSUNAMI_PCHIP0_WSBA3 = 0UL;
+ mb();
+ }
+
+ /*
+ * check ASN in HWRPB for validity, report if bad
+ */
+ if (hwrpb->max_asn != MAX_ASN) {
+ printk("TSUNAMI_init: max ASN from HWRPB is bad (0x%lx)\n",
+ hwrpb->max_asn);
+ hwrpb->max_asn = MAX_ASN;
+ }
+
+ return mem_start;
+}
+
+int tsunami_pci_clr_err(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ TSUNAMI_jd[cpu] = *((vulp)TSUNAMI_PCHIP0_PERROR);
+ DBG(("TSUNAMI_pci_clr_err: PERROR after read 0x%x\n", TSUNAMI_jd[cpu]));
+ *((vulp)TSUNAMI_PCHIP0_PERROR) = 0x040; mb();
+ TSUNAMI_jd[cpu] = *((vulp)TSUNAMI_PCHIP0_PERROR);
+ return 0;
+}
+
+void tsunami_machine_check(unsigned long vector, unsigned long la_ptr,
+ struct pt_regs * regs)
+{
+#if 1
+ printk("TSUNAMI machine check ignored\n") ;
+#else
+ struct el_common *mchk_header;
+ struct el_TSUNAMI_sysdata_mcheck *mchk_sysdata;
+ unsigned int cpu = smp_processor_id();
+
+ mchk_header = (struct el_common *)la_ptr;
+
+ mchk_sysdata =
+ (struct el_TSUNAMI_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset);
+
+#if 0
+ DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+ vector, la_ptr));
+ DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+ regs->pc, mchk_header->size, mchk_header->proc_offset,
+ mchk_header->sys_offset));
+ DBG_MCK(("tsunami_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
+ TSUNAMI_mcheck_expected[cpu], mchk_sysdata->epic_dcsr,
+ mchk_sysdata->epic_pear));
+#endif
+#ifdef DEBUG_MCHECK_DUMP
+ {
+ unsigned long *ptr;
+ int i;
+
+ ptr = (unsigned long *)la_ptr;
+ for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
+ printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]);
+ }
+ }
+#endif /* DEBUG_MCHECK_DUMP */
+ /*
+ * Check if machine check is due to a badaddr() and if so,
+ * ignore the machine check.
+ */
+ mb();
+ mb(); /* magic */
+ if (TSUNAMI_mcheck_expected[cpu]) {
+ DBG(("TSUNAMI machine check expected\n"));
+ TSUNAMI_mcheck_expected[cpu] = 0;
+ TSUNAMI_mcheck_taken[cpu] = 1;
+ mb();
+ mb(); /* magic */
+ draina();
+ tsunami_pci_clr_err();
+ wrmces(0x7);
+ mb();
+ }
+#if 1
+ else {
+ printk("TSUNAMI machine check NOT expected\n") ;
+ DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+ vector, la_ptr));
+ DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+ regs->pc, mchk_header->size, mchk_header->proc_offset,
+ mchk_header->sys_offset));
+ TSUNAMI_mcheck_expected[cpu] = 0;
+ TSUNAMI_mcheck_taken[cpu] = 1;
+ mb();
+ mb(); /* magic */
+ draina();
+ tsunami_pci_clr_err();
+ wrmces(0x7);
+ mb();
+ }
+#endif
+#endif
+}
+
+#endif /* CONFIG_ALPHA_TSUNAMI */
diff --git a/arch/alpha/lib/checksum.c b/arch/alpha/lib/checksum.c
index f95b535ca..5165279f0 100644
--- a/arch/alpha/lib/checksum.c
+++ b/arch/alpha/lib/checksum.c
@@ -37,6 +37,27 @@ unsigned short int csum_tcpudp_magic(unsigned long saddr,
((unsigned long) proto << 8));
}
+unsigned int csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ unsigned long result;
+
+ result = (saddr + daddr + sum +
+ ((unsigned long) ntohs(len) << 16) +
+ ((unsigned long) proto << 8));
+
+ /* Fold down to 32-bits so we don't loose in the typedef-less
+ network stack. */
+ /* 64 to 33 */
+ result = (result & 0xffffffff) + (result >> 32);
+ /* 33 to 32 */
+ result = (result & 0xffffffff) + (result >> 32);
+ return result;
+}
+
/*
* Do a 64-bit checksum on an arbitrary memory area..
*
diff --git a/arch/alpha/lib/copy_user.S b/arch/alpha/lib/copy_user.S
index da57fd6d1..aa309b9f5 100644
--- a/arch/alpha/lib/copy_user.S
+++ b/arch/alpha/lib/copy_user.S
@@ -27,11 +27,18 @@
*/
/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
+#define EXI(x,y...) \
99: x,##y; \
.section __ex_table,"a"; \
.gprel32 99b; \
- lda $31, $exit-99b($31); \
+ lda $31, $exitin-99b($31); \
+ .previous
+
+#define EXO(x,y...) \
+ 99: x,##y; \
+ .section __ex_table,"a"; \
+ .gprel32 99b; \
+ lda $31, $exitout-99b($31); \
.previous
.set noat
@@ -45,14 +52,14 @@ __copy_user:
subq $3,8,$3
.align 5
$37:
- EX( ldq_u $1,0($7) )
- EX( ldq_u $2,0($6) )
+ EXI( ldq_u $1,0($7) )
+ EXO( ldq_u $2,0($6) )
extbl $1,$7,$1
mskbl $2,$6,$2
insbl $1,$6,$1
addq $3,1,$3
bis $1,$2,$1
- EX( stq_u $1,0($6) )
+ EXO( stq_u $1,0($6) )
subq $0,1,$0
addq $6,1,$6
addq $7,1,$7
@@ -63,10 +70,10 @@ $36:
bic $0,7,$4
beq $1,$43
beq $4,$48
- EX( ldq_u $3,0($7) )
+ EXI( ldq_u $3,0($7) )
.align 5
$50:
- EX( ldq_u $2,8($7) )
+ EXI( ldq_u $2,8($7) )
subq $4,8,$4
extql $3,$7,$3
extqh $2,$7,$1
@@ -81,13 +88,13 @@ $48:
beq $0,$41
.align 5
$57:
- EX( ldq_u $1,0($7) )
- EX( ldq_u $2,0($6) )
+ EXI( ldq_u $1,0($7) )
+ EXO( ldq_u $2,0($6) )
extbl $1,$7,$1
mskbl $2,$6,$2
insbl $1,$6,$1
bis $1,$2,$1
- EX( stq_u $1,0($6) )
+ EXO( stq_u $1,0($6) )
subq $0,1,$0
addq $6,1,$6
addq $7,1,$7
@@ -98,7 +105,7 @@ $43:
beq $4,$65
.align 5
$66:
- EX( ldq $1,0($7) )
+ EXI( ldq $1,0($7) )
subq $4,8,$4
stq $1,0($6)
addq $7,8,$7
@@ -107,15 +114,31 @@ $66:
bne $4,$66
$65:
beq $0,$41
- EX( ldq $2,0($7) )
- EX( ldq $1,0($6) )
+ EXI( ldq $2,0($7) )
+ EXO( ldq $1,0($6) )
mskql $2,$0,$2
mskqh $1,$0,$1
bis $2,$1,$2
- EX( stq $2,0($6) )
+ EXO( stq $2,0($6) )
bis $31,$31,$0
$41:
$35:
-$exit:
+$exitout:
ret $31,($28),1
+
+$exitin:
+ /* A stupid byte-by-byte zeroing of the rest of the output
+ buffer. This cures security holes by never leaving
+ random kernel data around to be copied elsewhere. */
+
+ mov $0,$1
+$101:
+ EXO ( ldq_u $2,0($6) )
+ subq $1,1,$1
+ mskbl $2,$6,$2
+ EXO ( stq_u $2,0($6) )
+ addq $6,1,$6
+ bgt $1,$101
+ ret $31,($28),1
+
.end __copy_user
diff --git a/arch/alpha/lib/csum_partial_copy.c b/arch/alpha/lib/csum_partial_copy.c
index 1328eeaba..713081330 100644
--- a/arch/alpha/lib/csum_partial_copy.c
+++ b/arch/alpha/lib/csum_partial_copy.c
@@ -365,6 +365,12 @@ csum_partial_copy_from_user(const char *src, char *dst, int len,
}
unsigned int
+csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum)
+{
+ return do_csum_partial_copy_from_user(src, dst, len, sum, NULL);
+}
+
+unsigned int
csum_partial_copy (const char *src, char *dst, int len, unsigned int sum)
{
unsigned int ret;
diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c
index bcfac1020..dc708c73e 100644
--- a/arch/alpha/lib/memcpy.c
+++ b/arch/alpha/lib/memcpy.c
@@ -104,7 +104,7 @@ static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n)
DO_REST_ALIGNED(d,s,n);
}
-void * __memcpy(void * dest, const void *src, size_t n)
+void * memcpy(void * dest, const void *src, size_t n)
{
if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) {
__memcpy_aligned((unsigned long) dest, (unsigned long) src, n);
@@ -114,22 +114,5 @@ void * __memcpy(void * dest, const void *src, size_t n)
return dest;
}
-/*
- * Broken compiler uses "bcopy" to do internal
- * assignments. Silly OSF/1 BSDism.
- */
-char * bcopy(const char * src, char * dest, size_t n)
-{
- __memcpy(dest, src, n);
- return dest;
-}
-
-/*
- * gcc-2.7.1 and newer generate calls to memset and memcpy. So we
- * need to define that here:
- */
-#ifdef __ELF__
- asm (".weak memcpy; memcpy = __memcpy");
-#else
- asm (".weakext memcpy, __memcpy");
-#endif
+/* For backward modules compatibility, define __memcpy. */
+asm("__memcpy = memcpy; .globl __memcpy");
diff --git a/arch/alpha/math-emu/ieee-math.c b/arch/alpha/math-emu/ieee-math.c
index 59d7dfa6d..b3d896389 100644
--- a/arch/alpha/math-emu/ieee-math.c
+++ b/arch/alpha/math-emu/ieee-math.c
@@ -733,19 +733,23 @@ ieee_CVTQT (int f, unsigned long a, unsigned long *b)
* FPCR_INV if invalid operation occurred, etc.
*/
unsigned long
-ieee_CVTTQ (int f, unsigned long a, unsigned long *b)
+ieee_CVTTQ (int f, unsigned long a, unsigned long *pb)
{
unsigned int midway;
- unsigned long ov, uv, res = 0;
+ unsigned long ov, uv, res, b;
fpclass_t a_type;
EXTENDED temp;
- *b = 0;
a_type = extend_ieee(a, &temp, DOUBLE);
+
+ b = 0x7fffffffffffffff;
+ res = FPCR_INV;
if (a_type == NaN || a_type == INFTY)
- return FPCR_INV;
+ goto out;
+
+ res = 0;
if (a_type == QNaN)
- return 0;
+ goto out;
if (temp.e > 0) {
ov = 0;
@@ -757,7 +761,7 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b)
if (ov || (temp.f[1] & 0xffc0000000000000))
res |= FPCR_IOV | FPCR_INE;
}
- if (temp.e < 0) {
+ else if (temp.e < 0) {
while (temp.e < 0) {
++temp.e;
uv = temp.f[0] & 1; /* save sticky bit */
@@ -765,7 +769,8 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b)
temp.f[0] |= uv;
}
}
- *b = ((temp.f[1] << 9) | (temp.f[0] >> 55)) & 0x7fffffffffffffff;
+ b = (temp.f[1] << 9) | (temp.f[0] >> 55);
+
/*
* Notice: the fraction is only 52 bits long. Thus, rounding
* cannot possibly result in an integer overflow.
@@ -776,18 +781,18 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b)
midway = (temp.f[0] & 0x003fffffffffffff) == 0;
if ((midway && (temp.f[0] & 0x0080000000000000)) ||
!midway)
- ++*b;
+ ++b;
}
break;
case ROUND_PINF:
if ((temp.f[0] & 0x007fffffffffffff) != 0)
- ++*b;
+ ++b;
break;
case ROUND_NINF:
if ((temp.f[0] & 0x007fffffffffffff) != 0)
- --*b;
+ --b;
break;
case ROUND_CHOP:
@@ -798,8 +803,11 @@ ieee_CVTTQ (int f, unsigned long a, unsigned long *b)
res |= FPCR_INE;
if (temp.s) {
- *b = -*b;
+ b = -b;
}
+
+out:
+ *pb = b;
return res;
}
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index e44639fb2..603b33149 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -14,13 +14,52 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/mmu_context.h>
+#ifdef __SMP__
+unsigned long last_asn[NR_CPUS] = { /* gag */
+ ASN_FIRST_VERSION + (0 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (1 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (2 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (3 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (4 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (5 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (6 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (7 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (8 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (9 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (10 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (11 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (12 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (13 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (14 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (15 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (16 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (17 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (18 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (19 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (20 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (21 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (22 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (23 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (24 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (25 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (26 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (27 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (28 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (29 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (30 << WIDTH_HARDWARE_ASN),
+ ASN_FIRST_VERSION + (31 << WIDTH_HARDWARE_ASN)
+};
+#else
unsigned long asn_cache = ASN_FIRST_VERSION;
+#endif /* __SMP__ */
#ifndef BROKEN_ASN
/*
@@ -30,7 +69,8 @@ unsigned long asn_cache = ASN_FIRST_VERSION;
*/
void get_new_asn_and_reload(struct task_struct *tsk, struct mm_struct *mm)
{
- get_new_mmu_context(tsk, mm, asn_cache);
+ mm->context = 0;
+ get_new_mmu_context(tsk, mm);
reload_context(tsk);
}
#endif
@@ -84,6 +124,7 @@ asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr,
}
}
+ lock_kernel();
down(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
@@ -112,7 +153,7 @@ good_area:
}
handle_mm_fault(current, vma, address, cause > 0);
up(&mm->mmap_sem);
- return;
+ goto out;
/*
* Something tried to access memory that isn't in our memory map..
@@ -123,16 +164,17 @@ bad_area:
if (user_mode(regs)) {
force_sig(SIGSEGV, current);
- return;
+ goto out;
}
/* Are we prepared to handle this fault as an exception? */
if ((fixup = search_exception_table(regs->pc)) != 0) {
unsigned long newpc;
newpc = fixup_exception(dpf_reg, fixup, regs->pc);
- printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc);
+ printk("%s: Exception at [<%lx>] (%lx)\n",
+ current->comm, regs->pc, newpc);
regs->pc = newpc;
- return;
+ goto out;
}
/*
@@ -143,4 +185,7 @@ bad_area:
"virtual address %016lx\n", address);
die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16);
do_exit(SIGKILL);
+ out:
+ unlock_kernel();
}
+
diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c
index 67faa97d4..7562f6709 100644
--- a/arch/alpha/mm/init.c
+++ b/arch/alpha/mm/init.c
@@ -26,6 +26,8 @@
extern void die_if_kernel(char *,struct pt_regs *,long);
extern void show_net_buffers(void);
+struct thread_struct * original_pcb_ptr;
+
/*
* BAD_PAGE is the page that is used for page faults when linux
* is out-of-memory. Older versions of linux just did a
@@ -81,15 +83,22 @@ void show_mem(void)
extern unsigned long free_area_init(unsigned long, unsigned long);
-static void load_PCB(struct thread_struct * pcb)
+static struct thread_struct * load_PCB(struct thread_struct * pcb)
{
+ struct thread_struct *old_pcb;
+
__asm__ __volatile__(
- "stq $30,0(%0)\n\t"
- "bis %0,%0,$16\n\t"
- "call_pal %1"
- : /* no outputs */
+ "stq $30,0(%1)\n\t"
+ "bis %1,%1,$16\n\t"
+#ifdef CONFIG_ALPHA_DP264
+ "zap $16,0xe0,$16\n\t"
+#endif /* DP264 */
+ "call_pal %2\n\t"
+ "bis $0,$0,%0"
+ : "=r" (old_pcb)
: "r" (pcb), "i" (PAL_swpctx)
: "$0", "$1", "$16", "$22", "$23", "$24", "$25");
+ return old_pcb;
}
/*
@@ -107,7 +116,8 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
start_mem = free_area_init(start_mem, end_mem);
/* find free clusters, update mem_map[] accordingly */
- memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
+ memdesc = (struct memdesc_struct *)
+ (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
cluster = memdesc->cluster;
for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
unsigned long pfn, nr;
@@ -129,16 +139,47 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
memset((void *) ZERO_PAGE, 0, PAGE_SIZE);
memset(swapper_pg_dir, 0, PAGE_SIZE);
newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
- pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL);
+ pgd_val(swapper_pg_dir[1023]) =
+ (newptbr << 32) | pgprot_val(PAGE_KERNEL);
init_task.tss.ptbr = newptbr;
init_task.tss.pal_flags = 1; /* set FEN, clear everything else */
init_task.tss.flags = 0;
- load_PCB(&init_task.tss);
+ original_pcb_ptr =
+ phys_to_virt((unsigned long)load_PCB(&init_task.tss));
+#if 0
+printk("OKSP 0x%lx OPTBR 0x%lx\n",
+ original_pcb_ptr->ksp, original_pcb_ptr->ptbr);
+#endif
- flush_tlb_all();
+ tbia();
return start_mem;
}
+#ifdef __SMP__
+/*
+ * paging_init_secondary(), called ONLY by secondary CPUs,
+ * sets up current->tss contents appropriately and does a load_PCB.
+ * note that current should be pointing at the idle thread task struct
+ * for this CPU.
+ */
+void paging_init_secondary(void)
+{
+ current->tss.ptbr = init_task.tss.ptbr;
+ current->tss.pal_flags = 1;
+ current->tss.flags = 0;
+
+#if 0
+printk("paging_init_secondary: KSP 0x%lx PTBR 0x%lx\n",
+ current->tss.ksp, current->tss.ptbr);
+#endif
+
+ load_PCB(&current->tss);
+ tbia();
+
+ return;
+}
+#endif /* __SMP__ */
+
void mem_init(unsigned long start_mem, unsigned long end_mem)
{
unsigned long tmp;
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 5c1efb76a..0c39121be 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -12,6 +12,9 @@
#
# Copyright (C) 1995, 1996 by Russell King
+CFLAGS_PROC :=
+ASFLAGS_PROC :=
+
ifeq ($(CONFIG_CPU_ARM2),y)
PROCESSOR = armo
ASFLAGS_PROC += -m2
@@ -65,7 +68,6 @@ endif
# ZRELADDR - Compressed kernel relocating address (point at which uncompressed kernel is loaded).
#
-HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o
COMPRESSED_HEAD = head.o
ifeq ($(PROCESSOR),armo)
@@ -79,11 +81,13 @@ endif
ifeq ($(CONFIG_ARCH_A5K),y)
MACHINE = a5k
+ARCHDIR = arc
COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
endif
ifeq ($(CONFIG_ARCH_ARC),y)
MACHINE = arc
+ARCHDIR = arc
COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
endif
@@ -96,6 +100,7 @@ endif
ifeq ($(CONFIG_ARCH_RPC),y)
MACHINE = rpc
+ARCHDIR = rpc
COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
ZTEXTADDR = 0x10008000
ZRELADDR = 0x10008000
@@ -103,6 +108,14 @@ endif
ifeq ($(CONFIG_ARCH_EBSA110),y)
MACHINE = ebsa110
+ARCHDIR = ebsa110
+ZTEXTADDR = 0x00008000
+ZRELADDR = 0x00008000
+endif
+
+ifeq ($(CONFIG_ARCH_EBSA285),y)
+MACHINE = ebsa285
+ARCHDIR = ebsa285
ZTEXTADDR = 0x00008000
ZRELADDR = 0x00008000
endif
@@ -129,35 +142,55 @@ ifeq ($(CONFIG_FRAME_POINTER),y)
CFLAGS := $(CFLAGS:-fomit-frame-pointer=)
endif
CFLAGS := $(CFLAGS_PROC) $(CFLAGS) -pipe
-ASFLAGS := $(ASFLAGS_PROC) $(ASFLAGS) -D__ASSEMBLY__
+ASFLAGS := $(ASFLAGS_PROC) $(ASFLAGS)
LINKFLAGS = -T $(TOPDIR)/arch/arm/vmlinux.lds -e stext -Ttext $(TEXTADDR)
ZLINKFLAGS = -Ttext $(ZTEXTADDR)
SUBDIRS := $(SUBDIRS:drivers=) arch/arm/lib arch/arm/kernel arch/arm/mm arch/arm/drivers
+HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o
CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
-DRIVERS := arch/arm/drivers/block/block.a \
- arch/arm/drivers/char/char.a \
- drivers/misc/misc.a \
- arch/arm/drivers/net/net.a
+BLOCK_DRIVERS := arch/arm/drivers/block/block.a
+CDROM_DRIVERS := drivers/cdrom/cdrom.a
+CHAR_DRIVERS := arch/arm/drivers/char/char.a
+MISC_DRIVERS := drivers/misc/misc.a
+NET_DRIVERS := drivers/net/net.a
+PARIDE_DRIVERS := drivers/block/paride/paride.a
+PCI_DRIVERS := drivers/pci/pci.a
+SCSI_DRIVERS := drivers/scsi/scsi.a
+SOUND_DRIVERS := drivers/sound/sound.a
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS := $(DRIVERS) arch/arm/drivers/scsi/scsi.a
+ifeq ($(CONFIG_ARCH_ACORN),y)
+BLOCK_DRIVERS += drivers/acorn/block/acorn-block.a
+CHAR_DRIVERS += drivers/acorn/char/acorn-char.a
+NET_DRIVERS += drivers/acorn/net/acorn-net.a drivers/net/net.a
+SCSI_DRIVERS += drivers/acorn/scsi/acorn-scsi.a
endif
+DRIVERS := $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS)
+
+ifeq ($(CONFIG_SCSI),y)
+DRIVERS := $(DRIVERS) $(SCSI_DRIVERS)
+endif
ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
-DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
+DRIVERS := $(DRIVERS) $(CDROM_DRIVERS)
+endif
+ifdef CONFIG_PCI
+DRIVERS := $(DRIVERS) $(PCI_DRIVERS)
endif
-
ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) arch/arm/drivers/sound/sound.a
+DRIVERS := $(DRIVERS) $(SOUND_DRIVERS)
+endif
+ifeq ($(CONFIG_PARIDE),y)
+DRIVERS := $(DRIVERS) $(PARIDE_DRIVERS)
endif
symlinks::
$(RM) include/asm-arm/arch include/asm-arm/proc
- (cd include/asm-arm; ln -sf arch-$(MACHINE) arch; ln -sf proc-$(PROCESSOR) proc)
+ (cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc)
+# Once we've finished integrating the sources, the @$(MAKE) will disappear
mrproper::
rm -f include/asm-arm/arch include/asm-arm/proc
@$(MAKE) -C arch/$(ARCH)/drivers mrproper
@@ -183,7 +216,6 @@ install: vmlinux
@$(MAKEBOOT) install
# My testing targets (that short circuit a few dependencies)
-#
zImg:; @$(MAKEBOOT) zImage
Img:; @$(MAKEBOOT) Image
i:; @$(MAKEBOOT) install
@@ -191,8 +223,7 @@ zi:; @$(MAKEBOOT) zinstall
archclean:
@$(MAKEBOOT) clean
- @$(MAKE) -C arch/arm/lib clean
+ $(RM) arch/arm/lib/constants.h
archdep:
@$(MAKEBOOT) dep
-sed -e /^MACHINE..*=/s,= .*,= rpc,;/^PROCESSOR..*=/s,= .*,= armv, linux/arch/arm/Makefile.normal
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index e6050bf13..d210b92c2 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -25,11 +25,8 @@ install: $(CONFIGURE) Image
zinstall: $(CONFIGURE) zImage
sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
-tools/build: tools/build.c
- $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include
-
clean:
- rm -f Image zImage tools/build
+ rm -f Image zImage
@$(MAKE) -C compressed clean
dep:
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 8e49f5dd0..0c6a04c5b 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -26,7 +26,7 @@ piggy.o: $(SYSTEM)
$(OBJCOPY) $(SYSTEM) $$tmppiggy; \
gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \
echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \
- $(LD) -m elf_arm -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \
+ $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \
rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk;
clean:; rm -f vmlinux core
diff --git a/arch/arm/config.in b/arch/arm/config.in
index b95c2f16e..3396b1510 100644
--- a/arch/arm/config.in
+++ b/arch/arm/config.in
@@ -7,38 +7,25 @@ mainmenu_name "Linux Kernel Configuration"
define_bool CONFIG_ARM y
mainmenu_option next_comment
-comment 'Code maturity level options'
-bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
-endmenu
-
-mainmenu_option next_comment
-comment 'Loadable module support'
-bool 'Enable loadable module support' CONFIG_MODULES
-if [ "$CONFIG_MODULES" = "y" ]; then
- bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
- bool 'Kernel module loader' CONFIG_KMOD
-fi
-endmenu
-
-mainmenu_option next_comment
-comment 'General setup'
+comment 'System type and processor type'
choice 'ARM system type' \
"Archimedes CONFIG_ARCH_ARC \
A5000 CONFIG_ARCH_A5K \
RiscPC CONFIG_ARCH_RPC \
EBSA-110 CONFIG_ARCH_EBSA110 \
+ EBSA-285 CONFIG_ARCH_EBSA285 \
NexusPCI CONFIG_ARCH_NEXUSPCI" RiscPC
if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" -o "$CONFIG_ARCH_RPC" = "y" ]; then
define_bool CONFIG_ARCH_ACORN y
else
define_bool CONFIG_ARCH_ACORN n
fi
-if [ "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then
+if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o "$CONFIG_ARCH_EBSA285" = "y" ]; then
define_bool CONFIG_PCI y
else
define_bool CONFIG_PCI n
fi
-if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o "$CONFIG_ARCH_EBSA110" = "y" ]; then
+if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o "$CONFIG_ARCH_EBSA110" = "y" -o "$CONFIG_ARCH_EBSA285" = "y" ]; then
define_bool CONFIG_CPU_SA110 y
else
if [ "$CONFIG_ARCH_A5K" = "y" ]; then
@@ -51,11 +38,31 @@ else
StrongARM CONFIG_CPU_SA110" StrongARM
fi
fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
+comment 'Loadable module support'
+bool 'Enable loadable module support' CONFIG_MODULES
+if [ "$CONFIG_MODULES" = "y" ]; then
+ bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
+ bool 'Kernel module loader' CONFIG_KMOD
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'General setup'
bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
bool 'Use new compilation options (for GCC 2.8)' CONFIG_BINUTILS_NEW
bool 'Debug kernel errors' CONFIG_DEBUG_ERRORS
bool 'Networking support' CONFIG_NET
bool 'System V IPC' CONFIG_SYSVIPC
+# This needs kernel/acct.c to be updated
+#bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
bool 'Sysctl support' CONFIG_SYSCTL
tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
@@ -70,6 +77,7 @@ fi
endmenu
source arch/arm/drivers/block/Config.in
+source drivers/acorn/block/Config.in
if [ "$CONFIG_NET" = "y" ]; then
source net/Config.in
@@ -81,7 +89,7 @@ comment 'SCSI support'
tristate 'SCSI support?' CONFIG_SCSI
if [ "$CONFIG_SCSI" != "n" ]; then
- source arch/arm/drivers/scsi/Config.in
+ source drivers/scsi/Config.in
fi
endmenu
@@ -91,7 +99,7 @@ if [ "$CONFIG_NET" = "y" ]; then
bool 'Network device support?' CONFIG_NETDEVICES
if [ "$CONFIG_NETDEVICES" = "y" ]; then
- source arch/arm/drivers/net/Config.in
+ source drivers/net/Config.in
fi
endmenu
fi
@@ -128,7 +136,7 @@ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
tristate 'Sound support' CONFIG_SOUND
if [ "$CONFIG_SOUND" != "n" ]; then
- source arch/arm/drivers/sound/Config.in
+ source drivers/sound/Config.in
fi
endmenu
fi
diff --git a/arch/arm/defconfig b/arch/arm/defconfig
index 48358557b..5e587ffbf 100644
--- a/arch/arm/defconfig
+++ b/arch/arm/defconfig
@@ -31,6 +31,7 @@ CONFIG_ARCH_ACORN=y
CONFIG_CPU_SA110=y
CONFIG_FRAME_POINTER=y
# CONFIG_BINUTILS_NEW is not set
+CONFIG_DEBUG_ERRORS=y
CONFIG_NET=y
CONFIG_SYSVIPC=y
CONFIG_SYSCTL=y
@@ -76,7 +77,7 @@ CONFIG_BLK_DEV_PART=y
#
# Networking options
#
-CONFIG_PACKET=m
+# CONFIG_PACKET is not set
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
# CONFIG_NET_ALIAS is not set
@@ -112,8 +113,11 @@ CONFIG_IP_NOSR=y
# CONFIG_BRIDGE is not set
# CONFIG_LLC is not set
# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
#
# SCSI support
@@ -149,7 +153,6 @@ CONFIG_SCSI_POWERTECSCSI=m
# The following drives are not fully supported
#
CONFIG_SCSI_CUMANA_1=m
-CONFIG_SCSI_ECOSCSI=m
CONFIG_SCSI_OAK1=m
CONFIG_SCSI_PPA=m
CONFIG_SCSI_PPA_HAVE_PEDANTIC=2
@@ -199,6 +202,7 @@ CONFIG_LOCKD=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_UFS_FS is not set
CONFIG_ADFS_FS=y
+CONFIG_ADFS_FS=y
# CONFIG_MAC_PARTITION is not set
CONFIG_NLS=y
@@ -252,10 +256,10 @@ CONFIG_RPCMOUSE=y
#
# Sound
#
-# CONFIG_SOUND is not set
-# CONFIG_VIDC is not set
-# CONFIG_AUDIO is not set
-# DSP_BUFFSIZE is not set
+CONFIG_SOUND=m
+CONFIG_VIDC=y
+CONFIG_AUDIO=y
+DSP_BUFFSIZE=65536
#
# Kernel hacking
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 90e71345a..f263b2a7f 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -19,15 +19,24 @@ else
O_OBJS += armksyms.o
endif
+ifdef CONFIG_PCI
+ O_OBJS += bios32.o
+endif
+
ifdef CONFIG_ARCH_ACORN
O_OBJS += setup.o ecard.o iic.o dma.o
ifdef CONFIG_ARCH_ARC
O_OBJS += oldlatches.o
endif
+ O_OBJS += dma-$(MACHINE).o
endif
ifeq ($(MACHINE),ebsa110)
- O_OBJS += setup-ebsa110.o dma.o
+ O_OBJS += setup-ebsa110.o dma.o dma-dummy.o
+endif
+
+ifeq ($(MACHINE),ebsa285)
+ O_OBJS += dma.o dma-dummy.o leds-ebsa285.o setup-ebsa110.o
endif
ifeq ($(MACHINE),nexuspci)
@@ -37,9 +46,12 @@ endif
$(HEAD_OBJ): $(HEAD_OBJ:.o=.S)
$(CC) -D__ASSEMBLY__ -traditional -c $(HEAD_OBJ:.o=.S) -o $@
+$(ENTRY_OBJ): $(ENTRY_OBJ:.o=.S)
+ $(CC) $(CFLAGS) -D__ASSEMBLY__ -c $(ENTRY_OBJ:.o=.S) -o $@
+
include $(TOPDIR)/Rules.make
-$(ENTRY_OBJ:.o=.S): ../lib/constants.h
+$(ENTRY_OBJ): ../lib/constants.h
.PHONY: ../lib/constants.h
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 20c62e2e6..a5b49bf2a 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -63,7 +63,6 @@ extern void fp_send_sig(int);
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(dump_fpu);
EXPORT_SYMBOL(udelay);
-EXPORT_SYMBOL(dma_str);
EXPORT_SYMBOL(xchg_str);
/* expansion card support */
@@ -83,16 +82,33 @@ EXPORT_SYMBOL(outsw);
EXPORT_SYMBOL(inswb);
EXPORT_SYMBOL(insw);
-#ifdef CONFIG_ARCH_RPC
-EXPORT_SYMBOL(drambank);
+/* address translation */
+#ifndef __virt_to_phys__is_a_macro
+EXPORT_SYMBOL(__virt_to_phys);
+#endif
+#ifndef __phys_to_virt__is_a_macro
+EXPORT_SYMBOL(__phys_to_virt);
+#endif
+#ifndef __virt_to_bus__is_a_macro
+EXPORT_SYMBOL(__virt_to_bus);
#endif
+#ifndef __bus_to_virt__is_a_macro
+EXPORT_SYMBOL(__bus_to_virt);
+#endif
+
+EXPORT_SYMBOL(quicklists);
+EXPORT_SYMBOL(__bad_pmd);
+EXPORT_SYMBOL(__bad_pmd_kernel);
/* dma */
+EXPORT_SYMBOL(dma_str);
EXPORT_SYMBOL(enable_dma);
-EXPORT_SYMBOL(set_dma_mode);
+EXPORT_SYMBOL(disable_dma);
EXPORT_SYMBOL(set_dma_addr);
EXPORT_SYMBOL(set_dma_count);
+EXPORT_SYMBOL(set_dma_mode);
EXPORT_SYMBOL(get_dma_residue);
+EXPORT_SYMBOL(set_dma_sg);
/*
* floating point math emulator support.
@@ -177,7 +193,3 @@ EXPORT_SYMBOL(change_bit);
EXPORT_SYMBOL(test_and_change_bit);
EXPORT_SYMBOL(find_first_zero_bit);
EXPORT_SYMBOL(find_next_zero_bit);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
new file mode 100644
index 000000000..4400dda42
--- /dev/null
+++ b/arch/arm/kernel/bios32.c
@@ -0,0 +1,144 @@
+/*
+ * arch/arm/kernel/bios32.c: PCI functions for ARM
+ *
+ * Copyright (C) 1998 Russell King
+ */
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+int pcibios_present(void)
+{
+ return 1;
+}
+
+static unsigned long pcibios_base_address(unsigned char dev_fn)
+{
+ int slot = PCI_SLOT(dev_fn);
+
+ if (slot < 4)
+ return 0xf8000000 + (1 << (19 - slot));
+ else
+ return 0;
+}
+
+int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned char *val)
+{
+ unsigned long addr = pcibios_base_address(dev_fn);
+ unsigned char v;
+
+ if (addr) {
+ __asm__("ldr%?b %0, [%1, %2]"
+ : "=r" (v)
+ : "r" (addr), "r" (where));
+ *val = v;
+ } else
+ *val = 0xff;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned short *val)
+{
+ unsigned long addr = pcibios_base_address(dev_fn);
+ unsigned short v;
+
+ if (addr) {
+ __asm__("ldrh%? %0, [%1, %2]"
+ : "=r" (v)
+ : "r" (addr), "r" (where));
+ *val = v;
+ } else
+ *val = 0xffff;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned int *val)
+{
+ unsigned long addr = pcibios_base_address(dev_fn);
+ unsigned int v;
+
+ if (addr) {
+ __asm__("ldr%? %0, [%1, %2]"
+ : "=r" (v)
+ : "r" (addr), "r" (where));
+ *val = v;
+ } else
+ *val = 0xffffffff;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned char val)
+{
+ unsigned long addr = pcibios_base_address(dev_fn);
+
+ if (addr)
+ __asm__("str%?b %0, [%1, %2]"
+ : : "r" (val), "r" (addr), "r" (where));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned short val)
+{
+ unsigned long addr = pcibios_base_address(dev_fn);
+
+ if (addr)
+ __asm__("strh%? %0, [%1, %2]"
+ : : "r" (val), "r" (addr), "r" (where));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned int val)
+{
+ unsigned long addr = pcibios_base_address(dev_fn);
+
+ if (addr)
+ __asm__("str%? %0, [%1, %2]"
+ : : "r" (val), "r" (addr), "r" (where));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int irq[] = { 18, 8, 9, 11 };
+
+__initfunc(void pcibios_fixup(void))
+{
+ struct pci_dev *dev;
+ unsigned char pin;
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ pcibios_read_config_byte(dev->bus->number,
+ dev->devfn,
+ PCI_INTERRUPT_PIN,
+ &pin);
+
+ dev->irq = irq[(PCI_SLOT(dev->devfn) + pin) & 3];
+
+ pcibios_write_config_byte(dev->bus->number,
+ dev->devfn,
+ PCI_INTERRUPT_LINE,
+ dev->irq);
+
+ printk("PCI: %02x:%02x [%04x/%04x] pin %d irq %d\n",
+ dev->bus->number, dev->devfn,
+ dev->vendor, dev->device,
+ pin, dev->irq);
+ }
+}
+
+__initfunc(void pcibios_init(void))
+{
+ int rev;
+
+ rev = *(unsigned char *)0xfe000008;
+ printk("DEC21285 PCI revision %02X\n", rev);
+}
+
+__initfunc(char *pcibios_setup(char *str))
+{
+ return str;
+}
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 0d02eb85a..8a248c728 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -5,7 +5,7 @@
*/
#ifndef NR_SYSCALLS
#define NR_syscalls 256
-#define NR_SYSCALLS 182
+#define NR_SYSCALLS 184
#else
/* 0 */ .long SYMBOL_NAME(sys_setup)
@@ -190,5 +190,7 @@
.long SYMBOL_NAME(sys_rt_sigsuspend_wrapper)
/* 180 */ .long SYMBOL_NAME(sys_pread)
.long SYMBOL_NAME(sys_pwrite)
- .space (NR_syscalls - 182) * 4
+ .long SYMBOL_NAME(sys_xstat)
+ .long SYMBOL_NAME(sys_xmknod)
+ .space (NR_syscalls - 184) * 4
#endif
diff --git a/arch/arm/kernel/dma-a5k.c b/arch/arm/kernel/dma-a5k.c
new file mode 100644
index 000000000..f722809fa
--- /dev/null
+++ b/arch/arm/kernel/dma-a5k.c
@@ -0,0 +1,79 @@
+/*
+ * arch/arm/kernel/dma-a5k.c
+ *
+ * Copyright (C) 1998 Russell King
+ *
+ * DMA functions specific to A5000 architecture
+ */
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include "dma.h"
+
+int arch_request_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel == DMA_VIRTUAL_FLOPPY0)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel != DMA_VIRTUAL_FLOPPY0)
+ printk ("arch_free_dma: invalid channel %d\n", channel);
+}
+
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+ if (channel != DMA_VIRTUAL_FLOPPY0)
+ printk ("arch_dma_count: invalid channel %d\n", dmanr);
+ else {
+ extern int floppy_fiqresidual(void);
+ return floppy_fiqresidual();
+ }
+ return 0;
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel != DMA_VIRTUAL_FLOPPY0)
+ printk ("arch_enable_dma: invalid channel %d\n", channel);
+ else {
+ void *fiqhandler_start;
+ unsigned int fiqhandler_length;
+ extern void floppy_fiqsetup (unsigned long len, unsigned long addr,
+ unsigned long port);
+
+ if (dma->dma_mode == DMA_MODE_READ) {
+ extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
+ fiqhandler_start = &floppy_fiqin_start;
+ fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start;
+ } else {
+ extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
+ fiqhandler_start = &floppy_fiqout_start;
+ fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start;
+ }
+ memcpy ((void *)0x1c, fiqhandler_start, fiqhandler_length);
+ flush_page_to_ram(0);
+ floppy_fiqsetup (dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE);
+ enable_irq (dma->dma_irq);
+ }
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel != DMA_VIRTUAL_FLOPPY0)
+ printk ("arch_disable_dma: invalid channel %d\n", channel);
+ else
+ disable_irq (dma->dma_irq);
+}
+
+__initfunc(void arch_dma_init(dma_t *dma))
+{
+ dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64;
+}
diff --git a/arch/arm/kernel/dma-arc.c b/arch/arm/kernel/dma-arc.c
new file mode 100644
index 000000000..27a139ad4
--- /dev/null
+++ b/arch/arm/kernel/dma-arc.c
@@ -0,0 +1,115 @@
+/*
+ * arch/arm/kernel/dma-arc.c
+ *
+ * Copyright (C) 1998 Dave Gilbert / Russell King
+ *
+ * DMA functions specific to Archimedes architecture
+ */
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include "dma.h"
+
+int arch_request_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel == DMA_VIRTUAL_FLOPPY0 ||
+ channel == DMA_VIRTUAL_FLOPPY1)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel != DMA_VIRTUAL_FLOPPY0 &&
+ channel != DMA_VIRTUAL_FLOPPY1)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+ switch (channel) {
+ case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
+ switch (dma->dma_mode) {
+ case DMA_MODE_READ: /* read */
+ {
+ extern unsigned char fdc1772_dma_read, fdc1772_dma_read_end;
+ extern void fdc1772_setupdma(unsigned int count,unsigned int addr);
+ unsigned long flags;
+#ifdef DEBUG
+ printk("enable_dma fdc1772 data read\n");
+#endif
+ save_flags(flags);
+ cliIF();
+
+ memcpy ((void *)0x1c, (void *)&fdc1772_dma_read,
+ &fdc1772_dma_read_end - &fdc1772_dma_read);
+ fdc1772_setupdma(dma->buf.length, __bus_to_virt(dma->buf.address)); /* Sets data pointer up */
+ enable_irq (64);
+ restore_flags(flags);
+ }
+ break;
+
+ case DMA_MODE_WRITE: /* write */
+ {
+ extern unsigned char fdc1772_dma_write, fdc1772_dma_write_end;
+ extern void fdc1772_setupdma(unsigned int count,unsigned int addr);
+ unsigned long flags;
+
+#ifdef DEBUG
+ printk("enable_dma fdc1772 data write\n");
+#endif
+ save_flags(flags);
+ cliIF();
+ memcpy ((void *)0x1c, (void *)&fdc1772_dma_write,
+ &fdc1772_dma_write_end - &fdc1772_dma_write);
+ fdc1772_setupdma(dma->buf.length, __bus_to_virt(dma->buf.address)); /* Sets data pointer up */
+ enable_irq (64);
+
+ restore_flags(flags);
+ }
+ break;
+ default:
+ printk ("enable_dma: dma%d not initialised\n", channel);
+ return;
+ }
+ }
+ break;
+
+ case DMA_VIRTUAL_FLOPPY1: { /* Command end FIQ - actually just sets a flag */
+ /* Need to build a branch at the FIQ address */
+ extern void fdc1772_comendhandler(void);
+ unsigned long flags;
+
+ /*printk("enable_dma fdc1772 command end FIQ\n");*/
+ save_flags(flags);
+ cliIF();
+
+ *((unsigned int *)0x1c)=0xea000000 | (((unsigned int)fdc1772_comendhandler-(0x1c+8))/4); /* B fdc1772_comendhandler */
+
+ restore_flags(flags);
+ }
+ break;
+ }
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+ if (channel != DMA_VIRTUAL_FLOPPY0 &&
+ channel != DMA_VIRTUAL_FLOPPY1)
+ printk("arch_disable_dma: invalid channel %d\n", channel);
+ else
+ disable_irq(dma->dma_irq);
+}
+
+__initfunc(void arch_dma_init(dma_t *dma))
+{
+ dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64;
+ dma[DMA_VIRTUAL_FLOPPY1].dma_irq = 65;
+}
diff --git a/arch/arm/kernel/dma-dummy.c b/arch/arm/kernel/dma-dummy.c
new file mode 100644
index 000000000..af47512dd
--- /dev/null
+++ b/arch/arm/kernel/dma-dummy.c
@@ -0,0 +1,45 @@
+/*
+ * arch/arm/kernel/dma-dummy.c
+ *
+ * Copyright (C) 1998 Russell King
+ *
+ * Dummy DMA functions
+ */
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include "dma.h"
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char *devname)
+{
+ return -EINVAL;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+ printk ("arch_free_dma: invalid channel %d\n", channel);
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+ printk ("arch_enable_dma: invalid channel %d\n", channel);
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+ printk ("arch_disable_dma: invalid channel %d\n", channel);
+}
+
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+ printk ("arch_get_dma_residue: invalid channel %d\n", channel);
+ return 0;
+}
+
+__initfunc(void arch_dma_init(dma_t *dma))
+{
+}
diff --git a/arch/arm/kernel/dma-rpc.c b/arch/arm/kernel/dma-rpc.c
new file mode 100644
index 000000000..eb5bc87d7
--- /dev/null
+++ b/arch/arm/kernel/dma-rpc.c
@@ -0,0 +1,342 @@
+/*
+ * arch/arm/kernel/dma-rpc.c
+ *
+ * Copyright (C) 1998 Russell King
+ *
+ * DMA functions specific to RiscPC architecture
+ */
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+
+#include "dma.h"
+
+#if 0
+typedef enum {
+ dma_size_8 = 1,
+ dma_size_16 = 2,
+ dma_size_32 = 4,
+ dma_size_128 = 16
+} dma_size_t;
+
+typedef struct {
+ dma_size_t transfersize;
+} dma_t;
+#endif
+
+#define TRANSFER_SIZE 2
+
+#define CURA (0)
+#define ENDA ((IOMD_IO0ENDA - IOMD_IO0CURA) << 2)
+#define CURB ((IOMD_IO0CURB - IOMD_IO0CURA) << 2)
+#define ENDB ((IOMD_IO0ENDB - IOMD_IO0CURA) << 2)
+#define CR ((IOMD_IO0CR - IOMD_IO0CURA) << 2)
+#define ST ((IOMD_IO0ST - IOMD_IO0CURA) << 2)
+
+#define state_prog_a 0
+#define state_wait_a 1
+#define state_wait_b 2
+
+static void arch_get_next_sg(dmasg_t *sg, dma_t *dma)
+{
+ unsigned long end, offset, flags = 0;
+
+ if (dma->sg) {
+ sg->address = dma->sg->address;
+ offset = sg->address & ~PAGE_MASK;
+
+ end = offset + dma->sg->length;
+
+ if (end > PAGE_SIZE)
+ end = PAGE_SIZE;
+
+ if (offset + (int) TRANSFER_SIZE > end)
+ flags |= DMA_END_L;
+
+ sg->length = end - TRANSFER_SIZE;
+
+ dma->sg->length -= end - offset;
+ dma->sg->address += end - offset;
+
+ if (dma->sg->length == 0) {
+ if (dma->sgcount > 1) {
+ dma->sg++;
+ dma->sgcount--;
+ } else {
+ dma->sg = NULL;
+ flags |= DMA_END_S;
+ }
+ }
+ } else {
+ flags = DMA_END_S | DMA_END_L;
+ sg->address = 0;
+ sg->length = 0;
+ }
+
+ sg->length |= flags;
+}
+
+static inline void arch_setup_dma_a(dmasg_t *sg, dma_t *dma)
+{
+ outl_t(sg->address, dma->dma_base + CURA);
+ outl_t(sg->length, dma->dma_base + ENDA);
+}
+
+static inline void arch_setup_dma_b(dmasg_t *sg, dma_t *dma)
+{
+ outl_t(sg->address, dma->dma_base + CURB);
+ outl_t(sg->length, dma->dma_base + ENDB);
+}
+
+static void arch_dma_handle(int irq, void *dev_id, struct pt_regs *regs)
+{
+ dma_t *dma = (dma_t *)dev_id;
+ unsigned int status = 0, no_buffer = dma->sg == NULL;
+
+ do {
+ switch (dma->state) {
+ case state_prog_a:
+ arch_get_next_sg(&dma->cur_sg, dma);
+ arch_setup_dma_a(&dma->cur_sg, dma);
+ dma->state = state_wait_a;
+
+ case state_wait_a:
+ status = inb_t(dma->dma_base + ST);
+ switch (status & (DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB)) {
+ case DMA_ST_OFL|DMA_ST_INT:
+ arch_get_next_sg(&dma->cur_sg, dma);
+ arch_setup_dma_a(&dma->cur_sg, dma);
+ break;
+
+ case DMA_ST_INT:
+ arch_get_next_sg(&dma->cur_sg, dma);
+ arch_setup_dma_b(&dma->cur_sg, dma);
+ dma->state = state_wait_b;
+ break;
+
+ case DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB:
+ arch_setup_dma_b(&dma->cur_sg, dma);
+ dma->state = state_wait_b;
+ break;
+ }
+ break;
+
+ case state_wait_b:
+ status = inb_t(dma->dma_base + ST);
+ switch (status & (DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB)) {
+ case DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB:
+ arch_get_next_sg(&dma->cur_sg, dma);
+ arch_setup_dma_b(&dma->cur_sg, dma);
+ break;
+
+ case DMA_ST_INT|DMA_ST_AB:
+ arch_get_next_sg(&dma->cur_sg, dma);
+ arch_setup_dma_a(&dma->cur_sg, dma);
+ dma->state = state_wait_a;
+ break;
+
+ case DMA_ST_OFL|DMA_ST_INT:
+ arch_setup_dma_a(&dma->cur_sg, dma);
+ dma->state = state_wait_a;
+ break;
+ }
+ break;
+ }
+ } while (dma->sg && (status & DMA_ST_INT));
+
+ if (!no_buffer)
+ enable_irq(irq);
+}
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
+{
+ unsigned long flags;
+ int ret;
+
+ switch (channel) {
+ case DMA_0:
+ case DMA_1:
+ case DMA_2:
+ case DMA_3:
+ case DMA_S0:
+ case DMA_S1:
+ save_flags_cli(flags);
+ ret = request_irq(dma->dma_irq, arch_dma_handle, SA_INTERRUPT, dev_name, dma);
+ if (!ret)
+ disable_irq(dma->dma_irq);
+ restore_flags(flags);
+ break;
+
+ case DMA_VIRTUAL_FLOPPY:
+ case DMA_VIRTUAL_SOUND:
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+ switch (channel) {
+ case DMA_0:
+ case DMA_1:
+ case DMA_2:
+ case DMA_3:
+ case DMA_S0:
+ case DMA_S1:
+ free_irq(dma->dma_irq, dma);
+ break;
+
+ default:
+ break;
+ }
+}
+
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+ int residue = 0;
+
+ switch (channel) {
+ case DMA_0: /* Physical DMA channels */
+ case DMA_1:
+ case DMA_2:
+ case DMA_3:
+ case DMA_S0:
+ case DMA_S1:
+ break;
+
+ case DMA_VIRTUAL_FLOPPY: {
+ extern int floppy_fiqresidual(void);
+ residue = floppy_fiqresidual();
+ }
+ break;
+ }
+ return residue;
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+ unsigned long dma_base = dma->dma_base;
+ unsigned int ctrl;
+
+ switch (channel) {
+ case DMA_0: /* Physical DMA channels */
+ case DMA_1:
+ case DMA_2:
+ case DMA_3:
+ case DMA_S0:
+ case DMA_S1:
+ ctrl = TRANSFER_SIZE | DMA_CR_E;
+
+ if (dma->invalid) {
+ dma->invalid = 0;
+
+ outb_t(DMA_CR_C, dma_base + CR);
+ dma->state = state_prog_a;
+ }
+
+ if (dma->dma_mode == DMA_MODE_READ)
+ ctrl |= DMA_CR_D;
+
+ outb_t(ctrl, dma_base + CR);
+ enable_irq(dma->dma_irq);
+ break;
+
+ case DMA_VIRTUAL_FLOPPY: {
+ void *fiqhandler_start;
+ unsigned int fiqhandler_length;
+ extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
+ unsigned long port);
+
+ if (dma->dma_mode == DMA_MODE_READ) {
+ extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
+ fiqhandler_start = &floppy_fiqin_start;
+ fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start;
+ } else {
+ extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
+ fiqhandler_start = &floppy_fiqout_start;
+ fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start;
+ }
+ /* Allow access to page 0 via domains */
+ __asm__ __volatile__("mcr p15, 0, %0, c3, c0" :
+ : "r" (DOMAIN_USER_MANAGER |
+ DOMAIN_KERNEL_CLIENT |
+ DOMAIN_IO_CLIENT));
+ memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length);
+ /* set domain register to normal */
+ set_fs(get_fs());
+ flush_page_to_ram(0);
+ floppy_fiqsetup(dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE);
+ enable_irq(dma->dma_irq);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+ unsigned long dma_base = dma->dma_base;
+ unsigned int ctrl;
+
+ switch (channel) {
+ case DMA_0: /* Physical DMA channels */
+ case DMA_1:
+ case DMA_2:
+ case DMA_3:
+ case DMA_S0:
+ case DMA_S1:
+ disable_irq(dma->dma_irq);
+ ctrl = inb_t(dma_base + CR);
+ outb_t(ctrl & ~DMA_CR_E, dma_base + CR);
+ break;
+
+ case DMA_VIRTUAL_FLOPPY:
+ disable_irq(dma->dma_irq);
+ break;
+ }
+}
+
+__initfunc(void arch_dma_init(dma_t *dma))
+{
+ outb(0, IOMD_IO0CR);
+ outb(0, IOMD_IO1CR);
+ outb(0, IOMD_IO2CR);
+ outb(0, IOMD_IO3CR);
+
+// outb(0xf0, IOMD_DMATCR);
+
+ dma[0].dma_base = ioaddr(IOMD_IO0CURA);
+ dma[0].dma_irq = IRQ_DMA0;
+ dma[1].dma_base = ioaddr(IOMD_IO1CURA);
+ dma[1].dma_irq = IRQ_DMA1;
+ dma[2].dma_base = ioaddr(IOMD_IO2CURA);
+ dma[2].dma_irq = IRQ_DMA2;
+ dma[3].dma_base = ioaddr(IOMD_IO3CURA);
+ dma[3].dma_irq = IRQ_DMA3;
+ dma[4].dma_base = ioaddr(IOMD_SD0CURA);
+ dma[4].dma_irq = IRQ_DMAS0;
+ dma[5].dma_base = ioaddr(IOMD_SD1CURA);
+ dma[5].dma_irq = IRQ_DMAS1;
+ dma[6].dma_irq = 64;
+
+ /* Setup DMA channels 2,3 to be for podules
+ * and channels 0,1 for internal devices
+ */
+ outb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT);
+}
diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c
index 3c165c41d..6058a2863 100644
--- a/arch/arm/kernel/dma.c
+++ b/arch/arm/kernel/dma.c
@@ -1,199 +1,189 @@
/*
* linux/arch/arm/kernel/dma.c
*
- * Copyright (C) 1995, 1996 Russell King
+ * Copyright (C) 1995-1998 Russell King
+ *
+ * Front-end to the DMA handling. You must provide the following
+ * architecture-specific routines:
+ *
+ * int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_id);
+ * void arch_free_dma(dmach_t channel, dma_t *dma);
+ * void arch_enable_dma(dmach_t channel, dma_t *dma);
+ * void arch_disable_dma(dmach_t channel, dma_t *dma);
+ * int arch_get_dma_residue(dmach_t channel, dma_t *dma);
+ *
+ * Moved DMA resource allocation here...
*/
-
-#include <linux/config.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/mman.h>
+#include <linux/init.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
-#define KERNEL_ARCH_DMA
#include <asm/dma.h>
-static unsigned long dma_address[8];
-static unsigned long dma_count[8];
-static char dma_direction[8] = { -1, -1, -1, -1, -1, -1, -1};
+#include "dma.h"
-#if defined(CONFIG_ARCH_A5K) || defined(CONFIG_ARCH_RPC)
-#define DMA_PCIO
-#endif
-#if defined(CONFIG_ARCH_ARC) && defined(CONFIG_BLK_DEV_FD)
-#define DMA_OLD
-#endif
+static dma_t dma_chan[MAX_DMA_CHANNELS];
-void enable_dma (unsigned int dmanr)
+/* Get dma list
+ * for /proc/dma
+ */
+int get_dma_list(char *buf)
{
- switch (dmanr) {
-#ifdef DMA_PCIO
- case 2: {
- void *fiqhandler_start;
- unsigned int fiqhandler_length;
- extern void floppy_fiqsetup (unsigned long len, unsigned long addr,
- unsigned long port);
- switch (dma_direction[dmanr]) {
- case 1: {
- extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
- fiqhandler_start = &floppy_fiqin_start;
- fiqhandler_length = &floppy_fiqin_end - &floppy_fiqin_start;
- break;
- }
- case 0: {
- extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
- fiqhandler_start = &floppy_fiqout_start;
- fiqhandler_length = &floppy_fiqout_end - &floppy_fiqout_start;
- break;
- }
- default:
- printk ("enable_dma: dma%d not initialised\n", dmanr);
- return;
- }
- memcpy ((void *)0x1c, fiqhandler_start, fiqhandler_length);
- flush_page_to_ram(0);
- floppy_fiqsetup (dma_count[dmanr], dma_address[dmanr], (int)PCIO_FLOPPYDMABASE);
- enable_irq (64);
- return;
- }
-#endif
-#ifdef DMA_OLD
- case 0: { /* Data DMA */
- switch (dma_direction[dmanr]) {
- case 1: /* read */
- {
- extern unsigned char fdc1772_dma_read, fdc1772_dma_read_end;
- extern void fdc1772_setupdma(unsigned int count,unsigned int addr);
- unsigned long flags;
-#ifdef DEBUG
- printk("enable_dma fdc1772 data read\n");
-#endif
- save_flags(flags);
- cliIF();
-
- memcpy ((void *)0x1c, (void *)&fdc1772_dma_read,
- &fdc1772_dma_read_end - &fdc1772_dma_read);
- fdc1772_setupdma(dma_count[dmanr],dma_address[dmanr]); /* Sets data pointer up */
- enable_irq (64);
- restore_flags(flags);
- }
- break;
-
- case 0: /* write */
- {
- extern unsigned char fdc1772_dma_write, fdc1772_dma_write_end;
- extern void fdc1772_setupdma(unsigned int count,unsigned int addr);
- unsigned long flags;
-
-#ifdef DEBUG
- printk("enable_dma fdc1772 data write\n");
-#endif
- save_flags(flags);
- cliIF();
- memcpy ((void *)0x1c, (void *)&fdc1772_dma_write,
- &fdc1772_dma_write_end - &fdc1772_dma_write);
- fdc1772_setupdma(dma_count[dmanr],dma_address[dmanr]); /* Sets data pointer up */
- enable_irq (64);
-
- restore_flags(flags);
- }
- break;
- default:
- printk ("enable_dma: dma%d not initialised\n", dmanr);
- return;
- }
- }
- break;
+ int i, len = 0;
- case 1: { /* Command end FIQ - actually just sets a flag */
- /* Need to build a branch at the FIQ address */
- extern void fdc1772_comendhandler(void);
- unsigned long flags;
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ if (dma_chan[i].lock)
+ len += sprintf(buf + len, "%2d: %s\n",
+ i, dma_chan[i].device_id);
+ }
+ return len;
+}
- /*printk("enable_dma fdc1772 command end FIQ\n");*/
- save_flags(flags);
- cliIF();
-
- *((unsigned int *)0x1c)=0xea000000 | (((unsigned int)fdc1772_comendhandler-(0x1c+8))/4); /* B fdc1772_comendhandler */
+/* Request DMA channel
+ *
+ * On certain platforms, we have to allocate an interrupt as well...
+ */
+int request_dma(dmach_t channel, const char *device_id)
+{
+ if (channel < MAX_DMA_CHANNELS) {
+ int ret;
+
+ if (xchg(&dma_chan[channel].lock, 1) != 0)
+ return -EBUSY;
+
+ ret = arch_request_dma(channel, &dma_chan[channel], device_id);
+ if (!ret) {
+ dma_chan[channel].device_id = device_id;
+ dma_chan[channel].active = 0;
+ dma_chan[channel].invalid = 1;
+ } else
+ xchg(&dma_chan[channel].lock, 0);
+
+ return ret;
+ } else {
+ printk (KERN_ERR "Trying to allocate DMA%d\n", channel);
+ return -EINVAL;
+ }
+}
+
+/* Free DMA channel
+ *
+ * On certain platforms, we have to free interrupt as well...
+ */
+void free_dma(dmach_t channel)
+{
+ if (channel >= MAX_DMA_CHANNELS) {
+ printk (KERN_ERR "Trying to free DMA%d\n", channel);
+ return;
+ }
- restore_flags(flags);
+ if (xchg(&dma_chan[channel].lock, 0) == 0) {
+ if (dma_chan[channel].active) {
+ printk (KERN_ERR "Freeing active DMA%d\n", channel);
+ arch_disable_dma(channel, &dma_chan[channel]);
+ dma_chan[channel].active = 0;
}
- break;
-#endif
- case DMA_0:
- case DMA_1:
- case DMA_2:
- case DMA_3:
- case DMA_S0:
- case DMA_S1:
- arch_enable_dma (dmanr - DMA_0);
- break;
-
- default:
- printk ("enable_dma: dma %d not supported\n", dmanr);
+
+ printk (KERN_ERR "Trying to free free DMA%d\n", channel);
+ return;
}
+ arch_free_dma(channel, &dma_chan[channel]);
+}
+
+/* Set DMA Scatter-Gather list
+ */
+void set_dma_sg (dmach_t channel, dmasg_t *sg, int nr_sg)
+{
+ dma_chan[channel].sg = sg;
+ dma_chan[channel].sgcount = nr_sg;
+ dma_chan[channel].invalid = 1;
}
-void set_dma_mode (unsigned int dmanr, char mode)
+/* Set DMA address
+ *
+ * Copy address to the structure, and set the invalid bit
+ */
+void set_dma_addr (dmach_t channel, unsigned long physaddr)
{
- if (dmanr < 8) {
- if (mode == DMA_MODE_READ)
- dma_direction[dmanr] = 1;
- else if (mode == DMA_MODE_WRITE)
- dma_direction[dmanr] = 0;
- else
- printk ("set_dma_mode: dma%d: invalid mode %02X not supported\n",
- dmanr, mode);
- } else if (dmanr < MAX_DMA_CHANNELS)
- arch_set_dma_mode (dmanr - DMA_0, mode);
- else
- printk ("set_dma_mode: dma %d not supported\n", dmanr);
+ if (dma_chan[channel].active)
+ printk(KERN_ERR "set_dma_addr: altering DMA%d"
+ " address while DMA active\n",
+ channel);
+
+ dma_chan[channel].sg = &dma_chan[channel].buf;
+ dma_chan[channel].sgcount = 1;
+ dma_chan[channel].buf.address = physaddr;
+ dma_chan[channel].invalid = 1;
}
-void set_dma_addr (unsigned int dmanr, unsigned int addr)
+/* Set DMA byte count
+ *
+ * Copy address to the structure, and set the invalid bit
+ */
+void set_dma_count (dmach_t channel, unsigned long count)
{
- if (dmanr < 8)
- dma_address[dmanr] = (unsigned long)addr;
- else if (dmanr < MAX_DMA_CHANNELS)
- arch_set_dma_addr (dmanr - DMA_0, addr);
- else
- printk ("set_dma_addr: dma %d not supported\n", dmanr);
+ if (dma_chan[channel].active)
+ printk(KERN_ERR "set_dma_count: altering DMA%d"
+ " count while DMA active\n",
+ channel);
+
+ dma_chan[channel].sg = &dma_chan[channel].buf;
+ dma_chan[channel].sgcount = 1;
+ dma_chan[channel].buf.length = count;
+ dma_chan[channel].invalid = 1;
}
-void set_dma_count (unsigned int dmanr, unsigned int count)
+/* Set DMA direction mode
+ */
+void set_dma_mode (dmach_t channel, dmamode_t mode)
{
- if (dmanr < 8)
- dma_count[dmanr] = (unsigned long)count;
- else if (dmanr < MAX_DMA_CHANNELS)
- arch_set_dma_count (dmanr - DMA_0, count);
- else
- printk ("set_dma_count: dma %d not supported\n", dmanr);
+ if (dma_chan[channel].active)
+ printk(KERN_ERR "set_dma_mode: altering DMA%d"
+ " mode while DMA active\n",
+ channel);
+
+ dma_chan[channel].dma_mode = mode;
+ dma_chan[channel].invalid = 1;
}
-int get_dma_residue (unsigned int dmanr)
+/* Enable DMA channel
+ */
+void enable_dma (dmach_t channel)
{
- if (dmanr < 8) {
- switch (dmanr) {
-#if defined(CONFIG_ARCH_A5K) || defined(CONFIG_ARCH_RPC)
- case 2: {
- extern int floppy_fiqresidual (void);
- return floppy_fiqresidual ();
- }
-#endif
-#if defined(CONFIG_ARCH_ARC) && defined(CONFIG_BLK_DEV_FD)
- case 0: {
- extern unsigned int fdc1772_bytestogo;
- return fdc1772_bytestogo;
+ if (dma_chan[channel].lock) {
+ if (dma_chan[channel].active == 0) {
+ dma_chan[channel].active = 1;
+ arch_enable_dma(channel, &dma_chan[channel]);
}
-#endif
- default:
- return -1;
+ } else
+ printk (KERN_ERR "Trying to enable free DMA%d\n", channel);
+}
+
+/* Disable DMA channel
+ */
+void disable_dma (dmach_t channel)
+{
+ if (dma_chan[channel].lock) {
+ if (dma_chan[channel].active == 1) {
+ dma_chan[channel].active = 0;
+ arch_disable_dma(channel, &dma_chan[channel]);
}
- } else if (dmanr < MAX_DMA_CHANNELS)
- return arch_dma_count (dmanr - DMA_0);
- return -1;
+ } else
+ printk (KERN_ERR "Trying to disable free DMA%d\n", channel);
+}
+
+int get_dma_residue(dmach_t channel)
+{
+ return arch_get_dma_residue(channel, &dma_chan[channel]);
+}
+
+__initfunc(void init_dma(void))
+{
+ arch_dma_init(dma_chan);
}
diff --git a/arch/arm/kernel/dma.h b/arch/arm/kernel/dma.h
new file mode 100644
index 000000000..e4c72c6af
--- /dev/null
+++ b/arch/arm/kernel/dma.h
@@ -0,0 +1,70 @@
+/*
+ * arch/arm/kernel/dma.h
+ *
+ * Copyright (C) 1998 Russell King
+ *
+ * This header file describes the interface between the generic DMA handler
+ * (dma.c) and the architecture-specific DMA backends (dma-*.c)
+ */
+
+typedef struct {
+ dmasg_t buf; /* single DMA */
+ int sgcount; /* number of DMA SG */
+ dmasg_t *sg; /* DMA Scatter-Gather List */
+
+ unsigned int active:1; /* Transfer active */
+ unsigned int invalid:1; /* Address/Count changed */
+ dmamode_t dma_mode; /* DMA mode */
+
+ unsigned int lock; /* Device is allocated */
+ const char *device_id; /* Device name */
+
+ unsigned int dma_base; /* Controller base address */
+ int dma_irq; /* Controller IRQ */
+ int state; /* Controller state */
+ dmasg_t cur_sg; /* Current controller buffer */
+} dma_t;
+
+/* Prototype: int arch_request_dma(channel, dma, dev_id)
+ * Purpose : Perform architecture specific claiming of a DMA channel
+ * Params : channel - DMA channel number
+ * : dma - DMA structure (above) for channel
+ * : dev_id - device ID string passed with request
+ * Returns : 0 on success, E????? number on error
+ */
+int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_id);
+
+/* Prototype: int arch_free_dma(channel, dma)
+ * Purpose : Perform architecture specific freeing of a DMA channel
+ * Params : channel - DMA channel number
+ * : dma - DMA structure for channel
+ */
+void arch_free_dma(dmach_t channel, dma_t *dma);
+
+/* Prototype: void arch_enable_dma(channel, dma)
+ * Purpose : Enable a claimed DMA channel
+ * Params : channel - DMA channel number
+ * : dma - DMA structure for channel
+ */
+void arch_enable_dma(dmach_t channel, dma_t *dma);
+
+/* Prototype: void arch_disable_dma(channel, dma)
+ * Purpose : Disable a claimed DMA channel
+ * Params : channel - DMA channel number
+ * : dma - DMA structure for channel
+ */
+void arch_disable_dma(dmach_t channel, dma_t *dma);
+
+/* Prototype: int arch_get_dma_residue(channel, dma)
+ * Purpose : Return number of bytes left to DMA
+ * Params : channel - DMA channel number
+ * : dma - DMA structure for channel
+ * Returns : Number of bytes left to DMA
+ */
+int arch_get_dma_residue(dmach_t channel, dma_t *dma);
+
+/* Prototype: void arch_dma_init(dma)
+ * Purpose : Initialise architecture specific DMA
+ * Params : dma - pointer to array of DMA structures
+ */
+void arch_dma_init(dma_t *dma);
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c
index cc18252b3..f03147e66 100644
--- a/arch/arm/kernel/ecard.c
+++ b/arch/arm/kernel/ecard.c
@@ -13,6 +13,7 @@
* now register their own routine to control interrupts (recommended).
* 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled on reset from
* Linux. (Caused cards not to respond under RiscOS without hard reset).
+ * 15-Feb-1998 RMK Added DMA support
*/
#define ECARD_C
@@ -24,13 +25,14 @@
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/malloc.h>
+#include <linux/init.h>
-#include <asm/irq-no.h>
-#include <asm/ecard.h>
-#include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/arch/irq.h>
+#include <asm/ecard.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
#ifdef CONFIG_ARCH_ARC
#include <asm/arch/oldlatches.h>
@@ -59,9 +61,11 @@ static const struct expcard_blacklist {
BLACKLIST_LOADER(MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, atomwide_serial_loader),
BLACKLIST_LOADER(MANU_OAK, PROD_OAK_SCSI, oak_scsi_loader),
+/* Supported cards with broken loader */
+ { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI, noloader, "AlSystems PowerTec SCSI (loader blacklisted)" },
+
/* Unsupported cards with no loader */
-BLACKLIST_NOLOADER(MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI),
-BLACKLIST_NOLOADER(MANU_MCS, PROD_MCS_CONNECT32)
+ BLACKLIST_NOLOADER(MANU_MCS, PROD_MCS_CONNECT32)
};
extern int setup_arm_irq(int, struct irqaction *);
@@ -75,7 +79,6 @@ static ecard_t expcard[MAX_ECARDS];
static signed char irqno_to_expcard[16];
static unsigned int ecard_numcards, ecard_numirqcards;
static unsigned int have_expmask;
-static unsigned long kmem;
static void ecard_def_irq_enable (ecard_t *ec, int irqnr)
{
@@ -178,20 +181,6 @@ void ecard_disablefiq (unsigned int fiqnr)
}
}
-static void *ecard_malloc(int len)
-{
- int r;
-
- len = (len + 3) & ~3;
-
- if (kmem) {
- r = kmem;
- kmem += len;
- return (void *)r;
- } else
- return kmalloc(len, GFP_KERNEL);
-}
-
static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
{
const int num_cards = ecard_numirqcards;
@@ -301,13 +290,13 @@ static void ecard_readbytes (void *addr, ecard_t *ec, int off, int len, int usel
lowaddress = 0;
}
while (lowaddress <= laddr) {
- byte = inb (ec->podaddr + haddr);
+ byte = inb(ec->podaddr + haddr);
lowaddress += 1;
}
while (len--) {
*a++ = byte;
if (len) {
- byte = inb (ec->podaddr + haddr);
+ byte = inb(ec->podaddr + haddr);
lowaddress += 1;
}
}
@@ -417,7 +406,7 @@ int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
}
if (c_id(&excd) == 0x80) { /* loader */
if (!ec->loader) {
- ec->loader = (loader_t)ecard_malloc(c_len(&excd));
+ ec->loader = (loader_t)kmalloc(c_len(&excd), GFP_KERNEL);
ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld);
}
continue;
@@ -441,20 +430,39 @@ int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
return 1;
}
-unsigned int ecard_address (ecard_t *ec, card_type_t memc, card_speed_t speed)
+unsigned int ecard_address (ecard_t *ec, card_type_t type, card_speed_t speed)
{
switch (ec->slot_no) {
case 0:
case 1:
case 2:
case 3:
- return (memc ? MEMCECIO_BASE : IOCECIO_BASE + (speed << 17)) + (ec->slot_no << 12);
+ switch (type) {
+ case ECARD_MEMC:
+ return MEMCECIO_BASE + (ec->slot_no << 12);
+
+ case ECARD_IOC:
+ return IOCECIO_BASE + (speed << 17) + (ec->slot_no << 12);
+
+ default:
+ return 0;
+ }
+
#ifdef IOCEC4IO_BASE
case 4:
case 5:
case 6:
case 7:
- return (memc ? 0 : IOCEC4IO_BASE + (speed << 17)) + ((ec->slot_no - 4) << 12);
+ switch (type) {
+ case ECARD_MEMC:
+ return 0;
+
+ case ECARD_IOC:
+ return IOCEC4IO_BASE + (speed << 17) + ((ec->slot_no - 4) << 12);
+
+ default:
+ return 0;
+ }
#endif
#ifdef MEMCEC8IO_BASE
case 8:
@@ -470,7 +478,7 @@ unsigned int ecard_address (ecard_t *ec, card_type_t memc, card_speed_t speed)
* If bit 1 of the first byte of the card is set,
* then the card does not exist.
*/
-static int ecard_probe (int card, int freeslot)
+__initfunc(static int ecard_probe (int card, int freeslot))
{
ecard_t *ec = expcard + freeslot;
struct ex_ecld excld;
@@ -480,7 +488,7 @@ static int ecard_probe (int card, int freeslot)
irqno_to_expcard[card] = -1;
ec->slot_no = card;
- if ((ec->podaddr = ecard_address (ec, 0, ECARD_SYNC)) == 0)
+ if ((ec->podaddr = ecard_address (ec, ECARD_IOC, ECARD_SYNC)) == 0)
return 0;
excld.r_ecld = 2;
@@ -490,8 +498,9 @@ static int ecard_probe (int card, int freeslot)
irqno_to_expcard[card] = freeslot;
- ec->irq = -1;
- ec->fiq = -1;
+ ec->irq = NO_IRQ;
+ ec->fiq = NO_IRQ;
+ ec->dma = NO_DMA;
ec->cld.ecld = e_ecld(&excld);
ec->cld.manufacturer = e_manu(&excld);
ec->cld.product = e_prod(&excld);
@@ -514,15 +523,20 @@ static int ecard_probe (int card, int freeslot)
break;
}
- if (card != 8) {
- ec->irq = 32 + card;
+ ec->irq = 32 + card;
#if 0
- ec->fiq = 96 + card;
+ /* We don't support FIQs on expansion cards at the moment */
+ ec->fiq = 96 + card;
#endif
- } else {
+#ifdef CONFIG_ARCH_RPC
+ if (card != 8) {
+ /* On RiscPC, only first two slots have DMA capability
+ */
+ if (card < 2)
+ ec->dma = 2 + card;
+ } else
ec->irq = 11;
- ec->fiq = -1;
- }
+#endif
if ((ec->cld.ecld & 0x78) == 0) {
struct in_chunk_dir incd;
@@ -551,11 +565,10 @@ static struct irqaction irqexpansioncard = { ecard_irq_noexpmask, SA_INTERRUPT,
* Locate all hardware - interrupt management and
* actual cards.
*/
-unsigned long ecard_init(unsigned long start_mem)
+__initfunc(void ecard_init(void))
{
int i, nc = 0;
- kmem = (start_mem | 3) & ~3;
memset (expcard, 0, sizeof (expcard));
#ifdef HAS_EXPMASK
@@ -565,6 +578,7 @@ unsigned long ecard_init(unsigned long start_mem)
have_expmask = -1;
}
#endif
+
printk("Installed expansion cards:");
/*
@@ -578,27 +592,25 @@ unsigned long ecard_init(unsigned long start_mem)
ecard_numirqcards = nc;
- /*
- * Now probe other cards with different interrupt lines
+ /* Now probe other cards with different interrupt lines
*/
#ifdef MEMCEC8IO_BASE
if (ecard_probe (8, nc))
nc += 1;
#endif
+
printk("\n");
ecard_numcards = nc;
if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
printk ("Could not allocate interrupt for expansion cards\n");
- return kmem;
+ return;
}
#ifdef HAS_EXPMASK
if (nc && have_expmask)
EXPMASK_ENABLE = have_expmask;
#endif
+
oldlatch_init ();
- start_mem = kmem;
- kmem = 0;
- return start_mem;
}
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index a2de41f33..f2d752cd4 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -9,7 +9,7 @@
* Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes
* it to save wrong values... Be aware!
*/
-#include <linux/config.h> /* for CONFIG_ARCH_EBSA110 /*
+#include <linux/config.h> /* for CONFIG_ARCH_EBSA110 */
#include <linux/autoconf.h>
#include <linux/linkage.h>
@@ -21,15 +21,6 @@
.text
-@ Offsets into task structure
-@ ---------------------------
-@
-#define STATE 0
-#define COUNTER 4
-#define PRIORITY 8
-#define FLAGS 12
-#define SIGPENDING 16
-
#define PF_TRACESYS 0x20
@ Bad Abort numbers
@@ -82,21 +73,24 @@
strb r12, [r12, #0x38] @ Disable FIQ register
.endm
- .macro get_irqnr_and_base, irqnr, base
+ .macro get_irqnr_and_base, irqnr, irqstat, base
mov r4, #ioc_base_high @ point at IOC
.if ioc_base_low
orr r4, r4, #ioc_base_low
.endif
- ldrb \irqnr, [r4, #0x24] @ get high priority first
+ ldrb \irqstat, [r4, #0x24] @ get high priority first
adr \base, irq_prio_h
- teq \irqnr, #0
+ teq \irqstat, #0
#ifdef IOMD_BASE
- ldreqb \irqnr, [r4, #0x1f4] @ get dma
+ ldreqb \irqstat, [r4, #0x1f4] @ get dma
adreq \base, irq_prio_d
- teqeq \irqnr, #0
+ teqeq \irqstat, #0
#endif
- ldreqb \irqnr, [r4, #0x14] @ get low priority
+ ldreqb \irqstat, [r4, #0x14] @ get low priority
adreq \base, irq_prio_l
+
+ teq \irqstat, #0
+ ldrneb \irqnr, [r5, \irqstat] @ get IRQ number
.endm
/*
@@ -160,10 +154,13 @@ irq_prio_h: .byte 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
.macro disable_fiq
.endm
- .macro get_irqnr_and_base, irqnr, base
+ .macro get_irqnr_and_base, irqnr, irqstat, base
mov r4, #0xf3000000
- ldrb \irqnr, [r4] @ get interrupts
+ ldrb \irqstat, [r4] @ get interrupts
adr \base, irq_prio_ebsa110
+
+ teq \irqstat, #0
+ ldrneb \irqnr, [r5, \irqstat] @ get IRQ number
.endm
.macro irq_prio_table
@@ -189,6 +186,26 @@ irq_prio_ebsa110:
.byte 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2
.endm
+#elif defined(CONFIG_ARCH_EBSA285)
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base
+ mov r4, #0xfe000000
+ ldr \irqstat, [r4, #0x180] @ get interrupts
+ mov \irqnr, #0
+1001: tst \irqstat, #1
+ addeq \irqnr, \irqnr, #1
+ moveq \irqstat, \irqstat, lsr #1
+ tsteq \irqnr, #32
+ beq 1001b
+ teq \irqnr, #32
+ .endm
+
+ .macro irq_prio_table
+ .endm
+
#else
#error Unknown architecture
#endif
@@ -267,6 +284,16 @@ irq_prio_ebsa110:
.endm
/*=============================================================================
+ * Address exception handler
+ *-----------------------------------------------------------------------------
+ * These aren't too critical.
+ * (they're not supposed to happen, and won't happen in 32-bit mode).
+ */
+
+vector_addrexcptn:
+ b vector_addrexcptn
+
+/*=============================================================================
* Undefined FIQs
*-----------------------------------------------------------------------------
* Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
@@ -400,86 +427,6 @@ vector_data: @
b __dabt_svc @ 3 (SVC_26 / SVC_32)
/*=============================================================================
- * Undefined instruction handler
- *-----------------------------------------------------------------------------
- * Handles floating point instructions
- */
-__und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
- stmia sp, {r0 - r12} @ Save r0 - r12
- add r8, sp, #S_PC
- stmdb r8, {sp, lr}^ @ Save user r0 - r12
- ldr r4, .LCund
- ldmia r4, {r5 - r7}
- stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0
- mov fp, #0
-
- adr r1, .LC2
- ldmia r1, {r1, r4}
- ldr r1, [r1]
- get_current_task r2
- teq r1, r2
- blne SYMBOL_NAME(math_state_restore)
- adrsvc, al, r9, SYMBOL_NAME(fpreturn)
- adrsvc al, lr, SYMBOL_NAME(fpundefinstr)
- ldr pc, [r4] @ Call FP module USR entry point
-
- .globl SYMBOL_NAME(fpundefinstr)
-SYMBOL_NAME(fpundefinstr): @ Called by FP module on undefined instr
- mov r0, lr
- mov r1, sp
- mrs r4, cpsr @ Enable interrupts
- bic r4, r4, #I_BIT
- msr cpsr, r4
- bl SYMBOL_NAME(do_undefinstr)
- b ret_from_exception @ Normal FP exit
-
-__und_svc: sub sp, sp, #S_FRAME_SIZE
- stmia sp, {r0 - r12} @ save r0 - r12
- mov r6, lr
- ldr r7, .LCund
- ldmia r7, {r7 - r9}
- add r5, sp, #S_FRAME_SIZE
- add r4, sp, #S_SP
- stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
-
- adr r1, .LC2
- ldmia r1, {r1, r4}
- ldr r1, [r1]
- mov r2, sp, lsr #13
- mov r2, r2, lsl #13
- teq r1, r2
- blne SYMBOL_NAME(math_state_restore)
- adrsvc al, r9, SYMBOL_NAME(fpreturnsvc)
- adrsvc al, lr, SYMBOL_NAME(fpundefinstrsvc)
- ldr pc, [r4] @ Call FP module SVC entry point
-
- .globl SYMBOL_NAME(fpundefinstrsvc)
-SYMBOL_NAME(fpundefinstrsvc):
- mov r0, r5 @ unsigned long pc
- mov r1, sp @ struct pt_regs *regs
- bl SYMBOL_NAME(do_undefinstr)
-
- .globl SYMBOL_NAME(fpreturnsvc)
-SYMBOL_NAME(fpreturnsvc):
- ldr lr, [sp, #S_PSR] @ Get SVC cpsr
- msr spsr, lr
- ldmia sp, {r0 - pc}^ @ Restore SVC registers
-
-.LC2: .word SYMBOL_NAME(last_task_used_math)
- .word SYMBOL_NAME(fp_enter)
-
-__und_invalid: sub sp, sp, #S_FRAME_SIZE
- stmia sp, {r0 - lr}
- mov r7, r0
- ldr r4, .LCund
- ldmia r4, {r5, r6} @ Get UND/IRQ/FIQ/ABT pc, cpsr
- add r4, sp, #S_PC
- stmia r4, {r5, r6, r7} @ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0
- mov r0, sp @ struct pt_regs *regs
- mov r1, #BAD_UNDEFINSTR @ int reason
- and r2, r6, #31 @ int mode
- b SYMBOL_NAME(bad_mode) @ Does not ever return...
-/*=============================================================================
* Prefetch abort handler
*-----------------------------------------------------------------------------
*/
@@ -531,14 +478,62 @@ t: .ascii "*** undef ***\r\n\0"
#endif
/*=============================================================================
- * Address exception handler
+ * Data abort handler code
*-----------------------------------------------------------------------------
- * These aren't too critical.
- * (they're not supposed to happen, and won't happen in 32-bit mode).
*/
+.LCprocfns: .word SYMBOL_NAME(processor)
-vector_addrexcptn:
- b vector_addrexcptn
+__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
+ stmia sp, {r0 - r12} @ save r0 - r12
+ add r3, sp, #S_PC
+ stmdb r3, {sp, lr}^
+ ldr r0, .LCabt
+ ldmia r0, {r0 - r2} @ Get USR pc, cpsr
+ stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0
+ mov fp, #0
+ mrs r2, cpsr @ Enable interrupts if they were
+ bic r2, r2, #I_BIT @ previously
+ msr cpsr, r2
+ ldr r2, .LCprocfns
+ mov lr, pc
+ ldr pc, [r2, #8] @ call processor specific code
+ mov r3, sp
+ bl SYMBOL_NAME(do_DataAbort)
+ b ret_from_sys_call
+
+__dabt_svc: sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - r12} @ save r0 - r12
+ ldr r2, .LCabt
+ add r0, sp, #S_FRAME_SIZE
+ add r5, sp, #S_SP
+ mov r1, lr
+ ldmia r2, {r2 - r4} @ get pc, cpsr
+ stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+ tst r3, #I_BIT
+ mrseq r0, cpsr @ Enable interrupts if they were
+ biceq r0, r0, #I_BIT @ previously
+ msreq cpsr, r0
+ mov r0, r2
+ ldr r2, .LCprocfns
+ mov lr, pc
+ ldr pc, [r2, #8] @ call processor specific code
+ mov r3, sp
+ bl SYMBOL_NAME(do_DataAbort)
+ ldr r0, [sp, #S_PSR]
+ msr spsr, r0
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+
+__dabt_invalid: sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact]
+ mov r7, r0
+ ldr r4, .LCabt
+ ldmia r4, {r5, r6} @ Get SVC pc, cpsr
+ add r4, sp, #S_PC
+ stmia r4, {r5, r6, r7} @ Save SVC pc, cpsr, old_r0
+ mov r0, sp
+ mov r1, #BAD_DATA
+ and r2, r6, #31
+ b SYMBOL_NAME(bad_mode)
/*=============================================================================
* Interrupt (IRQ) handler
@@ -551,9 +546,7 @@ __irq_usr: sub sp, sp, #S_FRAME_SIZE
ldr r4, .LCirq
ldmia r4, {r5 - r7} @ get saved PC, SPSR
stmia r8, {r5 - r7} @ save pc, psr, old_r0
-1: get_irqnr_and_base r6, r5
- teq r6, #0
- ldrneb r0, [r5, r6] @ get IRQ number
+1: get_irqnr_and_base r0, r6, r5
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@@ -572,9 +565,7 @@ __irq_svc: sub sp, sp, #S_FRAME_SIZE
add r5, sp, #S_FRAME_SIZE
add r4, sp, #S_SP
stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
-1: get_irqnr_and_base r6, r5
- teq r6, #0
- ldrneb r0, [r5, r6] @ get IRQ number
+1: get_irqnr_and_base r0, r6, r5
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@@ -598,63 +589,85 @@ __irq_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate space on stack for frame
b SYMBOL_NAME(bad_mode)
/*=============================================================================
- * Data abort handler code
+ * Undefined instruction handler
*-----------------------------------------------------------------------------
+ * Handles floating point instructions
*/
-.LCprocfns: .word SYMBOL_NAME(processor)
-
-__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
- stmia sp, {r0 - r12} @ save r0 - r12
- add r3, sp, #S_PC
- stmdb r3, {sp, lr}^
- ldr r0, .LCabt
- ldmia r0, {r0 - r2} @ Get USR pc, cpsr
- stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0
+__und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
+ stmia sp, {r0 - r12} @ Save r0 - r12
+ add r8, sp, #S_PC
+ stmdb r8, {sp, lr}^ @ Save user r0 - r12
+ ldr r4, .LCund
+ ldmia r4, {r5 - r7}
+ stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0
mov fp, #0
- mrs r2, cpsr @ Enable interrupts if they were
- bic r2, r2, #I_BIT @ previously
- msr cpsr, r2
- ldr r2, .LCprocfns
- mov lr, pc
- ldr pc, [r2, #8] @ call processor specific code
- mov r3, sp
- bl SYMBOL_NAME(do_DataAbort)
- b ret_from_sys_call
-__dabt_svc: sub sp, sp, #S_FRAME_SIZE
+ adr r1, .LC2
+ ldmia r1, {r1, r4}
+ ldr r1, [r1]
+ get_current_task r2
+ teq r1, r2
+ blne SYMBOL_NAME(math_state_restore)
+ adrsvc al, r9, SYMBOL_NAME(fpreturn)
+ adrsvc al, lr, SYMBOL_NAME(fpundefinstr)
+ ldr pc, [r4] @ Call FP module USR entry point
+
+ .globl SYMBOL_NAME(fpundefinstr)
+SYMBOL_NAME(fpundefinstr): @ Called by FP module on undefined instr
+ mov r0, lr
+ mov r1, sp
+ mrs r4, cpsr @ Enable interrupts
+ bic r4, r4, #I_BIT
+ msr cpsr, r4
+ bl SYMBOL_NAME(do_undefinstr)
+ b ret_from_exception @ Normal FP exit
+
+__und_svc: sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
- ldr r2, .LCabt
- add r0, sp, #S_FRAME_SIZE
- add r5, sp, #S_SP
- mov r1, lr
- ldmia r2, {r2 - r4} @ get pc, cpsr
- stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
- tst r3, #I_BIT
- mrseq r0, cpsr @ Enable interrupts if they were
- biceq r0, r0, #I_BIT @ previously
- msreq cpsr, r0
- mov r0, r2
- ldr r2, .LCprocfns
- mov lr, pc
- ldr pc, [r2, #8] @ call processor specific code
- mov r3, sp
- bl SYMBOL_NAME(do_DataAbort)
- ldr r0, [sp, #S_PSR]
- msr spsr, r0
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ mov r6, lr
+ ldr r7, .LCund
+ ldmia r7, {r7 - r9}
+ add r5, sp, #S_FRAME_SIZE
+ add r4, sp, #S_SP
+ stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
-__dabt_invalid: sub sp, sp, #S_FRAME_SIZE
- stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact]
+ adr r1, .LC2
+ ldmia r1, {r1, r4}
+ ldr r1, [r1]
+ mov r2, sp, lsr #13
+ mov r2, r2, lsl #13
+ teq r1, r2
+ blne SYMBOL_NAME(math_state_restore)
+ adrsvc al, r9, SYMBOL_NAME(fpreturnsvc)
+ adrsvc al, lr, SYMBOL_NAME(fpundefinstrsvc)
+ ldr pc, [r4] @ Call FP module SVC entry point
+
+ .globl SYMBOL_NAME(fpundefinstrsvc)
+SYMBOL_NAME(fpundefinstrsvc):
+ mov r0, r5 @ unsigned long pc
+ mov r1, sp @ struct pt_regs *regs
+ bl SYMBOL_NAME(do_undefinstr)
+
+ .globl SYMBOL_NAME(fpreturnsvc)
+SYMBOL_NAME(fpreturnsvc):
+ ldr lr, [sp, #S_PSR] @ Get SVC cpsr
+ msr spsr, lr
+ ldmia sp, {r0 - pc}^ @ Restore SVC registers
+
+.LC2: .word SYMBOL_NAME(last_task_used_math)
+ .word SYMBOL_NAME(fp_enter)
+
+__und_invalid: sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - lr}
mov r7, r0
- ldr r4, .LCabt
- ldmia r4, {r5, r6} @ Get SVC pc, cpsr
+ ldr r4, .LCund
+ ldmia r4, {r5, r6} @ Get UND/IRQ/FIQ/ABT pc, cpsr
add r4, sp, #S_PC
- stmia r4, {r5, r6, r7} @ Save SVC pc, cpsr, old_r0
- mov r0, sp
- mov r1, #BAD_DATA
- and r2, r6, #31
- b SYMBOL_NAME(bad_mode)
-
+ stmia r4, {r5, r6, r7} @ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0
+ mov r0, sp @ struct pt_regs *regs
+ mov r1, #BAD_UNDEFINSTR @ int reason
+ and r2, r6, #31 @ int mode
+ b SYMBOL_NAME(bad_mode) @ Does not ever return...
#include "entry-common.S"
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 5725e1781..98c41266f 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -1,56 +1,54 @@
-/*
- *=============================================================================
- * Low-level interface code
- *-----------------------------------------------------------------------------
- * Trap initialisation
- *-----------------------------------------------------------------------------
- *
- * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20
- * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes
- * some excess cycles).
- *
- * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000
- * (the kernel).
- * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for
- * the actuall address to jump to.
- */
-/*
- * these go into 0x00
- */
-.Lbranches: swi SYS_ERROR0
- ldr pc, .Lbranches + 0xe4
- ldr pc, .Lbranches + 0xe8
- ldr pc, .Lbranches + 0xec
- ldr pc, .Lbranches + 0xf0
- ldr pc, .Lbranches + 0xf4
- ldr pc, .Lbranches + 0xf8
- ldr pc, .Lbranches + 0xfc
-/*
- * this is put into 0xe4 and above
- */
-.Ljump_addresses:
- .word vector_undefinstr @ 0xe4
- .word vector_swi @ 0xe8
- .word vector_prefetch @ 0xec
- .word vector_data @ 0xf0
- .word vector_addrexcptn @ 0xf4
- .word vector_IRQ @ 0xf8
- .word _unexp_fiq @ 0xfc
-/*
- * initialise the trap system
+/*============================================================================
+ * All exits to user mode from the kernel go through this code.
*/
-ENTRY(trap_init)
- stmfd sp!, {r4 - r7, lr}
- initialise_traps_extra
- mov r0, #0xe4
- adr r1, .Ljump_addresses
- ldmia r1, {r1 - r6}
- stmia r0, {r1 - r6}
- mov r0, #0
- adr r1, .Lbranches
- ldmia r1, {r1 - r7}
- stmia r0, {r1 - r7}
- LOADREGS(fd, sp!, {r4 - r7, pc})
+
+ .globl ret_from_sys_call
+
+ .globl SYMBOL_NAME(fpreturn)
+SYMBOL_NAME(fpreturn):
+ret_from_exception:
+ adr r0, 1f
+ ldmia r0, {r0, r1}
+ ldr r0, [r0]
+ ldr r1, [r1]
+ tst r0, r1
+ blne SYMBOL_NAME(do_bottom_half)
+ret_from_intr: ldr r0, [sp, #S_PSR]
+ tst r0, #3
+ beq ret_with_reschedule
+ b ret_from_all
+
+ret_signal: mov r1, sp
+ adrsvc al, lr, ret_from_all
+ b SYMBOL_NAME(do_signal)
+
+2: bl SYMBOL_NAME(schedule)
+
+ret_from_sys_call:
+ adr r0, 1f
+ ldmia r0, {r0, r1}
+ ldr r0, [r0]
+ ldr r1, [r1]
+ tst r0, r1
+ adrsvc ne, lr, ret_from_intr
+ bne SYMBOL_NAME(do_bottom_half)
+
+ret_with_reschedule:
+ ldr r0, 1f + 8
+ ldr r0, [r0]
+ teq r0, #0
+ bne 2b
+
+ get_current_task r1
+ ldr r1, [r1, #TSK_SIGPENDING]
+ teq r1, #0
+ bne ret_signal
+
+ret_from_all: restore_user_regs
+
+1: .word SYMBOL_NAME(bh_mask)
+ .word SYMBOL_NAME(bh_active)
+ .word SYMBOL_NAME(need_resched)
/*=============================================================================
* SWI handler
@@ -77,7 +75,7 @@ vector_swi: save_user_regs
bcs 2f
get_current_task r5
- ldr ip, [r5, #FLAGS] @ check for syscall tracing
+ ldr ip, [r5, #TSK_FLAGS] @ check for syscall tracing
tst ip, #PF_TRACESYS
bne 1f
@@ -91,7 +89,7 @@ vector_swi: save_user_regs
1: ldr r7, [sp, #S_IP] @ save old IP
mov r0, #0
- str r7, [sp, #S_IP] @ trace entry [IP = 0]
+ str r0, [sp, #S_IP] @ trace entry [IP = 0]
bl SYMBOL_NAME(syscall_trace)
str r7, [sp, #S_IP]
ldmia sp, {r0 - r3} @ have to reload r0 - r3
@@ -193,57 +191,59 @@ sys_rt_sigreturn_wrapper:
add r0, sp, #4
b SYMBOL_NAME(sys_rt_sigreturn)
-/*============================================================================
- * All exits to user mode from the kernel go through this code.
+/*
+ *=============================================================================
+ * Low-level interface code
+ *-----------------------------------------------------------------------------
+ * Trap initialisation
+ *-----------------------------------------------------------------------------
+ *
+ * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20
+ * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes
+ * some excess cycles).
+ *
+ * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000
+ * (the kernel).
+ * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for
+ * the actuall address to jump to.
*/
-
- .globl ret_from_sys_call
-
- .globl SYMBOL_NAME(fpreturn)
-SYMBOL_NAME(fpreturn):
-ret_from_exception:
- adr r0, 1f
- ldmia r0, {r0, r1}
- ldr r0, [r0]
- ldr r1, [r1]
- tst r0, r1
- blne SYMBOL_NAME(do_bottom_half)
-ret_from_intr: ldr r0, [sp, #S_PSR]
- tst r0, #3
- beq ret_with_reschedule
- b ret_from_all
-
-ret_signal: mov r1, sp
- adrsvc al, lr, ret_from_all
- b SYMBOL_NAME(do_signal)
-
-2: bl SYMBOL_NAME(schedule)
-
-ret_from_sys_call:
- adr r0, 1f
- ldmia r0, {r0, r1}
- ldr r0, [r0]
- ldr r1, [r1]
- tst r0, r1
- adrsvc ne, lr, ret_from_intr
- bne SYMBOL_NAME(do_bottom_half)
-
-ret_with_reschedule:
- ldr r0, 1f + 8
- ldr r0, [r0]
- teq r0, #0
- bne 2b
-
- get_current_task r1
- ldr r1, [r1, #SIGPENDING]
- teq r1, #0
- bne ret_signal
-
-ret_from_all: restore_user_regs
-
-1: .word SYMBOL_NAME(bh_mask)
- .word SYMBOL_NAME(bh_active)
- .word SYMBOL_NAME(need_resched)
+/*
+ * these go into 0x00
+ */
+.Lbranches: swi SYS_ERROR0
+ ldr pc, .Lbranches + 0xe4
+ ldr pc, .Lbranches + 0xe8
+ ldr pc, .Lbranches + 0xec
+ ldr pc, .Lbranches + 0xf0
+ ldr pc, .Lbranches + 0xf4
+ ldr pc, .Lbranches + 0xf8
+ ldr pc, .Lbranches + 0xfc
+/*
+ * this is put into 0xe4 and above
+ */
+.Ljump_addresses:
+ .word vector_undefinstr @ 0xe4
+ .word vector_swi @ 0xe8
+ .word vector_prefetch @ 0xec
+ .word vector_data @ 0xf0
+ .word vector_addrexcptn @ 0xf4
+ .word vector_IRQ @ 0xf8
+ .word _unexp_fiq @ 0xfc
+/*
+ * initialise the trap system
+ */
+ENTRY(trap_init)
+ stmfd sp!, {r4 - r7, lr}
+ initialise_traps_extra
+ mov r0, #0xe4
+ adr r1, .Ljump_addresses
+ ldmia r1, {r1 - r6}
+ stmia r0, {r1 - r6}
+ mov r0, #0
+ adr r1, .Lbranches
+ ldmia r1, {r1 - r7}
+ stmia r0, {r1 - r7}
+ LOADREGS(fd, sp!, {r4 - r7, pc})
/*============================================================================
* FP support
diff --git a/arch/arm/kernel/head-armo.S b/arch/arm/kernel/head-armo.S
index 7bd69ed5f..80d88e890 100644
--- a/arch/arm/kernel/head-armo.S
+++ b/arch/arm/kernel/head-armo.S
@@ -42,7 +42,7 @@ Lcontinue: str r5, [r6]
b SYMBOL_NAME(start_kernel)
LC1: .word SYMBOL_NAME(_stext)
-LC0: .word SYMBOL_NAME(_edata)
+LC0: .word SYMBOL_NAME(__bss_start)
.word SYMBOL_NAME(arm_id)
.word SYMBOL_NAME(_end)
.word SYMBOL_NAME(init_task_union)+8192
diff --git a/arch/arm/kernel/head-armv.S b/arch/arm/kernel/head-armv.S
index 0af401e43..e53b7144f 100644
--- a/arch/arm/kernel/head-armv.S
+++ b/arch/arm/kernel/head-armv.S
@@ -16,19 +16,21 @@
.globl __stext
/*
* Entry point and restart point. Entry *must* be called with r0 == 0,
- * MMU off.
+ * MMU off. Note! These should be unique!!! Please read Documentation/ARM-README
+ * for more information.
*
- * r1 = 0 -> ebsa (Ram @ 0x00000000)
- * r1 = 1 -> RPC (Ram @ 0x10000000)
- * r1 = 2 -> ebsit (???)
+ * r1 = 0 -> ebsa110 (Ram @ 0x00000000)
+ * r1 = 1 -> RPC (Ram @ 0x10000000)
+ * r1 = 2 -> ebsit (???)
* r1 = 3 -> nexuspci
+ * r1 = 4 -> ebsa285 (Ram @ 0x00000000)
*/
ENTRY(stext)
ENTRY(_stext)
__entry:
teq r0, #0 @ check for illegal entry...
bne .Lerror @ loop indefinitely
- cmp r1, #4 @ Unknown machine architecture
+ cmp r1, #5 @ Unknown machine architecture
bge .Lerror
@
@ First thing to do is to get the page tables set up so that we can call the kernel
@@ -57,18 +59,23 @@ __entry:
adr r4, .LCMachTypes
add r4, r4, r1, lsl #4
- ldmia r4, {r4, r5, r6} @ r4 = page dir in physical ram
-
+ ldmia r4, {r4, r5, r6}
+/*
+ * r4 = page dir in physical ram
+ * r5 = physical address of start of RAM
+ * r6 = I/O address
+ */
mov r0, r4
mov r1, #0
add r2, r0, #0x4000
1: str r1, [r0], #4 @ Clear page table
teq r0, r2
bne 1b
-@
-@ Add enough entries to allow the kernel to be called.
-@ It will sort out the real mapping in paging_init
-@
+/*
+ * Add enough entries to allow the kernel to be called.
+ * It will sort out the real mapping in paging_init.
+ * We map in 2MB of memory into 0xC0000000 - 0xC0200000
+ */
add r0, r4, #0x3000
mov r1, #0x0000000c @ SECT_CACHEABLE | SECT_BUFFERABLE
orr r1, r1, r8
@@ -121,7 +128,7 @@ __entry:
.Lbranch: .long .Lalready_done_mmap @ Real address of routine
- @ EBSA (pg dir phys, phys ram start, phys i/o)
+ @ EBSA110 (pg dir phys, phys ram start, phys i/o)
.LCMachTypes: .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
.long 0 @ Address of RAM
.long 0xe0000000 @ I/O address
@@ -145,6 +152,14 @@ __entry:
.long 0x10000000
.long 0
+ @ EBSA285
+ .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
+ .long 0 @ Address of RAM
+ .long 0x24000000 @ I/O base address (0x42000000 -> 0xFE000000)
+ .long 0
+
+
+
.LCProcTypes: @ ARM6 / 610
.long 0x41560600
.long 0xffffff00
@@ -168,7 +183,7 @@ __entry:
.long 0
-.LC0: .long SYMBOL_NAME(_edata)
+.LC0: .long SYMBOL_NAME(__bss_start)
.long SYMBOL_NAME(arm_id)
.long SYMBOL_NAME(_end)
.long SYMBOL_NAME(init_task_union)+8192
@@ -231,19 +246,84 @@ __entry:
b SYMBOL_NAME(start_kernel)
#if 1
+
+#if defined(CONFIG_ARCH_RPC)
+ .macro addruart,rx
+ mov \rx, #0xe0000000
+ orr \rx, \rx, #0x00010000
+ orr \rx, \rx, #0x00000fe0
+ .endm
+
+ .macro senduart,rd,rx
+ strb \rd, [\rx]
+ .endm
+
+ .macro busyuart,rd,rx
+1002: ldrb \rd, [\rx, #0x14]
+ and \rd, \rd, #0x60
+ bne 1002b
+ .endm
+
+ .macro waituart,rd,rx
+1001: ldrb \rd, [\rx, #0x18]
+ tst \rd, #0x10
+ beq 1001b
+ .endm
+
+#elif defined(CONFIG_ARCH_EBSA110)
+ .macro addruart,rx
+ mov \rx, #0xf0000000
+ orr \rx, \rx, #0x00000be0
+ .endm
+
+ .macro senduart,rd,rx
+ strb \rd, [\rx]
+ .endm
+
+ .macro busyuart,rd,rx
+1002: ldrb \rd, [\rx, #0x14]
+ and \rd, \rd, #0x60
+ bne 1002b
+ .endm
+
+ .macro waituart,rd,rx
+1001: ldrb \rd, [\rx, #0x18]
+ tst \rd, #0x10
+ beq 1001b
+ .endm
+
+#elif defined(CONFIG_ARCH_EBSA285)
+ .macro addruart,rx
+ mov \rx, #0xfe000000
+ .endm
+
+ .macro senduart,rd,rx
+ str \rd, [\rx, #0x160] @ UARTDR
+ .endm
+
+ .macro busyuart,rd,rx
+1001: ldr \rd, [\rx, #0x178] @ UARTFLG
+ tst \rd, #1 << 3
+ bne 1001b
+ .endm
+
+ .macro waituart,rd,rx
+ .endm
+#endif
+
/*
* Useful debugging routines
*/
- .globl _printhex8
-_printhex8: mov r1, #8
+ENTRY(printhex8)
+ mov r1, #8
b printhex
- .globl _printhex4
-_printhex4: mov r1, #4
+ENTRY(printhex4)
+ mov r1, #4
b printhex
- .globl _printhex2
-_printhex2: mov r1, #2
+ENTRY(printhex2)
+ mov r1, #2
printhex: ldr r2, =hexbuf
add r3, r2, r1
mov r1, #0
@@ -258,46 +338,23 @@ printhex: ldr r2, =hexbuf
bne 1b
mov r0, r2
- .globl _printascii
-_printascii:
-#ifdef CONFIG_ARCH_RPC
- mov r3, #0xe0000000
- orr r3, r3, #0x00010000
- orr r3, r3, #0x00000fe0
-#else
- mov r3, #0xf0000000
- orr r3, r3, #0x0be0
-#endif
- b 3f
-1: ldrb r2, [r3, #0x18]
- tst r2, #0x10
- beq 1b
- strb r1, [r3]
-2: ldrb r2, [r3, #0x14]
- and r2, r2, #0x60
- teq r2, #0x60
- bne 2b
+ENTRY(printascii)
+ addruart r3
+ b 2f
+1: waituart r2, r3
+ senduart r1, r3
+ busyuart r2, r3
teq r1, #'\n'
moveq r1, #'\r'
beq 1b
-3: teq r0, #0
+2: teq r0, #0
ldrneb r1, [r0], #1
teqne r1, #0
bne 1b
mov pc, lr
- .ltorg
-
- .globl _printch
-_printch:
-#ifdef CONFIG_ARCH_RPC
- mov r3, #0xe0000000
- orr r3, r3, #0x00010000
- orr r3, r3, #0x00000fe0
-#else
- mov r3, #0xf0000000
- orr r3, r3, #0x0be0
-#endif
+ENTRY(printch)
+ addruart r3
mov r1, r0
mov r0, #0
b 1b
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index e0fb7540a..1dde0e0ba 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -33,16 +33,10 @@
#include <asm/io.h>
#include <asm/system.h>
#include <asm/hardware.h>
-#include <asm/irq-no.h>
#include <asm/arch/irq.h>
+unsigned int local_bh_count[NR_CPUS];
unsigned int local_irq_count[NR_CPUS];
-#ifdef __SMP__
-atomic_t __arm_bh_counter;
-#else
-int __arm_bh_counter;
-#endif
-
spinlock_t irq_controller_lock;
#ifndef SMP
@@ -80,13 +74,21 @@ void enable_irq(unsigned int irq_nr)
struct irqaction *irq_action[NR_IRQS];
-/*
- * Bitmask indicating valid interrupt numbers
+#ifdef CONFIG_ARCH_ACORN
+/* Bitmask indicating valid interrupt numbers
+ * (to be moved to include/asm-arm/arch-*)
*/
unsigned long validirqs[NR_IRQS / 32] = {
- 0x003fffff, 0x000001ff, 0x000000ff, 0x00000000
+ 0x003ffe7f, 0x000001ff, 0x000000ff, 0x00000000
};
+#define valid_irq(x) ((x) < NR_IRQS && validirqs[(x) >> 5] & (1 << ((x) & 31)))
+#else
+
+#define valid_irq(x) ((x) < NR_IRQS)
+
+#endif
+
int get_irq_list(char *buf)
{
int i;
@@ -98,7 +100,7 @@ int get_irq_list(char *buf)
if (!action)
continue;
p += sprintf(p, "%3d: %10u %s",
- i, kstat.interrupts[i], action->name);
+ i, kstat_irqs(i), action->name);
for (action = action->next; action; action = action->next) {
p += sprintf(p, ", %s", action->name);
}
@@ -126,7 +128,7 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
cpu = smp_processor_id();
irq_enter(cpu, irq);
- kstat.interrupts[irq]++;
+ kstat.irqs[cpu][irq]++;
/* Return with this interrupt masked if no action */
status = 0;
@@ -143,13 +145,26 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
+
+ switch (irq) {
#if defined(HAS_IOMD) || defined(HAS_IOC)
- if (irq != IRQ_KEYBOARDTX && irq != IRQ_EXPANSIONCARD)
+ case IRQ_KEYBOARDTX:
+ case IRQ_EXPANSIONCARD:
+ break;
+#endif
+#ifdef HAS_IOMD
+ case IRQ_DMA0:
+ case IRQ_DMA1:
+ case IRQ_DMA2:
+ case IRQ_DMA3:
+ break;
#endif
- {
+
+ default:
spin_lock(&irq_controller_lock);
unmask_irq(irq);
spin_unlock(&irq_controller_lock);
+ break;
}
}
@@ -235,7 +250,7 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
unsigned long retval;
struct irqaction *action;
- if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31))))
+ if (!valid_irq(irq))
return -EINVAL;
if (!handler)
return -EINVAL;
@@ -263,7 +278,7 @@ void free_irq(unsigned int irq, void *dev_id)
struct irqaction * action, **p;
unsigned long flags;
- if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31)))) {
+ if (!valid_irq(irq)) {
printk(KERN_ERR "Trying to free IRQ%d\n",irq);
#ifdef CONFIG_DEBUG_ERRORS
__backtrace();
@@ -294,7 +309,7 @@ unsigned long probe_irq_on (void)
/* first snaffle up any unassigned irqs */
for (i = 15; i > 0; i--) {
- if (!irq_action[i]) {
+ if (!irq_action[i] && valid_irq(i)) {
enable_irq(i);
irqs |= 1 << i;
}
@@ -323,5 +338,7 @@ int probe_irq_off (unsigned long irqs)
__initfunc(void init_IRQ(void))
{
+ extern void init_dma(void);
irq_init_irq();
+ init_dma();
}
diff --git a/arch/arm/kernel/leds-ebsa285.c b/arch/arm/kernel/leds-ebsa285.c
new file mode 100644
index 000000000..f8add1ab7
--- /dev/null
+++ b/arch/arm/kernel/leds-ebsa285.c
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/kernel/leds-285.c
+ *
+ * Copyright (C) 1998 Russell King
+ *
+ * EBSA-285 LED control routines. We use the leds as follows:
+ *
+ * - Green - toggles state every 50 timer interrupts
+ * - Amber - On if system is not idle
+ * - Red - currently unused
+ */
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+static char led_state = XBUS_LED_RED | XBUS_LED_GREEN;
+
+void leds_event(led_event_t ledevt)
+{
+ unsigned long flags;
+
+ save_flags_cli(flags);
+
+ switch(ledevt) {
+ case led_idle_start:
+ led_state |= XBUS_LED_AMBER;
+ break;
+
+ case led_idle_end:
+ led_state &= ~XBUS_LED_AMBER;
+ break;
+
+ case led_timer:
+ led_state ^= XBUS_LED_GREEN;
+ break;
+
+ default:
+ break;
+ }
+
+ restore_flags(flags);
+
+ *XBUS_LEDS = led_state;
+}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 7f45e7c3c..9372539ef 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -69,8 +69,10 @@ asmlinkage int sys_idle(void)
current->priority = -100;
for (;;)
{
+#if 0 //def ARCH_IDLE_OK
if (!hlt_counter && !need_resched)
proc_idle ();
+#endif
run_task_queue(&tq_scheduler);
schedule();
}
@@ -142,7 +144,7 @@ void show_regs(struct pt_regs * regs)
" mrc p15, 0, %1, c2, c0\n"
" mrc p15, 0, %2, c3, c0\n"
: "=r" (ctrl), "=r" (transbase), "=r" (dac));
- printk("Control: %04X Table: %08X DAC: %08X",
+ printk("Control: %04X Table: %08X DAC: %08X ",
ctrl, transbase, dac);
}
#endif
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 5fa67df6c..f95e8de7e 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -27,18 +27,6 @@
*/
#define BREAKINST 0xef9f0001
-/* change a pid into a task struct. */
-static inline struct task_struct * get_task(int pid)
-{
- int i;
-
- for (i = 1; i < NR_TASKS; i++) {
- if (task[i] != NULL && (task[i]->pid == pid))
- return task[i];
- }
- return NULL;
-}
-
/*
* this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the TSS.
@@ -581,7 +569,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
if (pid == 1) /* you may not mess with init */
goto out;
ret = -ESRCH;
- if (!(child = get_task(pid)))
+ if (!(child = find_task_by_pid(pid)))
goto out;
ret = -EPERM;
if (request == PTRACE_ATTACH) {
diff --git a/arch/arm/kernel/setup-ebsa110.c b/arch/arm/kernel/setup-ebsa110.c
index 285284b7d..8cbfe7dc3 100644
--- a/arch/arm/kernel/setup-ebsa110.c
+++ b/arch/arm/kernel/setup-ebsa110.c
@@ -26,62 +26,95 @@
#include <linux/delay.h>
#include <linux/major.h>
#include <linux/utsname.h>
+#include <linux/init.h>
-#include <asm/segment.h>
-#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/setup.h>
+#include <asm/system.h>
#ifndef CONFIG_CMDLINE
#define CONFIG_CMDLINE "root=nfs rw console=ttyS1,38400n8"
#endif
+#define COMMAND_LINE_SIZE 256
+
#define MEM_SIZE (16*1024*1024)
-#define COMMAND_LINE_SIZE 256
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+ char saved_command_line[COMMAND_LINE_SIZE];
+struct processor processor;
+struct screen_info screen_info;
unsigned char aux_device_present;
unsigned long arm_id;
+
extern int root_mountflags;
extern int _etext, _edata, _end;
+extern const struct processor sa110_processor_functions;
#ifdef CONFIG_BLK_DEV_RAM
extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
extern int rd_image_start; /* starting block # of image */
-static inline void setup_ramdisk (void)
+static inline void setup_ramdisk(int start, int prompt, int load)
{
- rd_image_start = 0;
- rd_prompt = 1;
- rd_doload = 1;
+ rd_image_start = start;
+ rd_prompt = prompt;
+ rd_doload = load;
}
#else
-#define setup_ramdisk()
+#define setup_ramdisk(start,prompt,load)
#endif
+#ifdef PARAMS_BASE
+static struct param_struct *params = (struct param_struct *)PARAMS_BASE;
+
+static inline char *setup_params(unsigned long *mem_end_p)
+{
+ ROOT_DEV = to_kdev_t(params->u1.s.rootdev);
+ ORIG_X = params->u1.s.video_x;
+ ORIG_Y = params->u1.s.video_y;
+ ORIG_VIDEO_COLS = params->u1.s.video_num_cols;
+ ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
+
+ setup_ramdisk(params->u1.s.rd_start,
+ (params->u1.s.flags & FLAG_RDPROMPT) == 0,
+ (params->u1.s.flags & FLAG_RDLOAD) == 0);
+
+ *mem_end_p = 0xc0000000 + MEM_SIZE;
+
+ return params->commandline;
+}
+#else
static char default_command_line[] = CONFIG_CMDLINE;
-static char command_line[COMMAND_LINE_SIZE] = { 0, };
- char saved_command_line[COMMAND_LINE_SIZE];
-struct processor processor;
-extern const struct processor sa110_processor_functions;
+static inline char *setup_params(unsigned long *mem_end_p)
+{
+ ROOT_DEV = 0x00ff;
+
+ setup_ramdisk(0, 1, 1);
+
+ *mem_end_p = 0xc0000000 + MEM_SIZE;
+
+ return default_command_line;
+}
+#endif
-void setup_arch(char **cmdline_p,
- unsigned long * memory_start_p, unsigned long * memory_end_p)
+__initfunc(void setup_arch(char **cmdline_p,
+ unsigned long * memory_start_p, unsigned long * memory_end_p))
{
unsigned long memory_start, memory_end;
char c = ' ', *to = command_line, *from;
int len = 0;
memory_start = (unsigned long)&_end;
- memory_end = 0xc0000000 + MEM_SIZE;
- from = default_command_line;
processor = sa110_processor_functions;
- processor._proc_init ();
+ processor._proc_init();
- ROOT_DEV = 0x00ff;
- setup_ramdisk();
+ from = setup_params(&memory_end);
init_task.mm->start_code = TASK_SIZE;
init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index ac304fb3e..a3b86fef7 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -26,15 +26,16 @@
#include <linux/major.h>
#include <linux/utsname.h>
#include <linux/blk.h>
+#include <linux/init.h>
-#include <asm/segment.h>
-#include <asm/system.h>
#include <asm/hardware.h>
+#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/arch/mmu.h>
#include <asm/procinfo.h>
-#include <asm/io.h>
+#include <asm/segment.h>
#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/arch/mmu.h>
struct drive_info_struct { char dummy[32]; } drive_info;
struct screen_info screen_info;
@@ -153,13 +154,17 @@ static void setup_initrd (struct param_struct *params, unsigned long memory_end)
#define setup_initrd(p,m)
#endif
+#ifdef IOEB_BASE
static inline void check_ioeb_present(void)
{
if (((*IOEB_BASE) & 15) == 5)
armidlist[armidindex].features |= F_IOEB;
}
+#else
+#define check_ioeb_present()
+#endif
-static void get_processor_type (void)
+static inline void get_processor_type (void)
{
for (armidindex = 0; ; armidindex ++)
if (!((armidlist[armidindex].id ^ arm_id) &
@@ -179,11 +184,14 @@ static void get_processor_type (void)
#define COMMAND_LINE_SIZE 256
+/* Can this be initdata? --pb
+ * command_line can be, saved_command_line can't though
+ */
static char command_line[COMMAND_LINE_SIZE] = { 0, };
char saved_command_line[COMMAND_LINE_SIZE];
-void setup_arch(char **cmdline_p,
- unsigned long * memory_start_p, unsigned long * memory_end_p)
+__initfunc(void setup_arch(char **cmdline_p,
+ unsigned long * memory_start_p, unsigned long * memory_end_p))
{
static unsigned char smptrap;
unsigned long memory_start, memory_end;
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
index b1c679ec5..1f598e14d 100644
--- a/arch/arm/kernel/time.c
+++ b/arch/arm/kernel/time.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/smp.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -30,7 +31,6 @@
#include <asm/delay.h>
#include <linux/timex.h>
-#include <asm/irq-no.h>
#include <asm/hardware.h>
extern int setup_arm_irq(int, struct irqaction *);
@@ -143,12 +143,12 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
update_rtc ();
}
-static struct irqaction irqtimer0 = { timer_interrupt, 0, 0, "timer", NULL, NULL};
+static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL};
-void time_init(void)
+__initfunc(void time_init(void))
{
xtime.tv_sec = setup_timer();
xtime.tv_usec = 0;
- setup_arm_irq(IRQ_TIMER0, &irqtimer0);
+ setup_arm_irq(IRQ_TIMER, &irqtimer);
}
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 7ff7436c5..222fd6b17 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -59,17 +59,27 @@ static int verify_stack_pointer (unsigned long stackptr, int size)
return 0;
}
-static void dump_stack (unsigned long *start, unsigned long *end, int offset, int max)
+/*
+ * Dump out the contents of some memory nicely...
+ */
+void dump_mem(unsigned long bottom, unsigned long top)
{
- unsigned long *p;
+ unsigned long p = bottom & ~31;
int i;
- for (p = start + offset, i = 0; i < max && p < end; i++, p++) {
- if (i && (i & 7) == 0)
- printk ("\n ");
- printk ("%08lx ", *p);
+ for (p = bottom & ~31; p < top;) {
+ printk("%08lx: ", p);
+
+ for (i = 0; i < 8; i++, p += 4) {
+ if (p < bottom || p >= top)
+ printk(" ");
+ else
+ printk("%08lx ", *(unsigned long *)p);
+ if (i == 3)
+ printk(" ");
+ }
+ printk ("\n");
}
- printk ("\n");
}
/*
@@ -139,28 +149,26 @@ void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
break;
}
- console_verbose ();
- printk ("Internal error: %s: %x\n", str, err);
- printk ("CPU: %d", smp_processor_id());
- show_regs (regs);
- printk ("Process %s (pid: %d, stackpage=%08lx)\nStack: ",
+ console_verbose();
+ printk("Internal error: %s: %x\n", str, err);
+ printk("CPU: %d", smp_processor_id());
+ show_regs(regs);
+ printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, 4096+(unsigned long)current);
cstack = (unsigned long)(regs + 1);
sstack = 4096+(unsigned long)current;
- if (*(unsigned long *)sstack != STACK_MAGIC)
- printk ("*** corrupted stack page\n ");
-
- if (verify_stack_pointer (cstack, 4))
- printk ("%08lx invalid kernel stack pointer\n", cstack);
+ printk("Stack: ");
+ if (verify_stack_pointer(cstack, 4))
+ printk("invalid kernel stack pointer %08lx", cstack);
else if(cstack > sstack + 4096)
- printk("(sp overflow)\n");
+ printk("(sp overflow)");
else if(cstack < sstack)
- printk("(sp underflow)\n");
- else
- dump_stack ((unsigned long *)sstack, (unsigned long *)sstack + 1024,
- cstack - sstack, kstack_depth_to_print);
+ printk("(sp underflow)");
+ printk("\n");
+
+ dump_mem(cstack, sstack + 4096);
frameptr = regs->ARM_fp;
if (frameptr) {
@@ -172,7 +180,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
}
}
- dump_instr (instruction_pointer(regs));
+ dump_instr(instruction_pointer(regs));
died = 0;
if (ret != -1)
do_exit (ret);
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 10fad6b43..8ec13266d 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -5,17 +5,13 @@
#
L_TARGET := lib.a
-L_OBJS := backtrace.o bitops.o delay.o fp_support.o \
+L_OBJS := backtrace.o bitops.o checksum.o delay.o fp_support.o \
loaders.o memcpy.o memfastset.o system.o string.o uaccess.o
ifeq ($(PROCESSOR),armo)
L_OBJS += uaccess-armo.o
endif
-ifdef CONFIG_INET
- L_OBJS += checksum.o
-endif
-
ifdef CONFIG_ARCH_ACORN
L_OBJS += ll_char_wr.o io-acorn.o
ifdef CONFIG_ARCH_A5K
@@ -26,30 +22,27 @@ ifdef CONFIG_ARCH_ACORN
endif
endif
-ifdef CONFIG_ARCH_EBSA110
+ifeq ($(MACHINE),ebsa110)
L_OBJS += io-ebsa110.o
endif
-include $(TOPDIR)/Rules.make
+ifeq ($(MACHINE),ebsa285)
+ L_OBJS += io-ebsa285.o
+endif
-constants.h: getconstants
- ./getconstants > constants.h
+include $(TOPDIR)/Rules.make
-getconstants: getconstants.c getconstants.h
- $(HOSTCC) -D__KERNEL__ -o getconstants getconstants.c
+constants.h: getconsdata.o extractconstants.pl
+ $(PERL) extractconstants.pl $(OBJDUMP) > $@
-getconstants.h: getconsdata.c
+getconsdata.o: getconsdata.c
$(CC) $(CFLAGS) -c getconsdata.c
- $(PERL) extractinfo.perl $(OBJDUMP) > $@
%.o: %.S
-ifndef $(CONFIG_BINUTILS_NEW)
+ifneq ($(CONFIG_BINUTILS_NEW),y)
$(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s
$(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s
$(RM) ..tmp.$<.s
else
$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
endif
-
-clean:
- $(RM) getconstants constants.h getconstants.h
diff --git a/arch/arm/lib/extractconstants.pl b/arch/arm/lib/extractconstants.pl
new file mode 100644
index 000000000..ff095a232
--- /dev/null
+++ b/arch/arm/lib/extractconstants.pl
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+
+$OBJDUMP=$ARGV[0];
+
+sub swapdata {
+ local ($num) = @_;
+
+ return substr($num, 6, 2).substr($num, 4, 2).substr ($num, 2, 2).substr ($num, 0, 2);
+}
+
+open (DATA, $OBJDUMP.' --full-contents --section=.data getconsdata.o | grep \'^ 00\' |') ||
+ die ('Cant objdump!');
+while (<DATA>) {
+ ($addr, $data0, $data1, $data2, $data3) = split (' ');
+ $dat[hex($addr)] = hex(&swapdata($data0));
+ $dat[hex($addr)+4] = hex(&swapdata($data1));
+ $dat[hex($addr)+8] = hex(&swapdata($data2));
+ $dat[hex($addr)+12] = hex(&swapdata($data3));
+}
+close (DATA);
+
+open (DATA, $OBJDUMP.' --syms getconsdata.o |') || die ('Cant objdump!');
+while (<DATA>) {
+ /elf32/ && ( $elf = 1 );
+ /a.out/ && ( $aout = 1 );
+ next if ($aout && ! / 07 /);
+ next if ($elf && ! (/^00...... g/ && /.data/));
+ next if (!$aout && !$elf);
+
+ if ($aout) {
+ ($addr, $flags, $sect, $a1, $a2, $a3, $name) = split (' ');
+ $nam[hex($addr)] = substr($name, 1);
+ }
+ if ($elf) {
+ chomp;
+ $addr = substr ($_, 0, 8);
+ $name = substr ($_, 32);
+ $nam[hex($addr)] = $name;
+ }
+}
+close (DATA);
+
+print "/*\n * *** This file is automatically generated from getconsdata.c. Do not edit! ***\n */\n";
+for ($i = 0; $i < hex($addr)+4; $i += 4) {
+ print "#define $nam[$i] $dat[$i]\n";
+}
diff --git a/arch/arm/lib/getconsdata.c b/arch/arm/lib/getconsdata.c
index 901c1ad16..bdf09d5a3 100644
--- a/arch/arm/lib/getconsdata.c
+++ b/arch/arm/lib/getconsdata.c
@@ -1,9 +1,8 @@
/*
* linux/arch/arm/lib/getconsdata.c
*
- * Copyright (C) 1995, 1996 Russell King
+ * Copyright (C) 1995-1998 Russell King
*/
-
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -15,17 +14,55 @@
#define OFF_MM(n) (unsigned long)&(((struct mm_struct *)0)->n)
#ifdef KERNEL_DOMAIN
-unsigned long kernel_domain = KERNEL_DOMAIN;
+unsigned long DOM_KERNELDOMAIN = KERNEL_DOMAIN;
#endif
#ifdef USER_DOMAIN
-unsigned long user_domain = USER_DOMAIN;
-#endif
-unsigned long addr_limit = OFF_TSK(addr_limit);
-unsigned long tss_memmap = OFF_TSK(tss.memmap);
-unsigned long mm = OFF_TSK(mm);
-unsigned long pgd = OFF_MM(pgd);
-unsigned long tss_save = OFF_TSK(tss.save);
-unsigned long tss_fpesave = OFF_TSK(tss.fpstate.soft.save);
+unsigned long DOM_USERDOMAIN = USER_DOMAIN;
+#endif
+
+unsigned long TSK_STATE = OFF_TSK(state);
+unsigned long TSK_FLAGS = OFF_TSK(flags);
+unsigned long TSK_SIGPENDING = OFF_TSK(sigpending);
+unsigned long TSK_ADDR_LIMIT = OFF_TSK(addr_limit);
+
+unsigned long MM = OFF_TSK(mm);
+unsigned long PGD = OFF_MM(pgd);
+
+unsigned long TSS_MEMMAP = OFF_TSK(tss.memmap);
+unsigned long TSS_SAVE = OFF_TSK(tss.save);
+unsigned long TSS_FPESAVE = OFF_TSK(tss.fpstate.soft.save);
#if defined(CONFIG_CPU_ARM2) || defined(CONFIG_CPU_ARM3)
-unsigned long tss_memcmap = OFF_TSK(tss.memcmap);
+unsigned long TSS_MEMCMAP = OFF_TSK(tss.memcmap);
+#endif
+
+#ifdef _PAGE_PRESENT
+unsigned long PAGE_PRESENT = _PAGE_PRESENT;
+#endif
+#ifdef _PAGE_RW
+unsigned long PAGE_RW = _PAGE_RW;
#endif
+#ifdef _PAGE_USER
+unsigned long PAGE_USER = _PAGE_USER;
+#endif
+#ifdef _PAGE_ACCESSED
+unsigned long PAGE_ACCESSED = _PAGE_ACCESSED;
+#endif
+#ifdef _PAGE_DIRTY
+unsigned long PAGE_DIRTY = _PAGE_DIRTY;
+#endif
+#ifdef _PAGE_READONLY
+unsigned long PAGE_READONLY = _PAGE_READONLY;
+#endif
+#ifdef _PAGE_NOT_USER
+unsigned long PAGE_NOT_USER = _PAGE_NOT_USER;
+#endif
+#ifdef _PAGE_OLD
+unsigned long PAGE_OLD = _PAGE_OLD;
+#endif
+#ifdef _PAGE_CLEAN
+unsigned long PAGE_CLEAN = _PAGE_CLEAN;
+#endif
+
+unsigned long KSWI_BASE = 0x900000;
+unsigned long KSWI_SYS_BASE = 0x9f0000;
+unsigned long SYS_ERROR0 = 0x9f0000;
diff --git a/arch/arm/lib/io-acorn.S b/arch/arm/lib/io-acorn.S
index 172783b02..51550e014 100644
--- a/arch/arm/lib/io-acorn.S
+++ b/arch/arm/lib/io-acorn.S
@@ -57,7 +57,7 @@ ENTRY(insw)
mov r2, r2, lsl#1
ENTRY(inswb)
mov ip, sp
- stmfd sp!, {r4 - r10 ,fp ,ip ,lr ,pc}
+ stmfd sp!, {r4 - r10, fp, ip, lr, pc}
sub fp, ip, #4
addr r3, r0
add r0, r3, r0, lsl #2
@@ -70,7 +70,7 @@ ENTRY(inswb)
strgeb r4, [r1], #1
movgt r4, r4, LSR#8
strgtb r4, [r1], #1
- ldmleea fp, {r4 - r10, fp, sp, pc}^
+ LOADREGS(leea, fp, {r4 - r10, fp, sp, pc})
sub r2, r2, #2
Linswok: mov ip, #0xFF
orr ip, ip, ip, lsl #8
diff --git a/arch/arm/lib/io-ebsa285.S b/arch/arm/lib/io-ebsa285.S
new file mode 100644
index 000000000..a5c6386f0
--- /dev/null
+++ b/arch/arm/lib/io-ebsa285.S
@@ -0,0 +1,109 @@
+#define __ASSEMBLER__
+#include <linux/linkage.h>
+
+ENTRY(insl)
+ add r0, r0, #0xff000000
+ add r0, r0, #0x00e00000
+ ands ip, r1, #3
+ bne 2f
+
+1: ldr r3, [r0]
+ str r3, [r1], #4
+ subs r2, r2, #1
+ bne 1b
+ mov pc, lr
+
+2: cmp ip, #2
+ ldr ip, [r0]
+ blt 3f
+ bgt 4f
+
+ strh ip, [r1], #2
+ mov ip, ip, lsr #16
+1: subs r2, r2, #1
+ ldrne r3, [r0]
+ orrne ip, ip, r3, lsl #16
+ strne ip, [r1], #4
+ movne ip, r3, lsr #16
+ bne 1b
+ strh ip, [r1], #2
+ mov pc, lr
+
+3: strb ip, [r1], #1
+ mov ip, ip, lsr #8
+ strh ip, [r1], #2
+ mov ip, ip, lsr #16
+1: subs r2, r2, #1
+ ldrne r3, [r0]
+ orrne ip, ip, r3, lsl #8
+ strne ip, [r1], #4
+ movne ip, r3, lsr #24
+ bne 1b
+ strb ip, [r1], #1
+ mov pc, lr
+
+4: strb ip, [r1], #1
+ mov ip, ip, lsr #8
+1: subs r2, r2, #1
+ ldrne r3, [r0]
+ orrne ip, ip, r3, lsl #24
+ strne ip, [r1], #4
+ movne ip, r3, lsr #8
+ bne 1b
+ strb ip, [r1], #1
+ mov ip, ip, lsr #8
+ strh ip, [r1], #2
+ mov pc, lr
+
+ENTRY(outsl)
+ add r0, r0, #0xff000000
+ add r0, r0, #0x00e00000
+ ands ip, r1, #3
+ bne 2f
+
+1: ldr r3, [r1], #4
+ str r3, [r0]
+ subs r2, r2, #1
+ bne 1b
+ mov pc, lr
+
+2: bic r1, r1, #3
+ cmp ip, #2
+ ldr ip, [r1], #4
+ mov ip, ip, lsr #16
+ blt 3f
+ bgt 4f
+
+1: ldr r3, [r1], #4
+ orr ip, ip, r3, lsl #16
+ str ip, [r0]
+ mov ip, r3, lsr #16
+ subs r2, r2, #1
+ bne 1b
+ mov pc, lr
+
+3: ldr r3, [r1], #4
+ orr ip, ip, r3, lsl #8
+ str ip, [r0]
+ mov ip, r3, lsr #24
+ subs r2, r2, #1
+ bne 3b
+ mov pc, lr
+
+4: ldr r3, [r1], #4
+ orr ip, ip, r3, lsl #24
+ str ip, [r0]
+ mov ip, r3, lsr #8
+ subs r2, r2, #1
+ bne 4b
+ mov pc, lr
+
+
+ENTRY(outsw)
+ENTRY(outswb)
+ mov pc, lr
+
+ENTRY(insw)
+ENTRY(inswb)
+ mov pc, lr
+
diff --git a/arch/arm/lib/ll_char_wr.S b/arch/arm/lib/ll_char_wr.S
index 7df08d93b..fb1a5c5a4 100644
--- a/arch/arm/lib/ll_char_wr.S
+++ b/arch/arm/lib/ll_char_wr.S
@@ -3,9 +3,10 @@
*
* Copyright (C) 1995, 1996 Russell King.
*
- * Speedups & 1bpp code (C) 1996 Philip Blundel & Russell King.
+ * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
*
* 10-04-96 RMK Various cleanups & reduced register usage.
+ * 08-04-98 RMK Shifts re-ordered
*/
@ Regs: [] = corruptable
@@ -32,22 +33,22 @@ ENTRY(ll_write_char)
@
@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
@
- eor ip, r1, #UNDERLINE << 24
+ eor ip, r1, #UNDERLINE << 9
/*
* calculate colours
*/
- tst r1, #INVERSE << 24
- moveq r2, r1, lsr #8
- moveq r3, r1, lsr #16
- movne r2, r1, lsr #16
- movne r3, r1, lsr #8
+ tst r1, #INVERSE << 9
+ moveq r2, r1, lsr #16
+ moveq r3, r1, lsr #24
+ movne r2, r1, lsr #24
+ movne r3, r1, lsr #16
and r3, r3, #255
and r2, r2, #255
/*
* calculate offset into character table
*/
- and r1, r1, #255
- mov r1, r1, lsl #3
+ mov r1, r1, lsl #23
+ mov r1, r1, lsr #20
/*
* calculate offset required for each row [maybe I should make this an argument to this fn.
* Have to see what the register usage is like in the calling routines.
@@ -67,7 +68,7 @@ ENTRY(ll_write_char)
add r0, r0, r5, lsl #3 @ Move to bottom of character
add r1, r1, #7
ldrb r7, [r6, r1]
- tst ip, #UNDERLINE << 24
+ tst ip, #UNDERLINE << 9
eoreq r7, r7, #255
teq r4, #8
beq Lrow8bpplp
@@ -131,7 +132,7 @@ Lrow8bpplp: mov ip, r7, lsr #4
@
Lrow1bpp: add r6, r6, r1
ldmia r6, {r4, r7}
- tst ip, #INVERSE << 24
+ tst ip, #INVERSE << 9
mvnne r4, r4
mvnne r7, r7
strb r4, [r0], r5
@@ -147,7 +148,7 @@ Lrow1bpp: add r6, r6, r1
mov r7, r7, lsr #8
strb r7, [r0], r5
mov r7, r7, lsr #8
- tst ip, #UNDERLINE << 24
+ tst ip, #UNDERLINE << 9
mvneq r7, r7
strb r7, [r0], r5
LOADREGS(fd, sp!, {r4 - r7, pc})
diff --git a/arch/arm/lib/memcpy.S b/arch/arm/lib/memcpy.S
index 209768f9f..f26e6cb1c 100644
--- a/arch/arm/lib/memcpy.S
+++ b/arch/arm/lib/memcpy.S
@@ -8,6 +8,12 @@
#include <asm/assembler.h>
#include <linux/linkage.h>
+#ifndef ENTRY
+#define ENTRY(x...) \
+ .globl _##x; \
+_##x:
+#endif
+
.text
#define ENTER \
mov ip,sp ;\
@@ -84,6 +90,7 @@ ENTRY(memmove)
blt 6b
ands ip, r1, #3
beq 1b
+
8: bic r1, r1, #3
ldr r7, [r1], #4
cmp ip, #2
@@ -105,14 +112,14 @@ ENTRY(memmove)
subs r2, r2, #16
bge 9b
adds r2, r2, #12
- blt 1b
+ blt 100f
10: mov r3, r7, lsr #8
ldr r7, [r1], #4
orr r3, r3, r7, lsl #24
str r3, [r0], #4
subs r2, r2, #4
bge 10b
- sub r1, r1, #3
+100: sub r1, r1, #3
b 6b
11: cmp r2, #12
diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S
index a1524bee9..78256b319 100644
--- a/arch/arm/lib/uaccess.S
+++ b/arch/arm/lib/uaccess.S
@@ -13,13 +13,21 @@
#include <asm/errno.h>
.text
-
+#ifdef ENTRY
#define USER(x...) \
9999: x; \
.section __ex_table,"a"; \
.align 3; \
.long 9999b,9001f; \
.previous
+#else
+#define USER(x...) \
+ x
+#define ENTRY(x...) \
+ .globl _##x; \
+_##x:
+#define TESTING
+#endif
#define PAGE_SHIFT 12
@@ -278,12 +286,12 @@ USER( strgebt r3, [r0], #1) // May fault
USER( strgtbt r3, [r0], #1) // May fault
b .c2u_finished
+#ifndef TESTING
.section .fixup,"ax"
.align 0
9001: LOADREGS(fd,sp!, {r0, r4 - r7, pc})
.previous
-
-
+#endif
/* Prototype: unsigned long __arch_copy_from_user(void *to,const void *from,unsigned long n);
* Purpose : copy a block from user memory to kernel memory
@@ -538,10 +546,12 @@ USER( ldrget r3, [r1], #0) // May fault
strgtb r3, [r0], #1
b .cfu_finished
+#ifndef TESTING
.section .fixup,"ax"
.align 0
9001: LOADREGS(fd,sp!, {r0, r4 - r7, pc})
.previous
+#endif
/* Prototype: int __arch_clear_user(void *addr, size_t sz)
* Purpose : clear some user memory
@@ -556,7 +566,7 @@ ENTRY(__arch_clear_user)
blt 2f
ands ip, r0, #3
beq 1f
- cmp ip, #1
+ cmp ip, #2
USER( strbt r2, [r0], #1)
USER( strlebt r2, [r0], #1)
USER( strltbt r2, [r0], #1)
@@ -566,9 +576,9 @@ USER( strltbt r2, [r0], #1)
USER( strplt r2, [r0], #4)
USER( strplt r2, [r0], #4)
bpl 1b
-2: adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3
+ adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3
USER( strplt r2, [r0], #4)
- tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x
+2: tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x
USER( strnebt r2, [r0], #1)
USER( strnebt r2, [r0], #1)
tst r1, #1 @ x1 x0 x1 x0 x1 x0 x1
@@ -576,6 +586,7 @@ USER( strnebt r2, [r0], #1)
mov r0, #0
LOADREGS(fd,sp!, {r1, pc})
+#ifndef TESTING
.section .fixup,"ax"
.align 0
9001: LOADREGS(fd,sp!, {r0, pc})
@@ -611,21 +622,25 @@ USER( ldrbt r1, [r0], #1)
*/
ENTRY(__arch_strncpy_from_user)
stmfd sp!, {lr}
- mov ip, r2
+ add ip, r1, #1
1: subs r2, r2, #1
bmi 2f
USER( ldrbt r3, [r1], #1)
strb r3, [r0], #1
teq r3, #0
bne 1b
-2: subs r0, ip, r2
- LOADREGS(fd,sp!, {pc})
+ sub r0, r1, ip
+ LOADREGS(fd, sp!, {pc})
+2: sub ip, ip, #1
+ sub r0, r1, ip
+ LOADREGS(fd, sp!, {pc})
.section .fixup,"ax"
.align 0
9001: mov r0, #-EFAULT
- LOADREGS(fd,sp!, {pc})
+ LOADREGS(fd, sp!, {pc})
.previous
.align
+#endif
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 0488da561..be9be35f0 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -7,8 +7,14 @@
#
# Note 2! The CFLAGS definition is now in the main makefile...
+ifeq ($(MACHINE),a5k)
+MMARCH=arc
+else
+MMARCH=$(MACHINE)
+endif
+
O_TARGET := mm.o
-O_OBJS := init.o extable.o fault-$(PROCESSOR).o mm-$(MACHINE).o
+O_OBJS := init.o extable.o fault-$(PROCESSOR).o mm-$(MMARCH).o
ifeq ($(PROCESSOR),armo)
O_OBJS += proc-arm2,3.o
@@ -28,9 +34,9 @@ proc-sa110.o: ../lib/constants.h
../lib/constants.h:
@$(MAKE) -C ../lib constants.h
+ifneq ($(CONFIG_BINUTILS_NEW),y)
%.o: %.S
-ifndef $(CONFIG_BINUTILS_NEW)
- $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.s
- $(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.s
- $(RM) ..tmp.s
+ $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..$@.tmp.s
+ $(CC) $(CFLAGS:-pipe=) -c -o $@ ..$@.tmp.s
+ $(RM) ..$@.tmp.s
endif
diff --git a/arch/arm/mm/fault-armo.c b/arch/arm/mm/fault-armo.c
index a0fd65df2..6e1024c30 100644
--- a/arch/arm/mm/fault-armo.c
+++ b/arch/arm/mm/fault-armo.c
@@ -27,6 +27,50 @@
#define FAULT_CODE_WRITE 0x02
#define FAULT_CODE_USER 0x01
+struct pgtable_cache_struct quicklists;
+
+void __bad_pte(pmd_t *pmd)
+{
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ set_pmd(pmd, mk_pmd(BAD_PAGETABLE));
+}
+
+pgd_t *get_pgd_slow(void)
+{
+ pgd_t *pgd = (pgd_t *) kmalloc(PTRS_PER_PGD * BYTES_PER_PTR, GFP_KERNEL);
+ pgd_t *init;
+
+ if (pgd) {
+ init = pgd_offset(&init_mm, 0);
+ memzero (pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR);
+ memcpy (pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR);
+ }
+ return pgd;
+}
+
+pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) kmalloc (PTRS_PER_PTE * BYTES_PER_PTR, GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (pte) {
+ memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR);
+ set_pmd(pmd, mk_pmd(pte));
+ return pte + offset;
+ }
+ set_pmd(pmd, mk_pmd(BAD_PAGETABLE));
+ return NULL;
+ }
+ kfree (pte);
+ if (pmd_bad(*pmd)) {
+ __bad_pte(pmd);
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + offset;
+}
+
extern void die_if_kernel(char *msg, struct pt_regs *regs, unsigned int err, unsigned int ret);
static void kernel_page_fault (unsigned long addr, int mode, struct pt_regs *regs,
diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c
index 2925761fb..051b336bf 100644
--- a/arch/arm/mm/fault-armv.c
+++ b/arch/arm/mm/fault-armv.c
@@ -25,8 +25,96 @@
#define FAULT_CODE_READ 0x02
#define FAULT_CODE_USER 0x01
+struct pgtable_cache_struct quicklists;
+
+void __bad_pmd(pmd_t *pmd)
+{
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE));
+}
+
+void __bad_pmd_kernel(pmd_t *pmd)
+{
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE));
+}
+
+pgd_t *get_pgd_slow(void)
+{
+ /*
+ * need to get a 16k page for level 1
+ */
+ pgd_t *pgd = (pgd_t *) __get_free_pages(GFP_KERNEL,2);
+ pgd_t *init;
+
+ if (pgd) {
+ init = pgd_offset(&init_mm, 0);
+ memzero ((void *)pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR);
+ memcpy (pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR);
+ }
+ return pgd;
+}
+
+pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) get_small_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (pte) {
+ memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR);
+ set_pmd(pmd, mk_user_pmd(pte));
+ return pte + offset;
+ }
+ set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE));
+ return NULL;
+ }
+ free_small_page ((unsigned long) pte);
+ if (pmd_bad(*pmd)) {
+ __bad_pmd(pmd);
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + offset;
+}
+
+pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) get_small_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (pte) {
+ memzero (pte, PTRS_PER_PTE * BYTES_PER_PTR);
+ set_pmd(pmd, mk_kernel_pmd(pte));
+ return pte + offset;
+ }
+ set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE));
+ return NULL;
+ }
+ free_small_page ((unsigned long) pte);
+ if (pmd_bad(*pmd)) {
+ __bad_pmd_kernel(pmd);
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + offset;
+}
+
extern void die_if_kernel(char *msg, struct pt_regs *regs, unsigned int err, unsigned int ret);
+#ifdef DEBUG
+static int sp_valid (unsigned long *sp)
+{
+ unsigned long addr = (unsigned long) sp;
+
+ if (addr >= 0xb0000000 && addr < 0xd0000000)
+ return 1;
+ if (addr >= 0x03ff0000 && addr < 0x04000000)
+ return 1;
+ return 0;
+}
+#endif
+
static void kernel_page_fault (unsigned long addr, int mode, struct pt_regs *regs,
struct task_struct *tsk, struct mm_struct *mm)
{
@@ -103,6 +191,16 @@ bad_area:
printk ("%s: memory violation at pc=0x%08lx, lr=0x%08lx (bad address=0x%08lx, code %d)\n",
tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode);
#ifdef DEBUG
+ {
+ unsigned int i, j;
+ unsigned long *sp = (unsigned long *) (regs->ARM_sp - 128);
+ for (j = 0; j < 20 && sp_valid (sp); j++) {
+ printk ("%p: ", sp);
+ for (i = 0; i < 8 && sp_valid (sp); i += 1, sp++)
+ printk ("%08lx ", *sp);
+ printk ("\n");
+ }
+ }
show_regs (regs);
c_backtrace (regs->ARM_fp, regs->ARM_cpsr);
#endif
@@ -133,7 +231,6 @@ do_DataAbort (unsigned long addr, int fsr, int error_code, struct pt_regs *regs)
{
if (user_mode(regs))
error_code |= FAULT_CODE_USER;
-
#define DIE(signr,nam)\
force_sig(signr, current);\
die_if_kernel(nam, regs, fsr, signr);\
@@ -154,18 +251,22 @@ do_DataAbort (unsigned long addr, int fsr, int error_code, struct pt_regs *regs)
case 11:
DIE(SIGSEGV, "Domain fault")
case 13:/* permission fault on section */
-#ifndef DEBUG
+#ifdef DEBUG
{
- unsigned int i, j, a;
-static int count=2;
-if (count-- == 0) while (1);
- a = regs->ARM_sp;
- for (j = 0; j < 10; j++) {
- printk ("%08x: ", a);
- for (i = 0; i < 8; i += 1, a += 4)
- printk ("%08lx ", *(unsigned long *)a);
+ unsigned int i, j;
+ unsigned long *sp;
+
+ printk ("%s: section permission fault (bad address=0x%08lx, code %d)\n",
+ current->comm, addr, error_code);
+ sp = (unsigned long *) (regs->ARM_sp - 128);
+ for (j = 0; j < 20 && sp_valid (sp); j++) {
+ printk ("%p: ", sp);
+ for (i = 0; i < 8 && sp_valid (sp); i += 1, sp++)
+ printk ("%08lx ", *sp);
printk ("\n");
}
+ show_regs (regs);
+ c_backtrace(regs->ARM_fp, regs->ARM_cpsr);
}
#endif
DIE(SIGSEGV, "Permission fault")
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index b9e777a32..36b118eb2 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/smp.h>
+#include <linux/init.h>
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h>
#endif
@@ -30,7 +31,6 @@
pgd_t swapper_pg_dir[PTRS_PER_PGD];
-const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
extern char _etext, _stext, _edata, __bss_start, _end;
extern char __init_begin, __init_end;
@@ -103,7 +103,7 @@ void show_mem(void)
/*
* paging_init() sets up the page tables...
*/
-unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
+__initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem))
{
extern unsigned long free_area_init(unsigned long, unsigned long);
@@ -130,7 +130,7 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
* memory is free. This is done after various parts of the system have
* claimed their memory after the kernel image.
*/
-void mem_init(unsigned long start_mem, unsigned long end_mem)
+__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
{
extern void sound_init(void);
int codepages = 0;
diff --git a/arch/arm/mm/mm-arc.c b/arch/arm/mm/mm-arc.c
index 4a4b4718c..ff447a6be 100644
--- a/arch/arm/mm/mm-arc.c
+++ b/arch/arm/mm/mm-arc.c
@@ -5,3 +5,4 @@
*
* Copyright (C) 1998 Russell King
*/
+#include <asm/arch/mm-init.h>
diff --git a/arch/arm/mm/mm-ebsa110.c b/arch/arm/mm/mm-ebsa110.c
index 907a3f399..a937e098d 100644
--- a/arch/arm/mm/mm-ebsa110.c
+++ b/arch/arm/mm/mm-ebsa110.c
@@ -5,3 +5,22 @@
*
* Copyright (C) 1998 Russell King
*/
+
+#include <asm/io.h>
+
+/* map in IO */
+void setup_io_pagetables(void)
+{
+ unsigned long address = IO_START;
+ int spi = IO_BASE >> PGDIR_SHIFT;
+
+ pgd_val(swapper_pg_dir[spi-1]) = 0xc0000000 | PMD_TYPE_SECT |
+ PMD_DOMAIN(DOMAIN_KERNEL) | PMD_SECT_AP_WRITE;
+
+ while (address < IO_START + IO_SIZE && address) {
+ pgd_val(swapper_pg_dir[spi++]) = address | PMD_TYPE_SECT |
+ PMD_DOMAIN(DOMAIN_IO) |
+ PMD_SECT_AP_WRITE;
+ address += PGDIR_SIZE;
+ }
+}
diff --git a/arch/arm/mm/mm-ebsa285.c b/arch/arm/mm/mm-ebsa285.c
new file mode 100644
index 000000000..eb5d7152a
--- /dev/null
+++ b/arch/arm/mm/mm-ebsa285.c
@@ -0,0 +1,97 @@
+/*
+ * arch/arm/mm/mm-ebsa285.c
+ *
+ * Extra MM routines for the EBSA285 architecture
+ *
+ * Copyright (C) 1998 Russell King
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/io.h>
+#include <asm/proc/mm-init.h>
+
+/* Logical Physical
+ * 0xfff00000 0x40000000 X-Bus
+ * 0xffe00000 0x7c000000 PCI I/O space
+ * 0xfe000000 0x42000000 CSR
+ * 0xfd000000 0x78000000 Outbound write flush
+ * 0xfc000000 0x79000000 PCI IACK/special space
+ * 0xf9000000 0x7a000000 PCI Config type 1
+ * 0xf8000000 0x7b000000 PCI Config type 0
+ */
+
+static struct mapping {
+ unsigned long virtual;
+ unsigned long physical;
+ unsigned long length;
+} io_mapping[] = {
+ /*
+ * This is to allow us to fiddle with the EEPROM
+ * This entry will go away in time
+ */
+ { 0xd8000000, 0x41000000, 0x00400000 },
+
+ /*
+ * These ones are so that we can fiddle
+ * with the various cards (eg VGA)
+ * until we're happy with them...
+ */
+ { 0xdc000000, 0x7c000000, 0x00100000 },
+ { 0xe0000000, 0x80000000, 0x10000000 },
+
+ { 0xf8000000, 0x7b000000, 0x01000000 }, /* Type 0 Config */
+
+ { 0xf9000000, 0x7a000000, 0x01000000 }, /* Type 1 Config */
+
+ { 0xfc000000, 0x79000000, 0x01000000 }, /* PCI IACK */
+ { 0xfd000000, 0x78000000, 0x01000000 }, /* Outbound wflsh*/
+ { 0xfe000000, 0x42000000, 0x01000000 }, /* CSR */
+ { 0xffe00000, 0x7c000000, 0x00100000 }, /* PCI I/O */
+ { 0xfff00000, 0x40000000, 0x00100000 }, /* X-Bus */
+};
+
+#define SIZEOFIO (sizeof(io_mapping) / sizeof(io_mapping[0]))
+
+/* map in IO */
+unsigned long setup_io_pagetables(unsigned long start_mem)
+{
+ struct mapping *mp;
+ int i;
+
+ for (i = 0, mp = io_mapping; i < SIZEOFIO; i++, mp++) {
+ while ((mp->virtual & 1048575 || mp->physical & 1048575) && mp->length >= PAGE_SIZE) {
+ alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO,
+ PTE_AP_WRITE);
+
+ mp->length -= PAGE_SIZE;
+ mp->virtual += PAGE_SIZE;
+ mp->physical += PAGE_SIZE;
+ }
+
+ while (mp->length >= 1048576) {
+if (mp->virtual > 0xf0000000)
+ alloc_init_section(&start_mem, mp->virtual, mp->physical, DOMAIN_IO,
+ PMD_SECT_AP_WRITE);
+else
+alloc_init_section(&start_mem, mp->virtual, mp->physical, DOMAIN_USER, PMD_SECT_AP_WRITE | PMD_SECT_AP_READ);
+
+ mp->length -= 1048576;
+ mp->virtual += 1048576;
+ mp->physical += 1048576;
+ }
+
+ while (mp->length >= PAGE_SIZE) {
+ alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO,
+ PTE_AP_WRITE);
+
+ mp->length -= PAGE_SIZE;
+ mp->virtual += PAGE_SIZE;
+ mp->physical += PAGE_SIZE;
+ }
+ }
+ return start_mem;
+}
+
diff --git a/arch/arm/mm/mm-nexuspci.c b/arch/arm/mm/mm-nexuspci.c
index bbae80b19..2bb2d0fab 100644
--- a/arch/arm/mm/mm-nexuspci.c
+++ b/arch/arm/mm/mm-nexuspci.c
@@ -1,7 +1,28 @@
/*
* arch/arm/mm/mm-nexuspci.c
+ * from arch/arm/mm/mm-ebsa110.c
*
- * Extra MM routines for the Archimedes architecture
+ * Extra MM routines for the NexusPCI architecture
*
* Copyright (C) 1998 Russell King
*/
+
+#include <asm/io.h>
+
+/* map in IO */
+void setup_io_pagetables(void)
+{
+ unsigned long address = IO_START;
+ int spi = IO_BASE >> PGDIR_SHIFT;
+
+ pgd_val(swapper_pg_dir[spi-1]) = 0xc0000000 | PMD_TYPE_SECT |
+ PMD_DOMAIN(DOMAIN_KERNEL) | PMD_SECT_AP_WRITE;
+
+ while (address < IO_START + IO_SIZE && address) {
+ pgd_val(swapper_pg_dir[spi++]) = address | PMD_TYPE_SECT |
+ PMD_DOMAIN(DOMAIN_IO) |
+ PMD_SECT_AP_WRITE;
+ address += PGDIR_SIZE;
+ }
+}
+
diff --git a/arch/arm/mm/mm-rpc.c b/arch/arm/mm/mm-rpc.c
index 5eccb1f81..f789b8f79 100644
--- a/arch/arm/mm/mm-rpc.c
+++ b/arch/arm/mm/mm-rpc.c
@@ -6,7 +6,14 @@
* Copyright (C) 1998 Russell King
*/
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/pgtable.h>
#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/proc/mm-init.h>
+#include <asm/arch/mm-init.h>
#define NR_DRAM_BANKS 4
#define NR_VRAM_BANKS 1
@@ -21,8 +28,8 @@
#define FIRST_DRAM_ADDR 0x10000000
#define PHYS_TO_BANK(x) (((x) >> BANK_SHIFT) & (NR_DRAM_BANKS - 1))
-#define BANK_TO_PHYS(x) ((FIRST_DRAM_ADDR) +
- (((x) - FIRST_DRAM_BANK) << BANK_SHIFT)
+#define BANK_TO_PHYS(x) ((FIRST_DRAM_ADDR) + \
+ (((x) - FIRST_DRAM_BANK) << BANK_SHIFT))
struct ram_bank {
unsigned int virt_addr; /* virtual address of the *end* of this bank + 1 */
@@ -75,6 +82,56 @@ void init_dram_banks(struct param_struct *params)
rambank[bank].virt_addr = PAGE_OFFSET + bytes;
}
- drambank[4].phys_offset = 0xd6000000;
- drambank[4].virt_addr = 0xd8000000;
+ rambank[FIRST_VRAM_BANK].phys_offset = 0xd6000000;
+ rambank[FIRST_VRAM_BANK].virt_addr = 0xd8000000;
+
+ current->tss.memmap = __virt_to_phys((unsigned long)swapper_pg_dir);
+}
+
+static struct mapping {
+ unsigned long virtual;
+ unsigned long physical;
+ unsigned long length;
+} io_mapping[] = {
+ { SCREEN2_BASE, SCREEN_START, 2*1048576 }, /* VRAM */
+ { IO_BASE, IO_START, IO_SIZE } /* IO space */
+};
+
+#define SIZEOFIO (sizeof(io_mapping) / sizeof(io_mapping[0]))
+
+/* map in IO */
+unsigned long setup_io_pagetables(unsigned long start_mem)
+{
+ struct mapping *mp;
+ int i;
+
+ for (i = 0, mp = io_mapping; i < SIZEOFIO; i++, mp++) {
+ while ((mp->virtual & 1048575 || mp->physical & 1048575) && mp->length >= PAGE_SIZE) {
+ alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO,
+ PTE_AP_WRITE);
+
+ mp->length -= PAGE_SIZE;
+ mp->virtual += PAGE_SIZE;
+ mp->physical += PAGE_SIZE;
+ }
+
+ while (mp->length >= 1048576) {
+ alloc_init_section(&start_mem, mp->virtual, mp->physical, DOMAIN_IO,
+ PMD_SECT_AP_WRITE);
+ mp->length -= 1048576;
+ mp->virtual += 1048576;
+ mp->physical += 1048576;
+ }
+
+ while (mp->length >= PAGE_SIZE) {
+ alloc_init_page(&start_mem, mp->virtual, mp->physical, DOMAIN_IO,
+ PTE_AP_WRITE);
+
+ mp->length -= PAGE_SIZE;
+ mp->virtual += PAGE_SIZE;
+ mp->physical += PAGE_SIZE;
+ }
+ }
+
+ return start_mem;
}
diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S
index 776d0d57c..a853671fc 100644
--- a/arch/arm/mm/proc-arm6,7.S
+++ b/arch/arm/mm/proc-arm6,7.S
@@ -72,10 +72,10 @@ _arm6_7_switch_to:
stmfd sp!, {ip} @ Save cpsr_SVC
str sp, [r0, #TSS_SAVE] @ Save sp_SVC
ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC
- ldr r0, [r1, #ADDR_LIMIT]
+ ldr r0, [r1, #TSK_ADDR_LIMIT]
teq r0, #0
- moveq r0, #KERNEL_DOMAIN
- movne r0, #USER_DOMAIN
+ moveq r0, #DOM_KERNELDOMAIN
+ movne r0, #DOM_USERDOMAIN
mcr p15, 0, r0, c3, c0 @ Set domain reg
ldr r0, [r1, #TSS_MEMMAP] @ Page table pointer
mov r1, #0
diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S
index 7d53bf230..633e8c164 100644
--- a/arch/arm/mm/proc-sa110.S
+++ b/arch/arm/mm/proc-sa110.S
@@ -161,8 +161,6 @@ _sa110_flush_icache_area:
blt 1b
mcr p15, 0, r0, c7, c5, 0 @ flush I cache
mov pc, lr
-
-@LC0: .word _current
/*
* Function: sa110_switch_to (struct task_struct *prev, struct task_struct *next)
*
@@ -183,10 +181,10 @@ _sa110_switch_to:
stmfd sp!, {ip} @ Save cpsr_SVC
str sp, [r0, #TSS_SAVE] @ Save sp_SVC
ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC
- ldr r0, [r1, #ADDR_LIMIT]
+ ldr r0, [r1, #TSK_ADDR_LIMIT]
teq r0, #0
- moveq r0, #KERNEL_DOMAIN
- movne r0, #USER_DOMAIN
+ moveq r0, #DOM_KERNELDOMAIN
+ movne r0, #DOM_USERDOMAIN
mcr p15, 0, r0, c3, c0 @ Set segment
ldr r0, [r1, #TSS_MEMMAP] @ Page table pointer
ldr r3, =Lclean_switch
@@ -227,8 +225,6 @@ _sa110_data_abort:
mov r2, r2, lsr #19 @ b1 = L
and r3, r2, #0x69 << 2
and r2, r2, #2
-// teq r3, #0x21 << 2
-// orreq r2, r2, #1 @ b0 = {LD,ST}RT
mrc p15, 0, r1, c5, c0, 0 @ get FSR
and r1, r1, #255
mov pc, lr
diff --git a/arch/arm/vmlinux.lds b/arch/arm/vmlinux.lds
index 787e5c99d..db1f720e7 100644
--- a/arch/arm/vmlinux.lds
+++ b/arch/arm/vmlinux.lds
@@ -1,4 +1,5 @@
-/* ld script to make i386 Linux kernel
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")
@@ -34,11 +35,15 @@ SECTIONS
_edata = .; /* End of data section */
- . = ALIGN(4096); /* Init code and data */
+ /* This has to be aligned to a page boundary to do us any good. This
+ alignment is overkill for ARM6 up but needed for ARM3. Since all this
+ data will be thrown away I don't think the extra padding will hurt.
+ -- pb */
+ . = ALIGN(32768); /* Init code and data */
__init_begin = .;
.text.init : { *(.text.init) }
.data.init : { *(.data.init) }
- . = ALIGN(4096);
+ . = ALIGN(32768);
__init_end = .;
__bss_start = .; /* BSS */
diff --git a/arch/i386/Makefile b/arch/i386/Makefile
index 359c20b9b..98bde850f 100644
--- a/arch/i386/Makefile
+++ b/arch/i386/Makefile
@@ -58,9 +58,6 @@ SUBDIRS := $(SUBDIRS) arch/i386/math-emu
DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a
endif
-memsize: dummy
- @echo "__kernel_offset__ = (0x1000-$(CONFIG_MAX_MEMSIZE))*1024*1024;" > arch/i386/.kernel_offset.lds
-
arch/i386/kernel: dummy
$(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel
@@ -69,27 +66,27 @@ arch/i386/mm: dummy
MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
-zImage: memsize vmlinux
+zImage: vmlinux
@$(MAKEBOOT) zImage
-bzImage: memsize vmlinux
+bzImage: vmlinux
@$(MAKEBOOT) bzImage
compressed: zImage
-zlilo: memsize vmlinux
+zlilo: vmlinux
@$(MAKEBOOT) BOOTIMAGE=zImage zlilo
-bzlilo: memsize vmlinux
+bzlilo: vmlinux
@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
-zdisk: memsize vmlinux
+zdisk: vmlinux
@$(MAKEBOOT) BOOTIMAGE=zImage zdisk
-bzdisk: memsize vmlinux
+bzdisk: vmlinux
@$(MAKEBOOT) BOOTIMAGE=bzImage zdisk
-install: memsize vmlinux
+install: vmlinux
@$(MAKEBOOT) BOOTIMAGE=bzImage install
archclean:
diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S
index fb1bd5559..0aa8ddc44 100644
--- a/arch/i386/boot/compressed/head.S
+++ b/arch/i386/boot/compressed/head.S
@@ -37,10 +37,10 @@ startup_32:
cld
cli
movl $(__KERNEL_DS),%eax
- mov %ax,%ds
- mov %ax,%es
- mov %ax,%fs
- mov %ax,%gs
+ movl %ax,%ds
+ movl %ax,%es
+ movl %ax,%fs
+ movl %ax,%gs
#ifdef __SMP__
orw %bx,%bx # What state are we in BX=1 for SMP
# 0 for boot
diff --git a/arch/i386/config.in b/arch/i386/config.in
index 058f908e3..86c214a1e 100644
--- a/arch/i386/config.in
+++ b/arch/i386/config.in
@@ -17,7 +17,9 @@ choice 'Processor family' \
Pentium/K5/5x86/6x86 CONFIG_M586 \
PPro/K6/6x86MX CONFIG_M686" Pentium
bool 'Math emulation' CONFIG_MATH_EMULATION
-int ' Max physical memory in MB' CONFIG_MAX_MEMSIZE 1024
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
+fi
endmenu
mainmenu_option next_comment
@@ -37,7 +39,8 @@ bool 'PCI support' CONFIG_PCI
if [ "$CONFIG_PCI" = "y" ]; then
bool ' PCI BIOS support' CONFIG_PCI_BIOS
bool ' PCI direct access support' CONFIG_PCI_DIRECT
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' PCI quirks' CONFIG_PCI_QUIRKS
+ if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
fi
bool ' Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
@@ -114,17 +117,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
fi
endmenu
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_MCDX" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
source fs/nls/Config.in
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index f3792ce05..ab1c622c7 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -15,7 +15,6 @@
CONFIG_M586=y
# CONFIG_M686 is not set
# CONFIG_MATH_EMULATION is not set
-CONFIG_MAX_MEMSIZE=1024
#
# Loadable module support
@@ -31,6 +30,7 @@ CONFIG_NET=y
CONFIG_PCI=y
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
+CONFIG_PCI_QUIRKS=y
CONFIG_PCI_OLD_PROC=y
# CONFIG_MCA is not set
CONFIG_SYSVIPC=y
@@ -214,7 +214,6 @@ CONFIG_EEXPRESS_PRO100=y
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
-CONFIG_CDROM=y
#
# Filesystems
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index ce1e6652d..6f63d2c97 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -21,6 +21,7 @@ O_TARGET := kernel.o
O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o \
ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o
OX_OBJS := i386_ksyms.o
+MX_OBJS :=
ifdef CONFIG_PCI
O_OBJS += bios32.o
@@ -30,6 +31,14 @@ ifdef CONFIG_MCA
O_OBJS += mca.o
endif
+ifeq ($(CONFIG_MTRR),y)
+OX_OBJS += mtrr.o
+else
+ ifeq ($(CONFIG_MTRR),m)
+ MX_OBJS += mtrr.o
+ endif
+endif
+
ifdef SMP
diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c
index 7e865c417..f2955918a 100644
--- a/arch/i386/kernel/bios32.c
+++ b/arch/i386/kernel/bios32.c
@@ -1,7 +1,7 @@
/*
- * bios32.c - BIOS32, PCI BIOS functions.
+ * bios32.c - Low-Level PCI Access
*
- * $Id: bios32.c,v 1.5 1997/12/02 01:48:00 ralf Exp $
+ * $Id: bios32.c,v 1.29 1998/04/17 16:31:15 mj Exp $
*
* Sponsored by
* iX Multiuser Multitasking Magazine
@@ -64,14 +64,16 @@
*
* Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts
* and cleaned it up... Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj]
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/init.h>
+#include <linux/ioport.h>
#include <asm/page.h>
#include <asm/segment.h>
@@ -85,14 +87,20 @@
#include "irq.h"
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
/*
* Generic PCI access -- indirect calls according to detected HW.
*/
struct pci_access {
int pci_present;
- int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
- int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *);
int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
@@ -108,8 +116,6 @@ static int pci_stub(void)
static struct pci_access pci_access_none = {
0, /* No PCI present */
- (void *) pci_stub, /* No functions implemented */
- (void *) pci_stub,
(void *) pci_stub,
(void *) pci_stub,
(void *) pci_stub,
@@ -125,54 +131,10 @@ int pcibios_present(void)
return access_pci->pci_present;
}
-int pcibios_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *device_fn)
-{
- return access_pci->find_class(class_code, index, bus, device_fn);
-}
-
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus, unsigned char *device_fn)
-{
- return access_pci->find_device(vendor, device_id, index, bus, device_fn);
-}
-
int pcibios_read_config_byte (unsigned char bus,
unsigned char device_fn, unsigned char where, unsigned char *value)
{
- int res;
-
- res = access_pci->read_config_byte(bus, device_fn, where, value);
-
-#ifdef __SMP__
-/*
- * IOAPICs can take PCI IRQs directly, lets first check the mptable:
- */
- if (where == PCI_INTERRUPT_LINE) {
- int irq;
- char pin;
-
- /*
- * get the PCI IRQ INT _physical pin_ for this device
- */
- access_pci->read_config_byte(bus, device_fn,
- PCI_INTERRUPT_PIN, &pin);
- /*
- * subtle, PCI pins are numbered starting from 1 ...
- */
- pin--;
-
- irq = IO_APIC_get_PCI_irq_vector (bus,PCI_SLOT(device_fn),pin);
- if (irq != -1)
- *value = (unsigned char) irq;
-
- printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
- bus,PCI_SLOT(device_fn), pin, irq);
-
- }
-#endif
-
- return res;
+ return access_pci->read_config_byte(bus, device_fn, where, value);
}
int pcibios_read_config_word (unsigned char bus,
@@ -205,60 +167,19 @@ int pcibios_write_config_dword (unsigned char bus,
return access_pci->write_config_dword(bus, device_fn, where, value);
}
-/*
- * Direct access to PCI hardware...
- */
-
-/*
- * Given the vendor and device ids, find the n'th instance of that device
- * in the system.
- */
+#define PCI_PROBE_BIOS 1
+#define PCI_PROBE_CONF1 2
+#define PCI_PROBE_CONF2 4
+#define PCI_NO_SORT 0x100
+#define PCI_BIOS_SORT 0x200
-#ifdef CONFIG_PCI_DIRECT
-
-static int pci_direct_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus,
- unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->vendor == vendor && dev->device == device_id) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
+static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
/*
- * Given the class, find the n'th instance of that device
- * in the system.
+ * Direct access to PCI hardware...
*/
-static int pci_direct_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->class == class_code) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
+#ifdef CONFIG_PCI_DIRECT
/*
* Functions for accessing PCI configuration space with type 1 accesses
@@ -346,8 +267,6 @@ static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device
static struct pci_access pci_direct_conf1 = {
1,
- pci_direct_find_device,
- pci_direct_find_class,
pci_conf1_read_config_byte,
pci_conf1_read_config_word,
pci_conf1_read_config_dword,
@@ -458,8 +377,6 @@ static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device
static struct pci_access pci_direct_conf2 = {
1,
- pci_direct_find_device,
- pci_direct_find_class,
pci_conf2_read_config_byte,
pci_conf2_read_config_word,
pci_conf2_read_config_dword,
@@ -470,39 +387,43 @@ static struct pci_access pci_direct_conf2 = {
__initfunc(static struct pci_access *pci_check_direct(void))
{
- unsigned int tmp;
- unsigned long flags;
+ unsigned int tmp;
+ unsigned long flags;
- save_flags(flags); cli();
+ save_flags(flags); cli();
+
+ /*
+ * Check if configuration type 1 works.
+ */
+ if (pci_probe & PCI_PROBE_CONF1) {
+ outb (0x01, 0xCFB);
+ tmp = inl (0xCF8);
+ outl (0x80000000, 0xCF8);
+ if (inl (0xCF8) == 0x80000000) {
+ outl (tmp, 0xCF8);
+ restore_flags(flags);
+ printk("PCI: Using configuration type 1\n");
+ return &pci_direct_conf1;
+ }
+ outl (tmp, 0xCF8);
+ }
+
+ /*
+ * Check if configuration type 2 works.
+ */
+ if (pci_probe & PCI_PROBE_CONF2) {
+ outb (0x00, 0xCFB);
+ outb (0x00, 0xCF8);
+ outb (0x00, 0xCFA);
+ if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
+ restore_flags(flags);
+ printk("PCI: Using configuration type 2\n");
+ return &pci_direct_conf2;
+ }
+ }
- /*
- * Check if configuration type 1 works.
- */
- outb (0x01, 0xCFB);
- tmp = inl (0xCF8);
- outl (0x80000000, 0xCF8);
- if (inl (0xCF8) == 0x80000000) {
- outl (tmp, 0xCF8);
- restore_flags(flags);
- printk("PCI: Using configuration type 1\n");
- return &pci_direct_conf1;
- }
- outl (tmp, 0xCF8);
-
- /*
- * Check if configuration type 2 works.
- */
- outb (0x00, 0xCFB);
- outb (0x00, 0xCF8);
- outb (0x00, 0xCFA);
- if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
restore_flags(flags);
- printk("PCI: Using configuration type 2\n");
- return &pci_direct_conf2;
- }
- restore_flags(flags);
- printk("PCI: PCI hardware not found (i.e., not present or not supported).\n");
- return NULL;
+ return NULL;
}
#endif
@@ -599,7 +520,7 @@ static unsigned long bios32_service(unsigned long service)
printk("bios32_service(0x%lx): not present\n", service);
return 0;
default: /* Shouldn't happen */
- printk("bios32_service(0x%lx): returned 0x%x, mail drew@colorado.edu\n",
+ printk("bios32_service(0x%lx): returned 0x%x, report to <mj@ucw.cz>.\n",
service, return_code);
return 0;
}
@@ -642,7 +563,7 @@ __initfunc(static int check_pcibios(void))
if (present_status || (signature != PCI_SIGNATURE)) {
printk ("PCI: %s: BIOS32 Service Directory says PCI BIOS is present,\n"
" but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
- " and signature of 0x%08lx (%c%c%c%c). Mail drew@Colorado.EDU\n",
+ " and signature of 0x%08lx (%c%c%c%c). Report to <mj@ucw.cz>.\n",
(signature == PCI_SIGNATURE) ? "WARNING" : "ERROR",
present_status, signature,
(char) (signature >> 0), (char) (signature >> 8),
@@ -660,6 +581,8 @@ __initfunc(static int check_pcibios(void))
return 0;
}
+#if 0 /* Not used */
+
static int pci_bios_find_class (unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *device_fn)
{
@@ -684,8 +607,10 @@ static int pci_bios_find_class (unsigned int class_code, unsigned short index,
return (int) (ret & 0xff00) >> 8;
}
-static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus, unsigned char *device_fn)
+#endif
+
+__initfunc(static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus, unsigned char *device_fn))
{
unsigned short bx;
unsigned short ret;
@@ -847,8 +772,6 @@ static int pci_bios_write_config_dword (unsigned char bus,
static struct pci_access pci_bios_access = {
1,
- pci_bios_find_device,
- pci_bios_find_class,
pci_bios_read_config_byte,
pci_bios_read_config_word,
pci_bios_read_config_dword,
@@ -887,21 +810,17 @@ __initfunc(static struct pci_access *pci_find_bios(void))
if (sum != 0)
continue;
if (check->fields.revision != 0) {
- printk("PCI: unsupported BIOS32 revision %d at 0x%p, mail drew@colorado.edu\n",
+ printk("PCI: unsupported BIOS32 revision %d at 0x%p, report to <mj@ucw.cz>\n",
check->fields.revision, check);
continue;
}
- printk ("PCI: BIOS32 Service Directory structure at 0x%p\n", check);
+ DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check);
if (check->fields.entry >= 0x100000) {
-#ifdef CONFIG_PCI_DIRECT
- printk("PCI: BIOS32 entry in high memory, trying direct PCI access.\n");
- return pci_check_direct();
-#else
- printk("PCI: BIOS32 entry in high memory, cannot use.\n");
-#endif
+ printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check);
+ return NULL;
} else {
bios32_entry = check->fields.entry;
- printk ("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+ DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
bios32_indirect.address = bios32_entry + PAGE_OFFSET;
if (check_pcibios())
return &pci_bios_access;
@@ -912,36 +831,237 @@ __initfunc(static struct pci_access *pci_find_bios(void))
return NULL;
}
+/*
+ * Sort the device list according to PCI BIOS.
+ */
+
+__initfunc(void pcibios_sort(void))
+{
+ struct pci_dev *dev = pci_devices;
+ struct pci_dev **last = &pci_devices;
+ struct pci_dev *d, **dd, *e;
+ int idx;
+ unsigned char bus, devfn;
+
+ DBG("PCI: Sorting device list...\n");
+ while ((e = dev)) {
+ idx = 0;
+ while (pci_bios_find_device(e->vendor, e->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) {
+ idx++;
+ for(dd=&dev; (d = *dd); dd = &d->next) {
+ if (d->bus->number == bus && d->devfn == devfn) {
+ *dd = d->next;
+ *last = d;
+ last = &d->next;
+ break;
+ }
+ }
+ if (!d)
+ printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn);
+ }
+ if (!idx) {
+ printk("PCI: Device %02x:%02x not found by BIOS\n",
+ dev->bus->number, dev->devfn);
+ d = dev;
+ dev = dev->next;
+ *last = d;
+ last = &d->next;
+ }
+ }
+ *last = NULL;
+}
+
#endif
/*
- * No fixup function used.
+ * Several BIOS'es forget to assign addresses to I/O ranges.
+ * We try to fix it here, expecting there are free addresses
+ * starting with 0x5800. Ugly, but until we come with better
+ * resource management, it's the only simple solution.
*/
-__initfunc(unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end))
+static int pci_last_io_addr __initdata = 0x5800;
+
+__initfunc(void pcibios_fixup_io_addr(struct pci_dev *dev, int idx))
{
- return mem_start;
+ unsigned short cmd;
+ unsigned int reg = PCI_BASE_ADDRESS_0 + 4*idx;
+ unsigned int size, addr, try;
+ unsigned int bus = dev->bus->number;
+ unsigned int devfn = dev->devfn;
+
+ if (!pci_last_io_addr) {
+ printk("PCI: Unassigned I/O space for %02x:%02x\n", bus, devfn);
+ return;
+ }
+ pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+ pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
+ pcibios_write_config_dword(bus, devfn, reg, ~0);
+ pcibios_read_config_dword(bus, devfn, reg, &size);
+ size = (~(size & PCI_BASE_ADDRESS_IO_MASK) & 0xffff) + 1;
+ addr = 0;
+ if (!size || size > 0x100)
+ printk("PCI: Unable to handle I/O allocation for %02x:%02x (%04x), tell <mj@ucw.cz>\n", bus, devfn, size);
+ else {
+ do {
+ addr = (pci_last_io_addr + size - 1) & ~(size-1);
+ pci_last_io_addr = addr + size;
+ } while (check_region(addr, size));
+ printk("PCI: Assigning I/O space %04x-%04x to device %02x:%02x\n", addr, addr+size-1, bus, devfn);
+ pcibios_write_config_dword(bus, devfn, reg, addr | PCI_BASE_ADDRESS_SPACE_IO);
+ pcibios_read_config_dword(bus, devfn, reg, &try);
+ if ((try & PCI_BASE_ADDRESS_IO_MASK) != addr) {
+ addr = 0;
+ printk("PCI: Address setup failed, got %04x\n", try);
+ } else
+ dev->base_address[idx] = try;
+ }
+ if (!addr) {
+ pcibios_write_config_dword(bus, devfn, reg, 0);
+ dev->base_address[idx] = 0;
+ }
+ pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+}
+
+/*
+ * Arch-dependent fixups. We need to fix here base addresses, I/O
+ * and memory enables and IRQ's as the PCI BIOS'es are buggy as hell.
+ */
+
+__initfunc(void pcibios_fixup(void))
+{
+ struct pci_dev *dev;
+ int i, has_io, has_mem;
+ unsigned short cmd;
+
+ for(dev = pci_devices; dev; dev=dev->next) {
+ /*
+ * There are buggy BIOSes that forget to enable I/O and memory
+ * access to PCI devices. We try to fix this, but we need to
+ * be sure that the BIOS didn't forget to assign an address
+ * to the device. [mj]
+ */
+ has_io = has_mem = 0;
+ for(i=0; i<6; i++) {
+ unsigned long a = dev->base_address[i];
+ if (a & PCI_BASE_ADDRESS_SPACE_IO) {
+ has_io = 1;
+ a &= PCI_BASE_ADDRESS_IO_MASK;
+ if (!a || a == PCI_BASE_ADDRESS_IO_MASK)
+ pcibios_fixup_io_addr(dev, i);
+ } else if (a & PCI_BASE_ADDRESS_MEM_MASK)
+ has_mem = 1;
+ }
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (has_io && !(cmd & PCI_COMMAND_IO)) {
+ printk("PCI: Enabling I/O for device %02x:%02x\n",
+ dev->bus->number, dev->devfn);
+ cmd |= PCI_COMMAND_IO;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
+ printk("PCI: Enabling memory for device %02x:%02x\n",
+ dev->bus->number, dev->devfn);
+ cmd |= PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+#ifdef __SMP__
+ /*
+ * Recalculate IRQ numbers if we use the I/O APIC
+ */
+ {
+ int irq;
+ unsigned char pin;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if (pin) {
+ pin--; /* interrupt pins are numbered starting from 1 */
+ irq = IO_APIC_get_PCI_irq_vector (dev->bus->number, PCI_SLOT(dev->devfn), pin);
+ if (irq >= 0) {
+ printk("PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n",
+ dev->bus->number, PCI_SLOT(dev->devfn), pin, irq);
+ dev->irq = irq;
+ }
+ }
+ }
+#endif
+ /*
+ * Fix out-of-range IRQ numbers and report bogus IRQ.
+ */
+ if (dev->irq >= NR_IRQS)
+ dev->irq = 0;
+ }
+
+#ifdef CONFIG_PCI_BIOS
+ if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
+ pcibios_sort();
+#endif
}
/*
- * Initialization. Try all known PCI access methods.
+ * Initialization. Try all known PCI access methods. Note that we support
+ * using both PCI BIOS and direct access: in such cases, we use I/O ports
+ * to access config space, but we still keep BIOS order of cards to be
+ * compatible with 2.0.X. This should go away in 2.3.
*/
-__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end))
+__initfunc(void pcibios_init(void))
{
- struct pci_access *a = NULL;
+ struct pci_access *bios = NULL;
+ struct pci_access *dir = NULL;
#ifdef CONFIG_PCI_BIOS
- a = pci_find_bios();
-#else
+ if ((pci_probe & PCI_PROBE_BIOS) && ((bios = pci_find_bios())))
+ pci_probe |= PCI_BIOS_SORT;
+#endif
#ifdef CONFIG_PCI_DIRECT
- a = pci_check_direct();
-#else
-#error "You need to set CONFIG_PCI_BIOS or CONFIG_PCI_DIRECT if you want PCI support."
+ if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))
+ dir = pci_check_direct();
#endif
+ if (dir)
+ access_pci = dir;
+ else if (bios)
+ access_pci = bios;
+}
+
+#if !defined(CONFIG_PCI_BIOS) && !defined(CONFIG_PCI_DIRECT)
+#error PCI configured with neither PCI BIOS or PCI direct access support.
#endif
- if (a)
- access_pci = a;
- return memory_start;
+__initfunc(char *pcibios_setup(char *str))
+{
+ if (!strcmp(str, "off")) {
+ pci_probe = 0;
+ return NULL;
+ } else if (!strncmp(str, "io=", 3)) {
+ char *p;
+ unsigned int x = simple_strtoul(str+3, &p, 16);
+ if (p && *p)
+ return str;
+ pci_last_io_addr = x;
+ return NULL;
+ }
+#ifdef CONFIG_PCI_BIOS
+ else if (!strcmp(str, "bios")) {
+ pci_probe = PCI_PROBE_BIOS;
+ return NULL;
+ } else if (!strcmp(str, "nobios")) {
+ pci_probe &= ~PCI_PROBE_BIOS;
+ return NULL;
+ } else if (!strcmp(str, "nosort")) {
+ pci_probe |= PCI_NO_SORT;
+ return NULL;
+ }
+#endif
+#ifdef CONFIG_PCI_DIRECT
+ else if (!strcmp(str, "conf1")) {
+ pci_probe = PCI_PROBE_CONF1;
+ return NULL;
+ }
+ else if (!strcmp(str, "conf2")) {
+ pci_probe = PCI_PROBE_CONF2;
+ return NULL;
+ }
+#endif
+ return str;
}
diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
index 14b82b45b..b6541005f 100644
--- a/arch/i386/kernel/entry.S
+++ b/arch/i386/kernel/entry.S
@@ -81,8 +81,8 @@ ENOSYS = 38
#define SAVE_ALL \
cld; \
- push %es; \
- push %ds; \
+ pushl %es; \
+ pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
@@ -91,8 +91,8 @@ ENOSYS = 38
pushl %ecx; \
pushl %ebx; \
movl $(__KERNEL_DS),%edx; \
- mov %dx,%ds; \
- mov %dx,%es;
+ movl %dx,%ds; \
+ movl %dx,%es;
#define RESTORE_ALL \
popl %ebx; \
@@ -102,8 +102,8 @@ ENOSYS = 38
popl %edi; \
popl %ebp; \
popl %eax; \
- pop %ds; \
- pop %es; \
+ popl %ds; \
+ popl %es; \
addl $4,%esp; \
iret
@@ -155,7 +155,7 @@ ENTRY(system_call)
jae badsys
testb $0x20,flags(%ebx) # PF_TRACESYS
jne tracesys
- call SYMBOL_NAME(sys_call_table)(,%eax,4)
+ call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
ALIGN
.globl ret_from_sys_call
@@ -193,7 +193,7 @@ tracesys:
movl $-ENOSYS,EAX(%esp)
call SYMBOL_NAME(syscall_trace)
movl ORIG_EAX(%esp),%eax
- call SYMBOL_NAME(sys_call_table)(,%eax,4)
+ call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
call SYMBOL_NAME(syscall_trace)
jmp ret_from_sys_call
@@ -231,7 +231,7 @@ ENTRY(divide_error)
pushl $ SYMBOL_NAME(do_divide_error)
ALIGN
error_code:
- push %ds
+ pushl %ds
pushl %eax
xorl %eax,%eax
pushl %ebp
@@ -241,17 +241,27 @@ error_code:
decl %eax # eax = -1
pushl %ecx
pushl %ebx
+#if 1
xorl %ecx,%ecx # zero ecx
cld
mov %es,%cx # get the lower order bits of es
+#else
+ cld
+# Some older processors leave the top 16 bits of the 32 bit destination
+# register undefined, rather than zeroed in the following instruction.
+# This won't matter when restoring or loading a segment register from the
+# stack. It may be a problem if any code reads the full 32 bit value.
+# dosemu? kernel? Would somebody like to verify that this way is really OK?
+ movl %es,%cx
+#endif
xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. )
movl %esp,%edx
xchgl %ecx, ES(%esp) # get the address and save es.
pushl %eax # push the error code
pushl %edx
movl $(__KERNEL_DS),%edx
- mov %dx,%ds
- mov %dx,%es
+ movl %dx,%ds
+ movl %dx,%es
GET_CURRENT(%ebx)
call *%ecx
addl $8,%esp
@@ -533,6 +543,7 @@ ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_pread) /* 180 */
.long SYMBOL_NAME(sys_pwrite)
.long SYMBOL_NAME(sys_chown)
+ .long SYMBOL_NAME(sys_getcwd)
.rept NR_syscalls-182
.long SYMBOL_NAME(sys_ni_syscall)
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
index 048921838..86031f37f 100644
--- a/arch/i386/kernel/head.S
+++ b/arch/i386/kernel/head.S
@@ -45,10 +45,10 @@ startup_32:
*/
cld
movl $(__KERNEL_DS),%eax
- mov %ax,%ds
- mov %ax,%es
- mov %ax,%fs
- mov %ax,%gs
+ movl %ax,%ds
+ movl %ax,%es
+ movl %ax,%fs
+ movl %ax,%gs
#ifdef __SMP__
orw %bx,%bx
jz 1f
@@ -321,10 +321,10 @@ is386: pushl %ecx # restore original EFLAGS
lidt idt_descr
ljmp $(__KERNEL_CS),$1f
1: movl $(__KERNEL_DS),%eax # reload all the segment registers
- mov %ax,%ds # after changing gdt.
- mov %ax,%es
- mov %ax,%fs
- mov %ax,%gs
+ movl %ax,%ds # after changing gdt.
+ movl %ax,%es
+ movl %ax,%fs
+ movl %ax,%gs
#ifdef __SMP__
movl $(__KERNEL_DS), %eax
mov %ax,%ss # Reload the stack pointer (segment only)
@@ -404,16 +404,16 @@ ignore_int:
pushl %eax
pushl %ecx
pushl %edx
- push %es
- push %ds
+ pushl %es
+ pushl %ds
movl $(__KERNEL_DS),%eax
- mov %ax,%ds
- mov %ax,%es
+ movl %ax,%ds
+ movl %ax,%es
pushl $int_msg
call SYMBOL_NAME(printk)
popl %eax
- pop %ds
- pop %es
+ popl %ds
+ popl %es
popl %edx
popl %ecx
popl %eax
@@ -619,9 +619,6 @@ ENTRY(idt_table)
.fill 256,8,0 # idt is uninitialized
/*
- * This gdt setup gives the kernel a CONFIG_MAX_MEMSIZE sized address space at
- * virtual address PAGE_OFFSET.
- *
* This contains up to 8192 quadwords depending on NR_TASKS - 64kB of
* gdt entries. Ugh.
*
diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c
index d2837d648..66dec5fed 100644
--- a/arch/i386/kernel/i386_ksyms.c
+++ b/arch/i386/kernel/i386_ksyms.c
@@ -8,7 +8,6 @@
#include <linux/in6.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
-#include <linux/pci.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
@@ -64,13 +63,14 @@ EXPORT_SYMBOL(__generic_copy_to_user);
EXPORT_SYMBOL(strlen_user);
#ifdef __SMP__
-EXPORT_SYMBOL(apic_reg); /* Needed internally for the I386 inlines */
EXPORT_SYMBOL(cpu_data);
EXPORT_SYMBOL_NOVERS(kernel_flag);
EXPORT_SYMBOL_NOVERS(active_kernel_processor);
EXPORT_SYMBOL(smp_invalidate_needed);
EXPORT_SYMBOL_NOVERS(__lock_kernel);
EXPORT_SYMBOL(lk_lockmsg);
+EXPORT_SYMBOL(__cpu_logical_map);
+EXPORT_SYMBOL(smp_num_cpus);
/* Global SMP irq stuff */
EXPORT_SYMBOL(synchronize_irq);
@@ -82,6 +82,8 @@ EXPORT_SYMBOL(__global_cli);
EXPORT_SYMBOL(__global_sti);
EXPORT_SYMBOL(__global_save_flags);
EXPORT_SYMBOL(__global_restore_flags);
+EXPORT_SYMBOL(smp_message_pass);
+EXPORT_SYMBOL(mtrr_hook);
#endif
#ifdef CONFIG_MCA
@@ -97,7 +99,3 @@ EXPORT_SYMBOL(mca_set_adapter_procfn);
EXPORT_SYMBOL(mca_isenabled);
EXPORT_SYMBOL(mca_isadapter);
#endif
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c
index 219e7f853..6e422614e 100644
--- a/arch/i386/kernel/io_apic.c
+++ b/arch/i386/kernel/io_apic.c
@@ -28,13 +28,11 @@
#include "irq.h"
-#define IO_APIC_BASE 0xfec00000
-
/*
* volatile is justified in this case, it might change
* spontaneously, GCC should not cache it
*/
-volatile unsigned int * io_apic_reg = NULL;
+#define IO_APIC_BASE ((volatile int *)0xfec00000)
/*
* The structure of the IO-APIC:
@@ -96,17 +94,19 @@ int nr_ioapic_registers = 0; /* # of IRQ routing registers */
int mp_irq_entries = 0; /* # of MP IRQ source entries */
struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
/* MP IRQ source entries */
+int mpc_default_type = 0; /* non-0 if default (table-less)
+ MP configuration */
unsigned int io_apic_read (unsigned int reg)
{
- *io_apic_reg = reg;
- return *(io_apic_reg+4);
+ *IO_APIC_BASE = reg;
+ return *(IO_APIC_BASE+4);
}
void io_apic_write (unsigned int reg, unsigned int value)
{
- *io_apic_reg = reg;
- *(io_apic_reg+4) = value;
+ *IO_APIC_BASE = reg;
+ *(IO_APIC_BASE+4) = value;
}
void enable_IO_APIC_irq (unsigned int irq)
@@ -256,7 +256,7 @@ void setup_IO_APIC_irqs (void)
/*
* PCI IRQ redirection. Yes, limits are hardcoded.
*/
- if ((i>=16) && (i<=19)) {
+ if ((i>=16) && (i<=23)) {
if (pirq_entries[i-16] != -1) {
if (!pirq_entries[i-16]) {
printk("disabling PIRQ%d\n", i-16);
@@ -516,16 +516,16 @@ void print_IO_APIC (void)
static void init_sym_mode (void)
{
printk("enabling Symmetric IO mode ... ");
- outb (0x70, 0x22);
- outb (0x01, 0x23);
+ outb_p (0x70, 0x22);
+ outb_p (0x01, 0x23);
printk("...done.\n");
}
void init_pic_mode (void)
{
printk("disabling Symmetric IO mode ... ");
- outb (0x70, 0x22);
- outb (0x00, 0x23);
+ outb_p (0x70, 0x22);
+ outb_p (0x00, 0x23);
printk("...done.\n");
}
@@ -579,17 +579,85 @@ static int ioapic_blacklisted (void)
return in_ioapic_list(ioapic_blacklist);
}
+static void setup_ioapic_id (void)
+{
+ struct IO_APIC_reg_00 reg_00;
-void setup_IO_APIC (void)
+ /*
+ * 'default' mptable configurations mean a hardwired setup,
+ * 2 CPUs, 16 APIC registers. IO-APIC ID is usually set to 0,
+ * setting it to ID 2 should be fine.
+ */
+
+ /*
+ * Sanity check, is ID 2 really free? Every APIC in the
+ * system must have a unique ID or we get lots of nice
+ * 'stuck on smp_invalidate_needed IPI wait' messages.
+ */
+ if (cpu_present_map & (1<<0x2))
+ panic("APIC ID 2 already used");
+
+ /*
+ * set the ID
+ */
+ *(int *)&reg_00 = io_apic_read(0);
+ printk("... changing IO-APIC physical APIC ID to 2 ...\n");
+ reg_00.ID = 0x2;
+ io_apic_write(0, *(int *)&reg_00);
+
+ /*
+ * Sanity check
+ */
+ *(int *)&reg_00 = io_apic_read(0);
+ if (reg_00.ID != 0x2)
+ panic("could not set ID");
+}
+
+static void construct_default_ISA_mptable (void)
{
- int i;
+ int i, pos=0;
+
+ for (i=0; i<16; i++) {
+ if (!IO_APIC_IRQ(i))
+ continue;
+
+ mp_irqs[pos].mpc_irqtype = 0;
+ mp_irqs[pos].mpc_irqflag = 0;
+ mp_irqs[pos].mpc_srcbus = 0;
+ mp_irqs[pos].mpc_srcbusirq = i;
+ mp_irqs[pos].mpc_dstapic = 0;
+ mp_irqs[pos].mpc_dstirq = i;
+ pos++;
+ }
+ mp_irq_entries = pos;
+ mp_bus_id_to_type[0] = MP_BUS_ISA;
+
/*
- * Map the IO APIC into kernel space
+ * MP specification 1.4 defines some extra rules for default
+ * configurations, fix them up here:
*/
+
+ switch (mpc_default_type)
+ {
+ case 2:
+ break;
+ default:
+ /*
+ * pin 2 is IRQ0:
+ */
+ mp_irqs[0].mpc_dstirq = 2;
+ }
- printk("mapping IO APIC from standard address.\n");
- io_apic_reg = ioremap_nocache(IO_APIC_BASE,4096);
- printk("new virtual address: %p.\n",io_apic_reg);
+ setup_ioapic_id();
+}
+
+void setup_IO_APIC (void)
+{
+ int i;
+
+ if (!pirqs_enabled)
+ for (i=0; i<MAX_PIRQS; i++)
+ pirq_entries[i]=-1;
init_sym_mode();
{
@@ -605,12 +673,6 @@ void setup_IO_APIC (void)
for (i=0; i<nr_ioapic_registers; i++)
clear_IO_APIC_irq (i);
-#if DEBUG_1
- for (i=0; i<16; i++)
- if (IO_APIC_IRQ(i))
- setup_IO_APIC_irq_ISA_default (i);
-#endif
-
/*
* the following IO-APIC's can be enabled:
*
@@ -634,7 +696,18 @@ void setup_IO_APIC (void)
io_apic_irqs = 0;
}
+ /*
+ * If there are no explicit mp irq entries: it's either one of the
+ * default configuration types or we are broken. In both cases it's
+ * fine to set up most of the low 16 IOAPIC pins to ISA defaults.
+ */
+ if (!mp_irq_entries) {
+ printk("no explicit IRQ entries, using default mptable\n");
+ construct_default_ISA_mptable();
+ }
+
init_IO_APIC_traps();
+
setup_IO_APIC_irqs ();
if (!timer_irq_works ()) {
@@ -644,9 +717,9 @@ void setup_IO_APIC (void)
printk("..MP-BIOS bug: i8254 timer not connected to IO-APIC\n");
printk("..falling back to 8259A-based timer interrupt\n");
}
-
- printk("nr of MP irq sources: %d.\n", mp_irq_entries);
- printk("nr of IOAPIC registers: %d.\n", nr_ioapic_registers);
+
+ printk("nr of MP irq sources: %d.\n", mp_irq_entries);
+ printk("nr of IOAPIC registers: %d.\n", nr_ioapic_registers);
print_IO_APIC();
}
diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c
index 44fd26530..19587312a 100644
--- a/arch/i386/kernel/ioport.c
+++ b/arch/i386/kernel/ioport.c
@@ -76,8 +76,6 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
return 0;
}
-unsigned int *stack;
-
/*
* sys_iopl has to be used when you want to access the IO ports
* beyond the 0x3ff range: to get the full 65536 ports bitmapped
diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
index 95ce9fb14..2b8b86cc7 100644
--- a/arch/i386/kernel/irq.c
+++ b/arch/i386/kernel/irq.c
@@ -68,10 +68,6 @@ static unsigned int cached_irq_mask = (1<<NR_IRQS)-1;
spinlock_t irq_controller_lock;
-static unsigned int irq_events [NR_IRQS] = { -1, };
-static int disabled_irq [NR_IRQS] = { 0, };
-static int ipi_pending [NR_IRQS] = { 0, };
-
/*
* Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
* boards the timer interrupt and sometimes the keyboard interrupt is
@@ -124,11 +120,34 @@ static struct hw_interrupt_type ioapic_irq_type = {
};
#endif
-struct hw_interrupt_type *irq_handles[NR_IRQS] =
-{
- [0 ... 15] = &i8259A_irq_type /* standard ISA IRQs */
+/*
+ * Status: reason for being disabled: somebody has
+ * done a "disable_irq()" or we must not re-enter the
+ * already executing irq..
+ */
+#define IRQ_INPROGRESS 1
+#define IRQ_DISABLED 2
+
+/*
+ * This is the "IRQ descriptor", which contains various information
+ * about the irq, including what kind of hardware handling it has,
+ * whether it is disabled etc etc.
+ *
+ * Pad this out to 32 bytes for cache and indexing reasons.
+ */
+typedef struct {
+ unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */
+ unsigned int events; /* Do we have any pending events? */
+ unsigned int ipi; /* Have we sent off the pending IPI? */
+ struct hw_interrupt_type *handler; /* handle/enable/disable functions */
+ struct irqaction *action; /* IRQ action list */
+ unsigned int unused[3];
+} irq_desc_t;
+
+irq_desc_t irq_desc[NR_IRQS] = {
+ [0 ... 15] = { 0, 0, 0, &i8259A_irq_type, }, /* standard ISA IRQs */
#ifdef __SMP__
- , [16 ... NR_IRQS-1] = &ioapic_irq_type /* 'high' PCI IRQs */
+ [16 ... 23] = { 0, 0, 0, &ioapic_irq_type, }, /* 'high' PCI IRQs */
#endif
};
@@ -175,6 +194,7 @@ void set_8259A_irq_mask(unsigned int irq)
void unmask_generic_irq(unsigned int irq)
{
+ irq_desc[irq].status = 0;
if (IO_APIC_IRQ(irq))
enable_IO_APIC_irq(irq);
else {
@@ -241,6 +261,7 @@ BUILD_IRQ(23)
BUILD_SMP_INTERRUPT(reschedule_interrupt)
BUILD_SMP_INTERRUPT(invalidate_interrupt)
BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
+BUILD_SMP_INTERRUPT(mtrr_interrupt)
/*
* every pentium local APIC has two 'local interrupts', with a
@@ -297,17 +318,6 @@ static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL };
*/
static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL};
-static struct irqaction *irq_action[NR_IRQS] = {
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL
-#ifdef __SMP__
- ,NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL
-#endif
-};
-
int get_irq_list(char *buf)
{
int i, j;
@@ -320,7 +330,7 @@ int get_irq_list(char *buf)
*p++ = '\n';
for (i = 0 ; i < NR_IRQS ; i++) {
- action = irq_action[i];
+ action = irq_desc[i].action;
if (!action)
continue;
p += sprintf(p, "%3d: ",i);
@@ -335,7 +345,7 @@ int get_irq_list(char *buf)
if (IO_APIC_IRQ(i))
p += sprintf(p, " IO-APIC ");
else
- p += sprintf(p, " XT PIC ");
+ p += sprintf(p, " XT-PIC ");
p += sprintf(p, " %s", action->name);
for (action=action->next; action; action = action->next) {
@@ -535,20 +545,31 @@ static inline void get_irqlock(int cpu)
global_irq_holder = cpu;
}
+#define EFLAGS_IF_SHIFT 9
+
/*
* A global "cli()" while in an interrupt context
* turns into just a local cli(). Interrupts
* should use spinlocks for the (very unlikely)
* case that they ever want to protect against
* each other.
+ *
+ * If we already have local interrupts disabled,
+ * this will not turn a local disable into a
+ * global one (problems with spinlocks: this makes
+ * save_flags+cli+sti usable inside a spinlock).
*/
void __global_cli(void)
{
- int cpu = smp_processor_id();
+ unsigned int flags;
- __cli();
- if (!local_irq_count[cpu])
- get_irqlock(cpu);
+ __save_flags(flags);
+ if (flags & (1 << EFLAGS_IF_SHIFT)) {
+ int cpu = smp_processor_id();
+ __cli();
+ if (!local_irq_count[cpu])
+ get_irqlock(cpu);
+ }
}
void __global_sti(void)
@@ -560,33 +581,53 @@ void __global_sti(void)
__sti();
}
+/*
+ * SMP flags value to restore to:
+ * 0 - global cli
+ * 1 - global sti
+ * 2 - local cli
+ * 3 - local sti
+ */
unsigned long __global_save_flags(void)
{
- if (!local_irq_count[smp_processor_id()])
- return global_irq_holder == (unsigned char) smp_processor_id();
- else {
- unsigned long x;
- __save_flags(x);
- return x;
+ int retval;
+ int local_enabled;
+ unsigned long flags;
+
+ __save_flags(flags);
+ local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1;
+ /* default to local */
+ retval = 2 + local_enabled;
+
+ /* check for global flags if we're not in an interrupt */
+ if (!local_irq_count[smp_processor_id()]) {
+ if (local_enabled)
+ retval = 1;
+ if (global_irq_holder == (unsigned char) smp_processor_id())
+ retval = 0;
}
+ return retval;
}
void __global_restore_flags(unsigned long flags)
{
- if (!local_irq_count[smp_processor_id()]) {
- switch (flags) {
- case 0:
- __global_sti();
- break;
- case 1:
- __global_cli();
- break;
- default:
- printk("global_restore_flags: %08lx (%08lx)\n",
- flags, (&flags)[-1]);
- }
- } else
- __restore_flags(flags);
+ switch (flags) {
+ case 0:
+ __global_cli();
+ break;
+ case 1:
+ __global_sti();
+ break;
+ case 2:
+ __cli();
+ break;
+ case 3:
+ __sti();
+ break;
+ default:
+ printk("global_restore_flags: %08lx (%08lx)\n",
+ flags, (&flags)[-1]);
+ }
}
#endif
@@ -597,7 +638,7 @@ static int handle_IRQ_event(unsigned int irq, struct pt_regs * regs)
int status;
status = 0;
- action = *(irq + irq_action);
+ action = irq_desc[irq].action;
if (action) {
status |= 1;
@@ -618,125 +659,26 @@ static int handle_IRQ_event(unsigned int irq, struct pt_regs * regs)
return status;
}
-
-void disable_irq(unsigned int irq)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&irq_controller_lock, flags);
- irq_handles[irq]->disable(irq);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
-
- synchronize_irq();
-}
-
/*
* disable/enable_irq() wait for all irq contexts to finish
* executing. Also it's recursive.
*/
static void disable_8259A_irq(unsigned int irq)
{
- disabled_irq[irq]++;
cached_irq_mask |= 1 << irq;
set_8259A_irq_mask(irq);
}
-#ifdef __SMP__
-static void disable_ioapic_irq(unsigned int irq)
-{
- disabled_irq[irq]++;
- /*
- * We do not disable IO-APIC irqs in hardware ...
- */
-}
-#endif
-
void enable_8259A_irq (unsigned int irq)
{
- unsigned long flags;
- spin_lock_irqsave(&irq_controller_lock, flags);
- if (disabled_irq[irq])
- disabled_irq[irq]--;
- else {
- spin_unlock_irqrestore(&irq_controller_lock, flags);
- return;
- }
cached_irq_mask &= ~(1 << irq);
set_8259A_irq_mask(irq);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
-}
-
-#ifdef __SMP__
-void enable_ioapic_irq (unsigned int irq)
-{
- unsigned long flags, should_handle_irq;
- int cpu = smp_processor_id();
-
- spin_lock_irqsave(&irq_controller_lock, flags);
- if (disabled_irq[irq])
- disabled_irq[irq]--;
- else {
- spin_unlock_irqrestore(&irq_controller_lock, flags);
- return;
- }
-#if 0
- /*
- * In the SMP+IOAPIC case it might happen that there are an unspecified
- * number of pending IRQ events unhandled. These cases are very rare,
- * so we 'resend' these IRQs via IPIs, to the same CPU. It's much
- * better to do it this way as thus we dont have to be aware of
- * 'pending' interrupts in the IRQ path, except at this point.
- */
- if (!disabled_irq[irq] && irq_events[irq]) {
- if (!ipi_pending[irq]) {
- ipi_pending[irq] = 1;
- --irq_events[irq];
- send_IPI(cpu,IO_APIC_VECTOR(irq));
- }
- }
- spin_unlock_irqrestore(&irq_controller_lock, flags);
-#else
- if (!disabled_irq[irq] && irq_events[irq]) {
- struct pt_regs regs; /* FIXME: these are fake currently */
-
- disabled_irq[irq]++;
- hardirq_enter(cpu);
- spin_unlock(&irq_controller_lock);
-
- release_irqlock(cpu);
- while (test_bit(0,&global_irq_lock)) mb();
-again:
- handle_IRQ_event(irq, &regs);
-
- spin_lock(&irq_controller_lock);
- disabled_irq[irq]--;
- should_handle_irq=0;
- if (--irq_events[irq] && !disabled_irq[irq]) {
- should_handle_irq=1;
- disabled_irq[irq]++;
- }
- spin_unlock(&irq_controller_lock);
-
- if (should_handle_irq)
- goto again;
-
- irq_exit(cpu, irq);
- __restore_flags(flags);
- } else
- spin_unlock_irqrestore(&irq_controller_lock, flags);
-#endif
-}
-#endif
-
-void enable_irq(unsigned int irq)
-{
- irq_handles[irq]->enable(irq);
}
void make_8259A_irq (unsigned int irq)
{
io_apic_irqs &= ~(1<<irq);
- irq_handles[irq] = &i8259A_irq_type;
+ irq_desc[irq].handler = &i8259A_irq_type;
disable_irq(irq);
enable_irq(irq);
}
@@ -750,6 +692,7 @@ void make_8259A_irq (unsigned int irq)
static inline void mask_and_ack_8259A(unsigned int irq)
{
spin_lock(&irq_controller_lock);
+ irq_desc[irq].status |= IRQ_INPROGRESS;
cached_irq_mask |= 1 << irq;
if (irq & 8) {
inb(0xA1); /* DUMMY */
@@ -772,7 +715,8 @@ static void do_8259A_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
if (handle_IRQ_event(irq, regs)) {
spin_lock(&irq_controller_lock);
- unmask_8259A(irq);
+ if (!(irq_desc[irq].status &= IRQ_DISABLED))
+ unmask_8259A(irq);
spin_unlock(&irq_controller_lock);
}
@@ -780,41 +724,119 @@ static void do_8259A_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
}
#ifdef __SMP__
+
+/*
+ * In the SMP+IOAPIC case it might happen that there are an unspecified
+ * number of pending IRQ events unhandled. These cases are very rare,
+ * so we 'resend' these IRQs via IPIs, to the same CPU. It's much
+ * better to do it this way as thus we dont have to be aware of
+ * 'pending' interrupts in the IRQ path, except at this point.
+ */
+static void enable_ioapic_irq(unsigned int irq)
+{
+ irq_desc_t *desc = irq_desc + irq;
+ if (desc->events && !desc->ipi) {
+ desc->ipi = 1;
+ send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq));
+ }
+}
+
+/*
+ * We do not actually disable IO-APIC irqs in hardware ...
+ */
+static void disable_ioapic_irq(unsigned int irq)
+{
+}
+
static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs)
{
- int should_handle_irq = 0;
+ irq_desc_t *desc = irq_desc + irq;
+
+ spin_lock(&irq_controller_lock);
+ /* Ack the irq inside the lock! */
ack_APIC_irq();
+ desc->ipi = 0;
- spin_lock(&irq_controller_lock);
- if (ipi_pending[irq])
- ipi_pending[irq] = 0;
+ /* If the irq is disabled for whatever reason, just set a flag and return */
+ if (desc->status & (IRQ_DISABLED | IRQ_INPROGRESS)) {
+ desc->events = 1;
+ spin_unlock(&irq_controller_lock);
+ return;
+ }
- if (!irq_events[irq]++ && !disabled_irq[irq])
- should_handle_irq = 1;
+ desc->status = IRQ_INPROGRESS;
+ desc->events = 0;
hardirq_enter(cpu);
spin_unlock(&irq_controller_lock);
- if (should_handle_irq) {
- while (test_bit(0,&global_irq_lock)) mb();
-again:
- handle_IRQ_event(irq, regs);
+ while (test_bit(0,&global_irq_lock)) barrier();
+
+ for (;;) {
+ int pending;
+
+ /* If there is no IRQ handler, exit early, leaving the irq "in progress" */
+ if (!handle_IRQ_event(irq, regs))
+ goto no_handler;
spin_lock(&irq_controller_lock);
- should_handle_irq=0;
- if (--irq_events[irq] && !disabled_irq[irq])
- should_handle_irq=1;
+ pending = desc->events;
+ desc->events = 0;
+ if (!pending)
+ break;
spin_unlock(&irq_controller_lock);
-
- if (should_handle_irq)
- goto again;
}
+ desc->status &= IRQ_DISABLED;
+ spin_unlock(&irq_controller_lock);
+no_handler:
hardirq_exit(cpu);
release_irqlock(cpu);
}
+
#endif
+
+/*
+ * Generic enable/disable code: this just calls
+ * down into the PIC-specific version for the actual
+ * hardware disable after having gotten the irq
+ * controller lock.
+ */
+void disable_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ /*
+ * At this point we may actually have a pending interrupt being active
+ * on another CPU. So don't touch the IRQ_INPROGRESS bit..
+ */
+ irq_desc[irq].status |= IRQ_DISABLED;
+ irq_desc[irq].handler->disable(irq);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+
+ synchronize_irq();
+}
+
+void enable_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+ /*
+ * In contrast to the above, we should _not_ have any concurrent
+ * interrupt activity here, so we just clear both disabled bits.
+ *
+ * This allows us to have IRQ_INPROGRESS set until we actually
+ * install a handler for this interrupt (make irq autodetection
+ * work by just looking at the status field for the irq)
+ */
+ irq_desc[irq].status = 0;
+ irq_desc[irq].handler->enable(irq);
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
+}
+
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
@@ -845,7 +867,7 @@ asmlinkage void do_IRQ(struct pt_regs regs)
int cpu = smp_processor_id();
kstat.irqs[cpu][irq]++;
- irq_handles[irq]->handle(irq, cpu, &regs);
+ irq_desc[irq].handler->handle(irq, cpu, &regs);
/*
* This should be conditional: we should really get
@@ -865,7 +887,7 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new)
struct irqaction *old, **p;
unsigned long flags;
- p = irq_action + irq;
+ p = &irq_desc[irq].action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ))
@@ -890,7 +912,7 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new)
spin_lock(&irq_controller_lock);
#ifdef __SMP__
if (IO_APIC_IRQ(irq)) {
- irq_handles[irq] = &ioapic_irq_type;
+ irq_desc[irq].handler = &ioapic_irq_type;
/*
* First disable it in the 8259A:
*/
@@ -948,7 +970,7 @@ void free_irq(unsigned int irq, void *dev_id)
printk("Trying to free IRQ%d\n",irq);
return;
}
- for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+ for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
@@ -964,32 +986,29 @@ void free_irq(unsigned int irq, void *dev_id)
}
/*
- * probing is always single threaded [FIXME: is this true?]
+ * IRQ autodetection code..
+ *
+ * This depends on the fact that any interrupt that
+ * comes in on to an unassigned handler will get stuck
+ * with "IRQ_INPROGRESS" asserted and the interrupt
+ * disabled.
*/
-static unsigned int probe_irqs[NR_CPUS][NR_IRQS];
-
unsigned long probe_irq_on (void)
{
- unsigned int i, j, irqs = 0;
+ unsigned int i, irqs = 0;
unsigned long delay;
/*
- * save current irq counts
- */
- memcpy(probe_irqs,kstat.irqs,NR_CPUS*NR_IRQS*sizeof(int));
-
- /*
* first, enable any unassigned irqs
*/
+ spin_lock_irq(&irq_controller_lock);
for (i = NR_IRQS-1; i > 0; i--) {
- if (!irq_action[i]) {
- unsigned long flags;
- spin_lock_irqsave(&irq_controller_lock, flags);
+ if (!irq_desc[i].action) {
unmask_generic_irq(i);
irqs |= (1 << i);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
}
}
+ spin_unlock_irq(&irq_controller_lock);
/*
* wait for spurious interrupts to increase counters
@@ -1000,35 +1019,35 @@ unsigned long probe_irq_on (void)
/*
* now filter out any obviously spurious interrupts
*/
- for (i=0; i<NR_IRQS; i++)
- for (j=0; j<NR_CPUS; j++)
- if (kstat.irqs[j][i] != probe_irqs[j][i])
- irqs &= ~(1UL << i);
+ spin_lock_irq(&irq_controller_lock);
+ for (i=0; i<NR_IRQS; i++) {
+ if (irq_desc[i].status & IRQ_INPROGRESS)
+ irqs &= ~(1UL << i);
+ }
+ spin_unlock_irq(&irq_controller_lock);
return irqs;
}
int probe_irq_off (unsigned long irqs)
{
- int i,j, irq_found = -1;
+ int i, irq_found = -1;
+ spin_lock_irq(&irq_controller_lock);
for (i=0; i<NR_IRQS; i++) {
- int sum = 0;
- for (j=0; j<NR_CPUS; j++) {
- sum += kstat.irqs[j][i];
- sum -= probe_irqs[j][i];
- }
- if (sum && (irqs & (1UL << i))) {
+ if ((irqs & 1) && (irq_desc[i].status & IRQ_INPROGRESS)) {
if (irq_found != -1) {
irq_found = -irq_found;
goto out;
- } else
- irq_found = i;
+ }
+ irq_found = i;
}
+ irqs >>= 1;
}
if (irq_found == -1)
irq_found = 0;
out:
+ spin_unlock_irq(&irq_controller_lock);
return irq_found;
}
@@ -1050,7 +1069,7 @@ void init_IO_APIC_traps(void)
for (i = 0; i < NR_IRQS ; i++)
if (IO_APIC_VECTOR(i) <= 0xfe) /* HACK */ {
if (IO_APIC_IRQ(i)) {
- irq_handles[i] = &ioapic_irq_type;
+ irq_desc[i].handler = &ioapic_irq_type;
/*
* First disable it in the 8259A:
*/
@@ -1071,10 +1090,9 @@ __initfunc(void init_IRQ(void))
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
- printk("INIT IRQ\n");
for (i=0; i<NR_IRQS; i++) {
- irq_events[i] = 0;
- disabled_irq[i] = 0;
+ irq_desc[i].events = 0;
+ irq_desc[i].status = 0;
}
/*
* 16 old-style INTA-cycle interrupt gates:
@@ -1110,6 +1128,9 @@ __initfunc(void init_IRQ(void))
/* self generated IPI for local APIC timer */
set_intr_gate(0x41, apic_timer_interrupt);
+ /* IPI for MTRR control */
+ set_intr_gate(0x50, mtrr_interrupt);
+
#endif
request_region(0x20,0x20,"pic1");
request_region(0xa0,0x20,"pic2");
diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h
index 9824026dc..81795c85c 100644
--- a/arch/i386/kernel/irq.h
+++ b/arch/i386/kernel/irq.h
@@ -23,10 +23,7 @@ void init_pic_mode (void);
extern unsigned int io_apic_irqs;
-extern inline int IO_APIC_VECTOR (int irq)
-{
- return (0x51+(irq<<3));
-}
+#define IO_APIC_VECTOR(irq) (0x51+((irq)<<3))
#define MAX_IRQ_SOURCES 128
#define MAX_MP_BUSSES 32
@@ -83,8 +80,8 @@ static inline void irq_exit(int cpu, unsigned int irq)
#define SAVE_ALL \
"cld\n\t" \
- "push %es\n\t" \
- "push %ds\n\t" \
+ "pushl %es\n\t" \
+ "pushl %ds\n\t" \
"pushl %eax\n\t" \
"pushl %ebp\n\t" \
"pushl %edi\n\t" \
@@ -93,8 +90,8 @@ static inline void irq_exit(int cpu, unsigned int irq)
"pushl %ecx\n\t" \
"pushl %ebx\n\t" \
"movl $" STR(__KERNEL_DS) ",%edx\n\t" \
- "mov %dx,%ds\n\t" \
- "mov %dx,%es\n\t"
+ "movl %dx,%ds\n\t" \
+ "movl %dx,%es\n\t"
#define IRQ_NAME2(nr) nr##_interrupt(void)
#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
diff --git a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c
index 65c743195..64d4ab153 100644
--- a/arch/i386/kernel/ldt.c
+++ b/arch/i386/kernel/ldt.c
@@ -18,7 +18,7 @@
static int read_ldt(void * ptr, unsigned long bytecount)
{
- void * address = current->ldt;
+ void * address = current->mm->segments;
unsigned long size;
if (!ptr)
@@ -37,6 +37,7 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
{
struct modify_ldt_ldt_s ldt_info;
unsigned long *lp;
+ struct mm_struct * mm;
int error, i;
if (bytecount != sizeof(ldt_info))
@@ -48,19 +49,32 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
return -EINVAL;
- if (!current->ldt) {
+ mm = current->mm;
+
+ /*
+ * Horrible dependencies! Try to get rid of this. This is wrong,
+ * as it only reloads the ldt for the first process with this
+ * mm. The implications are that you should really make sure that
+ * you have a ldt before you do the first clone(), otherwise
+ * you get strange behaviour (the kernel is safe, it's just user
+ * space strangeness).
+ *
+ * For no good reason except historical, the GDT index of the LDT
+ * is chosen to follow the index number in the task[] array.
+ */
+ if (!mm->segments) {
for (i=1 ; i<NR_TASKS ; i++) {
if (task[i] == current) {
- if (!(current->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
+ if (!(mm->segments = (void *) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
return -ENOMEM;
- memset(current->ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
- set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES);
+ memset(mm->segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
+ set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, mm->segments, LDT_ENTRIES);
load_ldt(i);
}
}
}
- lp = (unsigned long *) &current->ldt[ldt_info.entry_number];
+ lp = (unsigned long *) (LDT_ENTRY_SIZE * ldt_info.entry_number + (unsigned long) mm->segments);
/* Allow LDTs to be cleared by the user. */
if (ldt_info.base_addr == 0 && ldt_info.limit == 0
&& (oldmode ||
diff --git a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c
index de6de8f14..ae67822bc 100644
--- a/arch/i386/kernel/mca.c
+++ b/arch/i386/kernel/mca.c
@@ -62,7 +62,7 @@ static struct MCA_info* mca_info = 0;
/*--------------------------------------------------------------------*/
#ifdef CONFIG_PROC_FS
-static long mca_do_proc_init( long memory_start, long memory_end );
+static void mca_do_proc_init( void );
static int mca_default_procfn( char* buf, int slot );
static ssize_t proc_mca_read( struct file*, char*, size_t, loff_t *);
@@ -79,7 +79,7 @@ static struct inode_operations proc_mca_inode_operations = {
/*--------------------------------------------------------------------*/
-__initfunc(long mca_init(long memory_start, long memory_end))
+__initfunc(void mca_init(void))
{
unsigned int i, j;
int foundscsi = 0;
@@ -96,21 +96,14 @@ __initfunc(long mca_init(long memory_start, long memory_end))
*/
if (!MCA_bus)
- return memory_start;
+ return;
cli();
/*
* Allocate MCA_info structure (at address divisible by 8)
*/
- if( ((memory_start+7)&(~7)) > memory_end )
- {
- /* uh oh */
- return memory_start;
- }
-
- mca_info = (struct MCA_info*) ((memory_start+7)&(~7));
- memory_start = ((long)mca_info) + sizeof(struct MCA_info);
+ mca_info = kmalloc(sizeof(struct MCA_info), GFP_ATOMIC);
/*
* Make sure adapter setup is off
@@ -194,10 +187,8 @@ __initfunc(long mca_init(long memory_start, long memory_end))
request_region(0x100,0x08,"POS (MCA)");
#ifdef CONFIG_PROC_FS
- memory_start = mca_do_proc_init( memory_start, memory_end );
+ mca_do_proc_init();
#endif
-
- return memory_start;
}
/*--------------------------------------------------------------------*/
@@ -418,12 +409,12 @@ int get_mca_info(char *buf)
/*--------------------------------------------------------------------*/
-__initfunc(long mca_do_proc_init( long memory_start, long memory_end ))
+__initfunc(void mca_do_proc_init( void ))
{
int i = 0;
struct proc_dir_entry* node = 0;
- if( mca_info == 0 ) return memory_start; /* never happens */
+ if( mca_info == 0 ) return; /* never happens */
proc_register( &proc_mca, &(struct proc_dir_entry) {
PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO,
@@ -439,11 +430,7 @@ __initfunc(long mca_do_proc_init( long memory_start, long memory_end ))
mca_info->slot[i].dev = 0;
if( ! mca_isadapter( i ) ) continue;
- if( memory_start + sizeof(struct proc_dir_entry) > memory_end ) {
- continue;
- }
- node = (struct proc_dir_entry*) memory_start;
- memory_start += sizeof(struct proc_dir_entry);
+ node = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC);
if( i < MCA_MAX_SLOT_NR ) {
node->low_ino = PROC_MCA_SLOT + i;
@@ -464,7 +451,6 @@ __initfunc(long mca_do_proc_init( long memory_start, long memory_end ))
proc_register( &proc_mca, node );
}
- return memory_start;
} /* mca_do_proc_init() */
/*--------------------------------------------------------------------*/
diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c
new file mode 100644
index 000000000..f2981c5cf
--- /dev/null
+++ b/arch/i386/kernel/mtrr.c
@@ -0,0 +1,1229 @@
+/* Generic MTRR (Memory Type Range Register) driver.
+
+ Copyright (C) 1997-1998 Richard Gooch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Richard Gooch may be reached by email at rgooch@atnf.csiro.au
+ The postal address is:
+ Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+ Source: "Pentium Pro Family Developer's Manual, Volume 3:
+ Operating System Writer's Guide" (Intel document number 242692),
+ section 11.11.7
+
+ ChangeLog
+
+ Prehistory Martin Tischhäuser <martin@ikcbarka.fzk.de>
+ Initial register-setting code (from proform-1.0).
+ 19971216 Richard Gooch <rgooch@atnf.csiro.au>
+ Original version for /proc/mtrr interface, SMP-safe.
+ v1.0
+ 19971217 Richard Gooch <rgooch@atnf.csiro.au>
+ Bug fix for ioctls()'s.
+ Added sample code in Documentation/mtrr.txt
+ v1.1
+ 19971218 Richard Gooch <rgooch@atnf.csiro.au>
+ Disallow overlapping regions.
+ 19971219 Jens Maurer <jmaurer@menuett.rhein-main.de>
+ Register-setting fixups.
+ v1.2
+ 19971222 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixups for kernel 2.1.75.
+ v1.3
+ 19971229 David Wragg <dpw@doc.ic.ac.uk>
+ Register-setting fixups and conformity with Intel conventions.
+ 19971229 Richard Gooch <rgooch@atnf.csiro.au>
+ Cosmetic changes and wrote this ChangeLog ;-)
+ 19980106 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixups for kernel 2.1.78.
+ v1.4
+ 19980119 David Wragg <dpw@doc.ic.ac.uk>
+ Included passive-release enable code (elsewhere in PCI setup).
+ v1.5
+ 19980131 Richard Gooch <rgooch@atnf.csiro.au>
+ Replaced global kernel lock with private spinlock.
+ v1.6
+ 19980201 Richard Gooch <rgooch@atnf.csiro.au>
+ Added wait for other CPUs to complete changes.
+ v1.7
+ 19980202 Richard Gooch <rgooch@atnf.csiro.au>
+ Bug fix in definition of <set_mtrr> for UP.
+ v1.8
+ 19980319 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixups for kernel 2.1.90.
+ 19980323 Richard Gooch <rgooch@atnf.csiro.au>
+ Move SMP BIOS fixup before secondary CPUs call <calibrate_delay>
+ v1.9
+ 19980325 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed test for overlapping regions: confused by adjacent regions
+ 19980326 Richard Gooch <rgooch@atnf.csiro.au>
+ Added wbinvd in <set_mtrr_prepare>.
+ 19980401 Richard Gooch <rgooch@atnf.csiro.au>
+ Bug fix for non-SMP compilation.
+ 19980418 David Wragg <dpw@doc.ic.ac.uk>
+ Fixed-MTRR synchronisation for SMP and use atomic operations
+ instead of spinlocks.
+ 19980418 Richard Gooch <rgooch@atnf.csiro.au>
+ Differentiate different MTRR register classes for BIOS fixup.
+ v1.10
+ 19980419 David Wragg <dpw@doc.ic.ac.uk>
+ Bug fix in variable MTRR synchronisation.
+ v1.11
+ 19980419 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixups for kernel 2.1.97.
+ v1.12
+ 19980421 Richard Gooch <rgooch@atnf.csiro.au>
+ Safer synchronisation across CPUs when changing MTRRs.
+ v1.13
+ 19980423 Richard Gooch <rgooch@atnf.csiro.au>
+ Bugfix for SMP systems without MTRR support.
+ v1.14
+ 19980427 Richard Gooch <rgooch@atnf.csiro.au>
+ Trap calls to <mtrr_add> and <mtrr_del> on non-MTRR machines.
+ v1.15
+ 19980427 Richard Gooch <rgooch@atnf.csiro.au>
+ Use atomic bitops for setting SMP change mask.
+ v1.16
+ 19980428 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed spurious diagnostic message.
+ v1.17
+ 19980429 Richard Gooch <rgooch@atnf.csiro.au>
+ Moved register-setting macros into this file.
+ Moved setup code from init/main.c to i386-specific areas.
+ v1.18
+*/
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#define MTRR_NEED_STRINGS
+#include <asm/mtrr.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/smp_lock.h>
+#include <asm/atomic.h>
+#include <linux/smp.h>
+
+#define MTRR_VERSION "1.18 (19980429)"
+
+#define TRUE 1
+#define FALSE 0
+
+#define X86_FEATURE_MTRR 0x1000 /* memory type registers */
+
+#define MTRRcap_MSR 0x0fe
+#define MTRRdefType_MSR 0x2ff
+
+#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg))
+#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1)
+
+#define NUM_FIXED_RANGES 88
+#define MTRRfix64K_00000_MSR 0x250
+#define MTRRfix16K_80000_MSR 0x258
+#define MTRRfix16K_A0000_MSR 0x259
+#define MTRRfix4K_C0000_MSR 0x268
+#define MTRRfix4K_C8000_MSR 0x269
+#define MTRRfix4K_D0000_MSR 0x26a
+#define MTRRfix4K_D8000_MSR 0x26b
+#define MTRRfix4K_E0000_MSR 0x26c
+#define MTRRfix4K_E8000_MSR 0x26d
+#define MTRRfix4K_F0000_MSR 0x26e
+#define MTRRfix4K_F8000_MSR 0x26f
+
+#ifdef __SMP__
+# define MTRR_CHANGE_MASK_FIXED 0x01
+# define MTRR_CHANGE_MASK_VARIABLE 0x02
+# define MTRR_CHANGE_MASK_DEFTYPE 0x04
+#endif
+
+/* In the processor's MTRR interface, the MTRR type is always held in
+ an 8 bit field: */
+typedef u8 mtrr_type;
+
+#define LINE_SIZE 80
+#define JIFFIE_TIMEOUT 100
+
+#ifdef __SMP__
+# define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type)
+#else
+# define set_mtrr(reg,base,size,type) set_mtrr_up (reg, base, size, type,TRUE)
+#endif
+
+#ifndef CONFIG_PROC_FS
+# define compute_ascii() while (0)
+#endif
+
+#ifdef CONFIG_PROC_FS
+static char *ascii_buffer = NULL;
+static unsigned int ascii_buf_bytes = 0;
+#endif
+static unsigned int *usage_table = NULL;
+#ifdef __SMP__
+static spinlock_t main_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/* Private functions */
+#ifdef CONFIG_PROC_FS
+static void compute_ascii (void);
+#endif
+
+
+struct set_mtrr_context
+{
+ unsigned long flags;
+ unsigned long deftype_lo;
+ unsigned long deftype_hi;
+ unsigned long cr4val;
+};
+
+/*
+ * Access to machine-specific registers (available on 586 and better only)
+ * Note: the rd* operations modify the parameters directly (without using
+ * pointer indirection), this allows gcc to optimize better
+ */
+#define rdmsr(msr,val1,val2) \
+ __asm__ __volatile__("rdmsr" \
+ : "=a" (val1), "=d" (val2) \
+ : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+ __asm__ __volatile__("wrmsr" \
+ : /* no outputs */ \
+ : "c" (msr), "a" (val1), "d" (val2))
+
+#define rdtsc(low,high) \
+ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdpmc(counter,low,high) \
+ __asm__ __volatile__("rdpmc" \
+ : "=a" (low), "=d" (high) \
+ : "c" (counter))
+
+
+/* Put the processor into a state where MTRRs can be safely set. */
+static void set_mtrr_prepare(struct set_mtrr_context *ctxt)
+{
+ unsigned long tmp;
+
+ /* disable interrupts */
+ save_flags(ctxt->flags); cli();
+
+ /* save value of CR4 and clear Page Global Enable (bit 7) */
+ asm volatile ("movl %%cr4, %0\n\t"
+ "movl %0, %1\n\t"
+ "andb $0x7f, %b1\n\t"
+ "movl %1, %%cr4\n\t"
+ : "=r" (ctxt->cr4val), "=q" (tmp) : : "memory");
+
+ /* disable and flush caches. Note that wbinvd flushes the TLBs as
+ a side-effect. */
+ asm volatile ("movl %%cr0, %0\n\t"
+ "orl $0x40000000, %0\n\t"
+ "wbinvd\n\t"
+ "movl %0, %%cr0\n\t"
+ "wbinvd\n\t"
+ : "=r" (tmp) : : "memory");
+
+ /* disable MTRRs, and set the default type to uncached. */
+ rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
+ wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);
+} /* End Function set_mtrr_prepare */
+
+
+/* Restore the processor after a set_mtrr_prepare */
+static void set_mtrr_done(struct set_mtrr_context *ctxt)
+{
+ unsigned long tmp;
+
+ /* flush caches and TLBs */
+ asm volatile ("wbinvd" : : : "memory" );
+
+ /* restore MTRRdefType */
+ wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
+
+ /* enable caches */
+ asm volatile ("movl %%cr0, %0\n\t"
+ "andl $0xbfffffff, %0\n\t"
+ "movl %0, %%cr0\n\t"
+ : "=r" (tmp) : : "memory");
+
+ /* restore value of CR4 */
+ asm volatile ("movl %0, %%cr4"
+ : : "r" (ctxt->cr4val) : "memory");
+
+ /* re-enable interrupts (if enabled previously) */
+ restore_flags(ctxt->flags);
+} /* End Function set_mtrr_done */
+
+
+/* this function returns the number of variable MTRRs */
+static unsigned int get_num_var_ranges (void)
+{
+ unsigned long config, dummy;
+
+ rdmsr(MTRRcap_MSR, config, dummy);
+ return (config & 0xff);
+} /* End Function get_num_var_ranges */
+
+
+/* non-zero if we have the write-combining memory type. */
+static int have_wrcomb (void)
+{
+ unsigned long config, dummy;
+
+ rdmsr(MTRRcap_MSR, config, dummy);
+ return (config & (1<<10));
+}
+
+
+static void get_mtrr (unsigned int reg, unsigned long *base,
+ unsigned long *size, mtrr_type *type)
+{
+ unsigned long dummy, mask_lo, base_lo;
+
+ rdmsr(MTRRphysMask_MSR(reg), mask_lo, dummy);
+ if ((mask_lo & 0x800) == 0) {
+ /* Invalid (i.e. free) range. */
+ *base = 0;
+ *size = 0;
+ *type = 0;
+ return;
+ }
+
+ rdmsr(MTRRphysBase_MSR(reg), base_lo, dummy);
+
+ /* We ignore the extra address bits (32-35). If someone wants to
+ run x86 Linux on a machine with >4GB memory, this will be the
+ least of their problems. */
+
+ /* Clean up mask_lo so it gives the real address mask. */
+ mask_lo = (mask_lo & 0xfffff000UL);
+
+ /* This works correctly if size is a power of two, i.e. a
+ contiguous range. */
+ *size = ~(mask_lo - 1);
+
+ *base = (base_lo & 0xfffff000UL);
+ *type = (base_lo & 0xff);
+} /* End Function get_mtrr */
+
+
+static void set_mtrr_up (unsigned int reg, unsigned long base,
+ unsigned long size, mtrr_type type, int do_safe)
+/* [SUMMARY] Set variable MTRR register on the local CPU.
+ <reg> The register to set.
+ <base> The base address of the region.
+ <size> The size of the region. If this is 0 the region is disabled.
+ <type> The type of the region.
+ <do_safe> If TRUE, do the change safely. If FALSE, safety measures should
+ be done externally.
+*/
+{
+ struct set_mtrr_context ctxt;
+
+ if (do_safe) set_mtrr_prepare (&ctxt);
+ if (size == 0)
+ {
+ /* The invalid bit is kept in the mask, so we simply clear the
+ relevant mask register to disable a range. */
+ wrmsr (MTRRphysMask_MSR (reg), 0, 0);
+ }
+ else
+ {
+ wrmsr (MTRRphysBase_MSR (reg), base | type, 0);
+ wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0);
+ }
+ if (do_safe) set_mtrr_done (&ctxt);
+} /* End Function set_mtrr_up */
+
+
+#ifdef __SMP__
+
+struct mtrr_var_range
+{
+ unsigned long base_lo;
+ unsigned long base_hi;
+ unsigned long mask_lo;
+ unsigned long mask_hi;
+};
+
+
+/* Get the MSR pair relating to a var range. */
+__initfunc(static void get_mtrr_var_range (unsigned int index,
+ struct mtrr_var_range *vr))
+{
+ rdmsr (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi);
+ rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi);
+} /* End Function get_mtrr_var_range */
+
+
+/* Set the MSR pair relating to a var range. Returns TRUE if
+ changes are made. */
+__initfunc(static int set_mtrr_var_range_testing (unsigned int index,
+ struct mtrr_var_range *vr))
+{
+ unsigned int lo, hi;
+ int changed = FALSE;
+
+ rdmsr(MTRRphysBase_MSR(index), lo, hi);
+
+ if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
+ || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) {
+ wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
+ changed = TRUE;
+ }
+
+ rdmsr(MTRRphysMask_MSR(index), lo, hi);
+
+ if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
+ || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) {
+ wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+
+__initfunc(static void get_fixed_ranges(mtrr_type *frs))
+{
+ unsigned long *p = (unsigned long *)frs;
+ int i;
+
+ rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
+
+ for (i = 0; i < 2; i++)
+ rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
+
+ for (i = 0; i < 8; i++)
+ rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
+}
+
+
+__initfunc(static int set_fixed_ranges_testing(mtrr_type *frs))
+{
+ unsigned long *p = (unsigned long *)frs;
+ int changed = FALSE;
+ int i;
+ unsigned long lo, hi;
+
+ rdmsr(MTRRfix64K_00000_MSR, lo, hi);
+ if (p[0] != lo || p[1] != hi) {
+ wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
+ changed = TRUE;
+ }
+
+ for (i = 0; i < 2; i++) {
+ rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
+ if (p[2 + i*2] != lo || p[3 + i*2] != hi) {
+ wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
+ changed = TRUE;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
+ if (p[6 + i*2] != lo || p[7 + i*2] != hi) {
+ wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
+ changed = TRUE;
+ }
+ }
+
+ return changed;
+}
+
+
+struct mtrr_state
+{
+ unsigned int num_var_ranges;
+ struct mtrr_var_range *var_ranges;
+ mtrr_type fixed_ranges[NUM_FIXED_RANGES];
+ unsigned char enabled;
+ mtrr_type def_type;
+};
+
+
+/* Grab all of the mtrr state for this cpu into *state. */
+__initfunc(static void get_mtrr_state(struct mtrr_state *state))
+{
+ unsigned int nvrs, i;
+ struct mtrr_var_range *vrs;
+ unsigned long lo, dummy;
+
+ nvrs = state->num_var_ranges = get_num_var_ranges();
+ vrs = state->var_ranges
+ = kmalloc(nvrs * sizeof(struct mtrr_var_range), GFP_KERNEL);
+ if (vrs == NULL)
+ nvrs = state->num_var_ranges = 0;
+
+ for (i = 0; i < nvrs; i++)
+ get_mtrr_var_range(i, &vrs[i]);
+
+ get_fixed_ranges(state->fixed_ranges);
+
+ rdmsr(MTRRdefType_MSR, lo, dummy);
+ state->def_type = (lo & 0xff);
+ state->enabled = (lo & 0xc00) >> 10;
+} /* End Function get_mtrr_state */
+
+
+/* Free resources associated with a struct mtrr_state */
+__initfunc(static void finalize_mtrr_state(struct mtrr_state *state))
+{
+ if (state->var_ranges) kfree (state->var_ranges);
+} /* End Function finalize_mtrr_state */
+
+
+__initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state,
+ struct set_mtrr_context *ctxt))
+/* [SUMMARY] Set the MTRR state for this CPU.
+ <state> The MTRR state information to read.
+ <ctxt> Some relevant CPU context.
+ [NOTE] The CPU must already be in a safe state for MTRR changes.
+ [RETURNS] 0 if no changes made, else a mask indication what was changed.
+*/
+{
+ unsigned int i;
+ unsigned long change_mask = 0;
+
+ for (i = 0; i < state->num_var_ranges; i++)
+ if (set_mtrr_var_range_testing(i, &state->var_ranges[i]))
+ change_mask |= MTRR_CHANGE_MASK_VARIABLE;
+
+ if (set_fixed_ranges_testing(state->fixed_ranges))
+ change_mask |= MTRR_CHANGE_MASK_FIXED;
+
+ /* set_mtrr_restore restores the old value of MTRRdefType,
+ so to set it we fiddle with the saved value. */
+ if ((ctxt->deftype_lo & 0xff) != state->def_type
+ || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled)
+ {
+ ctxt->deftype_lo |= (state->def_type | state->enabled << 10);
+ change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
+ }
+
+ return change_mask;
+} /* End Function set_mtrr_state */
+
+
+static atomic_t undone_count;
+static void (*handler_func) (struct set_mtrr_context *ctxt, void *info);
+static void *handler_info;
+static volatile int wait_barrier_execute = FALSE;
+static volatile int wait_barrier_cache_enable = FALSE;
+
+static void sync_handler (void)
+/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs.
+ [RETURNS] Nothing.
+*/
+{
+ struct set_mtrr_context ctxt;
+
+ set_mtrr_prepare (&ctxt);
+ /* Notify master CPU that I'm at the barrier and then wait */
+ atomic_dec (&undone_count);
+ while (wait_barrier_execute) barrier ();
+ /* The master has cleared me to execute */
+ (*handler_func) (&ctxt, handler_info);
+ /* Notify master CPU that I've executed the function */
+ atomic_dec (&undone_count);
+ /* Wait for master to clear me to enable cache and return */
+ while (wait_barrier_cache_enable) barrier ();
+ set_mtrr_done (&ctxt);
+} /* End Function sync_handler */
+
+static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt,
+ void *info),
+ void *info, int local)
+/* [SUMMARY] Execute a function on all CPUs, with caches flushed and disabled.
+ [PURPOSE] This function will synchronise all CPUs, flush and disable caches
+ on all CPUs, then call a specified function. When the specified function
+ finishes on all CPUs, caches are enabled on all CPUs.
+ <handler> The function to execute.
+ <info> An arbitrary information pointer which is passed to <<handler>>.
+ <local> If TRUE <<handler>> is executed locally.
+ [RETURNS] Nothing.
+*/
+{
+ unsigned long timeout;
+ struct set_mtrr_context ctxt;
+
+ mtrr_hook = sync_handler;
+ handler_func = handler;
+ handler_info = info;
+ wait_barrier_execute = TRUE;
+ wait_barrier_cache_enable = TRUE;
+ /* Send a message to all other CPUs and wait for them to enter the
+ barrier */
+ atomic_set (&undone_count, smp_num_cpus - 1);
+ smp_message_pass (MSG_ALL_BUT_SELF, MSG_MTRR_CHANGE, 0, 0);
+ /* Wait for it to be done */
+ timeout = jiffies + JIFFIE_TIMEOUT;
+ while ( (atomic_read (&undone_count) > 0) && (jiffies < timeout) )
+ barrier ();
+ if (atomic_read (&undone_count) > 0)
+ {
+ panic ("mtrr: timed out waiting for other CPUs\n");
+ }
+ mtrr_hook = NULL;
+ /* All other CPUs should be waiting for the barrier, with their caches
+ already flushed and disabled. Prepare for function completion
+ notification */
+ atomic_set (&undone_count, smp_num_cpus - 1);
+ /* Flush and disable the local CPU's cache and release the barier, which
+ should cause the other CPUs to execute the function. Also execute it
+ locally if required */
+ set_mtrr_prepare (&ctxt);
+ wait_barrier_execute = FALSE;
+ if (local) (*handler) (&ctxt, info);
+ /* Now wait for other CPUs to complete the function */
+ while (atomic_read (&undone_count) > 0) barrier ();
+ /* Now all CPUs should have finished the function. Release the barrier to
+ allow them to re-enable their caches and return from their interrupt,
+ then enable the local cache and return */
+ wait_barrier_cache_enable = FALSE;
+ set_mtrr_done (&ctxt);
+ handler_func = NULL;
+ handler_info = NULL;
+} /* End Function do_all_cpus */
+
+
+struct set_mtrr_data
+{
+ unsigned long smp_base;
+ unsigned long smp_size;
+ unsigned int smp_reg;
+ mtrr_type smp_type;
+};
+
+static void set_mtrr_handler (struct set_mtrr_context *ctxt, void *info)
+{
+ struct set_mtrr_data *data = info;
+
+ set_mtrr_up (data->smp_reg, data->smp_base, data->smp_size, data->smp_type,
+ FALSE);
+} /* End Function set_mtrr_handler */
+
+static void set_mtrr_smp (unsigned int reg, unsigned long base,
+ unsigned long size, mtrr_type type)
+{
+ struct set_mtrr_data data;
+
+ data.smp_reg = reg;
+ data.smp_base = base;
+ data.smp_size = size;
+ data.smp_type = type;
+ do_all_cpus (set_mtrr_handler, &data, TRUE);
+} /* End Function set_mtrr_smp */
+
+
+/* A warning that is common to the module and non-module cases. */
+/* Some BIOS's are fucked and don't set all MTRRs the same! */
+#ifdef MODULE
+static void mtrr_state_warn (unsigned long mask)
+#else
+__initfunc(static void mtrr_state_warn (unsigned long mask))
+#endif
+{
+ if (!mask) return;
+ if (mask & MTRR_CHANGE_MASK_FIXED)
+ printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n");
+ if (mask & MTRR_CHANGE_MASK_VARIABLE)
+ printk ("mtrr: your CPUs had inconsistent variable MTRR settings\n");
+ if (mask & MTRR_CHANGE_MASK_DEFTYPE)
+ printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n");
+ printk ("mtrr: probably your BIOS does not setup all CPUs\n");
+} /* End Function mtrr_state_warn */
+
+#ifdef MODULE
+/* As a module, copy the MTRR state using an IPI handler. */
+
+static volatile unsigned long smp_changes_mask = 0;
+
+static void copy_mtrr_state_handler (struct set_mtrr_context *ctxt, void *info)
+{
+ unsigned long mask, count;
+ struct mtrr_state *smp_mtrr_state = info;
+
+ mask = set_mtrr_state (smp_mtrr_state, ctxt);
+ /* Use the atomic bitops to update the global mask */
+ for (count = 0; count < sizeof mask * 8; ++count)
+ {
+ if (mask & 0x01) set_bit (count, &smp_changes_mask);
+ mask >>= 1;
+ }
+} /* End Function copy_mtrr_state_handler */
+
+/* Copies the entire MTRR state of this cpu to all the others. */
+static void copy_mtrr_state (void)
+{
+ struct mtrr_state ms;
+
+ get_mtrr_state (&ms);
+ do_all_cpus (copy_mtrr_state_handler, &ms, FALSE);
+ finalize_mtrr_state (&ms);
+ mtrr_state_warn (smp_changes_mask);
+} /* End Function copy_mtrr_state */
+
+#endif /* MODULE */
+#endif /* __SMP__ */
+
+static char *attrib_to_str (int x)
+{
+ return (x <= 6) ? mtrr_strings[x] : "?";
+} /* End Function attrib_to_str */
+
+static void init_table (void)
+{
+ int i, max;
+
+ max = get_num_var_ranges ();
+ if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) )
+ == NULL )
+ {
+ printk ("mtrr: could not allocate\n");
+ return;
+ }
+ for (i = 0; i < max; i++) usage_table[i] = 1;
+#ifdef CONFIG_PROC_FS
+ if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL )
+ {
+ printk ("mtrr: could not allocate\n");
+ return;
+ }
+ ascii_buf_bytes = 0;
+ compute_ascii ();
+#endif
+} /* End Function init_table */
+
+int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
+ char increment)
+/* [SUMMARY] Add an MTRR entry.
+ <base> The starting (base) address of the region.
+ <size> The size (in bytes) of the region.
+ <type> The type of the new region.
+ <increment> If true and the region already exists, the usage count will be
+ incremented.
+ [RETURNS] The MTRR register on success, else a negative number indicating
+ the error code.
+ [NOTE] This routine uses a spinlock.
+*/
+{
+ int i, max;
+ mtrr_type ltype;
+ unsigned long lbase, lsize, last;
+
+ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
+ if ( (base & 0xfff) || (size & 0xfff) )
+ {
+ printk ("mtrr: size and base must be multiples of 4kB\n");
+ printk ("mtrr: size: %lx base: %lx\n", size, base);
+ return -EINVAL;
+ }
+ if (base + size < 0x100000)
+ {
+ printk ("mtrr: cannot set region below 1 MByte (0x%lx,0x%lx)\n",
+ base, size);
+ return -EINVAL;
+ }
+ /* Check upper bits of base and last are equal and lower bits are 0 for
+ base and 1 for last */
+ last = base + size - 1;
+ for (lbase = base; !(lbase & 1) && (last & 1);
+ lbase = lbase >> 1, last = last >> 1);
+ if (lbase != last)
+ {
+ printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
+ base, size);
+ return -EINVAL;
+ }
+ if (type >= MTRR_NUM_TYPES)
+ {
+ printk ("mtrr: type: %u illegal\n", type);
+ return -EINVAL;
+ }
+ /* If the type is WC, check that this processor supports it */
+ if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
+ {
+ printk ("mtrr: your processor doesn't support write-combining\n");
+ return -ENOSYS;
+ }
+ increment = increment ? 1 : 0;
+ max = get_num_var_ranges ();
+ /* Search for existing MTRR */
+ spin_lock (&main_lock);
+ for (i = 0; i < max; ++i)
+ {
+ get_mtrr (i, &lbase, &lsize, &ltype);
+ if (base >= lbase + lsize) continue;
+ if ( (base < lbase) && (base + size <= lbase) ) continue;
+ /* At this point we know there is some kind of overlap/enclosure */
+ if ( (base < lbase) || (base + size > lbase + lsize) )
+ {
+ spin_unlock (&main_lock);
+ printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
+ base, size, lbase, lsize);
+ return -EINVAL;
+ }
+ if (ltype != type)
+ {
+ spin_unlock (&main_lock);
+ printk ( "mtrr: type missmatch for %lx,%lx old: %s new: %s\n",
+ base, size, attrib_to_str (ltype), attrib_to_str (type) );
+ return -EINVAL;
+ }
+ if (increment) ++usage_table[i];
+ compute_ascii ();
+ spin_unlock (&main_lock);
+ return i;
+ }
+ /* Search for an empty MTRR */
+ for (i = 0; i < max; ++i)
+ {
+ get_mtrr (i, &lbase, &lsize, &ltype);
+ if (lsize > 0) continue;
+ set_mtrr (i, base, size, type);
+ usage_table[i] = 1;
+ compute_ascii ();
+ spin_unlock (&main_lock);
+ return i;
+ }
+ spin_unlock (&main_lock);
+ printk ("mtrr: no more MTRRs available\n");
+ return -ENOSPC;
+} /* End Function mtrr_add */
+
+int mtrr_del (int reg, unsigned long base, unsigned long size)
+/* [SUMMARY] Delete MTRR/decrement usage count.
+ <reg> The register. If this is less than 0 then <<base>> and <<size>> must
+ be supplied.
+ <base> The base address of the region. This is ignored if <<reg>> is >= 0.
+ <size> The size of the region. This is ignored if <<reg>> is >= 0.
+ [RETURNS] The register on success, else a negative number indicating
+ the error code.
+ [NOTE] This routine uses a spinlock.
+*/
+{
+ int i, max;
+ mtrr_type ltype;
+ unsigned long lbase, lsize;
+
+ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
+ max = get_num_var_ranges ();
+ spin_lock (&main_lock);
+ if (reg < 0)
+ {
+ /* Search for existing MTRR */
+ for (i = 0; i < max; ++i)
+ {
+ get_mtrr (i, &lbase, &lsize, &ltype);
+ if ( (lbase == base) && (lsize == size) )
+ {
+ reg = i;
+ break;
+ }
+ }
+ if (reg < 0)
+ {
+ spin_unlock (&main_lock);
+ printk ("mtrr: no MTRR for %lx,%lx found\n", base, size);
+ return -EINVAL;
+ }
+ }
+ if (reg >= max)
+ {
+ spin_unlock (&main_lock);
+ printk ("mtrr: register: %d too big\n", reg);
+ return -EINVAL;
+ }
+ get_mtrr (reg, &lbase, &lsize, &ltype);
+ if (lsize < 1)
+ {
+ spin_unlock (&main_lock);
+ printk ("mtrr: MTRR %d not used\n", reg);
+ return -EINVAL;
+ }
+ if (usage_table[reg] < 1)
+ {
+ spin_unlock (&main_lock);
+ printk ("mtrr: reg: %d has count=0\n", reg);
+ return -EINVAL;
+ }
+ if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0);
+ compute_ascii ();
+ spin_unlock (&main_lock);
+ return reg;
+} /* End Function mtrr_del */
+
+#ifdef CONFIG_PROC_FS
+
+static int mtrr_file_add (unsigned long base, unsigned long size,
+ unsigned int type, char increment, struct file *file)
+{
+ int reg, max;
+ unsigned int *fcount = file->private_data;
+
+ max = get_num_var_ranges ();
+ if (fcount == NULL)
+ {
+ if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL )
+ {
+ printk ("mtrr: could not allocate\n");
+ return -ENOMEM;
+ }
+ memset (fcount, 0, max * sizeof *fcount);
+ file->private_data = fcount;
+ }
+ reg = mtrr_add (base, size, type, 1);
+ if (reg >= 0) ++fcount[reg];
+ return reg;
+} /* End Function mtrr_file_add */
+
+static int mtrr_file_del (unsigned long base, unsigned long size,
+ struct file *file)
+{
+ int reg;
+ unsigned int *fcount = file->private_data;
+
+ reg = mtrr_del (-1, base, size);
+ if (reg < 0) return reg;
+ if (fcount != NULL) --fcount[reg];
+ return reg;
+} /* End Function mtrr_file_del */
+
+static ssize_t mtrr_read (struct file *file, char *buf, size_t len,
+ loff_t *ppos)
+{
+ if (*ppos >= ascii_buf_bytes) return 0;
+ if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos;
+ if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT;
+ *ppos += len;
+ return len;
+} /* End Function mtrr_read */
+
+static ssize_t mtrr_write (struct file *file, const char *buf, size_t len,
+ loff_t *ppos)
+/* Format of control line:
+ "base=%lx size=%lx type=%s" OR:
+ "disable=%d"
+*/
+{
+ int i, err;
+ unsigned long reg, base, size;
+ char *ptr;
+ char line[LINE_SIZE];
+
+ if ( !suser () ) return -EPERM;
+ /* Can't seek (pwrite) on this device */
+ if (ppos != &file->f_pos) return -ESPIPE;
+ memset (line, 0, LINE_SIZE);
+ if (len > LINE_SIZE) len = LINE_SIZE;
+ if ( copy_from_user (line, buf, len - 1) ) return -EFAULT;
+ ptr = line + strlen (line) - 1;
+ if (*ptr == '\n') *ptr = '\0';
+ if ( !strncmp (line, "disable=", 8) )
+ {
+ reg = simple_strtoul (line + 8, &ptr, 0);
+ err = mtrr_del (reg, 0, 0);
+ if (err < 0) return err;
+ return len;
+ }
+ if ( strncmp (line, "base=", 5) )
+ {
+ printk ("mtrr: no \"base=\" in line: \"%s\"\n", line);
+ return -EINVAL;
+ }
+ base = simple_strtoul (line + 5, &ptr, 0);
+ for (; isspace (*ptr); ++ptr);
+ if ( strncmp (ptr, "size=", 5) )
+ {
+ printk ("mtrr: no \"size=\" in line: \"%s\"\n", line);
+ return -EINVAL;
+ }
+ size = simple_strtoul (ptr + 5, &ptr, 0);
+ for (; isspace (*ptr); ++ptr);
+ if ( strncmp (ptr, "type=", 5) )
+ {
+ printk ("mtrr: no \"type=\" in line: \"%s\"\n", line);
+ return -EINVAL;
+ }
+ ptr += 5;
+ for (; isspace (*ptr); ++ptr);
+ for (i = 0; i < MTRR_NUM_TYPES; ++i)
+ {
+ if ( strcmp (ptr, mtrr_strings[i]) ) continue;
+ err = mtrr_add (base, size, i, 1);
+ if (err < 0) return err;
+ return len;
+ }
+ printk ("mtrr: illegal type: \"%s\"\n", ptr);
+ return -EINVAL;
+} /* End Function mtrr_write */
+
+static int mtrr_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+ mtrr_type type;
+ struct mtrr_sentry sentry;
+ struct mtrr_gentry gentry;
+
+ switch (cmd)
+ {
+ default:
+ return -ENOIOCTLCMD;
+ case MTRRIOC_ADD_ENTRY:
+ if ( !suser () ) return -EPERM;
+ if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+ return -EFAULT;
+ err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file);
+ if (err < 0) return err;
+ break;
+ case MTRRIOC_SET_ENTRY:
+ if ( !suser () ) return -EPERM;
+ if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+ return -EFAULT;
+ err = mtrr_add (sentry.base, sentry.size, sentry.type, 0);
+ if (err < 0) return err;
+ break;
+ case MTRRIOC_DEL_ENTRY:
+ if ( !suser () ) return -EPERM;
+ if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+ return -EFAULT;
+ err = mtrr_file_del (sentry.base, sentry.size, file);
+ if (err < 0) return err;
+ break;
+ case MTRRIOC_GET_ENTRY:
+ if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) )
+ return -EFAULT;
+ if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL;
+ get_mtrr (gentry.regnum, &gentry.base, &gentry.size, &type);
+ gentry.type = type;
+ if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) )
+ return -EFAULT;
+ break;
+ }
+ return 0;
+} /* End Function mtrr_ioctl */
+
+static int mtrr_open (struct inode *ino, struct file *filep)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+} /* End Function mtrr_open */
+
+static int mtrr_close (struct inode *ino, struct file *file)
+{
+ int i, max;
+ unsigned int *fcount = file->private_data;
+
+ MOD_DEC_USE_COUNT;
+ if (fcount == NULL) return 0;
+ max = get_num_var_ranges ();
+ for (i = 0; i < max; ++i)
+ {
+ while (fcount[i] > 0)
+ {
+ if (mtrr_del (i, 0, 0) < 0) printk ("mtrr: reg %d not used\n", i);
+ --fcount[i];
+ }
+ }
+ kfree (fcount);
+ file->private_data = NULL;
+ return 0;
+} /* End Function mtrr_close */
+
+static struct file_operations mtrr_fops =
+{
+ NULL, /* Seek */
+ mtrr_read, /* Read */
+ mtrr_write, /* Write */
+ NULL, /* Readdir */
+ NULL, /* Poll */
+ mtrr_ioctl, /* IOctl */
+ NULL, /* MMAP */
+ mtrr_open, /* Open */
+ mtrr_close, /* Release */
+ NULL, /* Fsync */
+ NULL, /* Fasync */
+ NULL, /* CheckMediaChange */
+ NULL, /* Revalidate */
+ NULL, /* Lock */
+};
+
+static struct inode_operations proc_mtrr_inode_operations = {
+ &mtrr_fops, /* default property file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static struct proc_dir_entry proc_root_mtrr = {
+ PROC_MTRR, 4, "mtrr",
+ S_IFREG | S_IWUSR | S_IRUGO, 1, 0, 0,
+ 0, &proc_mtrr_inode_operations
+};
+
+static void compute_ascii (void)
+{
+ char factor;
+ int i, max;
+ mtrr_type type;
+ unsigned long base, size;
+
+ ascii_buf_bytes = 0;
+ max = get_num_var_ranges ();
+ for (i = 0; i < max; i++)
+ {
+ get_mtrr (i, &base, &size, &type);
+ if (size < 1) usage_table[i] = 0;
+ else
+ {
+ if (size < 0x100000)
+ {
+ /* 1MB */
+ factor = 'k';
+ size >>= 10;
+ }
+ else
+ {
+ factor = 'M';
+ size >>= 20;
+ }
+ sprintf
+ (ascii_buffer + ascii_buf_bytes,
+ "reg%02i: base=0x%08lx (%4liMB), size=%4li%cB: %s, count=%d\n",
+ i, base, base>>20, size, factor,
+ attrib_to_str (type), usage_table[i]);
+ ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes);
+ }
+ }
+ proc_root_mtrr.size = ascii_buf_bytes;
+} /* End Function compute_ascii */
+
+#endif /* CONFIG_PROC_FS */
+
+EXPORT_SYMBOL(mtrr_add);
+EXPORT_SYMBOL(mtrr_del);
+
+#if defined(__SMP__) && !defined(MODULE)
+
+static volatile unsigned long smp_changes_mask __initdata = 0;
+static struct mtrr_state smp_mtrr_state __initdata = {0, 0};
+
+__initfunc(void mtrr_init_boot_cpu (void))
+{
+ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
+ printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION);
+
+ get_mtrr_state (&smp_mtrr_state);
+} /* End Function mtrr_init_boot_cpu */
+
+__initfunc(void mtrr_init_secondary_cpu (void))
+{
+ unsigned long mask, count;
+ struct set_mtrr_context ctxt;
+
+ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
+ /* Note that this is not ideal, since the cache is only flushed/disabled
+ for this CPU while the MTRRs are changed, but changing this requires
+ more invasive changes to the way the kernel boots */
+ set_mtrr_prepare (&ctxt);
+ mask = set_mtrr_state (&smp_mtrr_state, &ctxt);
+ set_mtrr_done (&ctxt);
+ /* Use the atomic bitops to update the global mask */
+ for (count = 0; count < sizeof mask * 8; ++count)
+ {
+ if (mask & 0x01) set_bit (count, &smp_changes_mask);
+ mask >>= 1;
+ }
+} /* End Function mtrr_init_secondary_cpu */
+
+#endif
+
+#ifdef MODULE
+int init_module (void)
+#else
+__initfunc(int mtrr_init(void))
+#endif
+{
+# if !defined(__SMP__) || defined(MODULE)
+ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0;
+ printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION);
+#endif
+
+# ifdef __SMP__
+# ifdef MODULE
+ copy_mtrr_state ();
+# else /* MODULE */
+ finalize_mtrr_state (&smp_mtrr_state);
+ mtrr_state_warn (smp_changes_mask);
+# endif /* MODULE */
+# endif /* __SMP__ */
+
+# ifdef CONFIG_PROC_FS
+ proc_register (&proc_root, &proc_root_mtrr);
+# endif
+
+ init_table ();
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module (void)
+{
+ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return;
+# ifdef CONFIG_PROC_FS
+ proc_unregister (&proc_root, PROC_MTRR);
+# endif
+# ifdef __SMP__
+ mtrr_hook = NULL;
+# endif
+}
+#endif
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
index 6ba4e0ff8..a06477b9d 100644
--- a/arch/i386/kernel/process.c
+++ b/arch/i386/kernel/process.c
@@ -375,12 +375,12 @@ void machine_restart(char * __unused)
registers don't have to be reloaded after switching to real mode:
the values are consistent for real mode operation already. */
- __asm__ __volatile__ ("movw $0x0010,%%ax\n"
- "\tmovw %%ax,%%ds\n"
- "\tmovw %%ax,%%es\n"
- "\tmovw %%ax,%%fs\n"
- "\tmovw %%ax,%%gs\n"
- "\tmovw %%ax,%%ss" : : : "eax");
+ __asm__ __volatile__ ("movl $0x0010,%%eax\n"
+ "\tmovl %%ax,%%ds\n"
+ "\tmovl %%ax,%%es\n"
+ "\tmovl %%ax,%%fs\n"
+ "\tmovl %%ax,%%gs\n"
+ "\tmovl %%ax,%%ss" : : : "eax");
/* Jump to the 16-bit code that we copied earlier. It disables paging
and the cache, switches to real mode, and jumps to the BIOS reset
@@ -418,43 +418,37 @@ void show_regs(struct pt_regs * regs)
0xffff & regs->xds,0xffff & regs->xes);
}
+void release_segments(struct mm_struct *mm)
+{
+ void * ldt;
+
+ /* forget local segments */
+ __asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs ; lldt %w0"
+ : /* no outputs */
+ : "r" (0));
+ current->tss.ldt = 0;
+
+ ldt = mm->segments;
+ if (ldt) {
+ mm->segments = NULL;
+ vfree(ldt);
+ }
+}
+
/*
* Free current thread data structures etc..
*/
-
void exit_thread(void)
{
/* forget lazy i387 state */
if (last_task_used_math == current)
last_task_used_math = NULL;
- /* forget local segments */
- __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0"
- : /* no outputs */
- : "r" (0));
- current->tss.ldt = 0;
- if (current->ldt) {
- void * ldt = current->ldt;
- current->ldt = NULL;
- vfree(ldt);
- }
}
void flush_thread(void)
{
int i;
- if (current->ldt) {
- free_page((unsigned long) current->ldt);
- current->ldt = NULL;
- for (i=1 ; i<NR_TASKS ; i++) {
- if (task[i] == current) {
- set_ldt_desc(gdt+(i<<1)+
- FIRST_LDT_ENTRY,&default_ldt, 1);
- load_ldt(i);
- }
- }
- }
-
for (i=0 ; i<8 ; i++)
current->debugreg[i] = 0;
@@ -479,13 +473,30 @@ void release_thread(struct task_struct *dead_task)
{
}
+void copy_segments(int nr, struct task_struct *p, struct mm_struct *new_mm)
+{
+ int ldt_size = 1;
+ void * ldt = &default_ldt;
+ struct mm_struct * old_mm = current->mm;
+
+ p->tss.ldt = _LDT(nr);
+ if (old_mm->segments) {
+ new_mm->segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
+ if (new_mm->segments) {
+ ldt = new_mm->segments;
+ ldt_size = LDT_ENTRIES;
+ memcpy(ldt, old_mm->segments, LDT_ENTRIES*LDT_ENTRY_SIZE);
+ }
+ }
+ set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size);
+}
+
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
p->tss.tr = _TSS(nr);
- p->tss.ldt = _LDT(nr);
p->tss.es = __KERNEL_DS;
p->tss.cs = __KERNEL_CS;
p->tss.ss = __KERNEL_DS;
@@ -508,16 +519,8 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
childregs->eax = 0;
childregs->esp = esp;
p->tss.back_link = 0;
- if (p->ldt) {
- p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
- if (p->ldt != NULL)
- memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
- }
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
- if (p->ldt)
- set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512);
- else
- set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);
+
/*
* a bitmap offset pointing outside of the TSS limit causes a nicely
* controllable SIGSEGV. The first sys_ioperm() call sets up the
@@ -583,8 +586,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
dump->regs.eax = regs->eax;
dump->regs.ds = regs->xds;
dump->regs.es = regs->xes;
- __asm__("mov %%fs,%0":"=r" (dump->regs.fs));
- __asm__("mov %%gs,%0":"=r" (dump->regs.gs));
+ __asm__("movl %%fs,%0":"=r" (dump->regs.fs));
+ __asm__("movl %%gs,%0":"=r" (dump->regs.gs));
dump->regs.orig_eax = regs->orig_eax;
dump->regs.eip = regs->eip;
dump->regs.cs = regs->xcs;
diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c
index 12a777b5c..c17c13590 100644
--- a/arch/i386/kernel/signal.c
+++ b/arch/i386/kernel/signal.c
@@ -199,7 +199,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
&& (tmp & 0x4) != 0x4 /* not a LDT selector */ \
&& (tmp & 3) != 3) /* not a RPL3 GDT selector */ \
goto badframe; \
- __asm__ __volatile__("mov %w0,%%" #seg : : "r"(tmp)); }
+ __asm__ __volatile__("movl %w0,%%" #seg : : "r"(tmp)); }
GET_SEG(gs);
GET_SEG(fs);
@@ -337,9 +337,9 @@ setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate,
unsigned int tmp;
tmp = 0;
- __asm__("mov %%gs,%w0" : "=r"(tmp): "0"(tmp));
+ __asm__("movl %%gs,%w0" : "=r"(tmp): "0"(tmp));
__put_user(tmp, (unsigned int *)&sc->gs);
- __asm__("mov %%fs,%w0" : "=r"(tmp): "0"(tmp));
+ __asm__("movl %%fs,%w0" : "=r"(tmp): "0"(tmp));
__put_user(tmp, (unsigned int *)&sc->fs);
__put_user(regs->xes, (unsigned int *)&sc->es);
@@ -427,7 +427,7 @@ static void setup_frame(int sig, struct k_sigaction *ka,
regs->eip = (unsigned long) ka->sa.sa_handler;
{
unsigned long seg = __USER_DS;
- __asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
+ __asm__("movl %w0,%%fs ; movl %w0,%%gs": "=r"(seg) : "0"(seg));
set_fs(USER_DS);
regs->xds = seg;
regs->xes = seg;
@@ -492,7 +492,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->eip = (unsigned long) ka->sa.sa_handler;
{
unsigned long seg = __USER_DS;
- __asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
+ __asm__("movl %w0,%%fs ; movl %w0,%%gs": "=r"(seg) : "0"(seg));
set_fs(USER_DS);
regs->xds = seg;
regs->xes = seg;
diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c
index 9ca377128..0793410a6 100644
--- a/arch/i386/kernel/smp.c
+++ b/arch/i386/kernel/smp.c
@@ -28,6 +28,7 @@
* Alan Cox : Added EBDA scanning
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
@@ -47,6 +48,10 @@
#include <asm/smp.h>
#include <asm/io.h>
+#ifdef CONFIG_MTRR
+# include <asm/mtrr.h>
+#endif
+
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
@@ -128,9 +133,6 @@ unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */
static int smp_activated = 0; /* Tripped once we need to start cross invalidating */
int apic_version[NR_CPUS]; /* APIC version number */
static volatile int smp_commenced=0; /* Tripped when we start scheduling */
-unsigned long apic_addr = 0xFEE00000; /* Address of APIC (defaults to 0xFEE00000) */
-unsigned long nlong = 0; /* dummy used for apic_reg address + 0x20 */
-unsigned char *apic_reg=((unsigned char *)(&nlong))-0x20;/* Later set to the ioremap() of the APIC */
unsigned long apic_retval; /* Just debugging the assembler.. */
static volatile unsigned char smp_cpu_in_msg[NR_CPUS]; /* True if this processor is sending an IPI */
@@ -150,8 +152,10 @@ const char lk_lockmsg[] = "lock from interrupt context at %p\n";
int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, };
extern int mp_irq_entries;
extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES];
+extern int mpc_default_type;
int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, };
int mp_current_pci_id = 0;
+unsigned long mp_lapic_addr = 0;
/* #define SMP_DEBUG */
@@ -272,8 +276,8 @@ __initfunc(static int smp_read_mpc(struct mp_config_table *mpc))
printk("APIC at: 0x%lX\n",mpc->mpc_lapic);
- /* set the local APIC address */
- apic_addr = (unsigned long)phys_to_virt((unsigned long)mpc->mpc_lapic);
+ /* save the local APIC address, it might be non-default */
+ mp_lapic_addr = mpc->mpc_lapic;
/*
* Now process the configuration blocks.
@@ -454,7 +458,7 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length))
*/
cfg=pg0[0];
- pg0[0] = (apic_addr | 7);
+ pg0[0] = (mp_lapic_addr | 7);
local_flush_tlb();
boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID));
@@ -477,6 +481,14 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length))
cpu_present_map=3;
num_processors=2;
printk("I/O APIC at 0xFEC00000.\n");
+
+ /*
+ * Save the default type number, we
+ * need it later to set the IO-APIC
+ * up properly:
+ */
+ mpc_default_type = mpf->mpf_feature1;
+
printk("Bus #0 is ");
}
switch(mpf->mpf_feature1)
@@ -525,11 +537,6 @@ __initfunc(int smp_scan_config(unsigned long base, unsigned long length))
if(mpf->mpf_physptr)
smp_read_mpc((void *)mpf->mpf_physptr);
- /*
- * Now that the boot CPU id is known,
- * set some other information about it.
- */
- nlong = boot_cpu_id<<24; /* Dummy 'self' for bootup */
__cpu_logical_map[0] = boot_cpu_id;
global_irq_holder = boot_cpu_id;
current->processor = boot_cpu_id;
@@ -667,6 +674,10 @@ extern int cpu_idle(void * unused);
*/
__initfunc(int start_secondary(void *unused))
{
+#ifdef CONFIG_MTRR
+ /* Must be done before calibration delay is computed */
+ mtrr_init_secondary_cpu ();
+#endif
smp_callin();
while (!smp_commenced)
barrier();
@@ -727,7 +738,7 @@ __initfunc(static void do_boot_cpu(int i))
/* start_eip had better be page-aligned! */
start_eip = setup_trampoline();
- printk("Booting processor %d eip %lx: ", i, start_eip); /* So we see what's up */
+ printk("Booting processor %d eip %lx\n", i, start_eip); /* So we see what's up */
stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle);
/*
@@ -906,6 +917,10 @@ __initfunc(void smp_boot_cpus(void))
int i;
unsigned long cfg;
+#ifdef CONFIG_MTRR
+ /* Must be done before other processors booted */
+ mtrr_init_boot_cpu ();
+#endif
/*
* Initialize the logical to physical cpu number mapping
* and the per-CPU profiling counter/multiplier
@@ -938,7 +953,7 @@ __initfunc(void smp_boot_cpus(void))
{
printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n");
io_apic_irqs = 0;
- return;
+ goto smp_done;
}
/*
@@ -951,15 +966,6 @@ __initfunc(void smp_boot_cpus(void))
printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
}
- /*
- * Map the local APIC into kernel space
- */
-
- apic_reg = ioremap(apic_addr,4096);
-
- if(apic_reg == NULL)
- panic("Unable to map local apic.");
-
#ifdef SMP_DEBUG
{
int reg;
@@ -1106,6 +1112,12 @@ __initfunc(void smp_boot_cpus(void))
* go and set it up:
*/
setup_IO_APIC();
+
+smp_done:
+#ifdef CONFIG_MTRR
+ /* Must be done after other processors booted */
+ mtrr_init ();
+#endif
}
@@ -1196,6 +1208,10 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait)
irq = 0x40;
break;
+ case MSG_MTRR_CHANGE:
+ irq = 0x50;
+ break;
+
default:
printk("Unknown SMP message %d\n", msg);
return;
@@ -1494,10 +1510,18 @@ asmlinkage void smp_stop_cpu_interrupt(void)
for (;;) ;
}
+void (*mtrr_hook) (void) = NULL;
+
+asmlinkage void smp_mtrr_interrupt(void)
+{
+ ack_APIC_irq ();
+ if (mtrr_hook) (*mtrr_hook) ();
+}
+
/*
* This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts
* per second. We assume that the caller has already set up the local
- * APIC at apic_addr.
+ * APIC.
*
* The APIC timer is not exactly sync with the external timer chip, it
* closely follows bus clocks.
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index fdcf951f3..754e9371c 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -68,19 +68,19 @@ out: \
#define get_seg_byte(seg,addr) ({ \
register unsigned char __res; \
-__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
+__asm__("pushl %%fs;movl %%ax,%%fs;movb %%fs:%2,%%al;popl %%fs" \
:"=a" (__res):"0" (seg),"m" (*(addr))); \
__res;})
#define get_seg_long(seg,addr) ({ \
register unsigned long __res; \
-__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \
+__asm__("pushl %%fs;movl %%ax,%%fs;movl %%fs:%2,%%eax;popl %%fs" \
:"=a" (__res):"0" (seg),"m" (*(addr))); \
__res;})
#define _fs() ({ \
register unsigned short __res; \
-__asm__("mov %%fs,%%ax":"=a" (__res):); \
+__asm__("movl %%fs,%%ax":"=a" (__res):); \
__res;})
void page_exception(void);
diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c
index 5ae87b06a..db7da10fc 100644
--- a/arch/i386/kernel/vm86.c
+++ b/arch/i386/kernel/vm86.c
@@ -255,7 +255,7 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
mark_screen_rdonly(tsk);
unlock_kernel();
__asm__ __volatile__(
- "xorl %%eax,%%eax; mov %%ax,%%fs; mov %%ax,%%gs\n\t"
+ "xorl %%eax,%%eax; movl %%ax,%%fs; movl %%ax,%%gs\n\t"
"movl %0,%%esp\n\t"
"jmp ret_from_sys_call"
: /* no outputs */
diff --git a/arch/i386/lib/checksum.c b/arch/i386/lib/checksum.c
index 88f250d62..c246421a9 100644
--- a/arch/i386/lib/checksum.c
+++ b/arch/i386/lib/checksum.c
@@ -123,6 +123,8 @@ unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
unsigned int csum_partial_copy_generic (const char *src, char *dst,
int len, int sum, int *src_err_ptr, int *dst_err_ptr)
{
+ __u32 tmp_var;
+
__asm__ __volatile__ ( "
testl $2, %%edi # Check alignment.
jz 2f # Jump if alignment is ok.
@@ -137,7 +139,7 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst,
addw %%bx, %%ax
adcl $0, %%eax
2:
- pushl %%ecx
+ movl %%ecx, %8
shrl $5, %%ecx
jz 2f
testl %%esi, %%esi
@@ -174,7 +176,7 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst,
dec %%ecx
jne 1b
adcl $0, %%eax
- 2: popl %%edx
+ 2: movl %8, %%edx
movl %%edx, %%ecx
andl $0x1c, %%edx
je 4f
@@ -231,9 +233,10 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst,
################################################
"
- : "=a" (sum), "=m" (src_err_ptr), "=m" (dst_err_ptr)
- : "0" (sum), "c" (len), "S" (src), "D" (dst),
- "i" (-EFAULT)
+ : "=a" (sum)
+ : "m" (src_err_ptr), "m" (dst_err_ptr),
+ "0" (sum), "c" (len), "S" (src), "D" (dst),
+ "i" (-EFAULT), "m"(tmp_var)
: "bx", "cx", "dx", "si", "di" );
return(sum);
diff --git a/arch/i386/math-emu/fpu_system.h b/arch/i386/math-emu/fpu_system.h
index 42303f679..1571b2f38 100644
--- a/arch/i386/math-emu/fpu_system.h
+++ b/arch/i386/math-emu/fpu_system.h
@@ -20,7 +20,7 @@
of the stack frame of math_emulate() */
#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg
-#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3])
+#define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->segments)[(s) >> 3])
#define SEG_D_SIZE(x) ((x).b & (3 << 21))
#define SEG_G_BIT(x) ((x).b & (1 << 23))
#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1)
diff --git a/arch/i386/math-emu/poly.h b/arch/i386/math-emu/poly.h
index 397cb9e3e..37ddfa0ef 100644
--- a/arch/i386/math-emu/poly.h
+++ b/arch/i386/math-emu/poly.h
@@ -83,7 +83,7 @@ extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2)
/* Note: the constraints in the asm statement didn't always work properly
with gcc 2.5.8. Changing from using edi to using ecx got around the
problem, but keep fingers crossed! */
-extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
+extern inline void add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
{
asm volatile ("movl %2,%%ecx; movl %3,%%esi;
movl (%%esi),%%eax; addl %%eax,(%%ecx);
diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c
index ef3ac57f4..c33c53b9a 100644
--- a/arch/i386/mm/init.c
+++ b/arch/i386/mm/init.c
@@ -90,7 +90,6 @@ void show_mem(void)
shared += atomic_read(&mem_map[i].count) - 1;
}
printk("%d pages of RAM\n",total);
- printk("%d free pages\n",free);
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
printk("%d pages swap cached\n",cached);
@@ -174,29 +173,29 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
* It may also hold the MP configuration table when we are booting SMP.
*/
#ifdef __SMP__
- if (!smp_scan_config(0x0,0x400)) /* Scan the bottom 1K for a signature */
- {
+ /*
+ * FIXME: Linux assumes you have 640K of base ram..
+ * this continues the error...
+ *
+ * 1) Scan the bottom 1K for a signature
+ * 2) Scan the top 1K of base RAM
+ * 3) Scan the 64K of bios
+ */
+ if (!smp_scan_config(0x0,0x400) &&
+ !smp_scan_config(639*0x400,0x400) &&
+ !smp_scan_config(0xF0000,0x10000)) {
/*
- * FIXME: Linux assumes you have 640K of base ram.. this continues
- * the error...
+ * If it is an SMP machine we should know now, unless the
+ * configuration is in an EISA/MCA bus machine with an
+ * extended bios data area.
+ *
+ * there is a real-mode segmented pointer pointing to the
+ * 4K EBDA area at 0x40E, calculate and scan it here:
*/
- if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */
- {
- if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */
- {
- /*
- * If it is an SMP machine we should know now, unless the configuration
- * is in an EISA/MCA bus machine with an extended bios data area.
- */
-
- address = *(unsigned short *)phys_to_virt(0x40E); /* EBDA */
- address<<=4; /* Real mode segments to physical */
- smp_scan_config(address, 0x1000); /* Scan the EBDA */
- }
- }
+ address = *(unsigned short *)phys_to_virt(0x40E);
+ address<<=4;
+ smp_scan_config(address, 0x1000);
}
-
-/* smp_alloc_memory(8192); */
#endif
start_mem = PAGE_ALIGN(start_mem);
address = PAGE_OFFSET;
@@ -255,7 +254,52 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_
address += PAGE_SIZE;
}
}
+#ifdef __SMP__
+{
+ extern unsigned long mp_lapic_addr;
+ pte_t pte;
+ unsigned long apic_area = (unsigned long)APIC_BASE;
+
+ pg_dir = swapper_pg_dir + ((apic_area) >> PGDIR_SHIFT);
+ memset((void *)start_mem, 0, PAGE_SIZE);
+ pgd_val(*pg_dir) = _PAGE_TABLE | __pa(start_mem);
+ start_mem += PAGE_SIZE;
+
+ if (smp_found_config) {
+ /*
+ * Map the local APIC to FEE00000. (it's only the default
+ * value, thanks to Steve Hsieh for finding this out. We
+ * now save the real local-APIC physical address in smp_scan(),
+ * and use it here)
+ */
+ pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
+ pte = mk_pte(__va(mp_lapic_addr), PAGE_KERNEL);
+ set_pte(pg_table, pte);
+
+ /*
+ * Map the IO-APIC to FEC00000.
+ */
+ apic_area = 0xFEC00000; /*(unsigned long)IO_APIC_BASE;*/
+ pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
+ pte = mk_pte(__va(apic_area), PAGE_KERNEL);
+ set_pte(pg_table, pte);
+ } else {
+ /*
+ * No local APIC but we are compiled SMP ... set up a
+ * fake all zeroes page to simulate the local APIC.
+ */
+ pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
+ pte = mk_pte(start_mem, PAGE_KERNEL);
+ memset((void *)start_mem, 0, PAGE_SIZE);
+ start_mem += PAGE_SIZE;
+ set_pte(pg_table, pte);
+ }
+
+ local_flush_tlb();
+}
+#endif
local_flush_tlb();
+
return free_area_init(start_mem, end_mem);
}
diff --git a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds
index 0284015ad..3812c81db 100644
--- a/arch/i386/vmlinux.lds
+++ b/arch/i386/vmlinux.lds
@@ -1,13 +1,12 @@
/* ld script to make i386 Linux kernel
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*/
-INCLUDE arch/i386/.kernel_offset.lds
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
- . = __kernel_offset__ + 0x100000;
+ . = 0xC0000000 + 0x100000;
_text = .; /* Text and read-only data */
.text : {
*(.text)
diff --git a/arch/m68k/amiga/amiga_ksyms.c b/arch/m68k/amiga/amiga_ksyms.c
index 9206b8e92..2b9c131d7 100644
--- a/arch/m68k/amiga/amiga_ksyms.c
+++ b/arch/m68k/amiga/amiga_ksyms.c
@@ -1,4 +1,3 @@
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/zorro.h>
diff --git a/arch/m68k/atari/atari_ksyms.c b/arch/m68k/atari/atari_ksyms.c
index bd22bf672..c54889370 100644
--- a/arch/m68k/atari/atari_ksyms.c
+++ b/arch/m68k/atari/atari_ksyms.c
@@ -1,4 +1,3 @@
-#include <linux/config.h>
#include <linux/module.h>
#include <asm/ptrace.h>
diff --git a/arch/m68k/config.in b/arch/m68k/config.in
index e6c984bcb..8ad01e60b 100644
--- a/arch/m68k/config.in
+++ b/arch/m68k/config.in
@@ -269,17 +269,6 @@ endmenu
fi
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
if [ "$CONFIG_VME" = "n" ]; then
diff --git a/arch/m68k/mac/ksyms.c b/arch/m68k/mac/ksyms.c
index c1cc4fcd0..05373b04e 100644
--- a/arch/m68k/mac/ksyms.c
+++ b/arch/m68k/mac/ksyms.c
@@ -1,4 +1,3 @@
-#include <linux/config.h>
#include <linux/module.h>
#include <asm/ptrace.h>
#include <asm/traps.h>
diff --git a/arch/mips/config.in b/arch/mips/config.in
index 171170f3b..02a256200 100644
--- a/arch/mips/config.in
+++ b/arch/mips/config.in
@@ -183,17 +183,6 @@ if [ "$CONFIG_SGI" != "y" ]; then
endmenu
fi
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
source fs/nls/Config.in
diff --git a/arch/mips/defconfig b/arch/mips/defconfig
index 8f4d81b1f..d7d10aa89 100644
--- a/arch/mips/defconfig
+++ b/arch/mips/defconfig
@@ -214,6 +214,7 @@ CONFIG_PCNET32=y
# AX.25 network device drivers
#
# CONFIG_MKISS is not set
+# CONFIG_6PACK is not set
# CONFIG_BPQETHER is not set
# CONFIG_DMASCC is not set
# CONFIG_SCC is not set
@@ -231,7 +232,6 @@ CONFIG_PCNET32=y
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
-CONFIG_CDROM=y
#
# Filesystems
diff --git a/arch/mips/jazz/hw-access.c b/arch/mips/jazz/hw-access.c
index c9c423a66..4d2caf21b 100644
--- a/arch/mips/jazz/hw-access.c
+++ b/arch/mips/jazz/hw-access.c
@@ -1,4 +1,4 @@
-/*
+/* $Id: hw-access.c,v 1.7 1998/05/06 02:46:43 ralf Exp $
* Low-level hardware access stuff for Jazz family machines.
*
* This file is subject to the terms and conditions of the GNU General Public
@@ -6,8 +6,6 @@
* for more details.
*
* Copyright (C) 1995, 1996, 1997 by Ralf Baechle
- *
- * $Id: hw-access.c,v 1.6 1998/03/04 08:29:09 ralf Exp $
*/
#include <linux/delay.h>
#include <linux/init.h>
@@ -154,6 +152,8 @@ struct feature jazz_feature = {
static volatile keyboard_hardware *jazz_kh =
(keyboard_hardware *) JAZZ_KEYBOARD_ADDRESS;
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+
static unsigned char jazz_read_input(void)
{
return jazz_kh->data;
@@ -161,11 +161,21 @@ static unsigned char jazz_read_input(void)
static void jazz_write_output(unsigned char val)
{
+ int status;
+
+ do {
+ status = jazz_kh->command;
+ } while (status & KBD_STAT_IBF);
jazz_kh->data = val;
}
static void jazz_write_command(unsigned char val)
{
+ int status;
+
+ do {
+ status = jazz_kh->command;
+ } while (status & KBD_STAT_IBF);
jazz_kh->command = val;
}
diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c
index 619aaabaa..cf2ab9fca 100644
--- a/arch/mips/kernel/irixelf.c
+++ b/arch/mips/kernel/irixelf.c
@@ -725,6 +725,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->mmap = NULL;
+ current->flags &= ~PF_FORKNOEXEC;
elf_entry = (unsigned int) elf_ex.e_entry;
/* Do this so that we can load the interpreter, if need be. We will
@@ -775,8 +776,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
if (current->binfmt && current->binfmt->module)
__MOD_INC_USE_COUNT(current->binfmt->module);
- current->suid = current->euid = current->fsuid = bprm->e_uid;
- current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
bprm->p = (unsigned long)
create_irix_tables((char *)bprm->p, bprm->argc, bprm->envc,
diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c
index 728af4cc9..9dcca6184 100644
--- a/arch/mips/kernel/irq.c
+++ b/arch/mips/kernel/irq.c
@@ -4,7 +4,7 @@
* Copyright (C) 1992 Linus Torvalds
* Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle
*
- * $Id: irq.c,v 1.8 1998/03/17 22:07:35 ralf Exp $
+ * $Id: irq.c,v 1.9 1998/03/22 23:27:12 ralf Exp $
*/
#include <linux/errno.h>
#include <linux/init.h>
@@ -310,7 +310,15 @@ int probe_irq_off (unsigned long irqs)
return i;
}
+int (*irq_cannonicalize)(int irq);
+
+static int 8259a_irq_cannonicalize(int irq)
+{
+ return ((irq == 2) ? 9 : irq);
+}
+
__initfunc(void init_IRQ(void))
{
+ irq_cannonicalize = 8259a_irq_cannonicalize;
irq_setup();
}
diff --git a/arch/mips/kernel/pci.c b/arch/mips/kernel/pci.c
index 6f7374fd2..71a57b73e 100644
--- a/arch/mips/kernel/pci.c
+++ b/arch/mips/kernel/pci.c
@@ -5,7 +5,6 @@
*
* MIPS implementation of PCI BIOS services for PCI support.
*/
-#include <linux/bios32.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -36,53 +35,6 @@ int pcibios_present (void)
}
/*
- * Given the vendor and device ids, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus,
- unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->vendor == vendor && dev->device == device_id) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-/*
- * Given the class, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->class == class_code) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-/*
* The functions below are machine specific and must be reimplented for
* each PCI chipset configuration. We just run the hook to the machine
* specific implementation.
@@ -129,4 +81,9 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
return pci_ops->pcibios_write_config_dword(bus, dev_fn, where, val);
}
+__initfunc(char *pcibios_setup(char *str))
+{
+ return str;
+}
+
#endif /* defined(CONFIG_PCI) */
diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h
index 33bec5be8..3a5c55540 100644
--- a/arch/mips/kernel/syscalls.h
+++ b/arch/mips/kernel/syscalls.h
@@ -7,7 +7,7 @@
*
* Copyright (C) 1995, 1996 by Ralf Baechle
*
- * $Id: syscalls.h,v 1.10 1997/12/16 05:34:38 ralf Exp $
+ * $Id: syscalls.h,v 1.11 1998/03/17 22:07:37 ralf Exp $
*/
/*
@@ -34,7 +34,7 @@ SYS(sys_chdir, 1)
SYS(sys_time, 1)
SYS(sys_mknod, 3)
SYS(sys_chmod, 2) /* 4015 */
-SYS(sys_chown, 3)
+SYS(sys_lchown, 3)
SYS(sys_ni_syscall, 0)
SYS(sys_stat, 2)
SYS(sys_lseek, 3)
@@ -220,4 +220,5 @@ SYS(sys_rt_sigqueueinfo, 3)
SYS(sys_rt_sigsuspend, 2)
SYS(sys_pread, 4) /* 4200 */
SYS(sys_pwrite, 4)
-SYS(sys_lchown, 3)
+SYS(sys_chown, 3)
+SYS(sys_getcwd, 2)
diff --git a/arch/mips/lib/csum_partial_copy.S b/arch/mips/lib/csum_partial_copy.S
index 62ee35395..d5b281574 100644
--- a/arch/mips/lib/csum_partial_copy.S
+++ b/arch/mips/lib/csum_partial_copy.S
@@ -1,13 +1,13 @@
-/*
+/* $Id: csum_partial_copy.S,v 1.5 1998/05/06 02:43:34 ralf Exp $
+ *
+ * Unified implementation of csum_copy_partial, csum_copy_partial_from_user
+ * and csum_copy_partial_nocheck.
+ *
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1998 Ralf Baechle
- *
- * $Id: csum_partial_copy.S,v 1.3 1998/05/01 06:54:07 ralf Exp $
- *
- * Unified implementation of csum_copy_partial and csum_copy_partial_from_user.
*/
#include <asm/asm.h>
#include <asm/offset.h>
@@ -179,6 +179,7 @@ small_csumcpy:
.align 5
LEAF(csum_partial_copy_from_user)
addu t5, src, a2 # end address for fixup
+EXPORT(csum_partial_copy_nocheck)
EXPORT(csum_partial_copy)
move sum, zero # clear computed sum
move t7, zero # clear odd flag
diff --git a/arch/mips/lib/ide-std.c b/arch/mips/lib/ide-std.c
index 47b103c03..e6bf4dc5f 100644
--- a/arch/mips/lib/ide-std.c
+++ b/arch/mips/lib/ide-std.c
@@ -20,6 +20,8 @@ static int std_ide_default_irq(ide_ioreg_t base)
case 0x170: return 15;
case 0x1e8: return 11;
case 0x168: return 10;
+ case 0x1e0: return 8;
+ case 0x160: return 12;
default:
return 0;
}
@@ -32,6 +34,8 @@ static ide_ioreg_t std_ide_default_io_base(int index)
case 1: return 0x170;
case 2: return 0x1e8;
case 3: return 0x168;
+ case 4: return 0x1e0;
+ case 5: return 0x160;
default:
return 0;
}
diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c
index 950744328..65340f786 100644
--- a/arch/mips/sgi/kernel/indy_int.c
+++ b/arch/mips/sgi/kernel/indy_int.c
@@ -4,7 +4,7 @@
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*
- * $Id: indy_int.c,v 1.6 1998/03/17 22:07:41 ralf Exp $
+ * $Id: indy_int.c,v 1.7 1998/04/05 11:23:58 ralf Exp $
*/
#include <linux/config.h>
#include <linux/init.h>
@@ -417,8 +417,16 @@ void free_irq(unsigned int irq, void *dev_id)
printk("Trying to free free IRQ%d\n",irq);
}
+int (*irq_cannonicalize)(int irq);
+
+static int indy_irq_cannonicalize(int irq)
+{
+ return irq; /* Sane hardware, sane code ... */
+}
+
__initfunc(void init_IRQ(void))
{
+ irq_cannonicalize = indy_irq_cannonicalize;
irq_setup();
}
diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c
index 6b2c1846e..5107edb6f 100644
--- a/arch/mips/sgi/kernel/setup.c
+++ b/arch/mips/sgi/kernel/setup.c
@@ -1,9 +1,8 @@
-/*
+/* $Id: setup.c,v 1.8 1998/05/06 02:46:46 ralf Exp $
+ *
* setup.c: SGI specific setup, including init of the feature struct.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
- *
- * $Id: setup.c,v 1.7 1998/03/04 08:47:27 ralf Exp $
*/
#include <linux/init.h>
#include <linux/kernel.h>
@@ -31,6 +30,8 @@ struct feature sgi_feature = {
static volatile struct hpc_keyb *sgi_kh = (struct hpc_keyb *) (KSEG1 + 0x1fbd9800 + 64);
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+
static unsigned char sgi_read_input(void)
{
return sgi_kh->data;
@@ -38,11 +39,21 @@ static unsigned char sgi_read_input(void)
static void sgi_write_output(unsigned char val)
{
+ int status;
+
+ do {
+ status = sgi_kh->command;
+ } while (status & KBD_STAT_IBF);
sgi_kh->data = val;
}
static void sgi_write_command(unsigned char val)
{
+ int status;
+
+ do {
+ status = sgi_kh->command;
+ } while (status & KBD_STAT_IBF);
sgi_kh->command = val;
}
diff --git a/arch/mips/sni/hw-access.c b/arch/mips/sni/hw-access.c
index 744518971..b32e7dcce 100644
--- a/arch/mips/sni/hw-access.c
+++ b/arch/mips/sni/hw-access.c
@@ -1,4 +1,5 @@
-/*
+/* $Id: hw-access.c,v 1.5 1998/05/06 02:46:46 ralf Exp $
+ *
* Low-level hardware access stuff for SNI RM200 PCI
*
* This file is subject to the terms and conditions of the GNU General Public
@@ -6,8 +7,6 @@
* for more details.
*
* Copyright (C) 1996, 1997, 1998 by Ralf Baechle
- *
- * $Id: hw-access.c,v 1.3 1997/12/01 17:57:39 ralf Exp $
*/
#include <linux/delay.h>
#include <linux/init.h>
@@ -163,6 +162,8 @@ struct feature sni_rm200_pci_feature = {
rtc_write_data
};
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+
static unsigned char sni_read_input(void)
{
return inb(KBD_DATA_REG);
@@ -170,11 +171,21 @@ static unsigned char sni_read_input(void)
static void sni_write_output(unsigned char val)
{
+ int status;
+
+ do {
+ status = inb(KBD_CNTL_REG);
+ } while (status & KBD_STAT_IBF);
outb(val, KBD_DATA_REG);
}
static void sni_write_command(unsigned char val)
{
+ int status;
+
+ do {
+ status = inb(KBD_CNTL_REG);
+ } while (status & KBD_STAT_IBF);
outb(val, KBD_CNTL_REG);
}
diff --git a/arch/mips/sni/pci.c b/arch/mips/sni/pci.c
index e2255b127..0e497450a 100644
--- a/arch/mips/sni/pci.c
+++ b/arch/mips/sni/pci.c
@@ -5,10 +5,9 @@
*
* SNI specific PCI support for RM200/RM300.
*
- * $Id: pci.c,v 1.3 1998/03/04 08:47:29 ralf Exp $
+ * $Id: pci.c,v 1.5 1998/05/04 01:15:23 ralf Exp $
*/
#include <linux/config.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <asm/byteorder.h>
diff --git a/arch/ppc/8xx_io/.cvsignore b/arch/ppc/8xx_io/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/arch/ppc/8xx_io/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/arch/ppc/8xx_io/Makefile b/arch/ppc/8xx_io/Makefile
new file mode 100644
index 000000000..b33919a5d
--- /dev/null
+++ b/arch/ppc/8xx_io/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux MPC8xx ppc-specific parts of comm processor
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := 8xx_io.a
+O_OBJS = commproc.o uart.o enet.o
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/ppc/8xx_io/commproc.c b/arch/ppc/8xx_io/commproc.c
new file mode 100644
index 000000000..7e95afd08
--- /dev/null
+++ b/arch/ppc/8xx_io/commproc.c
@@ -0,0 +1,222 @@
+
+/*
+ * General Purpose functions for the global management of the
+ * Communication Processor Module.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * In addition to the individual control of the communication
+ * channels, there are a few functions that globally affect the
+ * communication processor.
+ *
+ * Buffer descriptors must be allocated from the dual ported memory
+ * space. The allocator for that is here. When the communication
+ * process is reset, we reclaim the memory available. There is
+ * currently no deallocator for this memory.
+ * The amount of space available is platform dependent. On the
+ * MBX, the EPPC software loads additional microcode into the
+ * communication processor, and uses some of the DP ram for this
+ * purpose. Current, the first 512 bytes and the last 256 bytes of
+ * memory are used. Right now I am conservative and only use the
+ * memory that can never be used for microcode. If there are
+ * applications that require more DP ram, we can expand the boundaries
+ * but then we have to be careful of any downloaded microcode.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/mbx.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/8xx_immap.h>
+#include "commproc.h"
+
+static uint dp_alloc_base; /* Starting offset in DP ram */
+static uint dp_alloc_top; /* Max offset + 1 */
+static uint host_buffer; /* One page of host buffer */
+static uint host_end; /* end + 1 */
+cpm8xx_t *cpmp; /* Pointer to comm processor space */
+
+/* CPM interrupt vector functions.
+*/
+struct cpm_action {
+ void (*handler)(void *);
+ void *dev_id;
+};
+static struct cpm_action cpm_vecs[CPMVEC_NR];
+static void cpm_interrupt(int irq, void * dev, struct pt_regs * regs);
+static void cpm_error_interrupt(void *);
+
+void
+mbx_cpm_reset(uint host_page_addr)
+{
+ volatile immap_t *imp;
+ volatile cpm8xx_t *commproc;
+ pte_t *pte;
+
+ imp = (immap_t *)MBX_IMAP_ADDR;
+ commproc = (cpm8xx_t *)&imp->im_cpm;
+
+#ifdef notdef
+ /* We can't do this. It seems to blow away the microcode
+ * patch that EPPC-Bug loaded for us. EPPC-Bug uses SCC1 for
+ * Ethernet, SMC1 for the console, and I2C for serial EEPROM.
+ * Our own drivers quickly reset all of these.
+ */
+
+ /* Perform a reset.
+ */
+ commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
+
+ /* Wait for it.
+ */
+ while (commproc->cp_cpcr & CPM_CR_FLG);
+#endif
+
+ /* Set SDMA Bus Request priority 5.
+ */
+ imp->im_siu_conf.sc_sdcr = 1;
+
+ /* Reclaim the DP memory for our use.
+ */
+ dp_alloc_base = CPM_DATAONLY_BASE;
+ dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE;
+
+ /* Set the host page for allocation.
+ */
+ host_buffer = host_page_addr; /* Host virtual page address */
+ host_end = host_page_addr + PAGE_SIZE;
+ pte = va_to_pte(&init_task, host_page_addr);
+ pte_val(*pte) |= _PAGE_NO_CACHE;
+ flush_tlb_page(current->mm->mmap, host_buffer);
+
+ /* Tell everyone where the comm processor resides.
+ */
+ cpmp = (cpm8xx_t *)commproc;
+
+ /* Initialize the CPM interrupt controller.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr =
+ (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
+ ((CPM_INTERRUPT/2) << 13) | CICR_HP_MASK;
+ ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr = 0;
+
+ /* Set our interrupt handler with the core CPU.
+ */
+ if (request_irq(CPM_INTERRUPT, cpm_interrupt, 0, "cpm", NULL) != 0)
+ panic("Could not allocate CPM IRQ!");
+
+ /* Install our own error handler.
+ */
+ cpm_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL);
+
+ ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cicr |= CICR_IEN;
+}
+
+/* CPM interrupt controller interrupt.
+*/
+static void
+cpm_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+ uint vec;
+
+ /* Get the vector by setting the ACK bit and then reading
+ * the register.
+ */
+ ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr = 1;
+ vec = ((volatile immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_civr;
+ vec >>= 11;
+
+ if (cpm_vecs[vec].handler != 0)
+ (*cpm_vecs[vec].handler)(cpm_vecs[vec].dev_id);
+ else
+ ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr &= ~(1 << vec);
+
+ /* After servicing the interrupt, we have to remove the status
+ * indicator.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cisr |= (1 << vec);
+
+}
+
+/* The CPM can generate the error interrupt when there is a race condition
+ * between generating and masking interrupts. All we have to do is ACK it
+ * and return. This is a no-op function so we don't need any special
+ * tests in the interrupt handler.
+ */
+static void
+cpm_error_interrupt(void *dev)
+{
+}
+
+/* Install a CPM interrupt handler.
+*/
+void
+cpm_install_handler(int vec, void (*handler)(void *), void *dev_id)
+{
+ if (cpm_vecs[vec].handler != 0)
+ printk("CPM interrupt %x replacing %x\n",
+ (uint)handler, (uint)cpm_vecs[vec].handler);
+ cpm_vecs[vec].handler = handler;
+ cpm_vecs[vec].dev_id = dev_id;
+ ((immap_t *)MBX_IMAP_ADDR)->im_cpic.cpic_cimr |= (1 << vec);
+}
+
+/* Allocate some memory from the dual ported ram. We may want to
+ * enforce alignment restrictions, but right now everyone is a good
+ * citizen.
+ */
+uint
+mbx_cpm_dpalloc(uint size)
+{
+ uint retloc;
+
+ if ((dp_alloc_base + size) >= dp_alloc_top)
+ return(CPM_DP_NOSPACE);
+
+ retloc = dp_alloc_base;
+ dp_alloc_base += size;
+
+ return(retloc);
+}
+
+/* We also own one page of host buffer space for the allocation of
+ * UART "fifos" and the like.
+ */
+uint
+mbx_cpm_hostalloc(uint size)
+{
+ uint retloc;
+
+ if ((host_buffer + size) >= host_end)
+ return(0);
+
+ retloc = host_buffer;
+ host_buffer += size;
+
+ return(retloc);
+}
+
+/* Set a baud rate generator. This needs lots of work. There are
+ * four BRGs, any of which can be wired to any channel.
+ * The internal baud rate clock is the system clock divided by 16.
+ * I need to find a way to get this system clock frequency, which is
+ * part of the VPD.......
+ */
+#define BRG_INT_CLK (40000000/16)
+
+void
+mbx_cpm_setbrg(uint brg, uint rate)
+{
+ volatile uint *bp;
+
+ /* This is good enough to get SMCs running.....
+ */
+ bp = (uint *)&cpmp->cp_brgc1;
+ bp += brg;
+ *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN;
+}
diff --git a/arch/ppc/8xx_io/commproc.h b/arch/ppc/8xx_io/commproc.h
new file mode 100644
index 000000000..38046a31c
--- /dev/null
+++ b/arch/ppc/8xx_io/commproc.h
@@ -0,0 +1,464 @@
+
+/*
+ * MPC8xx Communication Processor Module.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * This file contains structures and information for the communication
+ * processor channels. Some CPM control and status is available
+ * throught the MPC8xx internal memory map. See immap.h for details.
+ * This file only contains what I need for the moment, not the total
+ * CPM capabilities. I (or someone else) will add definitions as they
+ * are needed. -- Dan
+ *
+ * On the MBX board, EPPC-Bug loads CPM microcode into the first 512
+ * bytes of the DP RAM and relocates the I2C parameter area to the
+ * IDMA1 space. The remaining DP RAM is available for buffer descriptors
+ * or other use.
+ */
+#ifndef __CPM_8XX__
+#define __CPM_8XX__
+
+#include <asm/8xx_immap.h>
+
+/* CPM Command register.
+*/
+#define CPM_CR_RST ((ushort)0x8000)
+#define CPM_CR_OPCODE ((ushort)0x0f00)
+#define CPM_CR_CHAN ((ushort)0x00f0)
+#define CPM_CR_FLG ((ushort)0x0001)
+
+/* Some commands (there are more...later)
+*/
+#define CPM_CR_INIT_TRX ((ushort)0x0000)
+#define CPM_CR_INIT_RX ((ushort)0x0001)
+#define CPM_CR_INIT_TX ((ushort)0x0002)
+#define CPM_CR_STOP_TX ((ushort)0x0004)
+#define CPM_CR_RESTART_TX ((ushort)0x0006)
+#define CPM_CR_SET_GADDR ((ushort)0x0008)
+
+/* Channel numbers.
+*/
+#define CPM_CR_CH_SCC1 ((ushort)0x0000)
+#define CPM_CR_CH_I2C ((ushort)0x0001) /* I2C and IDMA1 */
+#define CPM_CR_CH_SCC2 ((ushort)0x0004)
+#define CPM_CR_CH_SPI ((ushort)0x0005) /* SPI / IDMA2 / Timers */
+#define CPM_CR_CH_SCC3 ((ushort)0x0008)
+#define CPM_CR_CH_SMC1 ((ushort)0x0009) /* SMC1 / DSP1 */
+#define CPM_CR_CH_SCC4 ((ushort)0x000c)
+#define CPM_CR_CH_SMC2 ((ushort)0x000d) /* SMC2 / DSP2 */
+
+#define mk_cr_cmd(CH, CMD) ((CMD << 8) | (CH << 4))
+
+/* The dual ported RAM is multi-functional. Some areas can be (and are
+ * being) used for microcode. There is an area that can only be used
+ * as data ram for buffer descriptors, which is all we use right now.
+ * Currently the first 512 and last 256 bytes are used for microcode.
+ */
+#define CPM_DATAONLY_BASE ((uint)0x0800)
+#define CPM_DATAONLY_SIZE ((uint)0x0700)
+#define CPM_DP_NOSPACE ((uint)0x7fffffff)
+
+/* Export the base address of the communication processor registers
+ * and dual port ram.
+ */
+extern cpm8xx_t *cpmp; /* Pointer to comm processor */
+uint mbx_cpm_dpalloc(uint size);
+uint mbx_cpm_hostalloc(uint size);
+void mbx_cpm_setbrg(uint brg, uint rate);
+
+/* Buffer descriptors used by many of the CPM protocols.
+*/
+typedef struct cpm_buf_desc {
+ ushort cbd_sc; /* Status and Control */
+ ushort cbd_datlen; /* Data length in buffer */
+ uint cbd_bufaddr; /* Buffer address in host memory */
+} cbd_t;
+
+#define BD_SC_EMPTY ((ushort)0x8000) /* Recieve is empty */
+#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */
+#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */
+#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */
+#define BD_SC_CM ((ushort)0x0200) /* Continous mode */
+#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */
+#define BD_SC_P ((ushort)0x0100) /* xmt preamble */
+#define BD_SC_BR ((ushort)0x0020) /* Break received */
+#define BD_SC_FR ((ushort)0x0010) /* Framing error */
+#define BD_SC_PR ((ushort)0x0008) /* Parity error */
+#define BD_SC_OV ((ushort)0x0002) /* Overrun */
+#define BD_SC_CD ((ushort)0x0001) /* ?? */
+
+/* Define enough so I can at least use the MBX serial port as a UART.
+ * The MBX uses SMC1 as the host serial port.
+ */
+typedef struct smc_uart {
+ ushort smc_rbase; /* Rx Buffer descriptor base address */
+ ushort smc_tbase; /* Tx Buffer descriptor base address */
+ u_char smc_rfcr; /* Rx function code */
+ u_char smc_tfcr; /* Tx function code */
+ ushort smc_mrblr; /* Max receive buffer length */
+ uint smc_rstate; /* Internal */
+ uint smc_idp; /* Internal */
+ ushort smc_rbptr; /* Internal */
+ ushort smc_ibc; /* Internal */
+ uint smc_rxtmp; /* Internal */
+ uint smc_tstate; /* Internal */
+ uint smc_tdp; /* Internal */
+ ushort smc_tbptr; /* Internal */
+ ushort smc_tbc; /* Internal */
+ uint smc_txtmp; /* Internal */
+ ushort smc_maxidl; /* Maximum idle characters */
+ ushort smc_tmpidl; /* Temporary idle counter */
+ ushort smc_brklen; /* Last received break length */
+ ushort smc_brkec; /* rcv'd break condition counter */
+ ushort smc_brkcr; /* xmt break count register */
+ ushort smc_rmask; /* Temporary bit mask */
+} smc_uart_t;
+
+#define PROFF_SMC1 ((uint)0x0280) /* Offset in Parameter RAM */
+#define PROFF_SMC2 ((uint)0x0380)
+
+/* Function code bits.
+*/
+#define SMC_EB ((u_char)0x10) /* Set big endian byte order */
+
+/* SMC uart mode register.
+*/
+#define SMCMR_REN ((ushort)0x0001)
+#define SMCMR_TEN ((ushort)0x0002)
+#define SMCMR_DM ((ushort)0x000c)
+#define SMCMR_SM_GCI ((ushort)0x0000)
+#define SMCMR_SM_UART ((ushort)0x0020)
+#define SMCMR_SM_TRANS ((ushort)0x0030)
+#define SMCMR_SM_MASK ((ushort)0x0030)
+#define SMCMR_PM_EVEN ((ushort)0x0100) /* Even parity, else odd */
+#define SMCMR_PEN ((ushort)0x0200) /* Parity enable */
+#define SMCMR_SL ((ushort)0x0400) /* Two stops, else one */
+#define SMCR_CLEN_MASK ((ushort)0x7800) /* Character length */
+#define smcr_mk_clen(C) (((C) << 11) & SMCR_CLEN_MASK)
+
+/* SMC Event and Mask register.
+*/
+#define SMCM_TXE ((unsigned char)0x10)
+#define SMCM_BSY ((unsigned char)0x14)
+#define SMCM_TX ((unsigned char)0x02)
+#define SMCM_RX ((unsigned char)0x01)
+
+/* Baud rate generators.
+*/
+#define CPM_BRG_RST ((uint)0x00020000)
+#define CPM_BRG_EN ((uint)0x00010000)
+#define CPM_BRG_EXTC_INT ((uint)0x00000000)
+#define CPM_BRG_EXTC_CLK2 ((uint)0x00004000)
+#define CPM_BRG_EXTC_CLK6 ((uint)0x00008000)
+#define CPM_BRG_ATB ((uint)0x00002000)
+#define CPM_BRG_CD_MASK ((uint)0x00001ffe)
+#define CPM_BRG_DIV16 ((uint)0x00000001)
+
+/* SCCs.
+*/
+#define SCC_GSMRH_IRP ((uint)0x00040000)
+#define SCC_GSMRH_GDE ((uint)0x00010000)
+#define SCC_GSMRH_TCRC_CCITT ((uint)0x00008000)
+#define SCC_GSMRH_TCRC_BISYNC ((uint)0x00004000)
+#define SCC_GSMRH_TCRC_HDLC ((uint)0x00000000)
+#define SCC_GSMRH_REVD ((uint)0x00002000)
+#define SCC_GSMRH_TRX ((uint)0x00001000)
+#define SCC_GSMRH_TTX ((uint)0x00000800)
+#define SCC_GSMRH_CDP ((uint)0x00000400)
+#define SCC_GSMRH_CTSP ((uint)0x00000200)
+#define SCC_GSMRH_CDS ((uint)0x00000100)
+#define SCC_GSMRH_CTSS ((uint)0x00000080)
+#define SCC_GSMRH_TFL ((uint)0x00000040)
+#define SCC_GSMRH_RFW ((uint)0x00000020)
+#define SCC_GSMRH_TXSY ((uint)0x00000010)
+#define SCC_GSMRH_SYNL16 ((uint)0x0000000c)
+#define SCC_GSMRH_SYNL8 ((uint)0x00000008)
+#define SCC_GSMRH_SYNL4 ((uint)0x00000004)
+#define SCC_GSMRH_RTSM ((uint)0x00000002)
+#define SCC_GSMRH_RSYN ((uint)0x00000001)
+
+#define SCC_GSMRL_SIR ((uint)0x80000000) /* SCC2 only */
+#define SCC_GSMRL_EDGE_NONE ((uint)0x60000000)
+#define SCC_GSMRL_EDGE_NEG ((uint)0x40000000)
+#define SCC_GSMRL_EDGE_POS ((uint)0x20000000)
+#define SCC_GSMRL_EDGE_BOTH ((uint)0x00000000)
+#define SCC_GSMRL_TCI ((uint)0x10000000)
+#define SCC_GSMRL_TSNC_3 ((uint)0x0c000000)
+#define SCC_GSMRL_TSNC_4 ((uint)0x08000000)
+#define SCC_GSMRL_TSNC_14 ((uint)0x04000000)
+#define SCC_GSMRL_TSNC_INF ((uint)0x00000000)
+#define SCC_GSMRL_RINV ((uint)0x02000000)
+#define SCC_GSMRL_TINV ((uint)0x01000000)
+#define SCC_GSMRL_TPL_128 ((uint)0x00c00000)
+#define SCC_GSMRL_TPL_64 ((uint)0x00a00000)
+#define SCC_GSMRL_TPL_48 ((uint)0x00800000)
+#define SCC_GSMRL_TPL_32 ((uint)0x00600000)
+#define SCC_GSMRL_TPL_16 ((uint)0x00400000)
+#define SCC_GSMRL_TPL_8 ((uint)0x00200000)
+#define SCC_GSMRL_TPL_NONE ((uint)0x00000000)
+#define SCC_GSMRL_TPP_ALL1 ((uint)0x00180000)
+#define SCC_GSMRL_TPP_01 ((uint)0x00100000)
+#define SCC_GSMRL_TPP_10 ((uint)0x00080000)
+#define SCC_GSMRL_TPP_ZEROS ((uint)0x00000000)
+#define SCC_GSMRL_TEND ((uint)0x00040000)
+#define SCC_GSMRL_TDCR_32 ((uint)0x00030000)
+#define SCC_GSMRL_TDCR_16 ((uint)0x00020000)
+#define SCC_GSMRL_TDCR_8 ((uint)0x00010000)
+#define SCC_GSMRL_TDCR_1 ((uint)0x00000000)
+#define SCC_GSMRL_RDCR_32 ((uint)0x0000c000)
+#define SCC_GSMRL_RDCR_16 ((uint)0x00008000)
+#define SCC_GSMRL_RDCR_8 ((uint)0x00004000)
+#define SCC_GSMRL_RDCR_1 ((uint)0x00000000)
+#define SCC_GSMRL_RENC_DFMAN ((uint)0x00003000)
+#define SCC_GSMRL_RENC_MANCH ((uint)0x00002000)
+#define SCC_GSMRL_RENC_FM0 ((uint)0x00001000)
+#define SCC_GSMRL_RENC_NRZI ((uint)0x00000800)
+#define SCC_GSMRL_RENC_NRZ ((uint)0x00000000)
+#define SCC_GSMRL_TENC_DFMAN ((uint)0x00000600)
+#define SCC_GSMRL_TENC_MANCH ((uint)0x00000400)
+#define SCC_GSMRL_TENC_FM0 ((uint)0x00000200)
+#define SCC_GSMRL_TENC_NRZI ((uint)0x00000100)
+#define SCC_GSMRL_TENC_NRZ ((uint)0x00000000)
+#define SCC_GSMRL_DIAG_LE ((uint)0x000000c0) /* Loop and echo */
+#define SCC_GSMRL_DIAG_ECHO ((uint)0x00000080)
+#define SCC_GSMRL_DIAG_LOOP ((uint)0x00000040)
+#define SCC_GSMRL_DIAG_NORM ((uint)0x00000000)
+#define SCC_GSMRL_ENR ((uint)0x00000020)
+#define SCC_GSMRL_ENT ((uint)0x00000010)
+#define SCC_GSMRL_MODE_ENET ((uint)0x0000000c)
+#define SCC_GSMRL_MODE_DDCMP ((uint)0x00000009)
+#define SCC_GSMRL_MODE_BISYNC ((uint)0x00000008)
+#define SCC_GSMRL_MODE_V14 ((uint)0x00000007)
+#define SCC_GSMRL_MODE_AHDLC ((uint)0x00000006)
+#define SCC_GSMRL_MODE_PROFIBUS ((uint)0x00000005)
+#define SCC_GSMRL_MODE_UART ((uint)0x00000004)
+#define SCC_GSMRL_MODE_SS7 ((uint)0x00000003)
+#define SCC_GSMRL_MODE_ATALK ((uint)0x00000002)
+#define SCC_GSMRL_MODE_HDLC ((uint)0x00000000)
+
+#define SCC_TODR_TOD ((ushort)0x8000)
+
+typedef struct scc_param {
+ ushort scc_rbase; /* Rx Buffer descriptor base address */
+ ushort scc_tbase; /* Tx Buffer descriptor base address */
+ u_char scc_rfcr; /* Rx function code */
+ u_char scc_tfcr; /* Tx function code */
+ ushort scc_mrblr; /* Max receive buffer length */
+ uint scc_rstate; /* Internal */
+ uint scc_idp; /* Internal */
+ ushort scc_rbptr; /* Internal */
+ ushort scc_ibc; /* Internal */
+ uint scc_rxtmp; /* Internal */
+ uint scc_tstate; /* Internal */
+ uint scc_tdp; /* Internal */
+ ushort scc_tbptr; /* Internal */
+ ushort scc_tbc; /* Internal */
+ uint scc_txtmp; /* Internal */
+ uint scc_rcrc; /* Internal */
+ uint scc_tcrc; /* Internal */
+} sccp_t;
+
+/* Function code bits.
+*/
+#define SCC_EB ((u_char)0x10) /* Set big endian byte order */
+
+/* CPM Ethernet through SCC1.
+ */
+typedef struct scc_enet {
+ sccp_t sen_genscc;
+ uint sen_cpres; /* Preset CRC */
+ uint sen_cmask; /* Constant mask for CRC */
+ uint sen_crcec; /* CRC Error counter */
+ uint sen_alec; /* alignment error counter */
+ uint sen_disfc; /* discard frame counter */
+ ushort sen_pads; /* Tx short frame pad character */
+ ushort sen_retlim; /* Retry limit threshold */
+ ushort sen_retcnt; /* Retry limit counter */
+ ushort sen_maxflr; /* maximum frame length register */
+ ushort sen_minflr; /* minimum frame length register */
+ ushort sen_maxd1; /* maximum DMA1 length */
+ ushort sen_maxd2; /* maximum DMA2 length */
+ ushort sen_maxd; /* Rx max DMA */
+ ushort sen_dmacnt; /* Rx DMA counter */
+ ushort sen_maxb; /* Max BD byte count */
+ ushort sen_gaddr1; /* Group address filter */
+ ushort sen_gaddr2;
+ ushort sen_gaddr3;
+ ushort sen_gaddr4;
+ uint sen_tbuf0data0; /* Save area 0 - current frame */
+ uint sen_tbuf0data1; /* Save area 1 - current frame */
+ uint sen_tbuf0rba; /* Internal */
+ uint sen_tbuf0crc; /* Internal */
+ ushort sen_tbuf0bcnt; /* Internal */
+ ushort sen_paddrh; /* physical address (MSB) */
+ ushort sen_paddrm;
+ ushort sen_paddrl; /* physical address (LSB) */
+ ushort sen_pper; /* persistence */
+ ushort sen_rfbdptr; /* Rx first BD pointer */
+ ushort sen_tfbdptr; /* Tx first BD pointer */
+ ushort sen_tlbdptr; /* Tx last BD pointer */
+ uint sen_tbuf1data0; /* Save area 0 - current frame */
+ uint sen_tbuf1data1; /* Save area 1 - current frame */
+ uint sen_tbuf1rba; /* Internal */
+ uint sen_tbuf1crc; /* Internal */
+ ushort sen_tbuf1bcnt; /* Internal */
+ ushort sen_txlen; /* Tx Frame length counter */
+ ushort sen_iaddr1; /* Individual address filter */
+ ushort sen_iaddr2;
+ ushort sen_iaddr3;
+ ushort sen_iaddr4;
+ ushort sen_boffcnt; /* Backoff counter */
+
+ /* NOTE: Some versions of the manual have the following items
+ * incorrectly documented. Below is the proper order.
+ */
+ ushort sen_taddrh; /* temp address (MSB) */
+ ushort sen_taddrm;
+ ushort sen_taddrl; /* temp address (LSB) */
+} scc_enet_t;
+
+#define PROFF_SCC1 ((uint)0x0000) /* Offset in Parameter RAM */
+
+/* Bits in parallel I/O port registers that have to be set/cleared
+ * to configure the pins for SCC1 use. The TCLK and RCLK seem unique
+ * to the MBX860 board. Any two of the four available clocks could be
+ * used, and the MPC860 cookbook manual has an example using different
+ * clock pins.
+ */
+#define PA_ENET_RXD ((ushort)0x0001)
+#define PA_ENET_TXD ((ushort)0x0002)
+#define PA_ENET_TCLK ((ushort)0x0200)
+#define PA_ENET_RCLK ((ushort)0x0800)
+#define PC_ENET_TENA ((ushort)0x0001)
+#define PC_ENET_CLSN ((ushort)0x0010)
+#define PC_ENET_RENA ((ushort)0x0020)
+
+/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to
+ * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero.
+ */
+#define SICR_ENET_MASK ((uint)0x000000ff)
+#define SICR_ENET_CLKRT ((uint)0x0000003d)
+
+/* SCC Event register as used by Ethernet.
+*/
+#define SCCE_ENET_GRA ((ushort)0x0080) /* Graceful stop complete */
+#define SCCE_ENET_TXE ((ushort)0x0010) /* Transmit Error */
+#define SCCE_ENET_RXF ((ushort)0x0008) /* Full frame received */
+#define SCCE_ENET_BSY ((ushort)0x0004) /* All incoming buffers full */
+#define SCCE_ENET_TXB ((ushort)0x0002) /* A buffer was transmitted */
+#define SCCE_ENET_RXB ((ushort)0x0001) /* A buffer was received */
+
+/* SCC Mode Register (PMSR) as used by Ethernet.
+*/
+#define SCC_PMSR_HBC ((ushort)0x8000) /* Enable heartbeat */
+#define SCC_PMSR_FC ((ushort)0x4000) /* Force collision */
+#define SCC_PMSR_RSH ((ushort)0x2000) /* Receive short frames */
+#define SCC_PMSR_IAM ((ushort)0x1000) /* Check individual hash */
+#define SCC_PMSR_ENCRC ((ushort)0x0800) /* Ethernet CRC mode */
+#define SCC_PMSR_PRO ((ushort)0x0200) /* Promiscuous mode */
+#define SCC_PMSR_BRO ((ushort)0x0100) /* Catch broadcast pkts */
+#define SCC_PMSR_SBT ((ushort)0x0080) /* Special backoff timer */
+#define SCC_PMSR_LPB ((ushort)0x0040) /* Set Loopback mode */
+#define SCC_PMSR_SIP ((ushort)0x0020) /* Sample Input Pins */
+#define SCC_PMSR_LCW ((ushort)0x0010) /* Late collision window */
+#define SCC_PMSR_NIB22 ((ushort)0x000a) /* Start frame search */
+#define SCC_PMSR_FDE ((ushort)0x0001) /* Full duplex enable */
+
+/* Buffer descriptor control/status used by Ethernet receive.
+*/
+#define BD_ENET_RX_EMPTY ((ushort)0x8000)
+#define BD_ENET_RX_WRAP ((ushort)0x2000)
+#define BD_ENET_RX_INTR ((ushort)0x1000)
+#define BD_ENET_RX_LAST ((ushort)0x0800)
+#define BD_ENET_RX_FIRST ((ushort)0x0400)
+#define BD_ENET_RX_MISS ((ushort)0x0100)
+#define BD_ENET_RX_LG ((ushort)0x0020)
+#define BD_ENET_RX_NO ((ushort)0x0010)
+#define BD_ENET_RX_SH ((ushort)0x0008)
+#define BD_ENET_RX_CR ((ushort)0x0004)
+#define BD_ENET_RX_OV ((ushort)0x0002)
+#define BD_ENET_RX_CL ((ushort)0x0001)
+#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */
+
+/* Buffer descriptor control/status used by Ethernet transmit.
+*/
+#define BD_ENET_TX_READY ((ushort)0x8000)
+#define BD_ENET_TX_PAD ((ushort)0x4000)
+#define BD_ENET_TX_WRAP ((ushort)0x2000)
+#define BD_ENET_TX_INTR ((ushort)0x1000)
+#define BD_ENET_TX_LAST ((ushort)0x0800)
+#define BD_ENET_TX_TC ((ushort)0x0400)
+#define BD_ENET_TX_DEF ((ushort)0x0200)
+#define BD_ENET_TX_HB ((ushort)0x0100)
+#define BD_ENET_TX_LC ((ushort)0x0080)
+#define BD_ENET_TX_RL ((ushort)0x0040)
+#define BD_ENET_TX_RCMASK ((ushort)0x003c)
+#define BD_ENET_TX_UN ((ushort)0x0002)
+#define BD_ENET_TX_CSL ((ushort)0x0001)
+#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */
+
+/* SCC Event and Mask registers when it is used as a UART.
+*/
+#define UART_SCCM_GLR ((ushort)0x1000)
+#define UART_SCCM_GLT ((ushort)0x0800)
+#define UART_SCCM_AB ((ushort)0x0200)
+#define UART_SCCM_IDL ((ushort)0x0100)
+#define UART_SCCM_GRA ((ushort)0x0080)
+#define UART_SCCM_BRKE ((ushort)0x0040)
+#define UART_SCCM_BRKS ((ushort)0x0020)
+#define UART_SCCM_CCR ((ushort)0x0008)
+#define UART_SCCM_BSY ((ushort)0x0004)
+#define UART_SCCM_TX ((ushort)0x0002)
+#define UART_SCCM_RX ((ushort)0x0001)
+
+/* CPM interrupts. There are nearly 32 interrupts generated by CPM
+ * channels or devices. All of these are presented to the PPC core
+ * as a single interrupt. The CPM interrupt handler dispatches its
+ * own handlers, in a similar fashion to the PPC core handler. We
+ * use the table as defined in the manuals (i.e. no special high
+ * priority and SCC1 == SCCa, etc...).
+ */
+#define CPMVEC_NR 32
+#define CPMVEC_PIO_PC15 ((ushort)0x1f)
+#define CPMVEC_SCC1 ((ushort)0x1e)
+#define CPMVEC_SCC2 ((ushort)0x1d)
+#define CPMVEC_SCC3 ((ushort)0x1c)
+#define CPMVEC_SCC4 ((ushort)0x1b)
+#define CPMVEC_PIO_PC14 ((ushort)0x1a)
+#define CPMVEC_TIMER1 ((ushort)0x19)
+#define CPMVEC_PIO_PC13 ((ushort)0x18)
+#define CPMVEC_PIO_PC12 ((ushort)0x17)
+#define CPMVEC_SDMA_CB_ERR ((ushort)0x16)
+#define CPMVEC_IDMA1 ((ushort)0x15)
+#define CPMVEC_IDMA2 ((ushort)0x14)
+#define CPMVEC_TIMER2 ((ushort)0x12)
+#define CPMVEC_RISCTIMER ((ushort)0x11)
+#define CPMVEC_I2C ((ushort)0x10)
+#define CPMVEC_PIO_PC11 ((ushort)0x0f)
+#define CPMVEC_PIO_PC10 ((ushort)0x0e)
+#define CPMVEC_TIMER3 ((ushort)0x0c)
+#define CPMVEC_PIO_PC9 ((ushort)0x0b)
+#define CPMVEC_PIO_PC8 ((ushort)0x0a)
+#define CPMVEC_PIO_PC7 ((ushort)0x09)
+#define CPMVEC_TIMER4 ((ushort)0x07)
+#define CPMVEC_PIO_PC6 ((ushort)0x06)
+#define CPMVEC_SPI ((ushort)0x05)
+#define CPMVEC_SMC1 ((ushort)0x04)
+#define CPMVEC_SMC2 ((ushort)0x03)
+#define CPMVEC_PIO_PC5 ((ushort)0x02)
+#define CPMVEC_PIO_PC4 ((ushort)0x01)
+#define CPMVEC_ERROR ((ushort)0x00)
+
+extern void cpm_install_handler(int vec, void (*handler)(void *), void *dev_id);
+
+/* CPM interrupt configuration vector.
+*/
+#define CICR_SCD_SCC4 ((uint)0x00c00000) /* SCC4 @ SCCd */
+#define CICR_SCC_SCC3 ((uint)0x00200000) /* SCC3 @ SCCc */
+#define CICR_SCB_SCC2 ((uint)0x00040000) /* SCC2 @ SCCb */
+#define CICR_SCA_SCC1 ((uint)0x00000000) /* SCC1 @ SCCa */
+#define CICR_IRL_MASK ((uint)0x0000e000) /* Core interrrupt */
+#define CICR_HP_MASK ((uint)0x00001f00) /* Hi-pri int. */
+#define CICR_IEN ((uint)0x00000080) /* Int. enable */
+#define CICR_SPS ((uint)0x00000001) /* SCC Spread */
+#endif /* __CPM_8XX__ */
diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c
new file mode 100644
index 000000000..1c43ddce0
--- /dev/null
+++ b/arch/ppc/8xx_io/enet.c
@@ -0,0 +1,912 @@
+/*
+ * Ethernet driver for Motorola MPC8xx.
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * I copied the basic skeleton from the lance driver, because I did not
+ * know how to write the Linux driver, but I did know how the LANCE worked.
+ * This version of the driver is specific to the MBX implementation,
+ * since the board contains control registers external to the processor
+ * for the control of the MC68160 SIA/transceiver. The MPC860 manual
+ * describes connections using the internal parallel port I/O.
+ *
+ * The MBX860 uses the CPM SCC1 serial port for the Ethernet interface.
+ * Buffer descriptors are kept in the CPM dual port RAM, and the frame
+ * buffers are in the host memory.
+ *
+ * Right now, I am very watseful with the buffers. I allocate memory
+ * pages and then divide them into 2K frame buffers. This way I know I
+ * have buffers large enough to hold one frame within one buffer descriptor.
+ * Once I get this working, I will use 64 or 128 byte CPM buffers, which
+ * will be much more memory efficient and will easily handle lots of
+ * small packets.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/init.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mbx.h>
+#include "commproc.h"
+#include <linux/delay.h>
+
+/*
+ * Theory of Operation
+ *
+ * The MPC8xx CPM performs the Ethernet processing on SCC1. It can use
+ * an aribtrary number of buffers on byte boundaries, but must have at
+ * least two receive buffers to prevent constand overrun conditions.
+ *
+ * The buffer descriptors are allocated from the CPM dual port memory
+ * with the data buffers allocated from host memory, just like all other
+ * serial communication protocols. The host memory buffers are allocated
+ * from the free page pool, and then divided into smaller receive and
+ * transmit buffers. The size of the buffers should be a power of two,
+ * since that nicely divides the page. This creates a ring buffer
+ * structure similar to the LANCE and other controllers.
+ *
+ * Like the LANCE driver:
+ * The driver runs as two independent, single-threaded flows of control. One
+ * is the send-packet routine, which enforces single-threaded use by the
+ * dev->tbusy flag. The other thread is the interrupt handler, which is single
+ * threaded by the hardware and other software.
+ *
+ * The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+ * flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+ * queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+ * the 'lp->tx_full' flag.
+ *
+ * The MBX has a control register external to the MPC8xx that has some
+ * control of the Ethernet interface. Control Register 1 has the
+ * following format:
+ * bit 0 - Set to enable Ethernet transceiver
+ * bit 1 - Set to enable Ethernet internal loopback
+ * bit 2 - Set to auto select AUI or TP port
+ * bit 3 - if bit 2 is 0, set to select TP port
+ * bit 4 - Set to disable full duplex (loopback)
+ * bit 5 - Set to disable XCVR collision test
+ * bit 6, 7 - Used for RS-232 control.
+ *
+ * EPPC-Bug sets this register to 0x98 for normal Ethernet operation,
+ * so we should not have to touch it.
+ *
+ * The following I/O is used by the MBX implementation of the MPC8xx to
+ * the MC68160 transceiver. It DOES NOT exactly follow the cookbook
+ * example from the MPC860 manual.
+ * Port A, 15 - SCC1 Ethernet Rx
+ * Port A, 14 - SCC1 Ethernet Tx
+ * Port A, 6 (CLK2) - SCC1 Ethernet Tx Clk
+ * Port A, 4 (CLK4) - SCC1 Ethernet Rx Clk
+ * Port C, 15 - SCC1 Ethernet Tx Enable
+ * Port C, 11 - SCC1 Ethernet Collision
+ * Port C, 10 - SCC1 Ethernet Rx Enable
+ */
+
+/* The number of Tx and Rx buffers. These are allocated from the page
+ * pool. The code may assume these are power of two, so it it best
+ * to keep them that size.
+ * We don't need to allocate pages for the transmitter. We just use
+ * the skbuffer directly.
+ */
+#define CPM_ENET_RX_PAGES 4
+#define CPM_ENET_RX_FRSIZE 2048
+#define CPM_ENET_RX_FRPPG (PAGE_SIZE / CPM_ENET_RX_FRSIZE)
+#define RX_RING_SIZE (CPM_ENET_RX_FRPPG * CPM_ENET_RX_PAGES)
+#define TX_RING_SIZE 8 /* Must be power of two */
+#define TX_RING_MOD_MASK 7 /* for this to work */
+
+/* The CPM stores dest/src/type, data, and checksum for receive packets.
+ */
+#define PKT_MAXBUF_SIZE 1518
+#define PKT_MINBUF_SIZE 64
+#define PKT_MAXBLR_SIZE 1520
+
+/* The CPM buffer descriptors track the ring buffers. The rx_bd_base and
+ * tx_bd_base always point to the base of the buffer descriptors. The
+ * cur_rx and cur_tx point to the currently available buffer.
+ * The dirty_tx tracks the current buffer that is being sent by the
+ * controller. The cur_tx and dirty_tx are equal under both completely
+ * empty and completely full conditions. The empty/ready indicator in
+ * the buffer descriptor determines the actual condition.
+ */
+struct cpm_enet_private {
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ ushort skb_cur;
+ ushort skb_dirty;
+
+ /* CPM dual port RAM relative addresses.
+ */
+ cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */
+ cbd_t *tx_bd_base;
+ cbd_t *cur_rx, *cur_tx; /* The next free ring entry */
+ cbd_t *dirty_tx; /* The ring entries to be free()ed. */
+ scc_t *sccp;
+ struct net_device_stats stats;
+ char tx_full;
+ unsigned long lock;
+};
+
+static int cpm_enet_open(struct device *dev);
+static int cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev);
+static int cpm_enet_rx(struct device *dev);
+static void cpm_enet_interrupt(void *dev_id);
+static int cpm_enet_close(struct device *dev);
+static struct net_device_stats *cpm_enet_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/* GET THIS FROM THE VPD!!!!
+*/
+static ushort my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };
+
+/* Initialize the CPM Ethernet on SCC1. If EPPC-Bug loaded us, or performed
+ * some other network I/O, a whole bunch of this has already been set up.
+ * It is no big deal if we do it again, we just have to disable the
+ * transmit and receive to make sure we don't catch the CPM with some
+ * inconsistent control information.
+ */
+__initfunc(int cpm_enet_init(void))
+{
+ struct device *dev;
+ struct cpm_enet_private *cep;
+ int i, j;
+ unsigned char *eap;
+ unsigned long mem_addr;
+ pte_t *pte;
+ volatile cbd_t *bdp;
+ volatile cpm8xx_t *cp;
+ volatile scc_t *sccp;
+ volatile scc_enet_t *ep;
+ volatile immap_t *immap;
+
+ cp = cpmp; /* Get pointer to Communication Processor */
+
+ immap = (immap_t *)MBX_IMAP_ADDR; /* and to internal registers */
+
+ /* Allocate some private information.
+ */
+ cep = (struct cpm_enet_private *)kmalloc(sizeof(*cep), GFP_KERNEL);
+ memset(cep, 0, sizeof(*cep));
+
+ /* Create an Ethernet device instance.
+ */
+ dev = init_etherdev(0, 0);
+
+ /* Get pointer to SCC1 area in parameter RAM.
+ */
+ ep = (scc_enet_t *)(&cp->cp_dparam[PROFF_SCC1]);
+
+ /* And another to the SCC register area.
+ */
+ sccp = (volatile scc_t *)(&cp->cp_scc[0]);
+ cep->sccp = (scc_t *)sccp; /* Keep the pointer handy */
+
+ /* Disable receive and transmit in case EPPC-Bug started it.
+ */
+ sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+ /* Cookbook style from the MPC860 manual.....
+ * Not all of this is necessary if EPPC-Bug has initialized
+ * the network.
+ */
+
+ /* Configure port A pins for Txd and Rxd.
+ */
+ immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD);
+ immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD);
+ immap->im_ioport.iop_paodr &= ~PA_ENET_TXD;
+
+ /* Configure port C pins to enable CLSN and RENA.
+ */
+ immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA);
+ immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA);
+ immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA);
+
+ /* Configure port A for TCLK and RCLK.
+ */
+ immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK);
+ immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK);
+
+ /* Configure Serial Interface clock routing.
+ * First, clear all SCC1 bits to zero, then set the ones we want.
+ */
+ cp->cp_sicr &= ~SICR_ENET_MASK;
+ cp->cp_sicr |= SICR_ENET_CLKRT;
+
+ /* Manual says set SDDR, but I can't find anything with that
+ * name. I think it is a misprint, and should be SDCR. This
+ * has already been set by the communication processor initialization.
+ */
+
+ /* Allocate space for the buffer descriptors in the DP ram.
+ * These are relative offsets in the DP ram address space.
+ * Initialize base addresses for the buffer descriptors.
+ */
+ i = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE);
+ ep->sen_genscc.scc_rbase = i;
+ cep->rx_bd_base = (cbd_t *)&cp->cp_dpmem[i];
+
+ i = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE);
+ ep->sen_genscc.scc_tbase = i;
+ cep->tx_bd_base = (cbd_t *)&cp->cp_dpmem[i];
+
+ cep->dirty_tx = cep->cur_tx = cep->tx_bd_base;
+ cep->cur_rx = cep->rx_bd_base;
+
+ /* Issue init Rx BD command for SCC1.
+ * Manual says to perform an Init Rx parameters here. We have
+ * to perform both Rx and Tx because the SCC may have been
+ * already running.
+ * In addition, we have to do it later because we don't yet have
+ * all of the BD control/status set properly.
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_RX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+ */
+
+ /* Initialize function code registers for big-endian.
+ */
+ ep->sen_genscc.scc_rfcr = SCC_EB;
+ ep->sen_genscc.scc_tfcr = SCC_EB;
+
+ /* Set maximum bytes per receive buffer.
+ * This appears to be an Ethernet frame size, not the buffer
+ * fragment size. It must be a multiple of four.
+ */
+ ep->sen_genscc.scc_mrblr = PKT_MAXBLR_SIZE;
+
+ /* Set CRC preset and mask.
+ */
+ ep->sen_cpres = 0xffffffff;
+ ep->sen_cmask = 0xdebb20e3;
+
+ ep->sen_crcec = 0; /* CRC Error counter */
+ ep->sen_alec = 0; /* alignment error counter */
+ ep->sen_disfc = 0; /* discard frame counter */
+
+ ep->sen_pads = 0x8888; /* Tx short frame pad character */
+ ep->sen_retlim = 15; /* Retry limit threshold */
+
+ ep->sen_maxflr = PKT_MAXBUF_SIZE; /* maximum frame length register */
+ ep->sen_minflr = PKT_MINBUF_SIZE; /* minimum frame length register */
+
+ ep->sen_maxd1 = PKT_MAXBUF_SIZE; /* maximum DMA1 length */
+ ep->sen_maxd2 = PKT_MAXBUF_SIZE; /* maximum DMA2 length */
+
+ /* Clear hash tables.
+ */
+ ep->sen_gaddr1 = 0;
+ ep->sen_gaddr2 = 0;
+ ep->sen_gaddr3 = 0;
+ ep->sen_gaddr4 = 0;
+ ep->sen_iaddr1 = 0;
+ ep->sen_iaddr2 = 0;
+ ep->sen_iaddr3 = 0;
+ ep->sen_iaddr4 = 0;
+
+ /* Set Ethernet station address. This must come from the
+ * Vital Product Data (VPD) EEPROM.....as soon as I get the
+ * I2C interface working.....
+ *
+ * Since we performed a diskless boot, the Ethernet controller
+ * has been initialized and we copy the address out into our
+ * own structure.
+ */
+#ifdef notdef
+ ep->sen_paddrh = my_enet_addr[0];
+ ep->sen_paddrm = my_enet_addr[1];
+ ep->sen_paddrl = my_enet_addr[2];
+#else
+ eap = (unsigned char *)&(ep->sen_paddrh);
+ for (i=5; i>=0; i--)
+ dev->dev_addr[i] = *eap++;
+#endif
+
+ ep->sen_pper = 0; /* 'cause the book says so */
+ ep->sen_taddrl = 0; /* temp address (LSB) */
+ ep->sen_taddrm = 0;
+ ep->sen_taddrh = 0; /* temp address (MSB) */
+
+ /* Now allocate the host memory pages and initialize the
+ * buffer descriptors.
+ */
+ bdp = cep->tx_bd_base;
+ for (i=0; i<TX_RING_SIZE; i++) {
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ bdp->cbd_sc = 0;
+ bdp->cbd_bufaddr = 0;
+ bdp++;
+ }
+
+ /* Set the last buffer to wrap.
+ */
+ bdp--;
+ bdp->cbd_sc |= BD_SC_WRAP;
+
+ bdp = cep->rx_bd_base;
+ for (i=0; i<CPM_ENET_RX_PAGES; i++) {
+
+ /* Allocate a page.
+ */
+ mem_addr = __get_free_page(GFP_KERNEL);
+
+ /* Make it uncached.
+ */
+ pte = va_to_pte(&init_task, mem_addr);
+ pte_val(*pte) |= _PAGE_NO_CACHE;
+ flush_tlb_page(current->mm->mmap, mem_addr);
+
+ /* Initialize the BD for every fragment in the page.
+ */
+ for (j=0; j<CPM_ENET_RX_FRPPG; j++) {
+ bdp->cbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR;
+ bdp->cbd_bufaddr = __pa(mem_addr);
+ mem_addr += CPM_ENET_RX_FRSIZE;
+ bdp++;
+ }
+ }
+
+ /* Set the last buffer to wrap.
+ */
+ bdp--;
+ bdp->cbd_sc |= BD_SC_WRAP;
+
+ /* Let's re-initialize the channel now. We have to do it later
+ * than the manual describes because we have just now finished
+ * the BD initialization.
+ */
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+
+ cep->skb_cur = cep->skb_dirty = 0;
+
+ sccp->scc_scce = 0xffff; /* Clear any pending events */
+
+ /* Enable interrupts for transmit error, complete frame
+ * received, and any transmit buffer we have also set the
+ * interrupt flag.
+ */
+ sccp->scc_sccm = (SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB);
+
+ /* Install our interrupt handler.
+ */
+ cpm_install_handler(CPMVEC_SCC1, cpm_enet_interrupt, dev);
+
+ /* Set GSMR_H to enable all normal operating modes.
+ * Set GSMR_L to enable Ethernet to MC68160.
+ */
+ sccp->scc_gsmrh = 0;
+ sccp->scc_gsmrl = (SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 | SCC_GSMRL_MODE_ENET);
+
+ /* Set sync/delimiters.
+ */
+ sccp->scc_dsr = 0xd555;
+
+ /* Set processing mode. Use Ethernet CRC, catch broadcast, and
+ * start frame search 22 bit times after RENA.
+ */
+ sccp->scc_pmsr = (SCC_PMSR_ENCRC | SCC_PMSR_BRO | SCC_PMSR_NIB22);
+
+ /* It is now OK to enable the Ethernet transmitter.
+ */
+ immap->im_ioport.iop_pcpar |= PC_ENET_TENA;
+ immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA;
+
+ dev->base_addr = (unsigned long)ep;
+ dev->priv = cep;
+ dev->name = "CPM_ENET";
+
+ /* The CPM Ethernet specific entries in the device structure. */
+ dev->open = cpm_enet_open;
+ dev->hard_start_xmit = cpm_enet_start_xmit;
+ dev->stop = cpm_enet_close;
+ dev->get_stats = cpm_enet_get_stats;
+ dev->set_multicast_list = set_multicast_list;
+
+ /* And last, enable the transmit and receive processing.
+ */
+ sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+
+ printk("CPM ENET Version 0.1, ");
+ for (i=0; i<5; i++)
+ printk("%02x:", dev->dev_addr[i]);
+ printk("%02x\n", dev->dev_addr[5]);
+
+ return 0;
+}
+
+static int
+cpm_enet_open(struct device *dev)
+{
+
+ /* I should reset the ring buffers here, but I don't yet know
+ * a simple way to do that.
+ */
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ return 0; /* Always succeed */
+}
+
+static int
+cpm_enet_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv;
+ volatile cbd_t *bdp;
+ unsigned long flags;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 20)
+ return 1;
+ printk("%s: transmit timed out.\n", dev->name);
+ cep->stats.tx_errors++;
+#ifndef final_version
+ {
+ int i;
+ cbd_t *bdp;
+ printk(" Ring data dump: cur_tx %x%s cur_rx %x.\n",
+ cep->cur_tx, cep->tx_full ? " (full)" : "",
+ cep->cur_rx);
+ bdp = cep->tx_bd_base;
+ for (i = 0 ; i < TX_RING_SIZE; i++)
+ printk("%04x %04x %08x\n",
+ bdp->cbd_sc,
+ bdp->cbd_datlen,
+ bdp->cbd_bufaddr);
+ bdp = cep->rx_bd_base;
+ for (i = 0 ; i < RX_RING_SIZE; i++)
+ printk("%04x %04x %08x\n",
+ bdp->cbd_sc,
+ bdp->cbd_datlen,
+ bdp->cbd_bufaddr);
+ }
+#endif
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ if (test_and_set_bit(0, (void*)&cep->lock) != 0) {
+ printk("%s: tx queue lock!.\n", dev->name);
+ /* don't clear dev->tbusy flag. */
+ return 1;
+ }
+
+ /* Fill in a Tx ring entry */
+ bdp = cep->cur_tx;
+
+#ifndef final_version
+ if (bdp->cbd_sc & BD_ENET_TX_READY) {
+ /* Ooops. All transmit buffers are full. Bail out.
+ * This should not happen, since dev->tbusy should be set.
+ */
+ printk("%s: tx queue full!.\n", dev->name);
+ cep->lock = 0;
+ return 1;
+ }
+#endif
+
+ /* Clear all of the status flags.
+ */
+ bdp->cbd_sc &= ~BD_ENET_TX_STATS;
+
+ /* If the frame is short, tell CPM to pad it.
+ */
+ if (skb->len <= ETH_ZLEN)
+ bdp->cbd_sc |= BD_ENET_TX_PAD;
+ else
+ bdp->cbd_sc &= ~BD_ENET_TX_PAD;
+
+ /* Set buffer length and buffer pointer.
+ */
+ bdp->cbd_datlen = skb->len;
+ bdp->cbd_bufaddr = __pa(skb->data);
+
+ /* Save skb pointer.
+ */
+ cep->tx_skbuff[cep->skb_cur] = skb;
+
+ cep->stats.tx_bytes += skb->len;
+ cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK;
+
+ /* Push the data cache so the CPM does not get stale memory
+ * data.
+ */
+ /*flush_dcache_range(skb->data, skb->data + skb->len);*/
+
+ /* Send it on its way. Tell CPM its ready, interrupt when done,
+ * its the last BD of the frame, and to put the CRC on the end.
+ */
+ bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+
+ dev->trans_start = jiffies;
+
+ /* If this was the last BD in the ring, start at the beginning again.
+ */
+ if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+ bdp = cep->tx_bd_base;
+ else
+ bdp++;
+
+ save_flags(flags);
+ cli();
+ cep->lock = 0;
+ if (bdp->cbd_sc & BD_ENET_TX_READY)
+ cep->tx_full = 1;
+ else
+ dev->tbusy=0;
+ restore_flags(flags);
+
+ cep->cur_tx = (cbd_t *)bdp;
+
+ return 0;
+}
+
+/* The interrupt handler.
+ * This is called from the CPM handler, not the MPC core interrupt.
+ */
+static void
+cpm_enet_interrupt(void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct cpm_enet_private *cep;
+ volatile cbd_t *bdp;
+ ushort int_events;
+ int must_restart;
+
+ cep = (struct cpm_enet_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ /* Get the interrupt events that caused us to be here.
+ */
+ int_events = cep->sccp->scc_scce;
+ must_restart = 0;
+
+ /* Handle receive event in its own function.
+ */
+ if (int_events & SCCE_ENET_RXF)
+ cpm_enet_rx(dev_id);
+
+ /* Check for a transmit error. The manual is a little unclear
+ * about this, so the debug code until I get it figured out. It
+ * appears that if TXE is set, then TXB is not set. However,
+ * if carrier sense is lost during frame transmission, the TXE
+ * bit is set, "and continues the buffer transmission normally."
+ * I don't know if "normally" implies TXB is set when the buffer
+ * descriptor is closed.....trial and error :-).
+ */
+ if (int_events & SCCE_ENET_TXE) {
+
+ /* Transmission errors.
+ */
+ bdp = cep->dirty_tx;
+#ifndef final_version
+ printk("CPM ENET xmit error %x\n", bdp->cbd_sc);
+ if (bdp->cbd_sc & BD_ENET_TX_READY)
+ printk("HEY! Enet xmit interrupt and TX_READY.\n");
+#endif
+ if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */
+ cep->stats.tx_heartbeat_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */
+ cep->stats.tx_window_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */
+ cep->stats.tx_aborted_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */
+ cep->stats.tx_fifo_errors++;
+ if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */
+ cep->stats.tx_carrier_errors++;
+
+ cep->stats.tx_errors++;
+
+ /* No heartbeat or Lost carrier are not really bad errors.
+ * The others require a restart transmit command.
+ */
+ if (bdp->cbd_sc &
+ (BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN))
+ must_restart = 1;
+ }
+
+ /* Transmit OK, or non-fatal error. Update the buffer descriptors.
+ */
+ if (int_events & (SCCE_ENET_TXE | SCCE_ENET_TXB)) {
+ cep->stats.tx_packets++;
+ bdp = cep->dirty_tx;
+#ifndef final_version
+ if (bdp->cbd_sc & BD_ENET_TX_READY)
+ printk("HEY! Enet xmit interrupt and TX_READY.\n");
+#endif
+ /* Deferred means some collisions occurred during transmit,
+ * but we eventually sent the packet OK.
+ */
+ if (bdp->cbd_sc & BD_ENET_TX_DEF)
+ cep->stats.collisions++;
+
+ /* Free the sk buffer associated with this last transmit.
+ */
+ dev_kfree_skb(cep->tx_skbuff[cep->skb_dirty]/*, FREE_WRITE*/);
+ cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK;
+
+ /* Update pointer to next buffer descriptor to be transmitted.
+ */
+ if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+ bdp = cep->tx_bd_base;
+ else
+ bdp++;
+
+ /* I don't know if we can be held off from processing these
+ * interrupts for more than one frame time. I really hope
+ * not. In such a case, we would now want to check the
+ * currently available BD (cur_tx) and determine if any
+ * buffers between the dirty_tx and cur_tx have also been
+ * sent. We would want to process anything in between that
+ * does not have BD_ENET_TX_READY set.
+ */
+
+ /* Since we have freed up a buffer, the ring is no longer
+ * full.
+ */
+ if (cep->tx_full && dev->tbusy) {
+ cep->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ cep->dirty_tx = (cbd_t *)bdp;
+ }
+
+ if (must_restart) {
+ volatile cpm8xx_t *cp;
+
+ /* Some transmit errors cause the transmitter to shut
+ * down. We now issue a restart transmit. Since the
+ * errors close the BD and update the pointers, the restart
+ * _should_ pick up without having to reset any of our
+ * pointers either.
+ */
+ cp = cpmp;
+ cp->cp_cpcr =
+ mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_RESTART_TX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+ }
+
+ /* Check for receive busy, i.e. packets coming but no place to
+ * put them. This "can't happen" because the receive interrupt
+ * is tossing previous frames.
+ */
+ if (int_events & SCCE_ENET_BSY) {
+ cep->stats.rx_dropped++;
+ printk("CPM ENET: BSY can't happen.\n");
+ }
+
+ /* Write the SCC event register with the events we have handled
+ * to clear them. Maybe we should do this sooner?
+ */
+ cep->sccp->scc_scce = int_events;
+
+ dev->interrupt = 0;
+
+ return;
+}
+
+/* During a receive, the cur_rx points to the current incoming buffer.
+ * When we update through the ring, if the next incoming buffer has
+ * not been given to the system, we just set the empty indicator,
+ * effectively tossing the packet.
+ */
+static int
+cpm_enet_rx(struct device *dev)
+{
+ struct cpm_enet_private *cep;
+ volatile cbd_t *bdp;
+ struct sk_buff *skb;
+ ushort pkt_len;
+
+ cep = (struct cpm_enet_private *)dev->priv;
+
+ /* First, grab all of the stats for the incoming packet.
+ * These get messed up if we get called due to a busy condition.
+ */
+ bdp = cep->cur_rx;
+
+for (;;) {
+ if (bdp->cbd_sc & BD_ENET_RX_EMPTY)
+ break;
+
+#ifndef final_version
+ /* Since we have allocated space to hold a complete frame, both
+ * the first and last indicators should be set.
+ */
+ if ((bdp->cbd_sc & (BD_ENET_RX_FIRST | BD_ENET_RX_LAST)) !=
+ (BD_ENET_RX_FIRST | BD_ENET_RX_LAST))
+ printk("CPM ENET: rcv is not first+last\n");
+#endif
+
+ /* Frame too long or too short.
+ */
+ if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH))
+ cep->stats.rx_length_errors++;
+ if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */
+ cep->stats.rx_frame_errors++;
+ if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */
+ cep->stats.rx_crc_errors++;
+ if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */
+ cep->stats.rx_crc_errors++;
+
+ /* Report late collisions as a frame error.
+ * On this error, the BD is closed, but we don't know what we
+ * have in the buffer. So, just drop this frame on the floor.
+ */
+ if (bdp->cbd_sc & BD_ENET_RX_CL) {
+ cep->stats.rx_frame_errors++;
+ }
+ else {
+
+ /* Process the incoming frame.
+ */
+ cep->stats.rx_packets++;
+ pkt_len = bdp->cbd_datlen;
+ cep->stats.rx_bytes += pkt_len;
+
+ /* This does 16 byte alignment, much more than we need.
+ */
+ skb = dev_alloc_skb(pkt_len);
+
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ cep->stats.rx_dropped++;
+ }
+ else {
+ skb->dev = dev;
+ skb_put(skb,pkt_len); /* Make room */
+ eth_copy_and_sum(skb,
+ (unsigned char *)__va(bdp->cbd_bufaddr),
+ pkt_len, 0);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ }
+ }
+
+ /* Clear the status flags for this buffer.
+ */
+ bdp->cbd_sc &= ~BD_ENET_RX_STATS;
+
+ /* Mark the buffer empty.
+ */
+ bdp->cbd_sc |= BD_ENET_RX_EMPTY;
+
+ /* Update BD pointer to next entry.
+ */
+ if (bdp->cbd_sc & BD_ENET_RX_WRAP)
+ bdp = cep->rx_bd_base;
+ else
+ bdp++;
+
+ }
+ cep->cur_rx = (cbd_t *)bdp;
+
+ return 0;
+}
+
+static int
+cpm_enet_close(struct device *dev)
+{
+ /* Don't know what to do yet.
+ */
+
+ return 0;
+}
+
+static struct net_device_stats *cpm_enet_get_stats(struct device *dev)
+{
+ struct cpm_enet_private *cep = (struct cpm_enet_private *)dev->priv;
+
+ return &cep->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * Skeleton taken from sunlance driver.
+ * The CPM Ethernet implementation allows Multicast as well as individual
+ * MAC address filtering. Some of the drivers check to make sure it is
+ * a group multicast address, and discard those that are not. I guess I
+ * will do the same for now, but just remove the test if you want
+ * individual filtering as well (do the upper net layers want or support
+ * this kind of feature?).
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ struct cpm_enet_private *cep;
+ struct dev_mc_list *dmi;
+ u_char *mcptr, *tdptr;
+ volatile scc_enet_t *ep;
+ int i, j;
+
+ cep = (struct cpm_enet_private *)dev->priv;
+
+ /* Get pointer to SCC1 area in parameter RAM.
+ */
+ ep = (scc_enet_t *)dev->base_addr;
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ cep->sccp->scc_pmsr |= SCC_PMSR_PRO;
+ } else {
+
+ cep->sccp->scc_pmsr &= ~SCC_PMSR_PRO;
+
+ if (dev->flags & IFF_ALLMULTI) {
+ /* Catch all multicast addresses, so set the
+ * filter to all 1's.
+ */
+ ep->sen_gaddr1 = 0xffff;
+ ep->sen_gaddr2 = 0xffff;
+ ep->sen_gaddr3 = 0xffff;
+ ep->sen_gaddr4 = 0xffff;
+ }
+ else {
+ /* Clear filter and add the addresses in the list.
+ */
+ ep->sen_gaddr1 = 0;
+ ep->sen_gaddr2 = 0;
+ ep->sen_gaddr3 = 0;
+ ep->sen_gaddr4 = 0;
+
+ dmi = dev->mc_list;
+
+ for (i=0; i<dev->mc_count; i++) {
+
+ /* Only support group multicast for now.
+ */
+ if (!(dmi->dmi_addr[0] & 1))
+ continue;
+
+ /* The address in dmi_addr is LSB first,
+ * and taddr is MSB first. We have to
+ * copy bytes MSB first from dmi_addr.
+ */
+ mcptr = (u_char *)dmi->dmi_addr + 5;
+ tdptr = (u_char *)&ep->sen_taddrh;
+ for (j=0; j<6; j++)
+ *tdptr++ = *mcptr--;
+
+ /* Ask CPM to run CRC and set bit in
+ * filter mask.
+ */
+ cpmp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC1, CPM_CR_SET_GADDR) | CPM_CR_FLG;
+ while (cpmp->cp_cpcr & CPM_CR_FLG);
+ }
+ }
+ }
+}
diff --git a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c
new file mode 100644
index 000000000..a60eda695
--- /dev/null
+++ b/arch/ppc/8xx_io/uart.c
@@ -0,0 +1,2530 @@
+/*
+ * UART driver for MPC860 CPM SCC or SMC
+ * Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * I used the serial.c driver as the framework for this driver.
+ * Give credit to those guys.
+ * The original code was written for the MBX860 board. I tried to make
+ * it generic, but there may be some assumptions in the structures that
+ * have to be fixed later.
+ * To save porting time, I did not bother to change any object names
+ * that are not accessed outside of this file.
+ * It still needs lots of work........When it was easy, I included code
+ * to support the SCCs, but this has never been tested, nor is it complete.
+ * Only the SCCs support modem control, so that is not complete either.
+ *
+ * This module exports the following rs232 io functions:
+ *
+ * int rs_8xx_init(void);
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include "commproc.h"
+
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+
+/* this defines the index into rs_table for the port to use
+*/
+#ifndef CONFIG_SERIAL_CONSOLE_PORT
+#define CONFIG_SERIAL_CONSOLE_PORT 0
+#endif
+#endif
+
+#define TX_WAKEUP ASYNC_SHARE_IRQ
+
+static char *serial_name = "CPM UART driver";
+static char *serial_version = "0.01";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/*
+ * Serial driver configuration section. Here are the various options:
+ */
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+#define _INLINE_ inline
+
+#define DBG_CNT(s)
+
+/* We overload some of the items in the data structure to meet our
+ * needs. For example, the port address is the CPM parameter ram
+ * offset for the SCC or SMC. The maximum number of ports is 4 SCCs and
+ * 2 SMCs. The "hub6" field is used to indicate the channel number, with
+ * 0 and 1 indicating the SMCs and 2, 3, 4, and 5 are the SCCs.
+ * Since these ports are so versatile, I don't yet have a strategy for
+ * their management. For example, SCC1 is used for Ethernet. Right
+ * now, just don't put them in the table. Of course, right now I just
+ * want the SMC to work as a uart :-)..
+ * The "type" field is currently set to 0, for PORT_UNKNOWN. It is
+ * not currently used. I should probably use it to indicate the port
+ * type of CMS or SCC.
+ * The SMCs do not support any modem control signals.
+ */
+#define smc_scc_num hub6
+#define SCC_NUM_BASE 2
+
+static struct serial_state rs_table[] = {
+ /* UART CLK PORT IRQ FLAGS NUM */
+ { 0, 0, PROFF_SMC1, CPMVEC_SMC1, 0, 0 }, /* SMC1 ttyS0 */
+ { 0, 0, PROFF_SMC2, CPMVEC_SMC2, 0, 1 }, /* SMC1 ttyS0 */
+};
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+/* The number of buffer descriptors and their sizes.
+*/
+#define RX_NUM_FIFO 4
+#define RX_BUF_SIZE 32
+#define TX_NUM_FIFO 4
+#define TX_BUF_SIZE 32
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* The async_struct in serial.h does not really give us what we
+ * need, so define our own here.
+ */
+typedef struct serial_info {
+ int magic;
+ int flags;
+ struct serial_state *state;
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int line;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ unsigned long event;
+ unsigned long last_active;
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+
+ /* CPM Buffer Descriptor pointers.
+ */
+ cbd_t *rx_bd_base;
+ cbd_t *rx_cur;
+ cbd_t *tx_bd_base;
+ cbd_t *tx_cur;
+} ser_info_t;
+
+static void change_speed(ser_info_t *info);
+static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout);
+
+static inline int serial_paranoia_check(ser_info_t *info,
+ kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%s) in %s\n";
+ static const char *badinfo =
+ "Warning: null async_struct for (%s) in %s\n";
+
+ if (!info) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (info->magic != SERIAL_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts,
+ * indexed by the termio value. The generic CPM functions are responsible
+ * for setting and assigning baud rate generators for us.
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_8xx_stop(struct tty_struct *tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ int idx;
+ unsigned long flags;
+ volatile scc_t *sccp;
+ volatile smc_t *smcp;
+
+ if (serial_paranoia_check(info, tty->device, "rs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+ smcp->smc_smcm &= ~SMCM_TX;
+ }
+ else {
+ sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+ sccp->scc_sccm &= ~UART_SCCM_TX;
+ }
+ restore_flags(flags);
+}
+
+static void rs_8xx_start(struct tty_struct *tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ int idx;
+ unsigned long flags;
+ volatile scc_t *sccp;
+ volatile smc_t *smcp;
+
+ if (serial_paranoia_check(info, tty->device, "rs_stop"))
+ return;
+
+ save_flags(flags); cli();
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+ smcp->smc_smcm |= SMCM_TX;
+ }
+ else {
+ sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+ sccp->scc_sccm |= UART_SCCM_TX;
+ }
+ restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(ser_info_t *info,
+ int event)
+{
+ info->event |= 1 << event;
+ queue_task(&info->tqueue, &tq_serial);
+ mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(ser_info_t *info)
+{
+ struct tty_struct *tty = info->tty;
+ unsigned char ch, *cp;
+ int ignored = 0;
+ int i;
+ ushort status;
+ struct async_icount *icount;
+ volatile cbd_t *bdp;
+
+ icount = &info->state->icount;
+
+ /* Just loop through the closed BDs and copy the characters into
+ * the buffer.
+ */
+ bdp = info->rx_cur;
+ for (;;) {
+ if (bdp->cbd_sc & BD_SC_EMPTY) /* If this one is empty */
+ break; /* we are all done */
+
+ /* The read status mask tell us what we should do with
+ * incoming characters, especially if errors occur.
+ * One special case is the use of BD_SC_EMPTY. If
+ * this is not set, we are supposed to be ignoring
+ * inputs. In this case, just mark the buffer empty and
+ * continue.
+ if (!(info->read_status_mask & BD_SC_EMPTY)) {
+ bdp->cbd_sc |= BD_SC_EMPTY;
+ bdp->cbd_sc &=
+ ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
+
+ if (bdp->cbd_sc & BD_SC_WRAP)
+ bdp = info->rx_bd_base;
+ else
+ bdp++;
+ continue;
+ }
+ */
+
+ /* Get the number of characters and the buffer pointer.
+ */
+ i = bdp->cbd_datlen;
+ cp = (unsigned char *)__va(bdp->cbd_bufaddr);
+ status = bdp->cbd_sc;
+
+ /* Check to see if there is room in the tty buffer for
+ * the characters in our BD buffer. If not, we exit
+ * now, leaving the BD with the characters. We'll pick
+ * them up again on the next receive interrupt (which could
+ * be a timeout).
+ */
+ if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE)
+ break;
+
+ while (i-- > 0) {
+ ch = *cp++;
+ *tty->flip.char_buf_ptr = ch;
+ icount->rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, *status);
+#endif
+ *tty->flip.flag_buf_ptr = 0;
+ if (status & (BD_SC_BR | BD_SC_FR |
+ BD_SC_PR | BD_SC_OV)) {
+ /*
+ * For statistics only
+ */
+ if (status & BD_SC_BR)
+ icount->brk++;
+ else if (status & BD_SC_PR)
+ icount->parity++;
+ else if (status & BD_SC_FR)
+ icount->frame++;
+ if (status & BD_SC_OV)
+ icount->overrun++;
+
+ /*
+ * Now check to see if character should be
+ * ignored, and mask off conditions which
+ * should be ignored.
+ if (status & info->ignore_status_mask) {
+ if (++ignored > 100)
+ break;
+ continue;
+ }
+ */
+ status &= info->read_status_mask;
+
+ if (status & (BD_SC_BR)) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling break....");
+#endif
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ if (info->flags & ASYNC_SAK)
+ do_SAK(tty);
+ } else if (status & BD_SC_PR)
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ else if (status & BD_SC_FR)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ if (status & BD_SC_OV) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ *tty->flip.flag_buf_ptr =
+ TTY_OVERRUN;
+ }
+ }
+ }
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ break;
+
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ }
+
+ /* This BD is ready to be used again. Clear status.
+ * Get next BD.
+ */
+ bdp->cbd_sc |= BD_SC_EMPTY;
+ bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
+
+ if (bdp->cbd_sc & BD_SC_WRAP)
+ bdp = info->rx_bd_base;
+ else
+ bdp++;
+ }
+
+ info->rx_cur = (cbd_t *)bdp;
+
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static _INLINE_ void transmit_chars(ser_info_t *info)
+{
+
+ if (info->flags & TX_WAKEUP) {
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ }
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+}
+
+#ifdef notdef
+ /* I need to do this for the SCCs, so it is left as a reminder.
+ */
+static _INLINE_ void check_modem_status(struct async_struct *info)
+{
+ int status;
+ struct async_icount *icount;
+
+ status = serial_in(info, UART_MSR);
+
+ if (status & UART_MSR_ANY_DELTA) {
+ icount = &info->state->icount;
+ /* update input line counters */
+ if (status & UART_MSR_TERI)
+ icount->rng++;
+ if (status & UART_MSR_DDSR)
+ icount->dsr++;
+ if (status & UART_MSR_DDCD) {
+ icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+ if ((info->flags & ASYNC_HARDPPS_CD) &&
+ (status & UART_MSR_DCD))
+ hardpps();
+#endif
+ }
+ if (status & UART_MSR_DCTS)
+ icount->cts++;
+ wake_up_interruptible(&info->delta_msr_wait);
+ }
+
+ if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttys%d CD now %s...", info->line,
+ (status & UART_MSR_DCD) ? "on" : "off");
+#endif
+ if (status & UART_MSR_DCD)
+ wake_up_interruptible(&info->open_wait);
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("scheduling hangup...");
+#endif
+ queue_task(&info->tqueue_hangup,
+ &tq_scheduler);
+ }
+ }
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ info->IER |= UART_IER_THRI;
+ serial_out(info, UART_IER, info->IER);
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ return;
+ }
+ } else {
+ if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ info->IER &= ~UART_IER_THRI;
+ serial_out(info, UART_IER, info->IER);
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_8xx_interrupt(void *dev_id)
+{
+ u_char events;
+ int idx;
+ ser_info_t *info;
+ volatile smc_t *smcp;
+
+ info = (ser_info_t *)dev_id;
+
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+ }
+ else {
+ panic("SCC UART Interrupt....not ready");
+ }
+
+ events = smcp->smc_smce;
+#ifdef SERIAL_DEBUG_INTR
+ printk("rs_interrupt_single(%d, %x)...",
+ info->state->smc_scc_num, events);
+#endif
+ if (events & SMCM_RX)
+ receive_chars(info);
+ if (events & SMCM_TX)
+ transmit_chars(info);
+ smcp->smc_smce = events;
+#ifdef modem_control
+ check_modem_status(info);
+#endif
+ info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+ run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+ ser_info_t *info = (ser_info_t *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct async_struct *info = (struct async_struct *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+static void rs_8xx_timer(void)
+{
+ printk("rs_8xx_timer\n");
+}
+
+
+static int startup(ser_info_t *info)
+{
+ unsigned long flags;
+ int retval=0;
+ int idx;
+ struct serial_state *state= info->state;
+ volatile smc_t *smcp;
+ volatile scc_t *sccp;
+ volatile smc_uart_t *up;
+
+
+ save_flags(flags); cli();
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ goto errout;
+ }
+
+#ifdef maybe
+ if (!state->port || !state->type) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ goto errout;
+ }
+#endif
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttys%d (irq %d)...", info->line, state->irq);
+#endif
+
+
+#ifdef modem_control
+ info->MCR = 0;
+ if (info->tty->termios->c_cflag & CBAUD)
+ info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+#endif
+
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info);
+
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+
+ /* Enable interrupts and I/O.
+ */
+ smcp->smc_smcm |= (SMCM_RX | SMCM_TX);
+ smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN);
+
+ /* We can tune the buffer length and idle characters
+ * to take advantage of the entire incoming buffer size.
+ * If mrblr is something other than 1, maxidl has to be
+ * non-zero or we never get an interrupt. The maxidl
+ * is the number of character times we wait after reception
+ * of the last character before we decide no more characters
+ * are coming.
+ */
+ up = (smc_uart_t *)&cpmp->cp_dparam[state->port];
+ up->smc_mrblr = 1; /* receive buffer length */
+ up->smc_maxidl = 0; /* wait forever for next char */
+ up->smc_brkcr = 1; /* number of break chars */
+ }
+ else {
+ sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+ sccp->scc_sccm |= UART_SCCM_RX;
+ }
+
+ info->flags |= ASYNC_INITIALIZED;
+ restore_flags(flags);
+ return 0;
+
+errout:
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(ser_info_t * info)
+{
+ unsigned long flags;
+ struct serial_state *state;
+ int idx;
+ volatile smc_t *smcp;
+ volatile scc_t *sccp;
+
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+ state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....", info->line,
+ state->irq);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+
+ /* Disable interrupts and I/O.
+ */
+ smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
+#ifdef CONFIG_SERIAL_CONSOLE
+ /* We can't disable the transmitter if this is the
+ * system console.
+ */
+ if (idx != CONFIG_SERIAL_CONSOLE_PORT)
+#endif
+ smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+ }
+ else {
+ sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+ sccp->scc_sccm &= ~UART_SCCM_RX;
+ }
+
+
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(ser_info_t *info)
+{
+ int baud_rate;
+ unsigned cflag, cval, prev_mode;
+ int i, bits, idx;
+ unsigned long flags;
+ volatile smc_t *smcp;
+ volatile scc_t *sccp;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ /* Character length programmed into the mode register is the
+ * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
+ * 1 or 2 stop bits, minus 1.
+ * The value 'bits' counts this for us.
+ */
+ cval = 0;
+
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5: bits = 5; break;
+ case CS6: bits = 6; break;
+ case CS7: bits = 7; break;
+ case CS8: bits = 8; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: bits = 8; break;
+ }
+ if (cflag & CSTOPB) {
+ cval |= SMCMR_SL; /* Two stops */
+ bits++;
+ }
+ if (cflag & PARENB) {
+ cval |= SMCMR_PEN;
+ bits++;
+ }
+ if (!(cflag & PARODD))
+ cval |= SMCMR_PM_EVEN;
+
+ /* Determine divisor based on baud rate */
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 4)
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+
+ baud_rate = baud_table[i];
+
+ info->timeout = (TX_BUF_SIZE*HZ*bits);
+ info->timeout += HZ/50; /* Add .02 seconds of slop */
+
+#ifdef modem_control
+ /* CTS flow control flag and modem status interrupts */
+ info->IER &= ~UART_IER_MSI;
+ if (info->flags & ASYNC_HARDPPS_CD)
+ info->IER |= UART_IER_MSI;
+ if (cflag & CRTSCTS) {
+ info->flags |= ASYNC_CTS_FLOW;
+ info->IER |= UART_IER_MSI;
+ } else
+ info->flags &= ~ASYNC_CTS_FLOW;
+ if (cflag & CLOCAL)
+ info->flags &= ~ASYNC_CHECK_CD;
+ else {
+ info->flags |= ASYNC_CHECK_CD;
+ info->IER |= UART_IER_MSI;
+ }
+ serial_out(info, UART_IER, info->IER);
+#endif
+
+ /*
+ * Set up parity check flag
+ */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV);
+ if (I_INPCK(info->tty))
+ info->read_status_mask |= BD_SC_FR | BD_SC_PR;
+ if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ info->read_status_mask |= BD_SC_BR;
+
+ /*
+ * Characters to ignore
+ */
+ info->ignore_status_mask = 0;
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= BD_SC_PR | BD_SC_FR;
+ if (I_IGNBRK(info->tty)) {
+ info->ignore_status_mask |= BD_SC_BR;
+ /*
+ * If we're ignore parity and break indicators, ignore
+ * overruns too. (For real raw support).
+ */
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= BD_SC_OV;
+ }
+ /*
+ * !!! ignore all characters if CREAD is not set
+ */
+ if ((cflag & CREAD) == 0)
+ info->read_status_mask &= ~BD_SC_EMPTY;
+ save_flags(flags); cli();
+
+ /* Start bit has not been added (so don't, because we would just
+ * subtract it later), and we need to add one for the number of
+ * stops bits (there is always at least one).
+ */
+ bits++;
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+
+ /* Set the mode register. We want to keep a copy of the
+ * enables, because we want to put them back if they were
+ * present.
+ */
+ prev_mode = smcp->smc_smcmr;
+ smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART;
+ smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN));
+ }
+ else {
+ sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+ sccp->scc_sccm &= ~UART_SCCM_RX;
+ }
+
+ mbx_cpm_setbrg(info->state->smc_scc_num, baud_rate);
+
+ restore_flags(flags);
+}
+
+static void rs_8xx_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ volatile cbd_t *bdp;
+
+ if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+ return;
+
+ if (!tty)
+ return;
+
+ bdp = info->tx_cur;
+ while (bdp->cbd_sc & BD_SC_READY);
+
+ *((char *)__va(bdp->cbd_bufaddr)) = ch;
+ bdp->cbd_datlen = 1;
+ bdp->cbd_sc |= BD_SC_READY;
+
+ /* Get next BD.
+ */
+ if (bdp->cbd_sc & BD_SC_WRAP)
+ bdp = info->tx_bd_base;
+ else
+ bdp++;
+
+ info->tx_cur = (cbd_t *)bdp;
+
+}
+
+static int rs_8xx_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int c, ret = 0;
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ volatile cbd_t *bdp;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write"))
+ return 0;
+
+ if (!tty)
+ return 0;
+
+ bdp = info->tx_cur;
+
+ while (1) {
+ c = MIN(count, TX_BUF_SIZE);
+
+ if (c <= 0)
+ break;
+
+ if (bdp->cbd_sc & BD_SC_READY) {
+ info->flags |= TX_WAKEUP;
+ break;
+ }
+
+ if (from_user) {
+ if (c !=
+ copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
+ } else {
+ memcpy(__va(bdp->cbd_bufaddr), buf, c);
+ }
+
+ bdp->cbd_datlen = c;
+ bdp->cbd_sc |= BD_SC_READY;
+
+ buf += c;
+ count -= c;
+ ret += c;
+
+ /* Get next BD.
+ */
+ if (bdp->cbd_sc & BD_SC_WRAP)
+ bdp = info->tx_bd_base;
+ else
+ bdp++;
+ info->tx_cur = (cbd_t *)bdp;
+ }
+ return ret;
+}
+
+static int rs_8xx_write_room(struct tty_struct *tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ int ret;
+
+ if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+ return 0;
+
+ if ((info->tx_cur->cbd_sc & BD_SC_READY) == 0) {
+ info->flags &= ~TX_WAKEUP;
+ ret = TX_BUF_SIZE;
+ }
+ else {
+ info->flags |= TX_WAKEUP;
+ ret = 0;
+ }
+ return ret;
+}
+
+/* I could track this with transmit counters....maybe later.
+*/
+static int rs_8xx_chars_in_buffer(struct tty_struct *tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+ return 0;
+ return 0;
+}
+
+static void rs_8xx_flush_buffer(struct tty_struct *tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+ return;
+
+ /* There is nothing to "flush", whatever we gave the CPM
+ * is on its way out.
+ */
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ info->flags &= ~TX_WAKEUP;
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_8xx_send_xchar(struct tty_struct *tty, char ch)
+{
+ volatile cbd_t *bdp;
+
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+ return;
+
+ bdp = info->tx_cur;
+ while (bdp->cbd_sc & BD_SC_READY);
+
+ *((char *)__va(bdp->cbd_bufaddr)) = ch;
+ bdp->cbd_datlen = 1;
+ bdp->cbd_sc |= BD_SC_READY;
+
+ /* Get next BD.
+ */
+ if (bdp->cbd_sc & BD_SC_WRAP)
+ bdp = info->tx_bd_base;
+ else
+ bdp++;
+
+ info->tx_cur = (cbd_t *)bdp;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_8xx_throttle(struct tty_struct * tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+ return;
+
+ if (I_IXOFF(tty))
+ rs_8xx_send_xchar(tty, STOP_CHAR(tty));
+
+#ifdef modem_control
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->MCR &= ~UART_MCR_RTS;
+
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+#endif
+}
+
+static void rs_8xx_unthrottle(struct tty_struct * tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ rs_8xx_send_xchar(tty, START_CHAR(tty));
+ }
+#ifdef modem_control
+ if (tty->termios->c_cflag & CRTSCTS)
+ info->MCR |= UART_MCR_RTS;
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+#ifdef maybe
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct async_struct * info, unsigned int *value)
+{
+ unsigned char status;
+ unsigned int result;
+
+ cli();
+ status = serial_in(info, UART_LSR);
+ sti();
+ result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+ return put_user(result,value);
+}
+#endif
+
+static int get_modem_info(ser_info_t *info, unsigned int *value)
+{
+ unsigned int result = 0;
+#ifdef modem_control
+ unsigned char control, status;
+
+ control = info->MCR;
+ cli();
+ status = serial_in(info, UART_MSR);
+ sti();
+ result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+ | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+#ifdef TIOCM_OUT1
+ | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
+ | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
+#endif
+ | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+ | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+ | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+ | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+#endif
+ return put_user(result,value);
+}
+
+static int set_modem_info(ser_info_t *info, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+
+ error = get_user(arg, value);
+ if (error)
+ return error;
+#ifdef modem_control
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ info->MCR |= UART_MCR_RTS;
+ if (arg & TIOCM_DTR)
+ info->MCR |= UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+ if (arg & TIOCM_OUT1)
+ info->MCR |= UART_MCR_OUT1;
+ if (arg & TIOCM_OUT2)
+ info->MCR |= UART_MCR_OUT2;
+#endif
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ info->MCR &= ~UART_MCR_RTS;
+ if (arg & TIOCM_DTR)
+ info->MCR &= ~UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+ if (arg & TIOCM_OUT1)
+ info->MCR &= ~UART_MCR_OUT1;
+ if (arg & TIOCM_OUT2)
+ info->MCR &= ~UART_MCR_OUT2;
+#endif
+ break;
+ case TIOCMSET:
+ info->MCR = ((info->MCR & ~(UART_MCR_RTS |
+#ifdef TIOCM_OUT1
+ UART_MCR_OUT1 |
+ UART_MCR_OUT2 |
+#endif
+ UART_MCR_DTR))
+ | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+#ifdef TIOCM_OUT1
+ | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
+ | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
+#endif
+ | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+ break;
+ default:
+ return -EINVAL;
+ }
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+#endif
+ return 0;
+}
+
+/* Sending a break is a two step process on the SMC/SCC. It is accomplished
+ * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT
+ * command. We take advantage of the begin/end functions to make this
+ * happen.
+ */
+static void begin_break(ser_info_t *info)
+{
+ volatile cpm8xx_t *cp;
+ ushort chan;
+ ushort num;
+
+ cp = cpmp;
+
+ if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ if (num == 0)
+ chan = CPM_CR_CH_SMC1;
+ else
+ chan = CPM_CR_CH_SMC2;
+ }
+ else {
+ num -= SCC_NUM_BASE;
+ switch (num) {
+ case 0: chan = CPM_CR_CH_SCC1; break;
+ case 1: chan = CPM_CR_CH_SCC2; break;
+ case 2: chan = CPM_CR_CH_SCC3; break;
+ case 3: chan = CPM_CR_CH_SCC4; break;
+ default: return;
+ }
+ }
+ cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+}
+
+static void end_break(ser_info_t *info)
+{
+ volatile cpm8xx_t *cp;
+ ushort chan;
+ ushort num;
+
+ cp = cpmp;
+
+ if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ if (num == 0)
+ chan = CPM_CR_CH_SMC1;
+ else
+ chan = CPM_CR_CH_SMC2;
+ }
+ else {
+ num -= SCC_NUM_BASE;
+ switch (num) {
+ case 0: chan = CPM_CR_CH_SCC1; break;
+ case 1: chan = CPM_CR_CH_SCC2; break;
+ case 2: chan = CPM_CR_CH_SCC3; break;
+ case 3: chan = CPM_CR_CH_SCC4; break;
+ default: return;
+ }
+ }
+ cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(ser_info_t *info, int duration)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+ printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
+#endif
+ begin_break(info);
+ schedule();
+ end_break(info);
+#ifdef SERIAL_DEBUG_SEND_BREAK
+ printk("done jiffies=%lu\n", jiffies);
+#endif
+}
+
+
+static int rs_8xx_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ int retval;
+ struct async_icount cnow; /* kernel counter temps */
+ struct serial_icounter_struct *p_cuser; /* user space */
+
+ if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+ return -ENODEV;
+
+ if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ if (!arg) {
+ send_break(info, HZ/4); /* 1/4 second */
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ if (signal_pending(current))
+ return -EINTR;
+ return 0;
+ case TIOCSBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ begin_break(info);
+ return 0;
+ case TIOCCBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ end_break(info);
+ return 0;
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ case TIOCSSOFTCAR:
+ error = get_user(arg, (unsigned int *) arg);
+ if (error)
+ return error;
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ return get_modem_info(info, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(info, cmd, (unsigned int *) arg);
+#ifdef maybe
+ case TIOCSERGETLSR: /* Get line status register */
+ return get_lsr_info(info, (unsigned int *) arg);
+#endif
+ /*
+ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ */
+ case TIOCMIWAIT:
+#ifdef modem_control
+ cli();
+ /* note the counters on entry */
+ cprev = info->state->icount;
+ sti();
+ while (1) {
+ interruptible_sleep_on(&info->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ cli();
+ cnow = info->state->icount; /* atomic copy */
+ sti();
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+ return 0;
+ }
+ cprev = cnow;
+ }
+ /* NOTREACHED */
+#else
+ return 0;
+#endif
+
+ /*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ * RI where only 0->1 is counted.
+ */
+ case TIOCGICOUNT:
+ cli();
+ cnow = info->state->icount;
+ sti();
+ p_cuser = (struct serial_icounter_struct *) arg;
+ error = put_user(cnow.cts, &p_cuser->cts);
+ if (error) return error;
+ error = put_user(cnow.dsr, &p_cuser->dsr);
+ if (error) return error;
+ error = put_user(cnow.rng, &p_cuser->rng);
+ if (error) return error;
+ error = put_user(cnow.dcd, &p_cuser->dcd);
+ if (error) return error;
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+/* FIX UP modem control here someday......
+*/
+static void rs_8xx_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+
+ if ( (tty->termios->c_cflag == old_termios->c_cflag)
+ && ( RELEVANT_IFLAG(tty->termios->c_iflag)
+ == RELEVANT_IFLAG(old_termios->c_iflag)))
+ return;
+
+ change_speed(info);
+
+#ifdef modem_control
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(tty->termios->c_cflag & CBAUD)) {
+ info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ info->MCR |= UART_MCR_DTR;
+ if (!tty->hw_stopped ||
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ info->MCR |= UART_MCR_RTS;
+ }
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ rs_8xx_start(tty);
+ }
+#endif
+
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_8xx_close(struct tty_struct *tty, struct file * filp)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ struct serial_state *state;
+ unsigned long flags;
+ int idx;
+ volatile smc_t *smcp;
+ volatile scc_t *sccp;
+
+ if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+ return;
+
+ state = info->state;
+
+ save_flags(flags); cli();
+
+ if (tty_hung_up_p(filp)) {
+ DBG_CNT("before DEC-hung");
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+ if ((tty->count == 1) && (state->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. state->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("rs_close: bad serial port count; tty->count is 1, "
+ "state->count is %d\n", state->count);
+ state->count = 1;
+ }
+ if (--state->count < 0) {
+ printk("rs_close: bad serial port count for ttys%d: %d\n",
+ info->line, state->count);
+ state->count = 0;
+ }
+ if (state->count) {
+ DBG_CNT("before DEC-2");
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+ return;
+ }
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->state->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->state->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ info->read_status_mask &= ~BD_SC_EMPTY;
+ if (info->flags & ASYNC_INITIALIZED) {
+ if ((idx = info->state->smc_scc_num) < SCC_NUM_BASE) {
+ smcp = &cpmp->cp_smc[idx];
+ smcp->smc_smcm &= ~SMCM_RX;
+ smcp->smc_smcmr &= ~SMCMR_REN;
+ }
+ else {
+ sccp = &cpmp->cp_scc[idx - SCC_NUM_BASE];
+ sccp->scc_sccm &= ~UART_SCCM_RX;
+ }
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ rs_8xx_wait_until_sent(tty, info->timeout);
+ }
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ info->event = 0;
+ info->tty = 0;
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ unsigned long orig_jiffies, char_time;
+ int lsr;
+ volatile cbd_t *bdp;
+
+ if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+ return;
+
+#ifdef maybe
+ if (info->state->type == PORT_UNKNOWN)
+ return;
+#endif
+
+ orig_jiffies = jiffies;
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = 1;
+ if (timeout)
+ char_time = MIN(char_time, timeout);
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+ printk("jiff=%lu...", jiffies);
+#endif
+
+ /* We go through the loop at least once because we can't tell
+ * exactly when the last character exits the shifter. There can
+ * be at least two characters waiting to be sent after the buffers
+ * are empty.
+ */
+ do {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+/* current->counter = 0; /* make us low-priority */
+ current->timeout = jiffies + char_time;
+ schedule();
+ if (signal_pending(current))
+ break;
+ if (timeout && ((orig_jiffies + timeout) < jiffies))
+ break;
+ bdp = info->tx_cur;
+ } while (bdp->cbd_sc & BD_SC_READY);
+ current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_8xx_hangup(struct tty_struct *tty)
+{
+ ser_info_t *info = (ser_info_t *)tty->driver_data;
+ struct serial_state *state = info->state;
+
+ if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+ return;
+
+ state = info->state;
+
+ rs_8xx_flush_buffer(tty);
+ shutdown(info);
+ info->event = 0;
+ state->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ ser_info_t *info)
+{
+#ifdef DO_THIS_LATER
+ struct wait_queue wait = { current, NULL };
+#endif
+ struct serial_state *state = info->state;
+ int retval;
+ int do_clocal = 0;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ * If this is an SMC port, we don't have modem control to wait
+ * for, so just get out here.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR)) ||
+ (info->state->smc_scc_num < SCC_NUM_BASE)) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (state->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, state->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+#ifdef DO_THIS_LATER
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttys%d, count = %d\n",
+ state->line, state->count);
+#endif
+ cli();
+ if (!tty_hung_up_p(filp))
+ state->count--;
+ sti();
+ info->blocked_open++;
+ while (1) {
+ cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD))
+ serial_out(info, UART_MCR,
+ serial_inp(info, UART_MCR) |
+ (UART_MCR_DTR | UART_MCR_RTS));
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) &&
+ (do_clocal || (serial_in(info, UART_MSR) &
+ UART_MSR_DCD)))
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ state->count++;
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttys%d, count = %d\n",
+ info->line, state->count);
+#endif
+#endif /* DO_THIS_LATER */
+ if (retval)
+ return retval;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+static int get_async_struct(int line, ser_info_t **ret_info)
+{
+ struct serial_state *sstate;
+
+ sstate = rs_table + line;
+ if (sstate->info) {
+ sstate->count++;
+ *ret_info = (ser_info_t *)sstate->info;
+ return 0;
+ }
+ else {
+ return -ENOMEM;
+ }
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_8xx_open(struct tty_struct *tty, struct file * filp)
+{
+ ser_info_t *info;
+ int retval, line;
+
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= NR_PORTS))
+ return -ENODEV;
+ retval = get_async_struct(line, &info);
+ if (retval)
+ return retval;
+ if (serial_paranoia_check(info, tty->device, "rs_open"))
+ return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+ info->state->count);
+#endif
+ tty->driver_data = info;
+ info->tty = tty;
+
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval)
+ return retval;
+
+ MOD_INC_USE_COUNT;
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ if ((info->state->count == 1) &&
+ (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->state->normal_termios;
+ else
+ *tty->termios = info->state->callout_termios;
+ change_speed(info);
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d successful...", info->line);
+#endif
+ return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct serial_state *state)
+{
+#ifdef notdef
+ struct async_struct *info = state->info, scr_info;
+ char stat_buf[30], control, status;
+#endif
+ int ret;
+
+ ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
+ state->line,
+ (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC",
+ state->port, state->irq);
+
+ if (!state->port || (state->type == PORT_UNKNOWN)) {
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+ }
+
+#ifdef notdef
+ /*
+ * Figure out the current RS-232 lines
+ */
+ if (!info) {
+ info = &scr_info; /* This is just for serial_{in,out} */
+
+ info->magic = SERIAL_MAGIC;
+ info->port = state->port;
+ info->flags = state->flags;
+ info->quot = 0;
+ info->tty = 0;
+ }
+ cli();
+ status = serial_in(info, UART_MSR);
+ control = info ? info->MCR : serial_in(info, UART_MCR);
+ sti();
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if (control & UART_MCR_RTS)
+ strcat(stat_buf, "|RTS");
+ if (status & UART_MSR_CTS)
+ strcat(stat_buf, "|CTS");
+ if (control & UART_MCR_DTR)
+ strcat(stat_buf, "|DTR");
+ if (status & UART_MSR_DSR)
+ strcat(stat_buf, "|DSR");
+ if (status & UART_MSR_DCD)
+ strcat(stat_buf, "|CD");
+ if (status & UART_MSR_RI)
+ strcat(stat_buf, "|RI");
+
+ if (info->quot) {
+ ret += sprintf(buf+ret, " baud:%d",
+ state->baud_base / info->quot);
+ }
+
+ ret += sprintf(buf+ret, " tx:%d rx:%d",
+ state->icount.tx, state->icount.rx);
+
+ if (state->icount.frame)
+ ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+
+ if (state->icount.parity)
+ ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+
+ if (state->icount.brk)
+ ret += sprintf(buf+ret, " brk:%d", state->icount.brk);
+
+ if (state->icount.overrun)
+ ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+ /*
+ * Last thing is the RS-232 status lines
+ */
+ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+#endif
+ return ret;
+}
+
+int rs_8xx_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ int i, len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+ for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ len += line_info(page + len, &rs_table[i]);
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ *eof = 1;
+done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static _INLINE_ void show_serial_version(void)
+{
+ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int rs_8xx_init(void))
+{
+ struct serial_state * state;
+ ser_info_t *info;
+ uint mem_addr, dp_addr;
+ int i, j;
+ ushort chan;
+ volatile cbd_t *bdp;
+ volatile cpm8xx_t *cp;
+ volatile smc_t *sp;
+ volatile smc_uart_t *up;
+
+ init_bh(SERIAL_BH, do_serial_bh);
+#if 0
+ timer_table[RS_TIMER].fn = rs_8xx_timer;
+ timer_table[RS_TIMER].expires = 0;
+#endif
+
+ show_serial_version();
+
+ /* Initialize the tty_driver structure */
+
+ memset(&serial_driver, 0, sizeof(struct tty_driver));
+ serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
+ serial_driver.name = "ttyS";
+ serial_driver.major = TTY_MAJOR;
+ serial_driver.minor_start = 64;
+ serial_driver.num = NR_PORTS;
+ serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ serial_driver.init_termios = tty_std_termios;
+ serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ serial_driver.refcount = &serial_refcount;
+ serial_driver.table = serial_table;
+ serial_driver.termios = serial_termios;
+ serial_driver.termios_locked = serial_termios_locked;
+
+ serial_driver.open = rs_8xx_open;
+ serial_driver.close = rs_8xx_close;
+ serial_driver.write = rs_8xx_write;
+ serial_driver.put_char = rs_8xx_put_char;
+ serial_driver.write_room = rs_8xx_write_room;
+ serial_driver.chars_in_buffer = rs_8xx_chars_in_buffer;
+ serial_driver.flush_buffer = rs_8xx_flush_buffer;
+ serial_driver.ioctl = rs_8xx_ioctl;
+ serial_driver.throttle = rs_8xx_throttle;
+ serial_driver.unthrottle = rs_8xx_unthrottle;
+ serial_driver.send_xchar = rs_8xx_send_xchar;
+ serial_driver.set_termios = rs_8xx_set_termios;
+ serial_driver.stop = rs_8xx_stop;
+ serial_driver.start = rs_8xx_start;
+ serial_driver.hangup = rs_8xx_hangup;
+ serial_driver.wait_until_sent = rs_8xx_wait_until_sent;
+ serial_driver.read_proc = rs_8xx_read_proc;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ callout_driver = serial_driver;
+ callout_driver.name = "cua";
+ callout_driver.major = TTYAUX_MAJOR;
+ callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
+
+ if (tty_register_driver(&serial_driver))
+ panic("Couldn't register serial driver\n");
+ if (tty_register_driver(&callout_driver))
+ panic("Couldn't register callout driver\n");
+
+ cp = cpmp; /* Get pointer to Communication Processor */
+
+ /* Configure SMCs Tx/Rx instead of port B parallel I/O.
+ */
+ cp->cp_pbpar |= 0x00000cc0;
+ cp->cp_pbdir &= ~0x00000cc0;
+ cp->cp_pbodr &= ~0x00000cc0;
+
+ /* Wire BRG1 to SMC1 and BRG2 to SMC2.
+ */
+ cp->cp_simode = 0x10000000;
+
+ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+ state->magic = SSTATE_MAGIC;
+ state->line = i;
+ state->type = PORT_UNKNOWN;
+ state->custom_divisor = 0;
+ state->close_delay = 5*HZ/10;
+ state->closing_wait = 30*HZ;
+ state->callout_termios = callout_driver.init_termios;
+ state->normal_termios = serial_driver.init_termios;
+ state->icount.cts = state->icount.dsr =
+ state->icount.rng = state->icount.dcd = 0;
+ state->icount.rx = state->icount.tx = 0;
+ state->icount.frame = state->icount.parity = 0;
+ state->icount.overrun = state->icount.brk = 0;
+ printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n",
+ i, state->port,
+ (state->smc_scc_num < SCC_NUM_BASE) ? "SMC" : "SCC");
+#ifdef CONFIG_SERIAL_CONSOLE
+ /* If we just printed the message on the console port, and
+ * we are about to initialize it for general use, we have
+ * to wait a couple of character times for the CR/NL to
+ * make it out of the transmit buffer.
+ */
+ if (i == CONFIG_SERIAL_CONSOLE_PORT)
+ udelay(2000);
+#endif
+ info = kmalloc(sizeof(ser_info_t), GFP_KERNEL);
+ if (info) {
+ memset(info, 0, sizeof(ser_info_t));
+ info->magic = SERIAL_MAGIC;
+ info->flags = state->flags;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->line = i;
+ info->state = state;
+ state->info = (struct async_struct *)info;
+
+ /* Right now, assume we are using SMCs.
+ */
+ sp = &cp->cp_smc[state->smc_scc_num];
+
+ up = (smc_uart_t *)&cp->cp_dparam[state->port];
+
+ /* We need to allocate a transmit and receive buffer
+ * descriptors from dual port ram, and a character
+ * buffer area from host mem.
+ */
+ dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_NUM_FIFO);
+
+ /* Allocate space for FIFOs in the host memory.
+ */
+ mem_addr = mbx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE);
+
+ /* Set the physical address of the host memory
+ * buffers in the buffer descriptors, and the
+ * virtual address for us to work with.
+ */
+ bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
+ up->smc_rbase = dp_addr;
+ info->rx_cur = info->rx_bd_base = (cbd_t *)bdp;
+
+ for (j=0; j<(RX_NUM_FIFO-1); j++) {
+ bdp->cbd_bufaddr = __pa(mem_addr);
+ bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
+ mem_addr += RX_BUF_SIZE;
+ bdp++;
+ }
+ bdp->cbd_bufaddr = __pa(mem_addr);
+ bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
+
+ dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_NUM_FIFO);
+
+ /* Allocate space for FIFOs in the host memory.
+ */
+ mem_addr = mbx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE);
+
+ /* Set the physical address of the host memory
+ * buffers in the buffer descriptors, and the
+ * virtual address for us to work with.
+ */
+ bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
+ up->smc_tbase = dp_addr;
+ info->tx_cur = info->tx_bd_base = (cbd_t *)bdp;
+
+ for (j=0; j<(TX_NUM_FIFO-1); j++) {
+ bdp->cbd_bufaddr = __pa(mem_addr);
+ bdp->cbd_sc = BD_SC_INTRPT;
+ mem_addr += TX_BUF_SIZE;
+ bdp++;
+ }
+ bdp->cbd_bufaddr = __pa(mem_addr);
+ bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT);
+
+ /* Set up the uart parameters in the parameter ram.
+ */
+ up->smc_rfcr = SMC_EB;
+ up->smc_tfcr = SMC_EB;
+
+ /* Set this to 1 for now, so we get single character
+ * interrupts. Using idle charater time requires
+ * some additional tuning.
+ */
+ up->smc_mrblr = 1; /* receive buffer length */
+ up->smc_maxidl = 0; /* wait forever for next char */
+ up->smc_brkcr = 1; /* number of break chars */
+
+ /* Send the CPM an initialize command.
+ */
+ if (state->smc_scc_num == 0)
+ chan = CPM_CR_CH_SMC1;
+ else
+ chan = CPM_CR_CH_SMC2;
+ cp->cp_cpcr = mk_cr_cmd(chan,
+ CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+
+ /* Set UART mode, 8 bit, no parity, one stop.
+ * Enable receive and transmit.
+ */
+ sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART;
+
+ /* Disable all interrupts and clear all pending
+ * events.
+ */
+ sp->smc_smcm = 0;
+ sp->smc_smce = 0xff;
+
+ /* Install interrupt handler.
+ */
+ cpm_install_handler(state->irq, rs_8xx_interrupt, info);
+
+ /* Set up the baud rate generator.
+ */
+ mbx_cpm_setbrg(state->smc_scc_num, 9600);
+
+ /* If the port is the console, enable Rx and Tx.
+ */
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (i == CONFIG_SERIAL_CONSOLE_PORT)
+ sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
+#endif
+ }
+ }
+ return 0;
+}
+
+/*
+ * The serial console driver used during boot. Note that these names
+ * clash with those found in "serial.c", so we currently can't support
+ * the 16xxx uarts and these at the same time. I will fix this to become
+ * an indirect function call from tty_io.c (or something).
+ */
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/*
+ * Print a string to the serial port trying not to disturb any possible
+ * real use of the port...
+ */
+static void serial_console_write(struct console *c, const char *s,
+ unsigned count)
+{
+ struct serial_state *ser;
+ ser_info_t *info;
+ unsigned i;
+ volatile cbd_t *bdp, *bdbase;
+ volatile smc_uart_t *up;
+ volatile u_char *cp;
+
+ ser = rs_table + c->index;
+
+ /* If the port has been initialized for general use, we have
+ * to use the buffer descriptors allocated there. Otherwise,
+ * we simply use the single buffer allocated.
+ */
+ if ((info = (ser_info_t *)ser->info) != NULL) {
+ bdp = info->tx_cur;
+ bdbase = info->tx_bd_base;
+ }
+ else {
+ /* Pointer to UART in parameter ram.
+ */
+ up = (smc_uart_t *)&cpmp->cp_dparam[ser->port];
+
+ /* Get the address of the host memory buffer.
+ */
+ bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
+ }
+
+ /*
+ * We need to gracefully shut down the transmitter, disable
+ * interrupts, then send our bytes out.
+ */
+
+ /*
+ * Now, do each character. This is not as bad as it looks
+ * since this is a holding FIFO and not a transmitting FIFO.
+ * We could add the complexity of filling the entire transmit
+ * buffer, but we would just wait longer between accesses......
+ */
+ for (i = 0; i < count; i++, s++) {
+
+ /* Wait for transmitter fifo to empty.
+ * Ready indicates output is ready, and xmt is doing
+ * that, not that it is ready for us to send.
+ */
+ while (bdp->cbd_sc & BD_SC_READY);
+
+ /* Send the character out. */
+ cp = __va(bdp->cbd_bufaddr);
+ *cp = *s;
+ bdp->cbd_datlen = 1;
+ bdp->cbd_sc |= BD_SC_READY;
+
+ if (bdp->cbd_sc & BD_SC_WRAP)
+ bdp = bdbase;
+ else
+ bdp++;
+
+ /* if a LF, also do CR... */
+ if (*s == 10) {
+ while (bdp->cbd_sc & BD_SC_READY);
+ cp = __va(bdp->cbd_bufaddr);
+ *cp = 13;
+ bdp->cbd_datlen = 1;
+ bdp->cbd_sc |= BD_SC_READY;
+
+ if (bdp->cbd_sc & BD_SC_WRAP) {
+ bdp = bdbase;
+ }
+ else {
+ bdp++;
+ }
+ }
+ }
+
+ /*
+ * Finally, Wait for transmitter & holding register to empty
+ * and restore the IER
+ */
+ while (bdp->cbd_sc & BD_SC_READY);
+
+ if (info)
+ info->tx_cur = (cbd_t *)bdp;
+}
+
+/*
+ * Receive character from the serial port. This only works well
+ * before the port is initialize for real use.
+ */
+static int serial_console_wait_key(struct console *co)
+{
+ struct serial_state *ser;
+ u_char c, *cp;
+ ser_info_t *info;
+ volatile cbd_t *bdp;
+ volatile smc_uart_t *up;
+
+ ser = rs_table + co->index;
+
+ /* Pointer to UART in parameter ram.
+ */
+ up = (smc_uart_t *)&cpmp->cp_dparam[ser->port];
+
+ /* Get the address of the host memory buffer.
+ * If the port has been initialized for general use, we must
+ * use information from the port structure.
+ */
+ if ((info = ser->info))
+ bdp = info->rx_cur;
+ else
+ bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+ /*
+ * We need to gracefully shut down the receiver, disable
+ * interrupts, then read the input.
+ */
+ while (bdp->cbd_sc & BD_SC_EMPTY); /* Wait for a character */
+ cp = __va(bdp->cbd_bufaddr);
+
+ if (info) {
+ if (bdp->cbd_sc & BD_SC_WRAP) {
+ bdp = info->rx_bd_base;
+ }
+ else {
+ bdp++;
+ }
+ info->rx_cur = (cbd_t *)bdp;
+ }
+
+ c = *cp;
+ return((int)c);
+}
+
+static kdev_t serial_console_device(struct console *c)
+{
+ return MKDEV(TTYAUX_MAJOR, 64 + c->index);
+}
+
+/* This must always be called before the rs_8xx_init() function, otherwise
+ * it blows away the port control information.
+*/
+__initfunc(static int serial_console_setup(struct console *co, char *options))
+{
+ struct serial_state *ser;
+ uint mem_addr, dp_addr;
+ volatile cbd_t *bdp;
+ volatile cpm8xx_t *cp;
+ volatile smc_t *sp;
+ volatile smc_uart_t *up;
+
+ co->cflag = CREAD|CLOCAL|B9600|CS8;
+
+ ser = rs_table + co->index;
+
+ cp = cpmp; /* Get pointer to Communication Processor */
+
+ /* Right now, assume we are using SMCs.
+ */
+ sp = &cp->cp_smc[ser->smc_scc_num];
+
+ /* When we get here, the CPM has been reset, so we need
+ * to configure the port.
+ * We need to allocate a transmit and receive buffer descriptor
+ * from dual port ram, and a character buffer area from host mem.
+ */
+ up = (smc_uart_t *)&cp->cp_dparam[ser->port];
+ cp->cp_pbpar = 0x00c0; /* Enable SMC1 instead of Port B I/O */
+
+ /* Allocate space for two buffer descriptors in the DP ram.
+ */
+ dp_addr = mbx_cpm_dpalloc(sizeof(cbd_t) * 2);
+
+ /* Allocate space for two 2 byte FIFOs in the host memory.
+ */
+ mem_addr = mbx_cpm_hostalloc(4);
+
+ /* Set the physical address of the host memory buffers in
+ * the buffer descriptors.
+ */
+ bdp = (cbd_t *)&cp->cp_dpmem[dp_addr];
+ bdp->cbd_bufaddr = __pa(mem_addr);
+ (bdp+1)->cbd_bufaddr = __pa(mem_addr+2);
+
+ /* For the receive, set empty and wrap.
+ * For transmit, set wrap.
+ */
+ bdp->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
+ (bdp+1)->cbd_sc = BD_SC_WRAP;
+
+ /* Set up the uart parameters in the parameter ram.
+ */
+ up->smc_rbase = dp_addr; /* Base of receive buffer desc. */
+ up->smc_tbase = dp_addr+sizeof(cbd_t); /* Base of xmt buffer desc. */
+ up->smc_rfcr = SMC_EB;
+ up->smc_tfcr = SMC_EB;
+
+ /* Set this to 1 for now, so we get single character interrupts.
+ */
+ up->smc_mrblr = 1; /* receive buffer length */
+ up->smc_maxidl = 0; /* wait forever for next char */
+
+ /* Send the CPM an initialize command.
+ */
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+
+ /* Set UART mode, 8 bit, no parity, one stop.
+ * Enable receive and transmit.
+ */
+ sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART;
+
+ /* Set up the baud rate generator.
+ */
+ mbx_cpm_setbrg(ser->smc_scc_num, 9600);
+
+ /* And finally, enable Rx and Tx.
+ */
+ sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
+
+ return 0;
+}
+
+static struct console sercons = {
+ "ttyS",
+ serial_console_write,
+ NULL,
+ serial_console_device,
+ serial_console_wait_key,
+ NULL,
+ serial_console_setup,
+ CON_PRINTBUFFER,
+ CONFIG_SERIAL_CONSOLE_PORT,
+ 0,
+ NULL
+};
+
+/*
+ * Register console.
+ */
+__initfunc (long console_8xx_init(long kmem_start, long kmem_end))
+{
+ register_console(&sercons);
+ return kmem_start;
+}
+
+#endif
diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile
index 832513c8d..cd81e45a4 100644
--- a/arch/ppc/Makefile
+++ b/arch/ppc/Makefile
@@ -12,14 +12,7 @@
# Rewritten by Cort Dougan and Paul Mackerras
#
-ifdef CONFIG_CHRP
-# XXX for now
-KERNELBASE =0x90000000
-KERNELLOAD =0x90010000
-else
-KERNELBASE =0xc0000000
-KERNELLOAD =0xc0000000
-endif
+KERNELLOAD =0xc0000000
# PowerPC (cross) tools
ifneq ($(shell uname -m),ppc)
@@ -32,21 +25,13 @@ ASFLAGS =
LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic
CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__
CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \
- -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring \
- -DKERNELBASE=$(KERNELBASE)
+ -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring
CPP = $(CC) -E $(CFLAGS)
-ifdef CONFIG_601
-CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601
+ifdef CONFIG_8xx
+CFLAGS := $(CFLAGS) -mcpu=860
endif
-ifdef CONFIG_603
-CFLAGS := $(CFLAGS) -mcpu=603 -DCPU=603
-endif
-
-ifdef CONFIG_604
-CFLAGS := $(CFLAGS) -mcpu=604 -DCPU=604
-endif
HEAD := arch/ppc/kernel/head.o
@@ -64,6 +49,11 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot
MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot
+ifdef CONFIG_8xx
+SUBDIRS += arch/ppc/8xx_io
+DRIVERS += arch/ppc/8xx_io/8xx_io.a drivers/net/net.a
+endif
+
checks:
@$(MAKE) -C arch/$(ARCH)/kernel checks
@@ -83,6 +73,19 @@ prep_config:
rm -f .config arch/ppc/defconfig
ln -s prep_defconfig arch/ppc/defconfig
+chrp_config:
+ rm -f .config arch/ppc/defconfig
+ ln -s chrp_defconfig arch/ppc/defconfig
+
+common_config:
+ rm -f .config arch/ppc/common_defconfig
+ ln -s common_defconfig arch/ppc/defconfig
+
+mbx_config:
+ rm -f .config arch/ppc/defconfig
+ ln -s mbx_defconfig arch/ppc/defconfig
+
+
tags:
etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h}
diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile
index 02f95f577..84be805b2 100644
--- a/arch/ppc/boot/Makefile
+++ b/arch/ppc/boot/Makefile
@@ -25,31 +25,70 @@ ZOFF = 0
ZSZ = 0
IOFF = 0
ISZ = 0
+ifeq ($(CONFIG_MBX),y)
+ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000
+else
#ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000
+endif
GZIP_FLAGS = -v9
-OBJECTS := head.o misc.o vreset.o kbd.o ../coffboot/zlib.o # inflate.o unzip.o
-#OBJECTS := crt0.o start.o vreset.o
+OBJECTS := head.o misc.o ../coffboot/zlib.o # inflate.o unzip.o
CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJCOPY_ARGS = -O elf32-powerpc
+ifeq ($(CONFIG_MBX),y)
+OBJECTS += mbxtty.o
+CFLAGS += -DCONFIG_MBX
+else
+OBJECTS += vreset.o kbd.o
+endif
+
all: zImage
+ifeq ($(CONFIG_ALL_PPC),y)
+CONFIG_PREP = y
+endif
ifeq ($(CONFIG_PREP),y)
-mkprep : mkprep.c
- $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c
+zvmlinux.initrd: zvmlinux
+ $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+ $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+ --add-section=initrd=ramdisk.image.gz \
+ --add-section=image=../coffboot/vmlinux.gz \
+ zvmlinux.initrd.tmp zvmlinux.initrd
+ $(CC) $(CFLAGS) -DINITRD_OFFSET=`sh offset zvmlinux.initrd initrd` \
+ -DINITRD_SIZE=`sh size zvmlinux.initrd initrd` \
+ -DZIMAGE_OFFSET=`sh offset zvmlinux.initrd image` \
+ -DZIMAGE_SIZE=`sh size zvmlinux.initrd image` \
+ -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
+ $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
+ $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
+ --add-section=initrd=ramdisk.image.gz \
+ --add-section=image=../coffboot/vmlinux.gz \
+ zvmlinux.initrd.tmp $@
+ rm zvmlinux.initrd.tmp
-floppy: $(TOPDIR)/vmlinux zImage
- dd if=zImage of=/dev/fd0H1440 bs=64b
+else
+zvmlinux.initrd:
+endif
-znetboot : zImage
- cp zImage /tftpboot/zImage.prep
+zImage: zvmlinux mkprep
+ifeq ($(CONFIG_PREP),y)
+ ./mkprep -pbp zvmlinux zImage
+endif
+ifeq ($(CONFIG_MBX),y)
+ ln -sf zvmlinux zImage
+endif
-znetboot.initrd : zImage.initrd
- cp zImage.initrd /tftpboot/zImage.prep
+zImage.initrd: zvmlinux.initrd mkprep
+ifeq ($(CONFIG_PREP),y)
+ ./mkprep -pbp zvmlinux.initrd zImage.initrd
+endif
+ifeq ($(CONFIG_MBX),y)
+ ln -sf zvmlinux.initrd zImage.initrd
+endif
zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
#
@@ -63,61 +102,39 @@ zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz
# then with the offset rebuild the bootloader so we know where the kernel is
#
$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
- -DZIMAGE_OFFSET=`./offset zvmlinux image` \
- -DZIMAGE_SIZE=`./size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \
+ -DZIMAGE_OFFSET=`sh offset zvmlinux image` \
+ -DZIMAGE_SIZE=`sh size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \
-c -o misc.o misc.c
$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS)
$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \
zvmlinux.tmp $@
rm zvmlinux.tmp
-zvmlinux.initrd: zvmlinux
- $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
- $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
- --add-section=initrd=ramdisk.image.gz \
- --add-section=image=../coffboot/vmlinux.gz \
- zvmlinux.initrd.tmp zvmlinux.initrd
- $(CC) $(CFLAGS) -DINITRD_OFFSET=`./offset zvmlinux.initrd initrd` \
- -DINITRD_SIZE=`./size zvmlinux.initrd initrd` \
- -DZIMAGE_OFFSET=`./offset zvmlinux.initrd image` \
- -DZIMAGE_SIZE=`./size zvmlinux.initrd image` \
- -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c
- $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS)
- $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
- --add-section=initrd=ramdisk.image.gz \
- --add-section=image=../coffboot/vmlinux.gz \
- zvmlinux.initrd.tmp $@
- rm zvmlinux.initrd.tmp
-
-zImage: zvmlinux mkprep
- ./mkprep -pbp zvmlinux zImage
-
-zImage.initrd: zvmlinux.initrd mkprep
- ./mkprep -pbp zvmlinux.initrd zImage.initrd
-else
-mkprep:
-
-floppy:
-
-znetboot:
-
-znetboot.initrd:
-
-zvmlinux:
-
-zvmlinux.initrd:
-
-zImage:
-
-zImage.initrd:
+floppy: $(TOPDIR)/vmlinux zImage
+ifeq ($(CONFIG_PREP),y)
+ dd if=zImage of=/dev/fd0H1440 bs=64b
endif
+mkprep : mkprep.c
+ifeq ($(CONFIG_PREP),y)
+ $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c
+endif
+znetboot : zImage
+ifeq ($(CONFIG_PREP),y)
+ cp zImage /tftpboot/zImage.prep
+endif
+ifeq ($(CONFIG_MBX),y)
+ cp zImage /tftpboot/zImage.mbx
+endif
-# just here to match coffboot/Makefile
-vmlinux.coff:
-
-vmlinux.coff.initrd:
+znetboot.initrd : zImage.initrd
+ifeq ($(CONFIG_PREP),y)
+ cp zImage.initrd /tftpboot/zImage.prep
+endif
+ifeq ($(CONFIG_MBX),y)
+ cp zImage.initrd /tftpboot/zImage.mbx
+endif
clean:
rm -f vmlinux* zvmlinux* mkprep zImage*
@@ -128,3 +145,7 @@ fastdep:
dep:
$(CPP) -M *.S *.c > .depend
+# just here to match coffboot/Makefile
+vmlinux.coff:
+
+vmlinux.coff.initrd:
diff --git a/arch/ppc/boot/head.S b/arch/ppc/boot/head.S
index ff0030154..26a0be180 100644
--- a/arch/ppc/boot/head.S
+++ b/arch/ppc/boot/head.S
@@ -1,19 +1,43 @@
+#include <linux/config.h>
#include "../kernel/ppc_defs.h"
#include "../kernel/ppc_asm.tmpl"
#include <asm/processor.h>
+#include <asm/cache.h>
.text
/*
* This code is loaded by the ROM loader at some arbitrary location.
* Move it to high memory so that it can load the kernel at 0x0000.
+ *
+ * The MBX EPPC-Bug understands ELF, so it loads us into the location
+ * specified in the header. This is a two step process. First, EPPC-Bug
+ * loads the file into the intermediate buffer memory location specified
+ * by the environment parameters. When it discovers this is an ELF
+ * binary, it relocates to the link address for us. Unfortunately, the
+ * header does not move with the file, so we have to find the
+ * intermediate load location and read the header from there. From
+ * information provided by Motorola (thank you), we know this intermediate
+ * location can be found from the NVRAM environment.
+ * All of these addresses must be somewhat carefully chosen to make sure
+ * we don't overlap the regions. I chose to load the kernel at 0, the
+ * compressed image loads at 0x00100000, and the MBX intermediate buffer
+ * was set to 0x00200000. Provided the loaded kernel image never grows
+ * over one megabyte (which I am going to ensure never happens :-), these
+ * will work fine. When we get called from EPPC-Bug, registers are:
+ * R1 - Stack pointer at a high memory address.
+ * R3 - Pointer to Board Information Block.
+ * R4 - Pointer to argument string.
+ * Interrupts masked, cache and MMU disabled.
*/
.globl start
start:
bl start_
start_:
- mr r11,r3 /* Save pointer to residual data */
+ mr r11,r3 /* Save pointer to residual/board data */
+
+#ifndef CONFIG_MBX
mfmsr r3 /* Turn off interrupts */
li r4,0
ori r4,r4,MSR_EE
@@ -33,7 +57,6 @@ start_:
bne 00b
mflr r21
mfctr r22
- bl flush_instruction_cache
mtlr r21
mtctr r22
bctr /* Jump to code */
@@ -70,6 +93,8 @@ relocate:
mtlr r3 /* Easiest way to do an absolute jump */
blr
start_ldr:
+#endif /* ndef CONFIG_MBX */
+
/* Clear all of BSS */
lis r3,edata@h
ori r3,r3,edata@l
@@ -89,12 +114,21 @@ start_ldr:
li r2,0x000F /* Mask pointer to 16-byte boundary */
andc r1,r1,r2
/* Run loader */
+#ifdef CONFIG_MBX
+ bl serial_init /* Init MBX serial port */
+#define ILAP_ADDRESS 0xfa000020
+ lis r8, ILAP_ADDRESS@h
+ lwz r8, ILAP_ADDRESS@l(r8)
+ li r9,end@h
+ ori r9,r9,end@l
+ sub r7,r8,r9
+ addis r8, r8, 1 /* Add 64K */
+#endif
mr r3,r8 /* Load point */
mr r4,r7 /* Program length */
mr r5,r6 /* Checksum */
mr r6,r11 /* Residual data */
bl decompress_kernel
-
/* changed to use r3 (as firmware does) for kernel
as ptr to residual -- Cort*/
lis r6,cmd_line@h
@@ -111,45 +145,25 @@ start_ldr:
lis r2,initrd_end@h
ori r2,r2,initrd_end@l
lwz r5,0(r2)
-
- li r9,0x00c /* Kernel code starts here */
+
+ /* tell kernel we're prep */
+ /*
+ * get start address of kernel code which is stored as a coff
+ * entry. see boot/head.S -- Cort
+ */
+ li r9,0x0
+ lwz r9,0(r9)
mtlr r9
+#ifndef CONFIG_MBX
+ li r9,0
+ lis r10,0xdeadc0de@h
+ ori r10,r10,0xdeadc0de@l
+ stw r10,0(r9)
+#endif
blr
hang:
b hang
- .globl _get_SP
-_get_SP:
- mr r3,r1
- blr
-
- .globl _get_PVR
-_get_PVR:
- mfspr r3,PVR
- blr
-
- .globl _get_MSR
-_get_MSR:
- mfmsr r3
- blr
-
- .globl _put_MSR
-_put_MSR:
- sync
- mtmsr r3
- blr
-
- .globl _get_HID0
-_get_HID0:
- mfspr r3,HID0
- blr
-
- .globl _put_HID0
-_put_HID0:
- sync
- mtspr HID0,r3
- blr
-
/*
* Delay for a number of microseconds
* -- Use the BUS timer (assumes 66MHz)
@@ -189,98 +203,4 @@ udelay:
blt 2b
3: blr
-/*
- * This space [buffer] is used to forceably flush the data cache when
- * running in copyback mode. This is necessary IFF the data cache could
- * contain instructions for which the instruction cache has stale data.
- * Since the instruction cache NEVER snoops the data cache, memory must
- * be made coherent with the data cache to insure that the instruction
- * cache gets a valid instruction stream. Note that this flushing is
- * only performed when switching from system to user mode since this is
- * the only juncture [as far as the OS goes] where the data cache may
- * contain instructions, e.g. after a disk read.
- */
-#define NUM_CACHE_LINES 128*8
-#define CACHE_LINE_SIZE 32
-#if 0
-cache_flush_buffer:
- .space NUM_CACHE_LINES*CACHE_LINE_SIZE /* CAUTION! these need to match hardware */
-#else
-#define cache_flush_buffer 0x1000
-#endif
-
-
-/*
- * Flush instruction cache
- * *** I'm really paranoid here!
- */
-_GLOBAL(flush_instruction_cache)
- mflr r5
- bl flush_data_cache
- mfspr r3,HID0 /* Caches are controlled by this register */
- li r4,0
- ori r4,r4,(HID0_ICE|HID0_ICFI)
- or r3,r3,r4 /* Need to enable+invalidate to clear */
- mtspr HID0,r3
- andc r3,r3,r4
- ori r3,r3,HID0_ICE /* Enable cache */
- mtspr HID0,r3
- mtlr r5
- blr
-
-/*
- * Flush data cache
- * *** I'm really paranoid here!
- */
-_GLOBAL(flush_data_cache)
- lis r3,cache_flush_buffer@h
- ori r3,r3,cache_flush_buffer@l
- li r4,NUM_CACHE_LINES
- mtctr r4
-#if 0
-00: dcbz 0,r3 /* Flush cache line with minimal BUS traffic */
-#else
-00: lwz r4,0(r3)
-#endif
- addi r3,r3,CACHE_LINE_SIZE /* Next line, please */
- bdnz 00b
-10: blr
-
-/*
- * Flush a particular page from the DATA cache
- * Note: this is necessary because the instruction cache does *not*
- * snoop from the data cache.
- * void flush_page(void *page)
- */
-_GLOBAL(flush_page)
- li r4,0x0FFF
- andc r3,r3,r4 /* Get page base address */
- li r4,4096/CACHE_LINE_SIZE /* Number of lines in a page */
- mtctr r4
-00: dcbf 0,r3 /* Clear line */
- icbi 0,r3
- addi r3,r3,CACHE_LINE_SIZE
- bdnz 00b
- blr
-
-/*
- * Execute a [foreign] function
- *
- * run(p1, p2, cp, ep, entry)
- *
- */
- .globl run
-run:
- mtctr r7 /* Entry point */
-#define IS_PreP 0x50726550 /* 'PreP' */
- lis r30,IS_PreP>>16
- ori r30,r30,IS_PreP&0xFFFF
- mr 11,r5
- mr 12,r6
- mr r28,r5
- mr r29,r6
- mr 11,r5
- mr 12,r6
- bctr
-
.comm .stack,4096*2,4
diff --git a/arch/ppc/boot/mbxtty.c b/arch/ppc/boot/mbxtty.c
new file mode 100644
index 000000000..ee6c59c90
--- /dev/null
+++ b/arch/ppc/boot/mbxtty.c
@@ -0,0 +1,118 @@
+
+
+/* Minimal serial functions needed to send messages out the serial
+ * port on the MBX console.
+ *
+ * The MBX uxes SMC1 for the serial port. We reset the port and use
+ * only the first BD that EPPC-Bug set up as a character FIFO.
+ *
+ * It's a big hack, but I don't have time right now....I want a kernel
+ * that boots.
+ */
+#include <linux/types.h>
+#include <asm/mbx.h>
+#include "../8xx_io/commproc.h"
+
+#define CPM_CPCR ((volatile ushort *)0xfa2009c0)
+#define SMC1_MODE ((volatile ushort *)0xfa200a82)
+#define SMC1_TBDF ((volatile bd_t *)0xfa202c90)
+#define SMC1_RBDF ((volatile bd_t *)0xfa202c10)
+
+static cpm8xx_t *cpmp = (cpm8xx_t *)&(((immap_t *)MBX_IMAP_ADDR)->im_cpm);
+
+void
+serial_init(void)
+{
+ volatile smc_t *sp;
+ volatile smc_uart_t *up;
+ volatile cbd_t *tbdf, *rbdf;
+ volatile cpm8xx_t *cp;
+
+ cp = cpmp;
+ sp = (smc_t*)&(cp->cp_smc[0]);
+ up = (smc_uart_t *)&cp->cp_dparam[PROFF_SMC1];
+
+ /* Disable transmitter/receiver.
+ */
+ sp->smc_smcm &= ~(SMCMR_REN | SMCMR_TEN);
+
+ tbdf = (cbd_t *)&cp->cp_dpmem[up->smc_tbase];
+ rbdf = (cbd_t *)&cp->cp_dpmem[up->smc_rbase];
+
+ /* Issue a stop transmit, and wait for it.
+ */
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_STOP_TX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+
+ /* Make the first buffer the only buffer.
+ */
+ tbdf->cbd_sc |= BD_SC_WRAP;
+ rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
+
+ /* Single character receive.
+ */
+ up->smc_mrblr = 1;
+ up->smc_maxidl = 0;
+
+ /* Initialize Tx/Rx parameters.
+ */
+ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC1, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+ while (cp->cp_cpcr & CPM_CR_FLG);
+
+ /* Enable transmitter/receiver.
+ */
+ sp->smc_smcm |= SMCMR_REN | SMCMR_TEN;
+}
+
+void
+serial_putchar(const char c)
+{
+ volatile cbd_t *tbdf;
+ volatile char *buf;
+ volatile smc_uart_t *up;
+
+ up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+ tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase];
+
+ /* Wait for last character to go.
+ */
+ buf = (char *)tbdf->cbd_bufaddr;
+ while (tbdf->cbd_sc & BD_SC_READY);
+
+ *buf = c;
+ tbdf->cbd_datlen = 1;
+ tbdf->cbd_sc |= BD_SC_READY;
+}
+
+char
+serial_getc()
+{
+ volatile cbd_t *rbdf;
+ volatile char *buf;
+ volatile smc_uart_t *up;
+ char c;
+
+ up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+ rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+ /* Wait for character to show up.
+ */
+ buf = (char *)rbdf->cbd_bufaddr;
+ while (rbdf->cbd_sc & BD_SC_EMPTY);
+ c = *buf;
+ rbdf->cbd_sc |= BD_SC_EMPTY;
+
+ return(c);
+}
+
+int
+serial_tstc()
+{
+ volatile cbd_t *rbdf;
+ volatile smc_uart_t *up;
+
+ up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC1];
+ rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase];
+
+ return(!(rbdf->cbd_sc & BD_SC_EMPTY));
+}
diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c
index bc6aa088f..33c7c3e0d 100644
--- a/arch/ppc/boot/misc.c
+++ b/arch/ppc/boot/misc.c
@@ -4,25 +4,35 @@
* Adapted for PowerPC by Gary Thomas
*
* Rewritten by Cort Dougan (cort@cs.nmt.edu)
- * Soon to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
+ * One day to be replaced by a single bootloader for chrp/prep/pmac. -- Cort
*/
#include "../coffboot/zlib.h"
#include "asm/residual.h"
#include <elf.h>
+#include <linux/config.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+bd_t hold_board_info;
+#endif
/* this is where the INITRD gets moved to for safe keeping */
-#define INITRD_DESTINATION /*0x00f00000*/ 0x01f00000
+#define INITRD_DESTINATION /*0x00f00000*/ 0x01800000
+#ifdef CONFIG_8xx
+char *avail_ram = (char *) 0x00200000;
+char *end_avail = (char *) 0x00400000;
+#else /* CONFIG_8xx */
/* this will do for now - Cort */
char *avail_ram = (char *) 0x00800000; /* start with 8M */
/* assume 15M max since this is where we copy the initrd to -- Cort */
char *end_avail = (char *) INITRD_DESTINATION;
+#endif /* CONFIG_8xx */
+char cmd_line[256];
RESIDUAL hold_residual;
unsigned long initrd_start = 0, initrd_end = 0;
char *zimage_start;
int zimage_size;
-char cmd_line[256];
char *vidmem = (char *)0xC00B8000;
int lines, cols;
@@ -47,6 +57,7 @@ void exit()
while(1);
}
+#ifndef CONFIG_MBX
static void clear_screen()
{
int i, j;
@@ -144,6 +155,39 @@ void puts(const char *s)
orig_x = x;
orig_y = y;
}
+#else
+/* The MBX is just the serial port.
+*/
+tstc(void)
+{
+ return (serial_tstc());
+}
+
+getc(void)
+{
+ while (1) {
+ if (serial_tstc()) return (serial_getc());
+ }
+}
+
+void
+putc(const char c)
+{
+ serial_putchar(c);
+}
+
+void puts(const char *s)
+{
+ char c;
+
+ while ( ( c = *s++ ) != '\0' ) {
+ serial_putchar(c);
+ if ( c == '\n' )
+ serial_putchar('\r');
+ }
+}
+
+#endif /* CONFIG_MBX */
void * memcpy(void * __dest, __const void * __src,
int __n)
@@ -253,6 +297,8 @@ void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
inflateEnd(&s);
}
+unsigned char sanity[0x2000];
+
unsigned long
decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual)
{
@@ -260,31 +306,60 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R
extern unsigned long start;
char *cp, ch;
unsigned long i;
-
-
+
lines = 25;
cols = 80;
orig_x = 0;
orig_y = 24;
- /* Turn off MMU. Since we are mapped 1-1, this is OK. */
- flush_instruction_cache();
- _put_HID0(_get_HID0() & ~0x0000C000);
- _put_MSR(_get_MSR() & ~0x0030);
-
+#ifndef CONFIG_8xx
vga_init(0xC0000000);
- /*clear_screen();*/
+ /* copy the residual data */
+ if (residual)
+ memcpy(&hold_residual,residual,sizeof(RESIDUAL));
+#endif /* CONFIG_8xx */
+#ifdef CONFIG_MBX
+ /* copy board data */
+ if (residual)
+ _bcopy((char *)residual, (char *)&hold_board_info,
+ sizeof(hold_board_info));
+#endif /* CONFIG_8xx */
+
- puts("loaded at: "); puthex(load_addr);
+ puts("loaded at: "); puthex(load_addr);
puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
-
- puts("relocated to: "); puthex((unsigned long)&start);
+ puts("relocated to: "); puthex((unsigned long)&start);
puts(" "); puthex((unsigned long)((unsigned long)&start + (4*num_words))); puts("\n");
-
+
+ if ( residual )
+ {
+ puts("board data at: "); puthex((unsigned long)residual);
+ puts(" ");
+#ifdef CONFIG_MBX
+ puthex((unsigned long)((unsigned long)residual + sizeof(bd_t)));
+#else
+ puthex((unsigned long)((unsigned long)residual + sizeof(RESIDUAL)));
+#endif
+ puts("\n");
+ puts("relocated to: ");
+#ifdef CONFIG_MBX
+ puthex((unsigned long)&hold_board_info);
+#else
+ puthex((unsigned long)&hold_residual);
+#endif
+ puts(" ");
+#ifdef CONFIG_MBX
+ puthex((unsigned long)((unsigned long)&hold_board_info + sizeof(bd_t)));
+#else
+ puthex((unsigned long)((unsigned long)&hold_residual + sizeof(RESIDUAL)));
+#endif
+ puts("\n");
+ }
+
zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
zimage_size = ZIMAGE_SIZE;
- puts("zimage at: "); puthex((unsigned long)zimage_start);
+ puts("zimage at: "); puthex((unsigned long)zimage_start);
puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
if ( INITRD_OFFSET )
@@ -296,18 +371,19 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R
/* relocate initrd */
if ( initrd_start )
{
- puts("initrd at: "); puthex(initrd_start);
+ puts("initrd at: "); puthex(initrd_start);
puts(" "); puthex(initrd_end); puts("\n");
memcpy ((void *)INITRD_DESTINATION,(void *)initrd_start,
INITRD_SIZE );
initrd_end = INITRD_DESTINATION + INITRD_SIZE;
initrd_start = INITRD_DESTINATION;
- puts("Moved initrd to: "); puthex(initrd_start);
+ puts("Moved initrd to: "); puthex(initrd_start);
puts(" "); puthex(initrd_end); puts("\n");
}
-
+#ifndef CONFIG_MBX
CRT_tstc(); /* Forces keyboard to be initialized */
+#endif
puts("\nLinux/PPC load: ");
timer = 0;
cp = cmd_line;
@@ -339,9 +415,12 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R
if ( initrd_start > (16<<20))
puts("initrd_start located > 16M\n");
-
puts("Uncompressing Linux...");
- gunzip(0, 0x400000, zimage_start, &zimage_size);
+
+ /* these _bcopy() calls are here so I can add breakpoints to the boot for mbx -- Cort */
+ /*_bcopy( (char *)0x100,(char *)&sanity, 0x2000-0x100);*/
+ gunzip(0, 0x400000, zimage_start, &zimage_size);
+ /*_bcopy( (char *)&sanity,(char *)0x100,0x2000-0x100);*/
puts("done.\n");
puts("Now booting the kernel\n");
return (unsigned long)&hold_residual;
diff --git a/arch/ppc/boot/vreset.c b/arch/ppc/boot/vreset.c
index 3bc3936c6..6086372a0 100644
--- a/arch/ppc/boot/vreset.c
+++ b/arch/ppc/boot/vreset.c
@@ -434,16 +434,15 @@ vga_init(unsigned char *ISA_mem)
{
int slot;
struct VgaRegs *VgaTextRegs;
-
+#if 0
if ((_get_PVR()>>16) == PPC_601) {
return(old_vga_init(ISA_mem));
}
-
-#if 1
+#endif
+
/* See if VGA already in TEXT mode - exit if so! */
outb(0x3CE, 0x06);
if ((inb(0x3CF) & 0x01) == 0){puts("VGA already in text mode\n"); return;}
-#endif
/* If no VGA responding in text mode, then we have some work to do...
*/
@@ -522,11 +521,6 @@ vga_init(unsigned char *ISA_mem)
return (1); /* 'CRT' I/O supported */
}
-static int
-NOP(int x)
-{
-}
-
/*
* Write to VGA Attribute registers.
*/
@@ -852,166 +846,5 @@ void printslots(void)
puts(" Vendor ID: ");
puthex(PCIVendor(i)); puts("\n");
#endif
-
- }
-}
-
-/*
- * OLD vreset.c
- *
- * Initialize the VGA control registers to 80x25 text mode.
- *
- * Adapted from a program by:
- * Steve Sellgren
- * San Francisco Indigo Company
- * sfindigo!sellgren@uunet.uu.net
- */
-
-unsigned char CRTC[24] = {
- 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F,
- 0x00, 0x4F, 0x0D, 0x0E, 0x00, 0x00, 0x00, 0x00, /*0x07, 0x80, */
- 0x9C, 0xAE, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3};
-unsigned char SEQ[5] = {0x3, 0x0, 0x3, 0x0, 0x2};
-unsigned char GC[9] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0xE, 0x0, 0xFF};
-
-#if 0
-static const unsigned char color_LUT[] =
- {
- 0x00, 0x00, 0x00, /* 0 - black */
- 0x00, 0x00, 0x2A, /* 1 - blue */
- 0x00, 0x2A, 0x00, /* 2 - green */
- 0x00, 0x2A, 0x2A, /* 3 - cyan */
- 0x2A, 0x00, 0x00, /* 4 - red */
- 0x2A, 0x00, 0x2A, /* 5 - magenta */
- 0x2A, 0x2A, 0x00, /* 6 - brown */
- 0x2A, 0x2A, 0x2A, /* 7 - white */
- 0x00, 0x00, 0x15, /* 8 - gray */
- 0x00, 0x00, 0x3F, /* 9 - light blue */
- 0x00, 0x2A, 0x15, /* 10 - light green */
- 0x00, 0x2A, 0x3F, /* 11 - light cyan */
- 0x2A, 0x00, 0x15, /* 12 - light red */
- 0x2A, 0x00, 0x3F, /* 13 - light magenta */
- 0x2A, 0x2A, 0x15, /* 14 - yellow */
- 0x2A, 0x2A, 0x3F, /* 15 - bright white */
- };
-#endif
-
-old_vga_init(unsigned char *ISA_mem)
-{
- int i, j;
- int value;
- unsigned char *font_page = (unsigned char *) &ISA_mem[0xA0000];
-
- /* See if VGA already in TEXT mode - exit if so! */
- outb(0x3CE, 0x06);
- if ((inb(0x3CF) & 0x01) == 0) return;
-
- /* From the S3 manual */
- outb(0x46E8, 0x10); /* Put into setup mode */
- outb(0x3C3, 0x10);
- outb(0x102, 0x01); /* Enable registers */
- outb(0x46E8, 0x08); /* Enable video */
- outb(0x3C3, 0x08);
- outb(0x4AE8, 0x00);
-
-#if 0
- outb(0x42E8, 0x80); /* Reset graphics engine? */
-#endif
-
- outb(0x3D4, 0x38); /* Unlock all registers */
- outb(0x3D5, 0x48);
- outb(0x3D4, 0x39);
- outb(0x3D5, 0xA5);
- outb(0x3D4, 0x40);
- outb(0x3D5, inb(0x3D5)|0x01);
- outb(0x3D4, 0x33);
- outb(0x3D5, inb(0x3D5)&~0x52);
- outb(0x3D4, 0x35);
- outb(0x3D5, inb(0x3D5)&~0x30);
- outb(0x3D4, 0x3A);
- outb(0x3D5, 0x00);
- outb(0x3D4, 0x53);
- outb(0x3D5, 0x00);
- outb(0x3D4, 0x31);
- outb(0x3D5, inb(0x3D5)&~0x4B);
- outb(0x3D4, 0x58);
- outb(0x3D5, 0);
-
- outb(0x3D4, 0x54);
- outb(0x3D5, 0x38);
- outb(0x3D4, 0x60);
- outb(0x3D5, 0x07);
- outb(0x3D4, 0x61);
- outb(0x3D5, 0x80);
- outb(0x3D4, 0x62);
- outb(0x3D5, 0xA1);
- outb(0x3D4, 0x69); /* High order bits for cursor address */
- outb(0x3D5, 0);
-
- outb(0x3D4, 0x32);
- outb(0x3D5, inb(0x3D5)&~0x10);
-
- outb(0x3C2, 0x67);
-
-#if 0
- /* Initialize DAC */
- outb(0x3C6,0xFF);
- inb(0x3C7);
- outb(0x3C8,0x00);
- inb(0x3C7);
- for (i=0; i<sizeof(color_LUT); i++) {
- outb(0x3C9, color_LUT[i]);
- }
- for (i; i<768; i += 3) {
- outb(0x3C9, 0x3F); /* White? */
- outb(0x3C9, 0x3F); /* White? */
- outb(0x3C9, 0x3F); /* White? */
- }
-
- /* Load font */
- NOP(inb(0x3DA)); /* Reset Address/Data FlipFlop for Attribute ctlr */
- outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */
- outw(0x3C4, 0x0001); /* reset sequencer */
- outw(0x3C4, 0x0204); /* write to plane 2 */
- outw(0x3C4, 0x0407); /* enable plane graphics */
- outw(0x3C4, 0x0003); /* reset sequencer */
- outw(0x3CE, 0x0402); /* read plane 2 */
- outw(0x3CE, 0x0500); /* write mode 0, read mode 0 */
- outw(0x3CE, 0x0600); /* set graphics */
- for (i = 0; i < sizeof(font); i += 16) {
- for (j = 0; j < 16; j++) {
- font_page[(2*i)+j] = font[i+j];
- }
- }
-#else
- outw(0x3C4, 0x0120); /* disable video */
- setTextCLUT(2); /* load color lookup table */
- loadFont(ISA_mem); /* load font */
-#endif
-
- for (i = 0; i < 24; i++) {
- outb(0x3D4, i);
- outb(0x3D5, CRTC[i]);
- }
- for (i = 0; i < 5; i++) {
- outb(0x3C4, i);
- outb(0x3C5, SEQ[i]);
- }
- for (i = 0; i < 9; i++) {
- outb(0x3CE, i);
- outb(0x3CF, GC[i]);
- }
- value = inb(0x3DA); /* reset flip-flop */
- for (i = 0; i < 16; i++) {
- outb(0x3C0, i);
- outb(0x3C0, AC[i]);
- }
- for (i = 16; i < 21; i++) {
- outb(0x3C0, i | 0x20);
- outb(0x3C0, AC[i]);
}
- clearVideoMemory();
- outw(0x3C4, 0x0100); /* re-enable video */
- outb(0x3C2, 0x23);
- return (1); /* Keyboard should work */
}
diff --git a/arch/ppc/chrp_defconfig b/arch/ppc/chrp_defconfig
new file mode 100644
index 000000000..ccbc0f942
--- /dev/null
+++ b/arch/ppc/chrp_defconfig
@@ -0,0 +1,321 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+CONFIG_CHRP=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_MBX is not set
+CONFIG_MACH_SPECIFIC=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KERNELD=y
+CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_ABSTRACT_CONSOLE is not set
+CONFIG_PMAC_CONSOLE=y
+CONFIG_MAC_KEYBOARD=y
+# CONFIG_MAC_FLOPPY is not set
+CONFIG_MACMOUSE=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_XMON is not set
+CONFIG_CONTROL_VIDEO=y
+CONFIG_PLATINUM_VIDEO=y
+CONFIG_VALKYRIE_VIDEO=y
+CONFIG_ATY_VIDEO=y
+CONFIG_IMSTT_VIDEO=y
+CONFIG_CHIPS_VIDEO=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Floppy, IDE, and other block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_ALIAS=y
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_RARP=y
+CONFIG_IP_NOSR=y
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+CONFIG_SCSI_AIC7XXX=m
+# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set
+# CONFIG_OVERRIDE_CMDS is not set
+# CONFIG_AIC7XXX_PAGE_ENABLE is not set
+CONFIG_AIC7XXX_PROC_STATS=y
+CONFIG_AIC7XXX_RESET_DELAY=15
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_MESH=y
+CONFIG_SCSI_MESH_SYNC_RATE=10
+CONFIG_SCSI_MAC53C94=y
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_TLAN is not set
+# CONFIG_ES3210 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+CONFIG_PROC_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFSD=y
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_MOUSE is not set
+# CONFIG_UMISC is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+CONFIG_NVRAM=y
+# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
diff --git a/arch/ppc/chrpboot/main.c b/arch/ppc/chrpboot/main.c
index 629256eaa..05a2f85d3 100644
--- a/arch/ppc/chrpboot/main.c
+++ b/arch/ppc/chrpboot/main.c
@@ -16,11 +16,11 @@ void gunzip(void *, int, unsigned char *, int *);
#define get_16be(x) (*(unsigned short *)(x))
#define get_32be(x) (*(unsigned *)(x))
-#define RAM_START 0x90000000
-#define RAM_END 0x90800000 /* only 8M mapped with BATs */
+#define RAM_START 0x00000000
+#define RAM_END 0x00800000 /* only 8M mapped with BATs */
-#define RAM_FREE 0x90540000 /* after image of chrpboot */
-#define PROG_START 0x90010000
+#define RAM_FREE 0x00540000 /* after image of chrpboot */
+#define PROG_START 0x00010000
char *avail_ram;
char *end_avail;
@@ -40,7 +40,7 @@ chrpboot(int a1, int a2, void *prom)
unsigned initrd_start, initrd_size;
printf("chrpboot starting\n\r");
- setup_bats();
+ /* setup_bats(); */
if (initrd_len) {
initrd_size = initrd_len;
diff --git a/arch/ppc/chrpboot/misc.S b/arch/ppc/chrpboot/misc.S
index 11a612af1..b2094b9d3 100644
--- a/arch/ppc/chrpboot/misc.S
+++ b/arch/ppc/chrpboot/misc.S
@@ -23,10 +23,10 @@ setup_bats:
b 5f
4: ori 4,4,0xff /* set up BAT registers for 604 */
li 5,2
- mtdbatu 0,4
- mtdbatl 0,5
-5: mtibatu 0,4
- mtibatl 0,5
+ mtdbatu 3,4
+ mtdbatl 3,5
+5: mtibatu 3,4
+ mtibatl 3,5
isync
blr
diff --git a/arch/ppc/coffboot/main.c b/arch/ppc/coffboot/main.c
index ab0ee6c4a..e70844b62 100644
--- a/arch/ppc/coffboot/main.c
+++ b/arch/ppc/coffboot/main.c
@@ -18,9 +18,10 @@ void gunzip(void *, int, unsigned char *, int *);
#define get_32be(x) (*(unsigned *)(x))
#define RAM_START 0xc0000000
-#define RAM_END 0xc0800000 /* only 8M mapped with BATs */
+#define PROG_START RAM_START
+#define RAM_END (RAM_START + 0x800000) /* only 8M mapped with BATs */
-#define RAM_FREE 0xc0540000 /* after image of coffboot */
+#define RAM_FREE (RAM_START + 0x540000) /* after image of coffboot */
char *avail_ram;
char *end_avail;
@@ -47,7 +48,7 @@ coffboot(int a1, int a2, void *prom)
printf("error getting load-base\n");
exit();
}
- setup_bats();
+ setup_bats(RAM_START);
eh = (struct external_filehdr *) loadbase;
ns = get_16be(eh->f_nscns);
@@ -82,7 +83,7 @@ coffboot(int a1, int a2, void *prom)
im = (unsigned char *)(loadbase + get_32be(isect->s_scnptr));
len = get_32be(isect->s_size);
- dst = (void *) RAM_START;
+ dst = (void *) PROG_START;
if (im[0] == 0x1f && im[1] == 0x8b) {
void *cp = (void *) RAM_FREE;
@@ -98,7 +99,7 @@ coffboot(int a1, int a2, void *prom)
flush_cache(dst, len);
- sa = *(unsigned *)dst + RAM_START;
+ sa = *(unsigned *)dst + PROG_START;
printf("start address = 0x%x\n", sa);
#if 0
diff --git a/arch/ppc/coffboot/misc.S b/arch/ppc/coffboot/misc.S
index ae08ee2fa..1a9f07129 100644
--- a/arch/ppc/coffboot/misc.S
+++ b/arch/ppc/coffboot/misc.S
@@ -9,24 +9,25 @@
.text
/*
- * Use the BAT0 registers to map the 1st 8MB of RAM to 0xc0000000.
+ * Use the BAT0 registers to map the 1st 8MB of RAM to
+ * the address given as the 1st argument.
*/
.globl setup_bats
setup_bats:
+ mr 4,3
mfpvr 3
rlwinm 3,3,16,16,31 /* r3 = 1 for 601, 4 for 604 */
cmpi 0,3,1
- lis 4,0xc000
bne 4f
ori 4,4,4 /* set up BAT registers for 601 */
li 5,0x7f
b 5f
4: ori 4,4,0xff /* set up BAT registers for 604 */
li 5,2
- mtdbatu 0,4
- mtdbatl 0,5
-5: mtibatu 0,4
- mtibatl 0,5
+ mtdbatu 3,4
+ mtdbatl 3,5
+5: mtibatu 3,4
+ mtibatl 3,5
isync
blr
diff --git a/arch/ppc/common_defconfig b/arch/ppc/common_defconfig
new file mode 100644
index 000000000..4f9983126
--- /dev/null
+++ b/arch/ppc/common_defconfig
@@ -0,0 +1,331 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+# CONFIG_CHRP is not set
+CONFIG_ALL_PPC=y
+# CONFIG_MBX is not set
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KERNELD=y
+CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+CONFIG_ABSTRACT_CONSOLE=y
+CONFIG_FB=y
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB_COMPAT_XPMAC=y
+CONFIG_MAC_KEYBOARD=y
+CONFIG_MAC_FLOPPY=y
+CONFIG_MACMOUSE=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_XMON is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Floppy, IDE, and other block devices
+#
+CONFIG_BLK_DEV_FD=y
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+CONFIG_IP_ACCT=y
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_RARP=y
+# CONFIG_IP_NOSR is not set
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+# CONFIG_CPU_IS_SLOW is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
+CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
+CONFIG_SCSI_NCR53C8XX_SYNC=5
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+CONFIG_SCSI_MESH=y
+CONFIG_SCSI_MESH_SYNC_RATE=5
+CONFIG_SCSI_MAC53C94=y
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+CONFIG_LANCE=y
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+CONFIG_PCNET32=y
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_TLAN is not set
+# CONFIG_ES3210 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
+CONFIG_PPP=y
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_PROC_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFSD=y
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=y
+# CONFIG_ROMFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+
+#
+# Frame buffer devices
+#
+CONFIG_FB_OPEN_FIRMWARE=y
+# CONFIG_FB_S3TRIO is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_MFB=y
+CONFIG_FBCON_CFB8=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
+CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_MOUSE=y
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_UMISC is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_NVRAM is not set
+# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
diff --git a/arch/ppc/config.in b/arch/ppc/config.in
index 72bab8928..4ab631988 100644
--- a/arch/ppc/config.in
+++ b/arch/ppc/config.in
@@ -1,30 +1,37 @@
-# $Id: config.in,v 1.36 1997/12/29 21:36:52 geert Exp $
+# $Id: config.in,v 1.47 1998/04/10 10:19:04 geert Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
mainmenu_name "Linux/PowerPC Kernel Configuration"
+
+
mainmenu_option next_comment
comment 'Platform support'
define_bool CONFIG_PPC y
-if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then
- define_bool CONFIG_CROSSCOMPILE y
-else
- define_bool CONFIG_NATIVE y
-fi
-
-define_bool CONFIG_MACH_SPECIFIC y
-bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC
-bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP
-bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP
+#if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then
+# define_bool CONFIG_CROSSCOMPILE y
+#else
+# define_bool CONFIG_NATIVE y
+#fi
choice 'Processor type' \
- "Common CONFIG_COMMON \
- 601 CONFIG_601 \
- 603 CONFIG_603 \
- 604 CONFIG_604" Common
+ "6xx/7xx CONFIG_6xx \
+ 860/821 CONFIG_8xx" 6xx/7xx
+
+choice 'Machine Type' \
+ "PowerMac CONFIG_PMAC \
+ PReP CONFIG_PREP \
+ CHRP CONFIG_CHRP \
+ PowerMac/PReP/CHRP CONFIG_ALL_PPC \
+ APUS CONFIG_APUS \
+ MBX CONFIG_MBX" PReP
endmenu
+if [ "$CONFIG_ALL_PPC" != "y" ]; then
+ define_bool CONFIG_MACH_SPECIFIC y
+fi
+
mainmenu_option next_comment
comment 'General setup'
@@ -35,9 +42,16 @@ if [ "$CONFIG_MODULES" = "y" ]; then
bool 'Kernel module loader' CONFIG_KMOD
fi
-define_bool CONFIG_PCI y
+if [ "$CONFIG_APUS" = "y" ]; then
+ define_bool CONFIG_PCI n
+else
+ define_bool CONFIG_PCI y
+fi
if [ "$CONFIG_PREP" = "y" ]; then
- bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE
+ bool 'PCI quirks' CONFIG_PCI_QUIRKS
+ if [ "$CONFIG_PCI_QUIRKS" = "y" ]; then
+ bool ' PCI bridge optimization' CONFIG_PCI_OPTIMIZE
+ fi
fi
bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC
bool 'Networking support' CONFIG_NET
@@ -51,6 +65,14 @@ define_bool CONFIG_KERNEL_ELF y
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+ dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+ if [ "$CONFIG_PARPORT_PC" != "n" ]; then
+ bool ' Support foreign hardware' CONFIG_PARPORT_OTHER
+ fi
+fi
+
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'New unified console driver (EXPERIMENTAL)' CONFIG_ABSTRACT_CONSOLE
fi
@@ -68,6 +90,7 @@ else
# if compiling specifically for prep or chrp, or supporting all arch's
if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
bool 'Support for frame buffer devices' CONFIG_FB
+ bool 'Support for VGA devices' CONFIG_VGA_CONSOLE
bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC
else
bool 'Support for PowerMac console' CONFIG_PMAC_CONSOLE
@@ -79,13 +102,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'Support for PowerMac mouse (EXPERIMENTAL)' CONFIG_MACMOUSE
fi
bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE
+bool 'Include kgdb kernel debugger' CONFIG_KGDB
bool 'Include xmon kernel debugger' CONFIG_XMON
-if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then
- if [ "$CONFIG_FB" != "y" ]; then
- define_bool CONFIG_VGA_CONSOLE y
- fi
-else
+if [ "$CONFIG_ABSTRACT_CONSOLE" != "y" ]; then
if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then
bool 'Support for Apple "control" display' CONFIG_CONTROL_VIDEO
bool 'Support for Apple "platinum" display' CONFIG_PLATINUM_VIDEO
@@ -149,17 +169,6 @@ if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
fi
endmenu
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
source fs/nls/Config.in
diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig
index 7e5caa71d..e498b5249 100644
--- a/arch/ppc/defconfig
+++ b/arch/ppc/defconfig
@@ -6,22 +6,21 @@
# Platform support
#
CONFIG_PPC=y
-CONFIG_NATIVE=y
-CONFIG_MACH_SPECIFIC=y
+# CONFIG_6xx is not set
+CONFIG_8xx=y
# CONFIG_PMAC is not set
-CONFIG_PREP=y
+# CONFIG_PREP is not set
# CONFIG_CHRP is not set
-CONFIG_COMMON=y
+# CONFIG_ALL_PPC is not set
+CONFIG_MBX=y
+CONFIG_MACH_SPECIFIC=y
#
# General setup
#
-CONFIG_EXPERIMENTAL=y
-CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
+# CONFIG_EXPERIMENTAL is not set
+# CONFIG_MODULES is not set
CONFIG_PCI=y
-# CONFIG_PCI_OPTIMIZE is not set
CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
# CONFIG_SYSCTL is not set
@@ -31,6 +30,7 @@ CONFIG_BINFMT_ELF=y
CONFIG_KERNEL_ELF=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
# CONFIG_PMAC_CONSOLE is not set
# CONFIG_MAC_KEYBOARD is not set
# CONFIG_MAC_FLOPPY is not set
@@ -46,25 +46,17 @@ CONFIG_VGA_CONSOLE=y
#
# Floppy, IDE, and other block devices
#
-CONFIG_BLK_DEV_FD=y
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_TRITON is not set
-# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+# CONFIG_BLK_DEV_HD_ONLY is not set
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_HD is not set
#
@@ -79,83 +71,30 @@ CONFIG_BLK_DEV_INITRD=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
-# CONFIG_IP_PNP is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_ALIAS is not set
# CONFIG_SYN_COOKIES is not set
-CONFIG_INET_RARP=y
-# CONFIG_IP_NOSR is not set
-CONFIG_SKB_LARGE=y
-# CONFIG_IPV6 is not set
+# CONFIG_INET_RARP is not set
+CONFIG_IP_NOSR=y
+# CONFIG_SKB_LARGE is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
-# CONFIG_X25 is not set
-# CONFIG_LAPB is not set
-# CONFIG_BRIDGE is not set
-# CONFIG_LLC is not set
-# CONFIG_WAN_ROUTER is not set
-# CONFIG_CPU_IS_SLOW is not set
-# CONFIG_NET_SCHED is not set
#
# SCSI support
#
-CONFIG_SCSI=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_ST=y
-CONFIG_BLK_DEV_SR=y
-CONFIG_BLK_DEV_SR_VENDOR=y
-# CONFIG_CHR_DEV_SG is not set
-# CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
-
-#
-# SCSI low-level drivers
-#
-# CONFIG_SCSI_7000FASST is not set
-# CONFIG_SCSI_AHA152X is not set
-# CONFIG_SCSI_AHA1542 is not set
-# CONFIG_SCSI_AHA1740 is not set
-# CONFIG_SCSI_AIC7XXX is not set
-# CONFIG_SCSI_ADVANSYS is not set
-# CONFIG_SCSI_IN2000 is not set
-# CONFIG_SCSI_AM53C974 is not set
-# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_DTC3280 is not set
-# CONFIG_SCSI_EATA_DMA is not set
-# CONFIG_SCSI_EATA_PIO is not set
-# CONFIG_SCSI_EATA is not set
-# CONFIG_SCSI_FUTURE_DOMAIN is not set
-# CONFIG_SCSI_GENERIC_NCR5380 is not set
-# CONFIG_SCSI_NCR53C406A is not set
-# CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set
-CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
-CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
-CONFIG_SCSI_NCR53C8XX_SYNC=5
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
-# CONFIG_SCSI_PPA is not set
-# CONFIG_SCSI_PAS16 is not set
-# CONFIG_SCSI_QLOGIC_FAS is not set
-# CONFIG_SCSI_QLOGIC_ISP is not set
-# CONFIG_SCSI_SEAGATE is not set
-# CONFIG_SCSI_DC390T is not set
-# CONFIG_SCSI_T128 is not set
-# CONFIG_SCSI_U14_34F is not set
-# CONFIG_SCSI_ULTRASTOR is not set
-# CONFIG_SCSI_MESH is not set
-# CONFIG_SCSI_MAC53C94 is not set
+# CONFIG_SCSI is not set
#
# Network device support
@@ -164,34 +103,28 @@ CONFIG_NETDEVICES=y
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
# CONFIG_EQUALIZER is not set
-# CONFIG_ETHERTAP is not set
CONFIG_NET_ETHERNET=y
# CONFIG_NET_VENDOR_3COM is not set
-CONFIG_LANCE=y
+# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
# CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
-CONFIG_PCNET32=y
-# CONFIG_AC3200 is not set
-# CONFIG_APRICOT is not set
-# CONFIG_CS89x0 is not set
-CONFIG_DE4X5=y
-# CONFIG_DEC_ELCP is not set
-# CONFIG_DGRS is not set
-# CONFIG_EEXPRESS_PRO100 is not set
-# CONFIG_TLAN is not set
-# CONFIG_ES3210 is not set
-# CONFIG_ZNET is not set
+# CONFIG_NET_EISA is not set
# CONFIG_NET_POCKET is not set
# CONFIG_FDDI is not set
# CONFIG_DLCI is not set
-# CONFIG_PLIP is not set
-CONFIG_PPP=m
-# CONFIG_NET_RADIO is not set
+# CONFIG_PPP is not set
# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
# CONFIG_TR is not set
-# CONFIG_SHAPER is not set
+# CONFIG_WAN_DRIVERS is not set
+# CONFIG_LAPBETHER is not set
+# CONFIG_X25_ASY is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
#
# ISDN subsystem
@@ -202,6 +135,7 @@ CONFIG_PPP=m
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
+# CONFIG_CDROM is not set
#
# Filesystems
@@ -209,63 +143,52 @@ CONFIG_PPP=m
# CONFIG_QUOTA is not set
# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
+# CONFIG_ISO9660_FS is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
-CONFIG_PROC_FS=y
+# CONFIG_PROC_FS is not set
CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
# CONFIG_NFSD is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
# CONFIG_SMB_FS is not set
# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
# CONFIG_ROMFS_FS is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_UFS_FS is not set
# CONFIG_MAC_PARTITION is not set
-
-#
-# Native Language Support
-#
# CONFIG_NLS is not set
#
# Character devices
#
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-# CONFIG_SOFTCURSOR is not set
+# CONFIG_VT is not set
CONFIG_SERIAL=y
-CONFIG_SERIAL_EXTENDED=y
-# CONFIG_SERIAL_MANY_PORTS is not set
-# CONFIG_SERIAL_SHARE_IRQ is not set
-# CONFIG_SERIAL_MULTIPORT is not set
-# CONFIG_HUB6 is not set
CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_PRINTER is not set
-CONFIG_MOUSE=y
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
-# CONFIG_UMISC is not set
+# CONFIG_MOUSE is not set
# CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
# CONFIG_APM is not set
# CONFIG_WATCHDOG is not set
# CONFIG_RTC is not set
# CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
# CONFIG_NVRAM is not set
# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
#
# Sound
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile
index 37776109d..3561fd26a 100644
--- a/arch/ppc/kernel/Makefile
+++ b/arch/ppc/kernel/Makefile
@@ -11,14 +11,32 @@
$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o
O_TARGET := kernel.o
-O_OBJS := misc.o traps.o process.o signal.o syscalls.o \
- align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \
- time.o prep_time.o pmac_time.o chrp_time.o \
- setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
- pci.o prep_pci.o pmac_pci.o chrp_pci.o \
- residual.o prom.o
OX_OBJS := ppc_ksyms.o
+
+O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \
+ bitops.o ppc_htab.o setup.o ptrace.o align.o
+
+ifdef CONFIG_PCI
+O_OBJS += pci.o
+endif
+ifdef CONFIG_KGDB
+O_OBJS += ppc-stub.o
+endif
+
+ifeq ($(CONFIG_MBX),y)
+O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o
+else
+ifeq ($(CONFIG_APUS),y)
+O_OBJS += prom.o openpic.o
+else
+O_OBJS += prep_time.o pmac_time.o chrp_time.o \
+ prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
+ prep_pci.o pmac_pci.o chrp_pci.o \
+ residual.o prom.o openpic.o
+endif
+endif
+
ifdef SMP
O_OBJS += smp.o
endif
@@ -38,10 +56,10 @@ ppc_defs.h: mk_defs.c ppc_defs.head \
rm mk_defs.s
find_name : find_name.c
- $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c
+ $(HOSTCC) -o find_name find_name.c
checks: checks.c
- $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
+ $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
./checks
include $(TOPDIR)/Rules.make
diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c
index 39cb04f77..70284674b 100644
--- a/arch/ppc/kernel/align.c
+++ b/arch/ppc/kernel/align.c
@@ -194,9 +194,13 @@ fix_alignment(struct pt_regs *regs)
return -EFAULT; /* bad address */
}
+#ifdef __SMP__
+ if ((flags & F) && (regs->msr & MSR_FP) )
+ smp_giveup_fpu(current);
+#else
if ((flags & F) && last_task_used_math == current)
giveup_fpu();
-
+#endif
if (flags & M)
return 0; /* too hard for now */
@@ -255,12 +259,22 @@ fix_alignment(struct pt_regs *regs)
* the kernel with -msoft-float so it doesn't use the
* fp regs for copying 8-byte objects. */
case LD+F+S:
+#ifdef __SMP__
+ if (regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
giveup_fpu();
+#endif
cvt_fd(&data.f, &current->tss.fpr[reg]);
/* current->tss.fpr[reg] = data.f; */
break;
case ST+F+S:
+#ifdef __SMP__
+ if (regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
giveup_fpu();
+#endif
cvt_df(&current->tss.fpr[reg], &data.f);
/* data.f = current->tss.fpr[reg]; */
break;
diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c
index 16b379254..6036efc28 100644
--- a/arch/ppc/kernel/chrp_pci.c
+++ b/arch/ppc/kernel/chrp_pci.c
@@ -4,13 +4,13 @@
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/openpic.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/hydra.h>
#include <asm/prom.h>
@@ -22,8 +22,12 @@
volatile struct Hydra *Hydra = NULL;
-
#if 1
+/*
+ * The VLSI Golden Gate II has only 512K of PCI configuration space, so we
+ * limit the bus number to 3 bits
+ */
+
int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned char *val)
{
@@ -32,11 +36,6 @@ int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
return PCIBIOS_DEVICE_NOT_FOUND;
}
*val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset));
- if (offset == PCI_INTERRUPT_LINE) {
- /* PCI interrupts are controlled by the OpenPIC */
- if (*val)
- *val = openpic_to_irq(*val);
- }
return PCIBIOS_SUCCESSFUL;
}
@@ -228,10 +227,11 @@ __initfunc(int w83c553f_init(void))
unsigned char t8;
unsigned short t16;
unsigned int t32;
- if (pcibios_find_device(PCI_VENDOR_ID_WINBOND,
- PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev)
- == PCIBIOS_SUCCESSFUL) {
- dev++;
+ struct pci_dev *pdev;
+ if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND,
+ PCI_DEVICE_ID_WINBOND_83C553, NULL))) {
+ bus = pdev->bus->number;
+ dev = pdev->devfn + 1;
chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) {
#if 0
diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c
index 6c5d2afa5..91ca5d595 100644
--- a/arch/ppc/kernel/chrp_setup.c
+++ b/arch/ppc/kernel/chrp_setup.c
@@ -60,21 +60,6 @@ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
extern int rd_image_start; /* starting block # of image */
#endif
-
-int chrp_ide_irq = 0;
-
-void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
-{
- ide_ioreg_t port = base;
- int i = 8;
-
- while (i--)
- *p++ = port++;
- *p++ = base + 0x206;
- if (irq != NULL)
- *irq = chrp_ide_irq;
-}
-
static const char *gg2_memtypes[4] = {
"FPM", "SDRAM", "EDO", "BEDO"
};
@@ -89,6 +74,34 @@ static const char *gg2_cachemodes[4] = {
"Disabled", "Write-Through", "Copy-Back", "Transparent Mode"
};
+#if 0
+#ifdef CONFIG_BLK_DEV_IDE
+int chrp_ide_ports_known;
+ide_ioreg_t chrp_ide_regbase[MAX_HWIFS];
+ide_ioreg_t chrp_idedma_regbase; /* one for both channels */
+unsigned int chrp_ide_irq;
+
+void chrp_ide_probe(void)
+{
+}
+
+void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+ int i;
+
+ *p = 0;
+ if (base == 0)
+ return;
+ for (i = 0; i < 8; ++i)
+ *p++ = base + i * 0x10;
+ *p = base + 0x160;
+ if (irq != NULL) {
+ *irq = chrp_ide_irq;
+ }
+}
+#endif /* CONFIG_BLK_DEV_IDE */
+#endif
+
int
chrp_get_cpuinfo(char *buffer)
{
@@ -139,12 +152,63 @@ chrp_get_cpuinfo(char *buffer)
}
/* L2 cache */
t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL));
- len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n",
+ len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n",
gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3],
gg2_cachemodes[t & 3]);
return len;
}
+ /*
+ * Fixes for the National Semiconductor PC78308VUL SuperI/O
+ *
+ * Some versions of Open Firmware incorrectly initialize the IRQ settings
+ * for keyboard and mouse
+ */
+
+__initfunc(static inline void sio_write(u8 val, u8 index))
+{
+ outb(index, 0x15c);
+ outb(val, 0x15d);
+}
+
+__initfunc(static inline u8 sio_read(u8 index))
+{
+ outb(index, 0x15c);
+ return inb(0x15d);
+}
+
+__initfunc(static void sio_init(void))
+{
+ u8 irq, type;
+
+ /* select logical device 0 (KBC/Keyboard) */
+ sio_write(0, 0x07);
+ irq = sio_read(0x70);
+ type = sio_read(0x71);
+ printk("sio: Keyboard irq %d, type %d: ", irq, type);
+ if (irq == 1 && type == 3)
+ printk("OK\n");
+ else {
+ printk("remapping to irq 1, type 3\n");
+ sio_write(1, 0x70);
+ sio_write(3, 0x71);
+ }
+
+ /* select logical device 1 (KBC/Mouse) */
+ sio_write(1, 0x07);
+ irq = sio_read(0x70);
+ type = sio_read(0x71);
+ printk("sio: Mouse irq %d, type %d: ", irq, type);
+ if (irq == 12 && type == 3)
+ printk("OK\n");
+ else {
+ printk("remapping to irq 12, type 3\n");
+ sio_write(12, 0x70);
+ sio_write(3, 0x71);
+ }
+}
+
+
__initfunc(void
chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
{
@@ -191,7 +255,12 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
hydra_init(); /* Mac I/O */
w83c553f_init(); /* PCI-ISA bridge and IDE */
-#ifdef CONFIG_ABSTRACT_CONSOLE
+ /*
+ * Fix the Super I/O configuration
+ */
+ sio_init();
+
+#ifdef CONFIG_FB
/* Frame buffer device based console */
conswitchp = &fb_con;
#endif
diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c
index 73dcf9844..7109c0e5d 100644
--- a/arch/ppc/kernel/chrp_time.c
+++ b/arch/ppc/kernel/chrp_time.c
@@ -38,7 +38,7 @@ void chrp_time_init(void)
rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
if (rtcs == NULL || rtcs->addrs == NULL)
return;
- base = ((int *)rtcs->addrs)[2];
+ base = rtcs->addrs[0].address;
nvram_as1 = 0;
nvram_as0 = base;
nvram_data = base + 1;
@@ -69,7 +69,7 @@ int chrp_set_rtc_time(unsigned long nowtime)
unsigned char save_control, save_freq_select;
struct rtc_time tm;
- to_tm(nowtime + 10*60*60, &tm); /* XXX for now */
+ to_tm(nowtime, &tm);
save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */
@@ -146,7 +146,7 @@ unsigned long chrp_get_rtc_time(void)
}
if ((year += 1900) < 1970)
year += 100;
- return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */;
+ return mktime(year, mon, day, hour, min, sec);
}
@@ -155,6 +155,9 @@ void chrp_calibrate_decr(void)
struct device_node *cpu;
int freq, *fp, divisor;
+ if (via_calibrate_decr())
+ return;
+
/*
* The cpu node should have a timebase-frequency property
* to tell us the rate at which the decrementer counts.
diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S
index 2924febc5..d8540e68d 100644
--- a/arch/ppc/kernel/head.S
+++ b/arch/ppc/kernel/head.S
@@ -9,6 +9,8 @@
* Low-level exception handlers and MMU support
* rewritten by Paul Mackerras.
* Copyright (C) 1996 Paul Mackerras.
+ * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
+ * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
*
* This file contains the low-level support and setup for the
* PowerPC platform, including trap and interrupt dispatch.
@@ -29,12 +31,14 @@
#include <linux/sys.h>
#include <linux/errno.h>
#include <linux/config.h>
+#ifdef CONFIG_8xx
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/cache.h>
+#endif
#ifdef CONFIG_APUS
-/* At CYBERBASEp we'll find the following sum:
- * -KERNELBASE+CyberStormMemoryBase
- */
-#define CYBERBASEp (0xfff00000)
+#include <asm/amigappc.h>
#endif
/* optimization for 603 to load the tlb directly from the linux table */
@@ -78,6 +82,8 @@ LG_CACHE_LINE_SIZE = 5
isync
/* This instruction is not implemented on the PPC 603 or 601 */
+#ifndef CONFIG_8xx
+/* This instruction is not implemented on the PPC 603 or 601 */
#define tlbia \
li r4,128; \
mtctr r4; \
@@ -85,6 +91,7 @@ LG_CACHE_LINE_SIZE = 5
0: tlbie r4; \
addi r4,r4,0x1000; \
bdnz 0b
+#endif
#define LOAD_BAT(n, offset, reg, RA, RB) \
lwz RA,offset+0(reg); \
@@ -96,6 +103,20 @@ LG_CACHE_LINE_SIZE = 5
mtspr DBAT##n##U,RA; \
mtspr DBAT##n##L,RB
+#ifndef CONFIG_APUS
+#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h
+#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h
+#else
+#define tophys(rd,rs,rt) \
+ lis rt,CYBERBASEp@h; \
+ lwz rt,0(rt); \
+ add rd,rs,rt
+#define tovirt(rd,rs,rt) \
+ lis rt,CYBERBASEp@h; \
+ lwz rt,0(rt); \
+ sub rd,rs,rt
+#endif
+
.text
.globl _stext
_stext:
@@ -130,12 +151,44 @@ _start:
*
* This just gets a minimal mmu environment setup so we can call
* start_here() to do the real work.
- * -- Cort
+ * -- Cort
+ *
+ * MPC8xx
+ * This port was done on an MBX board with an 860. Right now I only
+ * support an ELF compressed (zImage) boot from EPPC-Bug because the
+ * code there loads up some registers before calling us:
+ * r3: ptr to board info data
+ * r4: initrd_start or if no initrd then 0
+ * r5: initrd_end - unused if r4 is 0
+ * r6: Start of command line string
+ * r7: End of command line string
+ *
+ * I decided to use conditional compilation instead of checking PVR and
+ * adding more processor specific branches around code I don't need.
+ * Since this is an embedded processor, I also appreciate any memory
+ * savings I can get.
+ *
+ * The MPC8xx does not have any BATs, but it supports large page sizes.
+ * We first initialize the MMU to support 8M byte pages, then load one
+ * entry into each of the instruction and data TLBs to map the first
+ * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to
+ * the "internal" processor registers before MMU_init is called.
+ *
+ * The TLB code currently contains a major hack. Since I use the condition
+ * code register, I have to save and restore it. I am out of registers, so
+ * I just store it in memory location 0 (the TLB handlers are not reentrant).
+ * To avoid making any decisions, I need to use the "segment" valid bit
+ * in the first level table, but that would require many changes to the
+ * Linux page directory/table functions that I don't want to do right now.
+ *
+ * I used to use SPRG2 for a temporary register in the TLB handler, but it
+ * has since been put to other uses. I now use a hack to save a register
+ * and the CCR at memory location 0.....Someday I'll fix this.....
+ * -- Dan
*/
.globl __start
__start:
-
/*
* We have to do any OF calls before we map ourselves to KERNELBASE,
* because OF may have I/O devices mapped in in that area
@@ -145,12 +198,16 @@ __start:
mr r30,r4
mr r29,r5
mr r28,r6
- mr r29,r7
+ mr r27,r7
+#ifndef CONFIG_8xx
+#ifndef CONFIG_APUS
bl prom_init
+#endif
/*
* Use the first pair of BAT registers to map the 1st 16MB
- * of RAM to KERNELBASE.
+ * of RAM to KERNELBASE. From this point on we can't safely
+ * call OF any more.
*/
mfspr r9,PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
@@ -173,17 +230,134 @@ __start:
addis r8,r8,KERNELBASE@h
addi r8,r8,2
#endif
- mtspr DBAT0U,r11
+5: mtspr DBAT0U,r11
mtspr DBAT0L,r8
-5: mtspr IBAT0U,r11
+ mtspr IBAT0U,r11
mtspr IBAT0L,r8
isync
+
+/*
+ * We need to run with _start at physical address 0.
+ * On CHRP, we are loaded at 0x10000 since OF on CHRP uses
+ * the exception vectors at 0 (and therefore this copy
+ * overwrites OF's exception vectors with our own).
+ * If the MMU is already turned on, we copy stuff to KERNELBASE,
+ * otherwise we copy it to 0.
+ */
+ bl reloc_offset
+ mr r26,r3
+ addis r4,r3,KERNELBASE@h /* current address of _start */
+ cmpwi 0,r4,0 /* are we already running at 0? */
+ beq 2f /* assume it's OK if so */
+ li r3,0
+ mfmsr r0
+ andi. r0,r0,MSR_DR /* MMU enabled? */
+ beq 7f
+ lis r3,KERNELBASE@h /* if so, are we */
+ cmpw 0,r4,r3 /* already running at KERNELBASE? */
+ beq 2f
+ rlwinm r4,r4,0,8,31 /* translate source address */
+ add r4,r4,r3 /* to region mapped with BATs */
+7: addis r9,r26,klimit@ha /* fetch klimit */
+ lwz r25,klimit@l(r9)
+ addis r25,r25,-KERNELBASE@h
+ li r6,0 /* Destination */
+#ifdef CONFIG_APUS
+ lis r9,0x6170
+ ori r9,r9,0x7573
+ cmpw 0,r9,r31
+ bne 8f
+ lis r6,0xfff0 /* Copy to 0xfff00000 on APUS */
+8:
+#endif
+ li r5,0x4000 /* # bytes of memory to copy */
+ bl copy_and_flush /* copy the first 0x4000 bytes */
+#ifdef CONFIG_APUS
+ cmpw 0,r9,r31 /* That's all we need on APUS. */
+ beq 2f
+#endif
+ addi r0,r3,4f@l /* jump to the address of 4f */
+ mtctr r0 /* in copy and do the rest. */
+ bctr /* jump to the copy */
+4: mr r5,r25
+ bl copy_and_flush /* copy the rest */
+2:
/*
* we now have the 1st 16M of ram mapped with the bats.
* prep needs the mmu to be turned on here, but pmac already has it on.
* this shouldn't bother the pmac since it just gets turned on again
* as we jump to our code at KERNELBASE. -- Cort
*/
+
+#else /* CONFIG_8xx */
+ tlbia /* Invalidate all TLB entries */
+ li r8, 0
+ mtspr MI_CTR, r8 /* Set instruction control to zero */
+ lis r8, MD_RESETVAL@h
+ mtspr MD_CTR, r8 /* Set data TLB control */
+
+ /* Now map the lower 8 Meg into the TLBs. For this quick hack,
+ * we can load the instruction and data TLB registers with the
+ * same values.
+ */
+ lis r8, KERNELBASE@h /* Create vaddr for TLB */
+ ori r8, r8, MI_EVALID /* Mark it valid */
+ mtspr MI_EPN, r8
+ mtspr MD_EPN, r8
+ li r8, MI_PS8MEG /* Set 8M byte page */
+ ori r8, r8, MI_SVALID /* Make it valid */
+ mtspr MI_TWC, r8
+ mtspr MD_TWC, r8
+ li r8, MI_BOOTINIT /* Create RPN for address 0 */
+ mtspr MI_RPN, r8 /* Store TLB entry */
+ mtspr MD_RPN, r8
+ lis r8, MI_Kp@h /* Set the protection mode */
+ mtspr MI_AP, r8
+ mtspr MD_AP, r8
+#ifdef CONFIG_MBX
+ /* Map another 8 MByte at 0xfa000000 to get the processor
+ * internal registers (among other things).
+ */
+ lis r8, 0xfa000000@h /* Create vaddr for TLB */
+ ori r8, r8, MD_EVALID /* Mark it valid */
+ mtspr MD_EPN, r8
+ li r8, MD_PS8MEG /* Set 8M byte page */
+ ori r8, r8, MD_SVALID /* Make it valid */
+ mtspr MD_TWC, r8
+ lis r8, 0xfa000000@h /* Create paddr for TLB */
+ ori r8, r8, MI_BOOTINIT
+ mtspr MD_RPN, r8
+#endif
+
+ /* Since the cache is enabled according to the information we
+ * just loaded into the TLB, invalidate and enable the caches here.
+ * We should probably check/set other modes....later.
+ */
+ lis r8, IDC_INVALL@h
+ mtspr IC_CST, r8
+ mtspr DC_CST, r8
+ lis r8, IDC_ENABLE@h
+ mtspr IC_CST, r8
+#ifdef notdef
+ mtspr DC_CST, r8
+#else
+ /* I still have a bug somewhere because the Ethernet driver
+ * does not want to work with copyback enabled. For now,
+ * at least enable write through.
+ */
+#if 0
+ lis r8, DC_SFWT@h
+ mtspr DC_CST, r8
+ lis r8, IDC_ENABLE@h
+ mtspr DC_CST, r8
+#endif
+#endif
+
+/* We now have the lower 8 Meg mapped into TLB entries, and the caches
+ * ready to work.
+ */
+#endif /* CONFIG_8xx */
+
mfmsr r0
ori r0,r0,MSR_DR|MSR_IR
mtspr SRR1,r0
@@ -202,48 +376,6 @@ __start:
#define STACK_UNDERHEAD 64
/*
- * Macros for storing registers into and loading registers from
- * exception frames.
- */
-#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base)
-#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base)
-#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base)
-#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base)
-#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base)
-#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base)
-#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base)
-#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base)
-#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base)
-#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base)
-
-#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base)
-#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base)
-#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
-#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base)
-#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base)
-#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base)
-#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base)
-#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base)
-#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base)
-#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base)
-#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base)
-#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base)
-
-#ifndef CONFIG_APUS
-#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h
-#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h
-#else
-#define tophys(rd,rs,rt) \
- lis rt,CYBERBASEp@h; \
- lwz rt,0(rt); \
- add rd,rs,rt
-#define tovirt(rd,rs,rt) \
- lis rt,CYBERBASEp@h; \
- lwz rt,0(rt); \
- sub rd,rs,rt
-#endif
-
-/*
* Exception entry code. This code runs with address translation
* turned off, i.e. using physical addresses.
* We assume sprg3 has the physical address of the current
@@ -303,11 +435,15 @@ label: \
/* Machine check */
STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
-/* Data access exception */
+/* Data access exception.
+ * This is "never generated" by the MPC8xx. We jump to it for other
+ * translation errors.
+ */
. = 0x300
DataAccess:
EXCEPTION_PROLOG
mfspr r20,DSISR
+#ifndef CONFIG_8xx
andis. r0,r20,0xa470 /* weird error? */
bne 1f /* if not, try to put a PTE */
mfspr r3,DAR /* into the hash table */
@@ -315,6 +451,7 @@ DataAccess:
rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */
mfspr r5,SPRG3 /* phys addr of TSS */
bl hash_page
+#endif
1: stw r20,_DSISR(r21)
mr r5,r20
mfspr r4,DAR
@@ -326,10 +463,14 @@ DataAccess:
.long do_page_fault
.long int_return
-/* Instruction access exception */
+/* Instruction access exception.
+ * This is "never generated" by the MPC8xx. We jump to it for other
+ * translation errors.
+ */
. = 0x400
InstructionAccess:
EXCEPTION_PROLOG
+#ifndef CONFIG_8xx
andis. r0,r23,0x4000 /* no pte found? */
beq 1f /* if so, try to put a PTE */
mr r3,r22 /* into the hash table */
@@ -337,6 +478,7 @@ InstructionAccess:
mr r20,r23 /* SRR1 has reason bits */
mfspr r5,SPRG3 /* phys addr of TSS */
bl hash_page
+#endif
1: addi r3,r1,STACK_FRAME_OVERHEAD
mr r4,r22
mr r5,r23
@@ -347,7 +489,38 @@ InstructionAccess:
.long int_return
/* External interrupt */
- STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ)
+ . = 0x500;
+HardwareInterrupt:
+ EXCEPTION_PROLOG;
+#ifdef CONFIG_APUS
+ mfmsr 20
+ xori r20,r20,MSR_DR
+ sync
+ mtmsr r20
+ sync
+
+ lis r3,APUS_IPL_EMU@h
+
+ li r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT)
+ stb r20,APUS_IPL_EMU@l(r3)
+ sync
+
+ lbz r3,APUS_IPL_EMU@l(r3)
+
+ mfmsr r20
+ xori r20,r20,MSR_DR
+ sync
+ mtmsr r20
+ sync
+
+ stw r3,(_CCR+4)(r21);
+#endif
+ addi r3,r1,STACK_FRAME_OVERHEAD;
+ li r20,MSR_KERNEL;
+ bl transfer_to_handler;
+ .long do_IRQ;
+ .long int_return
+
/* Alignment exception */
. = 0x600
@@ -375,6 +548,7 @@ ProgramCheck:
.long ProgramCheckException
.long int_return
+#ifndef CONFIG_8xx
/* Floating-point unavailable */
. = 0x800
FPUnavailable:
@@ -384,6 +558,11 @@ FPUnavailable:
bl transfer_to_handler /* if from kernel, take a trap */
.long KernelFP
.long int_return
+#else
+/* No FPU on MPC8xx. This exception is not supposed to happen.
+*/
+ STD_EXCEPTION(0x800, FPUnavailable, UnknownException)
+#endif
STD_EXCEPTION(0x900, Decrementer, timer_interrupt)
STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
@@ -406,6 +585,7 @@ SystemCall:
STD_EXCEPTION(0xe00, Trap_0e, UnknownException)
STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
+#ifndef CONFIG_8xx
/*
* Handle TLB miss for instruction on 603/603e.
* Note: we get an alternate set of r0 - r3 to use automatically.
@@ -502,7 +682,14 @@ InstructionAddressInvalid:
sync /* Some chip revs have problems here... */
mtmsr r0
b InstructionAccess
+#else
+/* On the MPC8xx, this is a software emulation interrupt. It occurs
+ * for all unimplemented and illegal instructions.
+ */
+ STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation)
+#endif
+#ifndef CONFIG_8xx
/*
* Handle TLB miss for DATA Load operation on 603/603e
*/
@@ -598,12 +785,78 @@ DataAddressInvalid:
sync /* Some chip revs have problems here... */
mtmsr r0
b DataAccess
+#else
+/*
+ * For the MPC8xx, this is a software tablewalk to load the instruction
+ * TLB. It is modelled after the example in the Motorola manual. The task
+ * switch loads the M_TWB register with the pointer to the first level table.
+ * If we discover there is no second level table (the value is zero), the
+ * plan was to load that into the TLB, which causes another fault into the
+ * TLB Error interrupt where we can handle such problems. However, that did
+ * not work, so if we discover there is no second level table, we restore
+ * registers and branch to the error exception. We have to use the MD_xxx
+ * registers for the tablewalk because the equivalent MI_xxx registers
+ * only perform the attribute functions.
+ */
+InstructionTLBMiss:
+ mtspr M_TW, r20 /* Save a couple of working registers */
+ mfcr r20
+ stw r20, 0(r0)
+ stw r21, 4(r0)
+ mfspr r20, SRR0 /* Get effective address of fault */
+ mtspr MD_EPN, r20 /* Have to use MD_EPN for walk, MI_EPN can't */
+ mfspr r20, M_TWB /* Get level 1 table entry address */
+ lwz r21, 0(r20) /* Get the level 1 entry */
+ rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */
+ beq 2f /* If zero, don't try to find a pte */
+
+ /* We have a pte table, so load the MI_TWC with the attributes
+ * for this page, which has only bit 31 set.
+ */
+ tophys(r21,r21,0)
+ ori r21,r21,1 /* Set valid bit */
+ mtspr MI_TWC, r21 /* Set page attributes */
+ mtspr MD_TWC, r21 /* Load pte table base address */
+ mfspr r21, MD_TWC /* ....and get the pte address */
+ lwz r21, 0(r21) /* Get the pte */
+
+ /* Set four subpage valid bits (24, 25, 26, and 27).
+ * Since we currently run MI_CTR.PPCS = 0, the manual says,
+ * "If the page size is larger than 4k byte, then all the
+ * 4 bits should have the same value."
+ * I don't really know what to do if the page size is 4k Bytes,
+ * but I know setting them all to 0 does not work, and setting them
+ * all to 1 does, so that is the way it is right now.
+ * BTW, these four bits map to the software only bits in the
+ * linux page table. I used to turn them all of, but now just
+ * set them all for the hardware.
+ li r20, 0x00f0
+ andc r20, r21, r20
+ ori r20, r20, 0x0080
+ */
+ ori r20, r21, 0x00f0
+ mtspr MI_RPN, r20 /* Update TLB entry */
+
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ rfi
+
+2: mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ b InstructionAccess
+#endif /* CONFIG_8xx */
+
/*
* Handle TLB miss for DATA Store on 603/603e
*/
. = 0x1200
DataStoreTLBMiss:
+#ifndef CONFIG_8xx
#ifdef NO_RELOAD_HTAB
/*
* r0: stored ctr
@@ -671,27 +924,164 @@ DataStoreTLBMiss:
ori r3,r3,0x40 /* Set secondary hash */
b 00b /* Try lookup again */
#endif /* NO_RELOAD_HTAB */
-
+#else /* CONFIG_8xx */
+ mtspr M_TW, r20 /* Save a couple of working registers */
+ mfcr r20
+ stw r20, 0(r0)
+ stw r21, 4(r0)
+ mfspr r20, M_TWB /* Get level 1 table entry address */
+ lwz r21, 0(r20) /* Get the level 1 entry */
+ rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */
+ beq 2f /* If zero, don't try to find a pte */
+
+ /* We have a pte table, so load fetch the pte from the table.
+ */
+ tophys(r21, r21, 0)
+ ori r21, r21, 1 /* Set valid bit in physical L2 page */
+ mtspr MD_TWC, r21 /* Load pte table base address */
+ mfspr r21, MD_TWC /* ....and get the pte address */
+ lwz r21, 0(r21) /* Get the pte */
+
+ /* Set four subpage valid bits (24, 25, 26, and 27).
+ * Since we currently run MD_CTR.PPCS = 0, the manual says,
+ * "If the page size is larger than 4k byte, then all the
+ * 4 bits should have the same value."
+ * I don't really know what to do if the page size is 4k Bytes,
+ * but I know setting them all to 0 does not work, and setting them
+ * all to 1 does, so that is the way it is right now.
+ * BTW, these four bits map to the software only bits in the
+ * linux page table. I used to turn them all of, but now just
+ * set them all for the hardware.
+ li r20, 0x00f0
+ andc r20, r21, r20
+ ori r20, r20, 0x0080
+ */
+ ori r20, r21, 0x00f0
+ mtspr MD_RPN, r20 /* Update TLB entry */
+
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ rfi
+
+2: mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ b DataAccess
+#endif /* CONFIG_8xx */
+
+#ifndef CONFIG_8xx
/* Instruction address breakpoint exception (on 603/604) */
STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint)
+#else
+
+/* This is an instruction TLB error on the MPC8xx. This could be due
+ * to many reasons, such as executing guarded memory or illegal instruction
+ * addresses. There is nothing to do but handle a big time error fault.
+ */
+ . = 0x1300
+InstructionTLBError:
+ b InstructionAccess
+#endif
/* System management exception (603?) */
+#ifndef CONFIG_8xx
STD_EXCEPTION(0x1400, Trap_14, UnknownException)
+#else
+
+/* This is the data TLB error on the MPC8xx. This could be due to
+ * many reasons, including a dirty update to a pte. We can catch that
+ * one here, but anything else is an error. First, we track down the
+ * Linux pte. If it is valid, write access is allowed, but the
+ * page dirty bit is not set, we will set it and reload the TLB. For
+ * any other case, we bail out to a higher level function that can
+ * handle it.
+ */
+ . = 0x1400
+DataTLBError:
+ mtspr M_TW, r20 /* Save a couple of working registers */
+ mfcr r20
+ stw r20, 0(r0)
+ stw r21, 4(r0)
+
+ /* First, make sure this was a store operation.
+ */
+ mfspr r20, DSISR
+ andis. r21, r20, 0x0200 /* If set, indicates store op */
+ beq 2f
+
+ mfspr r20, M_TWB /* Get level 1 table entry address */
+ lwz r21, 0(r20) /* Get the level 1 entry */
+ rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */
+ beq 2f /* If zero, bail */
+
+ /* We have a pte table, so fetch the pte from the table.
+ */
+ tophys(r21, r21, 0)
+ ori r21, r21, 1 /* Set valid bit in physical L2 page */
+ mtspr MD_TWC, r21 /* Load pte table base address */
+ mfspr r21, MD_TWC /* ....and get the pte address */
+ lwz r21, 0(r21) /* Get the pte */
+
+ andi. r20, r21, _PAGE_RW /* Is it writeable? */
+ beq 2f /* Bail out if not */
+
+ ori r21, r21, _PAGE_DIRTY /* Update changed bit */
+ mfspr r20, MD_TWC /* Get pte address again */
+ stw r21, 0(r20) /* and update pte in table */
+
+ /* Set four subpage valid bits (24, 25, 26, and 27).
+ * Since we currently run MD_CTR.PPCS = 0, the manual says,
+ * "If the page size is larger than 4k byte, then all the
+ * 4 bits should have the same value."
+ * I don't really know what to do if the page size is 4k Bytes,
+ * but I know setting them all to 0 does not work, and setting them
+ * all to 1 does, so that is the way it is right now.
+ * BTW, these four bits map to the software only bits in the
+ * linux page table. I used to turn them all of, but now just
+ * set them all for the hardware.
+ li r20, 0x00f0
+ andc r20, r21, r20
+ ori r20, r20, 0x0080
+ */
+ ori r20, r21, 0x00f0
+
+ mtspr MD_RPN, r20 /* Update TLB entry */
+
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ rfi
+2:
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ b DataAccess
+#endif /* CONFIG_8xx */
STD_EXCEPTION(0x1500, Trap_15, UnknownException)
STD_EXCEPTION(0x1600, Trap_16, UnknownException)
- STD_EXCEPTION(0x1700, Trap_17, UnknownException)
+ STD_EXCEPTION(0x1700, Trap_17, TAUException)
STD_EXCEPTION(0x1800, Trap_18, UnknownException)
STD_EXCEPTION(0x1900, Trap_19, UnknownException)
STD_EXCEPTION(0x1a00, Trap_1a, UnknownException)
STD_EXCEPTION(0x1b00, Trap_1b, UnknownException)
+/* On the MPC8xx, these next four traps are used for development
+ * support of breakpoints and such. Someday I will get around to
+ * using them.
+ */
STD_EXCEPTION(0x1c00, Trap_1c, UnknownException)
STD_EXCEPTION(0x1d00, Trap_1d, UnknownException)
STD_EXCEPTION(0x1e00, Trap_1e, UnknownException)
STD_EXCEPTION(0x1f00, Trap_1f, UnknownException)
-/* Run mode exception */
+#ifndef CONFIG_8xx
+ /* Run mode exception */
STD_EXCEPTION(0x2000, RunMode, RunModeException)
STD_EXCEPTION(0x2100, Trap_21, UnknownException)
@@ -711,6 +1101,9 @@ DataStoreTLBMiss:
STD_EXCEPTION(0x2f00, Trap_2f, UnknownException)
. = 0x3000
+#else
+ . = 0x2000
+#endif
/*
* This code finishes saving the registers to the exception frame
@@ -720,6 +1113,8 @@ DataStoreTLBMiss:
.globl transfer_to_handler
transfer_to_handler:
stw r22,_NIP(r21)
+ lis r22,MSR_POW@h
+ andc r23,r23,r22
stw r23,_MSR(r21)
SAVE_GPR(7, r21)
SAVE_4GPRS(8, r21)
@@ -768,6 +1163,7 @@ stack_ovf:
SYNC
rfi
+#ifndef CONFIG_8xx
/*
* Continuation of the floating-point unavailable handler.
*/
@@ -790,9 +1186,18 @@ load_up_fpu:
ori r5,r5,MSR_FP
SYNC
mtmsr r5 /* enable use of fpu now */
+#ifndef __SMP__
SYNC
cmpi 0,r4,0
beq 1f
+#else
+/*
+ * All the saving of last_task_used_math is handled
+ * by a switch_to() call to smp_giveup_fpu() in SMP so
+ * last_task_used_math is not used. -- Cort
+ */
+ b 1f
+#endif
add r4,r4,r6
addi r4,r4,TSS /* want TSS of last_task_used_math */
SAVE_32FPRS(0, r4)
@@ -810,9 +1215,11 @@ load_up_fpu:
lfd fr0,TSS_FPSCR-4(r5)
mtfsf 0xff,fr0
REST_32FPRS(0, r5)
+#ifndef __SMP__
subi r4,r5,TSS
sub r4,r4,r6
stw r4,last_task_used_math@l(r3)
+#endif /* __SMP__ */
/* restore registers and return */
lwz r3,_CCR(r21)
lwz r4,_LINK(r21)
@@ -859,16 +1266,6 @@ Hash_msk = (((1 << Hash_bits) - 1) * 64)
.globl hash_page
hash_page:
-#ifdef __SMP__
- lis r6,hash_table_lock@h
- ori r6,r6,hash_table_lock@l
- tophys(r6,r6,r2)
-1011: lwarx r0,0,r6
- stwcx. r6,0,r6
- bne- 1011b
- cmpi 0,r0,0
- bne 1011b
-#endif /* __SMP__ */
/* Get PTE (linux-style) and check access */
lwz r5,PG_TABLES(r5)
tophys(r5,r5,r2) /* convert to phys addr */
@@ -1018,7 +1415,6 @@ found_slot:
lwz r3,0(r2)
addi r3,r3,1
stw r3,0(r2)
- SYNC
/* Return from the exception */
lwz r3,_CCR(r21)
@@ -1027,19 +1423,14 @@ found_slot:
mtcrf 0xff,r3
mtlr r4
mtctr r5
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- tophys(r5,r5,r6)
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
REST_GPR(0, r21)
REST_2GPRS(1, r21)
REST_4GPRS(3, r21)
/* we haven't used xer */
+ SYNC
mtspr SRR1,r23
mtspr SRR0,r22
+ SYNC
REST_GPR(20, r21)
REST_2GPRS(22, r21)
lwz r21,GPR21(r21)
@@ -1047,16 +1438,37 @@ found_slot:
rfi
hash_page_out:
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- tophys(r5,r5,r6)
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
blr
next_slot:
.long 0
+#endif /* CONFIG_8xx */
+
+#ifndef CONFIG_APUS
+/*
+ * Copy routine used to copy the kernel to start at physical address 0
+ * and flush and invalidate the caches as needed.
+ * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset
+ * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5.
+ */
+copy_and_flush:
+ addi r5,r5,-4
+ addi r6,r6,-4
+4: li r0,8
+ mtctr r0
+3: addi r6,r6,4 /* copy a cache line */
+ lwzx r0,r6,r4
+ stwx r0,r6,r3
+ bdnz 3b
+ dcbst r6,r3 /* write it to memory */
+ sync
+ icbi r6,r3 /* flush the icache line */
+ cmplw 0,r6,r5
+ blt 4b
+ isync
+ addi r5,r5,4
+ addi r6,r6,4
+ blr
+#endif
#ifdef CONFIG_APUS
/* On APUS the first 0x4000 bytes of the kernel will be mapped
@@ -1072,6 +1484,7 @@ next_slot:
*/
start_here:
+#ifndef CONFIG_8xx
/*
* Enable caches and 604-specific features if necessary.
*/
@@ -1108,6 +1521,7 @@ start_here:
ori r11,r11,HID0_BTCD
5: mtspr HID0,r11 /* superscalar exec & br history tbl */
4:
+#endif /* CONFIG_8xx */
/* ptr to current */
lis r2,init_task_union@h
ori r2,r2,init_task_union@l
@@ -1140,6 +1554,9 @@ start_here:
mr r6,r28
mr r7,r27
bl identify_machine
+#ifdef CONFIG_MBX
+ bl set_mbx_memory
+#endif
bl MMU_init
/*
@@ -1147,8 +1564,19 @@ start_here:
* for SDR1 (hash table pointer) and the segment registers
* and change to using our exception vectors.
*/
+#ifndef CONFIG_8xx
lis r6,_SDR1@ha
lwz r6,_SDR1@l(r6)
+#else
+ /* The right way to do this would be to track it down through
+ * init's TSS like the context switch code does, but this is
+ * easier......until someone changes init's static structures.
+ */
+ lis r6, swapper_pg_dir@h
+ tophys(r6,r6,0)
+ ori r6, r6, swapper_pg_dir@l
+ mtspr M_TWB, r6
+#endif
lis r4,2f@h
ori r4,r4,2f@l
tophys(r4,r4,r3)
@@ -1158,8 +1586,10 @@ start_here:
rfi
/* Load up the kernel context */
2:
+
SYNC /* Force all PTE updates to finish */
tlbia /* Clear all TLB entries */
+#ifndef CONFIG_8xx
mtspr SDR1,r6
li r0,16 /* load up segment register values */
mtctr r0 /* for context 0 */
@@ -1179,7 +1609,8 @@ start_here:
LOAD_BAT(1,16,r3,r4,r5)
LOAD_BAT(2,32,r3,r4,r5)
LOAD_BAT(3,48,r3,r4,r5)
-
+#endif /* CONFIG_8xx */
+
/* Set up for using our exception vectors */
/* ptr to phys current tss */
tophys(r4,r2,r4)
@@ -1188,36 +1619,6 @@ start_here:
li r3,0
mtspr SPRG2,r3 /* 0 => r1 has kernel sp */
-/* On CHRP copy exception vectors down to 0 */
- lis r5,_stext@ha
- addi r5,r5,_stext@l
- addis r5,r5,-KERNELBASE@h
- cmpwi 0,r5,0
- beq 77f /* vectors are already at 0 */
- li r3,0x1000
- mtctr r3
- li r4,-4
- addi r5,r5,-4
-74: lwzu r0,4(r5)
- stwu r0,4(r4)
- bdnz 74b
- /* need to flush/invalidate caches too */
- li r3,0x4000/CACHE_LINE_SIZE
- li r4,0
- mtctr r3
-73: dcbst 0,r4
- addi r4,r4,CACHE_LINE_SIZE
- bdnz 73b
- sync
- li r4,0
- mtctr r3
-72: icbi 0,r4
- addi r4,r4,CACHE_LINE_SIZE
- bdnz 72b
- sync
- isync
-77:
-
/* Now turn on the MMU for real! */
li r4,MSR_KERNEL
lis r3,start_kernel@h
@@ -1227,29 +1628,6 @@ start_here:
rfi /* enable MMU and jump to start_kernel */
- .globl reset_SDR1
-reset_SDR1:
- lis r6,_SDR1@ha
- lwz r6,_SDR1@l(r6)
- mfmsr r5
- li r4,0
- ori r4,r4,MSR_EE|MSR_IR|MSR_DR
- andc r3,r5,r4
- lis r4,2f@h
- ori r4,r4,2f@l
- tophys(r4,r4,r5)
- mtspr SRR0,r4
- mtspr SRR1,r3
- rfi
-2: /* load new SDR1 */
- tlbia
- mtspr SDR1,r6
- /* turn the mmu back on */
- mflr r3
- mtspr SRR0,r3
- mtspr SRR1,r5
- rfi
-
/*
* FP unavailable trap from kernel - print a message, but let
* the task use FP in the kernel until it returns to user mode.
@@ -1272,10 +1650,19 @@ KernelFP:
* and save its floating-point registers in its thread_struct.
* Enables the FPU for use in the kernel on return.
*/
+/* smp_giveup_fpu() takes an arg to tell it where to save the fpu
+ * regs since last_task_used_math can't be trusted (many many race
+ * conditions). -- Cort
+ */
+ .globl smp_giveup_fpu
+smp_giveup_fpu:
+ mr r4,r3
+ b 12f
.globl giveup_fpu
giveup_fpu:
lis r3,last_task_used_math@ha
lwz r4,last_task_used_math@l(r3)
+12:
mfmsr r5
ori r5,r5,MSR_FP
SYNC
@@ -1284,8 +1671,10 @@ giveup_fpu:
cmpi 0,r4,0
beqlr- /* if no previous owner, done */
addi r4,r4,TSS /* want TSS of last_task_used_math */
+#ifndef __SMP__
li r5,0
stw r5,last_task_used_math@l(r3)
+#endif /* __SMP__ */
SAVE_32FPRS(0, r4)
mffs fr0
stfd fr0,TSS_FPSCR-4(r4)
@@ -1445,6 +1834,18 @@ syscall_ret_2:
*
* The code which creates the new task context is in 'copy_thread'
* in arch/ppc/kernel/process.c
+ *
+ * The MPC8xx has something that currently happens "automagically."
+ * Unshared user space address translations are subject to ASID (context)
+ * match. During each task switch, the ASID is incremented. We can
+ * guarantee (I hope :-) that no entries currently match this ASID
+ * because every task will cause at least a TLB entry to be loaded for
+ * the first instruction and data access, plus the kernel running will
+ * have displaced several more TLBs. The MMU contains 32 entries for
+ * each TLB, and there are 16 contexts, so we just need to make sure
+ * two pages get replaced for every context switch, which currently
+ * happens. There are other TLB management techniques that I will
+ * eventually implement, but this is the easiest for now. -- Dan
*/
_GLOBAL(_switch)
stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1)
@@ -1476,6 +1877,7 @@ _GLOBAL(_switch)
SYNC
lwz r1,KSP(r4) /* Load new stack pointer */
addi r2,r4,-TSS /* Update current */
+#ifndef CONFIG_8xx
/* Set up segment registers for new task */
rlwinm r5,r5,4,8,27 /* VSID = context << 4 */
addis r5,r5,0x6000 /* Set Ks, Ku bits */
@@ -1486,9 +1888,32 @@ _GLOBAL(_switch)
addi r5,r5,1 /* next VSID */
addis r3,r3,0x1000 /* address of next segment */
bdnz 3b
+#else
+/* On the MPC8xx, we place the physical address of the new task
+ * page directory loaded into the MMU base register, and set the
+ * ASID compare register with the new "context".
+ */
+ mtspr M_CASID, r5 /* Update context */
+ lwz r5,MM-TSS(r4) /* Get virtual address of mm */
+ lwz r5,PGD(r5) /* get new->mm->pgd */
+ tophys(r5, r5, 0) /* convert to phys addr */
+ mtspr M_TWB, r5 /* Update MMU base address */
+#endif
SYNC
/* FALL THROUGH into int_return */
+#ifdef __SMP__
+ /* drop scheduler_lock since we weren't called by schedule() */
+ lwz r5,TSS_SMP_FORK_RET(r4)
+ cmpi 0,r5,0
+ beq+ int_return
+ li r3,0
+ lis r5,scheduler_lock@ha
+ stw r3,TSS_SMP_FORK_RET(r4)
+ stw r3,scheduler_lock@l+4(r5) /* owner_pc */
+ stw r3,scheduler_lock@l+8(r5) /* owner_cpu */
+ stw r3,scheduler_lock@l(r5) /* lock */
+#endif /* __SMP__ */
/*
* Trap exit.
@@ -1566,6 +1991,18 @@ int_return:
SYNC
rfi
+#if 0/*def __SMP__*/
+ .globl ret_from_smpfork
+ret_from_smpfork:
+ /* drop scheduler_lock since schedule() called us */
+ lis r4,scheduler_lock@ha
+ li r5,0
+ stw r5,scheduler_lock@l+4(r4) /* owner_pc */
+ stw r5,scheduler_lock@l+8(r4) /* owner_cpu */
+ stw r5,scheduler_lock@l(r4) /* lock */
+ b int_return
+#endif /* __SMP__ */
+
/*
* Fake an interrupt from kernel mode.
* This is used when enable_irq loses an interrupt.
@@ -1686,6 +2123,7 @@ _GLOBAL(flush_page_to_ram)
* Flush entries from the hash table with VSIDs in the range
* given.
*/
+#ifndef CONFIG_8xx
_GLOBAL(flush_hash_segments)
#ifdef NO_RELOAD_HTAB
/*
@@ -1700,15 +2138,6 @@ _GLOBAL(flush_hash_segments)
rlwnm. r0,r9,r0,0,0
bne 99f
#endif /* NO_RELOAD_HTAB */
-#ifdef __SMP__
- lis r6,hash_table_lock@h
- ori r6,r6,hash_table_lock@l
-1011: lwarx r0,0,r6
- stwcx. r6,0,r6
- bne- 1011b
- cmpi 0,r0,0
- bne 1011b
-#endif /* __SMP__ */
rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */
oris r3,r3,0x8000 /* set V bit */
rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */
@@ -1730,12 +2159,6 @@ _GLOBAL(flush_hash_segments)
stw r0,0(r5) /* invalidate entry */
2: bdnz 1b /* continue with loop */
sync
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
99: tlbia
isync
blr
@@ -1753,15 +2176,6 @@ _GLOBAL(flush_hash_page)
rlwnm. r0,r9,r0,0,0
bne 99f
#endif /* NO_RELOAD_HTAB */
-#ifdef __SMP__
- lis r6,hash_table_lock@h
- ori r6,r6,hash_table_lock@l
-1011: lwarx r0,0,r6
- stwcx. r6,0,r6
- bne- 1011b
- cmpi 0,r0,0
- bne 1011b
-#endif /* __SMP__ */
rlwinm r3,r3,11,1,20 /* put context into vsid */
rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */
oris r3,r3,0x8000 /* set V (valid) bit */
@@ -1794,22 +2208,17 @@ _GLOBAL(flush_hash_page)
3: li r0,0
stw r0,0(r7) /* invalidate entry */
4: sync
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
99: tlbie r4 /* in hw tlb too */
isync
blr
-
+#endif /* CONFIG_8xx */
/*
* This routine is just here to keep GCC happy - sigh...
*/
_GLOBAL(__main)
blr
+#ifndef CONFIG_8xx
/*
* On CHRP, the Run-Time Abstraction Services (RTAS) have to be
* called with the MMU off.
@@ -1819,9 +2228,9 @@ enter_rtas:
stwu r1,-16(r1)
mflr r0
stw r0,20(r1)
- addis r3,r3,-KERNELBASE@h
lis r4,rtas_data@ha
lwz r4,rtas_data@l(r4)
+ addis r4,r4,-KERNELBASE@h
lis r6,1f@ha /* physical return address for rtas */
addi r6,r6,1f@l
addis r6,r6,-KERNELBASE@h
@@ -1829,14 +2238,15 @@ enter_rtas:
addis r7,r7,-KERNELBASE@h
lis r8,rtas_entry@ha
lwz r8,rtas_entry@l(r8)
+ addis r5,r8,-KERNELBASE@h
mfmsr r9
stw r9,8(r1)
- li r0,0
ori r0,r0,MSR_EE|MSR_SE|MSR_BE
andc r0,r9,r0
andi. r9,r9,MSR_ME|MSR_RI
sync /* disable interrupts so SRR0/1 */
mtmsr r0 /* don't get trashed */
+ li r6,0
mtlr r6
mtspr SPRG2,r7
mtspr SRR0,r8
@@ -1850,23 +2260,26 @@ enter_rtas:
mtspr SRR0,r8
mtspr SRR1,r9
rfi /* return to caller */
+#endif /* CONFIG_8xx */
+#ifdef CONFIG_8xx
+/* This is called during an exec when new page tables are created.
+ * It maps to the SET_PAGE_DIR macro. I guess I should make it an
+ * inline function.
+ */
+_GLOBAL(set_page_dir)
+ addis r3,r3,-KERNELBASE@h /* convert to phys addr */
+ mtspr M_TWB, r3 /* Update MMU base address */
+ blr
+#endif
- .globl amhere
-amhere: .long 0
-
+
#ifdef __SMP__
/*
* Secondary processor begins executing here.
*/
.globl secondary_entry
secondary_entry:
- lis r0,amhere@h
- ori r0,r0,amhere@l
- addis r0,r0,-KERNELBASE@h
- stw r0,0(r0)
- sync
- isync
/* just like __start() with a few changes -- Cort */
mfspr r9,PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
@@ -1938,16 +2351,6 @@ secondary_entry:
ori r11,r11,HID0_BTCD
5: mtspr HID0,r11 /* superscalar exec & br history tbl */
4:
- /* get ptr to current */
- lis r2,current_set@h
- ori r2,r2,current_set@l
- /* assume we're second processor for now */
- lwz r2,4(r2)
- /* stack */
- addi r1,r2,TASK_UNION_SIZE
- li r0,0
- stwu r0,-STACK_FRAME_OVERHEAD(r1)
-
/*
* init_MMU on the first processor has setup the variables
* for us - all we need to do is load them -- Cort
@@ -1969,6 +2372,18 @@ secondary_entry:
rfi
/* Load up the kernel context */
2:
+ /* get ptr to current */
+ lis r2,current_set@h
+ ori r2,r2,current_set@l
+ /* assume we're second processor for now */
+ tophys(r2,r2,r10)
+ lwz r2,4(r2)
+ /* stack */
+ addi r1,r2,TASK_UNION_SIZE
+ li r0,0
+ tophys(r3,r1,r10)
+ stwu r0,-STACK_FRAME_OVERHEAD(r3)
+
SYNC /* Force all PTE updates to finish */
tlbia /* Clear all TLB entries */
mtspr SDR1,r6
@@ -2025,6 +2440,31 @@ secondary_entry:
/* should never return */
.long 0
#endif /* __SMP__ */
+
+#ifdef CONFIG_MBX
+/* Jump into the system reset for the MBX rom.
+ * We first disable the MMU, and then jump to the ROM reset address.
+ *
+ * This does not work, don't bother trying. There is no place in
+ * the ROM we can jump to cause a reset. We will have to program
+ * a watchdog of some type that we don't service to cause a processor
+ * reset.
+ */
+ .globl MBX_gorom
+MBX_gorom:
+ li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+ lis r4,2f@h
+ addis r4,r4,-KERNELBASE@h
+ ori r4,r4,2f@l
+ mtspr SRR0,r4
+ mtspr SRR1,r3
+ rfi
+2:
+ lis r4, 0xfe000000@h
+ addi r4, r4, 0xfe000000@l
+ mtlr r4
+ blr
+#endif
/*
* We put a few things here that have to be page-aligned.
diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c
index f47d6f3d6..21c6966e0 100644
--- a/arch/ppc/kernel/idle.c
+++ b/arch/ppc/kernel/idle.c
@@ -1,5 +1,5 @@
/*
- * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $
+ * $Id: idle.c,v 1.35 1998/04/07 20:24:23 cort Exp $
*
* Idle daemon for PowerPC. Idle daemon will handle any action
* that needs to be taken when the system becomes idle.
@@ -13,6 +13,7 @@
*/
#define __KERNEL_SYSCALLS__
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -31,41 +32,44 @@
#include <asm/smp_lock.h>
#include <asm/processor.h>
#include <asm/mmu.h>
+#include <asm/cache.h>
+#ifdef CONFIG_PMAC
+#include <asm/mediabay.h>
+#endif
-int zero_paged(void *unused);
-void inline power_save(void);
+void zero_paged(void);
+void power_save(void);
void inline htab_reclaim(void);
+unsigned long htab_reclaim_on = 0;
+unsigned long zero_paged_on = 0;
+
int idled(void *unused)
{
int ret = -EPERM;
- /*
- * want one per cpu since it would be nice to have all
- * processors who aren't doing anything
- * zero-ing pages since this daemon is lock-free
- * -- Cort
- */
- /* kernel_thread(zero_paged, NULL, 0); */
-
-#ifdef __SMP__
-printk("SMP %d: in idle. current = %s/%d\n",
- current->processor,current->comm,current->pid);
-#endif /* __SMP__ */
for (;;)
{
+ __sti();
+
/* endless loop with no priority at all */
current->priority = -100;
current->counter = -100;
+
+ check_pgt_cache();
- /* endless idle loop with no priority at all */
- /* htab_reclaim(); */
- schedule();
+ if ( !need_resched && zero_paged_on ) zero_paged();
+ if ( !need_resched && htab_reclaim_on ) htab_reclaim();
+
+ /*
+ * Only processor 1 may sleep now since processor 2 would
+ * never wake up. Need to add timer code for processor 2
+ * then it can sleep. -- Cort
+ */
#ifndef __SMP__
- /* can't do this on smp since second processor
- will never wake up -- Cort */
- /* power_save(); */
-#endif /* __SMP__ */
+ if ( !need_resched ) power_save();
+#endif /* __SMP__ */
+ schedule();
}
ret = 0;
return ret;
@@ -76,13 +80,16 @@ printk("SMP %d: in idle. current = %s/%d\n",
* Mark 'zombie' pte's in the hash table as invalid.
* This improves performance for the hash table reload code
* a bit since we don't consider unused pages as valid.
- * I haven't done any rigorous performance analysis yet
- * so it's still experimental and turned off here.
* -- Cort
*/
+PTE *reclaim_ptr = 0;
void inline htab_reclaim(void)
{
+#ifndef CONFIG_8xx
+#if 0
PTE *ptr, *start;
+ static int dir = 1;
+#endif
struct task_struct *p;
unsigned long valid = 0;
extern PTE *Hash, *Hash_end;
@@ -91,28 +98,33 @@ void inline htab_reclaim(void)
/* if we don't have a htab */
if ( Hash_size == 0 )
return;
- /*lock_dcache();*/
-
+ lock_dcache(1);
+
+#if 0
/* find a random place in the htab to start each time */
- start = &Hash[jiffies%(Hash_size/sizeof(ptr))];
- for ( ptr = start; ptr < Hash_end ; ptr++)
+ start = &Hash[jiffies%(Hash_size/sizeof(PTE))];
+ /* go a different direction each time */
+ dir *= -1;
+ for ( ptr = start;
+ !need_resched && (ptr != Hash_end) && (ptr != Hash);
+ ptr += dir)
{
- if ( ptr == start )
- return;
- if ( ptr == Hash_end )
- ptr = Hash;
- valid = 0;
- if (!ptr->v)
+#else
+ if ( !reclaim_ptr ) reclaim_ptr = Hash;
+ while ( !need_resched )
+ {
+ reclaim_ptr++;
+ if ( reclaim_ptr == Hash_end ) reclaim_ptr = Hash;
+#endif
+ if (!reclaim_ptr->v)
continue;
+ valid = 0;
for_each_task(p)
{
if ( need_resched )
- {
- /*unlock_dcache();*/
- return;
- }
+ goto out;
/* if this vsid/context is in use */
- if ( (ptr->vsid >> 4) == p->mm->context )
+ if ( (reclaim_ptr->vsid >> 4) == p->mm->context )
{
valid = 1;
break;
@@ -121,19 +133,28 @@ void inline htab_reclaim(void)
if ( valid )
continue;
/* this pte isn't used */
- ptr->v = 0;
+ reclaim_ptr->v = 0;
}
- /*unlock_dcache();*/
+out:
+ if ( need_resched ) printk("need_resched: %x\n", need_resched);
+ unlock_dcache();
+#endif /* CONFIG_8xx */
}
-
+
/*
* Syscall entry into the idle task. -- Cort
*/
asmlinkage int sys_idle(void)
{
+ extern int media_bay_task(void *);
if(current->pid != 0)
return -EPERM;
+#ifdef CONFIG_PMAC
+ if (media_bay_present)
+ kernel_thread(media_bay_task, NULL, 0);
+#endif
+
idled(NULL);
return 0; /* should never execute this but it makes gcc happy -- Cort */
}
@@ -157,10 +178,8 @@ unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */
unsigned long bytecount = 0; /* pointer into the currently being zero'd page */
unsigned long zerocount = 0; /* # currently pre-zero'd pages */
unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */
-unsigned long pageptr = 0; /* current page being zero'd */
unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */
unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */
-static struct wait_queue * page_zerod_wait = NULL;
#define PAGE_THRESHOLD 96 /* how many pages to keep pre-zero'd */
/*
@@ -189,7 +208,6 @@ unsigned long get_prezerod_page(void)
*/
atomic_inc((atomic_t *)&zeropage_hits);
atomic_dec((atomic_t *)&zerocount);
- wake_up(&page_zerod_wait);
need_resched = 1;
/* zero out the pointer to next in the page */
@@ -201,35 +219,18 @@ unsigned long get_prezerod_page(void)
/*
* Experimental stuff to zero out pages in the idle task
- * to speed up get_free_pages() -- Cort
- * Zero's out pages until we need to resched or
- * we've reached the limit of zero'd pages.
+ * to speed up get_free_pages(). Zero's out pages until
+ * we've reached the limit of zero'd pages. We handle
+ * reschedule()'s in here so when we return we know we've
+ * zero'd all we need to for now.
*/
-int zero_paged(void *unused)
+void zero_paged(void)
{
- extern pte_t *get_pte( struct mm_struct *mm, unsigned long address );
- pgd_t *dir;
- pmd_t *pmd;
+ unsigned long pageptr = 0; /* current page being zero'd */
pte_t *pte;
-
- sprintf(current->comm, "zero_paged (idle)");
- /* current->blocked = ~0UL; */
-#ifdef __SMP__
- printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id());
-#else
- printk("Started zero_paged\n");
-#endif /* __SMP__ */
-
- __sti();
- while ( 1 )
+ while ( zerocount <= PAGE_THRESHOLD )
{
- /* don't want to be pre-empted by swapper or power_save */
- current->priority = -98;
- current->counter = -98;
- /* we don't want to run until we have something to do */
- while ( zerocount >= PAGE_THRESHOLD )
- sleep_on(&page_zerod_wait);
/*
* Mark a page as reserved so we can mess with it
* If we're interrupted we keep this page and our place in it
@@ -237,7 +238,7 @@ int zero_paged(void *unused)
*/
pageptr = __get_free_pages(GFP_ATOMIC, 0);
if ( !pageptr )
- goto retry;
+ return;
if ( need_resched )
schedule();
@@ -245,20 +246,15 @@ int zero_paged(void *unused)
/*
* Make the page no cache so we don't blow our cache with 0's
*/
- dir = pgd_offset( init_task.mm, pageptr );
- if (dir)
+ pte = find_pte(init_task.mm, pageptr);
+ if ( !pte )
{
- pmd = pmd_offset(dir, pageptr & PAGE_MASK);
- if (pmd && pmd_present(*pmd))
- {
- pte = pte_offset(pmd, pageptr & PAGE_MASK);
- if (pte && pte_present(*pte))
- {
- pte_uncache(*pte);
- flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
- }
- }
+ printk("pte NULL in zero_paged()\n");
+ return;
}
+
+ pte_uncache(*pte);
+ flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
/*
* Important here to not take time away from real processes.
@@ -308,35 +304,34 @@ int zero_paged(void *unused)
*/
atomic_inc((atomic_t *)&zerocount);
atomic_inc((atomic_t *)&zerototal);
-retry:
- schedule();
}
}
-void inline power_save(void)
+int powersave_mode = HID0_DOZE;
+
+void power_save(void)
{
unsigned long msr, hid0;
- /* no powersaving modes on the 601 */
- if( (_get_PVR()>>16) == 1 )
+ /* only sleep on the 603-family/750 processors */
+ switch (_get_PVR() >> 16) {
+ case 3: /* 603 */
+ case 6: /* 603e */
+ case 7: /* 603ev */
+ case 8: /* 750 */
+ break;
+ default:
return;
+ }
- __sti();
- asm volatile(
- /* clear powersaving modes and set nap mode */
- "mfspr %3,1008 \n\t"
- "andc %3,%3,%4 \n\t"
- "or %3,%3,%5 \n\t"
- "mtspr 1008,%3 \n\t"
- /* enter the mode */
- "mfmsr %0 \n\t"
- "oris %0,%0,%2 \n\t"
- "sync \n\t"
- "mtmsr %0 \n\t"
- "isync \n\t"
- : "=&r" (msr)
- : "0" (msr), "i" (MSR_POW>>16),
- "r" (hid0),
- "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP),
- "r" (HID0_NAP));
+ save_flags(msr);
+ cli();
+ if (!need_resched) {
+ asm("mfspr %0,1008" : "=r" (hid0) :);
+ hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE);
+ hid0 |= powersave_mode | HID0_DPM;
+ asm("mtspr 1008,%0" : : "r" (hid0));
+ msr |= MSR_POW;
+ }
+ restore_flags(msr);
}
diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c
index 4616d59a2..462805a65 100644
--- a/arch/ppc/kernel/irq.c
+++ b/arch/ppc/kernel/irq.c
@@ -14,6 +14,14 @@
* instead of just grabbing them. Thus setups with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
+ *
+ * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the
+ * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit
+ * mask register (of which only 16 are defined), hence the weird shifting
+ * and compliment of the cached_irq_mask. I want to be able to stuff
+ * this right into the SIU SMASK register.
+ * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx
+ * to reduce code space and undefined function references.
*/
@@ -30,30 +38,41 @@
#include <linux/malloc.h>
#include <linux/openpic.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/openpic.h>
#include <asm/hydra.h>
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/gg2.h>
+#include <asm/cache.h>
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#include <asm/mbx.h>
+#endif
#undef SHOW_IRQ
unsigned lost_interrupts = 0;
unsigned int local_irq_count[NR_CPUS];
-static struct irqaction irq_action[NR_IRQS];
+static struct irqaction *irq_action[NR_IRQS];
static int spurious_interrupts = 0;
+#ifndef CONFIG_8xx
+static unsigned int cached_irq_mask = 0xffffffff;
+#else
static unsigned int cached_irq_mask = 0xffffffff;
+#endif
static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
-spinlock_t irq_controller_lock;
+/*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/
#ifdef __SMP__
atomic_t __ppc_bh_counter = ATOMIC_INIT(0);
#else
int __ppc_bh_counter = 0;
#endif
+static volatile unsigned char *gg2_int_ack_special;
+extern volatile unsigned long ipi_count;
#define cached_21 (((char *)(&cached_irq_mask))[3])
#define cached_A1 (((char *)(&cached_irq_mask))[2])
@@ -61,9 +80,19 @@ int __ppc_bh_counter = 0;
/*
* These are set to the appropriate functions by init_IRQ()
*/
+#ifndef CONFIG_8xx
void (*mask_and_ack_irq)(int irq_nr);
void (*mask_irq)(unsigned int irq_nr);
void (*unmask_irq)(unsigned int irq_nr);
+#else /* CONFIG_8xx */
+/* init_IRQ() happens too late for the MBX because we initialize the
+ * CPM early and it calls request_irq() before we have these function
+ * pointers initialized.
+ */
+#define mask_and_ack_irq(irq) mbx_mask_irq(irq)
+#define mask_irq(irq) mbx_mask_irq(irq)
+#define unmask_irq(irq) mbx_unmask_irq(irq)
+#endif /* CONFIG_8xx */
/* prep */
@@ -79,9 +108,46 @@ extern unsigned long route_pci_interrupts(void);
#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE))
+
+/* nasty hack for shared irq's since we need to do kmalloc calls but
+ * can't very very early in the boot when we need to do a request irq.
+ * this needs to be removed.
+ * -- Cort
+ */
+static char cache_bitmask = 0;
+static struct irqaction malloc_cache[4];
+extern int mem_init_done;
+
+void *irq_kmalloc(size_t size, int pri)
+{
+ unsigned int i;
+ if ( mem_init_done )
+ return kmalloc(size,pri);
+ for ( i = 0; i <= 3 ; i++ )
+ if ( ! ( cache_bitmask & (1<<i) ) )
+ {
+ cache_bitmask |= (1<<i);
+ return (void *)(&malloc_cache[i]);
+ }
+ return 0;
+}
+
+void irq_kfree(void *ptr)
+{
+ unsigned int i;
+ for ( i = 0 ; i <= 3 ; i++ )
+ if ( ptr == &malloc_cache[i] )
+ {
+ cache_bitmask &= ~(1<<i);
+ return;
+ }
+ kfree(ptr);
+}
+
+#ifndef CONFIG_8xx
void i8259_mask_and_ack_irq(int irq_nr)
{
- spin_lock(&irq_controller_lock);
+ /* spin_lock(&irq_controller_lock);*/
cached_irq_mask |= 1 << irq_nr;
if (irq_nr > 7) {
inb(0xA1); /* DUMMY */
@@ -96,20 +162,22 @@ void i8259_mask_and_ack_irq(int irq_nr)
outb(0x60|irq_nr,0x20); /* specific eoi */
}
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
}
void pmac_mask_and_ack_irq(int irq_nr)
{
unsigned long bit = 1UL << irq_nr;
- spin_lock(&irq_controller_lock);
+ /* spin_lock(&irq_controller_lock);*/
cached_irq_mask |= bit;
lost_interrupts &= ~bit;
out_le32(IRQ_ACK, bit);
out_le32(IRQ_ENABLE, ~cached_irq_mask);
out_le32(IRQ_ACK, bit);
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
+ /*if ( irq_controller_lock.lock )
+ panic("irq controller lock still held in mask and ack\n");*/
}
void chrp_mask_and_ack_irq(int irq_nr)
@@ -188,44 +256,96 @@ static void chrp_unmask_irq(unsigned int irq_nr)
else
openpic_enable_irq(irq_to_openpic(irq_nr));
}
+#else /* CONFIG_8xx */
+static void mbx_mask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask &= ~(1 << (31-irq_nr));
+ ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+ cached_irq_mask;
+}
+
+static void mbx_unmask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask |= (1 << (31-irq_nr));
+ ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+ cached_irq_mask;
+}
+#endif /* CONFIG_8xx */
void disable_irq(unsigned int irq_nr)
{
- unsigned long flags;
+ /*unsigned long flags;*/
- spin_lock_irqsave(&irq_controller_lock, flags);
+ /* spin_lock_irqsave(&irq_controller_lock, flags);*/
mask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/
synchronize_irq();
}
void enable_irq(unsigned int irq_nr)
{
- unsigned long flags;
+ /*unsigned long flags;*/
- spin_lock_irqsave(&irq_controller_lock, flags);
+ /* spin_lock_irqsave(&irq_controller_lock, flags);*/
unmask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/
}
int get_irq_list(char *buf)
{
- int i, len = 0;
+ int i, len = 0, j;
struct irqaction * action;
+ len += sprintf(buf+len, " ");
+ for (j=0; j<smp_num_cpus; j++)
+ len += sprintf(buf+len, "CPU%d ",j);
+ *(char *)(buf+len++) = '\n';
+
for (i = 0 ; i < NR_IRQS ; i++) {
- action = irq_action + i;
+ action = irq_action[i];
if (!action || !action->handler)
continue;
- len += sprintf(buf+len, "%2d: %10u %s",
- i, kstat.interrupts[i], action->name);
+ len += sprintf(buf+len, "%3d: ", i);
+#ifdef __SMP__
+ for (j = 0; j < smp_num_cpus; j++)
+ len += sprintf(buf+len, "%10u ",
+ kstat.irqs[cpu_logical_map(j)][i]);
+#else
+ len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#endif /* __SMP__ */
+ switch( _machine )
+ {
+ case _MACH_prep:
+ len += sprintf(buf+len, " 82c59 ");
+ break;
+ case _MACH_Pmac:
+ len += sprintf(buf+len, " PMAC-PIC ");
+ break;
+ case _MACH_chrp:
+ if ( is_8259_irq(i) )
+ len += sprintf(buf+len, " 82c59 ");
+ else
+ len += sprintf(buf+len, " OpenPIC ");
+ break;
+ }
+
+ len += sprintf(buf+len, " %s",action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ", %s", action->name);
}
len += sprintf(buf+len, "\n");
}
- len += sprintf(buf+len, "99: %10u spurious or short\n",
- spurious_interrupts);
+#ifdef __SMP__
+ /* should this be per processor send/receive? */
+ len += sprintf(buf+len, "IPI: %10lu\n", ipi_count);
+ for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+ len += sprintf(buf+len," ");
+ len += sprintf(buf+len, " interprocessor messages received\n");
+#endif
+ len += sprintf(buf+len, "BAD: %10u",spurious_interrupts);
+ for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+ len += sprintf(buf+len," ");
+ len += sprintf(buf+len, " spurious or short\n");
return len;
}
@@ -394,20 +514,31 @@ asmlinkage void do_IRQ(struct pt_regs *regs)
int status;
int openpic_eoi_done = 0;
+ /* save the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ unsigned dcache_locked;
+
+ dcache_locked = unlock_dcache();
+ hardirq_enter(cpu);
+#ifndef CONFIG_8xx
#ifdef __SMP__
if ( cpu != 0 )
- panic("cpu %d received interrupt", cpu);
-#endif /* __SMP__ */
-
- hardirq_enter(cpu);
+ {
+ if ( !lost_interrupts )
+ {
+ extern smp_message_recv(void);
+ goto out;
+
+ ipi_count++;
+ smp_message_recv();
+ goto out;
+ }
+ /* could be here due to a do_fake_interrupt call but we don't
+ mess with the controller from the second cpu -- Cort */
+ goto out;
+ }
+#endif /* __SMP__ */
- /*
- * I'll put this ugly mess of code into a function
- * such as get_pending_irq() or some such clear thing
- * so we don't have a switch in the irq code and
- * the chrp code is merged a bit with the prep.
- * -- Cort
- */
switch ( _machine )
{
case _MACH_Pmac:
@@ -425,7 +556,7 @@ asmlinkage void do_IRQ(struct pt_regs *regs)
*
* This should go in the above mask/ack code soon. -- Cort
*/
- irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL;
+ irq = *gg2_int_ack_special;
/*
* Acknowledge as soon as possible to allow i8259
* interrupt nesting
@@ -456,7 +587,6 @@ asmlinkage void do_IRQ(struct pt_regs *regs)
}
break;
case _MACH_prep:
-#if 1
outb(0x0C, 0x20);
irq = inb(0x20) & 7;
if (irq == 2)
@@ -470,23 +600,6 @@ retry_cascade:
irq = (irq&7) + 8;
}
bits = 1UL << irq;
-#else
- /*
- * get the isr from the intr controller since
- * the bit in the irr has been cleared
- */
- outb(0x0a, 0x20);
- bits = inb(0x20)&0xff;
- /* handle cascade */
- if ( bits & 4 )
- {
- bits &= ~4UL;
- outb(0x0a, 0xA0);
- bits |= inb(0xA0)<<8;
- }
- /* ignore masked irqs */
- bits &= ~cached_irq_mask;
-#endif
break;
}
@@ -494,43 +607,62 @@ retry_cascade:
printk("Bogus interrupt from PC = %lx\n", regs->nip);
goto out;
}
+
+#else /* CONFIG_8xx */
+ /* For MPC8xx, read the SIVEC register and shift the bits down
+ * to get the irq number.
+ */
+ bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec;
+ irq = bits >> 26;
+#endif /* CONFIG_8xx */
mask_and_ack_irq(irq);
status = 0;
- action = irq_action + irq;
- kstat.interrupts[irq]++;
- if (action->handler) {
+ action = irq_action[irq];
+ kstat.irqs[cpu][irq]++;
+ if ( action && action->handler) {
if (!(action->flags & SA_INTERRUPT))
__sti();
- status |= action->flags;
- action->handler(irq, action->dev_id, regs);
- /*if (status & SA_SAMPLE_RANDOM)
- add_interrupt_randomness(irq);*/
- __cli(); /* in case the handler turned them on */
- spin_lock(&irq_controller_lock);
+ do {
+ status |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ /*if (status & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);*/
+ action = action->next;
+ } while ( action );
+ __cli();
+ /* spin_lock(&irq_controller_lock);*/
unmask_irq(irq);
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
} else {
+#ifndef CONFIG_8xx
if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */
+#endif
spurious_interrupts++;
disable_irq( irq );
}
/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
+#ifndef CONFIG_8xx
if ( is_prep && (irq > 7) )
goto retry_cascade;
/* do_bottom_half is called if necessary from int_return in head.S */
out:
if (_machine == _MACH_chrp && !openpic_eoi_done)
openpic_eoi(0);
+#endif /* CONFIG_8xx */
hardirq_exit(cpu);
+
+ /* restore the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ lock_dcache(dcache_locked);
}
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char * devname, void *dev_id)
{
- struct irqaction * action;
+ struct irqaction *old, **p, *action;
unsigned long flags;
#ifdef SHOW_IRQ
@@ -540,49 +672,58 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
if (irq >= NR_IRQS)
return -EINVAL;
- action = irq + irq_action;
- if (action->handler)
- return -EBUSY;
+
if (!handler)
- return -EINVAL;
+ {
+ /* Free */
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next)
+ {
+ /* Found it - now free it */
+ save_flags(flags);
+ cli();
+ *p = action->next;
+ restore_flags(flags);
+ irq_kfree(action);
+ return 0;
+ }
+ return -ENOENT;
+ }
+
+ action = (struct irqaction *)
+ irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
save_flags(flags);
cli();
+
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->dev_id = dev_id;
+ action->next = NULL;
enable_irq(irq);
- restore_flags(flags);
+ p = irq_action + irq;
+
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & action->flags & SA_SHIRQ))
+ return -EBUSY;
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ }
+ *p = action;
+
+ restore_flags(flags);
return 0;
}
void free_irq(unsigned int irq, void *dev_id)
{
- struct irqaction * action = irq + irq_action;
- unsigned long flags;
-
-#ifdef SHOW_IRQ
- printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id);
-#endif /* SHOW_IRQ */
-
- if (irq >= NR_IRQS) {
- printk("Trying to free IRQ%d\n",irq);
- return;
- }
- if (!action->handler) {
- printk("Trying to free free IRQ%d\n",irq);
- return;
- }
- disable_irq(irq);
- save_flags(flags);
- cli();
- action->handler = NULL;
- action->flags = 0;
- action->mask = 0;
- action->name = NULL;
- action->dev_id = NULL;
- restore_flags(flags);
+ request_irq(irq, NULL, 0, NULL, dev_id);
}
unsigned long probe_irq_on (void)
@@ -595,6 +736,7 @@ int probe_irq_off (unsigned long irqs)
return 0;
}
+#ifndef CONFIG_8xx
__initfunc(static void i8259_init(void))
{
/* init master interrupt controller */
@@ -616,11 +758,17 @@ __initfunc(static void i8259_init(void))
panic("Could not allocate cascade IRQ!");
enable_irq(2); /* Enable cascade interrupt */
}
+#endif /* CONFIG_8xx */
+/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External
+ * interrupts can be either edge or level triggered, but there is no
+ * reason for us to change the EPPC-bug values (it would not work if we did).
+ */
__initfunc(void init_IRQ(void))
{
extern void xmon_irq(int, void *, struct pt_regs *);
+#ifndef CONFIG_8xx
switch (_machine)
{
case _MACH_Pmac:
@@ -637,7 +785,8 @@ __initfunc(void init_IRQ(void))
mask_and_ack_irq = chrp_mask_and_ack_irq;
mask_irq = chrp_mask_irq;
unmask_irq = chrp_unmask_irq;
- ioremap(GG2_INT_ACK_SPECIAL, 1);
+ gg2_int_ack_special = (volatile unsigned char *)
+ ioremap(GG2_INT_ACK_SPECIAL, 1);
openpic_init();
i8259_init();
#ifdef CONFIG_XMON
@@ -653,7 +802,7 @@ __initfunc(void init_IRQ(void))
i8259_init();
route_pci_interrupts();
/*
- * According to the Carolina spec from ibm irq's 0,1,2, and 8
+ * According to the Carolina spec from ibm irqs 0,1,2, and 8
* must be edge triggered. Also, the pci intrs must be level
* triggered and _only_ isa intrs can be level sensitive
* which are 3-7,9-12,14-15. 13 is special - it can be level.
@@ -686,5 +835,6 @@ __initfunc(void init_IRQ(void))
}
break;
- }
+ }
+#endif /* CONFIG_8xx */
}
diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c
new file mode 100644
index 000000000..30b7b1184
--- /dev/null
+++ b/arch/ppc/kernel/mbx_pci.c
@@ -0,0 +1,254 @@
+/*
+ * MBX pci routines.
+ * The MBX uses the QSpan PCI bridge. The config address register
+ * is located 0x500 from the base of the bridge control/status registers.
+ * The data register is located at 0x504.
+ * This is a two step operation. First, the address register is written,
+ * then the data register is read/written as required.
+ * I don't know what to do about interrupts (yet).
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/mbx.h>
+
+
+/*
+ * This blows......The MBX uses the Tundra QSpan PCI bridge. When
+ * reading the configuration space, if something does not respond
+ * the bus times out and we get a machine check interrupt. So, the
+ * good ol' exception tables come to mind to trap it and return some
+ * value.
+ *
+ * On an error we just return a -1, since that is what the caller wants
+ * returned if nothing is present. I copied this from __get_user_asm,
+ * with the only difference of returning -1 instead of EFAULT.
+ * There is an associated hack in the machine check trap code.
+ *
+ * The QSPAN is also a big endian device, that is it makes the PCI
+ * look big endian to us. This presents a problem for the Linux PCI
+ * functions, which assume little endian. For example, we see the
+ * first 32-bit word like this:
+ * ------------------------
+ * | Device ID | Vendor ID |
+ * ------------------------
+ * If we read/write as a double word, that's OK. But in our world,
+ * when read as a word, device ID is at location 0, not location 2 as
+ * the little endian PCI would believe. We have to switch bits in
+ * the PCI addresses given to us to get the data to/from the correct
+ * byte lanes.
+ *
+ * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5.
+ * It always forces the MS bit to zero. Therefore, dev_fn values
+ * greater than 128 are returned as "no device found" errors.
+ *
+ * The QSPAN can only perform long word (32-bit) configuration cycles.
+ * The "offset" must have the two LS bits set to zero. Read operations
+ * require we read the entire word and then sort out what should be
+ * returned. Write operations other than long word require that we
+ * read the long word, update the proper word or byte, then write the
+ * entire long word back.
+ *
+ * PCI Bridge hack. We assume (correctly) that bus 0 is the primary
+ * PCI bus from the QSPAN. If we are called with a bus number other
+ * than zero, we create a Type 1 configuration access that a downstream
+ * PCI bridge will interpret.
+ */
+
+#define __get_mbx_pci_config(x, addr, op) \
+ __asm__ __volatile__( \
+ "1: "op" %0,0(%1)\n" \
+ " eieio\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: li %0,-1\n" \
+ " b 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 2\n" \
+ " .long 1b,3b\n" \
+ ".text" \
+ : "=r"(x) : "r"(addr))
+
+#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500))
+#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504))
+
+#define mk_config_addr(bus, dev, offset) \
+ (((bus)<<16) | ((dev)<<8) | (offset & 0xfc))
+
+#define mk_config_type1(bus, dev, offset) \
+ mk_config_addr(bus, dev, offset) | 1;
+
+int mbx_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char *val)
+{
+ uint temp;
+ u_char *cp;
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
+
+ offset ^= 0x03;
+ cp = ((u_char *)&temp) + (offset & 0x03);
+ *val = *cp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short *val)
+{
+ uint temp;
+ ushort *sp;
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
+ offset ^= 0x02;
+
+ sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+ *val = *sp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int *val)
+{
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_mbx_pci_config(*val, QS_CONFIG_DATA, "lwz");
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char val)
+{
+ uint temp;
+ u_char *cp;
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+ offset ^= 0x03;
+ cp = ((u_char *)&temp) + (offset & 0x03);
+ *cp = val;
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *QS_CONFIG_DATA = temp;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short val)
+{
+ uint temp;
+ ushort *sp;
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+ offset ^= 0x02;
+ sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+ *sp = val;
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *QS_CONFIG_DATA = temp;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int val)
+{
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *(unsigned int *)QS_CONFIG_DATA = val;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
+ unsigned short index, unsigned char *bus_ptr,
+ unsigned char *dev_fn_ptr)
+{
+ int num, devfn;
+ unsigned int x, vendev;
+
+ if (vendor == 0xffff)
+ return PCIBIOS_BAD_VENDOR_ID;
+ vendev = (dev_id << 16) + vendor;
+ num = 0;
+ for (devfn = 0; devfn < 32; devfn++) {
+ mbx_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
+ if (x == vendev) {
+ if (index == num) {
+ *bus_ptr = 0;
+ *dev_fn_ptr = devfn<<3;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int mbx_pcibios_find_class(unsigned int class_code, unsigned short index,
+ unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+{
+ int devnr, x, num;
+
+ num = 0;
+ for (devnr = 0; devnr < 32; devnr++) {
+ mbx_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
+ if ((x>>8) == class_code) {
+ if (index == num) {
+ *bus_ptr = 0;
+ *dev_fn_ptr = devnr<<3;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c
new file mode 100644
index 000000000..c028bb12d
--- /dev/null
+++ b/arch/ppc/kernel/mbx_setup.c
@@ -0,0 +1,169 @@
+/*
+ * linux/arch/ppc/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/ioport.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/residual.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/ide.h>
+#include <asm/mbx.h>
+
+extern unsigned long loops_per_sec;
+
+unsigned long empty_zero_page[1024];
+
+#ifdef CONFIG_BLK_DEV_RAM
+extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start; /* starting block # of image */
+#endif
+
+extern char saved_command_line[256];
+
+extern unsigned long find_available_memory(void);
+extern void mbx_cpm_reset(uint);
+
+
+void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+
+ *p = 0;
+ *irq = 0;
+
+ if (base != 0) /* Only map the first ATA flash drive */
+ return;
+#ifdef ATA_FLASH
+ base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200);
+ for (i = 0; i < 8; ++i)
+ *p++ = base++;
+ *p = ++base; /* Does not matter */
+ if (irq)
+ *irq = 13;
+#endif
+}
+
+int
+mbx_get_cpuinfo(char *buffer)
+{
+ int pvr = _get_PVR();
+ int len;
+ char *model;
+ bd_t *bp;
+ extern RESIDUAL res;
+
+ /* I know the MPC860 is 0x50. I don't have the book handy
+ * to check the others.
+ */
+ if ((pvr>>16) == 0x50)
+ model = "MPC860";
+ else
+ model = "unknown";
+
+#ifdef __SMP__
+#define CD(X) (cpu_data[n].X)
+#else
+#define CD(X) (X)
+#define CPUN 0
+#endif
+ bp = (bd_t *)&res;
+
+ len = sprintf(buffer,"processor\t: %d\n"
+ "cpu\t\t: %s\n"
+ "revision\t: %d.%d\n"
+ "clock\t\t: %d MHz\n"
+ "bus clock\t: %d MHz\n",
+ CPUN,
+ model,
+ MAJOR(pvr), MINOR(pvr),
+ bp->bi_intfreq / 1000000,
+ bp->bi_busfreq / 1000000
+ );
+
+ return len;
+}
+
+__initfunc(void
+mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
+{
+ int cpm_page;
+
+ cpm_page = *memory_start_p;
+ *memory_start_p += PAGE_SIZE;
+
+ /* Reset the Communication Processor Module.
+ */
+ mbx_cpm_reset(cpm_page);
+
+#ifdef notdef
+ ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
+#endif
+
+#ifdef CONFIG_BLK_DEV_RAM
+#if 0
+ ROOT_DEV = to_kdev_t(0x0200); /* floppy */
+ rd_prompt = 1;
+ rd_doload = 1;
+ rd_image_start = 0;
+#endif
+ /* initrd_start and size are setup by boot/head.S and kernel/head.S */
+ if ( initrd_start )
+ {
+ if (initrd_end > *memory_end_p)
+ {
+ printk("initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ initrd_end,*memory_end_p);
+ initrd_start = 0;
+ }
+ }
+#endif
+
+#ifdef notdef
+ request_region(0x20,0x20,"pic1");
+ request_region(0xa0,0x20,"pic2");
+ request_region(0x00,0x20,"dma1");
+ request_region(0x40,0x20,"timer");
+ request_region(0x80,0x10,"dma page reg");
+ request_region(0xc0,0x20,"dma2");
+#endif
+}
+
+void
+abort(void)
+{
+#ifdef CONFIG_XMON
+ extern void xmon(void *);
+ xmon(0);
+#endif
+ machine_restart(NULL);
+}
diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S
index 2c866eed8..6607e00bd 100644
--- a/arch/ppc/kernel/misc.S
+++ b/arch/ppc/kernel/misc.S
@@ -12,6 +12,7 @@
*
*/
+#include <linux/config.h>
#include <linux/sys.h>
#include <asm/unistd.h>
#include <asm/errno.h>
@@ -19,6 +20,7 @@
#include "ppc_asm.tmpl"
#include "ppc_defs.h"
+#ifndef CONFIG_8xx
/* This instruction is not implemented on the PPC 601 or 603 */
#define tlbia \
li r4,128; \
@@ -27,7 +29,7 @@
0: tlbie r4; \
addi r4,r4,0x1000; \
bdnz 0b
-
+#endif
.text
/*
@@ -323,6 +325,18 @@ _GLOBAL(_get_SP)
mr r3,r1 /* Close enough */
blr
+_GLOBAL(_get_THRM1)
+ mfspr r3,THRM1
+ blr
+
+_GLOBAL(_set_THRM1)
+ mtspr THRM1,r3
+ blr
+
+_GLOBAL(_get_L2CR)
+ mfspr r3,L2CR
+ blr
+
_GLOBAL(_get_PVR)
mfspr r3,PVR
blr
@@ -348,33 +362,6 @@ cvt_df:
stfs 0,0(r4)
blr
-
-_GLOBAL(lock_dcache)
- mfspr r3,PVR /* nop on 601 */
- rlwinm r3,r3,16,16,31
- cmpwi 0,r3,1
- beqlr-
- mfspr r3,HID0
- ori r3,r3,HID0_DLOCK
- mtspr HID0,r3
- sync
- isync
- blr
-
-_GLOBAL(unlock_dcache)
- mfspr r3,PVR /* nop on 601 */
- rlwinm r3,r3,16,16,31
- cmpwi 0,r3,1
- beqlr-
- mfspr r3,HID0
- li r4,HID0_DLOCK
- andc r3,r3,r4
- mtspr HID0,r3
- sync
- isync
- blr
-
-
/*
* Create a kernel thread
* __kernel_thread(flags, fn, arg)
@@ -386,6 +373,16 @@ _GLOBAL(__kernel_thread)
bnelr /* return if parent */
mtlr r4 /* fn addr in lr */
mr r3,r5 /* load arg and call fn */
+#if 0/*def __SMP__*/
+ /* drop scheduler_lock since schedule() called us */
+ lis r4,scheduler_lock@ha
+ li r5,0
+ stw r5,scheduler_lock@l+4(r4) /* owner_pc */
+ stw r5,scheduler_lock@l+8(r4) /* owner_cpu */
+ stw r5,scheduler_lock@l(r4)
+ sync
+ isync
+#endif /* __SMP__ */
blrl
li r0,__NR_exit /* exit after child exits */
li r3,0
@@ -413,7 +410,9 @@ SYSCALL(execve)
SYSCALL(open)
SYSCALL(close)
SYSCALL(waitpid)
+SYSCALL(fork)
SYSCALL(delete_module)
+SYSCALL(_exit)
/* Why isn't this a) automatic, b) written in 'C'? */
@@ -593,5 +592,7 @@ sys_call_table:
.long sys_setresgid
.long sys_getresgid /* 170 */
.long sys_prctl
- .space (NR_syscalls-171)*4
+ .long sys_xstat
+ .long sys_xmknod
+ .space (NR_syscalls-173)*4
diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c
index 8db1763db..9de056504 100644
--- a/arch/ppc/kernel/mk_defs.c
+++ b/arch/ppc/kernel/mk_defs.c
@@ -19,6 +19,7 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <asm/io.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -29,9 +30,11 @@
void
main(void)
{
+ DEFINE(KERNELBASE, KERNELBASE);
DEFINE(STATE, offsetof(struct task_struct, state));
DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task));
DEFINE(COUNTER, offsetof(struct task_struct, counter));
+ DEFINE(PROCESSOR, offsetof(struct task_struct, processor));
DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending));
DEFINE(TSS, offsetof(struct task_struct, tss));
DEFINE(MM, offsetof(struct task_struct, mm));
@@ -45,6 +48,7 @@ main(void)
DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0]));
DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr));
+ DEFINE(TSS_SMP_FORK_RET, offsetof(struct thread_struct, smp_fork_ret));
/* Interrupt register frame */
DEFINE(TASK_UNION_SIZE, sizeof(union task_union));
DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD);
diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c
index 7e39487d8..186165a46 100644
--- a/arch/ppc/kernel/pci.c
+++ b/arch/ppc/kernel/pci.c
@@ -1,24 +1,27 @@
/*
- * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $
+ * $Id: pci.c,v 1.24 1998/02/19 21:29:49 cort Exp $
* Common pmac/prep/chrp pci routines. -- Cort
*/
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/pci.h>
+#include <linux/openpic.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#include <asm/irq.h>
-#if !defined(CONFIG_MACH_SPECIFIC)
+#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC)
unsigned long isa_io_base;
+#endif /* CONFIG_MACH_SPECIFIC || CONFIG_PMAC */
+#if !defined(CONFIG_MACH_SPECIFIC)
unsigned long isa_mem_base;
unsigned long pci_dram_offset;
#endif /* CONFIG_MACH_SPECIFIC */
@@ -121,49 +124,6 @@ int pcibios_present(void)
return 1;
}
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus,
- unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->vendor == vendor && dev->device == device_id) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-/*
- * Given the class, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->class == class_code) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-
__initfunc(unsigned long
pcibios_init(unsigned long mem_start,unsigned long mem_end))
{
@@ -203,6 +163,70 @@ __initfunc(void
__initfunc(unsigned long
pcibios_fixup(unsigned long mem_start, unsigned long mem_end))
+
{
+ extern route_pci_interrupts(void);
+ struct pci_dev *dev;
+ extern struct bridge_data **bridges;
+ extern unsigned char *Motherboard_map;
+ extern unsigned char *Motherboard_routes;
+
+ /*
+ * FIXME: This is broken: We should not assign IRQ's to IRQless
+ * devices (look at PCI_INTERRUPT_PIN) and we also should
+ * honor the existence of multi-function devices where
+ * different functions have different interrupt pins. [mj]
+ */
+ switch (_machine )
+ {
+ case _MACH_prep:
+ route_pci_interrupts();
+ for(dev=pci_devices; dev; dev=dev->next)
+ {
+ unsigned char d = PCI_SLOT(dev->devfn);
+ dev->irq = Motherboard_routes[Motherboard_map[d]];
+ }
+ break;
+ case _MACH_chrp:
+ /* PCI interrupts are controlled by the OpenPIC */
+ for(dev=pci_devices; dev; dev=dev->next)
+ if (dev->irq)
+ dev->irq = openpic_to_irq(dev->irq);
+ break;
+ case _MACH_Pmac:
+ for(dev=pci_devices; dev; dev=dev->next)
+ {
+ /*
+ * Open Firmware often doesn't initialize the,
+ * PCI_INTERRUPT_LINE config register properly, so we
+ * should find the device node and se if it has an
+ * AAPL,interrupts property.
+ */
+ struct bridge_data *bp = bridges[dev->bus->number];
+ struct device_node *node;
+ unsigned int *reg;
+ unsigned char pin;
+
+ if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) ||
+ !pin)
+ continue; /* No interrupt generated -> no fixup */
+ for (node = bp->node->child; node != 0;
+ node = node->sibling) {
+ reg = (unsigned int *) get_property(node, "reg", 0);
+ if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn)
+ continue;
+ /* this is the node, see if it has interrupts */
+ if (node->n_intrs > 0)
+ dev->irq = node->intrs[0].line;
+ break;
+ }
+ }
+ break;
+ }
return mem_start;
}
+
+__initfunc(char *pcibios_setup(char *str))
+{
+ return str;
+}
diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c
index d0144a567..a71aede12 100644
--- a/arch/ppc/kernel/pmac_pci.c
+++ b/arch/ppc/kernel/pmac_pci.c
@@ -12,27 +12,18 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
-struct bridge_data {
- volatile unsigned int *cfg_addr;
- volatile unsigned char *cfg_data;
- void *io_base;
- int bus_number;
- int max_bus;
- struct bridge_data *next;
- struct device_node *node;
-};
-
-static struct bridge_data **bridges, *bridge_list;
+struct bridge_data **bridges, *bridge_list;
static int max_bus;
static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
@@ -112,9 +103,11 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
int *bus_range;
int len;
struct bridge_data *bp;
+ struct reg_property *addr;
for (; dev != NULL; dev = dev->next) {
- if (dev->n_addrs < 1) {
+ addr = (struct reg_property *) get_property(dev, "reg", &len);
+ if (addr == NULL || len < sizeof(*addr)) {
printk(KERN_WARNING "Can't use %s: no address\n",
dev->full_name);
continue;
@@ -130,15 +123,18 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
else
printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
bus_range[1]);
- printk(" controlled by %s at %x\n",
- dev->name, dev->addrs[0].address);
+ printk(" controlled by %s at %x\n", dev->name, addr->address);
bp = (struct bridge_data *) *mem_ptr;
*mem_ptr += sizeof(struct bridge_data);
bp->cfg_addr = (volatile unsigned int *)
- ioremap(dev->addrs[0].address + 0x800000, 0x1000);
+ ioremap(addr->address + 0x800000, 0x1000);
bp->cfg_data = (volatile unsigned char *)
- ioremap(dev->addrs[0].address + 0xc00000, 0x1000);
- bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000);
+ ioremap(addr->address + 0xc00000, 0x1000);
+ bp->io_base = (void *) ioremap(addr->address, 0x10000);
+#ifdef CONFIG_PMAC
+ if (isa_io_base == 0)
+ isa_io_base = (unsigned long) bp->io_base;
+#endif
bp->bus_number = bus_range[0];
bp->max_bus = bus_range[1];
bp->next = bridge_list;
@@ -180,7 +176,7 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
}
int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned char *val)
+ unsigned char offset, unsigned char *val)
{
struct bridge_data *bp;
@@ -198,32 +194,11 @@ int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
}
udelay(2);
*val = in_8(bp->cfg_data + (offset & 3));
-
- if (offset == PCI_INTERRUPT_LINE) {
- /*
- * Open Firmware often doesn't initialize this
- * register properly, so we find the node and see
- * if it has an AAPL,interrupts property.
- */
- struct device_node *node;
- unsigned int *reg;
-
- for (node = bp->node->child; node != 0; node = node->sibling) {
- reg = (unsigned int *) get_property(node, "reg", 0);
- if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn)
- continue;
- /* this is the node, see if it has interrupts */
- if (node->n_intrs > 0)
- *val = node->intrs[0];
- break;
- }
- }
-
return PCIBIOS_SUCCESSFUL;
}
int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned short *val)
+ unsigned char offset, unsigned short *val)
{
struct bridge_data *bp;
@@ -245,7 +220,7 @@ int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned int *val)
+ unsigned char offset, unsigned int *val)
{
struct bridge_data *bp;
@@ -267,7 +242,7 @@ int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned char val)
+ unsigned char offset, unsigned char val)
{
struct bridge_data *bp;
@@ -288,7 +263,7 @@ int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned short val)
+ unsigned char offset, unsigned short val)
{
struct bridge_data *bp;
@@ -309,7 +284,7 @@ int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned int val)
+ unsigned char offset, unsigned int val)
{
struct bridge_data *bp;
diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c
index 4e37becd5..afcd4fd8b 100644
--- a/arch/ppc/kernel/pmac_setup.c
+++ b/arch/ppc/kernel/pmac_setup.c
@@ -47,23 +47,18 @@
#include <asm/ide.h>
#include <asm/pci-bridge.h>
#include <asm/adb.h>
+#include <asm/mediabay.h>
+#include <asm/ohare.h>
+#include <asm/mediabay.h>
#include "time.h"
-/*
- * A magic address and value to put into it on machines with the
- * "ohare" I/O controller. This makes the IDE CD work on Starmaxes.
- * Contributed by Harry Eaton.
- */
-#define OMAGICPLACE ((volatile unsigned *) 0xf3000038)
-#define OMAGICCONT 0xbeff7a
-
extern int root_mountflags;
unsigned char drive_info;
#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */
-static void gc_init(const char *, int);
+static void ohare_init(void);
void
pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
@@ -91,27 +86,44 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
loops_per_sec = 50000000;
}
+ /* this area has the CPU identification register
+ and some registers used by smp boards */
+ ioremap(0xf8000000, 0x1000);
+
*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
- gc_init("gc", 0);
- gc_init("ohare", 1);
-#ifdef CONFIG_ABSTRACT_CONSOLE
+ ohare_init();
+
+#ifdef CONFIG_FB
/* Frame buffer device based console */
conswitchp = &fb_con;
#endif
}
-static void gc_init(const char *name, int isohare)
+static volatile u32 *feature_addr;
+
+static void ohare_init(void)
{
struct device_node *np;
- for (np = find_devices(name); np != NULL; np = np->next) {
- if (np->n_addrs > 0)
- ioremap(np->addrs[0].address, np->addrs[0].size);
- if (isohare) {
- printk(KERN_INFO "Twiddling the magic ohare bits\n");
- out_le32(OMAGICPLACE, OMAGICCONT);
- }
+ np = find_devices("ohare");
+ if (np == 0)
+ return;
+ if (np->next != 0)
+ printk(KERN_WARNING "only using the first ohare\n");
+ if (np->n_addrs == 0) {
+ printk(KERN_ERR "No addresses for %s\n", np->full_name);
+ return;
+ }
+ feature_addr = (volatile u32 *)
+ ioremap(np->addrs[0].address + OHARE_FEATURE_REG, 4);
+
+ if (find_devices("via-pmu") == 0) {
+ printk(KERN_INFO "Twiddling the magic ohare bits\n");
+ out_le32(feature_addr, STARMAX_FEATURES);
+ } else {
+ out_le32(feature_addr, in_le32(feature_addr) | PBOOK_FEATURES);
+ printk(KERN_DEBUG "feature reg = %x\n", in_le32(feature_addr));
}
}
@@ -125,10 +137,15 @@ kdev_t boot_dev;
unsigned long
powermac_init(unsigned long mem_start, unsigned long mem_end)
{
- pmac_nvram_init();
+#ifdef CONFIG_KGDB
+ extern void zs_kgdb_hook(int tty_num);
+ zs_kgdb_hook(0);
+#endif
adb_init();
+ pmac_nvram_init();
if (_machine == _MACH_Pmac) {
pmac_read_rtc_time();
+ media_bay_init();
}
#ifdef CONFIG_PMAC_CONSOLE
pmac_find_display();
@@ -175,7 +192,7 @@ note_scsi_host(struct device_node *node, void *host)
#include "../../../drivers/scsi/sd.h"
#include "../../../drivers/scsi/hosts.h"
-int sd_find_target(void *host, int tgt)
+kdev_t sd_find_target(void *host, int tgt)
{
Scsi_Disk *dp;
int i;
@@ -190,7 +207,7 @@ int sd_find_target(void *host, int tgt)
void find_boot_device(void)
{
- int dev;
+ kdev_t dev;
if (kdev_t_to_nr(ROOT_DEV) != 0)
return;
@@ -201,7 +218,7 @@ void find_boot_device(void)
dev = sd_find_target(boot_host, boot_target);
if (dev == 0)
return;
- boot_dev = to_kdev_t(dev + boot_part);
+ boot_dev = MKDEV(MAJOR(dev), MINOR(dev) + boot_part);
#endif
/* XXX should cope with booting from IDE also */
}
@@ -221,39 +238,92 @@ void note_bootable_part(kdev_t dev, int part)
}
}
+#ifdef CONFIG_BLK_DEV_IDE
+int pmac_ide_ports_known;
+ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
+int pmac_ide_irq[MAX_HWIFS];
+
void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
{
- struct device_node *np;
int i;
- static struct device_node *atas;
- static int atas_valid;
*p = 0;
- *irq = 0;
- if (!atas_valid) {
- atas = find_devices("ATA");
- atas_valid = 1;
- }
- for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next)
- ;
- if (np == NULL)
+ if (base == 0)
return;
- if (np->n_addrs == 0) {
- printk("ide: no addresses for device %s\n", np->full_name);
+ if (base == mb_cd_base && !check_media_bay(MB_CD)) {
+ mb_cd_index = -1;
return;
}
- if (np->n_intrs == 0) {
- printk("ide: no intrs for device %s, using 13\n",
- np->full_name);
- *irq = 13;
- } else {
- *irq = np->intrs[0];
- }
- base = (unsigned long) ioremap(np->addrs[0].address, 0x200);
for (i = 0; i < 8; ++i)
*p++ = base + i * 0x10;
*p = base + 0x160;
+ if (irq != NULL) {
+ *irq = 0;
+ for (i = 0; i < MAX_HWIFS; ++i) {
+ if (base == pmac_ide_regbase[i]) {
+ *irq = pmac_ide_irq[i];
+ break;
+ }
+ }
+ }
+}
+
+void pmac_ide_probe(void)
+{
+ struct device_node *np;
+ int i;
+ struct device_node *atas;
+ struct device_node *p, **pp, *removables, **rp;
+
+ pp = &atas;
+ rp = &removables;
+ p = find_devices("ATA");
+ if (p == NULL)
+ p = find_devices("IDE");
+ /* Move removable devices such as the media-bay CDROM
+ on the PB3400 to the end of the list. */
+ for (; p != NULL; p = p->next) {
+ if (p->parent && p->parent->name
+ && strcasecmp(p->parent->name, "media-bay") == 0) {
+ *rp = p;
+ rp = &p->next;
+ } else {
+ *pp = p;
+ pp = &p->next;
+ }
+ }
+ *rp = NULL;
+ *pp = removables;
+
+ for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
+ if (np->n_addrs == 0) {
+ printk(KERN_WARNING "ide: no address for device %s\n",
+ np->full_name);
+ continue;
+ }
+ pmac_ide_regbase[i] = (unsigned long)
+ ioremap(np->addrs[0].address, 0x200);
+ if (np->n_intrs == 0) {
+ printk("ide: no intrs for device %s, using 13\n",
+ np->full_name);
+ pmac_ide_irq[i] = 13;
+ } else {
+ pmac_ide_irq[i] = np->intrs[0].line;
+ }
+
+ if (np->parent && np->parent->name
+ && strcasecmp(np->parent->name, "media-bay") == 0) {
+ mb_cd_index = i;
+ mb_cd_base = pmac_ide_regbase[i];
+ mb_cd_irq = pmac_ide_irq[i];
+ }
+
+ ++i;
+ }
+
+ pmac_ide_ports_known = 1;
}
+#endif /* CONFIG_BLK_DEV_IDE */
int
pmac_get_cpuinfo(char *buffer)
diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c
index 17226f8ff..4ceaa0dc9 100644
--- a/arch/ppc/kernel/pmac_support.c
+++ b/arch/ppc/kernel/pmac_support.c
@@ -7,8 +7,11 @@
#include <linux/nvram.h>
#include <asm/ptrace.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/prom.h>
+#include <asm/adb.h>
+#include <asm/pmu.h>
/*
* Read and write the non-volatile RAM on PowerMacs and CHRP machines.
@@ -32,8 +35,7 @@ void pmac_nvram_init(void)
}
nvram_naddrs = dp->n_addrs;
if (_machine == _MACH_chrp && nvram_naddrs == 1) {
- /* XXX for now */
- nvram_data = ioremap(0xf70e0000, NVRAM_SIZE);
+ nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
nvram_mult = 1;
} else if (nvram_naddrs == 1) {
nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
@@ -41,6 +43,8 @@ void pmac_nvram_init(void)
} else if (nvram_naddrs == 2) {
nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size);
nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size);
+ } else if (nvram_naddrs == 0 && adb_hardware == ADB_VIAPMU) {
+ nvram_naddrs = -1;
} else {
printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n",
nvram_naddrs);
@@ -49,7 +53,16 @@ void pmac_nvram_init(void)
unsigned char nvram_read_byte(int addr)
{
+ struct adb_request req;
+
switch (nvram_naddrs) {
+ case -1:
+ if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM,
+ (addr >> 8) & 0xff, addr & 0xff))
+ break;
+ while (!req.complete)
+ pmu_poll();
+ return req.reply[1];
case 1:
return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult];
case 2:
@@ -62,7 +75,16 @@ unsigned char nvram_read_byte(int addr)
void nvram_write_byte(unsigned char val, int addr)
{
+ struct adb_request req;
+
switch (nvram_naddrs) {
+ case -1:
+ if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM,
+ (addr >> 8) & 0xff, addr & 0xff, val))
+ break;
+ while (!req.complete)
+ pmu_poll();
+ break;
case 1:
nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val;
break;
diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c
index 1a31ffa9a..3c976506e 100644
--- a/arch/ppc/kernel/pmac_time.c
+++ b/arch/ppc/kernel/pmac_time.c
@@ -15,8 +15,11 @@
#include <linux/mm.h>
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/prom.h>
#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
#include "time.h"
@@ -44,7 +47,11 @@
/* Bits in IFR and IER */
#define T1_INT 0x40 /* Timer 1 interrupt */
-static int via_calibrate_decr(void)
+/*
+ * Calibrate the decrementer register using VIA timer 1.
+ * This is used both on powermacs and CHRP machines.
+ */
+int via_calibrate_decr(void)
{
struct device_node *vias;
volatile unsigned char *via;
@@ -54,9 +61,12 @@ static int via_calibrate_decr(void)
vias = find_devices("via-cuda");
if (vias == 0)
vias = find_devices("via-pmu");
+ if (vias == 0)
+ vias = find_devices("via");
if (vias == 0 || vias->n_addrs == 0)
return 0;
- via = (volatile unsigned char *) vias->addrs[0].address;
+ via = (volatile unsigned char *)
+ ioremap(vias->addrs[0].address, vias->addrs[0].size);
/* set timer 1 for continuous interrupts */
out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
@@ -123,14 +133,30 @@ pmac_get_rtc_time(void)
struct adb_request req;
/* Get the time from the RTC */
- cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME);
- while (!req.complete)
- cuda_poll();
- if (req.reply_len != 7)
- printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
- req.reply_len);
- return (req.reply[3] << 24) + (req.reply[4] << 16)
- + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET;
+ switch (adb_hardware) {
+ case ADB_VIACUDA:
+ if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
+ return 0;
+ while (!req.complete)
+ cuda_poll();
+ if (req.reply_len != 7)
+ printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
+ req.reply_len);
+ return (req.reply[3] << 24) + (req.reply[4] << 16)
+ + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET;
+ case ADB_VIAPMU:
+ if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
+ return 0;
+ while (!req.complete)
+ pmu_poll();
+ if (req.reply_len != 5)
+ printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
+ req.reply_len);
+ return (req.reply[1] << 24) + (req.reply[2] << 16)
+ + (req.reply[3] << 8) + req.reply[4] - RTC_OFFSET;
+ default:
+ return 0;
+ }
}
int pmac_set_rtc_time(unsigned long nowtime)
diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c
new file mode 100644
index 000000000..b7eab0fa1
--- /dev/null
+++ b/arch/ppc/kernel/ppc-stub.c
@@ -0,0 +1,705 @@
+/* $Id: ppc-stub.c,v 1.2 1998/04/11 17:29:03 geert Exp $
+ * ppc-stub.c: KGDB support for the Linux kernel.
+ *
+ * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC
+ * some stuff borrowed from Paul Mackerras' xmon
+ * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu)
+ *
+ * Modifications to run under Linux
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * This file originally came from the gdb sources, and the
+ * copyright notices have been retained below.
+ */
+
+/****************************************************************************
+
+ THIS SOFTWARE IS NOT COPYRIGHTED
+
+ HP offers the following for use in the public domain. HP makes no
+ warranty with regard to the software or its performance and the
+ user accepts the software "AS IS" with all faults.
+
+ HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+ TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ * Module name: remcom.c $
+ * Revision: 1.34 $
+ * Date: 91/03/09 12:29:49 $
+ * Contributor: Lake Stevens Instrument Division$
+ *
+ * Description: low level support for gdb debugger. $
+ *
+ * Considerations: only works on target hardware $
+ *
+ * Written by: Glenn Engel $
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * This code has been extensively tested on the Fujitsu SPARClite demo board.
+ *
+ * To enable debugger support, two things need to happen. One, a
+ * call to set_debug_traps() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint(). Breakpoint()
+ * simulates a breakpoint by executing a trap #1.
+ *
+ *************
+ *
+ * The following gdb commands are supported:
+ *
+ * command function Return value
+ *
+ * g return the value of the CPU registers hex data or ENN
+ * G set the value of the CPU registers OK or ENN
+ * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz
+ *
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
+ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ *
+ * k kill
+ *
+ * ? What was the last sigval ? SNN (signal NN)
+ *
+ * bBB..BB Set baud rate to BB..BB OK or BNN, then sets
+ * baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum. A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer. '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host: Reply:
+ * $m0,10#2a +$00010203040506070809101112131415#42
+ *
+ ****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+#include <asm/signal.h>
+#include <asm/system.h>
+#include <asm/kgdb.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+
+void breakinst(void);
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+static int initialized = 0;
+static int kgdb_active = 0;
+static u_int fault_jmp_buf[100];
+static int kdebug;
+
+static const char hexchars[]="0123456789abcdef";
+
+/* Place where we save old trap entries for restoration - sparc*/
+/* struct tt_entry kgdb_savettable[256]; */
+/* typedef void (*trapfunc_t)(void); */
+
+#if 0
+/* Install an exception handler for kgdb */
+static void exceptionHandler(int tnum, unsigned int *tfunc)
+{
+ /* We are dorking with a live trap table, all irqs off */
+}
+#endif
+
+int
+kgdb_setjmp(long *buf)
+{
+ asm ("mflr 0; stw 0,0(%0);"
+ "stw 1,4(%0); stw 2,8(%0);"
+ "mfcr 0; stw 0,12(%0);"
+ "stmw 13,16(%0)"
+ : : "r" (buf));
+ /* XXX should save fp regs as well */
+ return 0;
+}
+void
+kgdb_longjmp(long *buf, int val)
+{
+ if (val == 0)
+ val = 1;
+ asm ("lmw 13,16(%0);"
+ "lwz 0,12(%0); mtcrf 0x38,0;"
+ "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
+ "mtlr 0; mr 3,%1"
+ : : "r" (buf), "r" (val));
+}
+/* Convert ch from a hex digit to an int */
+static int
+hex(unsigned char ch)
+{
+ if (ch >= 'a' && ch <= 'f')
+ return ch-'a'+10;
+ if (ch >= '0' && ch <= '9')
+ return ch-'0';
+ if (ch >= 'A' && ch <= 'F')
+ return ch-'A'+10;
+ return -1;
+}
+
+/* Convert the memory pointed to by mem into hex, placing result in buf.
+ * Return a pointer to the last char put in buf (null), in case of mem fault,
+ * return 0.
+ */
+static unsigned char *
+mem2hex(char *mem, char *buf, int count)
+{
+ unsigned char ch;
+
+ if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+ debugger_fault_handler = kgdb_fault_handler;
+ while (count-- > 0) {
+ ch = *mem++;
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch & 0xf];
+ }
+ } else {
+ /* error condition */
+ }
+ debugger_fault_handler = 0;
+ *buf = 0;
+ return buf;
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem
+ * return a pointer to the character AFTER the last byte written.
+*/
+static char *
+hex2mem(char *buf, char *mem, int count)
+{
+ int i;
+ unsigned char ch;
+
+ if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+ debugger_fault_handler = kgdb_fault_handler;
+ for (i=0; i<count; i++) {
+ ch = hex(*buf++) << 4;
+ ch |= hex(*buf++);
+ *mem++ = ch;
+ }
+ flush_icache_range((int)mem, (int)mem+count);
+ } else {
+ /* error condition */
+ }
+ debugger_fault_handler = 0;
+ return mem;
+}
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+static int
+hexToInt(char **ptr, int *intValue)
+{
+ int numChars = 0;
+ int hexValue;
+
+ *intValue = 0;
+
+ if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+ debugger_fault_handler = kgdb_fault_handler;
+ while (**ptr) {
+ hexValue = hex(**ptr);
+ if (hexValue < 0)
+ break;
+
+ *intValue = (*intValue << 4) | hexValue;
+ numChars ++;
+
+ (*ptr)++;
+ }
+ } else {
+ /* error condition */
+ }
+ debugger_fault_handler = 0;
+
+ return (numChars);
+}
+
+/* scan for the sequence $<data>#<checksum> */
+static void
+getpacket(char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ unsigned char ch;
+
+ do {
+ /* wait around for the start character, ignore all other
+ * characters */
+ while ((ch = (getDebugChar() & 0x7f)) != '$') ;
+
+ checksum = 0;
+ xmitcsum = -1;
+
+ count = 0;
+
+ /* now, read until a # or end of buffer is found */
+ while (count < BUFMAX) {
+ ch = getDebugChar() & 0x7f;
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+
+ if (count >= BUFMAX)
+ continue;
+
+ buffer[count] = 0;
+
+ if (ch == '#') {
+ xmitcsum = hex(getDebugChar() & 0x7f) << 4;
+ xmitcsum |= hex(getDebugChar() & 0x7f);
+ if (checksum != xmitcsum)
+ putDebugChar('-'); /* failed checksum */
+ else {
+ putDebugChar('+'); /* successful transfer */
+ /* if a sequence char is present, reply the ID */
+ if (buffer[2] == ':') {
+ putDebugChar(buffer[0]);
+ putDebugChar(buffer[1]);
+ /* remove sequence chars from buffer */
+ count = strlen(buffer);
+ for (i=3; i <= count; i++)
+ buffer[i-3] = buffer[i];
+ }
+ }
+ }
+ } while (checksum != xmitcsum);
+}
+
+/* send the packet in buffer. */
+static void putpacket(unsigned char *buffer)
+{
+ unsigned char checksum;
+ int count;
+ unsigned char ch, recv;
+
+ /* $<packet info>#<checksum>. */
+ do {
+ putDebugChar('$');
+ checksum = 0;
+ count = 0;
+
+ while ((ch = buffer[count])) {
+ putDebugChar(ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ putDebugChar('#');
+ putDebugChar(hexchars[checksum >> 4]);
+ putDebugChar(hexchars[checksum & 0xf]);
+ recv = getDebugChar();
+ } while ((recv & 0x7f) != '+');
+}
+
+static void kgdb_flush_cache_all(void)
+{
+ flush_instruction_cache();
+}
+
+static inline int get_msr()
+{
+ int msr;
+ asm volatile("mfmsr %0" : "=r" (msr):);
+ return msr;
+}
+
+static inline void set_msr(int msr)
+{
+ asm volatile("mfmsr %0" : : "r" (msr));
+}
+
+/* Set up exception handlers for tracing and breakpoints
+ * [could be called kgdb_init()]
+ */
+void set_debug_traps(void)
+{
+#if 0
+ unsigned char c;
+
+ save_and_cli(flags);
+
+ /* In case GDB is started before us, ack any packets (presumably
+ * "$?#xx") sitting there.
+ *
+ * I've found this code causes more problems than it solves,
+ * so that's why it's commented out. GDB seems to work fine
+ * now starting either before or after the kernel -bwb
+ */
+
+ while((c = getDebugChar()) != '$');
+ while((c = getDebugChar()) != '#');
+ c = getDebugChar(); /* eat first csum byte */
+ c = getDebugChar(); /* eat second csum byte */
+ putDebugChar('+'); /* ack it */
+#endif
+ debugger = kgdb;
+ debugger_bpt = kgdb_bpt;
+ debugger_sstep = kgdb_sstep;
+ debugger_iabr_match = kgdb_iabr_match;
+ debugger_dabr_match = kgdb_dabr_match;
+
+ kgdb_interruptible(1);
+ initialized = 1;
+}
+
+static void kgdb_fault_handler(struct pt_regs *regs)
+{
+ kgdb_longjmp((long*)fault_jmp_buf, 1);
+}
+
+int kgdb_bpt(struct pt_regs *regs)
+{
+ handle_exception(regs);
+ return 1;
+}
+
+int kgdb_sstep(struct pt_regs *regs)
+{
+ handle_exception(regs);
+ return 1;
+}
+
+void kgdb(struct pt_regs *regs)
+{
+ handle_exception(regs);
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+ printk("kgdb doesn't support iabr, what?!?\n");
+ handle_exception(regs);
+ return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+ printk("kgdb doesn't support dabr, what?!?\n");
+ handle_exception(regs);
+ return 1;
+}
+
+/* Convert the SPARC hardware trap type code to a unix signal number. */
+/*
+ * This table contains the mapping between PowerPC hardware trap types, and
+ * signals, which are primarily what GDB understands.
+ */
+static struct hard_trap_info
+{
+ unsigned int tt; /* Trap type code for powerpc */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+ { 0x200, SIGSEGV }, /* machine check */
+ { 0x300, SIGSEGV }, /* address error (store) */
+ { 0x400, SIGBUS }, /* instruction bus error */
+ { 0x500, SIGINT }, /* interrupt */
+ { 0x600, SIGBUS }, /* alingment */
+ { 0x700, SIGILL }, /* reserved instruction or sumpin' */
+ { 0x800, SIGFPE }, /* fpu unavail */
+ { 0x900, SIGALRM }, /* decrementer */
+ { 0xa00, SIGILL }, /* reserved */
+ { 0xb00, SIGILL }, /* reserved */
+ { 0xc00, SIGCHLD }, /* syscall */
+ { 0xd00, SIGINT }, /* watch */
+ { 0xe00, SIGFPE }, /* fp assist */
+ { 0, 0} /* Must be last */
+};
+
+static int computeSignal(unsigned int tt)
+{
+ struct hard_trap_info *ht;
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ if (ht->tt == tt)
+ return ht->signo;
+
+ return SIGHUP; /* default for things we don't know about */
+}
+
+/*
+ * This function does all command processing for interfacing to gdb.
+ */
+static void
+handle_exception (struct pt_regs *regs)
+{
+ int sigval;
+ int addr;
+ int length;
+ char *ptr;
+ unsigned int msr;
+
+ if (debugger_fault_handler) {
+ debugger_fault_handler(regs);
+ panic("kgdb longjump failed!\n");
+ }
+ if (kgdb_active) {
+ printk("interrupt while in kgdb, returning\n");
+ return;
+ }
+ kgdb_active = 1;
+
+ printk("kgdb: entering handle_exception; trap [0x%x]\n",
+ (unsigned int)regs->trap);
+
+ kgdb_interruptible(0);
+ lock_kernel();
+ msr = get_msr();
+ set_msr(msr & ~MSR_EE); /* disable interrupts */
+
+ if (regs->nip == (unsigned long)breakinst) {
+ /* Skip over breakpoint trap insn */
+ regs->nip += 4;
+ }
+
+ /* reply to host that an exception has occurred */
+ sigval = computeSignal(regs->trap);
+ ptr = remcomOutBuffer;
+
+ *ptr++ = 'S';
+ *ptr++ = hexchars[sigval >> 4];
+ *ptr++ = hexchars[sigval & 0xf];
+
+ *ptr++ = 0;
+
+ putpacket(remcomOutBuffer);
+
+ /* XXX We may want to add some features dealing with poking the
+ * XXX page tables, ... (look at sparc-stub.c for more info)
+ * XXX also required hacking to the gdb sources directly...
+ */
+
+ while (1) {
+ remcomOutBuffer[0] = 0;
+
+ getpacket(remcomInBuffer);
+ switch (remcomInBuffer[0]) {
+ case '?': /* report most recent signal */
+ remcomOutBuffer[0] = 'S';
+ remcomOutBuffer[1] = hexchars[sigval >> 4];
+ remcomOutBuffer[2] = hexchars[sigval & 0xf];
+ remcomOutBuffer[3] = 0;
+ break;
+#if 0
+ case 'q': /* this screws up gdb for some reason...*/
+ {
+ extern long _start, sdata, __bss_start;
+
+ ptr = &remcomInBuffer[1];
+ if (strncmp(ptr, "Offsets", 7) != 0)
+ break;
+
+ ptr = remcomOutBuffer;
+ sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x",
+ &_start, &sdata, &__bss_start);
+ break;
+ }
+#endif
+ case 'd':
+ /* toggle debug flag */
+ kdebug ^= 1;
+ break;
+
+ case 'g': /* return the value of the CPU registers.
+ * some of them are non-PowerPC names :(
+ * they are stored in gdb like:
+ * struct {
+ * u32 gpr[32];
+ * f64 fpr[32];
+ * u32 pc, ps, cnd, lr; (ps=msr)
+ * u32 cnt, xer, mq;
+ * }
+ */
+ {
+ int i;
+ ptr = remcomOutBuffer;
+ /* General Purpose Regs */
+ ptr = mem2hex((char *)regs, ptr, 32 * 4);
+ /* Floating Point Regs - FIXME */
+ /*ptr = mem2hex((char *), ptr, 32 * 8);*/
+ for(i=0; i<(32*8*2); i++) { /* 2chars/byte */
+ ptr[i] = '0';
+ }
+ ptr += 32*8*2;
+ /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
+ ptr = mem2hex((char *)&regs->nip, ptr, 4);
+ ptr = mem2hex((char *)&regs->msr, ptr, 4);
+ ptr = mem2hex((char *)&regs->ccr, ptr, 4);
+ ptr = mem2hex((char *)&regs->link, ptr, 4);
+ ptr = mem2hex((char *)&regs->ctr, ptr, 4);
+ ptr = mem2hex((char *)&regs->xer, ptr, 4);
+ }
+ break;
+
+ case 'G': /* set the value of the CPU registers */
+ {
+ ptr = &remcomInBuffer[1];
+
+ /*
+ * If the stack pointer has moved, you should pray.
+ * (cause only god can help you).
+ */
+
+ /* General Purpose Regs */
+ hex2mem(ptr, (char *)regs, 32 * 4);
+
+ /* Floating Point Regs - FIXME?? */
+ /*ptr = hex2mem(ptr, ??, 32 * 8);*/
+ ptr += 32*8*2;
+
+ /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
+ ptr = hex2mem(ptr, (char *)&regs->nip, 4);
+ ptr = hex2mem(ptr, (char *)&regs->msr, 4);
+ ptr = hex2mem(ptr, (char *)&regs->ccr, 4);
+ ptr = hex2mem(ptr, (char *)&regs->link, 4);
+ ptr = hex2mem(ptr, (char *)&regs->ctr, 4);
+ ptr = hex2mem(ptr, (char *)&regs->xer, 4);
+
+ strcpy(remcomOutBuffer,"OK");
+ }
+ break;
+ case 'H':
+ /* dont do anything, yet, just acknowledge */
+ hexToInt(&ptr, &addr);
+ strcpy(remcomOutBuffer,"OK");
+ break;
+
+ case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ /* Try to read %x,%x. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt(&ptr, &addr)
+ && *ptr++ == ','
+ && hexToInt(&ptr, &length)) {
+ if (mem2hex((char *)addr, remcomOutBuffer,length))
+ break;
+ strcpy (remcomOutBuffer, "E03");
+ } else {
+ strcpy(remcomOutBuffer,"E01");
+ }
+ break;
+
+ case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+ /* Try to read '%x,%x:'. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt(&ptr, &addr)
+ && *ptr++ == ','
+ && hexToInt(&ptr, &length)
+ && *ptr++ == ':') {
+ if (hex2mem(ptr, (char *)addr, length)) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "E03");
+ }
+ } else {
+ strcpy(remcomOutBuffer, "E02");
+ }
+ break;
+
+
+ case 'k': /* kill the program, actually just continue */
+ case 'c': /* cAA..AA Continue; address AA..AA optional */
+ /* try to read optional parameter, pc unchanged if no parm */
+
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr, &addr)) {
+ regs->nip = addr;
+ }
+
+/* Need to flush the instruction cache here, as we may have deposited a
+ * breakpoint, and the icache probably has no way of knowing that a data ref to
+ * some location may have changed something that is in the instruction cache.
+ */
+ kgdb_flush_cache_all();
+ set_msr(msr);
+ kgdb_interruptible(1);
+ unlock_kernel();
+ kgdb_active = 0;
+ return;
+
+ case 's':
+ kgdb_flush_cache_all();
+ regs->msr |= MSR_SE;
+ set_msr(msr | MSR_SE);
+ unlock_kernel();
+ kgdb_active = 0;
+ return;
+
+ case 'r': /* Reset (if user process..exit ???)*/
+ panic("kgdb reset.");
+ break;
+ } /* switch */
+ if (remcomOutBuffer[0] && kdebug) {
+ printk("remcomInBuffer: %s\n", remcomInBuffer);
+ printk("remcomOutBuffer: %s\n", remcomOutBuffer);
+ }
+ /* reply to the request */
+ putpacket(remcomOutBuffer);
+ } /* while(1) */
+}
+
+/* This function will generate a breakpoint exception. It is used at the
+ beginning of a program to sync up with a debugger and can be used
+ otherwise as a quick means to stop program execution and "break" into
+ the debugger. */
+
+void
+breakpoint(void)
+{
+ if (!initialized) {
+ printk("breakpoint() called b4 kgdb init\n");
+ return;
+ }
+
+ asm(" .globl breakinst
+ breakinst: trap
+ ");
+}
diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c
index 386bfe3ac..e1803a269 100644
--- a/arch/ppc/kernel/ppc_htab.c
+++ b/arch/ppc/kernel/ppc_htab.c
@@ -1,5 +1,5 @@
/*
- * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $
+ * $Id: ppc_htab.c,v 1.17 1998/03/14 07:52:49 cort Exp $
*
* PowerPC hash table management proc entry. Will show information
* about the current hash table and will allow changes to it.
@@ -12,6 +12,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
@@ -88,6 +89,7 @@ struct inode_operations proc_ppc_htab_inode_operations = {
#define PMC1 953
#define PMC2 954
+#ifndef CONFIG_8xx
char *pmc1_lookup(unsigned long mmcr0)
{
switch ( mmcr0 & (0x7f<<7) )
@@ -123,7 +125,7 @@ char *pmc2_lookup(unsigned long mmcr0)
return "unknown";
}
}
-
+#endif /* CONFIG_8xx */
/*
* print some useful info about the hash table. This function
@@ -133,6 +135,7 @@ char *pmc2_lookup(unsigned long mmcr0)
static ssize_t ppc_htab_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
+#ifndef CONFIG_8xx
unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
int n = 0, valid;
unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0;
@@ -249,6 +252,9 @@ return_string:
copy_to_user(buf, buffer + *ppos, n);
*ppos += n;
return n;
+#else /* CONFIG_8xx */
+ return 0;
+#endif /* CONFIG_8xx */
}
/*
@@ -257,6 +263,7 @@ return_string:
static ssize_t ppc_htab_write(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
+#ifndef CONFIG_8xx
unsigned long tmp;
if ( current->uid != 0 )
return -EACCES;
@@ -493,6 +500,9 @@ static ssize_t ppc_htab_write(struct file * file, const char * buffer,
reset_SDR1();
#endif
return count;
+#else /* CONFIG_8xx */
+ return 0;
+#endif /* CONFIG_8xx */
}
@@ -512,4 +522,3 @@ ppc_htab_lseek(struct file * file, loff_t offset, int orig)
return(-EINVAL);
}
}
-
diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c
index 6ec6258ab..29df507d3 100644
--- a/arch/ppc/kernel/ppc_ksyms.c
+++ b/arch/ppc/kernel/ppc_ksyms.c
@@ -4,9 +4,7 @@
#include <linux/elfcore.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <linux/bios32.h>
#include <linux/interrupt.h>
-#include <linux/pci.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
@@ -18,9 +16,11 @@
#include <asm/pgtable.h>
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/pci-bridge.h>
+#include <asm/irq.h>
extern void transfer_to_handler(void);
extern void int_return(void);
@@ -49,9 +49,13 @@ EXPORT_SYMBOL(sys_sigreturn);
EXPORT_SYMBOL(lost_interrupts);
EXPORT_SYMBOL(do_lost_interrupts);
EXPORT_SYMBOL(__ppc_bh_counter);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
-#if !defined(CONFIG_MACH_SPECIFIC)
+#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC)
EXPORT_SYMBOL(isa_io_base);
+#endif
+#if !defined(CONFIG_MACH_SPECIFIC)
EXPORT_SYMBOL(pci_dram_offset);
#endif
@@ -114,11 +118,15 @@ EXPORT_SYMBOL(outw);
EXPORT_SYMBOL(outl);
EXPORT_SYMBOL(outsl);*/
+EXPORT_SYMBOL(_insb);
+EXPORT_SYMBOL(_outsb);
EXPORT_SYMBOL(_insw);
EXPORT_SYMBOL(_outsw);
EXPORT_SYMBOL(_insl);
EXPORT_SYMBOL(_outsl);
EXPORT_SYMBOL(ioremap);
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
EXPORT_SYMBOL(start_thread);
@@ -140,6 +148,10 @@ EXPORT_SYMBOL(adb_autopoll);
EXPORT_SYMBOL(adb_register);
EXPORT_SYMBOL(cuda_request);
EXPORT_SYMBOL(cuda_send_request);
+EXPORT_SYMBOL(cuda_poll);
+EXPORT_SYMBOL(pmu_request);
+EXPORT_SYMBOL(pmu_send_request);
+EXPORT_SYMBOL(pmu_poll);
EXPORT_SYMBOL(abort);
EXPORT_SYMBOL(find_devices);
EXPORT_SYMBOL(find_type_devices);
@@ -148,7 +160,3 @@ EXPORT_SYMBOL(get_property);
EXPORT_SYMBOL(pci_io_base);
EXPORT_SYMBOL(pci_device_loc);
EXPORT_SYMBOL(note_scsi_host);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c
index 193ded4df..999406200 100644
--- a/arch/ppc/kernel/prep_pci.c
+++ b/arch/ppc/kernel/prep_pci.c
@@ -1,5 +1,5 @@
/*
- * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $
+ * $Id: prep_pci.c,v 1.16 1998/02/23 02:47:32 davem Exp $
* PReP pci functions.
* Originally by Gary Thomas
* rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
@@ -8,7 +8,6 @@
*/
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -271,6 +270,12 @@ static char Nobis_pci_IRQ_routes[] = {
#define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */
#define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */
+/*
+ * FIXME: This code incorrectly assumes there's only bus #0, breaking all
+ * PCI-to-PCI bridges. Also multi-function devices are not supported
+ * at all. [mj]
+ */
+
int
prep_pcibios_read_config_dword (unsigned char bus,
unsigned char dev, unsigned char offset, unsigned int *val)
@@ -319,16 +324,6 @@ prep_pcibios_read_config_byte (unsigned char bus,
unsigned char _val;
volatile unsigned char *ptr;
dev >>= 3;
- /* Note: the configuration registers don't always have this right! */
- if (offset == PCI_INTERRUPT_LINE)
- {
- *val = Motherboard_routes[Motherboard_map[dev]];
-/*printk("dev %d map %d route %d on board %d\n",
- dev,Motherboard_map[dev],
- Motherboard_routes[Motherboard_map[dev]],
- *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/
- return PCIBIOS_SUCCESSFUL;
- }
if ((bus != 0) || (dev > MAX_DEVNR))
{
*(unsigned long *)val = (unsigned long) 0xFFFFFFFF;
@@ -406,7 +401,7 @@ __initfunc(unsigned long route_pci_interrupts(void))
int i;
if ( _prep_type == _PREP_Motorola)
- {
+ {
switch (inb(0x800) & 0xF0)
{
case 0x10: /* MVME16xx */
@@ -430,7 +425,6 @@ __initfunc(unsigned long route_pci_interrupts(void))
break;
case 0x40: /* PowerStack */
default: /* Can't hurt, can it? */
-
Motherboard_map_name = "Blackhawk (Powerstack)";
Motherboard_map = Blackhawk_pci_IRQ_map;
Motherboard_routes = Blackhawk_pci_IRQ_routes;
@@ -474,3 +468,4 @@ __initfunc(unsigned long route_pci_interrupts(void))
*ibc_pcicon |= 0x20;
return 0;
}
+
diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c
index 0aee7cff4..5234435ca 100644
--- a/arch/ppc/kernel/prep_setup.c
+++ b/arch/ppc/kernel/prep_setup.c
@@ -80,8 +80,7 @@ prep_get_cpuinfo(char *buffer)
{
extern char *Motherboard_map_name;
extern RESIDUAL res;
- int i;
- int len;
+ int len, i;
#ifdef __SMP__
#define CD(X) (cpu_data[n].X)
@@ -93,7 +92,7 @@ prep_get_cpuinfo(char *buffer)
if ( res.ResidualLength == 0 )
return len;
-
+
/* print info about SIMMs */
len += sprintf(buffer+len,"simms\t\t: ");
for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ )
@@ -106,6 +105,7 @@ prep_get_cpuinfo(char *buffer)
}
len += sprintf(buffer+len,"\n");
+#if 0
/* TLB */
len += sprintf(buffer+len,"tlb\t\t:");
switch(res.VitalProductData.TLBAttrib)
@@ -123,7 +123,6 @@ prep_get_cpuinfo(char *buffer)
len += sprintf(buffer+len," not present\n");
break;
}
-
/* L1 */
len += sprintf(buffer+len,"l1\t\t: ");
switch(res.VitalProductData.CacheAttrib)
@@ -144,6 +143,7 @@ prep_get_cpuinfo(char *buffer)
len += sprintf(buffer+len,"not present\n");
break;
}
+#endif
/* L2 */
if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */
@@ -201,7 +201,11 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
}
}
#endif
-
+ /* make the serial port the console */
+ /* strcat(cmd_line,"console=ttyS0,9600n8"); */
+ /* use the normal console but send output to the serial port, too */
+ /*strcat(cmd_line,"console=tty0 console=ttyS0,9600n8");*/
+ sprintf(cmd_line,"%s console=tty0 console=ttyS0,9600n8", cmd_line);
printk("Boot arguments: %s\n", cmd_line);
#ifdef CONFIG_CS4232
@@ -256,9 +260,5 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
#ifdef CONFIG_VGA_CONSOLE
conswitchp = &vga_con;
#endif
-#ifdef CONFIG_FB
- /* Frame buffer device based console */
- conswitchp = &fb_con;
-#endif
#endif
}
diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c
index 4c3a91f91..3536eab43 100644
--- a/arch/ppc/kernel/prep_time.c
+++ b/arch/ppc/kernel/prep_time.c
@@ -220,7 +220,7 @@ static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
#ifdef CONFIG_HEARTBEAT
/* use hard disk LED as a heartbeat instead -- much more useful
for debugging -- Cort */
- switch(kstat.interrupts[0] % 101)
+ switch(kstat_irqs(0) % 101)
{
/* act like an actual heart beat -- ie thump-thump-pause... */
case 0:
diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c
index 7ffaf58c0..1c993bdc1 100644
--- a/arch/ppc/kernel/process.c
+++ b/arch/ppc/kernel/process.c
@@ -1,4 +1,3 @@
-
/*
* linux/arch/ppc/kernel/process.c
*
@@ -77,8 +76,13 @@ struct task_struct *current_set[NR_CPUS] = {&init_task, };
int
dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs)
{
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if (last_task_used_math == current)
giveup_fpu();
+#endif
memcpy(fpregs, &current->tss.fpr[0], sizeof(*fpregs));
return 1;
}
@@ -98,7 +102,7 @@ int check_stack(struct task_struct *tsk)
printk("tss.magic bad: %08x\n", tsk->tss.magic);
}
#endif
-
+
if ( !tsk )
printk("check_stack(): tsk bad tsk %p\n",tsk);
@@ -157,17 +161,21 @@ switch_to(struct task_struct *prev, struct task_struct *new)
#endif
#ifdef SHOW_TASK_SWITCHES
- printk("%s/%d -> %s/%d cpu %d\n",
+ printk("%s/%d -> %s/%d NIP %08lx cpu %d sfr %d lock %x\n",
prev->comm,prev->pid,
- new->comm,new->pid,new->processor);
+ new->comm,new->pid,new->tss.regs->nip,new->processor,
+ new->tss.smp_fork_ret,scheduler_lock.lock);
#endif
#ifdef __SMP__
- /* bad news if last_task_used_math changes processors right now -- Cort */
- if ( (last_task_used_math == new) &&
- (new->processor != new->last_processor) )
- panic("last_task_used_math switched processors");
+ /* avoid complexity of lazy save/restore of fpu
+ * by just saving it every time we switch out -- Cort
+ */
+ if ( prev->tss.regs->msr & MSR_FP )
+ smp_giveup_fpu(prev);
+
/* be noisy about processor changes for debugging -- Cort */
- if ( new->last_processor != new->processor )
+ if ( (new->last_processor != NO_PROC_ID) &&
+ (new->last_processor != new->processor) )
printk("switch_to(): changing cpu's %d -> %d %s/%d\n",
new->last_processor,new->processor,
new->comm,new->pid);
@@ -181,11 +189,6 @@ switch_to(struct task_struct *prev, struct task_struct *new)
_enable_interrupts(s);
}
-asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs)
-{
- return 0;
-}
-
void show_regs(struct pt_regs * regs)
{
int i;
@@ -257,12 +260,11 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
((unsigned long)p + sizeof(union task_union)
- STACK_FRAME_OVERHEAD)) - 2;
*childregs = *regs;
-
if ((childregs->msr & MSR_PR) == 0)
childregs->gpr[2] = (unsigned long) p; /* `current' in new task */
childregs->gpr[3] = 0; /* Result from fork() */
p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD;
- p->tss.regs = childregs;
+ p->tss.regs = childregs;
if (usp >= (unsigned long) regs) {
/* Stack is in kernel space - must adjust */
childregs->gpr[1] = (unsigned long)(childregs + 1);
@@ -271,18 +273,28 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
childregs->gpr[1] = usp;
}
p->tss.last_syscall = -1;
-
+
/*
* copy fpu info - assume lazy fpu switch now always
* -- Cort
*/
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if ( last_task_used_math == current )
giveup_fpu();
+#endif
memcpy(&p->tss.fpr, &current->tss.fpr, sizeof(p->tss.fpr));
p->tss.fpscr = current->tss.fpscr;
childregs->msr &= ~MSR_FP;
+#ifdef __SMP__
+ if ( (p->pid != 0) || !(clone_flags & CLONE_PID) )
+ p->tss.smp_fork_ret = 1;
+ p->last_processor = NO_PROC_ID;
+#endif /* __SMP__ */
return 0;
}
@@ -337,20 +349,48 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp)
shove_aux_table(sp);
}
+asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
+ struct pt_regs *regs)
+{
+ unsigned long clone_flags = p1;
+ int res;
+ lock_kernel();
+ res = do_fork(clone_flags, regs->gpr[1], regs);
+ /*
+ * only parent returns here, child returns to either
+ * syscall_ret_1() or kernel_thread()
+ * -- Cort
+ */
+#ifdef __SMP__
+ /* When we clone the idle task we keep the same pid but
+ * the return value of 0 for both causes problems.
+ * -- Cort
+ */
+ if ((current->pid == 0) && (current == &init_task))
+ res = 1;
+#endif /* __SMP__ */
+ unlock_kernel();
+ return res;
+}
asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
struct pt_regs *regs)
{
- int ret;
+ int res;
lock_kernel();
- ret = do_fork(SIGCHLD, regs->gpr[1], regs);
-#if 0/*def __SMP__*/
- if ( ret ) /* drop scheduler lock in child */
- scheduler_lock.lock = 0L;
-#endif /* __SMP__ */
+ res = do_fork(SIGCHLD, regs->gpr[1], regs);
+ /* only parent returns here */
+#ifdef __SMP__
+ /* When we clone the idle task we keep the same pid but
+ * the return value of 0 for both causes problems.
+ * -- Cort
+ */
+ if ((current->pid == 0) && (current == &init_task))
+ res = 1;
+#endif /* __SMP__ */
unlock_kernel();
- return ret;
+ return res;
}
asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
@@ -374,28 +414,6 @@ out:
return error;
}
-asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
- struct pt_regs *regs)
-{
- unsigned long clone_flags = p1;
- int res;
-
- lock_kernel();
- res = do_fork(clone_flags, regs->gpr[1], regs);
-#ifdef __SMP__
- /* When we clone the idle task we keep the same pid but
- * the return value of 0 for both causes problems.
- * -- Cort
- */
- if ((current->pid == 0) && (current == &init_task))
- res = 1;
- if ( 0 /*res*/ ) /* drop scheduler lock in child */
- scheduler_lock.lock = 0L;
-#endif /* __SMP__ */
- unlock_kernel();
- return res;
-}
-
void
print_backtrace(unsigned long *sp)
{
diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c
index 121ffea73..0e656caa1 100644
--- a/arch/ppc/kernel/prom.c
+++ b/arch/ppc/kernel/prom.c
@@ -16,6 +16,8 @@
#include <asm/prom.h>
#include <asm/page.h>
#include <asm/processor.h>
+#include <asm/irq.h>
+#include <asm/io.h>
/*
* Properties whose value is longer than this get excluded from our
@@ -50,6 +52,19 @@ struct pci_range {
unsigned size_lo;
};
+struct isa_reg_property {
+ unsigned space;
+ unsigned address;
+ unsigned size;
+};
+
+typedef unsigned long interpret_func(struct device_node *, unsigned long);
+static interpret_func interpret_pci_props;
+static interpret_func interpret_dbdma_props;
+static interpret_func interpret_isa_props;
+static interpret_func interpret_macio_props;
+static interpret_func interpret_root_props;
+
char *prom_display_paths[FB_MAX] __initdata = { 0, };
unsigned int prom_num_displays = 0;
@@ -60,19 +75,21 @@ extern char *klimit;
char *bootpath = 0;
char *bootdevice = 0;
-unsigned int rtas_data = 0;
-unsigned int rtas_entry = 0;
+unsigned int rtas_data = 0; /* virtual pointer */
+unsigned int rtas_entry = 0; /* physical pointer */
+unsigned int rtas_size = 0;
+char chunk[PAGE_SIZE*64];
static struct device_node *allnodes = 0;
static void *call_prom(const char *service, int nargs, int nret, ...);
-static void prom_print(const char *msg);
+ void prom_print(const char *msg);
static void prom_exit(void);
static unsigned long copy_device_tree(unsigned long, unsigned long);
static unsigned long inspect_node(phandle, struct device_node *, unsigned long,
unsigned long, struct device_node ***);
static unsigned long finish_node(struct device_node *, unsigned long,
- unsigned long);
+ interpret_func *);
static unsigned long check_display(unsigned long);
static int prom_next_node(phandle *);
@@ -119,6 +136,18 @@ prom_exit()
;
}
+void
+prom_enter(void)
+{
+ struct prom_args args;
+ unsigned long offset = reloc_offset();
+
+ args.service = RELOC("enter");
+ args.nargs = 0;
+ args.nret = 0;
+ RELOC(prom)(&args);
+}
+
static void *
call_prom(const char *service, int nargs, int nret, ...)
{
@@ -140,7 +169,7 @@ call_prom(const char *service, int nargs, int nret, ...)
return prom_args.args[nargs];
}
-static void
+void
prom_print(const char *msg)
{
const char *p, *q;
@@ -160,6 +189,11 @@ prom_print(const char *msg)
}
}
+
+#ifdef CONFIG_ALL_PPC
+unsigned char OF_type[16], OF_model[16];
+#endif
+
/*
* We enter here early on, when the Open Firmware prom is still
* handling exceptions and the MMU hash table for us.
@@ -169,11 +203,14 @@ prom_init(int r3, int r4, prom_entry pp)
{
unsigned long mem;
ihandle prom_rtas;
- unsigned int rtas_size;
unsigned long offset = reloc_offset();
int l;
char *p, *d;
+ /* check if we're prep, return if we are */
+ if ( *(unsigned long *)(0) == 0xdeadc0de )
+ return;
+
/* First get a handle for the stdout device */
RELOC(prom) = pp;
RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1,
@@ -209,27 +246,57 @@ prom_init(int r3, int r4, prom_entry pp)
prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas"));
if (prom_rtas != (void *) -1) {
- rtas_size = 0;
+ RELOC(rtas_size) = 0;
call_prom(RELOC("getprop"), 4, 1, prom_rtas,
- RELOC("rtas-size"), &rtas_size, sizeof(rtas_size));
+ RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size));
prom_print(RELOC("instantiating rtas..."));
- if (rtas_size == 0) {
+ if (RELOC(rtas_size) == 0) {
RELOC(rtas_data) = 0;
} else {
mem = (mem + 4095) & -4096; /* round to page bdry */
RELOC(rtas_data) = mem - KERNELBASE;
- mem += rtas_size;
+ mem += RELOC(rtas_size);
+ }
+ prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas"));
+ RELOC(rtas_data) = ((ulong)chunk+4095)&-4096;
+ {
+ int i, nargs;
+ struct prom_args prom_args;
+ nargs = 3;
+ prom_args.service = RELOC("call-method");
+ prom_args.nargs = nargs;
+ prom_args.nret = 2;
+ prom_args.args[0] = RELOC("instantiate-rtas");
+ prom_args.args[1] = prom_rtas;
+ prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE);
+ RELOC(prom)(&prom_args);
+ if (prom_args.args[nargs] != 0)
+ i = 0;
+ else
+ i = (int)prom_args.args[nargs+1];
+ RELOC(rtas_entry) = i;
}
- RELOC(rtas_entry) = (unsigned int)
- call_prom(RELOC("instantiate-rtas"), 1, 1,
- RELOC(rtas_data));
- if (RELOC(rtas_entry) == -1)
+ if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0))
prom_print(RELOC(" failed\n"));
else
prom_print(RELOC(" done\n"));
}
RELOC(klimit) = (char *) (mem - offset);
+#ifdef CONFIG_ALL_PPC
+ {
+
+ ihandle prom_root;
+
+ RELOC(prom_root) = call_prom(RELOC("finddevice"), 1, 1, RELOC("/"));
+ call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root),
+ RELOC("device_type"), RELOC(OF_type),
+ (void *) 16);
+ call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root),
+ RELOC("model"), RELOC(OF_model),
+ (void *) 16);
+ }
+#endif
}
/*
@@ -397,12 +464,18 @@ inspect_node(phandle node, struct device_node *dad,
return mem_start;
}
+/*
+ * finish_device_tree is called once things are running normally
+ * (i.e. with text and data mapped to the address they were linked at).
+ * It traverses the device tree and fills in the name, type,
+ * {n_}addrs and {n_}intrs fields of each node.
+ */
void
finish_device_tree(void)
{
unsigned long mem = (unsigned long) klimit;
- mem = finish_node(allnodes, mem, 0UL);
+ mem = finish_node(allnodes, mem, NULL);
printk(KERN_INFO "device tree used %lu bytes\n",
mem - (unsigned long) allnodes);
klimit = (char *) mem;
@@ -410,23 +483,53 @@ finish_device_tree(void)
static unsigned long
finish_node(struct device_node *np, unsigned long mem_start,
- unsigned long base_address)
+ interpret_func *ifunc)
{
- struct reg_property *rp;
- struct pci_reg_property *pci_addrs;
- struct address_range *adr;
struct device_node *child;
- int i, l;
np->name = get_property(np, "name", 0);
np->type = get_property(np, "device_type", 0);
- /* get all the device addresses and interrupts */
- adr = (struct address_range *) mem_start;
+ /* get the device addresses and interrupts */
+ if (ifunc != NULL)
+ mem_start = ifunc(np, mem_start);
+
+ if (!strcmp(np->name, "device-tree"))
+ ifunc = interpret_root_props;
+ else if (np->type == 0)
+ ifunc = NULL;
+ else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
+ ifunc = interpret_pci_props;
+ else if (!strcmp(np->type, "dbdma")
+ || (ifunc == interpret_dbdma_props
+ && (!strcmp(np->type, "escc")
+ || !strcmp(np->type, "media-bay"))))
+ ifunc = interpret_dbdma_props;
+ else if (!strcmp(np->type, "mac-io"))
+ ifunc = interpret_macio_props;
+ else if (!strcmp(np->type, "isa"))
+ ifunc = interpret_isa_props;
+ else
+ ifunc = NULL;
+
+ for (child = np->child; child != NULL; child = child->sibling)
+ mem_start = finish_node(child, mem_start, ifunc);
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_pci_props(struct device_node *np, unsigned long mem_start)
+{
+ struct address_range *adr;
+ struct pci_reg_property *pci_addrs;
+ int i, l, *ip;
+
pci_addrs = (struct pci_reg_property *)
get_property(np, "assigned-addresses", &l);
- i = 0;
- if (pci_addrs != 0) {
+ if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
while ((l -= sizeof(struct pci_reg_property)) >= 0) {
/* XXX assumes PCI addresses mapped 1-1 to physical */
adr[i].space = pci_addrs[i].addr.a_hi;
@@ -434,36 +537,194 @@ finish_node(struct device_node *np, unsigned long mem_start,
adr[i].size = pci_addrs[i].size_lo;
++i;
}
- } else {
- rp = (struct reg_property *) get_property(np, "reg", &l);
- if (rp != 0) {
- while ((l -= sizeof(struct reg_property)) >= 0) {
- adr[i].space = 0;
- adr[i].address = rp[i].address + base_address;
- adr[i].size = rp[i].size;
- ++i;
- }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 0;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_dbdma_props(struct device_node *np, unsigned long mem_start)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ unsigned long base_address;
+ int i, l, *ip;
+ struct device_node *db;
+
+ base_address = 0;
+ for (db = np->parent; db != NULL; db = db->parent) {
+ if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
+ base_address = db->addrs[0].address;
+ break;
}
}
- if (i > 0) {
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 0;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
np->addrs = adr;
np->n_addrs = i;
mem_start += i * sizeof(struct address_range);
}
- np->intrs = (int *) get_property(np, "AAPL,interrupts", &l);
- if (np->intrs == 0)
- np->intrs = (int *) get_property(np, "interrupts", &l);
- if (np->intrs != 0)
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 0;
+ }
+ }
- if (np->type != 0 && np->n_addrs > 0
- && (strcmp(np->type, "dbdma") == 0
- || strcmp(np->type, "mac-io") == 0))
- base_address = np->addrs[0].address;
+ return mem_start;
+}
- for (child = np->child; child != NULL; child = child->sibling)
- mem_start = finish_node(child, mem_start, base_address);
+static unsigned long
+interpret_macio_props(struct device_node *np, unsigned long mem_start)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ unsigned long base_address;
+ int i, l, *ip;
+ struct device_node *db;
+
+ base_address = 0;
+ for (db = np->parent; db != NULL; db = db->parent) {
+ if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
+ base_address = db->addrs[0].address;
+ break;
+ }
+ }
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 0;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / (2 * sizeof(int));
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = openpic_to_irq(*ip++);
+ np->intrs[i].sense = *ip++;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_isa_props(struct device_node *np, unsigned long mem_start)
+{
+ struct isa_reg_property *rp;
+ struct address_range *adr;
+ int i, l, *ip;
+
+ rp = (struct isa_reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = rp[i].space;
+ adr[i].address = rp[i].address
+ + (adr[i].space? 0: _ISA_MEM_BASE);
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / (2 * sizeof(int));
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = *ip++;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_root_props(struct device_node *np, unsigned long mem_start)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ int i, l, *ip;
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 0;
+ adr[i].address = rp[i].address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 0;
+ }
+ }
return mem_start;
}
@@ -648,14 +909,14 @@ call_rtas(const char *service, int nargs, int nret,
printk(KERN_ERR "No RTAS service called %s\n", service);
return -1;
}
- u.words[0] = *tokp;
+ u.words[0] = __pa(*tokp);
u.words[1] = nargs;
u.words[2] = nret;
va_start(list, outputs);
for (i = 0; i < nargs; ++i)
u.words[i+3] = va_arg(list, unsigned long);
va_end(list);
- enter_rtas(&u);
+ enter_rtas((void *)__pa(&u));
if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i)
outputs[i] = u.words[i+nargs+4];
diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c
index 4f6068c6d..ce2f35058 100644
--- a/arch/ppc/kernel/ptrace.c
+++ b/arch/ppc/kernel/ptrace.c
@@ -390,8 +390,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
tmp = get_reg(child, addr);
}
else if (addr >= PT_FPR0 && addr <= PT_FPSCR) {
+#ifdef __SMP__
+ if (child->tss.regs->msr & MSR_FP )
+ smp_giveup_fpu(child);
+#else
+ /* only current can be last task to use math on SMP */
if (last_task_used_math == child)
giveup_fpu();
+#endif
tmp = ((long *)child->tss.fpr)[addr - PT_FPR0];
}
else
@@ -423,8 +429,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
goto out;
}
if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) {
+#ifndef __SMP__
+ if (child->tss.regs->msr & MSR_FP )
+ smp_giveup_fpu(child);
+#else
if (last_task_used_math == child)
giveup_fpu();
+#endif
((long *)child->tss.fpr)[addr - PT_FPR0] = data;
ret = 0;
goto out;
diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c
index fdf62e921..b5516d0e5 100644
--- a/arch/ppc/kernel/residual.c
+++ b/arch/ppc/kernel/residual.c
@@ -1,5 +1,5 @@
/*
- * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $
+ * $Id: residual.c,v 1.7 1998/03/08 05:49:20 davem Exp $
*
* Code to deal with the PReP residual data.
*
diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c
index 86407e273..bdf05af76 100644
--- a/arch/ppc/kernel/setup.c
+++ b/arch/ppc/kernel/setup.c
@@ -1,5 +1,5 @@
/*
- * $Id: setup.c,v 1.48 1998/01/01 10:04:44 paulus Exp $
+ * $Id: setup.c,v 1.68 1998/04/07 08:20:33 geert Exp $
* Common prep/pmac/chrp boot and setup code.
*/
@@ -13,10 +13,26 @@
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/residual.h>
#include <asm/io.h>
#include <asm/ide.h>
#include <asm/prom.h>
+#include <asm/processor.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+#include <asm/bootinfo.h>
+#include <asm/setup.h>
+#include <asm/amigappc.h>
+extern unsigned long m68k_machtype;
+extern void amiga_reset (void);
+extern struct mem_info m68k_ramdisk;
+extern int m68k_parse_bootinfo(const struct bi_record *);
+extern char _end[];
+#endif
extern char cmd_line[512];
char saved_command_line[256];
@@ -26,13 +42,13 @@ unsigned char aux_device_present;
unsigned long ISA_DMA_THRESHOLD;
unsigned long DMA_MODE_READ, DMA_MODE_WRITE;
int _machine;
+/* if we have openfirmware */
+unsigned long have_of;
#endif /* ! CONFIG_MACH_SPECIFIC */
/* copy of the residual data */
RESIDUAL res;
int _prep_type;
-/* if we have openfirmware */
-unsigned long have_of;
/*
* Perhaps we can put the pmac screen_info[] here
@@ -40,6 +56,7 @@ unsigned long have_of;
* Until we get multiple-console support in here
* that is. -- Cort
*/
+#ifndef CONFIG_MBX
#if !defined(CONFIG_PMAC_CONSOLE)
struct screen_info screen_info = {
0, 25, /* orig-x, orig-y */
@@ -65,29 +82,66 @@ void pmac_find_display(void)
}
#endif
+#else /* CONFIG_MBX */
+
+/* We need this to satisfy some external references until we can
+ * strip the kernel down.
+ */
+struct screen_info screen_info = {
+ 0, 25, /* orig-x, orig-y */
+ { 0, 0 }, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 80, /* orig-video-cols */
+ 0,0,0, /* ega_ax, ega_bx, ega_cx */
+ 25, /* orig-video-lines */
+ 0, /* orig-video-isVGA */
+ 16 /* orig-video-points */
+};
+#endif /* CONFIG_MBX */
+
/* cmd is ignored for now... */
void machine_restart(char *cmd)
{
struct adb_request req;
unsigned long flags;
unsigned long i = 10000;
-#if 0
+#if 0
int err;
#endif
switch(_machine)
{
case _MACH_Pmac:
- cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
- for (;;)
- cuda_poll();
+ switch (adb_hardware) {
+ case ADB_VIACUDA:
+ cuda_request(&req, NULL, 2, CUDA_PACKET,
+ CUDA_RESET_SYSTEM);
+ for (;;)
+ cuda_poll();
+ break;
+ case ADB_VIAPMU:
+ pmu_request(&req, NULL, 1, PMU_RESET);
+ for (;;)
+ pmu_poll();
+ break;
+ default:
+ }
break;
+
case _MACH_chrp:
#if 0 /* RTAS doesn't seem to work on Longtrail.
For now, do it the same way as the PReP. */
- err = call_rtas("system-reboot", 0, 1, NULL);
+ /*err = call_rtas("system-reboot", 0, 1, NULL);
printk("RTAS system-reboot returned %d\n", err);
- for (;;);
+ for (;;);*/
+
+ {
+ extern unsigned int rtas_entry, rtas_data, rtas_size;
+ unsigned long status, value;
+ printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n",
+ rtas_entry,rtas_data,rtas_size);
+ }
#endif
case _MACH_prep:
_disable_interrupts();
@@ -104,6 +158,23 @@ void machine_restart(char *cmd)
while ( i != 0 ) i++;
panic("restart failed\n");
break;
+ case _MACH_apus:
+ cli();
+ /* APUS:FIXME: Reset the system. Apparently there's
+ * more magic to it than this!?!?
+ */
+#if 0
+ APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET);
+ APUS_WRITE(APUS_REG_RESET,
+ REGRESET_PPCRESET|REGRESET_M68KRESET|
+ REGRESET_AMIGARESET|REGRESET_AUXRESET|
+ REGRESET_SCSIRESET);
+#endif
+ printk("\n**************************************\n");
+ printk("*** You can make a hard reset now! ***\n");
+ printk("**************************************\n");
+ for(;;);
+ break;
}
}
@@ -116,9 +187,23 @@ void machine_power_off(void)
switch (_machine) {
case _MACH_Pmac:
- cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
- for (;;)
- cuda_poll();
+ switch (adb_hardware) {
+ case ADB_VIACUDA:
+ cuda_request(&req, NULL, 2, CUDA_PACKET,
+ CUDA_POWERDOWN);
+ for (;;)
+ cuda_poll();
+ break;
+ case ADB_VIAPMU:
+ pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
+ 'M', 'A', 'T', 'T');
+ for (;;)
+ pmu_poll();
+ break;
+ default:
+ }
+ break;
+
case _MACH_chrp:
#if 0 /* RTAS doesn't seem to work on Longtrail.
For now, do it the same way as the PReP. */
@@ -126,9 +211,19 @@ void machine_power_off(void)
printk("RTAS system-reboot returned %d\n", err);
for (;;);
#endif
+
case _MACH_prep:
machine_restart(NULL);
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+ apm_set_power_state(APM_STATE_OFF);
+ for (;;);
+#endif
+#endif
}
+ for (;;)
+ ;
}
void machine_halt(void)
@@ -141,18 +236,90 @@ void machine_halt(void)
machine_power_off(); /* for now */
#endif
}
- else /* prep or chrp */
+ else /* prep, chrp or apus */
machine_restart(NULL);
}
+#ifdef CONFIG_BLK_DEV_IDE
void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
{
- if ( _machine == _MACH_Pmac )
+ switch (_machine) {
+ case _MACH_Pmac:
pmac_ide_init_hwif_ports(p,base,irq);
- else /* prep or chrp */
+ break;
+ case _MACH_chrp:
+ chrp_ide_init_hwif_ports(p,base,irq);
+ break;
+ case _MACH_prep:
prep_ide_init_hwif_ports(p,base,irq);
+ break;
+ }
+}
+#endif
+
+unsigned long cpu_temp(void)
+{
+ unsigned long i, temp, thrm1, dir;
+ int sanity;
+ /*
+ * setup thrm3 - need to give TAU at least 20us
+ * to do the compare so assume a 300MHz clock.
+ * We need 300*20 ticks then.
+ * -- Cort
+ */
+ asm("mtspr 1020, %1\n\t"
+ "mtspr 1021, %1\n\t"
+ "mtspr 1022, %0\n\t"::
+ "r" ( ((300*20)<<18) | THRM3_E), "r" (0) );
+
+#if 0
+ for ( i = 127 ; i >= 0 ; i-- )
+ {
+ asm("mtspr 1020, %0\n\t"::
+ "r" (THRM1_TID|THRM1_V|(i<<2)) );
+ /* check value */
+ while ( !( thrm1 & THRM1_TIV) )
+ asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );
+ if ( thrm1 & THRM1_TIN )
+ {
+ printk("tin set: %x tiv %x\n", thrm1,thrm1&THRM1_TIV);
+ goto out;
+ }
+
+ }
+#endif
+#if 0
+ i = 32; /* increment */
+ dir = 1; /* direction we're checking 0=up 1=down */
+ temp = 64; /* threshold checking against */
+ while ( i )
+ {
+ _set_THRM1((1<<29) | THRM1_V | (temp<<2) );
+ printk("checking %d in dir %d thrm set to %x/%x\n", temp,dir,
+ ( (1<<29) | THRM1_V | (temp<<2)),_get_THRM1());
+ /* check value */
+ sanity = 0x0fffffff;
+ while ( (!( thrm1 & THRM1_TIV)) && (sanity--) )
+ thrm1 = _get_THRM1();
+ /*asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );*/
+ if ( ! sanity || sanity==0xffffffff ) printk("no sanity\n");
+ /* temp is not in that direction */
+ if ( !(thrm1 & THRM1_TIN) )
+ {
+ printk("not in that dir thrm1 %x\n",thrm1);
+ if ( dir == 0 ) dir = 1;
+ else dir = 0;
+ }
+ if ( dir ) temp -= i;
+ else temp += i;
+ i /= 2;
+ }
+ asm("mtspr 1020, %0\n\t"
+ "mtspr 1022, %0\n\t" ::"r" (0) );
+#endif
+ return temp;
}
int get_cpuinfo(char *buffer)
@@ -160,9 +327,11 @@ int get_cpuinfo(char *buffer)
extern int pmac_get_cpuinfo(char *);
extern int chrp_get_cpuinfo(char *);
extern int prep_get_cpuinfo(char *);
+ extern int apus_get_cpuinfo(char *);
unsigned long len = 0;
unsigned long bogosum = 0;
unsigned long i;
+ unsigned long cr;
#ifdef __SMP__
extern unsigned long cpu_present_map;
extern struct cpuinfo_PPC cpu_data[NR_CPUS];
@@ -202,7 +371,18 @@ int get_cpuinfo(char *buffer)
len += sprintf(len+buffer, "603ev\n");
break;
case 8:
- len += sprintf(len+buffer, "750 (Arthur)\n");
+ len += sprintf(len+buffer,"750\n");
+ cr = _get_L2CR();
+ len += sprintf(len+buffer,"L2CR\t\t: %lx\n",cr);
+ if ( cr & (0x1<<1)) cr = 256;
+ else if ( cr & (0x2<<1)) cr = 512;
+ else if ( cr & (0x3<<1)) cr = 1024;
+ else cr = 0;
+ len += sprintf(len+buffer,"on-chip l2\t: "
+ "%ld KB (%s)\n",
+ cr,(_get_L2CR()&1) ? "on" : "off");
+ len += sprintf(len+buffer,"temperature \t: %lu C\n",
+ cpu_temp());
break;
case 9:
len += sprintf(len+buffer, "604e\n");
@@ -216,6 +396,7 @@ int get_cpuinfo(char *buffer)
break;
}
+
/*
* Assume here that all clock rates are the same in a
* smp system. -- Cort
@@ -290,6 +471,11 @@ int get_cpuinfo(char *buffer)
case _MACH_chrp:
len += chrp_get_cpuinfo(buffer+len);
break;
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ len += apus_get_cpuinfo(buffer+len);
+ break;
+#endif
}
return len;
}
@@ -302,43 +488,63 @@ __initfunc(unsigned long
identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7))
{
- extern unsigned long initrd_start, initrd_end;
extern setup_pci_ptrs(void);
- unsigned long boot_sdr1;
- ihandle prom_root;
- unsigned char type[16], model[16];
-
- asm("mfspr %0,25\n\t" :"=r" (boot_sdr1));
+#ifndef CONFIG_MBX8xx
- /*
- * if we have a sdr1 then we have openfirmware
- * and can ask it what machine we are (chrp/pmac/prep).
- * otherwise we're definitely prep. -- Cort
- */
- if ( !boot_sdr1 )
+#ifdef CONFIG_APUS
+ if ( r3 == 0x61707573 )
{
- /* we know for certain we're prep if no OF */
+ /* Parse bootinfo. The bootinfo is located right after
+ the kernel bss */
+ m68k_parse_bootinfo((const struct bi_record *)&_end);
+
have_of = 0;
- /* make a copy of residual data */
- if ( r3 )
- memcpy((void *)&res,(void *)(r3+KERNELBASE),
- sizeof(RESIDUAL));
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Take care of initrd if we have one. Use data from
+ bootinfo to avoid the need to initialize PPC
+ registers when kernel is booted via a PPC reset. */
+ if ( m68k_ramdisk.addr ) {
+ initrd_start = (unsigned long) __va(m68k_ramdisk.addr);
+ initrd_end = (unsigned long)
+ __va(m68k_ramdisk.size + m68k_ramdisk.addr);
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ return 0;
+ }
+#endif
+
#ifndef CONFIG_MACH_SPECIFIC
+ /* prep boot loader tells us if we're prep or not */
+ if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) )
+ {
_machine = _MACH_prep;
-#endif /* CONFIG_MACH_SPECIFIC */
+ have_of = 0;
+ } else
+ {
+ /* need to ask OF if we're chrp or pmac */
+ extern unsigned char OF_type[16], OF_model[16];
+ prom_print(OF_type);
+ prom_print(OF_model);
+ if ( !strncmp("chrp", OF_type,4) )
+ {
+ _machine = _MACH_chrp;
+ }
+ else
+ {
+ /*if ( !strncmp("Power Macintosh", type,15) )*/
+ _machine = _MACH_Pmac;
+ }
+ _machine = _MACH_Pmac;
+
}
- else
+#endif /* CONFIG_MACH_SPECIFIC */
+
+ if ( have_of )
{
- /*
- * init prom here, then ask the openfirmware
- * what machine we are (prep/chrp/pmac). We don't use
- * OF on prep just yet. -- Cort
- */
-#ifndef CONFIG_PREP /* don't use OF on prep yet */
- have_of = 1;
/* prom_init has already been called from __start */
finish_device_tree();
-
/*
* If we were booted via quik, r3 points to the physical
* address of the command-line parameters.
@@ -356,7 +562,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
} else {
struct device_node *chosen;
char *p;
-
+
#ifdef CONFIG_BLK_DEV_INITRD
if (r3 - KERNELBASE < 0x800000
&& r4 != 0 && r4 != 0xdeadbeef) {
@@ -365,7 +571,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
}
#endif
- chosen = find_path_device("/chosen");
+ chosen = find_devices("chosen");
if (chosen != NULL) {
p = get_property(chosen, "bootargs", NULL);
if (p != NULL)
@@ -373,47 +579,18 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
}
}
cmd_line[sizeof(cmd_line) - 1] = 0;
-#endif /* CONFIG_PREP */
-
-#ifndef CONFIG_MACH_SPECIFIC
-#if 0
- prom_root = call_prom("finddevice", 1, 1, "/");
- call_prom("getprop", 4, 1, prom_root, "device_type", &type,
- (void *) sizeof(type));
- call_prom("getprop", 4, 1, prom_root, "model", &type,
- (void *) sizeof(model));
- if ( !strncmp("chrp", type,4) )
- {
- _machine = _MACH_chrp;
- }
- else
- {
- /*if ( !strncmp("Power Macintosh", type,15) )*/
- _machine = _MACH_Pmac;
- }
-#else
-
-#ifdef CONFIG_CHRP
- _machine = _MACH_chrp;
-#endif /* CONFIG_CHRP */
-#ifdef CONFIG_PMAC
- _machine = _MACH_Pmac;
-#endif /* CONFIG_PMAC */
-#ifdef CONFIG_PREP
- _machine = _MACH_Prep;
-#endif /* CONFIG_PREP */
-#endif /* #if */
-#endif /* CONFIG_MACH_SPECIFIC */
}
+#ifdef CONFIG_PCI
/* so that pmac/chrp can use pci to find its console -- Cort */
setup_pci_ptrs();
-
+#endif
+
switch (_machine)
{
case _MACH_Pmac:
#if !defined(CONFIG_MACH_SPECIFIC)
- isa_io_base = PMAC_ISA_IO_BASE;
+ /* isa_io_base gets set in pmac_find_bridges */
isa_mem_base = PMAC_ISA_MEM_BASE;
pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
ISA_DMA_THRESHOLD = ~0L;
@@ -422,6 +599,10 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
#endif /* ! CONFIG_MACH_SPECIFIC */
break;
case _MACH_prep:
+ /* make a copy of residual data */
+ if ( r3 )
+ memcpy((void *)&res,(void *)(r3+KERNELBASE),
+ sizeof(RESIDUAL));
#if !defined(CONFIG_MACH_SPECIFIC)
isa_io_base = PREP_ISA_IO_BASE;
isa_mem_base = PREP_ISA_MEM_BASE;
@@ -434,13 +615,12 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
if ( res.ResidualLength != 0 )
{
if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) )
- _prep_type = 0x00;
+ _prep_type = _PREP_IBM;
else
- _prep_type = 0x01;
+ _prep_type = _PREP_Motorola;
}
else /* assume motorola if no residual (netboot?) */
_prep_type = _PREP_Motorola;
-
#ifdef CONFIG_BLK_DEV_RAM
/* take care of initrd if we have one */
if ( r4 )
@@ -457,7 +637,14 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
}
break;
case _MACH_chrp:
- /* LongTrail */
+#ifdef CONFIG_BLK_DEV_RAM
+ /* take care of initrd if we have one */
+ if ( r3 )
+ {
+ initrd_start = r3 + KERNELBASE;
+ initrd_end = r3+ r4 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_RAM */
#if !defined(CONFIG_MACH_SPECIFIC)
isa_io_base = CHRP_ISA_IO_BASE;
isa_mem_base = CHRP_ISA_MEM_BASE;
@@ -470,13 +657,33 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
default:
printk("Unknown machine type in identify_machine!\n");
}
- return 0;
-}
+#else /* CONFIG_MBX8xx */
+ extern setup_pci_ptrs(void);
-__initfunc(unsigned long
-bios32_init(unsigned long memory_start, unsigned long memory_end))
-{
- return memory_start;
+ if ( r3 )
+ memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
+
+ setup_pci_ptrs();
+
+#ifdef CONFIG_BLK_DEV_RAM
+ /* take care of initrd if we have one */
+ if ( r4 )
+ {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_RAM */
+ /* take care of cmd line */
+ if ( r6 )
+ {
+
+ *(char *)(r7+KERNELBASE) = 0;
+ strcpy(cmd_line, (char *)(r6+KERNELBASE));
+ }
+
+#endif /* CONFIG_MBX */
+
+ return 0;
}
__initfunc(void setup_arch(char **cmdline_p,
@@ -485,12 +692,18 @@ __initfunc(void setup_arch(char **cmdline_p,
extern void pmac_setup_arch(unsigned long *, unsigned long *);
extern void chrp_setup_arch(unsigned long *, unsigned long *);
extern void prep_setup_arch(unsigned long *, unsigned long *);
+ extern void apus_setup_arch(char **, unsigned long *, unsigned long *);
extern int panic_timeout;
extern char _etext[], _edata[];
extern char *klimit;
extern unsigned long find_available_memory(void);
extern unsigned long *end_of_DRAM;
+#ifdef CONFIG_XMON
+ extern void xmon_map_scc(void);
+ xmon_map_scc();
+#endif /* CONFIG_XMON */
+
/* reboot on panic */
panic_timeout = 180;
@@ -516,6 +729,12 @@ __initfunc(void setup_arch(char **cmdline_p,
case _MACH_chrp:
chrp_setup_arch(memory_start_p, memory_end_p);
break;
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ m68k_machtype = MACH_AMIGA;
+ apus_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+ break;
+#endif
default:
printk("Unknown machine %d in setup_arch()\n", _machine);
}
diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c
index ae6d3c5aa..1c977bb51 100644
--- a/arch/ppc/kernel/signal.c
+++ b/arch/ppc/kernel/signal.c
@@ -201,8 +201,13 @@ int sys_sigreturn(struct pt_regs *regs)
if (sc == (struct sigcontext_struct *)(sigctx.regs)) {
/* Last stacked signal - restore registers */
sr = (struct sigregs *) sigctx.regs;
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if (last_task_used_math == current)
giveup_fpu();
+#endif
if (copy_from_user(saved_regs, &sr->gp_regs,
sizeof(sr->gp_regs)))
goto badframe;
@@ -249,8 +254,13 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame,
if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
goto badframe;
- if (last_task_used_math == current)
- giveup_fpu();
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
+ if (last_task_used_math == current)
+ giveup_fpu();
+#endif
if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE)
|| __copy_to_user(&frame->fp_regs, current->tss.fpr,
ELF_NFPREG * sizeof(double))
diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c
index 33d3a23b4..217e695f8 100644
--- a/arch/ppc/kernel/smp.c
+++ b/arch/ppc/kernel/smp.c
@@ -1,5 +1,5 @@
/*
- * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $
+ * $Id: smp.c,v 1.22 1998/04/10 01:53:34 cort Exp $
*
* Smp support for ppc.
*
@@ -30,22 +30,27 @@
#include <asm/init.h>
#include <asm/io.h>
+#include "time.h"
+
int smp_threads_ready = 0;
volatile int smp_commenced = 0;
int smp_num_cpus = 1;
unsigned long cpu_present_map = 0;
volatile int cpu_number_map[NR_CPUS];
volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile int __cpu_logical_map[NR_CPUS];
static unsigned char boot_cpu_id = 0;
struct cpuinfo_PPC cpu_data[NR_CPUS];
-struct klock_info klock_info = { KLOCK_CLEAR, 0 };
+struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 };
volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */
-volatile unsigned long hash_table_lock;
+volatile unsigned long ipi_count;
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
int start_secondary(void *);
-extern void init_IRQ(void);
extern int cpu_idle(void *unused);
void smp_boot_cpus(void)
@@ -56,49 +61,75 @@ void smp_boot_cpus(void)
struct task_struct *p;
printk("Entering SMP Mode...\n");
+
+ for (i = 0; i < NR_CPUS; i++) {
+ cpu_number_map[i] = -1;
+ prof_counter[i] = 1;
+ prof_multiplier[i] = 1;
+ }
+
cpu_present_map = 0;
for(i=0; i < NR_CPUS; i++)
- cpu_number_map[i] = -1;
+ __cpu_logical_map[i] = -1;
smp_store_cpu_info(boot_cpu_id);
active_kernel_processor = boot_cpu_id;
current->processor = boot_cpu_id;
-
cpu_present_map |= 1;
+ cpu_number_map[boot_cpu_id] = 0;
+ __cpu_logical_map[0] = boot_cpu_id;
+
+ if ( _machine != _MACH_Pmac )
+ {
+ printk("SMP not supported on this machine.\n");
+ return;
+ }
+
/* assume a 2nd processor for now */
cpu_present_map |= (1 << 1);
smp_num_cpus = 2;
- cpu_number_map[boot_cpu_id] = 0;
/* create a process for second processor */
kernel_thread(start_secondary, NULL, CLONE_PID);
- cpu_number_map[1] = 1;
p = task[1];
if ( !p )
panic("No idle task for secondary processor\n");
p->processor = 1;
current_set[1] = p;
+ /* need to flush here since secondary bat's aren't setup */
+ dcbf((volatile unsigned long *)&current_set[1]);
+
/* setup entry point of secondary processor */
*(volatile unsigned long *)(0xf2800000)
= (unsigned long)secondary_entry-KERNELBASE;
- /* interrupt secondary to begin executing code */
eieio();
- *(volatile unsigned long *)(0xf80000c0) = 0;
+ /* interrupt secondary to begin executing code */
+ *(volatile unsigned long *)(0xf80000c0) = 0L;
eieio();
/* wait to see if the secondary made a callin (is actually up) */
- for ( timeout = 0; timeout < 1500 ; timeout++ )
+ for ( timeout = 0; timeout < 15000 ; timeout++ )
+ {
+ if(cpu_callin_map[1])
+ break;
udelay(100);
+ }
if(cpu_callin_map[1]) {
cpu_number_map[1] = 1;
+ __cpu_logical_map[i] = 1;
printk("Processor 1 found.\n");
+
+#if 0 /* this sync's the decr's */
+ set_dec(decrementer_count);
+#endif
+ /* interrupt secondary to sync the time bases */
+ smp_message_pass(1,0xf0f0, 0, 0);
+ /* interrupt secondary to begin executing code */
+ /**(volatile unsigned long *)(0xf80000c0) = 0L;
+ eieio();*/
} else {
smp_num_cpus--;
printk("Processor %d is stuck.\n", 1);
}
-{
- extern unsigned long amhere;
- printk("amhere: %x\n", amhere);
-}
}
void smp_commence(void)
@@ -128,16 +159,15 @@ int start_secondary(void *unused)
void smp_callin(void)
{
printk("SMP %d: smp_callin()\n",current->processor);
- /*calibrate_delay();*/
smp_store_cpu_info(1);
-
- /* assume we're just the secondary processor for now */
- cpu_callin_map[1] = 1;
- dcbf(&cpu_callin_map[1]);
+ set_dec(decrementer_count);
current->mm->mmap->vm_page_prot = PAGE_SHARED;
current->mm->mmap->vm_start = PAGE_OFFSET;
current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+
+ /* assume we're just the secondary processor for now */
+ cpu_callin_map[1] = 1;
while(!smp_commenced)
barrier();
@@ -149,21 +179,120 @@ void smp_setup(char *str, int *ints)
printk("SMP %d: smp_setup()\n",current->processor);
}
-void smp_message_pass(int target, int msg, unsigned long data, int wait)
+void smp_local_timer_interrupt(struct pt_regs * regs)
{
- printk("SMP %d: sending smp message\n",current->processor);
-#if 0
- if ( smp_processor_id() == 0 )
+ int cpu = smp_processor_id();
+ extern void update_one_process(struct task_struct *,unsigned long,
+ unsigned long,unsigned long,int);
+
+ if (!--prof_counter[cpu]) {
+ int user=0,system=0;
+ struct task_struct * p = current;
+
+ /*
+ * After doing the above, we need to make like
+ * a normal interrupt - otherwise timer interrupts
+ * ignore the global interrupt lock, which is the
+ * WrongThing (tm) to do.
+ */
+
+ if (user_mode(regs))
+ user=1;
+ else
+ system=1;
+
+ if (p->pid) {
+ update_one_process(p, 1, user, system, cpu);
+
+ p->counter -= 1;
+ if (p->counter < 0) {
+ p->counter = 0;
+ need_resched = 1;
+ }
+ if (p->priority < DEF_PRIORITY) {
+ kstat.cpu_nice += user;
+ kstat.per_cpu_nice[cpu] += user;
+ } else {
+ kstat.cpu_user += user;
+ kstat.per_cpu_user[cpu] += user;
+ }
+
+ kstat.cpu_system += system;
+ kstat.per_cpu_system[cpu] += system;
+
+ }
+ prof_counter[cpu]=prof_multiplier[cpu];
+ }
+}
+
+/*
+ * Dirty hack to get smp message passing working.
+ * Right now it only works for stop cpu's but will be setup
+ * later for more general message passing.
+ *
+ * As it is now, if we're sending two message as the same time
+ * we have race conditions. I avoided doing locks here since
+ * all that works right now is the stop cpu message.
+ *
+ * -- Cort
+ */
+int smp_message[NR_CPUS];
+void smp_message_recv(void)
+{
+ int msg = smp_message[smp_processor_id()];
+
+ printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);
+
+ /* make sure msg is for us */
+ if ( msg == -1 ) return;
+printk("recv after msg check\n");
+ switch( msg )
{
- /* interrupt secondary */
- *(volatile unsigned long *)(0xf80000c0) = 0;
+ case MSG_STOP_CPU:
+ __cli();
+ while (1) ;
+ break;
+ case 0xf0f0: /* syncing time bases - just return */
+ break;
+ default:
+ printk("SMP %d: smp_message_recv(): unknown msg %d\n",
+ smp_processor_id(), msg);
+ break;
}
- else
+ /* reset message */
+ smp_message[smp_processor_id()] = -1;
+}
+
+spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED;
+void smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ printk("SMP %d: sending smp message\n", current->processor);
+
+ spin_lock(&mesg_pass_lock);
+ if ( _machine != _MACH_Pmac )
+ return;
+
+#define OTHER (~smp_processor_id() & 1)
+
+ switch( target )
{
- /* interrupt primary */
- *(volatile unsigned long *)(0xf3019000);
+ case MSG_ALL:
+ smp_message[smp_processor_id()] = msg;
+ /* fall through */
+ case MSG_ALL_BUT_SELF:
+ smp_message[OTHER] = msg;
+ break;
+ default:
+ smp_message[target] = msg;
+ break;
}
-#endif
+ /* interrupt secondary processor */
+ /**(volatile unsigned long *)(0xf80000c0) = 0xffffffff;
+ eieio();*/
+ *(volatile unsigned long *)(0xf80000c0) = 0;
+ /* interrupt primary */
+ /**(volatile unsigned long *)(0xf3019000);*/
+ spin_unlock(&mesg_pass_lock);
}
int setup_profiling_timer(unsigned int multiplier)
diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c
new file mode 100644
index 000000000..dedf24806
--- /dev/null
+++ b/arch/ppc/kernel/softemu8xx.c
@@ -0,0 +1,96 @@
+/*
+ * Software emulation of some PPC instructions for the 8xx core.
+ *
+ * Copyright (C) 1998 Dan Malek (dmalek@jlc.net)
+ *
+ * Software floating emuation for the MPC8xx processor. I did this mostly
+ * because it was easier than trying to get the libraries compiled for
+ * software floating point. The goal is still to get the libraries done,
+ * but I lost patience and needed some hacks to at least get init and
+ * shells running. The first problem is the setjmp/longjmp that save
+ * and restore the floating point registers.
+ *
+ * For this emulation, our working registers are found on the register
+ * save area.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+
+/* Eventually we may need a look-up table, but this works for now.
+*/
+#define LFD 50
+#define LFDU 51
+#define STFD 54
+#define STFDU 55
+
+/*
+ * We return 0 on success, 1 on unimplemented instruction, and EFAULT
+ * if a load/store faulted.
+ */
+int
+Soft_emulate_8xx(struct pt_regs *regs)
+{
+ uint inst, instword;
+ uint flreg, idxreg, disp;
+ uint retval;
+ uint *ea, *ip;
+
+ retval = 0;
+
+ instword = *((uint *)regs->nip);
+ inst = instword >> 26;
+
+ flreg = (instword >> 21) & 0x1f;
+ idxreg = (instword >> 16) & 0x1f;
+ disp = instword & 0xffff;
+
+ ea = (uint *)(regs->gpr[idxreg] + disp);
+ ip = (uint *)&current->tss.fpr[flreg];
+
+ if (inst == LFD) {
+ if (copy_from_user(ip, ea, sizeof(double)))
+ retval = EFAULT;
+ }
+ else if (inst == LFDU) {
+
+ if (copy_from_user(ip, ea, sizeof(double)))
+ retval = EFAULT;
+ else
+ regs->gpr[idxreg] = (uint)ea;
+ }
+ else if (inst == STFD) {
+
+ if (copy_to_user(ea, ip, sizeof(double)))
+ retval = EFAULT;
+ }
+ else if (inst == STFDU) {
+
+ if (copy_to_user(ea, ip, sizeof(double)))
+ retval = EFAULT;
+ else
+ regs->gpr[idxreg] = (uint)ea;
+ }
+ else {
+ retval = 1;
+ }
+
+ if (retval == 0)
+ regs->nip += 4;
+ return(retval);
+}
diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c
index 319db15de..5a1063d4e 100644
--- a/arch/ppc/kernel/time.c
+++ b/arch/ppc/kernel/time.c
@@ -1,11 +1,25 @@
/*
- * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $
+ * $Id: time.c,v 1.28 1998/04/07 18:49:49 cort Exp $
* Common time routines among all ppc machines.
*
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
* Paul Mackerras' version and mine for PReP and Pmac.
+ * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net).
+ *
+ * Since the MPC8xx has a programmable interrupt timer, I decided to
+ * use that rather than the decrementer. Two reasons: 1.) the clock
+ * frequency is low, causing 2.) a long wait in the timer interrupt
+ * while ((d = get_dec()) == dval)
+ * loop. The MPC8xx can be driven from a variety of input clocks,
+ * so a number of assumptions have been made here because the kernel
+ * parameter HZ is a constant. We assume (correctly, today :-) that
+ * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal.
+ * This is then divided by 4, providing a 8192 Hz clock into the PIT.
+ * Since it is not possible to get a nice 100 Hz clock out of this, without
+ * creating a software PLL, I have set HZ to 128. -- Dan
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -22,12 +36,21 @@
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/nvram.h>
+#include <asm/cache.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#endif
#include "time.h"
/* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */
int (*set_rtc_time)(unsigned long);
+void smp_local_timer_interrupt(struct pt_regs *);
+
/* keep track of when we need to update the rtc */
unsigned long last_rtc_update = 0;
@@ -48,6 +71,14 @@ unsigned count_period_den; /* count_period_num / count_period_den us */
void timer_interrupt(struct pt_regs * regs)
{
int dval, d;
+ unsigned long cpu = smp_processor_id();
+ /* save the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ unsigned dcache_locked = unlock_dcache();
+
+if ( smp_processor_id() ) printk("SMP 1: timer intr\n");
+ hardirq_enter(cpu);
+
while ((dval = get_dec()) < 0) {
/*
* Wait for the decrementer to change, then jump
@@ -57,17 +88,49 @@ void timer_interrupt(struct pt_regs * regs)
while ((d = get_dec()) == dval)
;
set_dec(d + decrementer_count);
- do_timer(regs);
- /*
- * update the rtc when needed
- */
- if ( xtime.tv_sec > last_rtc_update + 660 )
- if (set_rtc_time(xtime.tv_sec) == 0)
- last_rtc_update = xtime.tv_sec;
- else
- last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ if ( !smp_processor_id() )
+ {
+ do_timer(regs);
+ /*
+ * update the rtc when needed
+ */
+ if ( xtime.tv_sec > last_rtc_update + 660 )
+ if (set_rtc_time(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
}
+#ifdef __SMP__
+ smp_local_timer_interrupt(regs);
+#endif
+
+ hardirq_exit(cpu);
+ /* restore the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ lock_dcache(dcache_locked);
+}
+
+#ifdef CONFIG_MBX
+/* A place holder for time base interrupts, if they are ever enabled.
+*/
+void timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+}
+
+/* The RTC on the MPC8xx is an internal register.
+ * We want to protect this during power down, so we need to unlock,
+ * modify, and re-lock.
+ */
+static int
+mbx_set_rtc_time(unsigned long time)
+{
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY;
+ ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time;
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY;
+ return(0);
}
+#endif /* CONFIG_MBX */
/*
* This version of gettimeofday has microsecond resolution.
@@ -108,6 +171,7 @@ void do_settimeofday(struct timeval *tv)
void
time_init(void)
{
+#ifndef CONFIG_MBX
if ((_get_PVR() >> 16) == 1) {
/* 601 processor: dec counts down by 128 every 128ns */
decrementer_count = DECREMENTER_COUNT_601;
@@ -119,9 +183,10 @@ time_init(void)
case _MACH_Pmac:
/* can't call pmac_get_rtc_time() yet,
because via-cuda isn't initialized yet. */
- if ((_get_PVR() >> 16) != 1)
+ if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) )
pmac_calibrate_decr();
- set_rtc_time = pmac_set_rtc_time;
+ if ( !smp_processor_id() )
+ set_rtc_time = pmac_set_rtc_time;
break;
case _MACH_chrp:
chrp_time_init();
@@ -135,18 +200,63 @@ time_init(void)
prep_calibrate_decr();
set_rtc_time = prep_set_rtc_time;
break;
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ {
+ xtime.tv_sec = apus_get_rtc_time();
+ apus_calibrate_decr();
+ set_rtc_time = apus_set_rtc_time;
+ break;
}
+#endif
+ }
+ xtime.tv_usec = 0;
+ set_dec(decrementer_count);
+#else
+ mbx_calibrate_decr();
+ set_rtc_time = mbx_set_rtc_time;
+
+ /* First, unlock all of the registers we are going to modify.
+ * To protect them from corruption during power down, registers
+ * that are maintained by keep alive power are "locked". To
+ * modify these registers we have to write the key value to
+ * the key location associated with the register.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY;
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY;
+
+
+ /* Disable the RTC one second and alarm interrupts.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &=
+ ~(RTCSC_SIE | RTCSC_ALE);
+
+ /* Enabling the decrementer also enables the timebase interrupts
+ * (or from the other point of view, to get decrementer interrupts
+ * we have to enable the timebase). The decrementer interrupt
+ * is wired into the vector table, nothing to do here for that.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr =
+ ((mk_int_int_mask(DEC_INTERRUPT) << 8) |
+ (TBSCR_TBF | TBSCR_TBE));
+ if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0)
+ panic("Could not allocate timer IRQ!");
+
+ /* Get time from the RTC.
+ */
+ xtime.tv_sec = ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc;
xtime.tv_usec = 0;
- /*
- * mark the rtc/on-chip timer as in sync
+#endif /* CONFIG_MBX */
+
+ /* mark the rtc/on-chip timer as in sync
* so we don't update right away
*/
last_rtc_update = xtime.tv_sec;
-
- set_dec(decrementer_count);
}
+#ifndef CONFIG_MBX
/*
* Uses the on-board timer to calibrate the on-chip decrementer register
* for prep systems. On the pmac the OF tells us what the frequency is
@@ -158,6 +268,27 @@ volatile int *done_ptr = &calibrate_done;
void prep_calibrate_decr(void)
{
unsigned long flags;
+
+ /* the Powerstack II's have trouble with the timer so
+ * we use a default value -- Cort
+ */
+ if ( (_prep_type == _PREP_Motorola) &&
+ ((inb(0x800) & 0xF0) & 0x40) )
+ {
+ unsigned long freq, divisor;
+ static unsigned long t2 = 0;
+
+ t2 = 998700000/60;
+ freq = t2 * 60; /* try to make freq/1e6 an integer */
+ divisor = 60;
+ printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
+ freq, divisor,t2>>20);
+ decrementer_count = freq / HZ / divisor;
+ count_period_num = divisor;
+ count_period_den = freq / 1000000;
+ return;
+ }
+
save_flags(flags);
@@ -181,7 +312,7 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)
{
unsigned long freq, divisor;
static unsigned long t1 = 0, t2 = 0;
-
+
if ( !t1 )
t1 = get_dec();
else if (!t2)
@@ -189,11 +320,6 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)
t2 = get_dec();
t2 = t1-t2; /* decr's in 1/HZ */
t2 = t2*HZ; /* # decrs in 1s - thus in Hz */
-if ( (t2>>20) > 100 )
-{
- printk("Decrementer frequency too high: %luMHz. Using 15MHz.\n",t2>>20);
- t2 = 998700000/60;
-}
freq = t2 * 60; /* try to make freq/1e6 an integer */
divisor = 60;
printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
@@ -205,6 +331,32 @@ if ( (t2>>20) > 100 )
}
}
+#else /* CONFIG_MBX */
+
+/* The decrementer counts at the system (internal) clock frequency divided by
+ * sixteen, or external oscillator divided by four. Currently, we only
+ * support the MBX, which is system clock divided by sixteen.
+ */
+void mbx_calibrate_decr(void)
+{
+ int freq, fp, divisor;
+
+ if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0)
+ printk("WARNING: Wrong decrementer source clock.\n");
+
+ /* The manual says the frequency is in Hz, but it is really
+ * as MHz. The value 'fp' is the number of decrementer ticks
+ * per second.
+ */
+ /*fp = (mbx_board_info.bi_intfreq * 1000000) / 16;*/
+ freq = fp*60; /* try to make freq/1e6 an integer */
+ divisor = 60;
+ printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+ decrementer_count = freq / HZ / divisor;
+ count_period_num = divisor;
+ count_period_den = freq / 1000000;
+}
+#endif /* CONFIG_MBX */
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h
index 538ac7bb1..64a07250c 100644
--- a/arch/ppc/kernel/time.h
+++ b/arch/ppc/kernel/time.h
@@ -1,5 +1,5 @@
/*
- * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $
+ * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $
* Common time prototypes and such for all ppc machines.
*
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -12,6 +12,7 @@
void prep_calibrate_decr_handler(int, void *,struct pt_regs *);
void prep_calibrate_decr(void);
void pmac_calibrate_decr(void);
+extern void apus_calibrate_decr(void);
extern unsigned decrementer_count;
extern unsigned count_period_num;
extern unsigned count_period_den;
@@ -24,12 +25,16 @@ extern unsigned long last_rtc_update;
unsigned long prep_get_rtc_time(void);
unsigned long pmac_get_rtc_time(void);
unsigned long chrp_get_rtc_time(void);
+unsigned long apus_get_rtc_time(void);
int prep_set_rtc_time(unsigned long nowtime);
int pmac_set_rtc_time(unsigned long nowtime);
int chrp_set_rtc_time(unsigned long nowtime);
+int apus_set_rtc_time(unsigned long nowtime);
void pmac_read_rtc_time(void);
void chrp_calibrate_decr(void);
void chrp_time_init(void);
+int via_calibrate_decr(void);
+void mbx_calibrate_decr(void);
/* Accessor functions for the decrementer register. */
static __inline__ unsigned int get_dec(void)
diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
index 7199dd5c1..c5c90b527 100644
--- a/arch/ppc/kernel/traps.c
+++ b/arch/ppc/kernel/traps.c
@@ -39,13 +39,31 @@ extern int fix_alignment(struct pt_regs *);
extern void bad_page_fault(struct pt_regs *, unsigned long);
#ifdef CONFIG_XMON
+extern void xmon(struct pt_regs *regs);
extern int xmon_bpt(struct pt_regs *regs);
extern int xmon_sstep(struct pt_regs *regs);
-extern void xmon(struct pt_regs *regs);
extern int xmon_iabr_match(struct pt_regs *regs);
+extern int xmon_dabr_match(struct pt_regs *regs);
extern void (*xmon_fault_handler)(struct pt_regs *regs);
#endif
+#ifdef CONFIG_XMON
+void (*debugger)(struct pt_regs *regs) = xmon;
+int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt;
+int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
+int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
+int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match;
+void (*debugger_fault_handler)(struct pt_regs *regs);
+#else
+#ifdef CONFIG_KGDB
+void (*debugger)(struct pt_regs *regs);
+int (*debugger_bpt)(struct pt_regs *regs);
+int (*debugger_sstep)(struct pt_regs *regs);
+int (*debugger_iabr_match)(struct pt_regs *regs);
+int (*debugger_dabr_match)(struct pt_regs *regs);
+void (*debugger_fault_handler)(struct pt_regs *regs);
+#endif
+#endif
/*
* Trap & Exception support
*/
@@ -61,8 +79,8 @@ _exception(int signr, struct pt_regs *regs)
if (!user_mode(regs))
{
show_regs(regs);
-#ifdef CONFIG_XMON
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ debugger(regs);
#endif
print_backtrace((unsigned long *)regs->gpr[1]);
panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
@@ -75,9 +93,9 @@ MachineCheckException(struct pt_regs *regs)
{
if ( !user_mode(regs) )
{
-#ifdef CONFIG_XMON
- if (xmon_fault_handler) {
- xmon_fault_handler(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_fault_handler) {
+ debugger_fault_handler(regs);
return;
}
#endif
@@ -103,8 +121,8 @@ MachineCheckException(struct pt_regs *regs)
printk("Unknown values in msr\n");
}
show_regs(regs);
-#ifdef CONFIG_XMON
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ debugger(regs);
#endif
print_backtrace((unsigned long *)regs->gpr[1]);
panic("machine check");
@@ -123,8 +141,8 @@ UnknownException(struct pt_regs *regs)
void
InstructionBreakpoint(struct pt_regs *regs)
{
-#ifdef CONFIG_XMON
- if (xmon_iabr_match(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_iabr_match(regs))
return;
#endif
_exception(SIGTRAP, regs);
@@ -144,8 +162,8 @@ ProgramCheckException(struct pt_regs *regs)
_exception(SIGFPE, regs);
} else if (regs->msr & 0x20000) {
/* trap exception */
-#ifdef CONFIG_XMON
- if (xmon_bpt(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_bpt(regs))
return;
#endif
_exception(SIGTRAP, regs);
@@ -158,8 +176,8 @@ void
SingleStepException(struct pt_regs *regs)
{
regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */
-#ifdef CONFIG_XMON
- if (xmon_sstep(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_sstep(regs))
return;
#endif
_exception(SIGTRAP, regs);
@@ -170,8 +188,13 @@ AlignmentException(struct pt_regs *regs)
{
int fixed;
+#ifdef __SMP__
+ if (regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if (last_task_used_math == current)
giveup_fpu();
+#endif
fixed = fix_alignment(regs);
if (fixed == 1) {
regs->nip += 4; /* skip over emulated instruction */
@@ -190,8 +213,8 @@ StackOverflow(struct pt_regs *regs)
{
printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n",
current, regs->gpr[1]);
-#ifdef CONFIG_XMON
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ debugger(regs);
#endif
show_regs(regs);
print_backtrace((unsigned long *)regs->gpr[1]);
@@ -205,3 +228,41 @@ trace_syscall(struct pt_regs *regs)
current, current->pid, regs->nip, regs->link, regs->gpr[0],
regs->ccr&0x10000000?"Error=":"", regs->gpr[3]);
}
+
+#ifdef CONFIG_8xx
+
+void
+SoftwareEmulation(struct pt_regs *regs)
+{
+ int errcode;
+ extern int Soft_emulate_8xx (struct pt_regs *regs);
+
+ if (user_mode(regs)) {
+#if 0
+ printk("(user mode)\n");
+ _exception(SIGTRAP, regs);
+#else
+ if (errcode = Soft_emulate_8xx(regs)) {
+printk("Software Emulation 0x%x: 0x%x ",
+ regs->nip, *((uint *)regs->nip));
+print_8xx_pte(current->mm, regs->nip);
+ if (errcode == EFAULT)
+ _exception(SIGBUS, regs);
+ else
+ _exception(SIGILL, regs);
+ }
+#endif
+ }
+ else {
+ printk("(kernel mode)\n");
+ panic("Kernel Mode Software Emulation");
+ }
+}
+#endif
+
+void
+TAUException(struct pt_regs *regs)
+{
+ printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n",
+ regs->nip, regs->msr, regs->trap);
+}
diff --git a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c
index 5e2ceb889..55cc665d7 100644
--- a/arch/ppc/lib/locks.c
+++ b/arch/ppc/lib/locks.c
@@ -1,5 +1,5 @@
/*
- * $Id: locks.c,v 1.7 1998/01/06 06:44:59 cort Exp $
+ * $Id: locks.c,v 1.17 1998/03/26 22:19:38 cort Exp $
*
* Locks for smp ppc
*
@@ -9,53 +9,78 @@
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/spinlock.h>
+#include <asm/io.h>
#define DEBUG_LOCKS 1
#undef INIT_STUCK
-#define INIT_STUCK 10000
-
-#undef STUCK
-#define STUCK \
-if(!--stuck) { printk("spin_lock(%p) CPU#%d nip %08lx\n", lock, cpu, nip); stuck = INIT_STUCK; }
+#define INIT_STUCK 1000000
void _spin_lock(spinlock_t *lock)
{
- unsigned long val, nip = (unsigned long)__builtin_return_address(0);
int cpu = smp_processor_id();
+#ifdef DEBUG_LOCKS
int stuck = INIT_STUCK;
-
-again:
+#endif /* DEBUG_LOCKS */
/* try expensive atomic load/store to get lock */
- __asm__ __volatile__(
- "10: \n\t"
- "lwarx %0,0,%1 \n\t"
- "stwcx. %2,0,%1 \n\t"
- "bne- 10b \n\t"
- : "=r" (val)
- : "r" (&(lock->lock)), "r" ( (cpu&3)|(nip&~3L) ));
- if(val) {
+ while((unsigned long )xchg_u32((void *)&lock->lock,0xffffffff)) {
/* try cheap load until it's free */
while(lock->lock) {
- STUCK;
+#ifdef DEBUG_LOCKS
+ if(!--stuck)
+ {
+ printk("_spin_lock(%p) CPU#%d NIP %p"
+ " holder: cpu %ld pc %08lX\n",
+ lock, cpu, __builtin_return_address(0),
+ lock->owner_cpu,lock->owner_pc);
+ stuck = INIT_STUCK;
+ /* steal the lock */
+ /*xchg_u32((void *)&lock->lock,0);*/
+ }
+#endif /* DEBUG_LOCKS */
barrier();
}
- goto again;
}
+ lock->owner_pc = (unsigned long)__builtin_return_address(0);
+ lock->owner_cpu = cpu;
+}
+
+int spin_trylock(spinlock_t *lock)
+{
+ unsigned long result;
+
+ result = (unsigned long )xchg_u32((void *)&lock->lock,0xffffffff);
+ if ( !result )
+ {
+ lock->owner_cpu = smp_processor_id();
+ lock->owner_pc = (unsigned long)__builtin_return_address(0);
+ }
+ return (result == 0);
}
+
+
void _spin_unlock(spinlock_t *lp)
{
+#ifdef DEBUG_LOCKS
+ if ( !lp->lock )
+ panic("_spin_unlock(%p): no lock cpu %d %s/%d\n", lp,
+ smp_processor_id(),current->comm,current->pid);
+ if ( lp->owner_cpu != smp_processor_id() )
+ panic("_spin_unlock(%p): cpu %d trying clear of cpu %d pc %lx val %lx\n",
+ lp, smp_processor_id(), (int)lp->owner_cpu,
+ lp->owner_pc,lp->lock);
+#endif /* DEBUG_LOCKS */
+ lp->owner_pc = lp->owner_cpu = 0;
+ eieio();
lp->lock = 0;
+ eieio();
}
-#undef STUCK
-#define STUCK \
-if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; }
-
/*
* Just like x86, implement read-write locks as a 32-bit counter
* with the high bit (sign) being the "write" bit.
@@ -63,8 +88,10 @@ if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; }
*/
void _read_lock(rwlock_t *rw)
{
+#ifdef DEBUG_LOCKS
unsigned long stuck = INIT_STUCK;
int cpu = smp_processor_id();
+#endif /* DEBUG_LOCKS */
again:
/* get our read lock in there */
@@ -76,7 +103,13 @@ again:
/* wait for the write lock to go away */
while ((signed long)((rw)->lock) < 0)
{
- STUCK;
+#ifdef DEBUG_LOCKS
+ if(!--stuck)
+ {
+ printk("_read_lock(%p) CPU#%d\n", rw, cpu);
+ stuck = INIT_STUCK;
+ }
+#endif /* DEBUG_LOCKS */
}
/* try to get the read lock again */
goto again;
@@ -87,33 +120,34 @@ void _read_unlock(rwlock_t *rw)
{
#ifdef DEBUG_LOCKS
if ( rw->lock == 0 )
- {
- if ( current)
printk("_read_unlock(): %s/%d (nip %08lX) lock %lx",
- current->comm,current->pid,current->tss.regs->nip,
+ current->comm,current->pid,current->tss.regs->nip,
rw->lock);
- else
- printk("no current\n");
- }
#endif /* DEBUG_LOCKS */
atomic_dec((atomic_t *) &(rw)->lock);
}
-#undef STUCK
-#define STUCK \
-if(!--stuck) { printk("write_lock(%p) CPU#%d lock %lx)\n", rw, cpu,rw->lock); stuck = INIT_STUCK; }
-
void _write_lock(rwlock_t *rw)
{
+#ifdef DEBUG_LOCKS
unsigned long stuck = INIT_STUCK;
int cpu = smp_processor_id();
+#endif /* DEBUG_LOCKS */
again:
if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */
{
while ( (rw)->lock & (1<<31) ) /* wait for write lock */
{
- STUCK;
+#ifdef DEBUG_LOCKS
+ if(!--stuck)
+ {
+ printk("write_lock(%p) CPU#%d lock %lx)\n",
+ rw, cpu,rw->lock);
+ stuck = INIT_STUCK;
+ }
+#endif /* DEBUG_LOCKS */
+ barrier();
}
goto again;
}
@@ -124,7 +158,15 @@ again:
clear_bit(31,&(rw)->lock);
while ( (rw)->lock & ~(1<<31) )
{
- STUCK;
+#ifdef DEBUG_LOCKS
+ if(!--stuck)
+ {
+ printk("write_lock(%p) 2 CPU#%d lock %lx)\n",
+ rw, cpu,rw->lock);
+ stuck = INIT_STUCK;
+ }
+#endif /* DEBUG_LOCKS */
+ barrier();
}
goto again;
}
@@ -134,14 +176,9 @@ void _write_unlock(rwlock_t *rw)
{
#ifdef DEBUG_LOCKS
if ( !(rw->lock & (1<<31)) )
- {
- if ( current)
printk("_write_lock(): %s/%d (nip %08lX) lock %lx",
current->comm,current->pid,current->tss.regs->nip,
rw->lock);
- else
- printk("no current\n");
- }
#endif /* DEBUG_LOCKS */
clear_bit(31,&(rw)->lock);
}
@@ -149,6 +186,8 @@ void _write_unlock(rwlock_t *rw)
void __lock_kernel(struct task_struct *task)
{
#ifdef DEBUG_LOCKS
+ unsigned long stuck = INIT_STUCK;
+
if ( (signed long)(task->lock_depth) < 0 )
{
printk("__lock_kernel(): %s/%d (nip %08lX) lock depth %x\n",
@@ -156,20 +195,40 @@ void __lock_kernel(struct task_struct *task)
task->lock_depth);
}
#endif /* DEBUG_LOCKS */
+
+ if ( atomic_inc_return((atomic_t *) &task->lock_depth) != 1 )
+ return;
/* mine! */
- if ( atomic_inc_return((atomic_t *) &task->lock_depth) == 1 )
- klock_info.akp = smp_processor_id();
+ while ( xchg_u32( (void *)&klock_info.kernel_flag, KLOCK_HELD) )
+ {
+ /* try cheap load until it's free */
+ while(klock_info.kernel_flag) {
+#ifdef DEBUG_LOCKS
+ if(!--stuck)
+ {
+ printk("_lock_kernel() CPU#%d NIP %p\n",
+ smp_processor_id(),
+ __builtin_return_address(0));
+ stuck = INIT_STUCK;
+ }
+#endif /* DEBUG_LOCKS */
+ barrier();
+ }
+ }
+
+ klock_info.akp = smp_processor_id();
/* my kernel mode! mine!!! */
}
-
+
void __unlock_kernel(struct task_struct *task)
{
#ifdef DEBUG_LOCKS
- if ( task->lock_depth == 0 )
+ if ( (task->lock_depth == 0) || (klock_info.kernel_flag != KLOCK_HELD) )
{
- printk("__unlock_kernel(): %s/%d (nip %08lX) lock depth %x\n",
- task->comm,task->pid,task->tss.regs->nip,
- task->lock_depth);
+ printk("__unlock_kernel(): %s/%d (nip %08lX) "
+ "lock depth %x flags %lx\n",
+ task->comm,task->pid,task->tss.regs->nip,
+ task->lock_depth, klock_info.kernel_flag);
klock_info.akp = NO_PROC_ID;
klock_info.kernel_flag = 0;
return;
@@ -177,8 +236,8 @@ void __unlock_kernel(struct task_struct *task)
#endif /* DEBUG_LOCKS */
if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) )
{
- klock_info.akp = NO_PROC_ID;
- klock_info.kernel_flag = 0;
+ klock_info.akp = NO_PROC_ID;
+ klock_info.kernel_flag = KLOCK_CLEAR;
}
}
@@ -192,4 +251,3 @@ void reacquire_kernel_lock(struct task_struct *task, int cpu,int depth)
__sti();
}
}
-
diff --git a/arch/ppc/mbx_defconfig b/arch/ppc/mbx_defconfig
new file mode 100644
index 000000000..e498b5249
--- /dev/null
+++ b/arch/ppc/mbx_defconfig
@@ -0,0 +1,196 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+CONFIG_8xx=y
+# CONFIG_PMAC is not set
+# CONFIG_PREP is not set
+# CONFIG_CHRP is not set
+# CONFIG_ALL_PPC is not set
+CONFIG_MBX=y
+CONFIG_MACH_SPECIFIC=y
+
+#
+# General setup
+#
+# CONFIG_EXPERIMENTAL is not set
+# CONFIG_MODULES is not set
+CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
+CONFIG_NET=y
+# CONFIG_SYSCTL is not set
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PMAC_CONSOLE is not set
+# CONFIG_MAC_KEYBOARD is not set
+# CONFIG_MAC_FLOPPY is not set
+# CONFIG_PROC_DEVICETREE is not set
+# CONFIG_XMON is not set
+CONFIG_VGA_CONSOLE=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Floppy, IDE, and other block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# NEW devices (io_request, all ALPHA and dangerous)
+#
+# CONFIG_IO_REQUEST is not set
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_NETLINK is not set
+# CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_RARP is not set
+CONFIG_IP_NOSR=y
+# CONFIG_SKB_LARGE is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_EISA is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_WAN_DRIVERS is not set
+# CONFIG_LAPBETHER is not set
+# CONFIG_X25_ASY is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+# CONFIG_CDROM is not set
+
+#
+# Filesystems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_MINIX_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_ISO9660_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_PROC_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_MOUSE is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_NVRAM is not set
+# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c
index 8eaa49e92..9d0914979 100644
--- a/arch/ppc/mm/fault.c
+++ b/arch/ppc/mm/fault.c
@@ -35,11 +35,11 @@
#include <asm/system.h>
#include <asm/uaccess.h>
-#ifdef CONFIG_XMON
-extern void xmon(struct pt_regs *);
-extern void (*xmon_fault_handler)(struct pt_regs *);
-extern int xmon_dabr_match(struct pt_regs *);
-int xmon_kernel_faults = 0;
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+extern void (*debugger)(struct pt_regs *);
+extern void (*debugger_fault_handler)(struct pt_regs *);
+extern int (*debugger_dabr_match)(struct pt_regs *);
+int debugger_kernel_faults = 0;
#endif
unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */
@@ -72,14 +72,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
(regs->trap == 0x400)?"instr":"data"
);*/
-#ifdef CONFIG_XMON
- if (xmon_fault_handler && regs->trap == 0x300) {
- xmon_fault_handler(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_fault_handler && regs->trap == 0x300) {
+ debugger_fault_handler(regs);
return;
}
if (error_code & 0x00400000) {
/* DABR match */
- if (xmon_dabr_match(regs))
+ if (debugger_dabr_match(regs))
return;
}
#endif
@@ -90,9 +90,9 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
printk("page fault in interrupt handler, addr=%lx\n",
address);
show_regs(regs);
-#ifdef CONFIG_XMON
- if (xmon_kernel_faults)
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_kernel_faults)
+ debugger(regs);
#endif
}
}
@@ -112,11 +112,22 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
goto bad_area;
good_area:
+#ifdef CONFIG_6xx
if (error_code & 0x95700000)
/* an error such as lwarx to I/O controller space,
address matching DABR, eciwx, etc. */
+#endif /* CONFIG_6xx */
+#ifdef CONFIG_8xx
+ /* The MPC8xx seems to always set 0x80000000, which is
+ * "undefined". Of those that can be set, this is the only
+ * one which seems bad.
+ */
+ if (error_code & 0x10000000)
+ /* Guarded storage error. */
+#endif /* CONFIG_8xx */
goto bad_area;
+
/* a write */
if (error_code & 0x02000000) {
if (!(vma->vm_flags & VM_WRITE))
@@ -190,14 +201,47 @@ bad_page_fault(struct pt_regs *regs, unsigned long address)
/* kernel has accessed a bad area */
show_regs(regs);
print_backtrace( (unsigned long *)regs->gpr[1] );
-#ifdef CONFIG_XMON
- if (xmon_kernel_faults)
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_kernel_faults)
+ debugger(regs);
#endif
panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d",
regs->nip,regs->link,address,current->comm,current->pid);
}
+/*
+ * I need a va to pte function for the MPC8xx so I can set the cache
+ * attributes on individual pages used by the Communication Processor
+ * Module.
+ */
+pte_t *va_to_pte(struct task_struct *tsk, unsigned long address)
+{
+ pgd_t *dir;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ dir = pgd_offset(tsk->mm, address & PAGE_MASK);
+ if (dir)
+ {
+ pmd = pmd_offset(dir, address & PAGE_MASK);
+ if (pmd && pmd_present(*pmd))
+ {
+ pte = pte_offset(pmd, address & PAGE_MASK);
+ if (pte && pte_present(*pte))
+ {
+ return(pte);
+ }
+ } else
+ {
+ return (0);
+ }
+ } else
+ {
+ return (0);
+ }
+ return (0);
+}
+
unsigned long va_to_phys(unsigned long address)
{
pgd_t *dir;
@@ -226,6 +270,57 @@ unsigned long va_to_phys(unsigned long address)
return (0);
}
+void
+print_8xx_pte(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t * pgd;
+ pmd_t * pmd;
+ pte_t * pte;
+
+ printk(" pte @ 0x%8lx: ", addr);
+ pgd = pgd_offset(mm, addr & PAGE_MASK);
+ if (pgd) {
+ pmd = pmd_offset(pgd, addr & PAGE_MASK);
+ if (pmd && pmd_present(*pmd)) {
+ pte = pte_offset(pmd, addr & PAGE_MASK);
+ if (pte) {
+ printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n",
+ (long)pgd, (long)pte, (long)pte_val(*pte));
+ }
+ else {
+ printk("no pte\n");
+ }
+ }
+ else {
+ printk("no pmd\n");
+ }
+ }
+ else {
+ printk("no pgd\n");
+ }
+}
+
+int
+get_8xx_pte(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t * pgd;
+ pmd_t * pmd;
+ pte_t * pte;
+ int retval = 0;
+
+ pgd = pgd_offset(mm, addr & PAGE_MASK);
+ if (pgd) {
+ pmd = pmd_offset(pgd, addr & PAGE_MASK);
+ if (pmd && pmd_present(*pmd)) {
+ pte = pte_offset(pmd, addr & PAGE_MASK);
+ if (pte) {
+ retval = (int)pte_val(*pte);
+ }
+ }
+ }
+ return(retval);
+}
+
#if 0
/*
* Misc debugging functions. Please leave them here. -- Cort
diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c
index 273076d15..5967e29e6 100644
--- a/arch/ppc/mm/init.c
+++ b/arch/ppc/mm/init.c
@@ -7,6 +7,7 @@
* Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
* and Cort Dougan (PReP) (cort@cs.nmt.edu)
* Copyright (C) 1996 Paul Mackerras
+ * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
*
* Derived from "arch/i386/mm/init.c"
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
@@ -31,6 +32,7 @@
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/stddef.h>
+#include <linux/vmalloc.h>
#include <asm/prom.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
@@ -40,12 +42,27 @@
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h> /* for initrd_* */
#endif
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#endif
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
-int prom_trashed;
-int next_mmu_context;
+#ifndef CONFIG_8xx
unsigned long _SDR1;
PTE *Hash, *Hash_end;
unsigned long Hash_size, Hash_mask;
+#endif /* CONFIG_8xx */
+
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#endif
+
+int prom_trashed;
+int next_mmu_context;
unsigned long *end_of_DRAM;
int mem_init_done;
extern pgd_t swapper_pg_dir[];
@@ -55,10 +72,16 @@ extern char __init_begin, __init_end;
extern RESIDUAL res;
char *klimit = _end;
struct device_node *memory_node;
+unsigned long ioremap_base;
+unsigned long ioremap_bot;
+#ifndef __SMP__
+struct pgtable_cache_struct quicklists;
+#endif
-void *find_mem_piece(unsigned, unsigned);
-static void mapin_ram(void);
+#ifndef CONFIG_8xx
static void hash_init(void);
+#endif /* CONFIG_8xx */
+static void mapin_ram(void);
static void *MMU_get_page(void);
void map_page(struct task_struct *, unsigned long va,
unsigned long pa, int flags);
@@ -68,6 +91,38 @@ extern unsigned long *find_end_of_memory(void);
extern struct task_struct *current_set[NR_CPUS];
+#ifdef CONFIG_MBX
+/* This is a big hack that may not yet work correctly.
+ * The MBX8xx boards have a single DIMM socket for additional memory.
+ * Although it appears you can set magical locations in the serial
+ * EEPROM to get EPPC-Bug to configure this memory, there are no tools
+ * (i.e. commands) to make this easy. If you screw up, you will most
+ * likely end up with a board that will not boot until you find a
+ * way to program the EEPROM correctly. I decided to simply program
+ * the memory controller here to add the additional memory.
+ * The reason this may not work correctly is that depending upon the
+ * on-board and DIMM memory size, there may be holes in the physical
+ * address space. This is the case for me, I have a 4 MB local memory
+ * and a 32 MB DIMM.
+ * The DIMM is 64 bits wide, and we see it as two banks of 32 bit
+ * memory. The holes are caused by the requirement to map the
+ * memory on a natural alignment, that is a 16 MB bank must begin on
+ * a 16 MB boundary. The DIMM_SIZE below represents the size of the
+ * bank, which is the total size divided by two.
+ * Although I may not have all of this working, the intention is to
+ * mark all of the page maps in the "hole" as reserved, and adjust
+ * num_physpages accordingly. In the current implementation, this
+ * seems to work, but there are some assumptions about contiguous
+ * memory. The correct solution is to modify the memory allocators
+ * to know about holes, but that will have to wait for another day.
+ *
+ * define DIMM_8xx to enable this feature.
+ * define DIMM_SIZE to reflect the bank size (DIMM size divided by two).
+ */
+/*#define DIMM_8xx 1 */
+#define DIMM_SIZE (16 * 1024 * 1024)
+#endif /* CONFIG_MBX */
+
/*
* this tells the system to map all of ram with the segregs
* (i.e. page tables) instead of the bats.
@@ -77,6 +132,34 @@ extern struct task_struct *current_set[NR_CPUS];
/* optimization for 603 to load the tlb directly from the linux table */
#define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */
+void __bad_pte(pmd_t *pmd)
+{
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;
+}
+
+pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) __get_free_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (pte) {
+ clear_page((unsigned long)pte);
+ pmd_val(*pmd) = (unsigned long)pte;
+ return pte + offset;
+ }
+ pmd_val(*pmd) = (unsigned long)BAD_PAGETABLE;
+ return NULL;
+ }
+ free_page((unsigned long)pte);
+ if (pmd_bad(*pmd)) {
+ __bad_pte(pmd);
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + offset;
+}
+
/*
* BAD_PAGE is the page that is used for page faults when linux
* is out-of-memory. Older versions of linux just did a
@@ -121,65 +204,32 @@ struct mem_pieces phys_mem;
struct mem_pieces phys_avail;
struct mem_pieces prom_mem;
-static void get_mem_prop(char *, struct mem_pieces *);
-static void sort_mem_pieces(struct mem_pieces *);
-static void coalesce_mem_pieces(struct mem_pieces *);
-static void append_mem_piece(struct mem_pieces *, unsigned, unsigned);
static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int);
+void *find_mem_piece(unsigned, unsigned);
static void print_mem_pieces(struct mem_pieces *);
-static void
-sort_mem_pieces(struct mem_pieces *mp)
-{
- unsigned long a, s;
- int i, j;
-
- for (i = 1; i < mp->n_regions; ++i) {
- a = mp->regions[i].address;
- s = mp->regions[i].size;
- for (j = i - 1; j >= 0; --j) {
- if (a >= mp->regions[j].address)
- break;
- mp->regions[j+1] = mp->regions[j];
- }
- mp->regions[j+1].address = a;
- mp->regions[j+1].size = s;
- }
-}
-
-static void
-coalesce_mem_pieces(struct mem_pieces *mp)
+/*
+ * Scan a region for a piece of a given size with the required alignment.
+ */
+void *
+find_mem_piece(unsigned size, unsigned align)
{
- unsigned long a, e;
- int i, j, d;
+ int i;
+ unsigned a, e;
+ struct mem_pieces *mp = &phys_avail;
- d = 0;
- for (i = 0; i < mp->n_regions; i = j) {
+ for (i = 0; i < mp->n_regions; ++i) {
a = mp->regions[i].address;
e = a + mp->regions[i].size;
- for (j = i + 1; j < mp->n_regions
- && mp->regions[j].address <= e; ++j)
- e = mp->regions[j].address + mp->regions[j].size;
- mp->regions[d].address = a;
- mp->regions[d].size = e - a;
- ++d;
+ a = (a + align - 1) & -align;
+ if (a + size <= e) {
+ remove_mem_piece(mp, a, size, 1);
+ return __va(a);
+ }
}
- mp->n_regions = d;
-}
-
-/*
- * Add some memory to an array of pieces
- */
-static void
-append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)
-{
- struct reg_property *rp;
-
- if (mp->n_regions >= MAX_MEM_REGIONS)
- return;
- rp = &mp->regions[mp->n_regions++];
- rp->address = start;
- rp->size = size;
+ printk("Couldn't find %u bytes at %u alignment\n", size, align);
+ abort();
+ return NULL;
}
/*
@@ -252,33 +302,73 @@ print_mem_pieces(struct mem_pieces *mp)
printk("\n");
}
-/*
- * Scan a region for a piece of a given size with the required alignment.
- */
-void *
-find_mem_piece(unsigned size, unsigned align)
+
+
+#ifndef CONFIG_8xx
+static void hash_init(void);
+static void get_mem_prop(char *, struct mem_pieces *);
+static void sort_mem_pieces(struct mem_pieces *);
+static void coalesce_mem_pieces(struct mem_pieces *);
+static void append_mem_piece(struct mem_pieces *, unsigned, unsigned);
+
+static void
+sort_mem_pieces(struct mem_pieces *mp)
{
- int i;
- unsigned a, e;
- struct mem_pieces *mp = &phys_avail;
+ unsigned long a, s;
+ int i, j;
- for (i = 0; i < mp->n_regions; ++i) {
+ for (i = 1; i < mp->n_regions; ++i) {
a = mp->regions[i].address;
- e = a + mp->regions[i].size;
- a = (a + align - 1) & -align;
- if (a + size <= e) {
- remove_mem_piece(mp, a, size, 1);
- return __va(a);
+ s = mp->regions[i].size;
+ for (j = i - 1; j >= 0; --j) {
+ if (a >= mp->regions[j].address)
+ break;
+ mp->regions[j+1] = mp->regions[j];
}
+ mp->regions[j+1].address = a;
+ mp->regions[j+1].size = s;
}
- printk("Couldn't find %u bytes at %u alignment\n", size, align);
- abort();
- return NULL;
+}
+
+static void
+coalesce_mem_pieces(struct mem_pieces *mp)
+{
+ unsigned long a, e;
+ int i, j, d;
+
+ d = 0;
+ for (i = 0; i < mp->n_regions; i = j) {
+ a = mp->regions[i].address;
+ e = a + mp->regions[i].size;
+ for (j = i + 1; j < mp->n_regions
+ && mp->regions[j].address <= e; ++j)
+ e = mp->regions[j].address + mp->regions[j].size;
+ mp->regions[d].address = a;
+ mp->regions[d].size = e - a;
+ ++d;
+ }
+ mp->n_regions = d;
+}
+
+/*
+ * Add some memory to an array of pieces
+ */
+static void
+append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)
+{
+ struct reg_property *rp;
+
+ if (mp->n_regions >= MAX_MEM_REGIONS)
+ return;
+ rp = &mp->regions[mp->n_regions++];
+ rp->address = start;
+ rp->size = size;
}
/*
* Read in a property describing some pieces of memory.
*/
+
static void
get_mem_prop(char *name, struct mem_pieces *mp)
{
@@ -365,6 +455,41 @@ unsigned long *pmac_find_end_of_memory(void)
return __va(total);
}
+#endif /* CONFIG_8xx */
+
+#ifdef CONFIG_APUS
+#define HARDWARE_MAPPED_SIZE (512*1024)
+unsigned long *apus_find_end_of_memory(void)
+{
+ unsigned long kstart, ksize;
+
+ /* Add the chunk that ADOS does not see. Removed again below. */
+ m68k_memory[0].size += HARDWARE_MAPPED_SIZE;
+
+ append_mem_piece(&phys_mem, m68k_memory[0].addr, m68k_memory[0].size);
+
+ phys_avail = phys_mem;
+ kstart = __pa(_stext);
+ ksize = PAGE_ALIGN(klimit - _stext);
+ remove_mem_piece(&phys_avail, kstart, ksize, 1);
+
+ /* Remove the upper HARDWARE_MAPPED_SIZE bytes where the address
+ * range 0xfff00000-0xfffx0000 is mapped to.
+ * We do it this way to ensure that the memory registered in the
+ * system has a power-of-two size.
+ */
+ remove_mem_piece(&phys_avail,
+ (m68k_memory[0].addr + m68k_memory[0].size
+ - HARDWARE_MAPPED_SIZE),
+ HARDWARE_MAPPED_SIZE, 1);
+
+ /* FIXME:APUS: Only handles one block of memory! Problem is
+ * that the VTOP/PTOV code in head.S would be a mess if it had
+ * to handle more than one block.
+ */
+ return __va(m68k_memory[0].addr + m68k_memory[0].size);
+}
+#endif
/*
* Find some memory for setup_arch to return.
@@ -381,6 +506,16 @@ unsigned long find_available_memory(void)
unsigned long start, end;
free = 0;
+ if (_machine == _MACH_mbx) {
+ /* Return the first, not the last region, because we
+ * may not yet have properly initialized the additonal
+ * memory DIMM.
+ */
+ a = PAGE_ALIGN(phys_avail.regions[0].address);
+ avail_start = (unsigned long) __va(a);
+ return avail_start;
+ }
+
for (i = 0; i < phys_avail.n_regions - 1; ++i) {
start = phys_avail.regions[i].address;
end = start + phys_avail.regions[i].size;
@@ -396,7 +531,7 @@ unsigned long find_available_memory(void)
void show_mem(void)
{
int i,free = 0,total = 0,reserved = 0;
- int shared = 0;
+ int shared = 0, cached = 0;
struct task_struct *p;
printk("Mem-info:\n");
@@ -407,6 +542,8 @@ void show_mem(void)
total++;
if (PageReserved(mem_map+i))
reserved++;
+ else if (PageSwapCache(mem_map+i))
+ cached++;
else if (!atomic_read(&mem_map[i].count))
free++;
else
@@ -416,6 +553,8 @@ void show_mem(void)
printk("%d free pages\n",free);
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
+ printk("%d pages swap cached\n",cached);
+ printk("%d pages in page table cache\n",(int)pgtable_cache_size);
show_buffers();
#ifdef CONFIG_NET
show_net_buffers();
@@ -487,6 +626,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem)
int codepages = 0;
int datapages = 0;
int initpages = 0;
+ extern unsigned int rtas_data, rtas_size;
end_mem &= PAGE_MASK;
high_memory = (void *) end_mem;
@@ -496,6 +636,7 @@ void mem_init(unsigned long start_mem, unsigned long end_mem)
/* mark usable pages in the mem_map[] */
start_mem = PAGE_ALIGN(start_mem);
+#ifndef CONFIG_8xx
remove_mem_piece(&phys_avail, __pa(avail_start),
start_mem - avail_start, 1);
@@ -520,7 +661,52 @@ void mem_init(unsigned long start_mem, unsigned long end_mem)
clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
}
prom_trashed = 1;
-
+#else /* CONFIG_8xx */
+ /* When we get here, all of the page maps have been set up and
+ * Linux thinks we have contiguous memory. Since the MBX can
+ * have memory holes, we need to compensate for that here.
+ * The memory holes are currently pages marked reserved (all
+ * pages right now are marked reserved).
+ * All of the memory allocated by the kernel up to this point
+ * had to come from region 0.
+ */
+
+ /* First, unreserve all memory from the page following start_mem
+ * to the end of region 0.
+ */
+ for (addr = start_mem + PAGE_SIZE ;
+ addr < (ulong) __va(phys_mem.regions[0].size);
+ addr += PAGE_SIZE) {
+ clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
+ }
+
+ /* Now add any additional regions to the system.
+ */
+ for (i = 1; i < phys_avail.n_regions; ++i) {
+ a = (unsigned long) __va(phys_avail.regions[i].address);
+ lim = a + phys_avail.regions[i].size;
+ a = PAGE_ALIGN(a);
+ for (; a < lim; a += PAGE_SIZE)
+ clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags);
+ }
+ phys_avail.n_regions = 0; /* Nothing available, kernel owns */
+ /* Count up the size of the holes. We look for the space
+ * between the end of one region and the start of the next.
+ */
+ lim = 0;
+ for (i = 0; i < phys_mem.n_regions-1; ++i) {
+ a = (unsigned long) phys_mem.regions[i].address;
+ a += phys_mem.regions[i].size;
+ lim += phys_mem.regions[i+1].address - a;
+ }
+
+ /* It appears that num_physpages is only used for quota checking,
+ * when pages are locked down. We subtract the size of the holes
+ * from it now.
+ */
+ num_physpages -= lim/PAGE_SIZE;
+#endif /* CONFIG_8xx */
+
for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
if (PageReserved(mem_map + MAP_NR(addr))) {
if (addr < (ulong) etext)
@@ -537,7 +723,12 @@ void mem_init(unsigned long start_mem, unsigned long end_mem)
if (!initrd_start ||
addr < (initrd_start & PAGE_MASK) || addr >= initrd_end)
#endif /* CONFIG_BLK_DEV_INITRD */
- free_page(addr);
+#ifndef CONFIG_8xx
+ if ( !rtas_data ||
+ addr < (rtas_data & PAGE_MASK) ||
+ addr >= (rtas_data+rtas_size))
+#endif /* CONFIG_8xx */
+ free_page(addr);
}
printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n",
@@ -594,6 +785,7 @@ void si_meminfo(struct sysinfo *val)
return;
}
+#ifndef CONFIG_8xx
union ubat { /* BAT register values to be loaded */
BAT bat;
P601_BAT bat_601;
@@ -692,7 +884,7 @@ unsigned long *prep_find_end_of_memory(void)
return (__va(total));
}
-
+#endif /* CONFIG_8xx */
/*
* Map in all of physical memory starting at KERNELBASE.
@@ -702,8 +894,9 @@ unsigned long *prep_find_end_of_memory(void)
static void mapin_ram()
{
int i;
- unsigned long tot, bl, done;
unsigned long v, p, s, f;
+#ifndef CONFIG_8xx
+ unsigned long tot, mem_base, bl, done;
#ifndef MAP_RAM_WITH_SEGREGS
/* Set up BAT2 and if necessary BAT3 to cover RAM. */
@@ -711,15 +904,17 @@ static void mapin_ram()
for (bl = 128<<10; bl < 256<<20; bl <<= 1)
if (bl * 2 > tot)
break;
- setbat(2, KERNELBASE, 0, bl, RAM_PAGE);
- done = __pa(bat_addrs[2].limit) + 1;
+
+ mem_base = __pa(KERNELBASE);
+ setbat(2, KERNELBASE, mem_base, bl, RAM_PAGE);
+ done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1;
if (done < tot) {
/* use BAT3 to cover a bit more */
tot -= done;
for (bl = 128<<10; bl < 256<<20; bl <<= 1)
if (bl * 2 > tot)
break;
- setbat(3, KERNELBASE+done, done, bl, RAM_PAGE);
+ setbat(3, KERNELBASE+done, mem_base+done, bl, RAM_PAGE);
}
#endif
@@ -734,6 +929,27 @@ static void mapin_ram()
/* On the powerpc, no user access
forces R/W kernel access */
f |= _PAGE_USER;
+#else /* CONFIG_8xx */
+ for (i = 0; i < phys_mem.n_regions; ++i) {
+ v = (ulong)__va(phys_mem.regions[i].address);
+ p = phys_mem.regions[i].address;
+ for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) {
+ /* On the MPC8xx, we want the page shared so we
+ * don't get ASID compares on kernel space.
+ */
+ f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED;
+
+ /* I don't really need the rest of this code, but
+ * I grabbed it because I think the line:
+ * f |= _PAGE_USER
+ * is incorrect. It needs to be set to bits we
+ * don't define to cause a kernel read-only. On
+ * the MPC8xx, the PAGE_DIRTY takes care of that
+ * for us (along with the RW software state).
+ */
+ if ((char *) v < _stext || (char *) v >= etext)
+ f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
+#endif /* CONFIG_8xx */
map_page(&init_task, v, p, f);
v += PAGE_SIZE;
p += PAGE_SIZE;
@@ -741,6 +957,7 @@ static void mapin_ram()
}
}
+#ifndef CONFIG_8xx
/*
* Initialize the hash table and patch the instructions in head.S.
*/
@@ -820,7 +1037,7 @@ static void hash_init(void)
Hash_end = 0;
}
-
+#endif /* CONFIG_8xx */
/*
* Do very early mm setup such as finding the size of memory
@@ -832,13 +1049,19 @@ static void hash_init(void)
void
MMU_init(void)
{
+#ifndef CONFIG_8xx
if (have_of)
end_of_DRAM = pmac_find_end_of_memory();
+#ifdef CONFIG_APUS
+ else if (_machine == _MACH_apus )
+ end_of_DRAM = apus_find_end_of_memory();
+#endif
else /* prep */
end_of_DRAM = prep_find_end_of_memory();
hash_init();
_SDR1 = __pa(Hash) | (Hash_mask >> 10);
+ ioremap_base = 0xf8000000;
/* Map in all of RAM starting at KERNELBASE */
mapin_ram();
@@ -856,16 +1079,40 @@ MMU_init(void)
IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0));
break;
case _MACH_chrp:
- setbat(0, 0xc0000000, 0xc0000000, 0x10000000, IO_PAGE);
- setbat(1, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE);
- setbat(3, 0x80000000, 0x80000000, 0x10000000, IO_PAGE);
+ setbat(0, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE);
break;
case _MACH_Pmac:
setbat(0, 0xf3000000, 0xf3000000, 0x100000, IO_PAGE);
- /* this is used to cover registers used by smp boards -- Cort */
- setbat(3, 0xf8000000, 0xf8000000, 0x100000, IO_PAGE);
+ ioremap_base = 0xf0000000;
break;
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ /* Map Cyberstorm PPC registers. */
+ /* FIXME:APUS: Performance penalty here. Restrict it
+ * to the Cyberstorm registers.
+ */
+ setbat(0, 0xfff00000, 0xfff00000, 0x00080000, IO_PAGE);
+ /* Map chip and ZorroII memory */
+ setbat(1, zTwoBase, 0x00000000, 0x01000000, IO_PAGE);
+ break;
+#endif
}
+ ioremap_bot = ioremap_base;
+#else /* CONFIG_8xx */
+
+ /* Map in all of RAM starting at KERNELBASE */
+ mapin_ram();
+
+ /* Now map in some of the I/O space that is generically needed
+ * or shared with multiple devices.
+ * All of this fits into the same 4Mbyte region, so it only
+ * requires one page table page.
+ */
+ ioremap(NVRAM_ADDR, NVRAM_SIZE);
+ ioremap(MBX_CSR_ADDR, MBX_CSR_SIZE);
+ ioremap(MBX_IMAP_ADDR, MBX_IMAP_SIZE);
+ ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE);
+#endif /* CONFIG_8xx */
}
static void *
@@ -887,27 +1134,98 @@ MMU_get_page()
void *
ioremap(unsigned long addr, unsigned long size)
{
- unsigned long p, end = addr + size;
+ return __ioremap(addr, size, _PAGE_NO_CACHE);
+}
+
+void *
+__ioremap(unsigned long addr, unsigned long size, unsigned long flags)
+{
+ unsigned long p, v, i;
+
+ /*
+ * Choose an address to map it to.
+ * Once the vmalloc system is running, we use it.
+ * Before then, we map addresses >= ioremap_base
+ * virt == phys; for addresses below this we use
+ * space going down from ioremap_base (ioremap_bot
+ * records where we're up to).
+ *
+ * We should also look out for a frame buffer and
+ * map it with a free BAT register, if there is one.
+ */
+ p = addr & PAGE_MASK;
+ size = PAGE_ALIGN(addr + size) - p;
+ if (size == 0)
+ return NULL;
- for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE)
- map_page(&init_task, p, p,
- pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED);
- return (void *) addr;
+ if (mem_init_done) {
+ struct vm_struct *area;
+ area = get_vm_area(size);
+ if (area == 0)
+ return NULL;
+ v = VMALLOC_VMADDR(area->addr);
+ } else {
+ if (p >= ioremap_base)
+ v = p;
+ else
+ v = (ioremap_bot -= size);
+ }
+
+ flags |= pgprot_val(PAGE_KERNEL);
+ if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU))
+ flags |= _PAGE_GUARDED;
+ for (i = 0; i < size; i += PAGE_SIZE)
+ map_page(&init_task, v+i, p+i, flags);
+
+ return (void *) (v + (addr & ~PAGE_MASK));
}
-void iounmap(unsigned long *addr)
+void iounmap(void *addr)
{
/* XXX todo */
}
+unsigned long iopa(unsigned long addr)
+{
+ unsigned long idx;
+ pmd_t *pd;
+ pte_t *pg;
+#ifndef CONFIG_8xx
+ int b;
+#endif
+ idx = addr & ~PAGE_MASK;
+ addr = addr & PAGE_MASK;
+
+#ifndef CONFIG_8xx
+ /* Check the BATs */
+ for (b = 0; b < 4; ++b)
+ if (addr >= bat_addrs[b].start && addr <= bat_addrs[b].limit)
+ return bat_addrs[b].phys | idx;
+#endif /* CONFIG_8xx */
+ /* Do we have a page table? */
+ if (init_task.mm->pgd == NULL)
+ return 0;
+
+ /* Use upper 10 bits of addr to index the first level map */
+ pd = (pmd_t *) (init_task.mm->pgd + (addr >> PGDIR_SHIFT));
+ if (pmd_none(*pd))
+ return 0;
+
+ /* Use middle 10 bits of addr to index the second-level map */
+ pg = pte_offset(pd, addr);
+ return (pte_val(*pg) & PAGE_MASK) | idx;
+}
+
void
map_page(struct task_struct *tsk, unsigned long va,
unsigned long pa, int flags)
{
pmd_t *pd;
pte_t *pg;
+#ifndef CONFIG_8xx
int b;
-
+#endif
+
if (tsk->mm->pgd == NULL) {
/* Allocate upper level page map */
tsk->mm->pgd = (pgd_t *) MMU_get_page();
@@ -915,6 +1233,7 @@ map_page(struct task_struct *tsk, unsigned long va,
/* Use upper 10 bits of VA to index the first level map */
pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT));
if (pmd_none(*pd)) {
+#ifndef CONFIG_8xx
/*
* Need to allocate second-level table, but first
* check whether this address is already mapped by
@@ -927,14 +1246,16 @@ map_page(struct task_struct *tsk, unsigned long va,
return;
}
}
+#endif /* CONFIG_8xx */
pg = (pte_t *) MMU_get_page();
pmd_val(*pd) = (unsigned long) pg;
}
/* Use middle 10 bits of VA to index the second-level map */
pg = pte_offset(pd, va);
set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags)));
- /*flush_hash_page(va >> 28, va);*/
+#ifndef CONFIG_8xx
flush_hash_page(0, va);
+#endif
}
/*
@@ -957,11 +1278,14 @@ map_page(struct task_struct *tsk, unsigned long va,
void
local_flush_tlb_all(void)
{
+#ifndef CONFIG_8xx
memset(Hash, 0, Hash_size);
_tlbia();
+#else
+ asm volatile ("tlbia" : : );
+#endif
}
-
/*
* Flush all the (user) entries for the address space described
* by mm. We can't rely on mm->mmap describing all the entries
@@ -970,33 +1294,43 @@ local_flush_tlb_all(void)
void
local_flush_tlb_mm(struct mm_struct *mm)
{
+#ifndef CONFIG_8xx
mm->context = NO_CONTEXT;
if (mm == current->mm) {
get_mmu_context(current);
/* done by get_mmu_context() now -- Cort */
/*set_context(current->mm->context);*/
}
+#else
+ asm volatile ("tlbia" : : );
+#endif
}
void
local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
+#ifndef CONFIG_8xx
if (vmaddr < TASK_SIZE)
flush_hash_page(vma->vm_mm->context, vmaddr);
else
flush_hash_page(0, vmaddr);
+#else
+ asm volatile ("tlbia" : : );
+#endif
}
-/* for each page addr in the range, call MMU_invalidate_page()
- if the range is very large and the hash table is small it might be faster to
- do a search of the hash table and just invalidate pages that are in the range
- but that's for study later.
- -- Cort
- */
+/*
+ * for each page addr in the range, call MMU_invalidate_page()
+ * if the range is very large and the hash table is small it might be
+ * faster to do a search of the hash table and just invalidate pages
+ * that are in the range but that's for study later.
+ * -- Cort
+ */
void
local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
{
+#ifndef CONFIG_8xx
start &= PAGE_MASK;
if (end - start > 20 * PAGE_SIZE)
@@ -1009,6 +1343,9 @@ local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long e
{
flush_hash_page(mm->context, start);
}
+#else
+ asm volatile ("tlbia" : : );
+#endif
}
/*
@@ -1020,6 +1357,7 @@ local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long e
void
mmu_context_overflow(void)
{
+#ifndef CONFIG_8xx
struct task_struct *tsk;
printk(KERN_DEBUG "mmu_context_overflow\n");
@@ -1034,6 +1372,12 @@ mmu_context_overflow(void)
/* make sure current always has a context */
current->mm->context = MUNGE_CONTEXT(++next_mmu_context);
set_context(current->mm->context);
+#else
+ /* We set the value to -1 because it is pre-incremented before
+ * before use.
+ */
+ next_mmu_context = -1;
+#endif
}
#if 0
@@ -1090,3 +1434,69 @@ void local_flush_cache_range(struct mm_struct *mm, unsigned long start,
}
#endif
+#ifdef CONFIG_MBX
+/*
+ * This is a big hack right now, but it may turn into something real
+ * someday.
+ *
+ * For the MBX860 (at this time anyway), there is nothing to initialize
+ * associated the the PROM. Rather than include all of the prom.c
+ * functions in the image just to get prom_init, all we really need right
+ * now is the initialization of the physical memory region.
+ */
+void
+set_mbx_memory(void)
+{
+ unsigned long kstart, ksize;
+ bd_t *binfo;
+#ifdef DIMM_8xx
+ volatile memctl8xx_t *mcp;
+#endif
+
+ binfo = (bd_t *)&res;
+
+ /* The MBX can have up to three memory regions, the on-board
+ * DRAM plus two more banks of DIMM socket memory. The DIMM is
+ * 64 bits, seen from the processor as two 32 bit banks.
+ * The on-board DRAM is reflected in the board information
+ * structure, and is either 4 Mbytes or 16 Mbytes.
+ * I think there is a way to program the serial EEPROM information
+ * so EPPC-Bug will initialize this memory, but I have not
+ * done that and it may not be a wise thing to do. If you
+ * remove the DIMM without reprogramming the EEPROM, bad things
+ * could happen since EPPC-Bug tries to use the upper 128K of
+ * memory.
+ */
+ phys_mem.n_regions = 1;
+ phys_mem.regions[0].address = 0;
+ phys_mem.regions[0].size = binfo->bi_memsize;
+ end_of_DRAM = __va(binfo->bi_memsize);
+
+#ifdef DIMM_8xx
+ /* This is a big hack. It assumes my 32 Mbyte DIMM in a 40 MHz
+ * MPC860. Don't do this (or change this) if you are running
+ * something else.
+ */
+ mcp = (memctl8xx_t *)(&(((immap_t *)MBX_IMAP_ADDR)->im_memctl));
+
+ mcp->memc_or2 = (~(DIMM_SIZE-1) | 0x00000400);
+ mcp->memc_br2 = DIMM_SIZE | 0x00000081;
+ mcp->memc_or3 = (~((2*DIMM_SIZE)-1) | 0x00000400);
+ mcp->memc_br3 = 2*DIMM_SIZE | 0x00000081;
+
+
+ phys_mem.regions[phys_mem.n_regions].address = DIMM_SIZE;
+ phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE;
+ phys_mem.regions[phys_mem.n_regions].address = 2 * DIMM_SIZE;
+ phys_mem.regions[phys_mem.n_regions++].size = DIMM_SIZE;
+
+ end_of_DRAM = __va(3 * DIMM_SIZE);
+#endif
+
+ phys_avail = phys_mem;
+
+ kstart = __pa(_stext); /* should be 0 */
+ ksize = PAGE_ALIGN(_end - _stext);
+ remove_mem_piece(&phys_avail, kstart, ksize, 0);
+}
+#endif
diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig
index 529b33138..8cf7eb8a6 100644
--- a/arch/ppc/pmac_defconfig
+++ b/arch/ppc/pmac_defconfig
@@ -1,5 +1,5 @@
#
-# Automatically generated by make menuconfig: don't edit
+# Automatically generated make config: don't edit
#
#
@@ -7,11 +7,12 @@
#
CONFIG_PPC=y
CONFIG_NATIVE=y
+CONFIG_PPC6XX=y
+# CONFIG_PPC8XX is not set
CONFIG_MACH_SPECIFIC=y
CONFIG_PMAC=y
# CONFIG_PREP is not set
# CONFIG_CHRP is not set
-CONFIG_COMMON=y
#
# General setup
@@ -19,8 +20,9 @@ CONFIG_COMMON=y
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
# CONFIG_MODVERSIONS is not set
-CONFIG_KMOD=y
+CONFIG_KERNELD=y
CONFIG_PCI=y
+CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
CONFIG_SYSCTL=y
CONFIG_SYSVIPC=y
@@ -28,14 +30,20 @@ CONFIG_SYSVIPC=y
CONFIG_BINFMT_ELF=y
CONFIG_KERNEL_ELF=y
CONFIG_BINFMT_MISC=m
-CONFIG_BINFMT_JAVA=m
+# CONFIG_BINFMT_JAVA is not set
+# CONFIG_ABSTRACT_CONSOLE is not set
CONFIG_PMAC_CONSOLE=y
CONFIG_MAC_KEYBOARD=y
CONFIG_MAC_FLOPPY=y
+CONFIG_MACMOUSE=y
CONFIG_PROC_DEVICETREE=y
# CONFIG_XMON is not set
+CONFIG_CONTROL_VIDEO=y
+CONFIG_PLATINUM_VIDEO=y
+CONFIG_VALKYRIE_VIDEO=y
CONFIG_ATY_VIDEO=y
CONFIG_IMSTT_VIDEO=y
+CONFIG_CHIPS_VIDEO=y
#
# Plug and Play support
@@ -47,6 +55,10 @@ CONFIG_IMSTT_VIDEO=y
#
# CONFIG_BLK_DEV_FD is not set
CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
CONFIG_BLK_DEV_IDECD=y
@@ -55,14 +67,20 @@ CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDESCSI is not set
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_TRITON is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
# CONFIG_IDE_CHIPSETS is not set
+
+#
+# Additional Block Devices
+#
CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=m
+# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_HD is not set
#
@@ -77,6 +95,7 @@ CONFIG_BLK_DEV_INITRD=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
@@ -85,37 +104,56 @@ CONFIG_IP_MULTICAST=y
# CONFIG_IP_ACCT is not set
# CONFIG_IP_MASQUERADE is not set
# CONFIG_IP_ROUTER is not set
-CONFIG_NET_IPIP=m
+# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_MROUTE is not set
CONFIG_IP_ALIAS=y
# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
CONFIG_INET_RARP=y
CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
# CONFIG_IPV6 is not set
+
+#
+#
+#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
# CONFIG_LLC is not set
# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
#
# SCSI support
#
CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
#
# SCSI low-level drivers
@@ -124,7 +162,12 @@ CONFIG_SCSI_CONSTANTS=y
# CONFIG_SCSI_AHA152X is not set
# CONFIG_SCSI_AHA1542 is not set
# CONFIG_SCSI_AHA1740 is not set
-# CONFIG_SCSI_AIC7XXX is not set
+CONFIG_SCSI_AIC7XXX=m
+# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set
+# CONFIG_OVERRIDE_CMDS is not set
+# CONFIG_AIC7XXX_PAGE_ENABLE is not set
+CONFIG_AIC7XXX_PROC_STATS=y
+CONFIG_AIC7XXX_RESET_DELAY=15
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_AM53C974 is not set
@@ -134,12 +177,16 @@ CONFIG_SCSI_CONSTANTS=y
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
# CONFIG_SCSI_NCR53C8XX is not set
# CONFIG_SCSI_PPA is not set
# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
# CONFIG_SCSI_QLOGIC_ISP is not set
# CONFIG_SCSI_SEAGATE is not set
@@ -158,27 +205,47 @@ CONFIG_NETDEVICES=y
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
# CONFIG_EQUALIZER is not set
-# CONFIG_ETHERTAP is not set
CONFIG_NET_ETHERNET=y
CONFIG_MACE=y
-CONFIG_DEC_ELCP=m
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
# CONFIG_NET_ISA is not set
-# CONFIG_NET_EISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=m
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_TLAN is not set
+# CONFIG_ES3210 is not set
+# CONFIG_ZNET is not set
# CONFIG_NET_POCKET is not set
# CONFIG_FDDI is not set
# CONFIG_DLCI is not set
# CONFIG_PLIP is not set
CONFIG_PPP=m
-# CONFIG_NET_RADIO is not set
+
+#
+# CCP compressors for PPP are only built as modules.
+#
# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
# CONFIG_TR is not set
# CONFIG_SHAPER is not set
#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
# ISDN subsystem
#
# CONFIG_ISDN is not set
@@ -187,28 +254,68 @@ CONFIG_PPP=m
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
#
# Filesystems
#
# CONFIG_QUOTA is not set
-CONFIG_MINIX_FS=m
+# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
CONFIG_ISO9660_FS=y
-# CONFIG_NLS is not set
+# CONFIG_JOLIET is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
-# CONFIG_NFSD is not set
+CONFIG_NFSD=y
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
# CONFIG_SMB_FS is not set
# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=m
# CONFIG_ROMFS_FS is not set
CONFIG_AUTOFS_FS=y
# CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
CONFIG_MAC_PARTITION=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
#
# Character devices
@@ -217,20 +324,25 @@ CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_SOFTCURSOR is not set
CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_PRINTER is not set
# CONFIG_MOUSE is not set
# CONFIG_UMISC is not set
# CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
# CONFIG_APM is not set
# CONFIG_WATCHDOG is not set
# CONFIG_RTC is not set
# CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
CONFIG_NVRAM=y
# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
#
# Sound
diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig
index 147fa3005..a323e652e 100644
--- a/arch/ppc/prep_defconfig
+++ b/arch/ppc/prep_defconfig
@@ -6,12 +6,14 @@
# Platform support
#
CONFIG_PPC=y
-CONFIG_NATIVE=y
-CONFIG_MACH_SPECIFIC=y
+CONFIG_6xx=y
+# CONFIG_8xx is not set
# CONFIG_PMAC is not set
CONFIG_PREP=y
# CONFIG_CHRP is not set
-CONFIG_COMMON=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_MBX is not set
+CONFIG_MACH_SPECIFIC=y
#
# General setup
@@ -19,20 +21,25 @@ CONFIG_COMMON=y
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
+CONFIG_KERNELD=y
CONFIG_PCI=y
+# CONFIG_PCI_QUIRKS is not set
# CONFIG_PCI_OPTIMIZE is not set
+CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
-# CONFIG_SYSCTL is not set
+CONFIG_SYSCTL=y
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_BINFMT_ELF=y
CONFIG_KERNEL_ELF=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_BINFMT_JAVA is not set
+# CONFIG_PARPORT is not set
+# CONFIG_ABSTRACT_CONSOLE is not set
# CONFIG_PMAC_CONSOLE is not set
# CONFIG_MAC_KEYBOARD is not set
# CONFIG_MAC_FLOPPY is not set
+# CONFIG_MACMOUSE is not set
# CONFIG_PROC_DEVICETREE is not set
# CONFIG_XMON is not set
CONFIG_VGA_CONSOLE=y
@@ -55,14 +62,16 @@ CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDESCSI is not set
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_TRITON is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
# CONFIG_IDE_CHIPSETS is not set
-# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_EZ is not set
+CONFIG_PARIDE_PARPORT=y
+# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_HD is not set
#
@@ -77,32 +86,34 @@ CONFIG_BLK_DEV_INITRD=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
# CONFIG_NET_ALIAS is not set
+# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
+CONFIG_IP_ACCT=y
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_ALIAS is not set
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
CONFIG_INET_RARP=y
# CONFIG_IP_NOSR is not set
CONFIG_SKB_LARGE=y
# CONFIG_IPV6 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
# CONFIG_LLC is not set
# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
# CONFIG_NET_SCHED is not set
+# CONFIG_NET_PROFILE is not set
#
# SCSI support
@@ -115,6 +126,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y
# CONFIG_CHR_DEV_SG is not set
# CONFIG_SCSI_MULTI_LUN is not set
# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
#
# SCSI low-level drivers
@@ -133,6 +145,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_NCR53C7xx is not set
@@ -143,8 +156,10 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
CONFIG_SCSI_NCR53C8XX_SYNC=5
# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
-# CONFIG_SCSI_PPA is not set
# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
# CONFIG_SCSI_QLOGIC_FAS is not set
# CONFIG_SCSI_QLOGIC_ISP is not set
# CONFIG_SCSI_SEAGATE is not set
@@ -152,6 +167,7 @@ CONFIG_SCSI_NCR53C8XX_SYNC=5
# CONFIG_SCSI_T128 is not set
# CONFIG_SCSI_U14_34F is not set
# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_DEBUG is not set
# CONFIG_SCSI_MESH is not set
# CONFIG_SCSI_MAC53C94 is not set
@@ -162,12 +178,13 @@ CONFIG_NETDEVICES=y
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
# CONFIG_EQUALIZER is not set
-# CONFIG_ETHERTAP is not set
CONFIG_NET_ETHERNET=y
# CONFIG_NET_VENDOR_3COM is not set
CONFIG_LANCE=y
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
# CONFIG_NET_ISA is not set
CONFIG_NET_EISA=y
CONFIG_PCNET32=y
@@ -184,14 +201,18 @@ CONFIG_DE4X5=y
# CONFIG_NET_POCKET is not set
# CONFIG_FDDI is not set
# CONFIG_DLCI is not set
-# CONFIG_PLIP is not set
-CONFIG_PPP=m
-# CONFIG_NET_RADIO is not set
+CONFIG_PPP=y
# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
# CONFIG_TR is not set
# CONFIG_SHAPER is not set
#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
# ISDN subsystem
#
# CONFIG_ISDN is not set
@@ -200,6 +221,7 @@ CONFIG_PPP=m
# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
#
# CONFIG_CD_NO_IDESCSI is not set
+CONFIG_CDROM=y
#
# Filesystems
@@ -208,29 +230,59 @@ CONFIG_PPP=m
# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
+CONFIG_JOLIET=y
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
# CONFIG_UMSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=y
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
-# CONFIG_NFSD is not set
+CONFIG_NFSD=y
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
# CONFIG_SMB_FS is not set
# CONFIG_HPFS_FS is not set
+# CONFIG_NTFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
# CONFIG_ROMFS_FS is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_UFS_FS is not set
+# CONFIG_ADFS_FS is not set
# CONFIG_MAC_PARTITION is not set
+CONFIG_NLS=y
#
# Native Language Support
#
-# CONFIG_NLS is not set
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
#
# Character devices
@@ -239,14 +291,9 @@ CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_SOFTCURSOR is not set
CONFIG_SERIAL=y
-CONFIG_SERIAL_EXTENDED=y
-# CONFIG_SERIAL_MANY_PORTS is not set
-# CONFIG_SERIAL_SHARE_IRQ is not set
-# CONFIG_SERIAL_MULTIPORT is not set
-# CONFIG_HUB6 is not set
CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_PRINTER is not set
CONFIG_MOUSE=y
# CONFIG_ATIXL_BUSMOUSE is not set
# CONFIG_BUSMOUSE is not set
@@ -256,14 +303,18 @@ CONFIG_PSMOUSE=y
# CONFIG_PC110_PAD is not set
# CONFIG_UMISC is not set
# CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
# CONFIG_APM is not set
# CONFIG_WATCHDOG is not set
# CONFIG_RTC is not set
# CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_BT848 is not set
# CONFIG_NVRAM is not set
# CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
#
# Sound
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 7a8d46a07..a4870e117 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.29 1997/07/11 11:05:23 jj Exp $
+# $Id: Makefile,v 1.34 1998/04/06 16:09:34 jj Exp $
# sparc/Makefile
#
# Makefile for the architecture dependent flags and dependencies on the
@@ -23,14 +23,18 @@ LINKFLAGS = -T arch/sparc/vmlinux.lds
HEAD := arch/sparc/kernel/head.o arch/sparc/kernel/init_task.o
-SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm \
- arch/sparc/prom
+# Note arch/sparc/mm has to be the last subdir
+SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/prom \
+ arch/sparc/mm
CORE_FILES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(CORE_FILES)
LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/prom/promlib.a \
$(TOPDIR)/arch/sparc/lib/lib.a
+SUBDIRS += arch/sparc/math-emu
+CORE_FILES += arch/sparc/math-emu/math-emu.o
+
ifdef CONFIG_AP1000
SUBDIRS := $(SUBDIRS) arch/sparc/ap1000 mpp
CORE_FILES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o \
@@ -40,11 +44,30 @@ CFLAGS := $(CFLAGS) -D__MPP__=1
endif
archclean:
+ -$(MAKE) -C arch/sparc/boot archclean
+ -$(MAKE) -C arch/sparc/math-emu cleansymlinks
archdep:
+ -$(MAKE) -C arch/sparc/math-emu symlinks
check_asm:
$(MAKE) -C arch/sparc/kernel check_asm
tftpboot.img:
$(MAKE) -C arch/sparc/boot tftpboot.img
+
+vmlinux.o: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
+ $(LD) -r $(VMLINUX.OBJS) -o vmlinux.o
+
+arch/sparc/boot/btfix.s: arch/sparc/boot/btfixupprep vmlinux.o
+ $(OBJDUMP) -x vmlinux.o | arch/sparc/boot/btfixupprep > arch/sparc/boot/btfix.s
+
+arch/sparc/boot/btfix.o: arch/sparc/boot/btfix.s
+ $(CC) -c -o arch/sparc/boot/btfix.o arch/sparc/boot/btfix.s
+
+arch/sparc/boot/btfixupprep: arch/sparc/boot/btfixupprep.c
+ $(MAKE) -C arch/sparc/boot btfixupprep
+
+vmlinux: arch/sparc/boot/btfix.o
+ $(LD) $(LINKFLAGS) vmlinux.o arch/sparc/boot/btfix.o -o vmlinux
+ $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
diff --git a/arch/sparc/ap1000/apmmu.c b/arch/sparc/ap1000/apmmu.c
index 0bafe3fc9..e07b4f4b1 100644
--- a/arch/sparc/ap1000/apmmu.c
+++ b/arch/sparc/ap1000/apmmu.c
@@ -36,16 +36,10 @@
#include <asm/viking.h>
-static unsigned long (*mmu_getpage)(void);
-static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
-static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
-
-static void (*flush_page_for_dma)(unsigned long page);
-static void (*flush_cache_page_to_uncache)(unsigned long page);
-static void (*flush_tlb_page_for_cbit)(unsigned long page);
-
extern void mc_tlb_flush_all(void);
+static void poke_viking(void);
+static void viking_flush_tlb_page_for_cbit)(unsigned long page);
static struct apmmu_stats {
int invall;
@@ -103,11 +97,6 @@ static inline unsigned long apmmu_swap(unsigned long *addr, unsigned long value)
static unsigned int apmmu_pmd_align(unsigned int addr) { return APMMU_PMD_ALIGN(addr); }
static unsigned int apmmu_pgdir_align(unsigned int addr) { return APMMU_PGDIR_ALIGN(addr); }
-static unsigned long apmmu_vmalloc_start(void)
-{
- return APMMU_VMALLOC_START;
-}
-
static inline int apmmu_device_memory(unsigned long x)
{
return ((x & 0xF0000000) != 0);
@@ -152,13 +141,6 @@ static int apmmu_pgd_present(pgd_t pgd)
static void apmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); }
-static int apmmu_pte_write(pte_t pte) { return pte_val(pte) & APMMU_WRITE; }
-static int apmmu_pte_dirty(pte_t pte) { return pte_val(pte) & APMMU_DIRTY; }
-static int apmmu_pte_young(pte_t pte) { return pte_val(pte) & APMMU_REF; }
-
-static pte_t apmmu_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~APMMU_WRITE);}
-static pte_t apmmu_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~APMMU_DIRTY);}
-static pte_t apmmu_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~APMMU_REF);}
static pte_t apmmu_pte_mkwrite(pte_t pte) { return __pte(pte_val(pte) | APMMU_WRITE);}
static pte_t apmmu_pte_mkdirty(pte_t pte) { return __pte(pte_val(pte) | APMMU_DIRTY);}
static pte_t apmmu_pte_mkyoung(pte_t pte) { return __pte(pte_val(pte) | APMMU_REF);}
@@ -221,7 +203,7 @@ static void apmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp)
{
if(tsk->mm->context != NO_CONTEXT) {
flush_cache_mm(current->mm);
- ctxd_set(&apmmu_context_table[tsk->mm->context], pgdp);
+ apmmu_ctxd_set(&apmmu_context_table[tsk->mm->context], pgdp);
flush_tlb_mm(current->mm);
}
}
@@ -311,8 +293,6 @@ static inline unsigned long apmmu_hwprobe(unsigned long vaddr)
return retval;
}
-
-
static inline void apmmu_uncache_page(unsigned long addr)
{
pgd_t *pgdp = apmmu_pgd_offset(init_task.mm, addr);
@@ -330,9 +310,8 @@ static inline void apmmu_uncache_page(unsigned long addr)
}
}
- flush_cache_page_to_uncache(addr);
set_pte(ptep, __pte((pte_val(*ptep) & ~APMMU_CACHE)));
- flush_tlb_page_for_cbit(addr);
+ viking_flush_tlb_page_for_cbit(addr);
}
static inline void apmmu_recache_page(unsigned long addr)
@@ -352,10 +331,10 @@ static inline void apmmu_recache_page(unsigned long addr)
}
}
set_pte(ptep, __pte((pte_val(*ptep) | APMMU_CACHE)));
- flush_tlb_page_for_cbit(addr);
+ viking_flush_tlb_page_for_cbit(addr);
}
-static unsigned long apmmu_getpage(void)
+static inline unsigned long apmmu_getpage(void)
{
unsigned long page = get_free_page(GFP_KERNEL);
@@ -368,13 +347,44 @@ static inline void apmmu_putpage(unsigned long page)
}
/* The easy versions. */
-#define NEW_PGD() (pgd_t *) mmu_getpage()
-#define NEW_PMD() (pmd_t *) mmu_getpage()
-#define NEW_PTE() (pte_t *) mmu_getpage()
+#define NEW_PGD() (pgd_t *) apmmu_getpage()
+#define NEW_PMD() (pmd_t *) apmmu_getpage()
+#define NEW_PTE() (pte_t *) apmmu_getpage()
#define FREE_PGD(chunk) apmmu_putpage((unsigned long)(chunk))
#define FREE_PMD(chunk) apmmu_putpage((unsigned long)(chunk))
#define FREE_PTE(chunk) apmmu_putpage((unsigned long)(chunk))
+static pte_t *apmmu_get_pte_fast(void)
+{
+ return (pte_t *)0;
+}
+
+static pmd_t *apmmu_get_pmd_fast(void)
+{
+ return (pmd_t *)0;
+}
+
+static pgd_t *apmmu_get_pgd_fast(void)
+{
+ return (pgd_t *)0;
+}
+
+static void apmmu_free_pte_slow(pte_t *pte)
+{
+/* TBD */
+}
+
+static void apmmu_free_pmd_slow(pmd_t *pmd)
+{
+/* TBD */
+}
+
+static void apmmu_free_pgd_slow(pgd_t *pgd)
+{
+/* TBD */
+}
+
+
/*
* Allocate and free page tables. The xxx_kernel() versions are
* used to allocate a kernel page table - this turns on ASN bits
@@ -392,17 +402,17 @@ static pte_t *apmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
pte_t *page = NEW_PTE();
if(apmmu_pmd_none(*pmd)) {
if(page) {
- pmd_set(pmd, page);
+ apmmu_pmd_set(pmd, page);
return page + address;
}
- pmd_set(pmd, BAD_PAGETABLE);
+ apmmu_pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
FREE_PTE(page);
}
if(apmmu_pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- pmd_set(pmd, BAD_PAGETABLE);
+ apmmu_pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
return (pte_t *) apmmu_pmd_page(*pmd) + address;
@@ -449,17 +459,17 @@ static pte_t *apmmu_pte_alloc(pmd_t * pmd, unsigned long address)
pte_t *page = NEW_PTE();
if(apmmu_pmd_none(*pmd)) {
if(page) {
- pmd_set(pmd, page);
+ apmmu_pmd_set(pmd, page);
return page + address;
}
- pmd_set(pmd, BAD_PAGETABLE);
+ apmmu_pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
FREE_PTE(page);
}
if(apmmu_pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- pmd_set(pmd, BAD_PAGETABLE);
+ apmmu_pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
return ((pte_t *) apmmu_pmd_page(*pmd)) + address;
@@ -525,7 +535,7 @@ static inline void alloc_context(struct task_struct *tsk)
struct mm_struct *mm = tsk->mm;
struct ctx_list *ctxp;
- if (tsk->taskid >= MPP_TASK_BASE) {
+ if (tsk->taskid >= MPP_TASK_BASE) {
mm->context = MPP_CONTEXT_BASE + (tsk->taskid - MPP_TASK_BASE);
return;
}
@@ -570,7 +580,7 @@ static void apmmu_switch_to_context(struct task_struct *tsk)
if(tsk->mm->context == NO_CONTEXT) {
alloc_context(tsk);
flush_cache_mm(current->mm);
- ctxd_set(&apmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+ apmmu_ctxd_set(&apmmu_context_table[tsk->mm->context], tsk->mm->pgd);
flush_tlb_mm(current->mm);
}
apmmu_set_context(tsk->mm->context);
@@ -590,29 +600,11 @@ struct task_struct *apmmu_alloc_task_struct(void)
return (struct task_struct *) kmalloc(sizeof(struct task_struct), GFP_KERNEL);
}
-static unsigned long apmmu_alloc_kernel_stack(struct task_struct *tsk)
-{
- unsigned long kstk = __get_free_pages(GFP_KERNEL, 1);
-
- if(!kstk)
- kstk = (unsigned long) vmalloc(PAGE_SIZE << 1);
-
- return kstk;
-}
-
static void apmmu_free_task_struct(struct task_struct *tsk)
{
kfree(tsk);
}
-static void apmmu_free_kernel_stack(unsigned long stack)
-{
- if(stack < VMALLOC_START)
- free_pages(stack, 1);
- else
- vfree((char *)stack);
-}
-
static void apmmu_null_func(void)
{
}
@@ -925,22 +917,20 @@ extern unsigned long sparc_context_init(unsigned long, int);
extern int physmem_mapped_contig;
extern int linux_num_cpus;
-void (*poke_apmmu)(void);
-
__initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned long end_mem))
{
int i;
physmem_mapped_contig = 1; /* for init.c:taint_real_pages() */
- num_contexts = AP_NUM_CONTEXTS;
+ num_contexts = AP_NUM_CONTEXTS;
mempool = PAGE_ALIGN(start_mem);
memset(swapper_pg_dir, 0, PAGE_SIZE);
apmmu_allocate_ptable_skeleton(KERNBASE, end_mem);
mempool = PAGE_ALIGN(mempool);
map_kernel();
- ap_setup_mappings();
+ ap_setup_mappings();
/* the MSC wants this aligned on a 16k boundary */
apmmu_context_table =
@@ -950,14 +940,14 @@ __initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned lon
num_contexts*sizeof(ctxd_t));
apmmu_ctx_table_phys = (ctxd_t *) apmmu_v2p((unsigned long) apmmu_context_table);
for(i = 0; i < num_contexts; i++)
- ctxd_set(&apmmu_context_table[i], swapper_pg_dir);
+ apmmu_ctxd_set(&apmmu_context_table[i], swapper_pg_dir);
start_mem = PAGE_ALIGN(mempool);
flush_cache_all();
apmmu_set_ctable_ptr((unsigned long) apmmu_ctx_table_phys);
flush_tlb_all();
- poke_apmmu();
+ poke_viking();
/* on the AP we don't put the top few contexts into the free
context list as these are reserved for parallel tasks */
@@ -967,11 +957,10 @@ __initfunc(unsigned long apmmu_paging_init(unsigned long start_mem, unsigned lon
return PAGE_ALIGN(start_mem);
}
-static char apmmuinfo[512];
-
-static char *apmmu_mmu_info(void)
+static int apmmu_mmu_info(char *buf)
{
- sprintf(apmmuinfo, "MMU type\t: %s\n"
+ return sprintf(buf,
+ "MMU type\t: %s\n"
"invall\t\t: %d\n"
"invmm\t\t: %d\n"
"invrnge\t\t: %d\n"
@@ -984,35 +973,12 @@ static char *apmmu_mmu_info(void)
module_stats.invpg,
num_contexts
);
- return apmmuinfo;
}
static void apmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
{
}
-static void apmmu_exit_hook(void)
-{
- struct mm_struct *mm = current->mm;
-
- if(mm->context != NO_CONTEXT && mm->count == 1) {
- ctxd_set(&apmmu_context_table[mm->context], swapper_pg_dir);
- viking_flush_tlb_mm(mm);
- free_context(mm->context);
- mm->context = NO_CONTEXT;
- }
-}
-
-static void apmmu_flush_hook(void)
-{
- if(current->tss.flags & SPARC_FLAG_KTHREAD) {
- alloc_context(current);
- ctxd_set(&apmmu_context_table[current->mm->context], current->mm->pgd);
- viking_flush_tlb_mm(current->mm);
- apmmu_set_context(current->mm->context);
- }
-}
-
__initfunc(static void poke_viking(void))
{
unsigned long mreg = apmmu_get_mmureg();
@@ -1020,7 +986,7 @@ __initfunc(static void poke_viking(void))
mreg |= VIKING_SPENABLE;
mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
mreg &= ~VIKING_ACENABLE;
- mreg &= ~VIKING_SBENABLE;
+ mreg &= ~VIKING_SBENABLE;
mreg |= VIKING_TCENABLE;
apmmu_set_mmureg(mreg);
}
@@ -1029,24 +995,18 @@ __initfunc(static void init_viking(void))
{
apmmu_name = "TI Viking/AP1000";
- flush_cache_page_to_uncache = apmmu_null_func;
- flush_page_for_dma = apmmu_null_func;
-
- flush_cache_all = apmmu_null_func;
- flush_cache_mm = apmmu_null_func;
- flush_cache_page = apmmu_null_func;
- flush_cache_range = apmmu_null_func;
+ BTFIXUPSET_CALL(flush_cache_all, apmmu_null_func, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_cache_mm, apmmu_null_func, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_cache_page, apmmu_null_func, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_cache_range, apmmu_null_func, BTFIXUPCALL_NOP);
- flush_tlb_all = viking_flush_tlb_all;
- flush_tlb_mm = viking_flush_tlb_mm;
- flush_tlb_page = viking_flush_tlb_page;
- flush_tlb_range = viking_flush_tlb_range;
+ BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
- flush_page_to_ram = apmmu_null_func;
- flush_sig_insns = apmmu_null_func;
- flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit;
-
- poke_apmmu = poke_viking;
+ BTFIXUPSET_CALL(flush_page_to_ram, apmmu_null_func, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_sig_insns, apmmu_null_func, BTFIXUPCALL_NOP);
}
@@ -1062,7 +1022,7 @@ extern unsigned long srmmu_fault;
iaddr = &(insn); \
daddr = &(dest); \
*iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \
- } while(0);
+ } while(0);
__initfunc(static void patch_window_trap_handlers(void))
{
@@ -1077,113 +1037,109 @@ __initfunc(static void patch_window_trap_handlers(void))
PATCH_BRANCH(sparc_ttable[SP_TRAP_DACC].inst_three, srmmu_fault);
}
-/* Load up routines and constants for sun4m mmu */
+/* Load up routines and constants for apmmu */
__initfunc(void ld_mmu_apmmu(void))
{
/* First the constants */
- pmd_shift = APMMU_PMD_SHIFT;
- pmd_size = APMMU_PMD_SIZE;
- pmd_mask = APMMU_PMD_MASK;
- pgdir_shift = APMMU_PGDIR_SHIFT;
- pgdir_size = APMMU_PGDIR_SIZE;
- pgdir_mask = APMMU_PGDIR_MASK;
-
- ptrs_per_pte = APMMU_PTRS_PER_PTE;
- ptrs_per_pmd = APMMU_PTRS_PER_PMD;
- ptrs_per_pgd = APMMU_PTRS_PER_PGD;
-
- page_none = APMMU_PAGE_NONE;
- page_shared = APMMU_PAGE_SHARED;
- page_copy = APMMU_PAGE_COPY;
- page_readonly = APMMU_PAGE_RDONLY;
- page_kernel = APMMU_PAGE_KERNEL;
+ BTFIXUPSET_SIMM13(pmd_shift, APMMU_PMD_SHIFT);
+ BTFIXUPSET_SETHI(pmd_size, APMMU_PMD_SIZE);
+ BTFIXUPSET_SETHI(pmd_mask, APMMU_PMD_MASK);
+ BTFIXUPSET_SIMM13(pgdir_shift, APMMU_PGDIR_SHIFT);
+ BTFIXUPSET_SETHI(pgdir_size, APMMU_PGDIR_SIZE);
+ BTFIXUPSET_SETHI(pgdir_mask, APMMU_PGDIR_MASK);
+
+ BTFIXUPSET_SIMM13(ptrs_per_pte, APMMU_PTRS_PER_PTE);
+ BTFIXUPSET_SIMM13(ptrs_per_pmd, APMMU_PTRS_PER_PMD);
+ BTFIXUPSET_SIMM13(ptrs_per_pgd, APMMU_PTRS_PER_PGD);
+
+ BTFIXUPSET_INT(page_none, pgprot_val(APMMU_PAGE_NONE));
+ BTFIXUPSET_INT(page_shared, pgprot_val(APMMU_PAGE_SHARED));
+ BTFIXUPSET_INT(page_copy, pgprot_val(APMMU_PAGE_COPY));
+ BTFIXUPSET_INT(page_readonly, pgprot_val(APMMU_PAGE_RDONLY));
+ BTFIXUPSET_INT(page_kernel, pgprot_val(APMMU_PAGE_KERNEL));
pg_iobits = APMMU_VALID | APMMU_WRITE | APMMU_REF;
/* Functions */
- mmu_getpage = apmmu_getpage;
- set_pte = apmmu_set_pte_cacheable;
- switch_to_context = apmmu_switch_to_context;
- pmd_align = apmmu_pmd_align;
- pgdir_align = apmmu_pgdir_align;
- vmalloc_start = apmmu_vmalloc_start;
-
- pte_page = apmmu_pte_page;
- pmd_page = apmmu_pmd_page;
- pgd_page = apmmu_pgd_page;
-
- sparc_update_rootmmu_dir = apmmu_update_rootmmu_dir;
-
- pte_none = apmmu_pte_none;
- pte_present = apmmu_pte_present;
- pte_clear = apmmu_pte_clear;
-
- pmd_none = apmmu_pmd_none;
- pmd_bad = apmmu_pmd_bad;
- pmd_present = apmmu_pmd_present;
- pmd_clear = apmmu_pmd_clear;
-
- pgd_none = apmmu_pgd_none;
- pgd_bad = apmmu_pgd_bad;
- pgd_present = apmmu_pgd_present;
- pgd_clear = apmmu_pgd_clear;
-
- mk_pte = apmmu_mk_pte;
- mk_pte_phys = apmmu_mk_pte_phys;
- pgd_set = apmmu_pgd_set;
- mk_pte_io = apmmu_mk_pte_io;
- pte_modify = apmmu_pte_modify;
- pgd_offset = apmmu_pgd_offset;
- pmd_offset = apmmu_pmd_offset;
- pte_offset = apmmu_pte_offset;
- pte_free_kernel = apmmu_pte_free_kernel;
- pmd_free_kernel = apmmu_pmd_free_kernel;
- pte_alloc_kernel = apmmu_pte_alloc_kernel;
- pmd_alloc_kernel = apmmu_pmd_alloc_kernel;
- pte_free = apmmu_pte_free;
- pte_alloc = apmmu_pte_alloc;
- pmd_free = apmmu_pmd_free;
- pmd_alloc = apmmu_pmd_alloc;
- pgd_free = apmmu_pgd_free;
- pgd_alloc = apmmu_pgd_alloc;
- pgd_flush = apmmu_pgd_flush;
-
- pte_write = apmmu_pte_write;
- pte_dirty = apmmu_pte_dirty;
- pte_young = apmmu_pte_young;
- pte_wrprotect = apmmu_pte_wrprotect;
- pte_mkclean = apmmu_pte_mkclean;
- pte_mkold = apmmu_pte_mkold;
- pte_mkwrite = apmmu_pte_mkwrite;
- pte_mkdirty = apmmu_pte_mkdirty;
- pte_mkyoung = apmmu_pte_mkyoung;
- update_mmu_cache = apmmu_update_mmu_cache;
- mmu_exit_hook = apmmu_exit_hook;
- mmu_flush_hook = apmmu_flush_hook;
- mmu_lockarea = apmmu_lockarea;
- mmu_unlockarea = apmmu_unlockarea;
-
- mmu_get_scsi_one = NULL;
- mmu_get_scsi_sgl = NULL;
- mmu_release_scsi_one = NULL;
- mmu_release_scsi_sgl = NULL;
-
- mmu_info = apmmu_mmu_info;
- mmu_v2p = apmmu_v2p;
- mmu_p2v = apmmu_p2v;
+ BTFIXUPSET_CALL(get_pte_fast, apmmu_get_pte_fast, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(get_pmd_fast, apmmu_get_pmd_fast, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(get_pgd_fast, apmmu_get_pgd_fast, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(free_pte_slow, apmmu_free_pte_slow, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(free_pmd_slow, apmmu_free_pmd_slow, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(free_pgd_slow, apmmu_free_pgd_slow, BTFIXUPCALL_NOP);
- /* Task struct and kernel stack allocating/freeing. */
- alloc_kernel_stack = apmmu_alloc_kernel_stack;
- alloc_task_struct = apmmu_alloc_task_struct;
- free_kernel_stack = apmmu_free_kernel_stack;
- free_task_struct = apmmu_free_task_struct;
+ BTFIXUPSET_CALL(set_pte, apmmu_set_pte_cacheable, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(switch_to_context, apmmu_switch_to_context, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(pte_page, apmmu_pte_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_page, apmmu_pmd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_page, apmmu_pgd_page, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(sparc_update_rootmmu_dir, apmmu_update_rootmmu_dir, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_SETHI(none_mask, 0xF0000000);
+
+ BTFIXUPSET_CALL(pte_present, apmmu_pte_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_clear, apmmu_pte_clear, BTFIXUPCALL_NORM);
- quick_kernel_fault = apmmu_quick_kernel_fault;
+ BTFIXUPSET_CALL(pmd_bad, apmmu_pmd_bad, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_present, apmmu_pmd_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_clear, apmmu_pmd_clear, BTFIXUPCALL_NORM);
- ctxd_set = apmmu_ctxd_set;
- pmd_set = apmmu_pmd_set;
+ BTFIXUPSET_CALL(pgd_none, apmmu_pgd_none, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_bad, apmmu_pgd_bad, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_present, apmmu_pgd_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_clear, apmmu_pgd_clear, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mk_pte, apmmu_mk_pte, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte_phys, apmmu_mk_pte_phys, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte_io, apmmu_mk_pte_io, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_set, apmmu_pgd_set, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_INT(pte_modify_mask, APMMU_CHG_MASK);
+ BTFIXUPSET_CALL(pgd_offset, apmmu_pgd_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_offset, apmmu_pmd_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_offset, apmmu_pte_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_free_kernel, apmmu_pte_free_kernel, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_free_kernel, apmmu_pmd_free_kernel, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_alloc_kernel, apmmu_pte_alloc_kernel, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_alloc_kernel, apmmu_pmd_alloc_kernel, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_free, apmmu_pte_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_alloc, apmmu_pte_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_free, apmmu_pmd_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_alloc, apmmu_pmd_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_free, apmmu_pgd_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_alloc, apmmu_pgd_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_flush, apmmu_pgd_flush, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_HALF(pte_writei, APMMU_WRITE);
+ BTFIXUPSET_HALF(pte_dirtyi, APMMU_DIRTY);
+ BTFIXUPSET_HALF(pte_youngi, APMMU_REF);
+ BTFIXUPSET_HALF(pte_wrprotecti, APMMU_WRITE);
+ BTFIXUPSET_HALF(pte_mkcleani, APMMU_DIRTY);
+ BTFIXUPSET_HALF(pte_mkoldi, APMMU_REF);
+ BTFIXUPSET_CALL(pte_mkwrite, apmmu_pte_mkwrite, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_mkdirty, apmmu_pte_mkdirty, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_mkyoung, apmmu_pte_mkyoung, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(update_mmu_cache, apmmu_update_mmu_cache, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(mmu_lockarea, apmmu_lockarea, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_unlockarea, apmmu_unlockarea, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mmu_get_scsi_one, apmmu_null_func, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(mmu_get_scsi_sgl, apmmu_null_func, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(mmu_release_scsi_one, apmmu_null_func, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(mmu_release_scsi_sgl, apmmu_null_func, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(mmu_info, apmmu_mmu_info, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_v2p, apmmu_v2p, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_p2v, apmmu_p2v, BTFIXUPCALL_NORM);
+
+ /* Task struct and kernel stack allocating/freeing. */
+ BTFIXUPSET_CALL(alloc_task_struct, apmmu_alloc_task_struct, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(free_task_struct, apmmu_free_task_struct, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(quick_kernel_fault, apmmu_quick_kernel_fault, BTFIXUPCALL_NORM);
init_viking();
patch_window_trap_handlers();
}
-
-
diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile
index af462db3a..c9301a79e 100644
--- a/arch/sparc/boot/Makefile
+++ b/arch/sparc/boot/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.4 1997/07/11 11:05:18 jj Exp $
+# $Id: Makefile,v 1.6 1998/02/23 01:44:39 rth Exp $
# Makefile for the Sparc boot stuff.
#
# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -19,5 +19,11 @@ tftpboot.img: piggyback
piggyback: piggyback.c
$(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c
+btfixupprep: btfixupprep.c
+ $(HOSTCC) $(HOSTCFLAGS) -o btfixupprep btfixupprep.c
+
+archclean:
+ rm -f btfixupprep piggyback tftpboot.img
+
dep:
diff --git a/arch/sparc/boot/btfixupprep.c b/arch/sparc/boot/btfixupprep.c
new file mode 100644
index 000000000..1bef965af
--- /dev/null
+++ b/arch/sparc/boot/btfixupprep.c
@@ -0,0 +1,345 @@
+/* $Id: btfixupprep.c,v 1.3 1998/03/09 14:03:10 jj Exp $
+ Simple utility to prepare vmlinux image for sparc.
+ Resolves all BTFIXUP uses and settings and creates
+ a special .s object to link to the image.
+
+ Copyright (C) 1998 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 <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#define MAXSYMS 1024
+
+static char *relrec = "RELOCATION RECORDS FOR [";
+static int rellen;
+
+struct _btfixup;
+
+typedef struct _btfixuprel {
+ char *sect;
+ unsigned long offset;
+ struct _btfixup *f;
+ int frel;
+ struct _btfixuprel *next;
+} btfixuprel;
+
+typedef struct _btfixup {
+ int type;
+ int setinitval;
+ unsigned int initval;
+ char *initvalstr;
+ char *name;
+ btfixuprel *rel;
+} btfixup;
+
+btfixup array[MAXSYMS];
+int last = 0;
+char buffer[1024];
+unsigned long lastfoffset = -1;
+unsigned long lastfrelno;
+btfixup *lastf;
+
+void fatal(void) __attribute__((noreturn));
+void fatal(void)
+{
+ fprintf(stderr, "Malformed output from objdump\n%s\n", buffer);
+ exit(1);
+}
+
+btfixup *find(int type, char *name)
+{
+ int i;
+ for (i = 0; i < last; i++) {
+ if (array[i].type == type && !strcmp(array[i].name, name))
+ return array + i;
+ }
+ array[last].type = type;
+ array[last].name = strdup(name);
+ array[last].setinitval = 0;
+ if (!array[last].name) fatal();
+ array[last].rel = NULL;
+ last++;
+ if (last >= MAXSYMS) {
+ fprintf(stderr, "Ugh. Something strange. More than %d different BTFIXUP symbols\n", MAXSYMS);
+ exit(1);
+ }
+ return array + last - 1;
+}
+
+int main(int argc,char **argv)
+{
+ char *p, *q;
+ char *sect;
+ int i, j, k;
+ unsigned int initval;
+ int shift;
+ btfixup *f;
+ btfixuprel *r, **rr;
+ unsigned long offset;
+ char *initvalstr;
+
+ rellen = strlen(relrec);
+ while (fgets (buffer, 1024, stdin) != NULL)
+ if (!strncmp (buffer, relrec, rellen))
+ goto main1;
+ fatal();
+main1:
+ sect = malloc(strlen (buffer + rellen) + 1);
+ if (!sect) fatal();
+ strcpy (sect, buffer + rellen);
+ p = strchr (sect, ']');
+ if (!p) fatal();
+ *p = 0;
+ if (fgets (buffer, 1024, stdin) == NULL)
+ fatal();
+ while (fgets (buffer, 1024, stdin) != NULL) {
+ if (!strncmp (buffer, relrec, rellen))
+ goto main1;
+ p = strchr (buffer, '\n');
+ if (p) *p = 0;
+ if (strlen (buffer) < 30)
+ continue;
+ if (strncmp (buffer + 8, " R_SPARC_", 9))
+ continue;
+ if (buffer[27] != '_' || buffer[28] != '_' || buffer[29] != '_')
+ continue;
+ switch (buffer[30]) {
+ case 'f': /* CALL */
+ case 'b': /* BLACKBOX */
+ case 's': /* SIMM13 */
+ case 'a': /* HALF */
+ case 'h': /* SETHI */
+ case 'i': /* INT */
+ break;
+ default:
+ continue;
+ }
+ p = strchr (buffer + 32, '+');
+ if (p) *p = 0;
+ shift = 32;
+ if (buffer[31] == 's' && buffer[32] == '_') {
+ shift = 33;
+ if (strcmp (sect, ".text.init")) {
+ fprintf(stderr, "Wrong use of '%s' BTFIXUPSET.\nBTFIXUPSET_CALL can be used only in __init sections\n", buffer+shift);
+ exit(1);
+ }
+ } else if (buffer[31] != '_')
+ continue;
+ if (strcmp (sect, ".text") && strcmp (sect, ".text.init") && (strcmp (sect, "__ksymtab") || buffer[30] != 'f')) {
+ if (buffer[30] == 'f')
+ fprintf(stderr, "Wrong use of '%s' in '%s' section. It can be only used in .text, .text.init and __ksymtab\n", buffer + shift, sect);
+ else
+ fprintf(stderr, "Wrong use of '%s' in '%s' section. It can be only used in .text and .text.init\n", buffer + shift, sect);
+ exit(1);
+ }
+ p = strstr (buffer + shift, "__btset_");
+ if (p && buffer[31] == 's') {
+ fprintf(stderr, "__btset_ in BTFIXUP name can only be used when defining the variable, not for setting\n%s\n", buffer);
+ exit(1);
+ }
+ initval = 0;
+ initvalstr = NULL;
+ if (p) {
+ if (p[8] != '0' || p[9] != 'x') {
+ fprintf(stderr, "Pre-initialized values can be only initialized with hexadecimal constants starting 0x\n%s\n", buffer);
+ exit(1);
+ }
+ initval = strtoul(p + 10, &q, 16);
+ if (*q || !initval) {
+ fprintf(stderr, "Pre-initialized values can be only in the form name__btset_0xXXXXXXXX where X are hex digits.\nThey cannot be name__btset_0x00000000 though. Use BTFIXUPDEF_XX instead of BTFIXUPDEF_XX_INIT then.\n%s\n", buffer);
+ exit(1);
+ }
+ initvalstr = p + 10;
+ *p = 0;
+ }
+ f = find(buffer[30], buffer + shift);
+ if (buffer[31] == 's')
+ continue;
+ switch (buffer[30]) {
+ case 'f':
+ if (initval) {
+ fprintf(stderr, "Cannot use pre-initalized fixups for calls\n%s\n", buffer);
+ exit(1);
+ }
+ if (!strcmp (sect, "__ksymtab")) {
+ if (strncmp (buffer + 17, "32 ", 10)) {
+ fprintf(stderr, "BTFIXUP_CALL in EXPORT_SYMBOL results in relocation other than R_SPARC_32\n\%s\n", buffer);
+ exit(1);
+ }
+ } else if (strncmp (buffer + 17, "WDISP30 ", 10) &&
+ strncmp (buffer + 17, "HI22 ", 10) &&
+ strncmp (buffer + 17, "LO10 ", 10)) {
+ fprintf(stderr, "BTFIXUP_CALL results in relocation other than R_SPARC_WDISP30, R_SPARC_HI22 or R_SPARC_LO10\n%s\n", buffer);
+ exit(1);
+ }
+ break;
+ case 'b':
+ if (initval) {
+ fprintf(stderr, "Cannot use pre-initialized fixups for blackboxes\n%s\n", buffer);
+ exit(1);
+ }
+ if (strncmp (buffer + 17, "HI22 ", 10)) {
+ fprintf(stderr, "BTFIXUP_BLACKBOX results in relocation other than R_SPARC_HI22\n%s\n", buffer);
+ exit(1);
+ }
+ break;
+ case 's':
+ if (initval + 0x1000 >= 0x2000) {
+ fprintf(stderr, "Wrong initializer for SIMM13. Has to be from $fffff000 to $00000fff\n%s\n", buffer);
+ exit(1);
+ }
+ if (strncmp (buffer + 17, "13 ", 10)) {
+ fprintf(stderr, "BTFIXUP_SIMM13 results in relocation other than R_SPARC_13\n%s\n", buffer);
+ exit(1);
+ }
+ break;
+ case 'a':
+ if (initval + 0x1000 >= 0x2000 && (initval & 0x3ff)) {
+ fprintf(stderr, "Wrong initializer for HALF.\n%s\n", buffer);
+ exit(1);
+ }
+ if (strncmp (buffer + 17, "13 ", 10)) {
+ fprintf(stderr, "BTFIXUP_HALF results in relocation other than R_SPARC_13\n%s\n", buffer);
+ exit(1);
+ }
+ break;
+ case 'h':
+ if (initval & 0x3ff) {
+ fprintf(stderr, "Wrong initializer for SETHI. Cannot have set low 10 bits\n%s\n", buffer);
+ exit(1);
+ }
+ if (strncmp (buffer + 17, "HI22 ", 10)) {
+ fprintf(stderr, "BTFIXUP_SETHI results in relocation other than R_SPARC_HI22\n%s\n", buffer);
+ exit(1);
+ }
+ break;
+ case 'i':
+ if (initval) {
+ fprintf(stderr, "Cannot use pre-initalized fixups for INT\n%s\n", buffer);
+ exit(1);
+ }
+ if (strncmp (buffer + 17, "HI22 ", 10) && strncmp (buffer + 17, "LO10 ", 10)) {
+ fprintf(stderr, "BTFIXUP_INT results in relocation other than R_SPARC_HI22 and R_SPARC_LO10\n%s\n", buffer);
+ exit(1);
+ }
+ break;
+ }
+ if (!f->setinitval) {
+ f->initval = initval;
+ if (initvalstr) {
+ f->initvalstr = strdup(initvalstr);
+ if (!f->initvalstr) fatal();
+ }
+ f->setinitval = 1;
+ } else if (f->initval != initval) {
+ fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer\n%s\n",
+ f->name, f->initvalstr ? : "0x00000000", buffer);
+ exit(1);
+ } else if (initval && strcmp(f->initvalstr, initvalstr)) {
+ fprintf(stderr, "Btfixup %s previously used with initializer %s which doesn't match with current initializer.\n"
+ "Initializers have to match literally as well.\n%s\n",
+ f->name, f->initvalstr, buffer);
+ exit(1);
+ }
+ offset = strtoul(buffer, &q, 16);
+ if (q != buffer + 8 || (!offset && strncmp (buffer, "00000000 ", 9))) {
+ fprintf(stderr, "Malformed relocation address in\n%s\n", buffer);
+ exit(1);
+ }
+ for (k = 0, r = f->rel, rr = &f->rel; r; rr = &r->next, r = r->next, k++)
+ if (r->offset == offset && !strcmp(r->sect, sect)) {
+ fprintf(stderr, "Ugh. One address has two relocation records\n");
+ exit(1);
+ }
+ *rr = malloc(sizeof(btfixuprel));
+ if (!*rr) fatal();
+ (*rr)->offset = offset;
+ (*rr)->f = NULL;
+ if (buffer[30] == 'f') {
+ lastf = f;
+ lastfoffset = offset;
+ lastfrelno = k;
+ } else if (lastfoffset + 4 == offset) {
+ (*rr)->f = lastf;
+ (*rr)->frel = lastfrelno;
+ }
+ (*rr)->sect = sect;
+ (*rr)->next = NULL;
+ }
+ printf("! Generated by btfixupprep. Do not edit.\n\n");
+ printf("\t.section\t\".data.init\",#alloc,#write\n\t.align\t4\n\n");
+ printf("\t.global\t___btfixup_start\n___btfixup_start:\n\n");
+ for (i = 0; i < last; i++) {
+ f = array + i;
+ printf("\t.global\t___%cs_%s\n", f->type, f->name);
+ if (f->type == 'f')
+ printf("___%cs_%s:\n\t.word 0x%08x,0,0,", f->type, f->name, f->type << 24);
+ else
+ printf("___%cs_%s:\n\t.word 0x%08x,0,", f->type, f->name, f->type << 24);
+ for (j = 0, r = f->rel; r != NULL; j++, r = r->next);
+ if (j)
+ printf("%d\n\t.word\t", j * 2);
+ else
+ printf("0\n");
+ for (r = f->rel, j--; r != NULL; j--, r = r->next) {
+ if (!strcmp (r->sect, ".text"))
+ printf ("_stext+0x%08x", r->offset);
+ else if (!strcmp (r->sect, ".text.init"))
+ printf ("__init_begin+0x%08x", r->offset);
+ else if (!strcmp (r->sect, "__ksymtab"))
+ printf ("__start___ksymtab+0x%08x", r->offset);
+ else
+ fatal();
+ if (f->type == 'f' || !r->f)
+ printf (",0");
+ else
+ printf (",___fs_%s+0x%08x", r->f->name, (4 + r->frel*2)*4 + 4);
+ if (j) printf (",");
+ else printf ("\n");
+ }
+ printf("\n");
+ }
+ printf("\n\t.global\t___btfixup_end\n___btfixup_end:\n");
+ printf("\n\n! Define undefined references\n\n");
+ for (i = 0; i < last; i++) {
+ f = array + i;
+ if (f->type == 'f') {
+ printf("\t.global\t___f_%s\n", f->name);
+ printf("___f_%s:\n", f->name);
+ }
+ }
+ printf("\tretl\n\t nop\n\n");
+ for (i = 0; i < last; i++) {
+ f = array + i;
+ if (f->type != 'f') {
+ if (!f->initval) {
+ printf("\t.global\t___%c_%s\n", f->type, f->name);
+ printf("___%c_%s = 0\n", f->type, f->name);
+ } else {
+ printf("\t.global\t___%c_%s__btset_0x%s\n", f->type, f->name, f->initvalstr);
+ printf("___%c_%s__btset_0x%s = 0x%08x\n", f->type, f->name, f->initvalstr, f->initval);
+ }
+ }
+ }
+ printf("\n\n");
+ exit(0);
+}
diff --git a/arch/sparc/config.in b/arch/sparc/config.in
index 4a087fca2..3f0ab09b6 100644
--- a/arch/sparc/config.in
+++ b/arch/sparc/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.51 1998/01/08 04:16:54 baccala Exp $
+# $Id: config.in,v 1.54 1998/03/27 06:59:39 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -34,6 +34,8 @@ if [ "$CONFIG_AP1000" = "y" ]; then
define_bool CONFIG_APBIF y
tristate 'OPIU DDV Driver' CONFIG_DDV
else
+ bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4
+
# Global things across all Sun machines.
define_bool CONFIG_SBUS y
define_bool CONFIG_SBUSCHAR y
@@ -45,8 +47,13 @@ else
define_bool CONFIG_SUN_CONSOLE y
define_bool CONFIG_SUN_AUXIO y
define_bool CONFIG_SUN_IO y
- source drivers/sbus/char/Config.in
- source drivers/sbus/audio/Config.in
+ if [ "$CONFIG_SUN4" = "y" ]; then
+ bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS
+ bool 'bwtwo support' SUN_FB_BWTWO
+ else
+ source drivers/sbus/char/Config.in
+ source drivers/sbus/audio/Config.in
+ fi
fi
tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
@@ -159,17 +166,6 @@ if [ "$CONFIG_NET" = "y" ]; then
endmenu
fi
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_SR" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_SR" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
source fs/nls/Config.in
diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig
index 38b6096cc..1abeca2d6 100644
--- a/arch/sparc/defconfig
+++ b/arch/sparc/defconfig
@@ -20,11 +20,13 @@ CONFIG_KMOD=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_AP1000 is not set
+# CONFIG_SUN4 is not set
CONFIG_SBUS=y
CONFIG_SBUSCHAR=y
CONFIG_SUN_MOUSE=y
CONFIG_SERIAL=y
CONFIG_SUN_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
CONFIG_SUN_KEYBOARD=y
CONFIG_SUN_CONSOLE=y
CONFIG_SUN_AUXIO=y
@@ -80,6 +82,7 @@ CONFIG_MD_RAID5=m
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_NBD is not set
#
# Networking options
@@ -89,8 +92,8 @@ CONFIG_NETLINK=y
CONFIG_RTNETLINK=y
# CONFIG_NETLINK_DEV is not set
CONFIG_FIREWALL=y
-# CONFIG_NET_SECURITY is not set
CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
@@ -107,11 +110,18 @@ CONFIG_IP_MASQUERADE=y
#
# Protocol-specific masquerading support will be built as modules.
#
+# CONFIG_IP_MASQUERADE_ICMP is not set
+
+#
+# Protocol-specific masquerading support will be built as modules.
+#
+# CONFIG_IP_MASQUERADE_IPAUTOFW is not set
+# CONFIG_IP_MASQUERADE_IPPORTFW is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_MROUTE is not set
-CONFIG_IP_ALIAS=m
+CONFIG_IP_ALIAS=y
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
@@ -123,31 +133,39 @@ CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
CONFIG_IPV6=m
# CONFIG_IPV6_EUI64 is not set
-# CONFIG_IPV6_NO_PB is not set
#
#
#
CONFIG_IPX=m
+
+#
+# IPX options
+#
# CONFIG_IPX_INTERN is not set
-# CONFIG_IPX_PPROP_ROUTING is not set
CONFIG_ATALK=m
-# CONFIG_AX25 is not set
CONFIG_X25=m
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
# CONFIG_LLC is not set
# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=m
CONFIG_NET_SCH_CSZ=m
-CONFIG_NET_SCH_HFQ=m
CONFIG_NET_SCH_RED=m
CONFIG_NET_SCH_SFQ=m
CONFIG_NET_SCH_TBF=y
CONFIG_NET_SCH_PFIFO=y
CONFIG_NET_SCH_PRIO=y
+# CONFIG_NET_PROFILE is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
#
# SCSI support
@@ -176,6 +194,21 @@ CONFIG_SCSI_SUNESP=y
CONFIG_SCSI_QLOGICPTI=m
#
+# Fibre Channel support
+#
+CONFIG_FC4=m
+
+#
+# FC4 drivers
+#
+CONFIG_FC4_SOC=m
+
+#
+# FC4 targets
+#
+CONFIG_SCSI_PLUTO=m
+
+#
# Network device support
#
CONFIG_NETDEVICES=y
@@ -193,6 +226,7 @@ CONFIG_SUNLANCE=y
CONFIG_HAPPYMEAL=m
CONFIG_SUNQE=m
CONFIG_MYRI_SBUS=m
+CONFIG_CDROM=y
#
# Filesystems
@@ -211,24 +245,35 @@ CONFIG_NFS_FS=y
CONFIG_NFSD=m
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
+# CONFIG_CODA_FS is not set
CONFIG_SMB_FS=m
CONFIG_SMB_WIN95=y
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_MOUNT_SUBDIR is not set
CONFIG_HPFS_FS=m
+# CONFIG_NTFS_FS is not set
CONFIG_SYSV_FS=m
CONFIG_AFFS_FS=m
+# CONFIG_HFS_FS is not set
CONFIG_ROMFS_FS=m
CONFIG_AUTOFS_FS=m
CONFIG_AMIGA_PARTITION=y
CONFIG_UFS_FS=y
CONFIG_BSD_DISKLABEL=y
CONFIG_SMD_DISKLABEL=y
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_ADFS_FS is not set
# CONFIG_MAC_PARTITION is not set
+CONFIG_NLS=y
#
# Native Language Support
#
-CONFIG_NLS=y
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 7be8ddd3a..53ed6d340 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.41 1997/11/19 15:11:59 jj Exp $
+# $Id: Makefile,v 1.43 1998/03/09 14:03:34 jj Exp $
# Makefile for the linux kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
@@ -15,8 +15,6 @@ ifdef SMP
.S.o:
$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c $< -o $*.o
-CHECKASM_CC = $(CC) -D__SMP__
-
else
.S.s:
@@ -25,7 +23,6 @@ else
.S.o:
$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
-CHECKASM_CC = $(CC)
endif
all: kernel.o head.o init_task.o
@@ -42,7 +39,7 @@ O_OBJS := entry.o wof.o wuf.o etrap.o rtrap.o traps.o ${IRQ_OBJS} \
OX_OBJS := sparc_ksyms.o
ifdef SMP
-O_OBJS += trampoline.o smp.o
+O_OBJS += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
endif
ifdef CONFIG_SUN_AUXIO
@@ -62,18 +59,61 @@ head.o: head.S
endif
check_asm: dummy
+ @echo "/* Automatically generated. Do not edit. */" > asm_offsets.h
+ @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h
+ @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#ifndef __SMP__" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#include <linux/sched.h>" > tmp.c
+ $(CC) -E tmp.c -o tmp.i
+ @echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/sched.h>" >> check_asm.c
+ @echo 'struct task_struct _task;' >> check_asm.c
+ @echo 'struct mm_struct _mm;' >> check_asm.c
+ @echo 'struct thread_struct _thread;' >> check_asm.c
+ @echo 'int main(void) {' >> check_asm.c
+ $(SH) ./check_asm.sh task tmp.i check_asm.c
+ $(SH) ./check_asm.sh mm tmp.i check_asm.c
+ $(SH) ./check_asm.sh thread tmp.i check_asm.c
+ @echo 'return 0; }' >> check_asm.c
+ @rm -f tmp.[ci]
+ $(CC) -o check_asm check_asm.c
+ ./check_asm >> asm_offsets.h
+ @rm -f check_asm check_asm.c
+ @echo "" >> asm_offsets.h
+ @echo "#else /* __SMP__ */" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
@echo "#include <linux/sched.h>" > tmp.c
- $(CHECKASM_CC) -E tmp.c -o tmp.i
- @echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c
+ $(CC) -D__SMP__ -E tmp.c -o tmp.i
+ @echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/sched.h>" >> check_asm.c
+ @echo 'struct task_struct _task;' >> check_asm.c
+ @echo 'struct mm_struct _mm;' >> check_asm.c
+ @echo 'struct thread_struct _thread;' >> check_asm.c
+ @echo 'int main(void) {' >> check_asm.c
$(SH) ./check_asm.sh task tmp.i check_asm.c
$(SH) ./check_asm.sh mm tmp.i check_asm.c
$(SH) ./check_asm.sh thread tmp.i check_asm.c
- @echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c
+ @echo 'return 0; }' >> check_asm.c
@rm -f tmp.[ci]
- $(CHECKASM_CC) -o check_asm check_asm.c
- ./check_asm > asm_offsets.h
- @if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi
+ $(CC) -D__SMP__ -o check_asm check_asm.c
+ ./check_asm >> asm_offsets.h
@rm -f check_asm check_asm.c
+ @echo "" >> asm_offsets.h
+ @echo "#endif /* __SMP__ */" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h
+ @if test -r $(HPATH)/asm/asm_offsets.h; then \
+ if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \
+ echo $(HPATH)/asm/asm_offsets.h is unchanged; \
+ rm -f asm_offsets.h; \
+ else \
+ mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+ fi; \
+ else \
+ mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+ fi
include $(TOPDIR)/Rules.make
diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio.c
index 5835347d1..13d34310f 100644
--- a/arch/sparc/kernel/auxio.c
+++ b/arch/sparc/kernel/auxio.c
@@ -17,9 +17,13 @@ __initfunc(void auxio_probe(void))
int node, auxio_nd;
struct linux_prom_registers auxregs[1];
- if (sparc_cpu_model == sun4d) {
+ switch (sparc_cpu_model) {
+ case sun4d:
+ case sun4:
auxio_register = 0;
return;
+ default:
+ break;
}
node = prom_getchild(prom_root_node);
auxio_nd = prom_searchsiblings(node, "auxiliary-io");
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
index aeb5a46c8..1ca98407a 100644
--- a/arch/sparc/kernel/cpu.c
+++ b/arch/sparc/kernel/cpu.c
@@ -6,6 +6,8 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/tasks.h>
#include <asm/oplib.h>
#include <asm/page.h>
#include <asm/head.h>
@@ -116,22 +118,25 @@ struct cpu_iu_info linux_sparc_chips[] = {
#define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
-char *sparc_cpu_type[NCPUS] = { "cpu-oops", "cpu-oops1", "cpu-oops2", "cpu-oops3" };
-char *sparc_fpu_type[NCPUS] = { "fpu-oops", "fpu-oops1", "fpu-oops2", "fpu-oops3" };
+char *sparc_cpu_type[NR_CPUS] = { 0 };
+char *sparc_fpu_type[NR_CPUS] = { 0 };
unsigned int fsr_storage;
__initfunc(void cpu_probe(void))
{
int psr_impl, psr_vers, fpu_vers;
- int i, cpuid;
+ int i, cpuid, psr;
- cpuid = get_cpuid();
+ cpuid = hard_smp_processor_id();
psr_impl = ((get_psr()>>28)&0xf);
psr_vers = ((get_psr()>>24)&0xf);
+ psr = get_psr();
+ put_psr(psr | PSR_EF);
fpu_vers = ((get_fsr()>>17)&0x7);
+ put_psr(psr);
for(i = 0; i<NSPARCCHIPS; i++) {
if(linux_sparc_chips[i].psr_impl == psr_impl)
diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c
index b9c6495cf..dd4dbb3c6 100644
--- a/arch/sparc/kernel/devices.c
+++ b/arch/sparc/kernel/devices.c
@@ -14,7 +14,7 @@
#include <asm/smp.h>
#include <asm/system.h>
-struct prom_cpuinfo linux_cpus[NCPUS];
+struct prom_cpuinfo linux_cpus[NR_CPUS];
int linux_num_cpus;
extern void cpu_probe(void);
@@ -26,7 +26,7 @@ device_scan(unsigned long mem_start))
{
char node_str[128];
int nd, prom_node_cpu, thismid;
- int cpu_nds[NCPUS]; /* One node for each cpu */
+ int cpu_nds[NR_CPUS]; /* One node for each cpu */
int cpu_ctr = 0;
prom_getstring(prom_root_node, "device_type", node_str, sizeof(node_str));
@@ -62,11 +62,9 @@ device_scan(unsigned long mem_start))
prom_getstring(node, "device_type", node_str, sizeof(node_str));
if (strcmp(node_str, "cpu") == 0) {
prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid));
- if (cpu_ctr < NCPUS) {
- cpu_nds[cpu_ctr] = node;
- linux_cpus[cpu_ctr].prom_node = node;
- linux_cpus[cpu_ctr].mid = thismid;
- }
+ cpu_nds[cpu_ctr] = node;
+ linux_cpus[cpu_ctr].prom_node = node;
+ linux_cpus[cpu_ctr].mid = thismid;
prom_printf("Found CPU %d <node=%08lx,mid=%d>\n",
cpu_ctr, (unsigned long) node,
thismid);
@@ -74,8 +72,6 @@ device_scan(unsigned long mem_start))
}
}
}
- if (cpu_ctr > NCPUS)
- cpu_ctr = NCPUS;
}
if(cpu_ctr == 0) {
printk("No CPU nodes found, cannot continue.\n");
@@ -99,7 +95,7 @@ device_scan(unsigned long mem_start))
#endif
clock_stop_probe();
- if (sparc_cpu_model == sun4c)
+ if (ARCH_SUN4C_SUN4)
sun4c_probe_memerr_reg();
return mem_start;
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index d82c098d5..d393a9543 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -1,10 +1,11 @@
-/* $Id: entry.S,v 1.142 1998/01/07 06:33:47 baccala Exp $
+/* $Id: entry.S,v 1.149 1998/03/19 15:36:30 jj Exp $
* arch/sparc/kernel/entry.S: Sparc trap low-level entry points.
*
* Copyright (C) 1995 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 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
*/
#include <linux/config.h>
@@ -21,9 +22,15 @@
#include <asm/vaddrs.h>
#include <asm/memreg.h>
#include <asm/page.h>
+#ifdef CONFIG_SUN4
+#include <asm/pgtsun4.h>
+#else
#include <asm/pgtsun4c.h>
+#endif
#include <asm/winmacro.h>
#include <asm/signal.h>
+#include <asm/obio.h>
+#include <asm/mxcc.h>
#include <asm/asmmacro.h>
@@ -288,10 +295,14 @@ real_irq_entry:
SAVE_ALL
#ifdef __SMP__
+ .globl patchme_maybe_smp_msg
+
cmp %l7, 12
- bgu maybe_smp_msg
+patchme_maybe_smp_msg:
+ bgu maybe_smp4m_msg
nop
#endif
+
real_irq_continue:
or %l0, PSR_PIL, %g2
wr %g2, 0x0, %psr
@@ -309,14 +320,14 @@ patch_handler_irq:
#ifdef __SMP__
/* SMP per-cpu ticker interrupts are handled specially. */
-smp_ticker:
- bne real_irq_continue
+smp4m_ticker:
+ bne real_irq_continue+4
or %l0, PSR_PIL, %g2
wr %g2, 0x0, %psr
WRITE_PAUSE
wr %g2, PSR_ET, %psr
WRITE_PAUSE
- call C_LABEL(smp_percpu_timer_interrupt)
+ call C_LABEL(smp4m_percpu_timer_interrupt)
add %sp, REGWIN_SZ, %o0
wr %l0, PSR_ET, %psr
WRITE_PAUSE
@@ -326,7 +337,7 @@ smp_ticker:
* on some level other than 15 which is the NMI and only used
* for cross calls. That has a seperate entry point below.
*/
-maybe_smp_msg:
+maybe_smp4m_msg:
GET_PROCESSOR_MID(o3, o2)
set C_LABEL(sun4m_interrupts), %l5
ld [%l5], %o5
@@ -334,7 +345,7 @@ maybe_smp_msg:
sll %o3, 12, %o3
ld [%o5 + %o3], %o1
andcc %o1, %o4, %g0
- be,a smp_ticker
+ be,a smp4m_ticker
cmp %l7, 14
cmp %l7, 13
add %o5, %o3, %o5
@@ -383,7 +394,7 @@ linux_trap_ipi15_sun4m:
WRITE_PAUSE
wr %l4, PSR_ET, %psr
WRITE_PAUSE
- call C_LABEL(smp_cross_call_irq)
+ call C_LABEL(smp4m_cross_call_irq)
nop
b ret_trap_lockless_ipi
clr %l6
@@ -409,6 +420,64 @@ linux_trap_ipi15_sun4m:
ld [%l5], %g0
WRITE_PAUSE
RESTORE_ALL
+
+ .globl smp4d_ticker
+ /* SMP per-cpu ticker interrupts are handled specially. */
+smp4d_ticker:
+ SAVE_ALL
+ or %l0, PSR_PIL, %g2
+ sethi %hi(CC_ICLR), %o0
+ sethi %hi(1 << 14), %o1
+ or %o0, %lo(CC_ICLR), %o0
+ stha %o1, [%o0] ASI_M_MXCC /* Clear PIL 14 in MXCC's ICLR */
+ wr %g2, 0x0, %psr
+ WRITE_PAUSE
+ wr %g2, PSR_ET, %psr
+ WRITE_PAUSE
+ call C_LABEL(smp4d_percpu_timer_interrupt)
+ add %sp, REGWIN_SZ, %o0
+ wr %l0, PSR_ET, %psr
+ WRITE_PAUSE
+ RESTORE_ALL
+
+ .align 4
+ .globl linux_trap_ipi15_sun4d
+linux_trap_ipi15_sun4d:
+ SAVE_ALL
+ sethi %hi(CC_BASE), %o4
+ sethi %hi(MXCC_ERR_ME|MXCC_ERR_PEW|MXCC_ERR_ASE|MXCC_ERR_PEE), %o2
+ or %o4, (CC_EREG - CC_BASE), %o0
+ ldda [%o0] ASI_M_MXCC, %o0
+ andcc %o0, %o2, %g0
+ bne 1f
+ sethi %hi(BB_STAT2), %o2
+ lduba [%o2] ASI_M_CTL, %o2
+ andcc %o2, BB_STAT2_MASK, %g0
+ bne 2f
+ or %o4, (CC_ICLR - CC_BASE), %o0
+ sethi %hi(1 << 15), %o1
+ stha %o1, [%o0] ASI_M_MXCC /* Clear PIL 15 in MXCC's ICLR */
+ or %l0, PSR_PIL, %l4
+ wr %l4, 0x0, %psr
+ WRITE_PAUSE
+ wr %l4, PSR_ET, %psr
+ WRITE_PAUSE
+ call C_LABEL(smp4d_cross_call_irq)
+ nop
+ b ret_trap_lockless_ipi
+ clr %l6
+
+1: /* MXCC error */
+2: /* BB error */
+ /* Disable PIL 15 */
+ set CC_IMSK, %l4
+ lduha [%l4] ASI_M_MXCC, %l5
+ sethi %hi(1 << 15), %l7
+ or %l5, %l7, %l5
+ stha %l5, [%l4] ASI_M_MXCC
+ /* FIXME */
+1: b,a 1b
+
#endif /* __SMP__ */
/* This routine handles illegal instructions and privileged
@@ -417,6 +486,12 @@ linux_trap_ipi15_sun4m:
.align 4
.globl bad_instruction
bad_instruction:
+ sethi %hi(0xc1f80000), %l4
+ ld [%l1], %l5
+ sethi %hi(0x81d80000), %l7
+ and %l5, %l4, %l5
+ cmp %l5, %l7
+ be 1f
SAVE_ALL
wr %l0, PSR_ET, %psr ! re-enable traps
@@ -430,6 +505,10 @@ bad_instruction:
RESTORE_ALL
+1: /* unimplemented flush - just skip */
+ jmpl %l2, %g0
+ rett %l2 + 4
+
.align 4
.globl priv_instruction
priv_instruction:
@@ -601,23 +680,6 @@ do_cp_disabled:
RESTORE_ALL
- /* This routine handles Unimplemented FLUSH Exceptions. */
- .align 4
- .globl do_bad_flush
-do_bad_flush:
- SAVE_ALL
-
- wr %l0, PSR_ET, %psr ! re-enable traps
- WRITE_PAUSE
-
- add %sp, REGWIN_SZ, %o0
- mov %l1, %o1
- mov %l2, %o2
- call C_LABEL(handle_bad_flush)
- mov %l0, %o3
-
- RESTORE_ALL
-
/* This routine handles Co-Processor Exceptions. */
.align 4
.globl do_cp_exception
@@ -766,6 +828,12 @@ C_LABEL(invalid_segment_patch1_ff): cmp %l4, 0xff
C_LABEL(invalid_segment_patch2_ff): mov 0xff, %l4
.align 4
+ .globl C_LABEL(invalid_segment_patch1_1ff)
+ .globl C_LABEL(invalid_segment_patch2_1ff)
+C_LABEL(invalid_segment_patch1_1ff): cmp %l4, 0x1ff
+C_LABEL(invalid_segment_patch2_1ff): mov 0x1ff, %l4
+
+ .align 4
.globl C_LABEL(num_context_patch1_16), C_LABEL(num_context_patch2_16)
C_LABEL(num_context_patch1_16): mov 0x10, %l7
C_LABEL(num_context_patch2_16): mov 0x10, %l7
@@ -776,7 +844,17 @@ C_LABEL(vac_linesize_patch_32): subcc %l7, 32, %l7
.align 4
.globl C_LABEL(vac_hwflush_patch1_on), C_LABEL(vac_hwflush_patch2_on)
+
+/*
+ * Ugly, but we cant use hardware flushing on the sun4 and we'd require
+ * two instructions (Anton)
+ */
+#ifdef CONFIG_SUN4
+C_LABEL(vac_hwflush_patch1_on): nop
+#else
C_LABEL(vac_hwflush_patch1_on): subcc %l7, (PAGE_SIZE - 4), %l7
+#endif
+
C_LABEL(vac_hwflush_patch2_on): sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG
.globl C_LABEL(invalid_segment_patch1), C_LABEL(invalid_segment_patch2)
@@ -786,11 +864,50 @@ C_LABEL(vac_hwflush_patch2_on): sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG
.align 4
.globl sun4c_fault
+
+! %l0 = %psr
+! %l1 = %pc
+! %l2 = %npc
+! %l3 = %wim
+! %l7 = 1 for textfault
+! We want error in %l5, vaddr in %l6
sun4c_fault:
+#ifdef CONFIG_SUN4
+ sethi C_LABEL(sun4c_memerr_reg), %l4
+ ld [%l4+%lo(C_LABEL(sun4c_memerr_reg))], %l4 ! memerr ctrl reg addr
+ ld [%l4], %l6 ! memerr ctrl reg
+ ld [%l4 + 4], %l5 ! memerr vaddr reg
+ andcc %l6, 0x80, %g0 ! check for error type
+ st %g0, [%l4 + 4] ! clear the error
+ be 0f ! normal error
+ sethi %hi(AC_BUS_ERROR), %l4 ! bus err reg addr
+
+ call C_LABEL(prom_halt) ! something weird happened
+ ! what exactly did happen?
+ ! what should we do here?
+
+0: or %l4, %lo(AC_BUS_ERROR), %l4 ! bus err reg addr
+ lduba [%l4] ASI_CONTROL, %l6 ! bus err reg
+
+ cmp %l7, 1 ! text fault?
+ be 1f ! yes
+ nop
+
+ ld [%l1], %l4 ! load instruction that caused fault
+ srl %l4, 21, %l4
+ andcc %l4, 1, %g0 ! store instruction?
+
+ be 1f ! no
+ sethi %hi(SUN4C_SYNC_BADWRITE), %l4 ! yep
+ ! %lo(SUN4C_SYNC_BADWRITE) = 0
+ or %l4, %l6, %l6 ! set write bit to emulate sun4c
+1:
+#else
sethi %hi(AC_SYNC_ERR), %l4
add %l4, 0x4, %l6 ! AC_SYNC_VA in %l6
lda [%l6] ASI_CONTROL, %l5 ! Address
lda [%l4] ASI_CONTROL, %l6 ! Error, retained for a bit
+#endif
andn %l5, 0xfff, %l5 ! Encode all info into l7
srl %l6, 14, %l4
@@ -830,17 +947,21 @@ sun4c_fault:
sethi %hi(SUN4C_VMALLOC_START), %l4
cmp %l5, %l4
blu,a C_LABEL(invalid_segment_patch1)
- lduba [%l5] ASI_SEGMAP, %l4
+ lduXa [%l5] ASI_SEGMAP, %l4
- srl %l5, SUN4C_PGDIR_SHIFT, %l6
sethi %hi(C_LABEL(swapper_pg_dir)), %l4
+ srl %l5, SUN4C_PGDIR_SHIFT, %l6
or %l4, %lo(C_LABEL(swapper_pg_dir)), %l4
sll %l6, 2, %l6
ld [%l4 + %l6], %l4
+#ifdef CONFIG_SUN4
+ sethi PAGE_MASK, %l6
+ andcc %l4, %l6, %g0
+#else
andcc %l4, PAGE_MASK, %g0
-
+#endif
be sun4c_fault_fromuser
- lduba [%l5] ASI_SEGMAP, %l4
+ lduXa [%l5] ASI_SEGMAP, %l4
C_LABEL(invalid_segment_patch1):
cmp %l4, 0x7f
@@ -889,7 +1010,11 @@ C_LABEL(invalid_segment_patch1):
ld [%l6 + 0x08], %l3 ! tmp = entry->vaddr
! Flush segment from the cache.
+#ifdef CONFIG_SUN4
+ sethi %hi((128 * 1024)), %l7
+#else
sethi %hi((64 * 1024)), %l7
+#endif
9:
C_LABEL(vac_hwflush_patch1):
C_LABEL(vac_linesize_patch):
@@ -928,7 +1053,7 @@ C_LABEL(invalid_segment_patch2):
deccc %l7
stba %l7, [%l3] ASI_CONTROL
bne 3b
- stba %l4, [%l5] ASI_SEGMAP
+ stXa %l4, [%l5] ASI_SEGMAP
stba %l6, [%l3] ASI_CONTROL
@@ -952,7 +1077,7 @@ C_LABEL(num_context_patch2):
deccc %l7
stba %l7, [%l3] ASI_CONTROL
bne 3b
- stba %l4, [%l5] ASI_SEGMAP
+ stXa %l4, [%l5] ASI_SEGMAP
stba %l6, [%l3] ASI_CONTROL
@@ -988,7 +1113,12 @@ C_LABEL(num_context_patch2):
or %l4, %lo(C_LABEL(swapper_pg_dir)), %l4
sll %l3, 2, %l3
ld [%l4 + %l3], %l4
+#ifndef CONFIG_SUN4
and %l4, PAGE_MASK, %l4
+#else
+ sethi PAGE_MASK, %l6
+ and %l4, %l6, %l4
+#endif
srl %l5, (PAGE_SHIFT - 2), %l6
and %l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6
@@ -1640,9 +1770,8 @@ C_LABEL(udelay):
call .umul
ld [%o3 + %lo(C_LABEL(loops_per_sec))], %o1
#else
- GET_PROCESSOR_OFFSET(o4)
+ GET_PROCESSOR_OFFSET(o4, o2)
set C_LABEL(cpu_data), %o3
- sll %o4, 1, %o4
call .umul
ld [%o3 + %o4], %o1
#endif
@@ -1718,4 +1847,11 @@ kuw_patch1:
retl ! return
st %g0, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] ! no windows saved
+ .align 4
+ .globl C_LABEL(restore_current)
+C_LABEL(restore_current):
+ LOAD_CURRENT(g6, o0)
+ retl
+ nop
+
/* End of entry.S */
diff --git a/arch/sparc/kernel/etrap.S b/arch/sparc/kernel/etrap.S
index fc6c4cead..5496b061e 100644
--- a/arch/sparc/kernel/etrap.S
+++ b/arch/sparc/kernel/etrap.S
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.26 1997/05/01 08:53:32 davem Exp $
+/* $Id: etrap.S,v 1.29 1998/02/09 13:48:40 jj Exp $
* etrap.S: Sparc trap window preparation for entry into the
* Linux kernel.
*
@@ -13,6 +13,7 @@
#include <asm/psr.h>
#include <asm/ptrace.h>
#include <asm/winmacro.h>
+#include <asm/asmmacro.h>
/* Registers to not touch at all. */
#define t_psr l0 /* Set by caller */
@@ -126,13 +127,13 @@ tsetup_patch2:
jmpl %t_retpc + 0x8, %g0 ! return to caller
mov %t_kstack, %sp ! and onto new kernel stack
+#define STACK_OFFSET (TASK_UNION_SIZE - (TRACEREG_SZ + REGWIN_SZ))
trap_setup_from_user:
/* We can't use %curptr yet. */
LOAD_CURRENT(t_kstack, t_twinmask)
- mov 1, %t_twinmask
- sll %t_twinmask, (PAGE_SHIFT + 1), %t_twinmask
- sub %t_twinmask, (TRACEREG_SZ + REGWIN_SZ), %t_twinmask
+ sethi %hi(STACK_OFFSET), %t_twinmask
+ or %t_twinmask, %lo(STACK_OFFSET), %t_twinmask
add %t_kstack, %t_twinmask, %t_kstack
mov 1, %t_twinmask
@@ -141,11 +142,18 @@ trap_setup_from_user:
/* Build pt_regs frame. */
STORE_PT_ALL(t_kstack, t_psr, t_pc, t_npc, g2)
- /* Clear current->tss.w_saved */
- mov 1, %curptr
- sll %curptr, (PAGE_SHIFT + 1), %curptr
- sub %curptr, (TRACEREG_SZ + REGWIN_SZ), %curptr
+#if 0
+ /* If we're sure every task_struct is TASK_UNION_SIZE aligned,
+ we can speed this up. */
+ sethi %hi(STACK_OFFSET), %curptr
+ or %curptr, %lo(STACK_OFFSET), %curptr
sub %t_kstack, %curptr, %curptr
+#else
+ sethi %hi(~(TASK_UNION_SIZE - 1)), %curptr
+ and %t_kstack, %curptr, %curptr
+#endif
+
+ /* Clear current->tss.w_saved */
st %g0, [%curptr + AOFF_task_tss + AOFF_thread_w_saved]
/* See if we are in the trap window. */
@@ -269,9 +277,8 @@ tsetup_sun4c_onepage:
.globl C_LABEL(tsetup_srmmu_stackchk)
C_LABEL(tsetup_srmmu_stackchk):
/* Check results of callers andcc %sp, 0x7, %g0 */
- sethi %hi(C_LABEL(page_offset)), %glob_tmp
bne trap_setup_user_stack_is_bolixed
- ld [%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp
+ GET_PAGE_OFFSET(glob_tmp)
cmp %glob_tmp, %sp
bleu,a 1f
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S
index 2de97e92b..1536a5e55 100644
--- a/arch/sparc/kernel/head.S
+++ b/arch/sparc/kernel/head.S
@@ -1,10 +1,11 @@
-/* $Id: head.S,v 1.84 1997/11/19 15:12:01 jj Exp $
+/* $Id: head.S,v 1.90 1998/03/24 18:12:05 jj Exp $
* head.S: The initial boot code for the Sparc port of Linux.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Peter Zaitcev (Zaitcev@ipmce.su)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
*/
#include <linux/version.h>
@@ -60,9 +61,16 @@ C_LABEL(cputypvar_sun4m):
.asciz "compatible"
.align 4
+
+#ifndef CONFIG_SUN4
sun4_notsup:
- .asciz "Sparc-Linux sun4 support not implemented yet\n\n"
+ .asciz "Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n"
+ .align 4
+#else
+sun4cdm_notsup:
+ .asciz "Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n"
.align 4
+#endif
sun4e_notsup:
.asciz "Sparc-Linux sun4e support does not exist\n\n"
@@ -111,13 +119,14 @@ t_irq14:TRAP_ENTRY_INTERRUPT(14) /* IRQ Timer #2 */
#ifndef __SMP__
t_nmi: NMI_TRAP /* Level 15 (NMI) */
#else
- TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+ .globl t_nmi
+t_nmi: TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
#endif
t_racc: TRAP_ENTRY(0x20, do_reg_access) /* General Register Access Error */
t_iacce:BAD_TRAP(0x21) /* Instr Access Error */
t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23)
t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled) /* Co-Processor Disabled */
-t_uflsh:TRAP_ENTRY(0x25, do_bad_flush) /* Unimplemented FLUSH inst. */
+t_uflsh:SKIP_TRAP(0x25, unimp_flush) /* Unimplemented FLUSH inst. */
t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27)
t_cpexc:TRAP_ENTRY(0x28, do_cp_exception) /* Co-Processor Exception */
t_dacce:SPARC_DFAULT /* Data Access Error */
@@ -205,7 +214,7 @@ C_LABEL(trapbase_cpu1):
TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
- BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush)
+ BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
@@ -273,7 +282,7 @@ C_LABEL(trapbase_cpu2):
TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
- BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush)
+ BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
@@ -341,7 +350,7 @@ C_LABEL(trapbase_cpu3):
TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
- BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) TRAP_ENTRY(0x25, do_bad_flush)
+ BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
@@ -394,28 +403,26 @@ C_LABEL(trapbase_cpu3):
BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
#endif
- .skip 4096
+ .align 4096
/* This was the only reasonable way I could think of to properly align
* these page-table data structures.
*/
.globl C_LABEL(bootup_user_stack)
- .globl C_LABEL(bootup_kernel_stack)
.globl C_LABEL(pg0), C_LABEL(pg1), C_LABEL(pg2), C_LABEL(pg3)
.globl C_LABEL(empty_bad_page)
.globl C_LABEL(empty_bad_page_table)
.globl C_LABEL(empty_zero_page)
.globl C_LABEL(swapper_pg_dir)
C_LABEL(bootup_user_stack): .skip 0x2000
-C_LABEL(bootup_kernel_stack): .skip 0x2000
-C_LABEL(swapper_pg_dir): .skip 0x1000
-C_LABEL(pg0): .skip 0x1000
-C_LABEL(pg1): .skip 0x1000
-C_LABEL(pg2): .skip 0x1000
-C_LABEL(pg3): .skip 0x1000
-C_LABEL(empty_bad_page): .skip 0x1000
-C_LABEL(empty_bad_page_table): .skip 0x1000
-C_LABEL(empty_zero_page): .skip 0x1000
+C_LABEL(swapper_pg_dir): .skip PAGE_SIZE
+C_LABEL(pg0): .skip PAGE_SIZE
+C_LABEL(pg1): .skip PAGE_SIZE
+C_LABEL(pg2): .skip PAGE_SIZE
+C_LABEL(pg3): .skip PAGE_SIZE
+C_LABEL(empty_bad_page): .skip PAGE_SIZE
+C_LABEL(empty_bad_page_table): .skip PAGE_SIZE
+C_LABEL(empty_zero_page): .skip PAGE_SIZE
.global C_LABEL(root_flags)
.global C_LABEL(ram_flags)
@@ -778,18 +785,24 @@ execute_in_high_mem:
* your code. Sun probably still does that because they don't even
* trust their own "OpenBoot" specifications.
*/
-
set LOAD_ADDR, %g6
cmp %o0, %g6 ! an old sun4?
- be no_sun4_here
+ be sun4_init
nop
found_version:
-
+#ifdef CONFIG_SUN4
+/* For people who try sun4 kernels, even if Configure.help advises them. */
+ ld [%g7 + 0x68], %o1
+ set sun4cdm_notsup, %o0
+ call %o1
+ nop
+ b halt_me
+ nop
+#endif
/* Get the machine type via the mysterious romvec node operations. */
- or %g0, %g7, %l1
- add %l1, 0x1c, %l1
+ add %g7, 0x1c, %l1
ld [%l1], %l0
ld [%l0], %l0
call %l0
@@ -825,10 +838,11 @@ got_prop:
set C_LABEL(cputypval), %o2
ldub [%o2 + 0x4], %l1
- cmp %l1, 'c' ! We already know we are not
- be 1f ! on a plain sun4 because of
- ! the check for 0x4000 in %o0
- cmp %l1, 'm' ! at start
+ cmp %l1, ' '
+ be 1f
+ cmp %l1, 'c'
+ be 1f
+ cmp %l1, 'm'
be 1f
cmp %l1, 'd'
be 1f
@@ -853,6 +867,9 @@ got_prop:
b sun4c_continue_boot
nop
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID 0xf0140000
+
sun4d_init:
/* Need to patch call to handler_irq */
set C_LABEL(patch_handler_irq), %g4
@@ -862,6 +879,21 @@ sun4d_init:
srl %g5, 2, %g5
or %g5, %g3, %g5
st %g5, [%g4]
+
+#ifdef __SMP__
+ /* Get our CPU id out of bootbus */
+ set SUN4D_BOOTBUS_CPUID, %g3
+ lduba [%g3] ASI_M_CTL, %g3
+ and %g3, 0xf8, %g3
+ srl %g3, 3, %g4
+ sta %g4, [%g0] ASI_M_VIKING_TMP1
+ sethi %hi(boot_cpu_id), %g5
+ stb %g4, [%g5 + %lo(boot_cpu_id)]
+ sll %g4, 2, %g4
+ sethi %hi(boot_cpu_id4), %g5
+ stb %g4, [%g5 + %lo(boot_cpu_id4)]
+#endif
+
/* Fall through to sun4m_init */
sun4m_init:
@@ -974,7 +1006,8 @@ sun4c_continue_boot:
/* I want a kernel stack NOW! */
set C_LABEL(bootup_user_stack), %g1
- add %g1, (PAGE_SIZE - REGWIN_SZ), %sp
+ set (0x2000 - REGWIN_SZ), %g2
+ add %g1, %g2, %sp
mov 0, %fp /* And for good luck */
/* Zero out our BSS section. */
@@ -988,10 +1021,16 @@ sun4c_continue_boot:
add %o0, 0x1, %o0
/* Initialize the umask value for init_task just in case.
- * But first make current_set[0] point to something useful.
+ * But first make current_set[boot_cpu_id] point to something useful.
*/
set C_LABEL(init_task_union), %g6
set C_LABEL(current_set), %g2
+#ifdef __SMP__
+ sethi %hi(C_LABEL(boot_cpu_id4)), %g3
+ ldub [%g3 + %lo(C_LABEL(boot_cpu_id4))], %g3
+ st %g6, [%g2]
+ add %g2, %g3, %g2
+#endif
st %g6, [%g2]
st %g0, [%g6 + AOFF_task_tss + AOFF_thread_uwinmask]
@@ -1114,19 +1153,28 @@ sun4c_continue_boot:
call halt_me
nop
+sun4_init:
+#ifdef CONFIG_SUN4
/* There, happy now Adrian? */
+ set C_LABEL(cputypval), %o2 ! Let everyone know we
+ set ' ', %o0 ! are a "sun4 " architecture
+ stb %o0, [%o2 + 0x4]
- /* XXX Fix this... XXX */
-no_sun4_here:
- sethi %hi(SUN4_PROM_VECTOR+SUN4_PRINTF), %o1
- ld [%o1 + %lo(SUN4_PROM_VECTOR+SUN4_PRINTF)], %o1
- set sun4_notsup, %o0
- call %o1
+ b got_prop
nop
-1:
- ba 1b ! Cannot exit into KMON
+#else
+ sethi %hi(SUN4_PROM_VECTOR+0x84), %o1
+ ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1
+ set sun4_notsup, %o0
+ call %o1 /* printf */
nop
-
+ sethi %hi(SUN4_PROM_VECTOR+0xc4), %o1
+ ld [%o1 + %lo(SUN4_PROM_VECTOR+0xc4)], %o1
+ call %o1 /* exittomon */
+ nop
+1: ba 1b ! Cannot exit into KMON
+ nop
+#endif
no_sun4e_here:
ld [%g7 + 0x68], %o1
set sun4e_notsup, %o0
diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c
index 1829daeea..506a98622 100644
--- a/arch/sparc/kernel/init_task.c
+++ b/arch/sparc/kernel/init_task.c
@@ -11,9 +11,9 @@ static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS;
struct mm_struct init_mm = INIT_MM;
-/* .text section in head.S is aligned at 2 page boundry and this gets linked
+/* .text section in head.S is aligned at 8k boundry and this gets linked
* right after that so that the init_task_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.
+ * If this is not aligned on a 8k boundry, then you should change code
+ * in etrap.S which assumes it.
*/
union task_union init_task_union __attribute__((__section__(".text"))) = { INIT_TASK };
diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c
index 08c0be5c6..b29eca496 100644
--- a/arch/sparc/kernel/irq.c
+++ b/arch/sparc/kernel/irq.c
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.77 1997/11/19 15:33:05 jj Exp $
+/* $Id: irq.c,v 1.85 1998/03/09 14:03:40 jj Exp $
* arch/sparc/kernel/irq.c: Interrupt request handling routines. On the
* Sparc the IRQ's are basically 'cast in stone'
* and you are supposed to probe the prom's device
@@ -67,22 +67,9 @@ static void irq_panic(void)
prom_halt();
}
-void (*enable_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*disable_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*enable_pil_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*disable_pil_irq)(unsigned int) = (void (*)(unsigned int)) irq_panic;
-void (*clear_clock_irq)(void) = irq_panic;
-void (*clear_profile_irq)(int) = (void (*)(int)) irq_panic;
-void (*load_profile_irq)(int, unsigned int) = (void (*)(int, unsigned int)) irq_panic;
void (*init_timers)(void (*)(int, void *,struct pt_regs *)) =
(void (*)(void (*)(int, void *,struct pt_regs *))) irq_panic;
-#ifdef __SMP__
-void (*set_cpu_int)(int, int);
-void (*clear_cpu_int)(int, int);
-void (*set_irq_udt)(int);
-#endif
-
/*
* Dave Redman (djhr@tadpole.co.uk)
*
@@ -109,6 +96,9 @@ int get_irq_list(char *buf)
{
int i, len = 0;
struct irqaction * action;
+#ifdef __SMP__
+ int j;
+#endif
if (sparc_cpu_model == sun4d) {
extern int sun4d_get_irq_list(char *);
@@ -119,8 +109,15 @@ int get_irq_list(char *buf)
action = *(i + irq_action);
if (!action)
continue;
- len += sprintf(buf+len, "%2d: %8d %c %s",
- i, kstat.interrupts[i],
+ len += sprintf(buf+len, "%3d: ", i);
+#ifndef __SMP__
+ len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#else
+ for (j = 0; j < smp_num_cpus; j++)
+ len += sprintf(buf+len, "%10u ",
+ kstat.irqs[cpu_logical_map(j)][i]);
+#endif
+ len += sprintf(buf+len, " %c %s",
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for (action=action->next; action; action = action->next) {
@@ -280,7 +277,7 @@ static inline void get_irqlock(int cpu, unsigned long where)
do {
STUCK;
barrier();
- } while (*((unsigned char *)&global_irq_lock));
+ } while (*((volatile unsigned char *)&global_irq_lock));
} while (!spin_trylock(&global_irq_lock));
}
/*
@@ -352,7 +349,7 @@ void irq_enter(int cpu, int irq, void *_opaque)
hardirq_enter(cpu);
barrier();
- while (*((unsigned char *)&global_irq_lock)) {
+ while (*((volatile unsigned char *)&global_irq_lock)) {
if ((unsigned char) cpu == global_irq_holder) {
struct pt_regs *regs = _opaque;
int sbh_cnt = atomic_read(&__sparc_bh_counter);
@@ -436,18 +433,20 @@ void handler_irq(int irq, struct pt_regs * regs)
struct irqaction * action;
int cpu = smp_processor_id();
#ifdef __SMP__
- extern void smp_irq_rotate(int cpu);
+ extern void smp4m_irq_rotate(int cpu);
#endif
-
+
disable_pil_irq(irq);
+#if 0 /* FIXME: rotating IRQs halts the machine during SCSI probe. -ecd */
#ifdef __SMP__
/* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */
if(irq < 10)
- smp_irq_rotate(cpu);
+ smp4m_irq_rotate(cpu);
+#endif
#endif
irq_enter(cpu, irq, regs);
action = *(irq + irq_action);
- kstat.interrupts[irq]++;
+ kstat.irqs[cpu][irq]++;
do {
if (!action || !action->handler)
unexpected_irq(irq, 0, regs);
@@ -467,6 +466,7 @@ void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
disable_pil_irq(irq);
irq_enter(cpu, irq, regs);
+ kstat.irqs[cpu][irq]++;
floppy_interrupt(irq, dev_id, regs);
irq_exit(cpu, irq);
enable_pil_irq(irq);
@@ -667,6 +667,7 @@ __initfunc(void init_IRQ(void))
switch(sparc_cpu_model) {
case sun4c:
+ case sun4:
sun4c_init_IRQ();
break;
@@ -688,4 +689,5 @@ __initfunc(void init_IRQ(void))
prom_printf("Cannot initialize IRQ's on this Sun machine...");
break;
}
+ btfixup();
}
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
index 32feff3de..8d4c29e62 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.102 1997/12/01 03:36:31 davem Exp $
+/* $Id: process.c,v 1.110 1998/04/08 16:15:51 jj Exp $
* linux/arch/sparc/kernel/process.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -40,6 +40,7 @@
#include <asm/elf.h>
extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
+extern void srmmu_check_pgt_cache(void);
struct task_struct *current_set[NR_CPUS] = {&init_task, };
@@ -62,7 +63,7 @@ asmlinkage int sys_idle(void)
current->priority = -100;
current->counter = -100;
for (;;) {
- if (sparc_cpu_model == sun4c) {
+ if (ARCH_SUN4C_SUN4) {
static int count = HZ;
static unsigned long last_jiffies = 0;
static unsigned long last_faults = 0;
@@ -91,7 +92,9 @@ asmlinkage int sys_idle(void)
}
}
restore_flags(flags);
- }
+ check_pgt_cache();
+ } else
+ srmmu_check_pgt_cache();
schedule();
}
ret = 0;
@@ -109,6 +112,7 @@ int cpu_idle(void *unused)
current->priority = -100;
while(1) {
+ srmmu_check_pgt_cache();
/*
* tq_scheduler currently assumes we're running in a process
* context (ie that we hold the kernel lock..)
@@ -187,12 +191,12 @@ void machine_power_off(void)
void show_regwindow(struct reg_window *rw)
{
- printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
+ printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
"l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
- printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
- "i4: %08lx i5: %08lx i6: %08lx i7: %08lx\n",
+ printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
+ "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
}
@@ -201,15 +205,13 @@ void show_regwindow(struct reg_window *rw)
static spinlock_t sparc_backtrace_lock = SPIN_LOCK_UNLOCKED;
#endif
-void show_backtrace(void)
+void __show_backtrace(unsigned long fp)
{
struct reg_window *rw;
unsigned long flags;
- unsigned long fp;
int cpu = smp_processor_id();
spin_lock_irqsave(&sparc_backtrace_lock, flags);
- __asm__ __volatile__("mov %%i6, %0" : "=r" (fp));
rw = (struct reg_window *) fp;
while(rw) {
printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] "
@@ -223,6 +225,31 @@ void show_backtrace(void)
spin_unlock_irqrestore(&sparc_backtrace_lock, flags);
}
+void show_backtrace(void)
+{
+ unsigned long fp;
+
+ __asm__ __volatile__(
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "save %%sp, -64, %%sp\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "restore\n\t"
+ "mov %%i6, %0" : "=r" (fp));
+ __show_backtrace(fp);
+}
+
#ifdef __SMP__
void smp_show_backtrace_all_cpus(void)
{
@@ -236,15 +263,15 @@ void show_stackframe(struct sparc_stackf *sf)
unsigned long *stk;
int i;
- printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
+ printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
"l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
- printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
- "i4: %08lx i5: %08lx fp: %08lx ret_pc: %08lx\n",
+ printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
+ "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
- printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx\n"
+ printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx "
"x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
(unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
@@ -265,36 +292,32 @@ void show_regs(struct pt_regs * regs)
#endif
printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx\n", regs->psr,
regs->pc, regs->npc, regs->y);
- printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx\n",
+ printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx ",
regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
regs->u_regs[3]);
printk("g4: %08lx g5: %08lx g6: %08lx g7: %08lx\n",
regs->u_regs[4], regs->u_regs[5], regs->u_regs[6],
regs->u_regs[7]);
- printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx\n",
+ printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx ",
regs->u_regs[8], regs->u_regs[9], regs->u_regs[10],
regs->u_regs[11]);
- printk("o4: %08lx o5: %08lx sp: %08lx ret_pc: %08lx\n",
+ printk("o4: %08lx o5: %08lx sp: %08lx o7: %08lx\n",
regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
regs->u_regs[15]);
show_regwindow((struct reg_window *)regs->u_regs[14]);
}
+#if NOTUSED
void show_thread(struct thread_struct *tss)
{
int i;
- printk("uwinmask: 0x%08lx\n", tss->uwinmask);
- printk("kregs: 0x%08lx\n", (unsigned long)tss->kregs);
+ printk("uwinmask: 0x%08lx kregs: 0x%08lx\n", tss->uwinmask, (unsigned long)tss->kregs);
show_regs(tss->kregs);
- printk("sig_address: 0x%08lx\n", tss->sig_address);
- printk("sig_desc: 0x%08lx\n", tss->sig_desc);
- printk("ksp: 0x%08lx\n", tss->ksp);
- printk("kpc: 0x%08lx\n", tss->kpc);
- printk("kpsr: 0x%08lx\n", tss->kpsr);
- printk("kwim: 0x%08lx\n", tss->kwim);
- printk("fork_kpsr: 0x%08lx\n", tss->fork_kpsr);
- printk("fork_kwim: 0x%08lx\n", tss->fork_kwim);
+ printk("sig_address: 0x%08lx sig_desc: 0x%08lx\n", tss->sig_address, tss->sig_desc);
+ printk("ksp: 0x%08lx kpc: 0x%08lx\n", tss->ksp, tss->kpc);
+ printk("kpsr: 0x%08lx kwim: 0x%08lx\n", tss->kpsr, tss->kwim);
+ printk("fork_kpsr: 0x%08lx fork_kwim: 0x%08lx\n", tss->fork_kpsr, tss->fork_kwim);
for (i = 0; i < NSWINS; i++) {
if (!tss->rwbuf_stkptrs[i])
@@ -306,19 +329,19 @@ void show_thread(struct thread_struct *tss)
printk("w_saved: 0x%08lx\n", tss->w_saved);
/* XXX missing: float_regs */
- printk("fsr: 0x%08lx\n", tss->fsr);
- printk("fpqdepth: 0x%08lx\n", tss->fpqdepth);
+ printk("fsr: 0x%08lx fpqdepth: 0x%08lx\n", tss->fsr, tss->fpqdepth);
/* XXX missing: fpqueue */
- printk("sstk_info.stack: 0x%08lx\n",
- (unsigned long)tss->sstk_info.the_stack);
- printk("sstk_info.status: 0x%08lx\n",
- (unsigned long)tss->sstk_info.cur_status);
- printk("flags: 0x%08lx\n", tss->flags);
- printk("current_ds: 0x%08x\n", tss->current_ds);
+ printk("sstk_info.stack: 0x%08lx sstk_info.status: 0x%08lx\n",
+ (unsigned long)tss->sstk_info.the_stack,
+ (unsigned long)tss->sstk_info.cur_status);
+ printk("flags: 0x%08lx current_ds: 0x%08lx\n", tss->flags, tss->current_ds.seg);
+
+ show_regwindow((struct reg_window *)tss->ksp);
/* XXX missing: core_exec */
}
+#endif
/*
* Free current thread data structures etc..
@@ -367,8 +390,11 @@ void flush_thread(void)
}
/* Now, this task is no longer a kernel thread. */
- current->tss.flags &= ~SPARC_FLAG_KTHREAD;
current->tss.current_ds = USER_DS;
+ if (current->tss.flags & SPARC_FLAG_KTHREAD) {
+ current->tss.flags &= ~SPARC_FLAG_KTHREAD;
+ switch_to_context(current);
+ }
}
static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src)
@@ -475,7 +501,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
}
/* Calculate offset to stack_frame & pt_regs */
- stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ);
+ stack_offset = TASK_UNION_SIZE - TRACEREG_SZ;
if(regs->psr & PSR_PS)
stack_offset -= REGWIN_SZ;
diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S
index 68f3dc9af..3c0d311ba 100644
--- a/arch/sparc/kernel/rtrap.S
+++ b/arch/sparc/kernel/rtrap.S
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.49 1997/12/14 23:24:24 ecd Exp $
+/* $Id: rtrap.S,v 1.50 1998/02/05 14:18:43 jj Exp $
* rtrap.S: Return from Sparc trap low-level code.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -296,9 +296,8 @@ sun4c_rett_onepage:
.globl C_LABEL(srmmu_rett_stackchk)
C_LABEL(srmmu_rett_stackchk):
- sethi %hi(C_LABEL(page_offset)), %g1
bne ret_trap_user_stack_is_bolixed
- ld [%g1 + %lo(C_LABEL(page_offset))], %g1
+ GET_PAGE_OFFSET(g1)
cmp %g1, %fp
bleu ret_trap_user_stack_is_bolixed
mov AC_M_SFSR, %g1
diff --git a/arch/sparc/kernel/sclow.S b/arch/sparc/kernel/sclow.S
index 06c028395..4da88bd0e 100644
--- a/arch/sparc/kernel/sclow.S
+++ b/arch/sparc/kernel/sclow.S
@@ -11,6 +11,7 @@
#include <asm/errno.h>
#include <asm/winmacro.h>
#include <asm/psr.h>
+#include <asm/page.h>
#define CC_AND_RETT \
set PSR_C, %l4; \
@@ -94,7 +95,7 @@ LABEL(sunossmask):
.globl LABEL(getpagesize)
LABEL(getpagesize):
- set 4096, %i0
+ set PAGE_SIZE, %i0
CC_AND_RETT
/* XXX sys_nice() XXX */
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
index e53c343da..b5625cd12 100644
--- a/arch/sparc/kernel/setup.c
+++ b/arch/sparc/kernel/setup.c
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.87 1997/12/18 02:42:42 ecd Exp $
+/* $Id: setup.c,v 1.93 1998/03/09 14:03:18 jj Exp $
* linux/arch/sparc/kernel/setup.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -43,6 +43,7 @@
#include <asm/spinlock.h>
#include <asm/softirq.h>
#include <asm/hardirq.h>
+#include <asm/machines.h>
struct screen_info screen_info = {
0, 0, /* orig-x, orig-y */
@@ -58,11 +59,6 @@ struct screen_info screen_info = {
unsigned int phys_bytes_of_ram, end_of_phys_memory;
-unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
-{
- return memory_start;
-}
-
/* Typing sync at the prom prompt calls the function pointed to by
* romvec->pv_synchook which I set to the following function.
* This should sync all filesystems and return, for now it just
@@ -127,7 +123,7 @@ unsigned int boot_flags;
extern char *console_fb_path;
static int console_fb = 0;
#endif
-static unsigned long memory_size = 0;
+static unsigned long memory_size __initdata = 0;
void kernel_enter_debugger(void)
{
@@ -260,7 +256,7 @@ extern void sun4c_probe_vac(void);
extern char cputypval;
extern unsigned long start, end;
extern void panic_setup(char *, int *);
-extern unsigned long srmmu_endmem_fixup(unsigned long);
+extern void srmmu_end_memory(unsigned long, unsigned long *);
extern unsigned long sun_serial_setup(unsigned long);
extern unsigned short root_flags;
@@ -311,6 +307,13 @@ __initfunc(void setup_arch(char **cmdline_p,
if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
+
+#ifdef CONFIG_SUN4
+ if (sparc_cpu_model != sun4) {
+ prom_printf("This kernel is for Sun4 architecture only.\n");
+ prom_halt();
+ }
+#endif
#if CONFIG_AP1000
sparc_cpu_model=ap1000;
strcpy(&cputypval, "ap+");
@@ -320,12 +323,10 @@ __initfunc(void setup_arch(char **cmdline_p,
switch(sparc_cpu_model) {
case sun4:
printk("SUN4\n");
- sun4c_probe_vac();
packed = 0;
break;
case sun4c:
printk("SUN4C\n");
- sun4c_probe_vac();
packed = 0;
break;
case sun4m:
@@ -356,6 +357,8 @@ __initfunc(void setup_arch(char **cmdline_p,
boot_flags_init(*cmdline_p);
idprom_init();
+ if (ARCH_SUN4C_SUN4)
+ sun4c_probe_vac();
load_mmu();
total = prom_probe_memory();
*memory_start_p = (((unsigned long) &end));
@@ -374,41 +377,37 @@ __initfunc(void setup_arch(char **cmdline_p,
}
}
}
- } else {
- unsigned int sum = 0;
+ *memory_end_p = (end_of_phys_memory + KERNBASE);
+ } else
+ srmmu_end_memory(memory_size, memory_end_p);
- for(i = 0; sp_banks[i].num_bytes != 0; i++) {
- sum += sp_banks[i].num_bytes;
- if (memory_size) {
- if (sum > memory_size) {
- sp_banks[i].num_bytes -=
- (sum - memory_size);
- sum = memory_size;
- sp_banks[++i].base_addr = 0xdeadbeef;
- sp_banks[i].num_bytes = 0;
- break;
- }
- }
+ if (!root_flags)
+ root_mountflags &= ~MS_RDONLY;
+ ROOT_DEV = to_kdev_t(root_dev);
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+ rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+ rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (ramdisk_image) {
+ initrd_start = ramdisk_image;
+ if (initrd_start < KERNBASE) initrd_start += KERNBASE;
+ initrd_end = initrd_start + ramdisk_size;
+ if (initrd_end > *memory_end_p) {
+ printk(KERN_CRIT "initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ initrd_end,*memory_end_p);
+ initrd_start = 0;
+ }
+ if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
+ initrd_below_start_ok = 1;
+ *memory_start_p = PAGE_ALIGN (initrd_end);
}
- end_of_phys_memory = sum;
}
-
+#endif
prom_setsync(prom_sync_me);
- *memory_end_p = (end_of_phys_memory + KERNBASE);
- if((sparc_cpu_model == sun4c) ||
- (sparc_cpu_model == sun4))
- goto not_relevant;
- if(end_of_phys_memory >= 0x0d000000) {
- *memory_end_p = 0xfd000000;
- } else {
- if((sparc_cpu_model == sun4m) ||
- (sparc_cpu_model == sun4d) ||
- (sparc_cpu_model == ap1000))
- *memory_end_p = srmmu_endmem_fixup(*memory_end_p);
- }
-not_relevant:
-
#ifdef CONFIG_SUN_SERIAL
*memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */
#endif
@@ -459,31 +458,6 @@ not_relevant:
breakpoint();
}
- if (!root_flags)
- root_mountflags &= ~MS_RDONLY;
- ROOT_DEV = to_kdev_t(root_dev);
-#ifdef CONFIG_BLK_DEV_RAM
- rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
- rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
- rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
-#endif
-#ifdef CONFIG_BLK_DEV_INITRD
- if (ramdisk_image) {
- initrd_start = ramdisk_image;
- if (initrd_start < KERNBASE) initrd_start += KERNBASE;
- initrd_end = initrd_start + ramdisk_size;
- if (initrd_end > *memory_end_p) {
- printk(KERN_CRIT "initrd extends beyond end of memory "
- "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
- initrd_end,*memory_end_p);
- initrd_start = 0;
- }
- if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
- initrd_below_start_ok = 1;
- *memory_start_p = PAGE_ALIGN (initrd_end);
- }
- }
-#endif
/* Due to stack alignment restrictions and assumptions... */
init_task.mm->mmap->vm_page_prot = PAGE_SHARED;
@@ -504,13 +478,12 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
extern char *sparc_cpu_type[];
extern char *sparc_fpu_type[];
-extern char *smp_info(void);
-
int get_cpuinfo(char *buffer)
{
- int cpuid=get_cpuid();
+ int cpuid=hard_smp_processor_id();
+ int len;
- return sprintf(buffer, "cpu\t\t: %s\n"
+ len = sprintf(buffer, "cpu\t\t: %s\n"
"fpu\t\t: %s\n"
"promlib\t\t: Version %d Revision %d\n"
"prom\t\t: %d.%d\n"
@@ -519,34 +492,23 @@ int get_cpuinfo(char *buffer)
"ncpus active\t: %d\n"
#ifndef __SMP__
"BogoMips\t: %lu.%02lu\n"
-#else
- "Cpu0Bogo\t: %lu.%02lu\n"
- "Cpu1Bogo\t: %lu.%02lu\n"
- "Cpu2Bogo\t: %lu.%02lu\n"
- "Cpu3Bogo\t: %lu.%02lu\n"
-#endif
- "%s"
-#ifdef __SMP__
- "%s"
#endif
,
- sparc_cpu_type[cpuid],
- sparc_fpu_type[cpuid],
+ sparc_cpu_type[cpuid] ? : "undetermined",
+ sparc_fpu_type[cpuid] ? : "undetermined",
romvec->pv_romvers, prom_rev, romvec->pv_printrev >> 16, (short)romvec->pv_printrev,
&cputypval,
- linux_num_cpus, smp_num_cpus,
+ linux_num_cpus, smp_num_cpus
#ifndef __SMP__
- loops_per_sec/500000, (loops_per_sec/5000) % 100,
-#else
- cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
- cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
- cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
- cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100,
+ , loops_per_sec/500000, (loops_per_sec/5000) % 100
#endif
- mmu_info()
+ );
#ifdef __SMP__
- , smp_info()
+ len += smp_bogo_info(buffer + len);
#endif
- );
-
+ len += mmu_info(buffer + len);
+#ifdef __SMP__
+ len += smp_info(buffer + len);
+#endif
+ return len;
}
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c
index 5c10faa81..efdb362a4 100644
--- a/arch/sparc/kernel/signal.c
+++ b/arch/sparc/kernel/signal.c
@@ -1,4 +1,4 @@
-/* $Id: signal.c,v 1.77 1997/12/22 03:06:32 ecd Exp $
+/* $Id: signal.c,v 1.79 1998/04/04 07:11:41 davem Exp $
* linux/arch/sparc/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
@@ -78,9 +78,20 @@ struct new_signal_frame {
__siginfo_fpu_t fpu_state;
};
+struct rt_signal_frame {
+ struct sparc_stackf ss;
+ siginfo_t info;
+ struct pt_regs regs;
+ sigset_t mask;
+ __siginfo_fpu_t *fpu_save;
+ unsigned int insns [2];
+ __siginfo_fpu_t fpu_state;
+};
+
/* Align macros */
#define SF_ALIGNEDSZ (((sizeof(struct signal_sframe) + 7) & (~7)))
#define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7)))
+#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7)))
/*
* atomically swap in the new signal mask, and wait for a signal.
@@ -318,7 +329,60 @@ segv_and_exit:
asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
{
- printk("XXX: FIXME: write do_rt_sigreturn\n");
+ struct rt_signal_frame *sf;
+ unsigned int psr, pc, npc;
+ __siginfo_fpu_t *fpu_save;
+ sigset_t set;
+
+ synchronize_user_stack();
+ sf = (struct rt_signal_frame *) regs->u_regs[UREG_FP];
+ if(verify_area(VERIFY_READ, sf, sizeof(*sf)) ||
+ (((unsigned long) sf) & 0x03))
+ goto segv;
+
+ get_user(pc, &sf->regs.pc);
+ __get_user(npc, &sf->regs.npc);
+ if((pc | npc) & 0x03)
+ goto segv;
+
+ regs->pc = pc;
+ regs->npc = npc;
+
+ __get_user(regs->y, &sf->regs.y);
+ __get_user(psr, &sf->regs.psr);
+
+ __get_user(regs->u_regs[UREG_G1], &sf->regs.u_regs[UREG_G1]);
+ __get_user(regs->u_regs[UREG_G2], &sf->regs.u_regs[UREG_G2]);
+ __get_user(regs->u_regs[UREG_G3], &sf->regs.u_regs[UREG_G3]);
+ __get_user(regs->u_regs[UREG_G4], &sf->regs.u_regs[UREG_G4]);
+ __get_user(regs->u_regs[UREG_G5], &sf->regs.u_regs[UREG_G5]);
+ __get_user(regs->u_regs[UREG_G6], &sf->regs.u_regs[UREG_G6]);
+ __get_user(regs->u_regs[UREG_G7], &sf->regs.u_regs[UREG_G7]);
+ __get_user(regs->u_regs[UREG_I0], &sf->regs.u_regs[UREG_I0]);
+ __get_user(regs->u_regs[UREG_I1], &sf->regs.u_regs[UREG_I1]);
+ __get_user(regs->u_regs[UREG_I2], &sf->regs.u_regs[UREG_I2]);
+ __get_user(regs->u_regs[UREG_I3], &sf->regs.u_regs[UREG_I3]);
+ __get_user(regs->u_regs[UREG_I4], &sf->regs.u_regs[UREG_I4]);
+ __get_user(regs->u_regs[UREG_I5], &sf->regs.u_regs[UREG_I5]);
+ __get_user(regs->u_regs[UREG_I6], &sf->regs.u_regs[UREG_I6]);
+ __get_user(regs->u_regs[UREG_I7], &sf->regs.u_regs[UREG_I7]);
+
+ regs->psr = (regs->psr & ~PSR_ICC) | (psr & PSR_ICC);
+
+ __get_user(fpu_save, &sf->fpu_save);
+ if(fpu_save)
+ restore_fpu_state(regs, &sf->fpu_state);
+ if(copy_from_user(&set, &sf->mask, sizeof(sigset_t)))
+ goto segv;
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sigmask_lock);
+ current->blocked = set;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+ return;
+segv:
+ lock_kernel();
+ do_exit(SIGSEGV);
}
/* Checks if the fp is valid */
@@ -514,7 +578,63 @@ static inline void
new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs,
int signo, sigset_t *oldset, siginfo_t *info)
{
- printk("XXX: FIXME: new_setup_rt_frame unimplemented\n");
+ struct rt_signal_frame *sf;
+ int sigframe_size;
+ unsigned int psr;
+ int i;
+
+ synchronize_user_stack();
+ sigframe_size = RT_ALIGNEDSZ;
+ if(!current->used_math)
+ sigframe_size -= sizeof(__siginfo_fpu_t);
+ sf = (struct rt_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size);
+ if(invalid_frame_pointer(sf, sigframe_size))
+ goto sigill;
+ if(current->tss.w_saved != 0)
+ goto sigill;
+
+ put_user(regs->pc, &sf->regs.pc);
+ __put_user(regs->npc, &sf->regs.npc);
+ __put_user(regs->y, &sf->regs.y);
+ psr = regs->psr;
+ if(current->used_math)
+ psr |= PSR_EF;
+ __put_user(psr, &sf->regs.psr);
+ for(i = 0; i < 16; i++)
+ __put_user(regs->u_regs[i], &sf->regs.u_regs[i]);
+ if(psr & PSR_EF) {
+ save_fpu_state(regs, &sf->fpu_state);
+ __put_user(&sf->fpu_state, &sf->fpu_save);
+ } else {
+ __put_user(0, &sf->fpu_save);
+ }
+ __copy_to_user(&sf->mask, &oldset->sig[0], sizeof(sigset_t));
+ copy_to_user(sf, (char *) regs->u_regs [UREG_FP],
+ sizeof (struct reg_window));
+
+ regs->u_regs[UREG_FP] = (unsigned long) sf;
+ regs->u_regs[UREG_I0] = signo;
+ regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
+
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+ regs->npc = (regs->pc + 4);
+
+ if(ka->ka_restorer)
+ regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
+ else {
+ regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2);
+
+ __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
+ __put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */
+
+ /* Flush instruction space. */
+ flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+ }
+ return;
+
+sigill:
+ lock_kernel();
+ do_exit(SIGILL);
}
/* Setup a Solaris stack frame */
@@ -783,6 +903,7 @@ handle_signal(unsigned long signr, struct k_sigaction *ka,
spin_lock_irq(&current->sigmask_lock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
sigaddset(&current->blocked, signr);
+ recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
}
}
diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c
index 15364d44f..c6d86d36c 100644
--- a/arch/sparc/kernel/smp.c
+++ b/arch/sparc/kernel/smp.c
@@ -1,9 +1,9 @@
/* smp.c: Sparc SMP support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
-#include <linux/config.h> /* for CONFIG_PROFILE */
#include <asm/head.h>
#include <linux/kernel.h>
@@ -13,6 +13,7 @@
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
+#include <linux/init.h>
#include <asm/ptrace.h>
#include <asm/atomic.h>
@@ -34,34 +35,29 @@
#define IRQ_STOP_CPU 14
#define IRQ_CROSS_CALL 15
-extern ctxd_t *srmmu_ctx_table_phys;
-extern int linux_num_cpus;
-
-extern void calibrate_delay(void);
-
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
-
volatile int smp_processors_ready = 0;
-
unsigned long cpu_present_map = 0;
int smp_num_cpus = 1;
int smp_threads_ready=0;
unsigned char mid_xlate[NR_CPUS] = { 0, 0, 0, 0, };
-volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile unsigned long cpu_callin_map[NR_CPUS] __initdata = {0,};
+#ifdef NOTUSED
volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+#endif
unsigned long smp_proc_in_lock[NR_CPUS] = { 0, };
struct cpuinfo_sparc cpu_data[NR_CPUS];
-static unsigned char boot_cpu_id = 0;
-static int smp_activated = 0;
+unsigned long cpu_offset[NR_CPUS];
+unsigned char boot_cpu_id = 0;
+unsigned char boot_cpu_id4 = 0; /* boot_cpu_id << 2 */
+int smp_activated = 0;
volatile int cpu_number_map[NR_CPUS];
-volatile int cpu_logical_map[NR_CPUS];
+volatile int __cpu_logical_map[NR_CPUS];
/* The only guaranteed locking primitive available on all Sparc
* processors is 'ldstub [%reg + immediate], %dest_reg' which atomically
* places the current byte at the effective address into dest_reg and
* places 0xff there afterwards. Pretty lame locking primitive
- * compared to the Alpha and the intel no? Most Sparcs have 'swap'
+ * compared to the Alpha and the Intel no? Most Sparcs have 'swap'
* instruction which is much better...
*/
struct klock_info klock_info = { KLOCK_CLEAR, 0 };
@@ -69,42 +65,11 @@ struct klock_info klock_info = { KLOCK_CLEAR, 0 };
volatile unsigned long ipi_count;
volatile int smp_process_available=0;
-
-/*#define SMP_DEBUG*/
-
-#ifdef SMP_DEBUG
-#define SMP_PRINTK(x) printk x
-#else
-#define SMP_PRINTK(x)
-#endif
-
volatile int smp_commenced = 0;
-static char smp_buf[512];
-
/* Not supported on Sparc yet. */
-void smp_setup(char *str, int *ints)
-{
-}
-
-char *smp_info(void)
-{
- sprintf(smp_buf,
-" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
-"State: %s\t\t%s\t\t%s\t\t%s\n",
-(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
-(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
-(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
-(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
- return smp_buf;
-}
-
-static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+__initfunc(void smp_setup(char *str, int *ints))
{
- __asm__ __volatile__("swap [%1], %0\n\t" :
- "=&r" (val), "=&r" (ptr) :
- "0" (val), "1" (ptr));
- return val;
}
/*
@@ -112,12 +77,12 @@ static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
* a given CPU
*/
-void smp_store_cpu_info(int id)
+__initfunc(void smp_store_cpu_info(int id))
{
cpu_data[id].udelay_val = loops_per_sec; /* this is it on sparc. */
}
-void smp_commence(void)
+__initfunc(void smp_commence(void))
{
/*
* Lets the callin's below out of their loop.
@@ -129,65 +94,19 @@ void smp_commence(void)
local_flush_tlb_all();
}
-static void smp_setup_percpu_timer(void);
-
-void smp_callin(void)
-{
- int cpuid = hard_smp_processor_id();
-
- local_flush_cache_all();
- local_flush_tlb_all();
- set_irq_udt(mid_xlate[boot_cpu_id]);
-
- /* Get our local ticker going. */
- smp_setup_percpu_timer();
-
- calibrate_delay();
- smp_store_cpu_info(cpuid);
- local_flush_cache_all();
- local_flush_tlb_all();
-
- /* Allow master to continue. */
- swap((unsigned long *)&cpu_callin_map[cpuid], 1);
- local_flush_cache_all();
- local_flush_tlb_all();
-
- while(!task[cpuid] || current_set[cpuid] != task[cpuid])
- barrier();
-
- /* Fix idle thread fields. */
- __asm__ __volatile__("ld [%0], %%g6\n\t"
- : : "r" (&current_set[cpuid])
- : "memory" /* paranoid */);
- current->mm->mmap->vm_page_prot = PAGE_SHARED;
- current->mm->mmap->vm_start = PAGE_OFFSET;
- current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
-
- while(!smp_commenced)
- barrier();
-
- local_flush_cache_all();
- local_flush_tlb_all();
-
- __sti();
-}
-
-extern int cpu_idle(void *unused);
-extern void init_IRQ(void);
-
/* Only broken Intel needs this, thus it should not even be referenced
* globally...
*/
-void initialize_secondary(void)
+__initfunc(void initialize_secondary(void))
{
}
+extern int cpu_idle(void *unused);
+
/* Activate a secondary processor. */
int start_secondary(void *unused)
{
- trap_init();
- init_IRQ();
- smp_callin();
+ prom_printf("Start secondary called. Should not happen\n");
return cpu_idle(NULL);
}
@@ -201,255 +120,25 @@ void cpu_panic(void)
* Cycle through the processors asking the PROM to start each one.
*/
-extern struct prom_cpuinfo linux_cpus[NCPUS];
-static struct linux_prom_registers penguin_ctable;
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+struct linux_prom_registers smp_penguin_ctable __initdata = { 0 };
-void smp_boot_cpus(void)
+__initfunc(void smp_boot_cpus(void))
{
- int cpucount = 0;
- int i = 0;
- int first, prev;
-
- printk("Entering SMP Mode...\n");
-
- penguin_ctable.which_io = 0;
- penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
- penguin_ctable.reg_size = 0;
-
- __sti();
- cpu_present_map = 0;
- for(i=0; i < linux_num_cpus; i++)
- cpu_present_map |= (1<<i);
- for(i=0; i < NR_CPUS; i++)
- cpu_number_map[i] = -1;
- for(i=0; i < NR_CPUS; i++)
- cpu_logical_map[i] = -1;
- mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8);
- cpu_number_map[boot_cpu_id] = 0;
- cpu_logical_map[0] = boot_cpu_id;
- klock_info.akp = boot_cpu_id;
- current->processor = boot_cpu_id;
- smp_store_cpu_info(boot_cpu_id);
- set_irq_udt(mid_xlate[boot_cpu_id]);
- smp_setup_percpu_timer();
- local_flush_cache_all();
- if(linux_num_cpus == 1)
- return; /* Not an MP box. */
- for(i = 0; i < NR_CPUS; i++) {
- if(i == boot_cpu_id)
- continue;
-
- if(cpu_present_map & (1 << i)) {
- extern unsigned long sparc_cpu_startup;
- unsigned long *entry = &sparc_cpu_startup;
- struct task_struct *p;
- int timeout;
-
- /* Cook up an idler for this guy. */
- kernel_thread(start_secondary, NULL, CLONE_PID);
-
- p = task[++cpucount];
-
- p->processor = i;
- current_set[i] = p;
-
- /* See trampoline.S for details... */
- entry += ((i-1) * 3);
-
- /* whirrr, whirrr, whirrrrrrrrr... */
- printk("Starting CPU %d at %p\n", i, entry);
- mid_xlate[i] = (linux_cpus[i].mid & ~8);
- local_flush_cache_all();
- prom_startcpu(linux_cpus[i].prom_node,
- &penguin_ctable, 0, (char *)entry);
-
- /* wheee... it's going... */
- for(timeout = 0; timeout < 5000000; timeout++) {
- if(cpu_callin_map[i])
- break;
- udelay(100);
- }
- if(cpu_callin_map[i]) {
- /* Another "Red Snapper". */
- cpu_number_map[i] = i;
- cpu_logical_map[i] = i;
- } else {
- cpucount--;
- printk("Processor %d is stuck.\n", i);
- }
- }
- if(!(cpu_callin_map[i])) {
- cpu_present_map &= ~(1 << i);
- cpu_number_map[i] = -1;
- }
- }
- local_flush_cache_all();
- if(cpucount == 0) {
- printk("Error: only one Processor found.\n");
- cpu_present_map = (1 << smp_processor_id());
- } else {
- unsigned long bogosum = 0;
- for(i = 0; i < NR_CPUS; i++) {
- if(cpu_present_map & (1 << i))
- bogosum += cpu_data[i].udelay_val;
- }
- printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
- cpucount + 1,
- (bogosum + 2500)/500000,
- ((bogosum + 2500)/5000)%100);
- smp_activated = 1;
- smp_num_cpus = cpucount + 1;
- }
-
- /* Setup CPU list for IRQ distribution scheme. */
- first = prev = -1;
- for(i = 0; i < NR_CPUS; i++) {
- if(cpu_present_map & (1 << i)) {
- if(first == -1)
- first = i;
- if(prev != -1)
- cpu_data[i].next = i;
- cpu_data[i].mid = mid_xlate[i];
- prev = i;
- }
- }
- cpu_data[prev].next = first;
-
- /* Ok, they are spinning and ready to go. */
- smp_processors_ready = 1;
-}
-
-/* At each hardware IRQ, we get this called to forward IRQ reception
- * to the next processor. The caller must disable the IRQ level being
- * serviced globally so that there are no double interrupts received.
- */
-void smp_irq_rotate(int cpu)
-{
- if(smp_processors_ready)
- set_irq_udt(cpu_data[cpu_data[cpu].next].mid);
-}
-
-/* Cross calls, in order to work efficiently and atomically do all
- * the message passing work themselves, only stopcpu and reschedule
- * messages come through here.
- */
-void smp_message_pass(int target, int msg, unsigned long data, int wait)
-{
- static unsigned long smp_cpu_in_msg[NR_CPUS];
- unsigned long mask;
- int me = smp_processor_id();
- int irq, i;
-
- if(msg == MSG_RESCHEDULE) {
- irq = IRQ_RESCHEDULE;
-
- if(smp_cpu_in_msg[me])
- return;
- } else if(msg == MSG_STOP_CPU) {
- irq = IRQ_STOP_CPU;
- } else {
- goto barf;
- }
-
- smp_cpu_in_msg[me]++;
- if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
- mask = cpu_present_map;
- if(target == MSG_ALL_BUT_SELF)
- mask &= ~(1 << me);
- for(i = 0; i < 4; i++) {
- if(mask & (1 << i))
- set_cpu_int(mid_xlate[i], irq);
- }
- } else {
- set_cpu_int(mid_xlate[target], irq);
- }
- smp_cpu_in_msg[me]--;
-
- return;
-barf:
- printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
- panic("Bogon SMP message pass.");
-}
-
-struct smp_funcall {
- smpfunc_t func;
- unsigned long arg1;
- unsigned long arg2;
- unsigned long arg3;
- unsigned long arg4;
- unsigned long arg5;
- unsigned long processors_in[NR_CPUS]; /* Set when ipi entered. */
- unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */
-} ccall_info;
-
-static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
-
-/* Cross calls must be serialized, at least currently. */
-void smp_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
- unsigned long arg3, unsigned long arg4, unsigned long arg5)
-{
- if(smp_processors_ready) {
- register int ncpus = smp_num_cpus;
- unsigned long flags;
-
- spin_lock_irqsave(&cross_call_lock, flags);
-
- /* Init function glue. */
- ccall_info.func = func;
- ccall_info.arg1 = arg1;
- ccall_info.arg2 = arg2;
- ccall_info.arg3 = arg3;
- ccall_info.arg4 = arg4;
- ccall_info.arg5 = arg5;
-
- /* Init receive/complete mapping, plus fire the IPI's off. */
- {
- register void (*send_ipi)(int,int) = set_cpu_int;
- register unsigned long mask;
- register int i;
-
- mask = (cpu_present_map & ~(1 << smp_processor_id()));
- for(i = 0; i < ncpus; i++) {
- if(mask & (1 << i)) {
- ccall_info.processors_in[i] = 0;
- ccall_info.processors_out[i] = 0;
- send_ipi(mid_xlate[i], IRQ_CROSS_CALL);
- } else {
- ccall_info.processors_in[i] = 1;
- ccall_info.processors_out[i] = 1;
- }
- }
- }
-
- /* First, run local copy. */
- func(arg1, arg2, arg3, arg4, arg5);
-
- {
- register int i;
-
- i = 0;
- do {
- while(!ccall_info.processors_in[i])
- barrier();
- } while(++i < ncpus);
-
- i = 0;
- do {
- while(!ccall_info.processors_out[i])
- barrier();
- } while(++i < ncpus);
- }
-
- spin_unlock_irqrestore(&cross_call_lock, flags);
- } else
- func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */
+ extern void smp4m_boot_cpus(void);
+ extern void smp4d_boot_cpus(void);
+
+ if (sparc_cpu_model == sun4m)
+ smp4m_boot_cpus();
+ else
+ smp4d_boot_cpus();
}
void smp_flush_cache_all(void)
-{ xc0((smpfunc_t) local_flush_cache_all); }
+{ xc0((smpfunc_t) BTFIXUP_CALL(local_flush_cache_all)); }
void smp_flush_tlb_all(void)
-{ xc0((smpfunc_t) local_flush_tlb_all); }
+{ xc0((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_all)); }
void smp_flush_cache_mm(struct mm_struct *mm)
{
@@ -457,7 +146,7 @@ void smp_flush_cache_mm(struct mm_struct *mm)
if(mm->cpu_vm_mask == (1 << smp_processor_id()))
local_flush_cache_mm(mm);
else
- xc1((smpfunc_t) local_flush_cache_mm, (unsigned long) mm);
+ xc1((smpfunc_t) BTFIXUP_CALL(local_flush_cache_mm), (unsigned long) mm);
}
}
@@ -467,7 +156,7 @@ void smp_flush_tlb_mm(struct mm_struct *mm)
if(mm->cpu_vm_mask == (1 << smp_processor_id())) {
local_flush_tlb_mm(mm);
} else {
- xc1((smpfunc_t) local_flush_tlb_mm, (unsigned long) mm);
+ xc1((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_mm), (unsigned long) mm);
if(mm->count == 1 && current->mm == mm)
mm->cpu_vm_mask = (1 << smp_processor_id());
}
@@ -481,7 +170,7 @@ void smp_flush_cache_range(struct mm_struct *mm, unsigned long start,
if(mm->cpu_vm_mask == (1 << smp_processor_id()))
local_flush_cache_range(mm, start, end);
else
- xc3((smpfunc_t) local_flush_cache_range, (unsigned long) mm,
+ xc3((smpfunc_t) BTFIXUP_CALL(local_flush_cache_range), (unsigned long) mm,
start, end);
}
}
@@ -493,7 +182,7 @@ void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start,
if(mm->cpu_vm_mask == (1 << smp_processor_id()))
local_flush_tlb_range(mm, start, end);
else
- xc3((smpfunc_t) local_flush_tlb_range, (unsigned long) mm,
+ xc3((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_range), (unsigned long) mm,
start, end);
}
}
@@ -506,7 +195,7 @@ void smp_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
if(mm->cpu_vm_mask == (1 << smp_processor_id()))
local_flush_cache_page(vma, page);
else
- xc2((smpfunc_t) local_flush_cache_page,
+ xc2((smpfunc_t) BTFIXUP_CALL(local_flush_cache_page),
(unsigned long) vma, page);
}
}
@@ -519,7 +208,7 @@ void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
if(mm->cpu_vm_mask == (1 << smp_processor_id()))
local_flush_tlb_page(vma, page);
else
- xc2((smpfunc_t) local_flush_tlb_page, (unsigned long) vma, page);
+ xc2((smpfunc_t) BTFIXUP_CALL(local_flush_tlb_page), (unsigned long) vma, page);
}
}
@@ -532,7 +221,7 @@ void smp_flush_page_to_ram(unsigned long page)
* XXX This experiment failed, research further... -DaveM
*/
#if 1
- xc1((smpfunc_t) local_flush_page_to_ram, page);
+ xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_to_ram), page);
#else
local_flush_page_to_ram(page);
#endif
@@ -543,7 +232,7 @@ void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
if(mm->cpu_vm_mask == (1 << smp_processor_id()))
local_flush_sig_insns(mm, insn_addr);
else
- xc2((smpfunc_t) local_flush_sig_insns, (unsigned long) mm, insn_addr);
+ xc2((smpfunc_t) BTFIXUP_CALL(local_flush_sig_insns), (unsigned long) mm, insn_addr);
}
/* Reschedule call back. */
@@ -552,17 +241,6 @@ void smp_reschedule_irq(void)
need_resched = 1;
}
-/* Running cross calls. */
-void smp_cross_call_irq(void)
-{
- int i = smp_processor_id();
-
- ccall_info.processors_in[i] = 1;
- ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
- ccall_info.arg4, ccall_info.arg5);
- ccall_info.processors_out[i] = 1;
-}
-
/* Stopping processors. */
void smp_stop_cpu_irq(void)
{
@@ -571,84 +249,10 @@ void smp_stop_cpu_irq(void)
barrier();
}
-/* Protects counters touched during level14 ticker */
-spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
-
-#ifdef CONFIG_PROFILE
-
-/* 32-bit Sparc specific profiling function. */
-static inline void sparc_do_profile(unsigned long pc)
-{
- if(prof_buffer && current->pid) {
- extern int _stext;
-
- pc -= (unsigned long) &_stext;
- pc >>= prof_shift;
-
- spin_lock(&ticker_lock);
- if(pc < prof_len)
- prof_buffer[pc]++;
- else
- prof_buffer[prof_len - 1]++;
- spin_unlock(&ticker_lock);
- }
-}
-
-#endif
-
unsigned int prof_multiplier[NR_CPUS];
unsigned int prof_counter[NR_CPUS];
-
-extern void update_one_process(struct task_struct *p, unsigned long ticks,
- unsigned long user, unsigned long system);
-
-void smp_percpu_timer_interrupt(struct pt_regs *regs)
-{
- int cpu = smp_processor_id();
-
- clear_profile_irq(mid_xlate[cpu]);
-#ifdef CONFIG_PROFILE
- if(!user_mode(regs))
- sparc_do_profile(regs->pc);
-#endif
- if(!--prof_counter[cpu]) {
- int user = user_mode(regs);
- if(current->pid) {
- update_one_process(current, 1, user, !user);
-
- if(--current->counter < 0) {
- current->counter = 0;
- need_resched = 1;
- }
-
- spin_lock(&ticker_lock);
- if(user) {
- if(current->priority < DEF_PRIORITY)
- kstat.cpu_nice++;
- else
- kstat.cpu_user++;
- } else {
- kstat.cpu_system++;
- }
- spin_unlock(&ticker_lock);
- }
- prof_counter[cpu] = prof_multiplier[cpu];
- }
-}
-
extern unsigned int lvl14_resolution;
-static void smp_setup_percpu_timer(void)
-{
- int cpu = smp_processor_id();
-
- prof_counter[cpu] = prof_multiplier[cpu] = 1;
- load_profile_irq(mid_xlate[cpu], lvl14_resolution);
-
- if(cpu == boot_cpu_id)
- enable_pil_irq(14);
-}
-
int setup_profiling_timer(unsigned int multiplier)
{
int i;
diff --git a/arch/sparc/kernel/sparc-stub.c b/arch/sparc/kernel/sparc-stub.c
index e259ffade..9426ec0d9 100644
--- a/arch/sparc/kernel/sparc-stub.c
+++ b/arch/sparc/kernel/sparc-stub.c
@@ -1,4 +1,4 @@
-/* $Id: sparc-stub.c,v 1.22 1998/01/07 06:33:48 baccala Exp $
+/* $Id: sparc-stub.c,v 1.24 1998/02/08 07:58:44 ecd Exp $
* sparc-stub.c: KGDB support for the Linux kernel.
*
* Modifications to run under Linux
@@ -165,9 +165,10 @@ unsigned long get_sun4csegmap(unsigned long addr)
return entry;
}
-static void flush_cache_all_nop(void)
-{
-}
+#if 0
+/* Have to sort this out. This cannot be done after initialization. */
+static void flush_cache_all_nop(void) {}
+#endif
/* Place where we save old trap entries for restoration */
struct tt_entry kgdb_savettable[256];
@@ -398,10 +399,12 @@ set_debug_traps(void)
{
struct hard_trap_info *ht;
unsigned long flags;
- unsigned char c;
save_and_cli(flags);
- flush_cache_all = flush_cache_all_nop;
+#if 0
+/* Have to sort this out. This cannot be done after initialization. */
+ BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP);
+#endif
/* Initialize our copy of the Linux Sparc trap table */
eh_init();
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
index 92be6f74d..1690a7c69 100644
--- a/arch/sparc/kernel/sparc_ksyms.c
+++ b/arch/sparc/kernel/sparc_ksyms.c
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.61 1997/11/19 07:57:44 jj Exp $
+/* $Id: sparc_ksyms.c,v 1.64 1998/03/19 15:36:43 jj Exp $
* arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -15,7 +15,6 @@
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/in6.h>
-#include <linux/pci.h>
#include <asm/oplib.h>
#include <asm/delay.h>
@@ -138,7 +137,6 @@ EXPORT_SYMBOL(page_offset);
EXPORT_SYMBOL(stack_top);
/* Atomic operations. */
-EXPORT_SYMBOL_PRIVATE(_xchg32);
EXPORT_SYMBOL_PRIVATE(_atomic_add);
EXPORT_SYMBOL_PRIVATE(_atomic_sub);
@@ -168,13 +166,23 @@ EXPORT_SYMBOL(request_fast_irq);
EXPORT_SYMBOL(sparc_alloc_io);
EXPORT_SYMBOL(sparc_free_io);
EXPORT_SYMBOL(io_remap_page_range);
-EXPORT_SYMBOL(mmu_v2p);
-EXPORT_SYMBOL(mmu_unlockarea);
-EXPORT_SYMBOL(mmu_lockarea);
-EXPORT_SYMBOL(mmu_get_scsi_sgl);
-EXPORT_SYMBOL(mmu_get_scsi_one);
-EXPORT_SYMBOL(mmu_release_scsi_sgl);
-EXPORT_SYMBOL(mmu_release_scsi_one);
+
+/* Btfixup stuff cannot have versions, it would be complicated too much */
+#ifndef __SMP__
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(___xchg32));
+#else
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(__smp_processor_id));
+#endif
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(enable_irq));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(disable_irq));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_v2p));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_unlockarea));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_lockarea));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_get_scsi_sgl));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_get_scsi_one));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_release_scsi_sgl));
+EXPORT_SYMBOL_NOVERS(BTFIXUP_CALL(mmu_release_scsi_one));
+
EXPORT_SYMBOL(_sparc_dvma_malloc);
EXPORT_SYMBOL(sun4c_unmapioaddr);
EXPORT_SYMBOL(srmmu_unmapioaddr);
@@ -272,7 +280,3 @@ EXPORT_SYMBOL_DOT(mul);
EXPORT_SYMBOL_DOT(umul);
EXPORT_SYMBOL_DOT(div);
EXPORT_SYMBOL_DOT(udiv);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c
index bc9569688..cef6370ce 100644
--- a/arch/sparc/kernel/sun4c_irq.c
+++ b/arch/sparc/kernel/sun4c_irq.c
@@ -30,6 +30,9 @@
#include <asm/traps.h>
#include <asm/irq.h>
#include <asm/io.h>
+#include <asm/sun4paddr.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
/* Pointer to the interrupt enable byte
*
@@ -128,7 +131,7 @@ __initfunc(static void sun4c_init_timers(void (*counter_fn)(int, void *, struct
/* Map the Timer chip, this is implemented in hardware inside
* the cache chip on the sun4c.
*/
- sun4c_timers = sparc_alloc_io (SUN4C_TIMER_PHYSADDR, 0,
+ sun4c_timers = sparc_alloc_io (SUN_TIMER_PHYSADDR, 0,
sizeof(struct sun4c_timer_info),
"timer", 0x0, 0x0);
@@ -160,30 +163,41 @@ __initfunc(void sun4c_init_IRQ(void))
{
struct linux_prom_registers int_regs[2];
int ie_node;
+
+ if (ARCH_SUN4) {
+ interrupt_enable =
+ (char *) sparc_alloc_io(SUN4_IE_PHYSADDR, 0,
+ PAGE_SIZE,
+ "sun4c_interrupts",
+ 0x0, 0x0);
+ } else {
- ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
- "interrupt-enable");
- if(ie_node == 0)
- panic("Cannot find /interrupt-enable node");
-
- /* Depending on the "address" property is bad news... */
- prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs));
- interrupt_enable = (char *) sparc_alloc_io(int_regs[0].phys_addr, 0,
- int_regs[0].reg_size,
- "sun4c_interrupts",
- int_regs[0].which_io, 0x0);
- enable_irq = sun4c_enable_irq;
- disable_irq = sun4c_disable_irq;
- enable_pil_irq = sun4c_enable_irq;
- disable_pil_irq = sun4c_disable_irq;
- clear_clock_irq = sun4c_clear_clock_irq;
- clear_profile_irq = sun4c_clear_profile_irq;
- load_profile_irq = sun4c_load_profile_irq;
+ ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
+ "interrupt-enable");
+ if(ie_node == 0)
+ panic("Cannot find /interrupt-enable node");
+
+ /* Depending on the "address" property is bad news... */
+ prom_getproperty(ie_node, "reg", (char *) int_regs, sizeof(int_regs));
+ interrupt_enable =
+ (char *) sparc_alloc_io(int_regs[0].phys_addr, 0,
+ int_regs[0].reg_size,
+ "sun4c_interrupts",
+ int_regs[0].which_io, 0x0);
+ }
+
+ BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP);
init_timers = sun4c_init_timers;
#ifdef __SMP__
- set_cpu_int = (void (*) (int, int))sun4c_nop;
- clear_cpu_int = (void (*) (int, int))sun4c_nop;
- set_irq_udt = (void (*) (int))sun4c_nop;
+ BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP);
#endif
*interrupt_enable = (SUN4C_INT_ENABLE);
/* Cannot enable interrupts until OBP ticker is disabled. */
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
index f22fe1495..302df86f4 100644
--- a/arch/sparc/kernel/sun4d_irq.c
+++ b/arch/sparc/kernel/sun4d_irq.c
@@ -1,8 +1,8 @@
-/* $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $
- * arch/sparc/kernel/sun4d_irq.c:
+/* $Id: sun4d_irq.c,v 1.12 1998/03/19 15:36:36 jj Exp $
+ * arch/sparc/kernel/sun4d_irq.c:
* SS1000/SC2000 interrupt handling.
*
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Heavily based on arch/sparc/kernel/irq.c.
*/
@@ -36,32 +36,47 @@
#include <asm/sbus.h>
#include <asm/sbi.h>
+/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */
+/* #define DISTRIBUTE_IRQS */
+
struct sun4d_timer_regs *sun4d_timers;
#define TIMER_IRQ 10
#define MAX_STATIC_ALLOC 4
extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
extern int static_irq_count;
+unsigned char cpu_leds[32];
+#ifdef __SMP__
+unsigned char sbus_tid[32];
+#endif
extern struct irqaction *irq_action[];
struct sbus_action {
struct irqaction *action;
- unsigned char lock;
- unsigned char active;
- unsigned char disabled;
+ /* For SMP this needs to be extended */
} *sbus_actions;
static int pil_to_sbus[] = {
0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
};
+static int sbus_to_pil[] = {
+ 0, 2, 3, 5, 7, 9, 11, 13,
+};
+
static int nsbi;
+#ifdef __SMP__
+spinlock_t sun4d_imsk_lock = SPIN_LOCK_UNLOCKED;
+#endif
int sun4d_get_irq_list(char *buf)
{
int i, j = 0, k = 0, len = 0, sbusl;
struct irqaction * action;
+#ifdef __SMP__
+ int x;
+#endif
for (i = 0 ; i < NR_IRQS ; i++) {
sbusl = pil_to_sbus[i];
@@ -77,8 +92,15 @@ int sun4d_get_irq_list(char *buf)
}
continue;
}
-found_it: len += sprintf(buf+len, "%2d: %8d %c %s",
- i, kstat.interrupts[i],
+found_it: len += sprintf(buf+len, "%3d: ", i);
+#ifndef __SMP__
+ len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#else
+ for (x = 0; x < smp_num_cpus; x++)
+ len += sprintf(buf+len, "%10u ",
+ kstat.irqs[cpu_logical_map(x)][i]);
+#endif
+ len += sprintf(buf+len, "%c %s",
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
action = action->next;
@@ -172,7 +194,7 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs)
cc_set_iclr(1 << irq);
irq_enter(cpu, irq, regs);
- kstat.interrupts[irq]++;
+ kstat.irqs[cpu][irq]++;
if (!sbusl) {
action = *(irq + irq_action);
if (!action)
@@ -183,7 +205,6 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs)
} while (action);
} else {
int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
- int lock;
int sbino;
struct sbus_action *actionp;
unsigned mask, slot;
@@ -202,19 +223,13 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs)
if (mask & slot) {
mask &= ~slot;
action = actionp->action;
- __asm__ __volatile__ ("ldstub [%1 + 4], %0"
- : "=r" (lock) : "r" (actionp));
- if (!lock) {
- if (!action)
- unexpected_irq(irq, 0, regs);
- do {
- action->handler(irq, action->dev_id, regs);
- action = action->next;
- } while (action);
- actionp->lock = 0;
- } else
- actionp->active = 1;
+ if (!action)
+ unexpected_irq(irq, 0, regs);
+ do {
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
release_sbi(SBI2DEVID(sbino), slot);
}
}
@@ -305,79 +320,49 @@ int sun4d_request_irq(unsigned int irq,
else
*actionp = action;
- if (ret) irq = *ret;
-
- if (irq > NR_IRQS) {
- struct sbus_action *s = sbus_actions + irq - (1 << 5);
-
- if (s->disabled) {
- s->disabled = 0;
- s->active = 0;
- s->lock = 0;
- }
- }
-
+ enable_irq(irq);
restore_flags(flags);
return 0;
}
static void sun4d_disable_irq(unsigned int irq)
{
- struct sbus_action *s;
-
- if (irq < NR_IRQS) {
- /* FIXME */
- printk ("Unable to disable IRQ %d\n", irq);
- return;
- }
- s = sbus_actions + irq - (1 << 5);
+#ifdef __SMP__
+ int tid = sbus_tid[(irq >> 5) - 1];
+ unsigned long flags;
+#endif
- if (s->disabled) return;
- s->disabled = 1;
- __asm__ __volatile__ ("
-1: ldstub [%0 + 4], %%g1
- orcc %%g1, 0, %%g0
- bne 1b"
- : : "r" (s) : "g1", "cc");
+ if (irq < NR_IRQS) return;
+#ifdef __SMP__
+ spin_lock_irqsave(&sun4d_imsk_lock, flags);
+ cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
+ spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else
+ cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
}
static void sun4d_enable_irq(unsigned int irq)
{
- struct sbus_action *s;
- struct irqaction *action;
-
- if (irq < NR_IRQS)
- /* FIXME */
- return;
- s = sbus_actions + irq - (1 << 5);
+#ifdef __SMP__
+ int tid = sbus_tid[(irq >> 5) - 1];
+ unsigned long flags;
+#endif
- if (!s->disabled) return;
- action = s->action;
- s->disabled = 0;
- while (s->active) {
- s->active = 0;
- while (action) {
- /* FIXME: Hope no sbus intr handler uses regs */
- action->handler(irq, action->dev_id, NULL);
- action = action->next;
- }
- }
- s->lock = 0;
+ if (irq < NR_IRQS) return;
+#ifdef __SMP__
+ spin_lock_irqsave(&sun4d_imsk_lock, flags);
+ cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+ spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else
+ cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
}
#ifdef __SMP__
-
-/* +-------+-------------+-----------+------------------------------------+
- * | bcast | devid | sid | levels mask |
- * +-------+-------------+-----------+------------------------------------+
- * 31 30 23 22 15 14 0
- */
-#define IGEN_MESSAGE(bcast, devid, sid, levels) \
- (((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels))
-
-static void sun4d_send_ipi(int cpu, int level)
+static void sun4d_set_cpu_int(int cpu, int level)
{
- cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1)));
+ sun4d_send_ipi(cpu, level);
}
static void sun4d_clear_ipi(int cpu, int level)
@@ -387,6 +372,55 @@ static void sun4d_clear_ipi(int cpu, int level)
static void sun4d_set_udt(int cpu)
{
}
+
+/* Setup IRQ distribution scheme. */
+__initfunc(void sun4d_distribute_irqs(void))
+{
+#ifdef DISTRIBUTE_IRQS
+ struct linux_sbus *sbus;
+ unsigned long sbus_serving_map;
+
+ sbus_serving_map = cpu_present_map;
+ for_each_sbus(sbus) {
+ if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1))))
+ sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+ else if (cpu_present_map & (1 << (sbus->board * 2)))
+ sbus_tid[sbus->board] = (sbus->board * 2);
+ else if (cpu_present_map & (1 << (sbus->board * 2 + 1)))
+ sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+ else
+ sbus_tid[sbus->board] = 0xff;
+ if (sbus_tid[sbus->board] != 0xff)
+ sbus_serving_map &= ~(1 << sbus_tid[sbus->board]);
+ }
+ for_each_sbus(sbus)
+ if (sbus_tid[sbus->board] == 0xff) {
+ int i = 31;
+
+ if (!sbus_serving_map)
+ sbus_serving_map = cpu_present_map;
+ while (!(sbus_serving_map & (1 << i)))
+ i--;
+ sbus_tid[sbus->board] = i;
+ sbus_serving_map &= ~(1 << i);
+ }
+ for_each_sbus(sbus) {
+ printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]);
+ set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3);
+ }
+#else
+ struct linux_sbus *sbus;
+ int cpuid = cpu_logical_map(1);
+
+ if (cpuid == -1)
+ cpuid = cpu_logical_map(0);
+ for_each_sbus(sbus) {
+ sbus_tid[sbus->board] = cpuid;
+ set_sbi_tid(sbus->devid, cpuid << 3);
+ }
+ printk("All sbus IRQs directed to CPU%d\n", cpuid);
+#endif
+}
#endif
static void sun4d_clear_clock_irq(void)
@@ -408,7 +442,7 @@ static void sun4d_load_profile_irq(int cpu, unsigned int limit)
__initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *)))
{
int irq;
- extern struct prom_cpuinfo linux_cpus[NCPUS];
+ extern struct prom_cpuinfo linux_cpus[NR_CPUS];
int cpu;
/* Map the User Timer registers. */
@@ -431,15 +465,39 @@ __initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct
/* Enable user timer free run for CPU 0 in BW */
/* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
- for(cpu = 0; cpu < NCPUS; cpu++)
- sun4d_load_profile_irq(linux_cpus[cpu].mid, 0);
+ for(cpu = 0; cpu < linux_num_cpus; cpu++)
+ sun4d_load_profile_irq((linux_cpus[cpu].mid >> 3), 0);
+
+#ifdef __SMP__
+ {
+ unsigned long flags;
+ extern unsigned long lvl14_save[4];
+ struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
+ extern unsigned int real_irq_entry[], smp4d_ticker[];
+ extern unsigned int patchme_maybe_smp_msg[];
+
+ /* Adjust so that we jump directly to smp4d_ticker */
+ lvl14_save[2] += smp4d_ticker - real_irq_entry;
+
+ /* For SMP we use the level 14 ticker, however the bootup code
+ * has copied the firmwares level 14 vector into boot cpu's
+ * trap table, we must fix this now or we get squashed.
+ */
+ __save_and_cli(flags);
+ patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
+ trap_table->inst_one = lvl14_save[0];
+ trap_table->inst_two = lvl14_save[1];
+ trap_table->inst_three = lvl14_save[2];
+ trap_table->inst_four = lvl14_save[3];
+ local_flush_cache_all();
+ __restore_flags(flags);
+ }
+#endif
}
__initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
{
struct linux_sbus *sbus;
- struct sbus_action *s;
- int i;
unsigned mask;
nsbi = 0;
@@ -449,11 +507,13 @@ __initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
sbus_actions = (struct sbus_action *)memory_start;
memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action));
memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action)));
- for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) {
- s->lock = 0xff;
- s->disabled = 1;
- }
for_each_sbus(sbus) {
+#ifdef __SMP__
+ extern unsigned char boot_cpu_id;
+
+ set_sbi_tid(sbus->devid, boot_cpu_id << 3);
+ sbus_tid[sbus->board] = boot_cpu_id;
+#endif
/* Get rid of pending irqs from PROM */
mask = acquire_sbi(sbus->devid, 0xffffffff);
if (mask) {
@@ -468,16 +528,16 @@ __initfunc(void sun4d_init_IRQ(void))
{
__cli();
- enable_irq = sun4d_enable_irq;
- disable_irq = sun4d_disable_irq;
- clear_clock_irq = sun4d_clear_clock_irq;
- clear_profile_irq = sun4d_clear_profile_irq;
- load_profile_irq = sun4d_load_profile_irq;
+ BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
init_timers = sun4d_init_timers;
#ifdef __SMP__
- set_cpu_int = (void (*) (int, int))sun4d_send_ipi;
- clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi;
- set_irq_udt = (void (*) (int))sun4d_set_udt;
+ BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
#endif
/* Cannot enable interrupts until OBP ticker is disabled. */
}
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
new file mode 100644
index 000000000..46ce7a83f
--- /dev/null
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -0,0 +1,576 @@
+/* sun4d_smp.c: Sparc SS1000/SC2000 SMP support.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Based on sun4m's smp.c, which is:
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h> /* for CONFIG_PROFILE */
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/atops.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+#include <asm/sbus.h>
+#include <asm/sbi.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#define IRQ_CROSS_CALL 15
+
+extern ctxd_t *srmmu_ctx_table_phys;
+extern int linux_num_cpus;
+
+extern void calibrate_delay(void);
+
+extern struct task_struct *current_set[NR_CPUS];
+extern volatile int smp_processors_ready;
+extern unsigned long cpu_present_map;
+extern int smp_num_cpus;
+static int smp_highest_cpu = 0;
+extern int smp_threads_ready;
+extern unsigned char mid_xlate[NR_CPUS];
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
+extern unsigned long smp_proc_in_lock[NR_CPUS];
+extern struct cpuinfo_sparc cpu_data[NR_CPUS];
+extern unsigned long cpu_offset[NR_CPUS];
+extern unsigned char boot_cpu_id;
+extern int smp_activated;
+extern volatile int cpu_number_map[NR_CPUS];
+extern volatile int __cpu_logical_map[NR_CPUS];
+extern struct klock_info klock_info;
+extern volatile unsigned long ipi_count;
+extern volatile int smp_process_available;
+extern volatile int smp_commenced;
+extern int __smp4d_processor_id(void);
+
+/* #define SMP_DEBUG */
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x) printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
+int smp4d_bogo_info(char *buf)
+{
+ int len = 0, i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (cpu_present_map & (1 << i))
+ len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n",
+ i,
+ cpu_data[i].udelay_val/500000,
+ (cpu_data[i].udelay_val/5000)%100);
+ return len;
+}
+
+int smp4d_info(char *buf)
+{
+ int len = 0, i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (cpu_present_map & (1 << i))
+ len += sprintf(buf + len, "CPU%d\t\t: %s\n",
+ i,
+ (klock_info.akp == i) ? "akp" : "online");
+ return len;
+}
+
+static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+{
+ __asm__ __volatile__("swap [%1], %0\n\t" :
+ "=&r" (val), "=&r" (ptr) :
+ "0" (val), "1" (ptr));
+ return val;
+}
+
+static void smp_setup_percpu_timer(void);
+extern void cpu_probe(void);
+extern void sun4d_distribute_irqs(void);
+
+__initfunc(void smp4d_callin(void))
+{
+ int cpuid = hard_smp4d_processor_id();
+ extern spinlock_t sun4d_imsk_lock;
+ unsigned long flags;
+
+ /* Show we are alive */
+ cpu_leds[cpuid] = 0x6;
+ show_leds(cpuid);
+
+ /* Enable level15 interrupt, disable level14 interrupt for now */
+ cc_set_imsk((cc_get_imsk() & ~0x8000) | 0x4000);
+
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ /* Get our local ticker going. */
+ smp_setup_percpu_timer();
+
+ calibrate_delay();
+ smp_store_cpu_info(cpuid);
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ /* Allow master to continue. */
+ swap((unsigned long *)&cpu_callin_map[cpuid], 1);
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ cpu_probe();
+
+ while((unsigned long)current_set[cpuid] < PAGE_OFFSET)
+ barrier();
+
+ while(current_set[cpuid]->processor != cpuid)
+ barrier();
+
+ /* Fix idle thread fields. */
+ __asm__ __volatile__("ld [%0], %%g6\n\t"
+ "sta %%g6, [%%g0] %1\n\t"
+ : : "r" (&current_set[cpuid]), "i" (ASI_M_VIKING_TMP2)
+ : "memory" /* paranoid */);
+
+ cpu_leds[cpuid] = 0x9;
+ show_leds(cpuid);
+
+ current->mm->mmap->vm_page_prot = PAGE_SHARED;
+ current->mm->mmap->vm_start = PAGE_OFFSET;
+ current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ __sti(); /* We don't allow PIL 14 yet */
+
+ while(!smp_commenced)
+ barrier();
+
+ spin_lock_irqsave(&sun4d_imsk_lock, flags);
+ cc_set_imsk(cc_get_imsk() & ~0x4000); /* Allow PIL 14 as well */
+ spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+}
+
+extern int cpu_idle(void *unused);
+extern void init_IRQ(void);
+extern void cpu_panic(void);
+extern int start_secondary(void *unused);
+
+/*
+ * Cycle through the processors asking the PROM to start each one.
+ */
+
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+extern struct linux_prom_registers smp_penguin_ctable;
+extern unsigned long trapbase_cpu1[];
+extern unsigned long trapbase_cpu2[];
+extern unsigned long trapbase_cpu3[];
+
+__initfunc(void smp4d_boot_cpus(void))
+{
+ int cpucount = 0;
+ int i = 0;
+
+ printk("Entering SMP Mode...\n");
+
+ smp_penguin_ctable.which_io = 0;
+ smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
+ smp_penguin_ctable.reg_size = 0;
+
+ for (i = 0; i < NR_CPUS; i++)
+ cpu_offset[i] = (char *)&cpu_data[i] - (char *)&cpu_data;
+
+ if (boot_cpu_id)
+ current_set[0] = NULL;
+
+ __sti();
+ cpu_present_map = 0;
+ for(i=0; i < linux_num_cpus; i++)
+ cpu_present_map |= (1<<linux_cpus[i].mid);
+ SMP_PRINTK(("cpu_present_map %08lx\n", cpu_present_map));
+ for(i=0; i < NR_CPUS; i++)
+ cpu_number_map[i] = -1;
+ for(i=0; i < NR_CPUS; i++)
+ __cpu_logical_map[i] = -1;
+ for(i=0; i < NR_CPUS; i++)
+ mid_xlate[i] = i;
+ cpu_number_map[boot_cpu_id] = 0;
+ __cpu_logical_map[0] = boot_cpu_id;
+ klock_info.akp = boot_cpu_id;
+ current->processor = boot_cpu_id;
+ smp_store_cpu_info(boot_cpu_id);
+ smp_setup_percpu_timer();
+ local_flush_cache_all();
+ if(linux_num_cpus == 1)
+ return; /* Not an MP box. */
+ SMP_PRINTK(("Iterating over CPUs\n"));
+ for(i = 0; i < NR_CPUS; i++) {
+ if(i == boot_cpu_id)
+ continue;
+
+ if(cpu_present_map & (1 << i)) {
+ extern unsigned long sun4d_cpu_startup;
+ unsigned long *entry = &sun4d_cpu_startup;
+ struct task_struct *p;
+ int timeout;
+ int no;
+
+ /* Cook up an idler for this guy. */
+ kernel_thread(start_secondary, NULL, CLONE_PID);
+
+ p = task[++cpucount];
+
+ p->processor = i;
+ current_set[i] = p;
+
+ for (no = 0; no < linux_num_cpus; no++)
+ if (linux_cpus[no].mid == i)
+ break;
+
+ /* whirrr, whirrr, whirrrrrrrrr... */
+ SMP_PRINTK(("Starting CPU %d at %p task %d node %08x\n", i, entry, cpucount, linux_cpus[no].prom_node));
+ local_flush_cache_all();
+ prom_startcpu(linux_cpus[no].prom_node,
+ &smp_penguin_ctable, 0, (char *)entry);
+
+ SMP_PRINTK(("prom_startcpu returned :)\n"));
+
+ /* wheee... it's going... */
+ for(timeout = 0; timeout < 5000000; timeout++) {
+ if(cpu_callin_map[i])
+ break;
+ udelay(100);
+ }
+
+ if(cpu_callin_map[i]) {
+ /* Another "Red Snapper". */
+ cpu_number_map[i] = cpucount;
+ __cpu_logical_map[cpucount] = i;
+ } else {
+ cpucount--;
+ printk("Processor %d is stuck.\n", i);
+ }
+ }
+ if(!(cpu_callin_map[i])) {
+ cpu_present_map &= ~(1 << i);
+ cpu_number_map[i] = -1;
+ }
+ }
+ local_flush_cache_all();
+ if(cpucount == 0) {
+ printk("Error: only one Processor found.\n");
+ cpu_present_map = (1 << hard_smp4d_processor_id());
+ } else {
+ unsigned long bogosum = 0;
+
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i)) {
+ bogosum += cpu_data[i].udelay_val;
+ smp_highest_cpu = i;
+ }
+ }
+ SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, (bogosum + 2500)/500000, ((bogosum + 2500)/5000)%100));
+ printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
+ cpucount + 1,
+ (bogosum + 2500)/500000,
+ ((bogosum + 2500)/5000)%100);
+ smp_activated = 1;
+ smp_num_cpus = cpucount + 1;
+ }
+
+ /* Free unneeded trap tables */
+
+ mem_map[MAP_NR((unsigned long)trapbase_cpu1)].flags &= ~(1 << PG_reserved);
+ free_page((unsigned long)trapbase_cpu1);
+ mem_map[MAP_NR((unsigned long)trapbase_cpu2)].flags &= ~(1 << PG_reserved);
+ free_page((unsigned long)trapbase_cpu2);
+ mem_map[MAP_NR((unsigned long)trapbase_cpu3)].flags &= ~(1 << PG_reserved);
+ free_page((unsigned long)trapbase_cpu3);
+
+ /* Ok, they are spinning and ready to go. */
+ smp_processors_ready = 1;
+ sun4d_distribute_irqs();
+}
+
+static struct smp_funcall {
+ smpfunc_t func;
+ unsigned long arg1;
+ unsigned long arg2;
+ unsigned long arg3;
+ unsigned long arg4;
+ unsigned long arg5;
+ unsigned char processors_in[NR_CPUS]; /* Set when ipi entered. */
+ unsigned char processors_out[NR_CPUS]; /* Set when ipi exited. */
+} ccall_info __attribute__((aligned(8)));
+
+static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
+
+/* Cross calls must be serialized, at least currently. */
+void smp4d_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+ if(smp_processors_ready) {
+ register int high = smp_highest_cpu;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cross_call_lock, flags);
+
+ {
+ /* If you make changes here, make sure gcc generates proper code... */
+ smpfunc_t f asm("i0") = func;
+ unsigned long a1 asm("i1") = arg1;
+ unsigned long a2 asm("i2") = arg2;
+ unsigned long a3 asm("i3") = arg3;
+ unsigned long a4 asm("i4") = arg4;
+ unsigned long a5 asm("i5") = arg5;
+
+ __asm__ __volatile__("
+ std %0, [%6]
+ std %2, [%6 + 8]
+ std %4, [%6 + 16]" : :
+ "r"(f), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
+ "r" (&ccall_info.func));
+ }
+
+ /* Init receive/complete mapping, plus fire the IPI's off. */
+ {
+ register unsigned long mask;
+ register int i;
+
+ mask = (cpu_present_map & ~(1 << hard_smp4d_processor_id()));
+ for(i = 0; i <= high; i++) {
+ if(mask & (1 << i)) {
+ ccall_info.processors_in[i] = 0;
+ ccall_info.processors_out[i] = 0;
+ sun4d_send_ipi(i, IRQ_CROSS_CALL);
+ }
+ }
+ }
+
+ /* First, run local copy. */
+ func(arg1, arg2, arg3, arg4, arg5);
+
+ {
+ register int i;
+
+ i = 0;
+ do {
+ while(!ccall_info.processors_in[i])
+ barrier();
+ } while(++i <= high);
+
+ i = 0;
+ do {
+ while(!ccall_info.processors_out[i])
+ barrier();
+ } while(++i <= high);
+ }
+
+ spin_unlock_irqrestore(&cross_call_lock, flags);
+ } else
+ func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */
+}
+
+/* Running cross calls. */
+void smp4d_cross_call_irq(void)
+{
+ int i = hard_smp4d_processor_id();
+
+ ccall_info.processors_in[i] = 1;
+ ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+ ccall_info.arg4, ccall_info.arg5);
+ ccall_info.processors_out[i] = 1;
+}
+
+static int smp4d_stop_cpu_sender;
+
+static void smp4d_stop_cpu(void)
+{
+ int me = hard_smp4d_processor_id();
+
+ if (me != smp4d_stop_cpu_sender)
+ while(1) barrier();
+}
+
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
+ */
+void smp4d_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ int me = hard_smp4d_processor_id();
+
+ SMP_PRINTK(("smp4d_message_pass %d %d %08lx %d\n", target, msg, data, wait));
+ if (msg == MSG_STOP_CPU && target == MSG_ALL_BUT_SELF) {
+ unsigned long flags;
+ static spinlock_t stop_cpu_lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_irqsave(&stop_cpu_lock, flags);
+ smp4d_stop_cpu_sender = me;
+ smp4d_cross_call((smpfunc_t)smp4d_stop_cpu, 0, 0, 0, 0, 0);
+ spin_unlock_irqrestore(&stop_cpu_lock, flags);
+ }
+ printk("Yeeee, trying to send SMP msg(%d) to %d on cpu %d\n", msg, target, me);
+ panic("Bogon SMP message pass.");
+}
+
+/* Protects counters touched during level14 ticker */
+static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_PROFILE
+
+/* 32-bit Sparc specific profiling function. */
+static inline void sparc_do_profile(unsigned long pc)
+{
+ if(prof_buffer && current->pid) {
+ extern int _stext;
+
+ pc -= (unsigned long) &_stext;
+ pc >>= prof_shift;
+
+ spin_lock(&ticker_lock);
+ if(pc < prof_len)
+ prof_buffer[pc]++;
+ else
+ prof_buffer[prof_len - 1]++;
+ spin_unlock(&ticker_lock);
+ }
+}
+
+#endif
+
+extern unsigned int prof_multiplier[NR_CPUS];
+extern unsigned int prof_counter[NR_CPUS];
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+ unsigned long user, unsigned long system,
+ int cpu);
+
+
+void smp4d_percpu_timer_interrupt(struct pt_regs *regs)
+{
+ int cpu = hard_smp4d_processor_id();
+ static int cpu_tick[NR_CPUS];
+ static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd };
+
+ bw_get_prof_limit(cpu);
+ bw_clear_intr_mask(0, 1); /* INTR_TABLE[0] & 1 is Profile IRQ */
+
+ cpu_tick[cpu]++;
+ if (!(cpu_tick[cpu] & 15)) {
+ if (cpu_tick[cpu] == 0x60)
+ cpu_tick[cpu] = 0;
+ cpu_leds[cpu] = led_mask[cpu_tick[cpu] >> 4];
+ show_leds(cpu);
+ }
+
+#ifdef CONFIG_PROFILE
+ if(!user_mode(regs))
+ sparc_do_profile(regs->pc);
+#endif
+ if(!--prof_counter[cpu]) {
+ int user = user_mode(regs);
+ if(current->pid) {
+ update_one_process(current, 1, user, !user, cpu);
+
+ if(--current->counter < 0) {
+ current->counter = 0;
+ need_resched = 1;
+ }
+
+ spin_lock(&ticker_lock);
+ if(user) {
+ if(current->priority < DEF_PRIORITY) {
+ kstat.cpu_nice++;
+ kstat.per_cpu_nice[cpu]++;
+ } else {
+ kstat.cpu_user++;
+ kstat.per_cpu_user[cpu]++;
+ }
+ } else {
+ kstat.cpu_system++;
+ kstat.per_cpu_system[cpu]++;
+ }
+ spin_unlock(&ticker_lock);
+ }
+ prof_counter[cpu] = prof_multiplier[cpu];
+ }
+}
+
+extern unsigned int lvl14_resolution;
+
+__initfunc(static void smp_setup_percpu_timer(void))
+{
+ int cpu = hard_smp4d_processor_id();
+
+ prof_counter[cpu] = prof_multiplier[cpu] = 1;
+ load_profile_irq(cpu, lvl14_resolution);
+}
+
+__initfunc(void smp4d_blackbox_id(unsigned *addr))
+{
+ int rd = *addr & 0x3e000000;
+
+ addr[0] = 0xc0800800 | rd; /* lda [%g0] ASI_M_VIKING_TMP1, reg */
+ addr[1] = 0x01000000; /* nop */
+ addr[2] = 0x01000000; /* nop */
+}
+
+__initfunc(void smp4d_blackbox_current(unsigned *addr))
+{
+ /* We have a nice Linux current register :) */
+ int rd = addr[1] & 0x3e000000;
+
+ addr[0] = 0x10800006; /* b .+24 */
+ addr[1] = 0xc0800820 | rd; /* lda [%g0] ASI_M_VIKING_TMP2, reg */
+}
+
+__initfunc(void sun4d_init_smp(void))
+{
+ int i;
+ extern unsigned int patchme_store_new_current[];
+ extern unsigned int t_nmi[], linux_trap_ipi15_sun4d[], linux_trap_ipi15_sun4m[];
+
+ /* Store current into Linux current register :) */
+ __asm__ __volatile__("sta %%g6, [%%g0] %0" : : "i"(ASI_M_VIKING_TMP2));
+
+ /* Patch switch_to */
+ patchme_store_new_current[0] = (patchme_store_new_current[0] & 0x3e000000) | 0xc0a00820;
+
+ /* Patch ipi15 trap table */
+ t_nmi[1] = t_nmi[1] + (linux_trap_ipi15_sun4d - linux_trap_ipi15_sun4m);
+
+ /* And set btfixup... */
+ BTFIXUPSET_BLACKBOX(smp_processor_id, smp4d_blackbox_id);
+ BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current);
+ BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(smp_message_pass, smp4d_message_pass, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(smp_bogo_info, smp4d_bogo_info, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(smp_info, smp4d_info, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(__smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM);
+
+ for (i = 0; i < NR_CPUS; i++) {
+ ccall_info.processors_in[i] = 1;
+ ccall_info.processors_out[i] = 1;
+ }
+}
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
index 81db1a4ce..e55839a7a 100644
--- a/arch/sparc/kernel/sun4m_irq.c
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -83,9 +83,9 @@ inline unsigned long sun4m_get_irqmask(unsigned int irq)
if (!mask)
printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
} else {
- /* Soft Interrupts will come here
- * Currently there is no way to trigger them but I'm sure something
- * could be cooked up.
+ /* Soft Interrupts will come here.
+ * Currently there is no way to trigger them but I'm sure
+ * something could be cooked up.
*/
irq &= 0xf;
mask = SUN4M_SOFT_INT(irq);
@@ -349,18 +349,18 @@ __initfunc(void sun4m_init_IRQ(void))
&sun4m_interrupts->undirected_target;
sun4m_interrupts->undirected_target = 0;
}
- enable_irq = sun4m_enable_irq;
- disable_irq = sun4m_disable_irq;
- enable_pil_irq = sun4m_enable_pil_irq;
- disable_pil_irq = sun4m_disable_pil_irq;
- clear_clock_irq = sun4m_clear_clock_irq;
- clear_profile_irq = sun4m_clear_profile_irq;
- load_profile_irq = sun4m_load_profile_irq;
+ BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
init_timers = sun4m_init_timers;
#ifdef __SMP__
- set_cpu_int = (void (*) (int, int))sun4m_send_ipi;
- clear_cpu_int = (void (*) (int, int))sun4m_clear_ipi;
- set_irq_udt = (void (*) (int))sun4m_set_udt;
+ BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
#endif
/* Cannot enable interrupts until OBP ticker is disabled. */
}
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
new file mode 100644
index 000000000..ec1ef424b
--- /dev/null
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -0,0 +1,545 @@
+/* sun4m_smp.c: Sparc SUN4M SMP support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h> /* for CONFIG_PROFILE */
+#include <asm/head.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/atops.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+#define IRQ_RESCHEDULE 13
+#define IRQ_STOP_CPU 14
+#define IRQ_CROSS_CALL 15
+
+extern ctxd_t *srmmu_ctx_table_phys;
+extern int linux_num_cpus;
+
+extern void calibrate_delay(void);
+
+extern struct task_struct *current_set[NR_CPUS];
+extern volatile int smp_processors_ready;
+extern unsigned long cpu_present_map;
+extern int smp_num_cpus;
+extern int smp_threads_ready;
+extern unsigned char mid_xlate[NR_CPUS];
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
+extern unsigned long smp_proc_in_lock[NR_CPUS];
+extern struct cpuinfo_sparc cpu_data[NR_CPUS];
+extern unsigned long cpu_offset[NR_CPUS];
+extern unsigned char boot_cpu_id;
+extern int smp_activated;
+extern volatile int cpu_number_map[NR_CPUS];
+extern volatile int __cpu_logical_map[NR_CPUS];
+extern struct klock_info klock_info;
+extern volatile unsigned long ipi_count;
+extern volatile int smp_process_available;
+extern volatile int smp_commenced;
+extern int __smp4m_processor_id(void);
+
+/*#define SMP_DEBUG*/
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x) printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
+int smp4m_bogo_info(char *buf)
+{
+ return sprintf(buf,
+ "Cpu0Bogo\t: %lu.%02lu\n"
+ "Cpu1Bogo\t: %lu.%02lu\n"
+ "Cpu2Bogo\t: %lu.%02lu\n"
+ "Cpu3Bogo\t: %lu.%02lu\n",
+ cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
+ cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
+ cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
+ cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100);
+}
+
+int smp4m_info(char *buf)
+{
+ return sprintf(buf,
+" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
+"State: %s\t\t%s\t\t%s\t\t%s\n",
+(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
+(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
+(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
+(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
+}
+
+static inline unsigned long swap(volatile unsigned long *ptr, unsigned long val)
+{
+ __asm__ __volatile__("swap [%1], %0\n\t" :
+ "=&r" (val), "=&r" (ptr) :
+ "0" (val), "1" (ptr));
+ return val;
+}
+
+static void smp_setup_percpu_timer(void);
+extern void cpu_probe(void);
+
+__initfunc(void smp4m_callin(void))
+{
+ int cpuid = hard_smp_processor_id();
+
+ local_flush_cache_all();
+ local_flush_tlb_all();
+ set_irq_udt(mid_xlate[boot_cpu_id]);
+
+ /* Get our local ticker going. */
+ smp_setup_percpu_timer();
+
+ calibrate_delay();
+ smp_store_cpu_info(cpuid);
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ /* Allow master to continue. */
+ swap((unsigned long *)&cpu_callin_map[cpuid], 1);
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ cpu_probe();
+
+ while(!task[cpuid] || current_set[cpuid] != task[cpuid])
+ barrier();
+
+ /* Fix idle thread fields. */
+ __asm__ __volatile__("ld [%0], %%g6\n\t"
+ : : "r" (&current_set[cpuid])
+ : "memory" /* paranoid */);
+ current->mm->mmap->vm_page_prot = PAGE_SHARED;
+ current->mm->mmap->vm_start = PAGE_OFFSET;
+ current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+
+ while(!smp_commenced)
+ barrier();
+
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ __sti();
+}
+
+extern int cpu_idle(void *unused);
+extern void init_IRQ(void);
+extern void cpu_panic(void);
+extern int start_secondary(void *unused);
+
+/*
+ * Cycle through the processors asking the PROM to start each one.
+ */
+
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+extern struct linux_prom_registers smp_penguin_ctable;
+extern unsigned long trapbase_cpu1[];
+extern unsigned long trapbase_cpu2[];
+extern unsigned long trapbase_cpu3[];
+
+__initfunc(void smp4m_boot_cpus(void))
+{
+ int cpucount = 0;
+ int i = 0;
+ int first, prev;
+
+ printk("Entering SMP Mode...\n");
+
+ smp_penguin_ctable.which_io = 0;
+ smp_penguin_ctable.phys_addr = (unsigned int) srmmu_ctx_table_phys;
+ smp_penguin_ctable.reg_size = 0;
+
+ for (i = 0; i < NR_CPUS; i++)
+ cpu_offset[i] = (char *)&cpu_data[i] - (char *)&cpu_data;
+
+ __sti();
+ cpu_present_map = 0;
+ for(i=0; i < linux_num_cpus; i++)
+ cpu_present_map |= (1<<i);
+ for(i=0; i < NR_CPUS; i++)
+ cpu_number_map[i] = -1;
+ for(i=0; i < NR_CPUS; i++)
+ __cpu_logical_map[i] = -1;
+ mid_xlate[boot_cpu_id] = (linux_cpus[boot_cpu_id].mid & ~8);
+ cpu_number_map[boot_cpu_id] = 0;
+ __cpu_logical_map[0] = boot_cpu_id;
+ klock_info.akp = boot_cpu_id;
+ current->processor = boot_cpu_id;
+ smp_store_cpu_info(boot_cpu_id);
+ set_irq_udt(mid_xlate[boot_cpu_id]);
+ smp_setup_percpu_timer();
+ local_flush_cache_all();
+ if(linux_num_cpus == 1)
+ return; /* Not an MP box. */
+ for(i = 0; i < NR_CPUS; i++) {
+ if(i == boot_cpu_id)
+ continue;
+
+ if(cpu_present_map & (1 << i)) {
+ extern unsigned long sun4m_cpu_startup;
+ unsigned long *entry = &sun4m_cpu_startup;
+ struct task_struct *p;
+ int timeout;
+
+ /* Cook up an idler for this guy. */
+ kernel_thread(start_secondary, NULL, CLONE_PID);
+
+ p = task[++cpucount];
+
+ p->processor = i;
+ current_set[i] = p;
+
+ /* See trampoline.S for details... */
+ entry += ((i-1) * 3);
+
+ /* whirrr, whirrr, whirrrrrrrrr... */
+ printk("Starting CPU %d at %p\n", i, entry);
+ mid_xlate[i] = (linux_cpus[i].mid & ~8);
+ local_flush_cache_all();
+ prom_startcpu(linux_cpus[i].prom_node,
+ &smp_penguin_ctable, 0, (char *)entry);
+
+ /* wheee... it's going... */
+ for(timeout = 0; timeout < 5000000; timeout++) {
+ if(cpu_callin_map[i])
+ break;
+ udelay(100);
+ }
+ if(cpu_callin_map[i]) {
+ /* Another "Red Snapper". */
+ cpu_number_map[i] = i;
+ __cpu_logical_map[i] = i;
+ } else {
+ cpucount--;
+ printk("Processor %d is stuck.\n", i);
+ }
+ }
+ if(!(cpu_callin_map[i])) {
+ cpu_present_map &= ~(1 << i);
+ cpu_number_map[i] = -1;
+ }
+ }
+ local_flush_cache_all();
+ if(cpucount == 0) {
+ printk("Error: only one Processor found.\n");
+ cpu_present_map = (1 << smp_processor_id());
+ } else {
+ unsigned long bogosum = 0;
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i))
+ bogosum += cpu_data[i].udelay_val;
+ }
+ printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
+ cpucount + 1,
+ (bogosum + 2500)/500000,
+ ((bogosum + 2500)/5000)%100);
+ smp_activated = 1;
+ smp_num_cpus = cpucount + 1;
+ }
+
+ /* Setup CPU list for IRQ distribution scheme. */
+ first = prev = -1;
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i)) {
+ if(first == -1)
+ first = i;
+ if(prev != -1)
+ cpu_data[prev].next = i;
+ cpu_data[i].mid = mid_xlate[i];
+ prev = i;
+ }
+ }
+ cpu_data[prev].next = first;
+
+ /* Free unneeded trap tables */
+
+ if (!(cpu_present_map & (1 << 1))) {
+ mem_map[MAP_NR((unsigned long)trapbase_cpu1)].flags &= ~(1 << PG_reserved);
+ free_page((unsigned long)trapbase_cpu1);
+ }
+ if (!(cpu_present_map & (1 << 2))) {
+ mem_map[MAP_NR((unsigned long)trapbase_cpu2)].flags &= ~(1 << PG_reserved);
+ free_page((unsigned long)trapbase_cpu2);
+ }
+ if (!(cpu_present_map & (1 << 3))) {
+ mem_map[MAP_NR((unsigned long)trapbase_cpu3)].flags &= ~(1 << PG_reserved);
+ free_page((unsigned long)trapbase_cpu3);
+ }
+
+ /* Ok, they are spinning and ready to go. */
+ smp_processors_ready = 1;
+}
+
+/* At each hardware IRQ, we get this called to forward IRQ reception
+ * to the next processor. The caller must disable the IRQ level being
+ * serviced globally so that there are no double interrupts received.
+ */
+void smp4m_irq_rotate(int cpu)
+{
+ if(smp_processors_ready)
+ set_irq_udt(cpu_data[cpu_data[cpu].next].mid);
+}
+
+/* Cross calls, in order to work efficiently and atomically do all
+ * the message passing work themselves, only stopcpu and reschedule
+ * messages come through here.
+ */
+void smp4m_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ static unsigned long smp_cpu_in_msg[NR_CPUS];
+ unsigned long mask;
+ int me = smp_processor_id();
+ int irq, i;
+
+ if(msg == MSG_RESCHEDULE) {
+ irq = IRQ_RESCHEDULE;
+
+ if(smp_cpu_in_msg[me])
+ return;
+ } else if(msg == MSG_STOP_CPU) {
+ irq = IRQ_STOP_CPU;
+ } else {
+ goto barf;
+ }
+
+ smp_cpu_in_msg[me]++;
+ if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
+ mask = cpu_present_map;
+ if(target == MSG_ALL_BUT_SELF)
+ mask &= ~(1 << me);
+ for(i = 0; i < 4; i++) {
+ if(mask & (1 << i))
+ set_cpu_int(mid_xlate[i], irq);
+ }
+ } else {
+ set_cpu_int(mid_xlate[target], irq);
+ }
+ smp_cpu_in_msg[me]--;
+
+ return;
+barf:
+ printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+ panic("Bogon SMP message pass.");
+}
+
+static struct smp_funcall {
+ smpfunc_t func;
+ unsigned long arg1;
+ unsigned long arg2;
+ unsigned long arg3;
+ unsigned long arg4;
+ unsigned long arg5;
+ unsigned long processors_in[NR_CPUS]; /* Set when ipi entered. */
+ unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */
+} ccall_info;
+
+static spinlock_t cross_call_lock = SPIN_LOCK_UNLOCKED;
+
+/* Cross calls must be serialized, at least currently. */
+void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4, unsigned long arg5)
+{
+ if(smp_processors_ready) {
+ register int ncpus = smp_num_cpus;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cross_call_lock, flags);
+
+ /* Init function glue. */
+ ccall_info.func = func;
+ ccall_info.arg1 = arg1;
+ ccall_info.arg2 = arg2;
+ ccall_info.arg3 = arg3;
+ ccall_info.arg4 = arg4;
+ ccall_info.arg5 = arg5;
+
+ /* Init receive/complete mapping, plus fire the IPI's off. */
+ {
+ register unsigned long mask;
+ register int i;
+
+ mask = (cpu_present_map & ~(1 << smp_processor_id()));
+ for(i = 0; i < ncpus; i++) {
+ if(mask & (1 << i)) {
+ ccall_info.processors_in[i] = 0;
+ ccall_info.processors_out[i] = 0;
+ set_cpu_int(mid_xlate[i], IRQ_CROSS_CALL);
+ } else {
+ ccall_info.processors_in[i] = 1;
+ ccall_info.processors_out[i] = 1;
+ }
+ }
+ }
+
+ /* First, run local copy. */
+ func(arg1, arg2, arg3, arg4, arg5);
+
+ {
+ register int i;
+
+ i = 0;
+ do {
+ while(!ccall_info.processors_in[i])
+ barrier();
+ } while(++i < ncpus);
+
+ i = 0;
+ do {
+ while(!ccall_info.processors_out[i])
+ barrier();
+ } while(++i < ncpus);
+ }
+
+ spin_unlock_irqrestore(&cross_call_lock, flags);
+ } else
+ func(arg1, arg2, arg3, arg4, arg5); /* Just need to run local copy. */
+}
+
+/* Running cross calls. */
+void smp4m_cross_call_irq(void)
+{
+ int i = smp_processor_id();
+
+ ccall_info.processors_in[i] = 1;
+ ccall_info.func(ccall_info.arg1, ccall_info.arg2, ccall_info.arg3,
+ ccall_info.arg4, ccall_info.arg5);
+ ccall_info.processors_out[i] = 1;
+}
+
+/* Protects counters touched during level14 ticker */
+static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_PROFILE
+
+/* 32-bit Sparc specific profiling function. */
+static inline void sparc_do_profile(unsigned long pc)
+{
+ if(prof_buffer && current->pid) {
+ extern int _stext;
+
+ pc -= (unsigned long) &_stext;
+ pc >>= prof_shift;
+
+ spin_lock(&ticker_lock);
+ if(pc < prof_len)
+ prof_buffer[pc]++;
+ else
+ prof_buffer[prof_len - 1]++;
+ spin_unlock(&ticker_lock);
+ }
+}
+
+#endif
+
+extern unsigned int prof_multiplier[NR_CPUS];
+extern unsigned int prof_counter[NR_CPUS];
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+ unsigned long user, unsigned long system,
+ int cpu);
+
+void smp4m_percpu_timer_interrupt(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+ clear_profile_irq(mid_xlate[cpu]);
+#ifdef CONFIG_PROFILE
+ if(!user_mode(regs))
+ sparc_do_profile(regs->pc);
+#endif
+ if(!--prof_counter[cpu]) {
+ int user = user_mode(regs);
+ if(current->pid) {
+ update_one_process(current, 1, user, !user, cpu);
+
+ if(--current->counter < 0) {
+ current->counter = 0;
+ need_resched = 1;
+ }
+
+ spin_lock(&ticker_lock);
+ if(user) {
+ if(current->priority < DEF_PRIORITY) {
+ kstat.cpu_nice++;
+ kstat.per_cpu_nice[cpu]++;
+ } else {
+ kstat.cpu_user++;
+ kstat.per_cpu_user[cpu]++;
+ }
+ } else {
+ kstat.cpu_system++;
+ kstat.per_cpu_system[cpu]++;
+ }
+ spin_unlock(&ticker_lock);
+ }
+ prof_counter[cpu] = prof_multiplier[cpu];
+ }
+}
+
+extern unsigned int lvl14_resolution;
+
+__initfunc(static void smp_setup_percpu_timer(void))
+{
+ int cpu = smp_processor_id();
+
+ prof_counter[cpu] = prof_multiplier[cpu] = 1;
+ load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+
+ if(cpu == boot_cpu_id)
+ enable_pil_irq(14);
+}
+
+__initfunc(void smp4m_blackbox_id(unsigned *addr))
+{
+ int rd = *addr & 0x3e000000;
+ int rs1 = rd >> 11;
+
+ addr[0] = 0x81580000 | rd; /* rd %tbr, reg */
+ addr[1] = 0x8130200c | rd | rs1; /* srl reg, 0xc, reg */
+ addr[2] = 0x80082003 | rd | rs1; /* and reg, 3, reg */
+}
+
+__initfunc(void smp4m_blackbox_current(unsigned *addr))
+{
+ int rd = *addr & 0x3e000000;
+ int rs1 = rd >> 11;
+
+ addr[0] = 0x81580000 | rd; /* rd %tbr, reg */
+ addr[2] = 0x8130200a | rd | rs1; /* srl reg, 0xa, reg */
+ addr[4] = 0x8008200c | rd | rs1; /* and reg, 3, reg */
+}
+
+__initfunc(void sun4m_init_smp(void))
+{
+ BTFIXUPSET_BLACKBOX(smp_processor_id, smp4m_blackbox_id);
+ BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current);
+ BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(smp_message_pass, smp4m_message_pass, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(smp_bogo_info, smp4m_bogo_info, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(smp_info, smp4m_info, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(__smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM);
+}
diff --git a/arch/sparc/kernel/sunos_ioctl.c b/arch/sparc/kernel/sunos_ioctl.c
index 4f6b2accc..deb1aa79e 100644
--- a/arch/sparc/kernel/sunos_ioctl.c
+++ b/arch/sparc/kernel/sunos_ioctl.c
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl.c,v 1.29 1997/09/18 10:37:31 rth Exp $
+/* $Id: sunos_ioctl.c,v 1.30 1998/01/21 06:17:32 ecd Exp $
* sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility.
*
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -114,7 +114,7 @@ asmlinkage int sunos_ioctl (int fd, unsigned long cmd, unsigned long arg)
ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg);
goto out;
case _IOW('i', 24, struct ifreq):
- ret = sys_ioctl(fd, SIOCGIFBRDADDR, arg);
+ ret = sys_ioctl(fd, SIOCSIFBRDADDR, arg);
goto out;
case _IOWR('i', 25, struct ifreq):
ret = sys_ioctl(fd, SIOCGIFNETMASK, arg);
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c
index 5b82aa8eb..2fe56b344 100644
--- a/arch/sparc/kernel/sys_sparc.c
+++ b/arch/sparc/kernel/sys_sparc.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.38 1998/01/09 16:42:48 jj Exp $
+/* $Id: sys_sparc.c,v 1.40 1998/03/28 08:29:26 davem Exp $
* linux/arch/sparc/kernel/sys_sparc.c
*
* This file contains various random system calls that
@@ -10,8 +10,9 @@
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/config.h>
-#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
@@ -39,7 +40,7 @@ asmlinkage unsigned long sparc_brk(unsigned long brk)
unsigned long ret;
lock_kernel();
- if(sparc_cpu_model == sun4c) {
+ if(ARCH_SUN4C_SUN4) {
if(brk >= 0x20000000 && brk < 0xe0000000) {
ret = current->mm->brk;
goto out;
@@ -192,31 +193,34 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
lock_kernel();
if (!(flags & MAP_ANONYMOUS)) {
- if (fd >= NR_OPEN || !(file = current->files->fd[fd])){
+ file = fget(fd);
+ if (!file)
goto out;
- }
}
retval = -ENOMEM;
if(!(flags & MAP_FIXED) && !addr) {
addr = get_unmapped_area(addr, len);
- if(!addr){
- goto out;
- }
+ if(!addr)
+ goto out_putf;
}
/* See asm-sparc/uaccess.h */
retval = -EINVAL;
if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
- goto out;
+ goto out_putf;
- if(sparc_cpu_model == sun4c) {
+ if(ARCH_SUN4C_SUN4) {
if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
retval = current->mm->brk;
- goto out;
+ goto out_putf;
}
}
retval = do_mmap(file, addr, len, prot, flags, off);
+
+out_putf:
+ if (file)
+ fput(file);
out:
unlock_kernel();
return retval;
diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
index 29f00f6b8..bd7bf5d77 100644
--- a/arch/sparc/kernel/sys_sunos.c
+++ b/arch/sparc/kernel/sys_sunos.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.83 1997/12/14 23:24:28 ecd Exp $
+/* $Id: sys_sunos.c,v 1.87 1998/03/29 03:48:16 shadow Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/resource.h>
#include <linux/ipc.h>
#include <linux/shm.h>
@@ -77,14 +78,19 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len,
flags &= ~MAP_NORESERVE;
}
retval = -EBADF;
- if(!(flags & MAP_ANONYMOUS))
- if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ if(!(flags & MAP_ANONYMOUS)) {
+ if (fd >= SUNOS_NR_OPEN)
goto out;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+
retval = -ENOMEM;
if(!(flags & MAP_FIXED) && !addr) {
addr = get_unmapped_area(addr, len);
if(!addr)
- goto out;
+ goto out_putf;
}
/* If this is ld.so or a shared library doing an mmap
* of /dev/zero, transform it into an anonymous mapping.
@@ -105,18 +111,22 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len,
/* See asm-sparc/uaccess.h */
retval = -EINVAL;
if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
- goto out;
+ goto out_putf;
- if(sparc_cpu_model == sun4c) {
+ if(ARCH_SUN4C_SUN4) {
if(((addr >= 0x20000000) && (addr < 0xe0000000))) {
retval = current->mm->brk;
- goto out;
+ goto out_putf;
}
}
retval = do_mmap(file, addr, len, prot, flags, off);
if(!ret_type)
retval = ((retval < PAGE_OFFSET) ? 0 : retval);
+
+out_putf:
+ if (file)
+ fput(file);
out:
unlock_kernel();
return retval;
@@ -139,7 +149,7 @@ asmlinkage int sunos_brk(unsigned long brk)
unsigned long newbrk, oldbrk;
lock_kernel();
- if(sparc_cpu_model == sun4c) {
+ if(ARCH_SUN4C_SUN4) {
if(brk >= 0x20000000 && brk < 0xe0000000) {
goto out;
}
@@ -423,39 +433,48 @@ static int sunos_filldir(void * __buf, const char * name, int namlen,
asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt)
{
struct file * file;
+ struct inode * inode;
struct sunos_dirent * lastdirent;
struct sunos_dirent_callback buf;
int error = -EBADF;
lock_kernel();
- if(fd >= SUNOS_NR_OPEN)
+ if (fd >= SUNOS_NR_OPEN)
goto out;
- file = current->files->fd[fd];
- if(!file)
+ file = fget(fd);
+ if (!file)
goto out;
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
error = -EINVAL;
- if(cnt < (sizeof(struct sunos_dirent) + 255))
- goto out;
+ if (cnt < (sizeof(struct sunos_dirent) + 255))
+ goto out_putf;
buf.curr = (struct sunos_dirent *) dirent;
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
+
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
error = file->f_op->readdir(file, &buf, sunos_filldir);
+ up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
+
lastdirent = buf.previous;
error = buf.error;
if (lastdirent) {
put_user(file->f_pos, &lastdirent->d_off);
error = cnt - buf.count;
}
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -503,39 +522,48 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen,
asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep)
{
struct file * file;
+ struct inode * inode;
struct sunos_direntry * lastdirent;
struct sunos_direntry_callback buf;
int error = -EBADF;
lock_kernel();
- if(fd >= SUNOS_NR_OPEN)
+ if (fd >= SUNOS_NR_OPEN)
goto out;
- file = current->files->fd[fd];
- if(!file)
+ file = fget(fd);
+ if (!file)
goto out;
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
error = -EINVAL;
if(cnt < (sizeof(struct sunos_direntry) + 255))
- goto out;
+ goto out_putf;
buf.curr = (struct sunos_direntry *) dirent;
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
+
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
error = file->f_op->readdir(file, &buf, sunos_filldirentry);
+ up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
+
lastdirent = buf.previous;
error = buf.error;
if (lastdirent) {
put_user(file->f_pos, basep);
error = cnt - buf.count;
}
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -669,6 +697,15 @@ asmlinkage int sunos_select(int width, fd_set *inp, fd_set *outp, fd_set *exp, s
lock_kernel();
current->personality |= STICKY_TIMEOUTS;
ret = sys_select (width, inp, outp, exp, tvp);
+ if (ret == -EINTR && tvp) {
+ time_t sec, usec;
+
+ __get_user(sec, &tvp->tv_sec);
+ __get_user(usec, &tvp->tv_usec);
+
+ if (sec == 0 && usec == 0)
+ ret = 0;
+ }
unlock_kernel();
return ret;
}
@@ -720,7 +757,7 @@ extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen);
/* Bind the socket on a local reserved port and connect it to the
* remote server. This on Linux/i386 is done by the mount program,
- * not by the kernel.
+ * not by the kernel.
*/
static int
sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
@@ -728,16 +765,16 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
struct sockaddr_in local;
struct sockaddr_in server;
int try_port;
- int ret;
struct socket *socket;
- struct dentry *dentry;
struct inode *inode;
struct file *file;
+ int ret, result = 0;
- file = current->files->fd [fd];
- dentry = file->f_dentry;
- if(!dentry || !(inode = dentry->d_inode))
- return 0;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ if (!file->f_dentry || !(inode = file->f_dentry->d_inode))
+ goto out_putf;
socket = &inode->u.socket_i;
local.sin_family = AF_INET;
@@ -752,7 +789,7 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
} while (ret && try_port > (1024 / 2));
if (ret)
- return 0;
+ goto out_putf;
server.sin_family = AF_INET;
server.sin_addr = addr->sin_addr;
@@ -761,9 +798,13 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
/* Call sys_connect */
ret = socket->ops->connect (socket, (struct sockaddr *) &server,
sizeof (server), file->f_flags);
- if (ret < 0)
- return 0;
- return 1;
+ if (ret >= 0)
+ result = 1;
+
+out_putf:
+ fput(file);
+out:
+ return result;
}
static int get_default (int value, int def_value)
@@ -1139,10 +1180,13 @@ asmlinkage int sunos_open(const char *filename, int flags, int mode)
file descriptors that have been set non-blocking
using 4.2BSD style calls. (tridge) */
-static inline int check_nonblock(int ret,int fd)
+static inline int check_nonblock(int ret, int fd)
{
- if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY))
- return -SUNOS_EWOULDBLOCK;
+ if (ret == -EAGAIN) {
+ struct file * file = fcheck(fd);
+ if (file && (file->f_flags & O_NDELAY))
+ ret = -SUNOS_EWOULDBLOCK;
+ }
return ret;
}
@@ -1215,12 +1259,41 @@ asmlinkage int sunos_send(int fd, void * buff, int len, unsigned flags)
return ret;
}
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+ char *optval, int optlen);
+
+asmlinkage int sunos_socket(int family, int type, int protocol)
+{
+ int ret, one = 1;
+
+ lock_kernel();
+ ret = sys_socket(family, type, protocol);
+ if (ret < 0)
+ goto out;
+
+ sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *)&one, sizeof(one));
+out:
+ unlock_kernel();
+ return ret;
+}
+
asmlinkage int sunos_accept(int fd, struct sockaddr *sa, int *addrlen)
{
- int ret;
+ int ret, one = 1;
lock_kernel();
- ret = check_nonblock(sys_accept(fd,sa,addrlen),fd);
+ while (1) {
+ ret = check_nonblock(sys_accept(fd,sa,addrlen),fd);
+ if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
+ break;
+ }
+ if (ret < 0)
+ goto out;
+
+ sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *)&one, sizeof(one));
+out:
unlock_kernel();
return ret;
}
diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S
index 5ea64d2a4..320264255 100644
--- a/arch/sparc/kernel/systbls.S
+++ b/arch/sparc/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.68 1997/12/24 17:26:38 ecd Exp $
+/* $Id: systbls.S,v 1.71 1998/03/24 06:25:06 ecd Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
@@ -23,9 +23,9 @@ C_LABEL(sys_call_table):
/*5*/ .long C_LABEL(sys_open), C_LABEL(sys_close), C_LABEL(sys_wait4)
.long C_LABEL(sys_creat), C_LABEL(sys_link)
/*10*/ .long C_LABEL(sys_unlink), C_LABEL(sunos_execv), C_LABEL(sys_chdir)
- .long C_LABEL(sys_nis_syscall), C_LABEL(sys_mknod)
-/*15*/ .long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sparc_brk)
- .long C_LABEL(sys_nis_syscall), C_LABEL(sys_lseek)
+ .long C_LABEL(sys_xstat), C_LABEL(sys_mknod)
+/*15*/ .long C_LABEL(sys_chmod), C_LABEL(sys_lchown), C_LABEL(sparc_brk)
+ .long C_LABEL(sys_xmknod), C_LABEL(sys_lseek)
/*20*/ .long C_LABEL(sys_getpid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
.long C_LABEL(sys_setuid), C_LABEL(sys_getuid)
/*25*/ .long C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm)
@@ -137,7 +137,7 @@ C_LABEL(sunos_sys_table):
.long C_LABEL(sys_close), C_LABEL(sunos_wait4), C_LABEL(sys_creat)
.long C_LABEL(sys_link), C_LABEL(sys_unlink), C_LABEL(sunos_execv)
.long C_LABEL(sys_chdir), C_LABEL(sunos_nosys), C_LABEL(sys_mknod)
- .long C_LABEL(sys_chmod), C_LABEL(sys_chown), C_LABEL(sunos_brk)
+ .long C_LABEL(sys_chmod), C_LABEL(sys_lchown), C_LABEL(sunos_brk)
.long C_LABEL(sunos_nosys), C_LABEL(sys_lseek), C_LABEL(sunos_getpid)
.long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys)
.long C_LABEL(sunos_getuid), C_LABEL(sunos_nosys), C_LABEL(sys_ptrace)
@@ -164,7 +164,7 @@ C_LABEL(sunos_sys_table):
.long C_LABEL(sys_getitimer), C_LABEL(sys_gethostname), C_LABEL(sys_sethostname)
.long C_LABEL(sunos_getdtablesize), C_LABEL(sys_dup2), C_LABEL(sunos_nop)
.long C_LABEL(sys_fcntl), C_LABEL(sunos_select), C_LABEL(sunos_nop)
- .long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_socket)
+ .long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sunos_socket)
.long C_LABEL(sys_connect), C_LABEL(sunos_accept)
/*100*/ .long C_LABEL(sys_getpriority), C_LABEL(sunos_send), C_LABEL(sunos_recv)
.long C_LABEL(sunos_nosys), C_LABEL(sys_bind), C_LABEL(sunos_setsockopt)
diff --git a/arch/sparc/kernel/tadpole.c b/arch/sparc/kernel/tadpole.c
index 03fe3cff9..f618bf53b 100644
--- a/arch/sparc/kernel/tadpole.c
+++ b/arch/sparc/kernel/tadpole.c
@@ -6,6 +6,7 @@
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/init.h>
#include <asm/asi.h>
#include <asm/oplib.h>
@@ -94,7 +95,7 @@ static void swift_clockstop(void)
clk_ctrl[0] = 0;
}
-void clock_stop_probe(void)
+__initfunc(void clock_stop_probe(void))
{
unsigned int node, clk_nd;
char name[20];
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c
index 77401391e..5eb49e22c 100644
--- a/arch/sparc/kernel/time.c
+++ b/arch/sparc/kernel/time.c
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.29 1997/04/18 09:48:44 davem Exp $
+/* $Id: time.c,v 1.32 1998/03/23 08:41:13 jj Exp $
* linux/arch/sparc/kernel/time.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -24,6 +24,10 @@
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/sun4paddr.h>
+#include <asm/page.h>
enum sparc_clock_type sp_clock_typ;
struct mostek48t02 *mstk48t02_regs = 0;
@@ -88,7 +92,7 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon,
}
/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
-static void kick_start_clock(void)
+__initfunc(static void kick_start_clock(void))
{
register struct mostek48t02 *regs = mstk48t02_regs;
unsigned char sec;
@@ -137,7 +141,7 @@ static void kick_start_clock(void)
}
/* Return nonzero if the clock chip battery is low. */
-static int has_low_battery(void)
+static __inline__ int has_low_battery(void)
{
register struct mostek48t02 *regs = mstk48t02_regs;
unsigned char data1, data2;
@@ -150,8 +154,24 @@ static int has_low_battery(void)
return (data1 == data2); /* Was the write blocked? */
}
-/* Probe for the real time clock chip. */
-__initfunc(static void clock_probe(void))
+/* Probe for the real time clock chip on Sun4/300. */
+static __inline__ void sun4_clock_probe(void)
+{
+ sp_clock_typ = MSTK48T02;
+ mstk48t02_regs = (struct mostek48t02 *)
+ sparc_alloc_io(SUN4_300_MOSTEK_PHYSADDR, 0,
+ sizeof(*mstk48t02_regs),
+ "clock", 0x0, 0x0);
+ mstk48t08_regs = 0; /* To catch weirdness */
+ /* Kick start the clock if it is completely stopped. */
+ if (mstk48t02_regs->sec & MSTK_STOP) {
+ kick_start_clock();
+ }
+
+}
+
+/* Probe for the mostek real time clock chip. */
+static __inline__ void clock_probe(void)
{
struct linux_prom_registers clk_reg[2];
char model[128];
@@ -247,7 +267,11 @@ __initfunc(void time_init(void))
return;
#endif
- clock_probe();
+ if (ARCH_SUN4)
+ sun4_clock_probe();
+ else
+ clock_probe();
+
init_timers(timer_interrupt);
mregs = mstk48t02_regs;
diff --git a/arch/sparc/kernel/trampoline.S b/arch/sparc/kernel/trampoline.S
index 9ee5bd14a..3515e265d 100644
--- a/arch/sparc/kernel/trampoline.S
+++ b/arch/sparc/kernel/trampoline.S
@@ -1,7 +1,8 @@
-/* $Id: trampoline.S,v 1.9 1997/05/01 08:53:34 davem Exp $
+/* $Id: trampoline.S,v 1.12 1998/03/19 15:36:38 jj Exp $
* trampoline.S: SMP cpu boot-up trampoline code.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <asm/cprefix.h>
@@ -12,9 +13,12 @@
#include <asm/ptrace.h>
#include <asm/vaddrs.h>
#include <asm/contregs.h>
+#include <asm/init.h>
+ .globl C_LABEL(sun4m_cpu_startup), C_LABEL(__smp4m_processor_id)
+ .globl C_LABEL(sun4d_cpu_startup), C_LABEL(__smp4d_processor_id)
- .text
+ __INIT
.align 4
/* When we start up a cpu for the first time it enters this routine.
@@ -22,8 +26,7 @@
* in and sets PIL in %psr to 15, no irqs.
*/
- .globl C_LABEL(sparc_cpu_startup)
-C_LABEL(sparc_cpu_startup):
+C_LABEL(sun4m_cpu_startup):
cpu1_startup:
sethi %hi(C_LABEL(trapbase_cpu1)), %g3
b 1f
@@ -60,9 +63,8 @@ cpu3_startup:
and %g4, 0xc, %g4
ld [%g5 + %g4], %g6
- mov 1, %sp
- sll %sp, (PAGE_SHIFT + 1), %sp
- sub %sp, REGWIN_SZ, %sp
+ sethi %hi(TASK_UNION_SIZE - REGWIN_SZ), %sp
+ or %sp, %lo(TASK_UNION_SIZE - REGWIN_SZ), %sp
add %g6, %sp, %sp
/* Turn on traps (PSR_ET). */
@@ -77,11 +79,84 @@ cpu3_startup:
nop
/* Start this processor. */
- call C_LABEL(smp_callin)
+ call C_LABEL(smp4m_callin)
nop
+ b,a smp_do_cpu_idle
+
+ .text
+ .align 4
+
+smp_do_cpu_idle:
call C_LABEL(cpu_idle)
mov 0, %o0
call C_LABEL(cpu_panic)
nop
+
+C_LABEL(__smp4m_processor_id):
+ rd %tbr, %g2
+ srl %g2, 12, %g2
+ and %g2, 3, %g2
+ retl
+ mov %g1, %o7
+
+C_LABEL(__smp4d_processor_id):
+ lda [%g0] ASI_M_VIKING_TMP1, %g2
+ retl
+ mov %g1, %o7
+
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID 0xf0140000
+
+ __INIT
+ .align 4
+
+C_LABEL(sun4d_cpu_startup):
+ /* Set up a sane %psr -- PIL<0xf> S<0x1> PS<0x1> CWP<0x0> */
+ set (PSR_PIL | PSR_S | PSR_PS), %g1
+ wr %g1, 0x0, %psr ! traps off though
+ WRITE_PAUSE
+
+ /* Our %wim is one behind CWP */
+ mov 2, %g1
+ wr %g1, 0x0, %wim
+ WRITE_PAUSE
+
+ /* Set tbr - we use just one trap table. */
+ set C_LABEL(trapbase), %g1
+ wr %g1, 0x0, %tbr
+ WRITE_PAUSE
+
+ /* Get our CPU id out of bootbus */
+ set SUN4D_BOOTBUS_CPUID, %g3
+ lduba [%g3] ASI_M_CTL, %g3
+ and %g3, 0xf8, %g3
+ srl %g3, 3, %g1
+ sta %g1, [%g0] ASI_M_VIKING_TMP1
+
+ /* Give ourselves a stack and curptr. */
+ set C_LABEL(current_set), %g5
+ srl %g3, 1, %g4
+ ld [%g5 + %g4], %g6
+
+ sethi %hi(TASK_UNION_SIZE - REGWIN_SZ), %sp
+ or %sp, %lo(TASK_UNION_SIZE - REGWIN_SZ), %sp
+ add %g6, %sp, %sp
+
+ /* Turn on traps (PSR_ET). */
+ rd %psr, %g1
+ wr %g1, PSR_ET, %psr ! traps on
+ WRITE_PAUSE
+
+ /* Init our caches, etc. */
+ set C_LABEL(poke_srmmu), %g5
+ ld [%g5], %g5
+ call %g5
+ nop
+
+ /* Start this processor. */
+ call C_LABEL(smp4d_callin)
+ nop
+
+ b,a smp_do_cpu_idle
diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c
index 19a3afbd0..015d05357 100644
--- a/arch/sparc/kernel/traps.c
+++ b/arch/sparc/kernel/traps.c
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.53 1997/01/25 02:43:05 miguel Exp $
+/* $Id: traps.c,v 1.56 1998/04/06 16:08:32 jj Exp $
* arch/sparc/kernel/traps.c
*
* Copyright 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -62,6 +62,14 @@ void sun4m_nmi(struct pt_regs *regs)
prom_halt();
}
+void sun4d_nmi(struct pt_regs *regs)
+{
+ printk("Aieee: sun4d NMI received!\n");
+ printk("you lose buddy boy...\n");
+ show_regs(regs);
+ prom_halt();
+}
+
void instruction_dump (unsigned long *pc)
{
int i;
@@ -229,10 +237,13 @@ static unsigned long fake_fsr;
static unsigned long fake_queue[32] __attribute__ ((aligned (8)));
static unsigned long fake_depth;
+extern int do_mathemu(struct pt_regs *, struct task_struct *);
+
void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
unsigned long psr)
{
static calls = 0;
+ int ret;
#ifndef __SMP__
struct task_struct *fpt = last_task_used_math;
#else
@@ -255,6 +266,40 @@ void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
}
fpsave(&fpt->tss.float_regs[0], &fpt->tss.fsr,
&fpt->tss.fpqueue[0], &fpt->tss.fpqdepth);
+#ifdef DEBUG_FPU
+ printk("Hmm, FP exception, fsr was %016lx\n", fpt->tss.fsr);
+#endif
+
+ switch ((fpt->tss.fsr & 0x1c000)) {
+ /* switch on the contents of the ftt [floating point trap type] field */
+#ifdef DEBUG_FPU
+ case (1 << 14):
+ printk("IEEE_754_exception\n");
+ break;
+#endif
+ case (2 << 14): /* unfinished_FPop (underflow & co) */
+ case (3 << 14): /* unimplemented_FPop (quad stuff, maybe sqrt) */
+ ret = do_mathemu(regs, fpt);
+ break;
+#ifdef DEBUG_FPU
+ case (4 << 14):
+ printk("sequence_error (OS bug...)\n");
+ break;
+ case (5 << 14):
+ printk("hardware_error (uhoh!)\n");
+ break;
+ case (6 << 14):
+ printk("invalid_fp_register (user error)\n");
+ break;
+#endif /* DEBUG_FPU */
+ }
+ /* If we successfully emulated the FPop, we pretend the trap never happened :-> */
+ if (ret) {
+ fpload(&current->tss.float_regs[0], &current->tss.fsr);
+ return;
+ }
+ /* nope, better SIGFPE the offending process... */
+
fpt->tss.sig_address = pc;
fpt->tss.sig_desc = SUBSIG_FPERROR; /* as good as any */
#ifdef __SMP__
@@ -331,19 +376,6 @@ void handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long np
unlock_kernel();
}
-void handle_bad_flush(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- lock_kernel();
-#ifdef TRAP_DEBUG
- printk("Unimplemented FLUSH Exception at PC %08lx NPC %08lx PSR %08lx\n",
- pc, npc, psr);
-#endif
- printk("INSTRUCTION=%08lx\n", *((unsigned long *) regs->pc));
- send_sig(SIGILL, current, 1);
- unlock_kernel();
-}
-
void handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc,
unsigned long psr)
{
diff --git a/arch/sparc/kernel/wof.S b/arch/sparc/kernel/wof.S
index 890676bfb..1d321d521 100644
--- a/arch/sparc/kernel/wof.S
+++ b/arch/sparc/kernel/wof.S
@@ -1,4 +1,4 @@
-/* $Id: wof.S,v 1.36 1997/05/01 08:53:35 davem Exp $
+/* $Id: wof.S,v 1.38 1998/02/06 14:14:22 jj Exp $
* wof.S: Sparc window overflow handler.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -234,9 +234,10 @@ spwin_user_stack_is_bolixed:
spnwin_patch3: and %twin_tmp, 0xff, %twin_tmp ! patched on 7win Sparcs
st %twin_tmp, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]
- mov 1, %sp
- sll %sp, (PAGE_SHIFT + 1), %sp
- sub %sp, (TRACEREG_SZ + REGWIN_SZ), %sp
+#define STACK_OFFSET (TASK_UNION_SIZE - TRACEREG_SZ - REGWIN_SZ)
+
+ sethi %hi(STACK_OFFSET), %sp
+ or %sp, %lo(STACK_OFFSET), %sp
add %curptr, %sp, %sp
/* Restore the saved globals and build a pt_regs frame. */
@@ -244,9 +245,8 @@ spnwin_patch3: and %twin_tmp, 0xff, %twin_tmp ! patched on 7win Sparcs
mov %saved_g6, %g6
STORE_PT_ALL(sp, t_psr, t_pc, t_npc, g1)
- mov 1, %g6
- sll %g6, (PAGE_SHIFT + 1), %g6
- sub %g6, (TRACEREG_SZ + REGWIN_SZ), %g6
+ sethi %hi(STACK_OFFSET), %g6
+ or %g6, %lo(STACK_OFFSET), %g6
sub %sp, %g6, %g6
/* Turn on traps and call c-code to deal with it. */
@@ -394,9 +394,8 @@ C_LABEL(spwin_srmmu_stackchk):
* kernel is page aligned, which should always be the case.
*/
/* Check results of callers andcc %sp, 0x7, %g0 */
- sethi %hi(C_LABEL(page_offset)), %glob_tmp
bne spwin_user_stack_is_bolixed
- ld [%glob_tmp + %lo(C_LABEL(page_offset))], %glob_tmp
+ GET_PAGE_OFFSET(glob_tmp)
cmp %glob_tmp, %sp
bleu spwin_user_stack_is_bolixed
mov AC_M_SFSR, %glob_tmp
diff --git a/arch/sparc/kernel/wuf.S b/arch/sparc/kernel/wuf.S
index cb407aa69..bfa5b5191 100644
--- a/arch/sparc/kernel/wuf.S
+++ b/arch/sparc/kernel/wuf.S
@@ -1,4 +1,4 @@
-/* $Id: wuf.S,v 1.34 1997/05/01 08:53:36 davem Exp $
+/* $Id: wuf.S,v 1.37 1998/02/19 21:25:50 ecd Exp $
* wuf.S: Window underflow trap handler for the Sparc.
*
* Copyright (C) 1995 David S. Miller
@@ -138,6 +138,8 @@ fwin_from_user:
C_LABEL(fwin_mmu_patchme): b C_LABEL(sun4c_fwin_stackchk)
andcc %sp, 0x7, %g0
+#define STACK_OFFSET (TASK_UNION_SIZE - TRACEREG_SZ - REGWIN_SZ)
+
fwin_user_stack_is_bolixed:
/* LOCATION: Window 'W' */
@@ -146,9 +148,8 @@ fwin_user_stack_is_bolixed:
*/
LOAD_CURRENT(l4, l5)
- mov 1, %l5
- sll %l5, (PAGE_SHIFT + 1), %l5
- sub %l5, (TRACEREG_SZ + REGWIN_SZ), %l5
+ sethi %hi(STACK_OFFSET), %l5
+ or %l5, %lo(STACK_OFFSET), %l5
add %l4, %l5, %l5
/* Store globals into pt_regs frame. */
@@ -169,10 +170,9 @@ fwin_user_stack_is_bolixed:
/* LOCATION: Window 'T' */
- mov 1, %sp
- sll %sp, (PAGE_SHIFT + 1), %sp
- sub %sp, (TRACEREG_SZ + REGWIN_SZ), %sp
- add %curptr, %sp, %sp
+ sethi %hi(STACK_OFFSET), %l5
+ or %l5, %lo(STACK_OFFSET), %l5
+ add %curptr, %l5, %sp
/* Build rest of pt_regs. */
STORE_PT_INS(sp)
@@ -299,9 +299,8 @@ C_LABEL(srmmu_fwin_stackchk):
/* LOCATION: Window 'W' */
/* Caller did 'andcc %sp, 0x7, %g0' */
- sethi %hi(C_LABEL(page_offset)), %l5
bne fwin_user_stack_is_bolixed
- ld [%l5 + %lo(C_LABEL(page_offset))], %l5
+ GET_PAGE_OFFSET(l5)
/* Check if the users stack is in kernel vma, then our
* trial and error technique below would succeed for
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index cefe7a851..6ec986c86 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.24 1997/05/08 17:45:26 davem Exp $
+# $Id: Makefile,v 1.25 1998/01/30 10:58:43 jj Exp $
# Makefile for Sparc library files..
#
@@ -16,19 +16,19 @@ lib.a: $(OBJS)
sync
checksum.o: checksum.S
- $(CC) -ansi -c -o checksum.o checksum.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S
memcpy.o: memcpy.S
$(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S
memcmp.o: memcmp.S
- $(CC) -ansi -c -o memcmp.o memcmp.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S
memscan.o: memscan.S
- $(CC) -ansi -c -o memscan.o memscan.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S
strncmp.o: strncmp.S
- $(CC) -ansi -c -o strncmp.o strncmp.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S
strncpy_from_user.o: strncpy_from_user.S
$(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S
@@ -40,7 +40,7 @@ copy_user.o: copy_user.S
$(CC) -D__ASSEMBLY__ -ansi -c -o copy_user.o copy_user.S
blockops.o: blockops.S
- $(CC) -ansi -c -o blockops.o blockops.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o blockops.o blockops.S
memset.o: memset.S
$(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S
@@ -73,34 +73,34 @@ bitops.o: bitops.S
endif
strlen.o: strlen.S
- $(CC) -ansi -c -o strlen.o strlen.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S
divdi3.o: divdi3.S
- $(CC) -ansi -c -o divdi3.o divdi3.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o divdi3.o divdi3.S
udivdi3.o: udivdi3.S
- $(CC) -ansi -c -o udivdi3.o udivdi3.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o udivdi3.o udivdi3.S
mul.o: mul.S
- $(CC) -c -o mul.o mul.S
+ $(CC) -D__ASSEMBLY__ -c -o mul.o mul.S
rem.o: rem.S
- $(CC) -DST_DIV0=0x2 -c -o rem.o rem.S
+ $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o rem.o rem.S
sdiv.o: sdiv.S
- $(CC) -DST_DIV0=0x2 -c -o sdiv.o sdiv.S
+ $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o sdiv.o sdiv.S
udiv.o: udiv.S
- $(CC) -DST_DIV0=0x2 -c -o udiv.o udiv.S
+ $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o udiv.o udiv.S
umul.o: umul.S
- $(CC) -c -o umul.o umul.S
+ $(CC) -D__ASSEMBLY__ -c -o umul.o umul.S
urem.o: urem.S
- $(CC) -DST_DIV0=0x2 -c -o urem.o urem.S
+ $(CC) -D__ASSEMBLY__ -DST_DIV0=0x2 -c -o urem.o urem.S
ashrdi3.o: ashrdi3.S
- $(CC) -c -o ashrdi3.o ashrdi3.S
+ $(CC) -D__ASSEMBLY__ -c -o ashrdi3.o ashrdi3.S
dep:
diff --git a/arch/sparc/lib/atomic.S b/arch/sparc/lib/atomic.S
index 89bf2c5fb..4e4aa0646 100644
--- a/arch/sparc/lib/atomic.S
+++ b/arch/sparc/lib/atomic.S
@@ -10,8 +10,9 @@
.text
.align 4
- .globl ___xchg32
-___xchg32:
+#ifndef __SMP__
+ .globl ___xchg32_sun4c
+___xchg32_sun4c:
rd %psr, %g3
andcc %g3, PSR_PIL, %g0
bne 1f
@@ -27,9 +28,16 @@ ___xchg32:
nop; nop; nop
1:
mov %g7, %g2
- jmpl %o7, %g0 /* Note, not + 0x8, see call in system.h */
+ jmpl %o7 + 8, %g0
mov %g4, %o7
+ .globl ___xchg32_sun4md
+___xchg32_sun4md:
+ swap [%g1], %g2
+ jmpl %o7 + 8, %g0
+ mov %g4, %o7
+#endif
+
/* Read asm-sparc/atomic.h carefully to understand how this works for SMP.
* Really, some things here for SMP are overly clever, go read the header.
*/
diff --git a/arch/sparc/lib/blockops.S b/arch/sparc/lib/blockops.S
index a5a4bffad..3f09ec1db 100644
--- a/arch/sparc/lib/blockops.S
+++ b/arch/sparc/lib/blockops.S
@@ -1,10 +1,11 @@
-/* $Id: blockops.S,v 1.7 1997/05/20 07:58:28 jj Exp $
+/* $Id: blockops.S,v 1.8 1998/01/30 10:58:44 jj Exp $
* blockops.S: Common block zero optimized routines.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
*/
#include <asm/cprefix.h>
+#include <asm/page.h>
/* Zero out 64 bytes of memory at (buf + offset).
* Assumes %g1 contains zero.
@@ -53,7 +54,7 @@ C_LABEL(bzero_1page):
/* %o0 = buf */
or %g0, %g0, %g1
or %o0, %g0, %o1
- or %g0, 0x10, %g2
+ or %g0, (PAGE_SIZE >> 8), %g2
1:
BLAST_BLOCK(%o0, 0x00)
BLAST_BLOCK(%o0, 0x40)
@@ -70,7 +71,7 @@ C_LABEL(__copy_1page):
/* NOTE: If you change the number of insns of this routine, please check
* arch/sparc/mm/hypersparc.S */
/* %o0 = dst, %o1 = src */
- or %g0, 0x10, %g1
+ or %g0, (PAGE_SIZE >> 8), %g1
1:
MIRROR_BLOCK(%o0, %o1, 0x00, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
MIRROR_BLOCK(%o0, %o1, 0x20, %o2, %o3, %o4, %o5, %g2, %g3, %g4, %g5)
diff --git a/arch/sparc/math-emu/.cvsignore b/arch/sparc/math-emu/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/arch/sparc/math-emu/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/arch/sparc/math-emu/Makefile b/arch/sparc/math-emu/Makefile
new file mode 100644
index 000000000..d7642b2e9
--- /dev/null
+++ b/arch/sparc/math-emu/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for the FPU instruction emulation.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := math-emu.o
+O_OBJS := math.o ashldi3.o fabss.o faddd.o faddq.o fadds.o \
+ fcmpd.o fcmped.o fcmpeq.o fcmpes.o fcmpq.o fcmps.o \
+ fdivd.o fdivq.o fdivs.o fdmulq.o fdtoi.o fdtoq.o \
+ fdtos.o fitoq.o fmovs.o fmuld.o fmulq.o fmuls.o \
+ fnegs.o fqtod.o fqtoi.o fqtos.o fsmuld.o fsqrtd.o \
+ fsqrtq.o fsqrts.o fstod.o fstoi.o fstoq.o fsubd.o \
+ fsubq.o fsubs.o udivmodti4.o
+
+LINKS := double.h faddd.c faddq.c fadds.c fdivd.c fdivq.c fdivs.c \
+ fdtoi.c fitoq.c fmuld.c fmulq.c fmuls.c fqtoi.c \
+ fsqrtd.c fsqrtq.c fsqrts.c fstoi.c fsubd.c \
+ fsubq.c fsubs.c op-1.h op-2.h op-4.h op-common.h quad.h \
+ single.h soft-fp.h udivmodti4.c
+
+.S.s:
+ $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+
+.S.o:
+ $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+
+include $(TOPDIR)/Rules.make
+
+symlinks:
+ ln -sf $(patsubst %,../../sparc64/math-emu/%,$(LINKS)) .
+
+cleansymlinks:
+ rm -f $(LINKS)
diff --git a/arch/sparc/math-emu/ashldi3.S b/arch/sparc/math-emu/ashldi3.S
new file mode 100644
index 000000000..eab1d0972
--- /dev/null
+++ b/arch/sparc/math-emu/ashldi3.S
@@ -0,0 +1,36 @@
+/* $Id: ashldi3.S,v 1.1 1998/04/06 16:09:28 jj Exp $
+ * ashldi3.S: Math-emu code creates all kinds of references to
+ * this little routine on the sparc with gcc.
+ *
+ * Copyright (C) 1998 Jakub Jelinek(jj@ultra.linux.cz)
+ */
+
+#include <asm/cprefix.h>
+
+ .globl C_LABEL(__ashldi3)
+C_LABEL(__ashldi3):
+ tst %o2
+ be 3f
+ mov 32, %g2
+
+ sub %g2, %o2, %g2
+
+ tst %g2
+ bg 1f
+ srl %o1, %g2, %g3
+
+ clr %o5
+ neg %g2
+ ba 2f
+ sll %o1, %g2, %o4
+
+1:
+ sll %o1, %o2, %o5
+ srl %o0, %o2, %g2
+ or %g2, %g3, %o4
+2:
+ mov %o4, %o0
+ mov %o5, %o1
+3:
+ jmpl %o7 + 8, %g0
+ nop
diff --git a/arch/sparc/math-emu/fabss.c b/arch/sparc/math-emu/fabss.c
new file mode 100644
index 000000000..accfd4f59
--- /dev/null
+++ b/arch/sparc/math-emu/fabss.c
@@ -0,0 +1,6 @@
+int FABSS(unsigned long *rd, unsigned long *rs2)
+{
+ /* Clear the sign bit (high bit of word 0) */
+ rd[0] = rs2[0] & 0x7fffffffUL;
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fcmpd.c b/arch/sparc/math-emu/fcmpd.c
new file mode 100644
index 000000000..3a9926575
--- /dev/null
+++ b/arch/sparc/math-emu/fcmpd.c
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FCMPD(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_D(A); FP_DECL_D(B);
+ long ret;
+ unsigned long *fsr = rd;
+
+ __FP_UNPACK_D(A, rs1);
+ __FP_UNPACK_D(B, rs2);
+ FP_CMP_D(ret, B, A, 2);
+ if (ret == -1)
+ ret = 2;
+
+ *fsr = (*fsr & ~0xc00) | (ret << 10);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fcmped.c b/arch/sparc/math-emu/fcmped.c
new file mode 100644
index 000000000..a8c188042
--- /dev/null
+++ b/arch/sparc/math-emu/fcmped.c
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "double.h"
+
+int FCMPED(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_D(A); FP_DECL_D(B);
+ long ret;
+ unsigned long *fsr = rd;
+
+ __FP_UNPACK_D(A, rs1);
+ __FP_UNPACK_D(B, rs2);
+ FP_CMP_D(ret, B, A, 2);
+ if (ret == -1)
+ ret = 2;
+
+ *fsr = (*fsr & ~0xc00) | (ret << 10);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fcmpeq.c b/arch/sparc/math-emu/fcmpeq.c
new file mode 100644
index 000000000..c109c51ce
--- /dev/null
+++ b/arch/sparc/math-emu/fcmpeq.c
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FCMPEQ(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_Q(A); FP_DECL_Q(B);
+ long ret;
+ unsigned long fsr;
+
+ __FP_UNPACK_Q(A, rs1);
+ __FP_UNPACK_Q(B, rs2);
+ FP_CMP_Q(ret, B, A, 3);
+ if (ret == -1) ret = 2;
+ fsr = *(unsigned long *)rd;
+ fsr &= ~0xc00; fsr |= (ret << 10);
+ *(unsigned long *)rd = fsr;
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fcmpes.c b/arch/sparc/math-emu/fcmpes.c
new file mode 100644
index 000000000..e20884cfd
--- /dev/null
+++ b/arch/sparc/math-emu/fcmpes.c
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FCMPES(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_S(A); FP_DECL_S(B);
+ long ret;
+ unsigned long *fsr = rd;
+
+ __FP_UNPACK_S(A, rs1);
+ __FP_UNPACK_S(B, rs2);
+ FP_CMP_S(ret, B, A, 1);
+ if (ret == -1)
+ ret = 2;
+
+ *fsr = (*fsr & ~0xc00) | (ret << 10);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fcmpq.c b/arch/sparc/math-emu/fcmpq.c
new file mode 100644
index 000000000..549f02cae
--- /dev/null
+++ b/arch/sparc/math-emu/fcmpq.c
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "quad.h"
+
+int FCMPQ(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_Q(A); FP_DECL_Q(B);
+ long ret;
+ unsigned long fsr;
+
+ __FP_UNPACK_Q(A, rs1);
+ __FP_UNPACK_Q(B, rs2);
+ FP_CMP_Q(ret, B, A, 3);
+ if (ret == -1) ret = 2;
+ fsr = *(unsigned long *)rd;
+ fsr &= ~0xc00; fsr |= (ret << 10);
+ *(unsigned long *)rd = fsr;
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fcmps.c b/arch/sparc/math-emu/fcmps.c
new file mode 100644
index 000000000..1b53312ae
--- /dev/null
+++ b/arch/sparc/math-emu/fcmps.c
@@ -0,0 +1,18 @@
+#include "soft-fp.h"
+#include "single.h"
+
+int FCMPS(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_S(A); FP_DECL_S(B);
+ long ret;
+ unsigned long *fsr = rd;
+
+ __FP_UNPACK_S(A, rs1);
+ __FP_UNPACK_S(B, rs2);
+ FP_CMP_S(ret, B, A, 1);
+ if (ret == -1)
+ ret = 2;
+
+ *fsr = (*fsr & ~0xc00) | (ret << 10);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fdmulq.c b/arch/sparc/math-emu/fdmulq.c
new file mode 100644
index 000000000..1d5bc5053
--- /dev/null
+++ b/arch/sparc/math-emu/fdmulq.c
@@ -0,0 +1,16 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FDMULQ(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
+
+ __FP_UNPACK_D(IN, rs1);
+ FP_CONV(Q,D,4,2,A,IN);
+ __FP_UNPACK_D(IN, rs2);
+ FP_CONV(Q,D,4,2,B,IN);
+ FP_MUL_Q(R, A, B);
+ __FP_PACK_Q(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fdtoq.c b/arch/sparc/math-emu/fdtoq.c
new file mode 100644
index 000000000..84ebcf4a2
--- /dev/null
+++ b/arch/sparc/math-emu/fdtoq.c
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FDTOQ(void *rd, void *rs2)
+{
+ FP_DECL_D(A); FP_DECL_Q(R);
+
+ __FP_UNPACK_D(A, rs2);
+ FP_CONV(Q,D,4,2,R,A);
+ __FP_PACK_Q(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fdtos.c b/arch/sparc/math-emu/fdtos.c
new file mode 100644
index 000000000..83b8a14ed
--- /dev/null
+++ b/arch/sparc/math-emu/fdtos.c
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FDTOS(void *rd, void *rs2)
+{
+ FP_DECL_D(A); FP_DECL_S(R);
+
+ __FP_UNPACK_D(A, rs2);
+ FP_CONV(S,D,1,2,R,A);
+ __FP_PACK_S(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fmovs.c b/arch/sparc/math-emu/fmovs.c
new file mode 100644
index 000000000..f113c0bb1
--- /dev/null
+++ b/arch/sparc/math-emu/fmovs.c
@@ -0,0 +1,5 @@
+int FMOVS(unsigned long *rd, unsigned long *rs2)
+{
+ rd[0] = rs2[0];
+ return 0;
+}
diff --git a/arch/sparc/math-emu/fnegs.c b/arch/sparc/math-emu/fnegs.c
new file mode 100644
index 000000000..39188eea6
--- /dev/null
+++ b/arch/sparc/math-emu/fnegs.c
@@ -0,0 +1,7 @@
+int FNEGS(unsigned long *rd, unsigned long *rs2)
+{
+ /* just change the sign bit */
+ rd[0] = rs2[0] ^ 0x80000000UL;
+ return 1;
+}
+
diff --git a/arch/sparc/math-emu/fqtod.c b/arch/sparc/math-emu/fqtod.c
new file mode 100644
index 000000000..dc5b6f9aa
--- /dev/null
+++ b/arch/sparc/math-emu/fqtod.c
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "double.h"
+
+int FQTOD(void *rd, void *rs2)
+{
+ FP_DECL_Q(A); FP_DECL_D(R);
+
+ __FP_UNPACK_Q(A, rs2);
+ FP_CONV(D,Q,2,4,R,A);
+ __FP_PACK_D(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fqtos.c b/arch/sparc/math-emu/fqtos.c
new file mode 100644
index 000000000..608f57be0
--- /dev/null
+++ b/arch/sparc/math-emu/fqtos.c
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "single.h"
+
+int FQTOS(void *rd, void *rs2)
+{
+ FP_DECL_Q(A); FP_DECL_S(R);
+
+ __FP_UNPACK_Q(A, rs2);
+ FP_CONV(S,Q,1,4,R,A);
+ __FP_PACK_S(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fsmuld.c b/arch/sparc/math-emu/fsmuld.c
new file mode 100644
index 000000000..dead5a042
--- /dev/null
+++ b/arch/sparc/math-emu/fsmuld.c
@@ -0,0 +1,16 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FSMULD(void *rd, void *rs2, void *rs1)
+{
+ FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
+
+ __FP_UNPACK_S(IN, rs1);
+ FP_CONV(D,S,2,1,A,IN);
+ __FP_UNPACK_S(IN, rs2);
+ FP_CONV(D,S,2,1,B,IN);
+ FP_MUL_D(R, A, B);
+ __FP_PACK_D(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fstod.c b/arch/sparc/math-emu/fstod.c
new file mode 100644
index 000000000..cb34329c9
--- /dev/null
+++ b/arch/sparc/math-emu/fstod.c
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "double.h"
+#include "single.h"
+
+int FSTOD(void *rd, void *rs2)
+{
+ FP_DECL_S(A); FP_DECL_D(R);
+
+ __FP_UNPACK_S(A, rs2);
+ FP_CONV(D,S,2,1,R,A);
+ __FP_PACK_D(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/fstoq.c b/arch/sparc/math-emu/fstoq.c
new file mode 100644
index 000000000..081c4d4d0
--- /dev/null
+++ b/arch/sparc/math-emu/fstoq.c
@@ -0,0 +1,13 @@
+#include "soft-fp.h"
+#include "quad.h"
+#include "single.h"
+
+int FSTOQ(void *rd, void *rs2)
+{
+ FP_DECL_S(A); FP_DECL_Q(R);
+
+ __FP_UNPACK_S(A, rs2);
+ FP_CONV(Q,S,4,1,R,A);
+ __FP_PACK_Q(rd, R);
+ return 1;
+}
diff --git a/arch/sparc/math-emu/math.c b/arch/sparc/math-emu/math.c
new file mode 100644
index 000000000..df5c879c5
--- /dev/null
+++ b/arch/sparc/math-emu/math.c
@@ -0,0 +1,416 @@
+/*
+ * arch/sparc/math-emu/math.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ * Based on the sparc64 code by Jakub Jelinek.
+ *
+ * This is a good place to start if you're trying to understand the
+ * emulation code, because it's pretty simple. What we do is
+ * essentially analyse the instruction to work out what the operation
+ * is and which registers are involved. We then execute the appropriate
+ * FXXXX function. [The floating point queue introduces a minor wrinkle;
+ * see below...]
+ * The fxxxxx.c files each emulate a single insn. They look relatively
+ * simple because the complexity is hidden away in an unholy tangle
+ * of preprocessor macros.
+ *
+ * WARNING : don't look at the macro definitions unless you
+ * absolutely have to! They're extremely ugly, rather complicated
+ * and a single line in an fxxxx.c file can expand to the equivalent
+ * of 30 lines or more of C. Of course, any error in those 30 lines
+ * is reported by the compiler as an error in the single line with the
+ * macro usage...
+ * Question: should we replace them with inline functions?
+ *
+ * The first layer of macros is single.h, double.h, quad.h. Generally
+ * these files define macros for working with floating point numbers
+ * of the three IEEE formats. FP_ADD_D(R,A,B) is for adding doubles,
+ * for instance. These macros are usually defined as calls to more
+ * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number
+ * of machine words required to store the given IEEE format is passed
+ * as a parameter. [double.h and co check the number of bits in a word
+ * and define FP_ADD_D & co appropriately].
+ * The generic macros are defined in op-common.h. This is where all
+ * the grotty stuff like handling NaNs is coded. To handle the possible
+ * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc()
+ * where wc is the 'number of machine words' parameter (here 2).
+ * These are defined in the third layer of macros: op-1.h, op-2.h
+ * and op-4.h. These handle operations on floating point numbers composed
+ * of 1,2 and 4 machine words respectively. [For example, on sparc64
+ * doubles are one machine word so macros in double.h eventually use
+ * constructs in op-1.h, but on sparc32 they use op-2.h definitions.]
+ * soft-fp.h is on the same level as op-common.h, and defines some
+ * macros which are independent of both word size and FP format.
+ * Finally, sfp-machine.h is the machine dependent part of the
+ * code: it defines the word size and what type a word is. It also
+ * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h
+ * provide several possible flavours of multiply algorithm, most
+ * of which require that you supply some form of asm or C primitive to
+ * do the actual multiply. (such asm primitives should be defined
+ * in sfp-machine.h too). udivmodti4.c is the same sort of thing.
+ *
+ * There may be some errors here because I'm working from a
+ * SPARC architecture manual V9, and what I really want is V8...
+ * Also, the insns which can generate exceptions seem to be a
+ * greater subset of the FPops than for V9 (for example, FCMPED
+ * has to be emulated on V8). So I think I'm going to have
+ * to emulate them all just to be on the safe side...
+ *
+ * Emulation routines originate from soft-fp package, which is
+ * part of glibc and has appropriate copyrights in it (allegedly).
+ *
+ * NB: on sparc int == long == 4 bytes, long long == 8 bytes.
+ * Most bits of the kernel seem to go for long rather than int,
+ * so we follow that practice...
+ */
+
+/* WISHLIST:
+ *
+ * + Replace all the macros with inline functions. These should
+ * have the same effect but be much easier to work with.
+ *
+ * + Emulate the IEEE exception flags. We don't currently do this
+ * because a) it would require significant alterations to
+ * the emulation macros [see the comments about _FP_NEG()
+ * in op-common.c and note that we'd need to invent a convention
+ * for passing in the flags to FXXXX fns and returning them] and
+ * b) SPARClinux doesn't let users access the flags anyway
+ * [contrast Solaris, which allows you to examine, clear or set
+ * the flags, and request that exceptions cause SIGFPE
+ * [which you then set up a signal handler for, obviously...]].
+ * Erm, (b) may quite possibly be garbage. %fsr is user-writable
+ * so you don't need a syscall. There may or may not be library
+ * support.
+ *
+ * + Emulation of FMULQ, FDIVQ, FSQRTQ, FDMULQ needs to be
+ * written!
+ *
+ * + reindent code to conform to Linux kernel standard :->
+ *
+ * + work out whether all the compile-time warnings are bogus
+ *
+ * + check that conversion to/from integers works
+ *
+ * + check with the SPARC architecture manual to see if we resolve
+ * the implementation-dependent bits of the IEEE spec in the
+ * same manner as the hardware.
+ *
+ * + more test cases for the test script always welcome!
+ *
+ * + illegal opcodes currently cause SIGFPEs. We should arrange
+ * to tell the traps.c code to SIGILL instead. Currently,
+ * everywhere that we return 0 should cause SIGILL, I think.
+ * SIGFPE should only be caused if we set an IEEE exception bit
+ * and the relevant trap bit is also set. (this means that
+ * traps.c should do this; also it should handle the case of
+ * IEEE exception generated directly by the hardware.)
+ * Should illegal_fp_register (which is a flavour of fp exception)
+ * cause SIGFPE or SIGILL?
+ *
+ * + the test script needs to be extended to handle the quadword
+ * and comparison insns.
+ *
+ * + _FP_DIV_MEAT_2_udiv_64() appears to work but it should be
+ * checked by somebody who understands the algorithm :->
+ *
+ * + fpsave() saves the FP queue but fpload() doesn't reload it.
+ * Therefore when we context switch or change FPU ownership
+ * we have to check to see if the queue had anything in it and
+ * emulate it if it did. This is going to be a pain.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+
+#define FLOATFUNC(x) extern int x(void *,void *,void *)
+
+/* Current status: we don't properly emulate the difficult quadword
+ * insns (MUL, DIV, SQRT).
+ * There are also some ops involving the FP registers which we don't
+ * emulate: the branch on FP condition flags and the load/store to
+ * FP regs or FSR. I'm assuming that these will never generate traps
+ * (not unreasonable if there's an FPU at all; comments in the NetBSD
+ * kernel source agree on this point). If we wanted to allow
+ * purely software-emulation of the FPU with FPU totally disabled
+ * or non-existent, we'd have to emulate these as well. We'd also
+ * need to alter the fp_disabled trap handler to call the math-emu
+ * code appropriately. The structure of do_one_mathemu() is also
+ * inappropriate for these ops (as it has no way to alter the pc,
+ * for a start) and it might be better to special-case them in do_mathemu().
+ * Oh, and you'd need to alter the traps.c code so it didn't try to
+ * fpsave() and fpload(). If there's genuinely no FPU then there's
+ * probably bits of kernel stuff that just won't work anyway...
+ */
+
+/* The Vn labels indicate what version of the SPARC architecture gas thinks
+ * each insn is. This is from the binutils source :->
+ */
+/* quadword instructions */
+FLOATFUNC(FSQRTQ); /* v8 NYI */
+FLOATFUNC(FADDQ); /* v8 */
+FLOATFUNC(FSUBQ); /* v8 */
+FLOATFUNC(FMULQ); /* v8 NYI */
+FLOATFUNC(FDIVQ); /* v8 NYI */
+FLOATFUNC(FDMULQ); /* v8 NYI */
+FLOATFUNC(FQTOS); /* v8 */
+FLOATFUNC(FQTOD); /* v8 */
+FLOATFUNC(FITOQ); /* v8 */
+FLOATFUNC(FSTOQ); /* v8 */
+FLOATFUNC(FDTOQ); /* v8 */
+FLOATFUNC(FQTOI); /* v8 */
+FLOATFUNC(FCMPQ); /* v8 */
+FLOATFUNC(FCMPEQ); /* v8 */
+/* single/double instructions (subnormal): should all work */
+FLOATFUNC(FSQRTS); /* v7 */
+FLOATFUNC(FSQRTD); /* v7 */
+FLOATFUNC(FADDS); /* v6 */
+FLOATFUNC(FADDD); /* v6 */
+FLOATFUNC(FSUBS); /* v6 */
+FLOATFUNC(FSUBD); /* v6 */
+FLOATFUNC(FMULS); /* v6 */
+FLOATFUNC(FMULD); /* v6 */
+FLOATFUNC(FDIVS); /* v6 */
+FLOATFUNC(FDIVD); /* v6 */
+FLOATFUNC(FSMULD); /* v8 */
+FLOATFUNC(FDTOS); /* v6 */
+FLOATFUNC(FSTOD); /* v6 */
+FLOATFUNC(FSTOI); /* v6 */
+FLOATFUNC(FDTOI); /* v6 */
+FLOATFUNC(FABSS); /* v6 */
+FLOATFUNC(FCMPS); /* v6 */
+FLOATFUNC(FCMPES); /* v6 */
+FLOATFUNC(FCMPD); /* v6 */
+FLOATFUNC(FCMPED); /* v6 */
+FLOATFUNC(FMOVS); /* v6 */
+FLOATFUNC(FNEGS); /* v6 */
+FLOATFUNC(FITOS); /* v6 */
+FLOATFUNC(FITOD); /* v6 */
+
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);
+
+/* Unlike the Sparc64 version (which has a struct fpustate), we
+ * pass the taskstruct corresponding to the task which currently owns the
+ * FPU. This is partly because we don't have the fpustate struct and
+ * partly because the task owning the FPU isn't always current (as is
+ * the case for the Sparc64 port). This is probably SMP-related...
+ * This function returns 1 if all queued insns were emulated successfully.
+ * The test for unimplemented FPop in kernel mode has been moved into
+ * kernel/traps.c for simplicity.
+ */
+int do_mathemu(struct pt_regs *regs, struct task_struct *fpt)
+{
+ /* regs->pc isn't necessarily the PC at which the offending insn is sitting.
+ * The FPU maintains a queue of FPops which cause traps.
+ * When it hits an instruction that requires that the trapped op succeeded
+ * (usually because it reads a reg. that the trapped op wrote) then it
+ * causes this exception. We need to emulate all the insns on the queue
+ * and then allow the op to proceed.
+ * This code should also handle the case where the trap was precise,
+ * in which case the queue length is zero and regs->pc points at the
+ * single FPop to be emulated. (this case is untested, though :->)
+ * You'll need this case if you want to be able to emulate all FPops
+ * because the FPU either doesn't exist or has been software-disabled.
+ * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it
+ * might sound because the Ultra does funky things with a superscalar
+ * architecture.]
+ */
+
+ /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
+
+ int i;
+ int retcode = 0; /* assume all succeed */
+ unsigned long insn;
+
+#ifdef DEBUG_MATHEMU
+ printk("In do_mathemu()... pc is %08lx\n", regs->pc);
+ printk("fpqdepth is %ld\n",fpt->tss.fpqdepth);
+ for (i = 0; i < fpt->tss.fpqdepth; i++)
+ printk("%d: %08lx at %08lx\n",i,fpt->tss.fpqueue[i].insn, (unsigned long)fpt->tss.fpqueue[i].insn_addr);
+#endif
+
+ if (fpt->tss.fpqdepth == 0) { /* no queue, guilty insn is at regs->pc */
+#ifdef DEBUG_MATHEMU
+ printk("precise trap at %08lx\n", regs->pc);
+#endif
+ if (!get_user(insn, (u32 *)regs->pc)) {
+ retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs);
+ if (retcode) {
+ /* in this case we need to fix up PC & nPC */
+ regs->pc = regs->npc;
+ regs->npc += 4;
+ }
+ }
+ return retcode;
+ }
+
+ /* Normal case: need to empty the queue... */
+ for (i = 0; i < fpt->tss.fpqdepth; i++)
+ {
+ retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs);
+ if (!retcode) /* insn failed, no point doing any more */
+ break;
+ }
+ /* Now empty the queue and clear the queue_not_empty flag */
+ fpt->tss.fsr &= ~0x3000;
+ fpt->tss.fpqdepth = 0;
+
+ return retcode;
+}
+
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs)
+{
+ /* Emulate the given insn, updating fsr and fregs appropriately. */
+ int type = 0;
+ /* 01 is single, 10 is double, 11 is quad,
+ * 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc)
+ * 111100000000 tells which ftt that may happen in
+ * (this field not used on sparc32 code, as we can't
+ * extract trap type info for ops on the FP queue)
+ */
+ int freg;
+ int (*func)(void *,void *,void *) = NULL;
+ void *rs1 = NULL, *rs2 = NULL, *rd = NULL;
+
+#ifdef DEBUG_MATHEMU
+ printk("In do_mathemu(), emulating %08lx\n", insn);
+#endif
+
+ if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
+ switch ((insn >> 5) & 0x1ff) {
+ /* QUAD - ftt == 3 */
+ case 0x001: type = 0x314; func = FMOVS; break;
+ case 0x005: type = 0x314; func = FNEGS; break;
+ case 0x009: type = 0x314; func = FABSS; break;
+ case 0x02b: type = 0x33c; func = FSQRTQ; break;
+ case 0x043: type = 0x33f; func = FADDQ; break;
+ case 0x047: type = 0x33f; func = FSUBQ; break;
+ case 0x04b: type = 0x33f; func = FMULQ; break;
+ case 0x04f: type = 0x33f; func = FDIVQ; break;
+ case 0x06e: type = 0x33a; func = FDMULQ; break;
+ case 0x0c7: type = 0x31c; func = FQTOS; break;
+ case 0x0cb: type = 0x32c; func = FQTOD; break;
+ case 0x0cc: type = 0x334; func = FITOQ; break;
+ case 0x0cd: type = 0x334; func = FSTOQ; break;
+ case 0x0ce: type = 0x338; func = FDTOQ; break;
+ case 0x0d3: type = 0x31c; func = FQTOI; break;
+ /* SUBNORMAL - ftt == 2 */
+ case 0x029: type = 0x214; func = FSQRTS; break;
+ case 0x02a: type = 0x228; func = FSQRTD; break;
+ case 0x041: type = 0x215; func = FADDS; break;
+ case 0x042: type = 0x22a; func = FADDD; break;
+ case 0x045: type = 0x215; func = FSUBS; break;
+ case 0x046: type = 0x22a; func = FSUBD; break;
+ case 0x049: type = 0x215; func = FMULS; break;
+ case 0x04a: type = 0x22a; func = FMULD; break;
+ case 0x04d: type = 0x215; func = FDIVS; break;
+ case 0x04e: type = 0x22a; func = FDIVD; break;
+ case 0x069: type = 0x225; func = FSMULD; break;
+ case 0x0c6: type = 0x218; func = FDTOS; break;
+ case 0x0c9: type = 0x224; func = FSTOD; break;
+ case 0x0d1: type = 0x214; func = FSTOI; break;
+ case 0x0d2: type = 0x218; func = FDTOI; break;
+ default:
+#ifdef DEBUG_MATHEMU
+ printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff);
+#endif
+ }
+ }
+ else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
+ switch ((insn >> 5) & 0x1ff) {
+ case 0x051: type = 0x305; func = FCMPS; break;
+ case 0x052: type = 0x30a; func = FCMPD; break;
+ case 0x053: type = 0x30f; func = FCMPQ; break;
+ case 0x055: type = 0x305; func = FCMPES; break;
+ case 0x056: type = 0x30a; func = FCMPED; break;
+ case 0x057: type = 0x30f; func = FCMPEQ; break;
+ default:
+#ifdef DEBUG_MATHEMU
+ printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff);
+#endif
+ }
+ }
+
+ if (!type) { /* oops, didn't recognise that FPop */
+ printk("attempt to emulate unrecognised FPop!\n");
+ return 0;
+ }
+
+ /* Decode the registers to be used */
+ freg = (*fsr >> 14) & 0xf;
+
+ *fsr &= ~0x1c000; /* clear the traptype bits */
+
+ freg = ((insn >> 14) & 0x1f);
+ switch (type & 0x3) /* is rs1 single, double or quad? */
+ {
+ case 3:
+ if (freg & 3) /* quadwords must have bits 4&5 of the */
+ { /* encoded reg. number set to zero. */
+ *fsr |= (6 << 14);
+ return 0; /* simulate invalid_fp_register exception */
+ }
+ /* fall through */
+ case 2:
+ if (freg & 1) /* doublewords must have bit 5 zeroed */
+ {
+ *fsr |= (6 << 14);
+ return 0;
+ }
+ }
+ rs1 = (void *)&fregs[freg];
+ freg = (insn & 0x1f);
+ switch ((type >> 2) & 0x3)
+ { /* same again for rs2 */
+ case 3:
+ if (freg & 3) /* quadwords must have bits 4&5 of the */
+ { /* encoded reg. number set to zero. */
+ *fsr |= (6 << 14);
+ return 0; /* simulate invalid_fp_register exception */
+ }
+ /* fall through */
+ case 2:
+ if (freg & 1) /* doublewords must have bit 5 zeroed */
+ {
+ *fsr |= (6 << 14);
+ return 0;
+ }
+ }
+ rs2 = (void *)&fregs[freg];
+ freg = ((insn >> 25) & 0x1f);
+ switch ((type >> 4) & 0x3) /* and finally rd. This one's a bit different */
+ {
+ case 0: /* dest is fcc. (this must be FCMPQ or FCMPEQ) */
+ if (freg) /* V8 has only one set of condition codes, so */
+ { /* anything but 0 in the rd field is an error */
+ *fsr |= (6 << 14); /* (should probably flag as invalid opcode */
+ return 0; /* but SIGFPE will do :-> ) */
+ }
+ rd = (void *)(fsr); /* FCMPQ and FCMPEQ are special and only */
+ break; /* set bits they're supposed to :-> */
+ case 3:
+ if (freg & 3) /* quadwords must have bits 4&5 of the */
+ { /* encoded reg. number set to zero. */
+ *fsr |= (6 << 14);
+ return 0; /* simulate invalid_fp_register exception */
+ }
+ /* fall through */
+ case 2:
+ if (freg & 1) /* doublewords must have bit 5 zeroed */
+ {
+ *fsr |= (6 << 14);
+ return 0;
+ }
+ /* fall through */
+ case 1:
+ rd = (void *)&fregs[freg];
+ break;
+ }
+#ifdef DEBUG_MATHEMU
+ printk("executing insn...\n");
+#endif
+ func(rd, rs2, rs1); /* do the Right Thing */
+ return 1; /* success! */
+}
diff --git a/arch/sparc/math-emu/sfp-machine.h b/arch/sparc/math-emu/sfp-machine.h
new file mode 100644
index 000000000..eafad4273
--- /dev/null
+++ b/arch/sparc/math-emu/sfp-machine.h
@@ -0,0 +1,363 @@
+/* Machine-dependent software floating-point definitions. Sparc version.
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If
+ not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Actually, this is a sparc (32bit) version, written based on the
+ i386 and sparc64 versions, by me,
+ Peter Maydell (pmaydell@chiark.greenend.org.uk).
+ Comments are by and large also mine, although they may be inaccurate.
+
+ In picking out asm fragments I've gone with the lowest common
+ denominator, which also happens to be the hardware I have :->
+ That is, a SPARC without hardware multiply and divide.
+ */
+
+
+/* basic word size definitions */
+#define _FP_W_TYPE_SIZE 32
+#define _FP_W_TYPE unsigned long
+#define _FP_WS_TYPE signed long
+#define _FP_I_TYPE long
+
+/* You can optionally code some things like addition in asm. For
+ * example, i386 defines __FP_FRAC_ADD_2 as asm. If you don't
+ * then you get a fragment of C code [if you change an #ifdef 0
+ * in op-2.h] or a call to add_ssaaaa (see below).
+ * Good places to look for asm fragments to use are gcc and glibc.
+ * gcc's longlong.h is useful.
+ */
+
+/* We need to know how to multiply and divide. If the host word size
+ * is >= 2*fracbits you can use FP_MUL_MEAT_n_imm(t,R,X,Y) which
+ * codes the multiply with whatever gcc does to 'a * b'.
+ * _FP_MUL_MEAT_n_wide(t,R,X,Y,f) is used when you have an asm
+ * function that can multiply two 1W values and get a 2W result.
+ * Otherwise you're stuck with _FP_MUL_MEAT_n_hard(t,R,X,Y) which
+ * does bitshifting to avoid overflow.
+ * For division there is FP_DIV_MEAT_n_imm(t,R,X,Y,f) for word size
+ * >= 2*fracbits, where f is either _FP_DIV_HELP_imm or
+ * _FP_DIV_HELP_ldiv (see op-1.h).
+ * _FP_DIV_MEAT_udiv() is if you have asm to do 2W/1W => (1W, 1W).
+ * [GCC and glibc have longlong.h which has the asm macro udiv_qrnnd
+ * to do this.]
+ * In general, 'n' is the number of words required to hold the type,
+ * and 't' is either S, D or Q for single/double/quad.
+ * -- PMM
+ */
+/* Example: SPARC64:
+ * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y)
+ * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm)
+ * #define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm)
+ *
+ * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm)
+ * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y)
+ * #define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y)
+ *
+ * Example: i386:
+ * #define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,_i386_mul_32_64)
+ * #define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,_i386_mul_32_64)
+ *
+ * #define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y,_i386_div_64_32)
+ * #define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y)
+ */
+#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm)
+#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm)
+/* FIXME: This is not implemented, but should be soon */
+#define _FP_MUL_MEAT_Q(R,X,Y) _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4)
+#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y)
+#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv_64(D,R,X,Y)
+/* FIXME: This is not implemented, but should be soon */
+#define _FP_DIV_MEAT_Q(R,X,Y) _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4)
+
+/* These macros define what NaN looks like. They're supposed to expand to
+ * a comma-separated set of 32bit unsigned ints that encode NaN.
+ */
+#define _FP_NANFRAC_S _FP_QNANBIT_S
+#define _FP_NANFRAC_D _FP_QNANBIT_D, 0
+#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0, 0, 0
+
+#define _FP_KEEPNANFRACP 1
+
+/* This macro appears to be called when both X and Y are NaNs, and
+ * has to choose one and copy it to R. i386 goes for the larger of the
+ * two, sparc64 just picks Y. I don't understand this at all so I'll
+ * go with sparc64 because it's shorter :-> -- PMM
+ */
+#define _FP_CHOOSENAN(fs, wc, R, X, Y) \
+ do { \
+ R##_s = Y##_s; \
+ _FP_FRAC_COPY_##wc(R,Y); \
+ R##_c = FP_CLS_NAN; \
+ } while (0)
+
+#define __FP_UNPACK_RAW_1(fs, X, val) \
+ do { \
+ union _FP_UNION_##fs *_flo = \
+ (union _FP_UNION_##fs *)val; \
+ \
+ X##_f = _flo->bits.frac; \
+ X##_e = _flo->bits.exp; \
+ X##_s = _flo->bits.sign; \
+ } while (0)
+
+#define __FP_PACK_RAW_1(fs, val, X) \
+ do { \
+ union _FP_UNION_##fs *_flo = \
+ (union _FP_UNION_##fs *)val; \
+ \
+ _flo->bits.frac = X##_f; \
+ _flo->bits.exp = X##_e; \
+ _flo->bits.sign = X##_s; \
+ } while (0)
+
+#define __FP_UNPACK_RAW_2(fs, X, val) \
+ do { \
+ union _FP_UNION_##fs *_flo = \
+ (union _FP_UNION_##fs *)val; \
+ \
+ X##_f0 = _flo->bits.frac0; \
+ X##_f1 = _flo->bits.frac1; \
+ X##_e = _flo->bits.exp; \
+ X##_s = _flo->bits.sign; \
+ } while (0)
+
+#define __FP_PACK_RAW_2(fs, val, X) \
+ do { \
+ union _FP_UNION_##fs *_flo = \
+ (union _FP_UNION_##fs *)val; \
+ \
+ _flo->bits.frac0 = X##_f0; \
+ _flo->bits.frac1 = X##_f1; \
+ _flo->bits.exp = X##_e; \
+ _flo->bits.sign = X##_s; \
+ } while (0)
+
+#define __FP_UNPACK_RAW_4(fs, X, val) \
+ do { \
+ union _FP_UNION_##fs *_flo = \
+ (union _FP_UNION_##fs *)val; \
+ \
+ X##_f[0] = _flo->bits.frac0; \
+ X##_f[1] = _flo->bits.frac1; \
+ X##_f[2] = _flo->bits.frac2; \
+ X##_f[3] = _flo->bits.frac3; \
+ X##_e = _flo->bits.exp; \
+ X##_s = _flo->bits.sign; \
+ } while (0)
+
+#define __FP_PACK_RAW_4(fs, val, X) \
+ do { \
+ union _FP_UNION_##fs *_flo = \
+ (union _FP_UNION_##fs *)val; \
+ \
+ _flo->bits.frac0 = X##_f[0]; \
+ _flo->bits.frac1 = X##_f[1]; \
+ _flo->bits.frac2 = X##_f[2]; \
+ _flo->bits.frac3 = X##_f[3]; \
+ _flo->bits.exp = X##_e; \
+ _flo->bits.sign = X##_s; \
+ } while (0)
+
+#define __FP_UNPACK_S(X,val) \
+ do { \
+ __FP_UNPACK_RAW_1(S,X,val); \
+ _FP_UNPACK_CANONICAL(S,1,X); \
+ } while (0)
+
+#define __FP_PACK_S(val,X) \
+ do { \
+ _FP_PACK_CANONICAL(S,1,X); \
+ __FP_PACK_RAW_1(S,val,X); \
+ } while (0)
+
+#define __FP_UNPACK_D(X,val) \
+ do { \
+ __FP_UNPACK_RAW_2(D,X,val); \
+ _FP_UNPACK_CANONICAL(D,2,X); \
+ } while (0)
+
+#define __FP_PACK_D(val,X) \
+ do { \
+ _FP_PACK_CANONICAL(D,2,X); \
+ __FP_PACK_RAW_2(D,val,X); \
+ } while (0)
+
+#define __FP_UNPACK_Q(X,val) \
+ do { \
+ __FP_UNPACK_RAW_4(Q,X,val); \
+ _FP_UNPACK_CANONICAL(Q,4,X); \
+ } while (0)
+
+#define __FP_PACK_Q(val,X) \
+ do { \
+ _FP_PACK_CANONICAL(Q,4,X); \
+ __FP_PACK_RAW_4(Q,val,X); \
+ } while (0)
+
+/* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* add_ssaaaa is used in op-2.h and should be equivalent to
+ * #define add_ssaaaa(sh,sl,ah,al,bh,bl) (sh = ah+bh+ (( sl = al+bl) < al))
+ * add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+ * high_addend_2, low_addend_2) adds two UWtype integers, composed by
+ * HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+ * respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow
+ * (i.e. carry out) is not stored anywhere, and is lost.
+ */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addcc %r4,%5,%1
+ addx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ : "cc")
+
+
+/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to
+ * #define sub_ddmmss(sh, sl, ah, al, bh, bl) (sh = ah-bh - ((sl = al-bl) > al))
+ * sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+ * high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+ * composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+ * LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE
+ * and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
+ * and is lost.
+ */
+
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subcc %r4,%5,%1
+ subx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ : "cc")
+
+
+/* asm fragments for mul and div */
+/* umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
+ * UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
+ * word product in HIGH_PROD and LOW_PROD.
+ * These look ugly because the sun4/4c don't have umul/udiv/smul/sdiv in
+ * hardware.
+ */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("! Inlined umul_ppmm
+ wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr
+ sra %3,31,%%g2 ! Don't move this insn
+ and %2,%%g2,%%g2 ! Don't move this insn
+ andcc %%g0,0,%%g1 ! Don't move this insn
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,%3,%%g1
+ mulscc %%g1,0,%%g1
+ add %%g1,%%g2,%0
+ rd %%y,%1" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "%rI" ((USItype)(u)), \
+ "r" ((USItype)(v)) \
+ : "%g1", "%g2", "cc")
+
+/* udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ * denominator) divides a UDWtype, composed by the UWtype integers
+ * HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+ * in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less
+ * than DENOMINATOR for correct operation. If, in addition, the most
+ * significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+ * UDIV_NEEDS_NORMALIZATION is defined to 1.
+ */
+
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("! Inlined udiv_qrnnd
+ mov 32,%%g1
+ subcc %1,%2,%%g0
+1: bcs 5f
+ addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb
+ sub %1,%2,%1 ! this kills msb of n
+ addx %1,%1,%1 ! so this can't give carry
+ subcc %%g1,1,%%g1
+2: bne 1b
+ subcc %1,%2,%%g0
+ bcs 3f
+ addxcc %0,%0,%0 ! shift n1n0 and a q-bit in lsb
+ b 3f
+ sub %1,%2,%1 ! this kills msb of n
+4: sub %1,%2,%1
+5: addxcc %1,%1,%1
+ bcc 2b
+ subcc %%g1,1,%%g1
+! Got carry from n. Subtract next step to cancel this carry.
+ bne 4b
+ addcc %0,%0,%0 ! shift n1n0 and a 0-bit in lsb
+ sub %1,%2,%1
+3: xnor %0,0,%0
+ ! End of inline udiv_qrnnd" \
+ : "=&r" ((USItype) (q)), \
+ "=&r" ((USItype) (r)) \
+ : "r" ((USItype) (d)), \
+ "1" ((USItype) (n1)), \
+ "0" ((USItype) (n0)) : "%g1", "cc")
+
+#define UDIV_NEEDS_NORMALIZATION 0
+
+#define abort() \
+ return 0
+
+#ifdef __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
index bae5d323a..18eeb1f52 100644
--- a/arch/sparc/mm/Makefile
+++ b/arch/sparc/mm/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.27 1997/11/07 15:01:27 jj Exp $
+# $Id: Makefile,v 1.30 1998/03/09 14:03:53 jj Exp $
# Makefile for the linux Sparc-specific parts of the memory manager.
#
# Note! Dependencies are done automagically by 'make dep', which also
@@ -8,9 +8,17 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := mm.o
-O_OBJS := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \
- tsunami.o loadmmu.o generic.o asyncd.o extable.o \
- turbosparc.o iommu.o io-unit.o
+O_OBJS := fault.o init.o loadmmu.o generic.o asyncd.o extable.o btfixup.o
+ifeq ($(CONFIG_SUN4),y)
+O_OBJS += nosrmmu.o
+else
+O_OBJS += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o turbosparc.o
+endif
+ifdef SMP
+O_OBJS += nosun4c.o
+else
+O_OBJS += sun4c.o
+endif
include $(TOPDIR)/Rules.make
diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c
new file mode 100644
index 000000000..e61ccc158
--- /dev/null
+++ b/arch/sparc/mm/btfixup.c
@@ -0,0 +1,334 @@
+/* $Id: btfixup.c,v 1.7 1998/03/09 14:03:56 jj Exp $
+ * btfixup.c: Boot time code fixup and relocator, so that
+ * we can get rid of most indirect calls to achieve single
+ * image sun4c and srmmu kernel.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/btfixup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+
+#define BTFIXUP_OPTIMIZE_NOP
+#define BTFIXUP_OPTIMIZE_OTHER
+
+extern char *srmmu_name;
+static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for ";
+#ifdef CONFIG_SUN4
+static char str_sun4c[] __initdata = "sun4\n";
+#else
+static char str_sun4c[] __initdata = "sun4c\n";
+#endif
+static char str_srmmu[] __initdata = "srmmu[%s]/";
+static char str_iommu[] __initdata = "iommu\n";
+static char str_iounit[] __initdata = "io-unit\n";
+
+static int visited __initdata = 0;
+extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[];
+extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[];
+static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n";
+static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n";
+static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n";
+static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n";
+static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n";
+static char wrong[] __initdata = "Wrong address for %c fixup %p\n";
+static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n";
+static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n";
+static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n";
+static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n";
+static char fca_und[] __initdata = "flush_cache_all undefined in btfixup()\n";
+static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n";
+
+#ifdef BTFIXUP_OPTIMIZE_OTHER
+__initfunc(static void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value))
+{
+ if (!fmangled)
+ *addr = value;
+ else {
+ unsigned int *q = (unsigned int *)q1;
+ if (*addr == 0x01000000) {
+ /* Noped */
+ *q = value;
+ } else if (addr[-1] == *q) {
+ /* Moved */
+ addr[-1] = value;
+ *q = value;
+ } else {
+ prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value);
+ prom_halt();
+ }
+ }
+}
+#else
+static __inline__ void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
+{
+ *addr = value;
+}
+#endif
+
+__initfunc(void btfixup(void))
+{
+ unsigned int *p, *q;
+ int type, count;
+ unsigned insn;
+ unsigned *addr;
+ int fmangled = 0;
+ void (*flush_cacheall)(void);
+
+ if (!visited) {
+ visited++;
+ printk(version);
+ if (ARCH_SUN4C_SUN4)
+ printk(str_sun4c);
+ else {
+ printk(str_srmmu, srmmu_name);
+ if (sparc_cpu_model == sun4d)
+ printk(str_iounit);
+ else
+ printk(str_iommu);
+ }
+ }
+ for (p = ___btfixup_start; p < ___btfixup_end; ) {
+ count = p[2];
+ q = p + 3;
+ switch (type = *(unsigned char *)p) {
+ case 'f':
+ count = p[3];
+ q = p + 4;
+ if (((p[0] & 1) || p[1])
+ && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) {
+ prom_printf(wrong_f, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 'b':
+ if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) {
+ prom_printf(wrong_b, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 's':
+ if (p[1] + 0x1000 >= 0x2000) {
+ prom_printf(wrong_s, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 'h':
+ if (p[1] & 0x3ff) {
+ prom_printf(wrong_h, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 'a':
+ if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) {
+ prom_printf(wrong_a, p, p[1]);
+ prom_halt();
+ }
+ break;
+ }
+ if (p[0] & 1) {
+ p[0] &= ~1;
+ while (count) {
+ fmangled = 0;
+ addr = (unsigned *)*q;
+ if (addr < _stext || addr >= _end) {
+ prom_printf(wrong, type, p);
+ prom_halt();
+ }
+ insn = *addr;
+#ifdef BTFIXUP_OPTIMIZE_OTHER
+ if (type != 'f' && q[1]) {
+ insn = *(unsigned int *)q[1];
+ if (!insn || insn == 1)
+ insn = *addr;
+ else
+ fmangled = 1;
+ }
+#endif
+ switch (type) {
+ case 'f': /* CALL */
+ if (addr >= __start___ksymtab && addr < __stop___ksymtab) {
+ *addr = p[1];
+ break;
+ } else if (!q[1]) {
+ if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */
+ *addr = (insn & 0xffc00000) | (p[1] >> 10); break;
+ } else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */
+ *addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break;
+ } else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */
+ bad_f:
+ prom_printf(insn_f, p, addr, insn, addr[1]);
+ prom_halt();
+ }
+ } else if (q[1] != 1)
+ addr[1] = q[1];
+ if (p[2] == BTFIXUPCALL_NORM) {
+ norm_f:
+ *addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2);
+ q[1] = 0;
+ break;
+ }
+#ifndef BTFIXUP_OPTIMIZE_NOP
+ goto norm_f;
+#else
+ if (!(addr[1] & 0x80000000)) {
+ if ((addr[1] & 0xc1c00000) != 0x01000000) /* !SETHI */
+ goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */
+ } else {
+ if ((addr[1] & 0x01800000) == 0x01800000) {
+ if ((addr[1] & 0x01f80000) == 0x01e80000) {
+ /* RESTORE */
+ goto norm_f; /* It is dangerous to patch that */
+ }
+ goto bad_f;
+ }
+ if ((addr[1] & 0xffffe003) == 0x9e03e000) {
+ /* ADD %O7, XX, %o7 */
+ int displac = (addr[1] << 19);
+
+ displac = (displac >> 21) + 2;
+ *addr = (0x10800000) + (displac & 0x3fffff);
+ q[1] = addr[1];
+ addr[1] = p[2];
+ break;
+ }
+ if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000)
+ goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */
+ if ((addr[1] & 0x3e000000) == 0x1e000000)
+ goto norm_f; /* rd is %o7. We'd better take care. */
+ }
+ if (p[2] == BTFIXUPCALL_NOP) {
+ *addr = 0x01000000;
+ q[1] = 1;
+ break;
+ }
+#ifndef BTFIXUP_OPTIMIZE_OTHER
+ goto norm_f;
+#else
+ if (addr[1] == 0x01000000) { /* NOP in the delay slot */
+ q[1] = addr[1];
+ *addr = p[2];
+ break;
+ }
+ if ((addr[1] & 0xc0000000) != 0xc0000000) {
+ /* Not a memory operation */
+ if ((addr[1] & 0x30000000) == 0x10000000) {
+ /* Ok, non-memory op with rd %oX */
+ if ((addr[1] & 0x3e000000) == 0x1c000000)
+ goto bad_f; /* Aiee. Someone is playing strange %sp tricks */
+ if ((addr[1] & 0x3e000000) > 0x12000000 ||
+ ((addr[1] & 0x3e000000) == 0x12000000 &&
+ p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) ||
+ ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) {
+ /* Nobody uses the result. We can nop it out. */
+ *addr = p[2];
+ q[1] = addr[1];
+ addr[1] = 0x01000000;
+ break;
+ }
+ if ((addr[1] & 0xf1ffffe0) == 0x90100000) {
+ /* MOV %reg, %Ox */
+ if ((addr[1] & 0x3e000000) == 0x10000000 &&
+ (p[2] & 0x7c000) == 0x20000) {
+ /* Ok, it is call xx; mov reg, %o0 and call optimizes
+ to doing something on %o0. Patch the patch. */
+ *addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14);
+ q[1] = addr[1];
+ addr[1] = 0x01000000;
+ break;
+ }
+ if ((addr[1] & 0x3e000000) == 0x12000000 &&
+ p[2] == BTFIXUPCALL_STO1O0) {
+ *addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25);
+ q[1] = addr[1];
+ addr[1] = 0x01000000;
+ break;
+ }
+ }
+ }
+ }
+ *addr = addr[1];
+ q[1] = addr[1];
+ addr[1] = p[2];
+ break;
+#endif /* BTFIXUP_OPTIMIZE_OTHER */
+#endif /* BTFIXUP_OPTIMIZE_NOP */
+ case 'b': /* BLACKBOX */
+ /* Has to be sethi i, xx */
+ if ((insn & 0xc1c00000) != 0x01000000) {
+ prom_printf(insn_b, p, addr, insn);
+ prom_halt();
+ } else {
+ void (*do_fixup)(unsigned *);
+
+ do_fixup = (void (*)(unsigned *))p[1];
+ do_fixup(addr);
+ }
+ break;
+ case 's': /* SIMM13 */
+ /* Has to be or %g0, i, xx */
+ if ((insn & 0xc1ffe000) != 0x80102000) {
+ prom_printf(insn_s, p, addr, insn);
+ prom_halt();
+ }
+ set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff));
+ break;
+ case 'h': /* SETHI */
+ /* Has to be sethi i, xx */
+ if ((insn & 0xc1c00000) != 0x01000000) {
+ prom_printf(insn_h, p, addr, insn);
+ prom_halt();
+ }
+ set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+ break;
+ case 'a': /* HALF */
+ /* Has to be sethi i, xx or or %g0, i, xx */
+ if ((insn & 0xc1c00000) != 0x01000000 &&
+ (insn & 0xc1ffe000) != 0x80102000) {
+ prom_printf(insn_a, p, addr, insn);
+ prom_halt();
+ }
+ if (p[1] & 0x3ff)
+ set_addr(addr, q[1], fmangled,
+ (insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff));
+ else
+ set_addr(addr, q[1], fmangled,
+ (insn & 0x3e000000) | 0x01000000 | (p[1] >> 10));
+ break;
+ case 'i': /* INT */
+ if ((insn & 0xc1c00000) == 0x01000000) /* %HI */
+ set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+ else if ((insn & 0x80002000) == 0x80002000 &&
+ (insn & 0x01800000) != 0x01800000) /* %LO */
+ set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff));
+ else {
+ prom_printf(insn_i, p, addr, insn);
+ prom_halt();
+ }
+ break;
+ }
+ count -= 2;
+ q += 2;
+ }
+ } else
+ p = q + count;
+ }
+#ifdef __SMP__
+ flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(local_flush_cache_all);
+#else
+ flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(flush_cache_all);
+#endif
+ if (!flush_cacheall) {
+ prom_printf(fca_und);
+ prom_halt();
+ }
+ (*flush_cacheall)();
+}
diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c
index 0d6490860..274b9eebf 100644
--- a/arch/sparc/mm/fault.c
+++ b/arch/sparc/mm/fault.c
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.92 1997/05/15 21:14:21 davem Exp $
+/* $Id: fault.c,v 1.93 1998/03/25 10:43:16 jj Exp $
* fault.c: Page fault handlers for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -271,7 +271,7 @@ bad_area:
#endif
tsk->tss.sig_address = address;
tsk->tss.sig_desc = SUBSIG_NOMAPPING;
- send_sig(SIGSEGV, tsk, 1);
+ force_sig(SIGSEGV, tsk);
goto out;
}
unhandled_fault (address, tsk, regs);
diff --git a/arch/sparc/mm/hypersparc.S b/arch/sparc/mm/hypersparc.S
index 2c27bfdab..dcf3fd990 100644
--- a/arch/sparc/mm/hypersparc.S
+++ b/arch/sparc/mm/hypersparc.S
@@ -1,4 +1,4 @@
-/* $Id: hypersparc.S,v 1.12 1997/11/27 15:42:30 jj Exp $
+/* $Id: hypersparc.S,v 1.13 1998/02/13 15:35:09 jj Exp $
* hypersparc.S: High speed Hypersparc mmu/cache operations.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -301,14 +301,13 @@ hypersparc_flush_tlb_range:
cmp %o3, -1
be hypersparc_flush_tlb_range_out
#endif
- srl %o1, SRMMU_PGDIR_SHIFT, %o1
+ sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
sta %o3, [%g1] ASI_M_MMUREGS
- sll %o1, SRMMU_PGDIR_SHIFT, %o1
- sethi %hi(1 << SRMMU_PGDIR_SHIFT), %o4
+ and %o1, %o4, %o1
add %o1, 0x200, %o1
sta %g0, [%o1] ASI_M_FLUSH_PROBE
1:
- add %o1, %o4, %o1
+ sub %o1, %o4, %o1
cmp %o1, %o2
blu,a 1b
sta %g0, [%o1] ASI_M_FLUSH_PROBE
diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
index aa85666c6..db6559214 100644
--- a/arch/sparc/mm/init.c
+++ b/arch/sparc/mm/init.c
@@ -1,8 +1,9 @@
-/* $Id: init.c,v 1.50 1998/01/10 18:19:42 ecd Exp $
+/* $Id: init.c,v 1.59 1998/03/27 06:59:57 davem Exp $
* linux/arch/sparc/mm/init.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
@@ -30,11 +31,18 @@
#include <asm/pgtable.h>
#include <asm/vaddrs.h>
+/* Turn this off if you suspect some place in some physical memory hole
+ might get into page tables (something would be broken very much). */
+
+#define FREE_UNUSED_MEM_MAP
+
extern void show_net_buffers(void);
struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
unsigned long sparc_unmapped_base;
+struct pgtable_cache_struct pgt_quicklists;
+
/* References to section boundaries */
extern char __init_begin, __init_end, etext;
@@ -65,26 +73,38 @@ pte_t __bad_page(void)
void show_mem(void)
{
- int i,free = 0,total = 0,reserved = 0;
- int shared = 0;
+ int free = 0,total = 0,reserved = 0;
+ int shared = 0, cached = 0;
+ struct page *page, *end;
printk("\nMem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
- i = max_mapnr;
- while (i-- > 0) {
+ for (page = mem_map, end = mem_map + max_mapnr;
+ page < end; page++) {
+ if (PageSkip(page)) {
+ if (page->next_hash < page)
+ break;
+ page = page->next_hash;
+ }
total++;
- if (PageReserved(mem_map + i))
+ if (PageReserved(page))
reserved++;
- else if (!atomic_read(&mem_map[i].count))
+ else if (PageSwapCache(page))
+ cached++;
+ else if (!atomic_read(&page->count))
free++;
else
- shared += atomic_read(&mem_map[i].count) - 1;
+ shared += atomic_read(&page->count) - 1;
}
printk("%d pages of RAM\n",total);
printk("%d free pages\n",free);
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
+ printk("%d pages swap cached\n",cached);
+ printk("%ld page tables cached\n",pgtable_cache_size);
+ if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d)
+ printk("%ld page dirs cached\n", pgd_cache_size);
show_buffers();
#ifdef CONFIG_NET
show_net_buffers();
@@ -128,19 +148,23 @@ paging_init(unsigned long start_mem, unsigned long end_mem))
switch(sparc_cpu_model) {
case sun4c:
case sun4e:
+ case sun4:
start_mem = sun4c_paging_init(start_mem, end_mem);
sparc_unmapped_base = 0xe0000000;
+ BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000);
break;
case sun4m:
case sun4d:
start_mem = srmmu_paging_init(start_mem, end_mem);
sparc_unmapped_base = 0x50000000;
+ BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
break;
case ap1000:
#if CONFIG_AP1000
start_mem = apmmu_paging_init(start_mem, end_mem);
sparc_unmapped_base = 0x50000000;
+ BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
break;
#endif
@@ -168,6 +192,7 @@ paging_init(unsigned long start_mem, unsigned long end_mem))
protection_map[13] = PAGE_READONLY;
protection_map[14] = PAGE_SHARED;
protection_map[15] = PAGE_SHARED;
+ btfixup();
return device_scan(start_mem);
}
@@ -175,7 +200,7 @@ struct cache_palias *sparc_aliases;
extern void srmmu_frob_mem_map(unsigned long);
-int physmem_mapped_contig = 1;
+int physmem_mapped_contig __initdata = 1;
__initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
{
@@ -210,7 +235,8 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
int codepages = 0;
int datapages = 0;
int initpages = 0;
- unsigned long tmp2, addr;
+ unsigned long addr;
+ struct page *page, *end;
/* Saves us work later. */
memset((void *) ZERO_PAGE, 0, PAGE_SIZE);
@@ -220,33 +246,60 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
high_memory = (void *) end_mem;
start_mem = PAGE_ALIGN(start_mem);
- num_physpages = (start_mem - KERNBASE) >> PAGE_SHIFT;
+ num_physpages = 0;
addr = KERNBASE;
while(addr < start_mem) {
#ifdef CONFIG_BLK_DEV_INITRD
- if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) {
+ if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
- num_physpages--;
- } else
+ else
#endif
mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
addr += PAGE_SIZE;
}
taint_real_pages(start_mem, end_mem);
+
+#ifdef FREE_UNUSED_MEM_MAP
+ end = mem_map + max_mapnr;
+ for (page = mem_map; page < end; page++) {
+ if (PageSkip(page)) {
+ unsigned long low, high;
+
+ low = PAGE_ALIGN((unsigned long)(page+1));
+ if (page->next_hash < page)
+ high = ((unsigned long)end) & PAGE_MASK;
+ else
+ high = ((unsigned long)page->next_hash) & PAGE_MASK;
+ while (low < high) {
+ mem_map[MAP_NR(low)].flags &= ~(1<<PG_reserved);
+ low += PAGE_SIZE;
+ }
+ }
+ }
+#endif
+
for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
+ if (PageSkip(mem_map + MAP_NR(addr))) {
+ unsigned long next = mem_map[MAP_NR(addr)].next_hash - mem_map;
+
+ next = (next << PAGE_SHIFT) + PAGE_OFFSET;
+ if (next < addr || next >= end_mem)
+ break;
+ addr = next;
+ }
+ num_physpages++;
if(PageReserved(mem_map + MAP_NR(addr))) {
if ((addr < (unsigned long) &etext) && (addr >= KERNBASE))
codepages++;
- else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
- initpages++;
- else if((addr < start_mem) && (addr >= KERNBASE))
+ else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
+ initpages++;
+ else if((addr < start_mem) && (addr >= KERNBASE))
datapages++;
continue;
}
atomic_set(&mem_map[MAP_NR(addr)].count, 1);
- num_physpages++;
#ifdef CONFIG_BLK_DEV_INITRD
if (!initrd_start ||
(addr < initrd_start || addr >= initrd_end))
@@ -254,14 +307,12 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
free_page(addr);
}
- tmp2 = nr_free_pages << PAGE_SHIFT;
-
- printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n",
- tmp2 >> 10,
+ printk("Memory: %dk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n",
+ nr_free_pages << (PAGE_SHIFT-10),
codepages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10),
initpages << (PAGE_SHIFT-10),
- PAGE_OFFSET, end_mem);
+ (unsigned long)PAGE_OFFSET, end_mem);
freepages.min = nr_free_pages >> 7;
if(freepages.min < 16)
@@ -284,20 +335,25 @@ void free_initmem (void)
void si_meminfo(struct sysinfo *val)
{
- int i;
+ struct page *page, *end;
- i = MAP_NR(high_memory);
val->totalram = 0;
val->sharedram = 0;
val->freeram = nr_free_pages << PAGE_SHIFT;
val->bufferram = buffermem;
- while (i-- > 0) {
- if (PageReserved(mem_map + i))
+ for (page = mem_map, end = mem_map + max_mapnr;
+ page < end; page++) {
+ if (PageSkip(page)) {
+ if (page->next_hash < page)
+ break;
+ page = page->next_hash;
+ }
+ if (PageReserved(page))
continue;
val->totalram++;
- if (!atomic_read(&mem_map[i].count))
+ if (!atomic_read(&page->count))
continue;
- val->sharedram += atomic_read(&mem_map[i].count) - 1;
+ val->sharedram += atomic_read(&page->count) - 1;
}
val->totalram <<= PAGE_SHIFT;
val->sharedram <<= PAGE_SHIFT;
diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c
index 519c124c9..d293fc71c 100644
--- a/arch/sparc/mm/io-unit.c
+++ b/arch/sparc/mm/io-unit.c
@@ -1,7 +1,7 @@
-/* $Id: io-unit.c,v 1.5 1997/12/22 16:09:26 jj Exp $
+/* $Id: io-unit.c,v 1.10 1998/03/03 12:31:14 jj Exp $
* io-unit.c: IO-UNIT specific routines for memory management.
*
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
@@ -13,28 +13,41 @@
#include <asm/io.h>
#include <asm/io-unit.h>
#include <asm/mxcc.h>
+#include <asm/spinlock.h>
+#include <asm/bitops.h>
+
+/* #define IOUNIT_DEBUG */
+#ifdef IOUNIT_DEBUG
+#define IOD(x) printk(x)
+#else
+#define IOD(x) do { } while (0)
+#endif
#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
#define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID)
-#define MKIOPTE(phys) ((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
+#define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
-unsigned long sun4d_dma_base;
-unsigned long sun4d_dma_vbase;
-unsigned long sun4d_dma_size;
__initfunc(unsigned long
iounit_init(int sbi_node, int io_node, unsigned long memory_start,
unsigned long memory_end, struct linux_sbus *sbus))
{
iopte_t *xpt, *xptend;
- unsigned long paddr;
struct iounit_struct *iounit;
struct linux_prom_registers iommu_promregs[PROMREG_MAX];
memory_start = LONG_ALIGN(memory_start);
iounit = (struct iounit_struct *)memory_start;
- memory_start += sizeof(struct iounit_struct);
-
+ memory_start = LONG_ALIGN(memory_start + sizeof(struct iounit_struct));
+
+ memset(iounit, 0, sizeof(*iounit));
+ iounit->limit[0] = IOUNIT_BMAP1_START;
+ iounit->limit[1] = IOUNIT_BMAP2_START;
+ iounit->limit[2] = IOUNIT_BMAPM_START;
+ iounit->limit[3] = IOUNIT_BMAPM_END;
+ iounit->rotor[1] = IOUNIT_BMAP2_START;
+ iounit->rotor[2] = IOUNIT_BMAPM_START;
+
prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
sizeof(iommu_promregs));
prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3);
@@ -46,11 +59,6 @@ iounit_init(int sbi_node, int io_node, unsigned long memory_start,
sbus->iommu = (struct iommu_struct *)iounit;
iounit->page_table = xpt;
- /* Initialize new table. */
- paddr = IOUNIT_DMA_BASE - sun4d_dma_base;
- for (xptend = xpt + (sun4d_dma_size >> PAGE_SHIFT);
- xpt < xptend; paddr++)
- *xpt++ = MKIOPTE(paddr);
for (xptend = iounit->page_table + (16 * PAGE_SIZE) / sizeof(iopte_t);
xpt < xptend;)
*xpt++ = 0;
@@ -58,36 +66,108 @@ iounit_init(int sbi_node, int io_node, unsigned long memory_start,
return memory_start;
}
+/* One has to hold iounit->lock to call this */
+static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size)
+{
+ int i, j, k, npages;
+ unsigned long rotor, scan, limit;
+ iopte_t iopte;
+
+ npages = ((vaddr & ~PAGE_MASK) + size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+
+ /* A tiny bit of magic ingredience :) */
+ switch (npages) {
+ case 1: i = 0x0231; break;
+ case 2: i = 0x0132; break;
+ default: i = 0x0213; break;
+ }
+
+ IOD(("iounit_get_area(%08lx,%d[%d])=", vaddr, size, npages));
+
+next: j = (i & 15);
+ rotor = iounit->rotor[j - 1];
+ limit = iounit->limit[j];
+ scan = rotor;
+nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan);
+ if (scan + npages > limit) {
+ if (limit != rotor) {
+ limit = rotor;
+ scan = iounit->limit[j - 1];
+ goto nexti;
+ }
+ i >>= 4;
+ if (!(i & 15))
+ panic("iounit_get_area: Couldn't find free iopte slots for (%08lx,%d)\n", vaddr, size);
+ goto next;
+ }
+ for (k = 1, scan++; k < npages; k++)
+ if (test_bit(scan++, iounit->bmap))
+ goto nexti;
+ iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1];
+ scan -= npages;
+ iopte = MKIOPTE(mmu_v2p(vaddr & PAGE_MASK));
+ vaddr = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT) + (vaddr & ~PAGE_MASK);
+ for (k = 0; k < npages; k++, iopte = __iopte(iopte_val(iopte) + 0x100), scan++) {
+ set_bit(scan, iounit->bmap);
+ iounit->page_table[scan] = iopte;
+ }
+ IOD(("%08lx\n", vaddr));
+ return vaddr;
+}
+
static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
{
- /* Viking MXCC is IO coherent, just need to translate the address to DMA handle */
-#ifdef IOUNIT_DEBUG
- if ((((unsigned long) vaddr) & PAGE_MASK) < sun4d_dma_vaddr ||
- (((unsigned long) vaddr) & PAGE_MASK) + len > sun4d_dma_vbase + sun4d_dma_size)
- panic("Using non-DMA memory for iounit_get_scsi_one");
-#endif
- return (__u32)(sun4d_dma_base + mmu_v2p((long)vaddr));
+ unsigned long ret, flags;
+ struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+ spin_lock_irqsave(&iounit->lock, flags);
+ ret = iounit_get_area(iounit, (unsigned long)vaddr, len);
+ spin_unlock_irqrestore(&iounit->lock, flags);
+ return ret;
}
static void iounit_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
{
- /* Viking MXCC is IO coherent, just need to translate the address to DMA handle */
+ unsigned long flags;
+ struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+ /* FIXME: Cache some resolved pages - often several sg entries are to the same page */
+ spin_lock_irqsave(&iounit->lock, flags);
for (; sz >= 0; sz--) {
-#ifdef IOUNIT_DEBUG
- unsigned long page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
- if (page < sun4d_dma_vbase || page + sg[sz].len > sun4d_dma_vbase + sun4d_dma_size)
- panic("Using non-DMA memory for iounit_get_scsi_sgl");
-#endif
- sg[sz].dvma_addr = (__u32) (sun4d_dma_base + mmu_v2p((long)sg[sz].addr));;
+ sg[sz].dvma_addr = iounit_get_area(iounit, (unsigned long)sg[sz].addr, sg[sz].len);
}
+ spin_unlock_irqrestore(&iounit->lock, flags);
}
static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus)
{
+ unsigned long flags;
+ struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+ spin_lock_irqsave(&iounit->lock, flags);
+ len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+ vaddr = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+ IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
+ for (len += vaddr; vaddr < len; vaddr++)
+ clear_bit(vaddr, iounit->bmap);
+ spin_unlock_irqrestore(&iounit->lock, flags);
}
static void iounit_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
{
+ unsigned long flags;
+ unsigned long vaddr, len;
+ struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu;
+
+ spin_lock_irqsave(&iounit->lock, flags);
+ for (; sz >= 0; sz--) {
+ len = ((sg[sz].dvma_addr & ~PAGE_MASK) + sg[sz].len + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+ vaddr = (sg[sz].dvma_addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+ IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
+ for (len += vaddr; vaddr < len; vaddr++)
+ clear_bit(vaddr, iounit->bmap);
+ }
+ spin_unlock_irqrestore(&iounit->lock, flags);
}
#ifdef CONFIG_SBUS
@@ -135,24 +215,26 @@ static void iounit_map_dma_area(unsigned long addr, int len)
static char *iounit_lockarea(char *vaddr, unsigned long len)
{
+/* FIXME: Write this */
return vaddr;
}
static void iounit_unlockarea(char *vaddr, unsigned long len)
{
+/* FIXME: Write this */
}
__initfunc(void ld_mmu_iounit(void))
{
- mmu_lockarea = iounit_lockarea;
- mmu_unlockarea = iounit_unlockarea;
+ BTFIXUPSET_CALL(mmu_lockarea, iounit_lockarea, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(mmu_unlockarea, iounit_unlockarea, BTFIXUPCALL_NOP);
- mmu_get_scsi_one = iounit_get_scsi_one;
- mmu_get_scsi_sgl = iounit_get_scsi_sgl;
- mmu_release_scsi_one = iounit_release_scsi_one;
- mmu_release_scsi_sgl = iounit_release_scsi_sgl;
+ BTFIXUPSET_CALL(mmu_get_scsi_one, iounit_get_scsi_one, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_get_scsi_sgl, iounit_get_scsi_sgl, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_release_scsi_one, iounit_release_scsi_one, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_release_scsi_sgl, iounit_release_scsi_sgl, BTFIXUPCALL_NORM);
#ifdef CONFIG_SBUS
- mmu_map_dma_area = iounit_map_dma_area;
+ BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM);
#endif
}
diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c
index 301946326..e46216233 100644
--- a/arch/sparc/mm/iommu.c
+++ b/arch/sparc/mm/iommu.c
@@ -1,10 +1,10 @@
-/* $Id: iommu.c,v 1.4 1997/11/21 17:31:31 jj Exp $
+/* $Id: iommu.c,v 1.7 1998/02/22 10:32:26 ecd Exp $
* iommu.c: IOMMU specific routines for memory management.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
@@ -18,8 +18,10 @@
/* srmmu.c */
extern int viking_mxcc_present;
-extern void (*flush_page_for_dma)(unsigned long page);
+BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long)
+#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page)
extern int flush_page_for_dma_global;
+static int viking_flush = 0;
/* viking.S */
extern void viking_flush_page(unsigned long page);
extern void viking_mxcc_flush_page(unsigned long page);
@@ -113,7 +115,7 @@ iommu_init(int iommund, unsigned long memory_start,
viking_mxcc_flush_page(start);
start += PAGE_SIZE;
}
- } else if(flush_page_for_dma == viking_flush_page) {
+ } else if (viking_flush) {
unsigned long start = (unsigned long) iommu->page_table;
unsigned long end = (start + ptsize);
while(start < end) {
@@ -199,7 +201,7 @@ static void iommu_map_dma_area(unsigned long addr, int len)
pgprot_t dvma_prot;
struct iommu_struct *iommu = SBus_chain->iommu;
iopte_t *iopte = iommu->page_table;
- iopte_t *iopte_first = iopte;
+ iopte_t *first;
if(viking_mxcc_present)
dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
@@ -207,6 +209,7 @@ static void iommu_map_dma_area(unsigned long addr, int len)
dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
iopte += ((addr - iommu->start) >> PAGE_SHIFT);
+ first = iopte;
end = PAGE_ALIGN((addr + len));
while(addr < end) {
page = get_free_page(GFP_KERNEL);
@@ -223,21 +226,20 @@ static void iommu_map_dma_area(unsigned long addr, int len)
ptep = pte_offset(pmdp, addr);
set_pte(ptep, pte_val(mk_pte(page, dvma_prot)));
-
iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page));
}
addr += PAGE_SIZE;
}
flush_cache_all();
if(viking_mxcc_present) {
- unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+ unsigned long start = ((unsigned long) first) & PAGE_MASK;
unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
while(start < end) {
viking_mxcc_flush_page(start);
start += PAGE_SIZE;
}
- } else if(flush_page_for_dma == viking_flush_page) {
- unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK;
+ } else if(viking_flush) {
+ unsigned long start = ((unsigned long) first) & PAGE_MASK;
unsigned long end = PAGE_ALIGN(((unsigned long) iopte));
while(start < end) {
viking_flush_page(start);
@@ -260,25 +262,26 @@ static void iommu_unlockarea(char *vaddr, unsigned long len)
__initfunc(void ld_mmu_iommu(void))
{
- mmu_lockarea = iommu_lockarea;
- mmu_unlockarea = iommu_unlockarea;
+ viking_flush = (BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page);
+ BTFIXUPSET_CALL(mmu_lockarea, iommu_lockarea, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(mmu_unlockarea, iommu_unlockarea, BTFIXUPCALL_NOP);
- if (!flush_page_for_dma) {
+ if (!BTFIXUPVAL_CALL(flush_page_for_dma)) {
/* IO coherent chip */
- mmu_get_scsi_one = iommu_get_scsi_one_noflush;
- mmu_get_scsi_sgl = iommu_get_scsi_sgl_noflush;
+ BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_noflush, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_noflush, BTFIXUPCALL_NORM);
} else if (flush_page_for_dma_global) {
/* flush_page_for_dma flushes everything, no matter of what page is it */
- mmu_get_scsi_one = iommu_get_scsi_one_gflush;
- mmu_get_scsi_sgl = iommu_get_scsi_sgl_gflush;
+ BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_gflush, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_gflush, BTFIXUPCALL_NORM);
} else {
- mmu_get_scsi_one = iommu_get_scsi_one_pflush;
- mmu_get_scsi_sgl = iommu_get_scsi_sgl_pflush;
+ BTFIXUPSET_CALL(mmu_get_scsi_one, iommu_get_scsi_one_pflush, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_get_scsi_sgl, iommu_get_scsi_sgl_pflush, BTFIXUPCALL_NORM);
}
- mmu_release_scsi_one = iommu_release_scsi_one;
- mmu_release_scsi_sgl = iommu_release_scsi_sgl;
+ BTFIXUPSET_CALL(mmu_release_scsi_one, iommu_release_scsi_one, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(mmu_release_scsi_sgl, iommu_release_scsi_sgl, BTFIXUPCALL_NOP);
#ifdef CONFIG_SBUS
- mmu_map_dma_area = iommu_map_dma_area;
+ BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM);
#endif
}
diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c
index 10eebecce..b38eea6d8 100644
--- a/arch/sparc/mm/loadmmu.c
+++ b/arch/sparc/mm/loadmmu.c
@@ -1,9 +1,10 @@
-/* $Id: loadmmu.c,v 1.46 1997/04/10 05:12:51 davem Exp $
+/* $Id: loadmmu.c,v 1.50 1998/02/05 14:19:02 jj Exp $
* loadmmu.c: This code loads up all the mm function pointers once the
* machine type has been determined. It also sets the static
* mmu values such as PAGE_NONE, etc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/kernel.h>
@@ -16,6 +17,7 @@
#include <asm/pgtable.h>
#include <asm/a.out.h>
#include <asm/mmu_context.h>
+#include <asm/oplib.h>
unsigned long page_offset = 0xf0000000;
unsigned long stack_top = 0xf0000000 - PAGE_SIZE;
@@ -24,132 +26,8 @@ struct ctx_list *ctx_list_pool;
struct ctx_list ctx_free;
struct ctx_list ctx_used;
-unsigned long (*alloc_kernel_stack)(struct task_struct *tsk);
-void (*free_kernel_stack)(unsigned long stack);
-struct task_struct *(*alloc_task_struct)(void);
-void (*free_task_struct)(struct task_struct *tsk);
-
-void (*quick_kernel_fault)(unsigned long);
-
-void (*init_new_context)(struct mm_struct *mm);
-void (*destroy_context)(struct mm_struct *mm);
-
-/* translate between physical and virtual addresses */
-unsigned long (*mmu_v2p)(unsigned long);
-unsigned long (*mmu_p2v)(unsigned long);
-
-char *(*mmu_lockarea)(char *, unsigned long);
-void (*mmu_unlockarea)(char *, unsigned long);
-
-__u32 (*mmu_get_scsi_one)(char *, unsigned long, struct linux_sbus *sbus);
-void (*mmu_get_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
-void (*mmu_release_scsi_one)(__u32, unsigned long, struct linux_sbus *sbus);
-void (*mmu_release_scsi_sgl)(struct mmu_sglist *, int, struct linux_sbus *sbus);
-
-void (*mmu_map_dma_area)(unsigned long addr, int len);
-
-void (*update_mmu_cache)(struct vm_area_struct *vma, unsigned long address, pte_t pte);
-
-#ifdef __SMP__
-void (*local_flush_cache_all)(void);
-void (*local_flush_cache_mm)(struct mm_struct *);
-void (*local_flush_cache_range)(struct mm_struct *, unsigned long start,
- unsigned long end);
-void (*local_flush_cache_page)(struct vm_area_struct *, unsigned long address);
-
-void (*local_flush_tlb_all)(void);
-void (*local_flush_tlb_mm)(struct mm_struct *);
-void (*local_flush_tlb_range)(struct mm_struct *, unsigned long start,
- unsigned long end);
-void (*local_flush_tlb_page)(struct vm_area_struct *, unsigned long address);
-void (*local_flush_page_to_ram)(unsigned long address);
-void (*local_flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr);
-#endif
-
-void (*flush_cache_all)(void);
-void (*flush_cache_mm)(struct mm_struct *);
-void (*flush_cache_range)(struct mm_struct *, unsigned long start,
- unsigned long end);
-void (*flush_cache_page)(struct vm_area_struct *, unsigned long address);
-
-void (*flush_tlb_all)(void);
-void (*flush_tlb_mm)(struct mm_struct *);
-void (*flush_tlb_range)(struct mm_struct *, unsigned long start,
- unsigned long end);
-void (*flush_tlb_page)(struct vm_area_struct *, unsigned long address);
-
-void (*flush_page_to_ram)(unsigned long page);
-
-void (*flush_sig_insns)(struct mm_struct *mm, unsigned long insn_addr);
-
-void (*set_pte)(pte_t *pteptr, pte_t pteval);
-
-unsigned int pmd_shift, pmd_size, pmd_mask;
-unsigned int (*pmd_align)(unsigned int);
-unsigned int pgdir_shift, pgdir_size, pgdir_mask;
-unsigned int (*pgdir_align)(unsigned int);
-unsigned int ptrs_per_pte, ptrs_per_pmd, ptrs_per_pgd;
unsigned int pg_iobits;
-pgprot_t page_none, page_shared, page_copy, page_readonly, page_kernel;
-
-unsigned long (*pte_page)(pte_t);
-unsigned long (*pmd_page)(pmd_t);
-unsigned long (*pgd_page)(pgd_t);
-
-void (*sparc_update_rootmmu_dir)(struct task_struct *, pgd_t *pgdir);
-unsigned long (*(vmalloc_start))(void);
-void (*switch_to_context)(struct task_struct *tsk);
-
-int (*pte_none)(pte_t);
-int (*pte_present)(pte_t);
-void (*pte_clear)(pte_t *);
-
-int (*pmd_none)(pmd_t);
-int (*pmd_bad)(pmd_t);
-int (*pmd_present)(pmd_t);
-void (*pmd_clear)(pmd_t *);
-
-int (*pgd_none)(pgd_t);
-int (*pgd_bad)(pgd_t);
-int (*pgd_present)(pgd_t);
-void (*pgd_clear)(pgd_t *);
-
-pte_t (*mk_pte)(unsigned long, pgprot_t);
-pte_t (*mk_pte_phys)(unsigned long, pgprot_t);
-pte_t (*mk_pte_io)(unsigned long, pgprot_t, int);
-void (*pgd_set)(pgd_t *, pmd_t *);
-pte_t (*pte_modify)(pte_t, pgprot_t);
-pgd_t * (*pgd_offset)(struct mm_struct *, unsigned long);
-pmd_t * (*pmd_offset)(pgd_t *, unsigned long);
-pte_t * (*pte_offset)(pmd_t *, unsigned long);
-void (*pte_free_kernel)(pte_t *);
-pte_t * (*pte_alloc_kernel)(pmd_t *, unsigned long);
-
-void (*pmd_free_kernel)(pmd_t *);
-pmd_t * (*pmd_alloc_kernel)(pgd_t *, unsigned long);
-void (*pte_free)(pte_t *);
-pte_t * (*pte_alloc)(pmd_t *, unsigned long);
-
-void (*pmd_free)(pmd_t *);
-pmd_t * (*pmd_alloc)(pgd_t *, unsigned long);
-void (*pgd_free)(pgd_t *);
-
-pgd_t * (*pgd_alloc)(void);
-
-int (*pte_write)(pte_t);
-int (*pte_dirty)(pte_t);
-int (*pte_young)(pte_t);
-
-pte_t (*pte_wrprotect)(pte_t);
-pte_t (*pte_mkclean)(pte_t);
-pte_t (*pte_mkold)(pte_t);
-pte_t (*pte_mkwrite)(pte_t);
-pte_t (*pte_mkdirty)(pte_t);
-pte_t (*pte_mkyoung)(pte_t);
-
-char *(*mmu_info)(void);
-
extern void ld_mmu_sun4c(void);
extern void ld_mmu_srmmu(void);
@@ -157,6 +35,7 @@ __initfunc(void load_mmu(void))
{
switch(sparc_cpu_model) {
case sun4c:
+ case sun4:
ld_mmu_sun4c();
break;
case sun4m:
@@ -169,9 +48,8 @@ __initfunc(void load_mmu(void))
break;
#endif
default:
- printk("load_mmu:MMU support not available for this architecture\n");
- printk("load_mmu:sparc_cpu_model = %d\n", (int) sparc_cpu_model);
- printk("load_mmu:Halting...\n");
- panic("load_mmu()");
+ prom_printf("load_mmu: %d unsupported\n", (int)sparc_cpu_model);
+ prom_halt();
}
+ btfixup();
}
diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c
new file mode 100644
index 000000000..f82599f42
--- /dev/null
+++ b/arch/sparc/mm/nosrmmu.c
@@ -0,0 +1,50 @@
+/* $Id: nosrmmu.c,v 1.1 1998/03/09 14:04:15 jj Exp $
+ * nosrmmu.c: This file is a bunch of dummies for sun4 compiles,
+ * so that it does not need srmmu and avoid ifdefs.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/mbus.h>
+
+static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n";
+
+enum mbus_module srmmu_modtype;
+
+__initfunc(static void should_not_happen(void))
+{
+ prom_printf(shouldnothappen);
+ prom_halt();
+}
+
+__initfunc(void srmmu_frob_mem_map(unsigned long start_mem))
+{
+ should_not_happen();
+}
+
+__initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem))
+{
+ should_not_happen();
+ return 0;
+}
+
+__initfunc(void ld_mmu_srmmu(void))
+{
+ should_not_happen();
+}
+
+void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+}
+
+void srmmu_unmapioaddr(unsigned long virt_addr)
+{
+}
+
+__initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *mem_end_p))
+{
+ return 0;
+}
diff --git a/arch/sparc/mm/nosun4c.c b/arch/sparc/mm/nosun4c.c
new file mode 100644
index 000000000..7da883a31
--- /dev/null
+++ b/arch/sparc/mm/nosun4c.c
@@ -0,0 +1,77 @@
+/* $Id: nosun4c.c,v 1.1 1998/03/09 14:04:16 jj Exp $
+ * nosun4c.c: This file is a bunch of dummies for SMP compiles,
+ * so that it does not need sun4c and avoid ifdefs.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/pgtable.h>
+
+static char shouldnothappen[] __initdata = "32bit SMP kernel only supports sun4m and sun4d\n";
+
+/* Dummies */
+struct sun4c_mmu_ring {
+ unsigned long xxx1[3];
+ unsigned char xxx2[2];
+ int xxx3;
+};
+struct sun4c_mmu_ring sun4c_kernel_ring;
+struct sun4c_mmu_ring sun4c_kfree_ring;
+unsigned long sun4c_kernel_faults;
+unsigned long *sun4c_memerr_reg;
+
+__initfunc(static void should_not_happen(void))
+{
+ prom_printf(shouldnothappen);
+ prom_halt();
+}
+
+__initfunc(unsigned long sun4c_paging_init(unsigned long start_mem, unsigned long end_mem))
+{
+ should_not_happen();
+ return 0;
+}
+
+__initfunc(void ld_mmu_sun4c(void))
+{
+ should_not_happen();
+}
+
+void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+}
+
+void sun4c_unmapioaddr(unsigned long virt_addr)
+{
+}
+
+void sun4c_complete_all_stores(void)
+{
+}
+
+pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
+{
+ return NULL;
+}
+
+pte_t *sun4c_pte_offset(pmd_t * dir, unsigned long address)
+{
+ return NULL;
+}
+
+void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+}
+
+__initfunc(void sun4c_probe_vac(void))
+{
+ should_not_happen();
+}
+
+__initfunc(void sun4c_probe_memerr_reg(void))
+{
+ should_not_happen();
+}
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
index b16e3cc1e..f9794125d 100644
--- a/arch/sparc/mm/srmmu.c
+++ b/arch/sparc/mm/srmmu.c
@@ -1,10 +1,10 @@
-/* $Id: srmmu.c,v 1.156 1997/11/28 14:23:42 jj Exp $
+/* $Id: srmmu.c,v 1.170 1998/03/09 14:04:01 jj Exp $
* srmmu.c: SRMMU specific routines for memory management.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
@@ -30,6 +30,7 @@
#include <asm/a.out.h>
#include <asm/mmu_context.h>
#include <asm/io-unit.h>
+#include <asm/spinlock.h>
/* Now the cpu specific definitions. */
#include <asm/viking.h>
@@ -39,6 +40,11 @@
#include <asm/swift.h>
#include <asm/turbosparc.h>
+#include <asm/btfixup.h>
+
+/* #define DEBUG_MAP_KERNEL */
+/* #define PAGESKIP_DEBUG */
+
enum mbus_module srmmu_modtype;
unsigned int hwbug_bitmask;
int vac_cache_size;
@@ -47,10 +53,6 @@ int vac_badbits;
extern unsigned long sparc_iobase_vaddr;
-extern unsigned long sun4d_dma_base;
-extern unsigned long sun4d_dma_size;
-extern unsigned long sun4d_dma_vbase;
-
#ifdef __SMP__
#define FLUSH_BEGIN(mm)
#define FLUSH_END
@@ -60,16 +62,24 @@ extern unsigned long sun4d_dma_vbase;
#endif
static int phys_mem_contig;
-long page_contig_offset;
+BTFIXUPDEF_SETHI(page_contig_offset)
+
+BTFIXUPDEF_CALL(void, ctxd_set, ctxd_t *, pgd_t *)
+BTFIXUPDEF_CALL(void, pmd_set, pmd_t *, pte_t *)
-static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
-static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
+#define ctxd_set(ctxp,pgdp) BTFIXUP_CALL(ctxd_set)(ctxp,pgdp)
+#define pmd_set(pmdp,ptep) BTFIXUP_CALL(pmd_set)(pmdp,ptep)
-void (*flush_page_for_dma)(unsigned long page);
+BTFIXUPDEF_CALL(void, flush_page_for_dma, unsigned long)
+BTFIXUPDEF_CALL(void, flush_chunk, unsigned long)
+
+#define flush_page_for_dma(page) BTFIXUP_CALL(flush_page_for_dma)(page)
int flush_page_for_dma_global = 1;
-static void (*flush_chunk)(unsigned long chunk);
+#define flush_chunk(chunk) BTFIXUP_CALL(flush_chunk)(chunk)
#ifdef __SMP__
-static void (*local_flush_page_for_dma)(unsigned long page);
+BTFIXUPDEF_CALL(void, local_flush_page_for_dma, unsigned long)
+
+#define local_flush_page_for_dma(page) BTFIXUP_CALL(local_flush_page_for_dma)(page)
#endif
static struct srmmu_stats {
@@ -79,7 +89,7 @@ static struct srmmu_stats {
int invmm;
} module_stats;
-static char *srmmu_name;
+char *srmmu_name;
ctxd_t *srmmu_ctx_table_phys;
ctxd_t *srmmu_context_table;
@@ -96,8 +106,8 @@ static struct srmmu_trans {
#define SRMMU_HASHSZ 256
/* Not static, viking.S uses it. */
-struct srmmu_trans *srmmu_v2p_hash[SRMMU_HASHSZ];
-static struct srmmu_trans *srmmu_p2v_hash[SRMMU_HASHSZ];
+unsigned long srmmu_v2p_hash[SRMMU_HASHSZ];
+static unsigned long srmmu_p2v_hash[SRMMU_HASHSZ];
#define srmmu_ahashfn(addr) ((addr) >> 24)
@@ -111,20 +121,17 @@ int viking_mxcc_present = 0;
*/
static inline unsigned long srmmu_v2p(unsigned long vaddr)
{
- struct srmmu_trans *tp = srmmu_v2p_hash[srmmu_ahashfn(vaddr)];
-
- if(tp)
- return (vaddr - tp->vbase + tp->pbase);
- else
- return 0xffffffffUL;
+ unsigned long off = srmmu_v2p_hash[srmmu_ahashfn(vaddr)];
+
+ return (vaddr + off);
}
static inline unsigned long srmmu_p2v(unsigned long paddr)
{
- struct srmmu_trans *tp = srmmu_p2v_hash[srmmu_ahashfn(paddr)];
-
- if(tp)
- return (paddr - tp->pbase + tp->vbase);
+ unsigned long off = srmmu_p2v_hash[srmmu_ahashfn(paddr)];
+
+ if (off != 0xffffffffUL)
+ return (paddr - off);
else
return 0xffffffffUL;
}
@@ -132,16 +139,47 @@ static inline unsigned long srmmu_p2v(unsigned long paddr)
/* Physical memory on most SS1000/SC2000 can be contiguous, so we handle that case
* as a special case to make things faster.
*/
+/* FIXME: gcc is stupid here and generates very very bad code in this
+ * heavily used routine. So we help it a bit. */
static inline unsigned long srmmu_c_v2p(unsigned long vaddr)
{
+#if KERNBASE != 0xf0000000
if (vaddr >= KERNBASE) return vaddr - KERNBASE;
- return (vaddr - page_contig_offset);
+ return vaddr - BTFIXUP_SETHI(page_contig_offset);
+#else
+ register unsigned long kernbase;
+
+ __asm__ ("sethi %%hi(0xf0000000), %0" : "=r"(kernbase));
+ return vaddr - ((vaddr >= kernbase) ? kernbase : BTFIXUP_SETHI(page_contig_offset));
+#endif
}
static inline unsigned long srmmu_c_p2v(unsigned long paddr)
{
+#if KERNBASE != 0xf0000000
if (paddr < (0xfd000000 - KERNBASE)) return paddr + KERNBASE;
- return (paddr + page_contig_offset);
+ return (paddr + BTFIXUP_SETHI(page_contig_offset));
+#else
+ register unsigned long kernbase;
+ register unsigned long limit;
+
+ __asm__ ("sethi %%hi(0x0d000000), %0" : "=r"(limit));
+ __asm__ ("sethi %%hi(0xf0000000), %0" : "=r"(kernbase));
+
+ return paddr + ((paddr < limit) ? kernbase : BTFIXUP_SETHI(page_contig_offset));
+#endif
+}
+
+/* On boxes where there is no lots_of_ram, KERNBASE is mapped to PA<0> and highest
+ PA is below 0x0d000000, we can optimize even more :) */
+static inline unsigned long srmmu_s_v2p(unsigned long vaddr)
+{
+ return vaddr - PAGE_OFFSET;
+}
+
+static inline unsigned long srmmu_s_p2v(unsigned long paddr)
+{
+ return paddr + PAGE_OFFSET;
}
/* In general all page table modifications should use the V8 atomic
@@ -157,19 +195,43 @@ static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
/* Functions really use this, not srmmu_swap directly. */
#define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry))
+#ifdef PAGESKIP_DEBUG
+#define PGSKIP_DEBUG(from,to) prom_printf("PG_skip %ld->%ld\n", (long)(from), (long)(to)); printk("PG_skip %ld->%ld\n", (long)(from), (long)(to))
+#else
+#define PGSKIP_DEBUG(from,to) do { } while (0)
+#endif
+
__initfunc(void srmmu_frob_mem_map(unsigned long start_mem))
{
- unsigned long bank_start, bank_end;
+ unsigned long bank_start, bank_end = 0;
unsigned long addr;
int i;
/* First, mark all pages as invalid. */
for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
+
+ /* Next, pg[0-3] is sun4c cruft, so we can free it... */
+ mem_map[MAP_NR(pg0)].flags &= ~(1<<PG_reserved);
+ mem_map[MAP_NR(pg1)].flags &= ~(1<<PG_reserved);
+ mem_map[MAP_NR(pg2)].flags &= ~(1<<PG_reserved);
+ mem_map[MAP_NR(pg3)].flags &= ~(1<<PG_reserved);
start_mem = PAGE_ALIGN(start_mem);
for(i = 0; srmmu_map[i].size; i++) {
bank_start = srmmu_map[i].vbase;
+
+ if (i && bank_start - bank_end > 2 * PAGE_SIZE) {
+ mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+ mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(bank_start);
+ PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(bank_start));
+ if (bank_end > KERNBASE && bank_start < KERNBASE) {
+ mem_map[0].flags |= (1<<PG_skip);
+ mem_map[0].next_hash = mem_map + MAP_NR(bank_start);
+ PGSKIP_DEBUG(0, MAP_NR(bank_start));
+ }
+ }
+
bank_end = bank_start + srmmu_map[i].size;
while(bank_start < bank_end) {
if((bank_start >= KERNBASE) &&
@@ -180,23 +242,28 @@ __initfunc(void srmmu_frob_mem_map(unsigned long start_mem))
mem_map[MAP_NR(bank_start)].flags &= ~(1<<PG_reserved);
bank_start += PAGE_SIZE;
}
+
+ if (bank_end == 0xfd000000)
+ bank_end = PAGE_OFFSET;
}
- if (sparc_cpu_model == sun4d) {
- for (addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE)
- if (addr < sun4d_dma_vbase || addr >= sun4d_dma_vbase + sun4d_dma_size)
- clear_bit(PG_DMA, &mem_map[MAP_NR(addr)].flags);
+
+ if (bank_end < KERNBASE) {
+ mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+ mem_map[MAP_NR(bank_end)].next_hash = mem_map + MAP_NR(KERNBASE);
+ PGSKIP_DEBUG(MAP_NR(bank_end), MAP_NR(KERNBASE));
+ } else if (MAP_NR(bank_end) < max_mapnr) {
+ mem_map[MAP_NR(bank_end)].flags |= (1<<PG_skip);
+ if (mem_map[0].flags & (1 << PG_skip)) {
+ mem_map[MAP_NR(bank_end)].next_hash = mem_map[0].next_hash;
+ PGSKIP_DEBUG(MAP_NR(bank_end), mem_map[0].next_hash - mem_map);
+ } else {
+ mem_map[MAP_NR(bank_end)].next_hash = mem_map;
+ PGSKIP_DEBUG(MAP_NR(bank_end), 0);
+ }
}
}
/* The very generic SRMMU page table operations. */
-static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
-static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); }
-
-static unsigned long srmmu_vmalloc_start(void)
-{
- return SRMMU_VMALLOC_START;
-}
-
static inline int srmmu_device_memory(unsigned long x)
{
return ((x & 0xF0000000) != 0);
@@ -220,44 +287,53 @@ static unsigned long srmmu_c_pmd_page(pmd_t pmd)
static unsigned long srmmu_c_pte_page(pte_t pte)
{ return srmmu_device_memory(pte_val(pte))?~0:srmmu_c_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
-static int srmmu_pte_none(pte_t pte)
+static unsigned long srmmu_s_pgd_page(pgd_t pgd)
+{ return srmmu_device_memory(pgd_val(pgd))?~0:srmmu_s_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
+
+static unsigned long srmmu_s_pmd_page(pmd_t pmd)
+{ return srmmu_device_memory(pmd_val(pmd))?~0:srmmu_s_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
+
+static unsigned long srmmu_s_pte_page(pte_t pte)
+{ return srmmu_device_memory(pte_val(pte))?~0:srmmu_s_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
+
+static inline int srmmu_pte_none(pte_t pte)
{ return !(pte_val(pte) & 0xFFFFFFF); }
-static int srmmu_pte_present(pte_t pte)
+static inline int srmmu_pte_present(pte_t pte)
{ return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE); }
-static void srmmu_pte_clear(pte_t *ptep) { set_pte(ptep, __pte(0)); }
+static inline void srmmu_pte_clear(pte_t *ptep) { set_pte(ptep, __pte(0)); }
-static int srmmu_pmd_none(pmd_t pmd)
+static inline int srmmu_pmd_none(pmd_t pmd)
{ return !(pmd_val(pmd) & 0xFFFFFFF); }
-static int srmmu_pmd_bad(pmd_t pmd)
+static inline int srmmu_pmd_bad(pmd_t pmd)
{ return (pmd_val(pmd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }
-static int srmmu_pmd_present(pmd_t pmd)
+static inline int srmmu_pmd_present(pmd_t pmd)
{ return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
-static void srmmu_pmd_clear(pmd_t *pmdp) { set_pte((pte_t *)pmdp, __pte(0)); }
+static inline void srmmu_pmd_clear(pmd_t *pmdp) { set_pte((pte_t *)pmdp, __pte(0)); }
-static int srmmu_pgd_none(pgd_t pgd)
+static inline int srmmu_pgd_none(pgd_t pgd)
{ return !(pgd_val(pgd) & 0xFFFFFFF); }
-static int srmmu_pgd_bad(pgd_t pgd)
+static inline int srmmu_pgd_bad(pgd_t pgd)
{ return (pgd_val(pgd) & SRMMU_ET_MASK) != SRMMU_ET_PTD; }
-static int srmmu_pgd_present(pgd_t pgd)
+static inline int srmmu_pgd_present(pgd_t pgd)
{ return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
-static void srmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); }
+static inline void srmmu_pgd_clear(pgd_t * pgdp) { set_pte((pte_t *)pgdp, __pte(0)); }
-static int srmmu_pte_write(pte_t pte) { return pte_val(pte) & SRMMU_WRITE; }
-static int srmmu_pte_dirty(pte_t pte) { return pte_val(pte) & SRMMU_DIRTY; }
-static int srmmu_pte_young(pte_t pte) { return pte_val(pte) & SRMMU_REF; }
+static inline int srmmu_pte_write(pte_t pte) { return pte_val(pte) & SRMMU_WRITE; }
+static inline int srmmu_pte_dirty(pte_t pte) { return pte_val(pte) & SRMMU_DIRTY; }
+static inline int srmmu_pte_young(pte_t pte) { return pte_val(pte) & SRMMU_REF; }
-static pte_t srmmu_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_WRITE);}
-static pte_t srmmu_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_DIRTY);}
-static pte_t srmmu_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_REF);}
-static pte_t srmmu_pte_mkwrite(pte_t pte) { return __pte(pte_val(pte) | SRMMU_WRITE);}
-static pte_t srmmu_pte_mkdirty(pte_t pte) { return __pte(pte_val(pte) | SRMMU_DIRTY);}
-static pte_t srmmu_pte_mkyoung(pte_t pte) { return __pte(pte_val(pte) | SRMMU_REF);}
+static inline pte_t srmmu_pte_wrprotect(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_WRITE);}
+static inline pte_t srmmu_pte_mkclean(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_DIRTY);}
+static inline pte_t srmmu_pte_mkold(pte_t pte) { return __pte(pte_val(pte) & ~SRMMU_REF);}
+static inline pte_t srmmu_pte_mkwrite(pte_t pte) { return __pte(pte_val(pte) | SRMMU_WRITE);}
+static inline pte_t srmmu_pte_mkdirty(pte_t pte) { return __pte(pte_val(pte) | SRMMU_DIRTY);}
+static inline pte_t srmmu_pte_mkyoung(pte_t pte) { return __pte(pte_val(pte) | SRMMU_REF);}
/*
* Conversion functions: convert a page and protection to a page entry,
@@ -269,6 +345,9 @@ static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot)
static pte_t srmmu_c_mk_pte(unsigned long page, pgprot_t pgprot)
{ return __pte(((srmmu_c_v2p(page)) >> 4) | pgprot_val(pgprot)); }
+static pte_t srmmu_s_mk_pte(unsigned long page, pgprot_t pgprot)
+{ return __pte(((srmmu_s_v2p(page)) >> 4) | pgprot_val(pgprot)); }
+
static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot)
{ return __pte(((page) >> 4) | pgprot_val(pgprot)); }
@@ -307,41 +386,64 @@ static void srmmu_c_pmd_set(pmd_t * pmdp, pte_t * ptep)
set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) ptep) >> 4)));
}
-static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
+static void srmmu_s_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
+{
+ set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) pgdp) >> 4)));
+}
+
+static void srmmu_s_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+ set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) pmdp) >> 4)));
+}
+
+static void srmmu_s_pmd_set(pmd_t * pmdp, pte_t * ptep)
+{
+ set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_s_v2p((unsigned long) ptep) >> 4)));
+}
+
+static inline pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
{
return __pte((pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot));
}
/* to find an entry in a top-level page table... */
-static pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address)
+static inline pgd_t *srmmu_pgd_offset(struct mm_struct * mm, unsigned long address)
{
- return mm->pgd + ((address >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1));
+ return mm->pgd + (address >> SRMMU_PGDIR_SHIFT);
}
/* Find an entry in the second-level page table.. */
-static pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
+static inline pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
{
return (pmd_t *) srmmu_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
}
/* Find an entry in the third-level page table.. */
-static pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
+static inline pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
{
return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
}
-/* Find an entry in the second-level page table.. */
-static pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address)
+static inline pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address)
{
return (pmd_t *) srmmu_c_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
}
-/* Find an entry in the third-level page table.. */
-static pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address)
+static inline pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address)
{
return (pte_t *) srmmu_c_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
}
+static inline pmd_t *srmmu_s_pmd_offset(pgd_t * dir, unsigned long address)
+{
+ return (pmd_t *) srmmu_s_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+}
+
+static inline pte_t *srmmu_s_pte_offset(pmd_t * dir, unsigned long address)
+{
+ return (pte_t *) srmmu_s_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+}
+
/* This must update the context table entry for this process. */
static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp)
{
@@ -352,334 +454,146 @@ static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp)
}
}
-static inline void srmmu_putpage(unsigned long page)
-{
- free_page(page);
-}
-
-#define LC_HIGH_WATER 128
-#define BC_HIGH_WATER 32
-
-static unsigned long *lcnks = 0;
-static unsigned long *bcnks = 0;
-static int lcwater = 0;
-static int bcwater = 0;
-static int chunk_pages = 0;
-static int clct_pages = 0;
-
-#define RELAX_JIFFIES 16
-
-static int lcjiffies;
-static int bcjiffies;
-
-struct chunk {
- struct chunk *next;
- struct chunk *prev;
- struct chunk *npage;
- struct chunk *ppage;
- int count;
-};
-
-static int garbage_calls = 0;
-
-#define OTHER_PAGE(p,q) (((unsigned long)(p) ^ (unsigned long)(q)) & PAGE_MASK)
-
-static int garbage_collect(unsigned long **cnks, int n, int cpp)
+static inline pte_t *srmmu_get_pte_fast(void)
{
- struct chunk *root = (struct chunk *)*cnks;
- struct chunk *p, *q, *curr, *next;
- int water = n;
-
- next = root->next;
- curr = root->prev = root->next = root->npage = root->ppage = root;
- root->count = 1;
-
- garbage_calls++;
-
- while (--n) {
- p = next;
- next = next->next;
-
- if (OTHER_PAGE(p, curr)) {
-
- q = curr->npage;
- while (q != curr) {
- if (!OTHER_PAGE(p, q))
- break;
- q = q->npage;
- }
-
- if (q == curr) {
-
- (p->npage = curr->npage)->ppage = p;
- curr->npage = p;
- p->ppage = curr;
-
- p->next = p->prev = p;
- p->count = 1;
-
- curr = p;
-
- continue;
- }
- curr = q;
- }
-
- (p->next = curr->next)->prev = p;
- curr->next = p;
- p->prev = curr;
-
- if (++curr->count == cpp) {
-
- q = curr->npage;
- if (curr == q) {
-
- srmmu_putpage((unsigned long)curr & PAGE_MASK);
- water -= cpp;
-
- clct_pages++;
- chunk_pages--;
-
- if (--n) {
- p = next;
- next = next->next;
-
- curr = root->prev =
- root->next = root->npage =
- root->ppage = root = p;
- root->count = 1;
-
- continue;
- }
- return 0;
- }
-
- if (curr == root)
- root = q;
-
- curr->ppage->npage = q;
- q->ppage = curr->ppage;
-
- srmmu_putpage((unsigned long)curr & PAGE_MASK);
- water -= cpp;
-
- clct_pages++;
- chunk_pages--;
-
- curr = q;
- }
- }
-
- p = root;
- while (p->npage != root) {
- p->prev->next = p->npage;
- p = p->npage;
+ struct page *ret;
+
+ spin_lock(&pte_spinlock);
+ if ((ret = (struct page *)pte_quicklist) != NULL) {
+ unsigned int mask = (unsigned int)ret->pprev_hash;
+ unsigned int tmp, off;
+
+ if (mask & 0xff)
+ for (tmp = 0x001, off = 0; (mask & tmp) == 0; tmp <<= 1, off += 256);
+ else
+ for (tmp = 0x100, off = 2048; (mask & tmp) == 0; tmp <<= 1, off += 256);
+ (unsigned int)ret->pprev_hash = mask & ~tmp;
+ if (!(mask & ~tmp))
+ pte_quicklist = (unsigned long *)ret->next_hash;
+ ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off);
+ pgtable_cache_size--;
}
-
- *cnks = (unsigned long *)root;
- return water;
+ spin_unlock(&pte_spinlock);
+ return (pte_t *)ret;
}
-static unsigned long *get_small_chunk(void)
+static inline pte_t *srmmu_get_pte_slow(void)
{
- unsigned long *rval;
- unsigned long flags;
-
- save_and_cli(flags);
- if(lcwater) {
- lcwater--;
- rval = lcnks;
- lcnks = (unsigned long *) *rval;
- } else {
- rval = (unsigned long *) __get_free_page(GFP_KERNEL);
-
- if(!rval) {
- restore_flags(flags);
- return 0;
- }
- chunk_pages++;
-
- lcnks = (rval + 64);
-
- /* Cache stomping, I know... */
- *(rval + 64) = (unsigned long) (rval + 128);
- *(rval + 128) = (unsigned long) (rval + 192);
- *(rval + 192) = (unsigned long) (rval + 256);
- *(rval + 256) = (unsigned long) (rval + 320);
- *(rval + 320) = (unsigned long) (rval + 384);
- *(rval + 384) = (unsigned long) (rval + 448);
- *(rval + 448) = (unsigned long) (rval + 512);
- *(rval + 512) = (unsigned long) (rval + 576);
- *(rval + 576) = (unsigned long) (rval + 640);
- *(rval + 640) = (unsigned long) (rval + 704);
- *(rval + 704) = (unsigned long) (rval + 768);
- *(rval + 768) = (unsigned long) (rval + 832);
- *(rval + 832) = (unsigned long) (rval + 896);
- *(rval + 896) = (unsigned long) (rval + 960);
- *(rval + 960) = 0;
- lcwater = 15;
+ pte_t *ret;
+ struct page *page;
+
+ ret = (pte_t *)get_free_page(GFP_KERNEL);
+ if (ret) {
+ page = mem_map + MAP_NR(ret);
+ flush_chunk((unsigned long)ret);
+ (unsigned int)page->pprev_hash = 0xfffe;
+ spin_lock(&pte_spinlock);
+ (unsigned long *)page->next_hash = pte_quicklist;
+ pte_quicklist = (unsigned long *)page;
+ pgtable_cache_size += 15;
}
- lcjiffies = jiffies;
- restore_flags(flags);
- memset(rval, 0, 256);
- flush_chunk((unsigned long)rval);
- return rval;
-}
-
-static inline void free_small_chunk(unsigned long *it)
-{
- unsigned long flags;
-
- save_and_cli(flags);
- *it = (unsigned long) lcnks;
- lcnks = it;
- lcwater++;
-
- if ((lcwater > LC_HIGH_WATER) &&
- (jiffies > lcjiffies + RELAX_JIFFIES))
- lcwater = garbage_collect(&lcnks, lcwater, 16);
-
- restore_flags(flags);
+ return ret;
}
-static unsigned long *get_big_chunk(void)
+static inline pgd_t *srmmu_get_pgd_fast(void)
{
- unsigned long *rval;
- unsigned long flags;
-
- save_and_cli(flags);
- if(bcwater) {
- bcwater--;
- rval = bcnks;
- bcnks = (unsigned long *) *rval;
- } else {
- rval = (unsigned long *) __get_free_page(GFP_KERNEL);
-
- if(!rval) {
- restore_flags(flags);
- return 0;
- }
- chunk_pages++;
-
- bcnks = (rval + 256);
+ struct page *ret;
- /* Cache stomping, I know... */
- *(rval + 256) = (unsigned long) (rval + 512);
- *(rval + 512) = (unsigned long) (rval + 768);
- *(rval + 768) = 0;
- bcwater = 3;
+ spin_lock(&pgd_spinlock);
+ if ((ret = (struct page *)pgd_quicklist) != NULL) {
+ unsigned int mask = (unsigned int)ret->pprev_hash;
+ unsigned int tmp, off;
+
+ for (tmp = 0x001, off = 0; (mask & tmp) == 0; tmp <<= 1, off += 1024);
+ (unsigned int)ret->pprev_hash = mask & ~tmp;
+ if (!(mask & ~tmp))
+ pgd_quicklist = (unsigned long *)ret->next_hash;
+ ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off);
+ pgd_cache_size--;
}
- bcjiffies = jiffies;
- restore_flags(flags);
- memset(rval, 0, 1024);
- flush_chunk((unsigned long)rval);
- return rval;
+ spin_unlock(&pgd_spinlock);
+ return (pte_t *)ret;
}
-static inline void free_big_chunk(unsigned long *it)
+static inline pgd_t *srmmu_get_pgd_slow(void)
{
- unsigned long flags;
-
- save_and_cli(flags);
- *it = (unsigned long) bcnks;
- bcnks = it;
- bcwater++;
-
- if ((bcwater > BC_HIGH_WATER) &&
- (jiffies > bcjiffies + RELAX_JIFFIES))
- bcwater = garbage_collect(&bcnks, bcwater, 4);
-
- restore_flags(flags);
+ pgd_t *ret;
+ struct page *page;
+
+ ret = (pgd_t *)__get_free_page(GFP_KERNEL);
+ if (ret) {
+ pgd_t *init = pgd_offset(&init_mm, 0);
+ memset(ret + (0 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ memcpy(ret + (0 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ memset(ret + (1 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ memcpy(ret + (1 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ memset(ret + (2 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ memcpy(ret + (2 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ memset(ret + (3 * PTRS_PER_PGD), 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ memcpy(ret + (3 * PTRS_PER_PGD) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ page = mem_map + MAP_NR(ret);
+ flush_chunk((unsigned long)ret);
+ (unsigned int)page->pprev_hash = 0xe;
+ spin_lock(&pgd_spinlock);
+ (unsigned long *)page->next_hash = pgd_quicklist;
+ pgd_quicklist = (unsigned long *)page;
+ pgd_cache_size += 3;
+ spin_unlock(&pgd_spinlock);
+ }
+ return ret;
}
-#define NEW_PGD() (pgd_t *) get_big_chunk()
-#define NEW_PMD() (pmd_t *) get_small_chunk()
-#define NEW_PTE() (pte_t *) get_small_chunk()
-#define FREE_PGD(chunk) free_big_chunk((unsigned long *)(chunk))
-#define FREE_PMD(chunk) free_small_chunk((unsigned long *)(chunk))
-#define FREE_PTE(chunk) free_small_chunk((unsigned long *)(chunk))
-
-/*
- * Allocate and free page tables. The xxx_kernel() versions are
- * used to allocate a kernel page table - this turns on ASN bits
- * if any, and marks the page tables reserved.
- */
-static void srmmu_pte_free_kernel(pte_t *pte)
+static void srmmu_free_pte_slow(pte_t *pte)
{
- FREE_PTE(pte);
}
-static pte_t *srmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
+static void srmmu_free_pgd_slow(pgd_t *pgd)
{
- address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
- if(srmmu_pmd_none(*pmd)) {
- pte_t *page = NEW_PTE();
- if(srmmu_pmd_none(*pmd)) {
- if(page) {
- pmd_set(pmd, page);
- return page + address;
- }
- pmd_set(pmd, BAD_PAGETABLE);
- return NULL;
- }
- FREE_PTE(page);
- }
- if(srmmu_pmd_bad(*pmd)) {
- printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
- pmd_set(pmd, BAD_PAGETABLE);
- return NULL;
- }
- return (pte_t *) pmd_page(*pmd) + address;
}
-static void srmmu_pmd_free_kernel(pmd_t *pmd)
+static inline void srmmu_pte_free(pte_t *pte)
{
- FREE_PMD(pmd);
-}
+ struct page *page = mem_map + MAP_NR(pte);
-static pmd_t *srmmu_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
-{
- address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
- if(srmmu_pgd_none(*pgd)) {
- pmd_t *page;
- page = NEW_PMD();
- if(srmmu_pgd_none(*pgd)) {
- if(page) {
- pgd_set(pgd, page);
- return page + address;
- }
- pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
- return NULL;
- }
- FREE_PMD(page);
+ spin_lock(&pte_spinlock);
+ if (!page->pprev_hash) {
+ (unsigned long *)page->next_hash = pte_quicklist;
+ pte_quicklist = (unsigned long *)page;
}
- if(srmmu_pgd_bad(*pgd)) {
- printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
- pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
- return NULL;
- }
- return (pmd_t *) pgd_page(*pgd) + address;
-}
-
-static void srmmu_pte_free(pte_t *pte)
-{
- FREE_PTE(pte);
+ (unsigned int)page->pprev_hash |= (1 << ((((unsigned long)pte) >> 8) & 15));
+ pgtable_cache_size++;
+ spin_unlock(&pte_spinlock);
}
static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address)
{
address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
if(srmmu_pmd_none(*pmd)) {
- pte_t *page = NEW_PTE();
+ pte_t *page = srmmu_get_pte_fast();
+
+ if (page) {
+ pmd_set(pmd, page);
+ return page + address;
+ }
+ page = srmmu_get_pte_slow();
if(srmmu_pmd_none(*pmd)) {
if(page) {
+ spin_unlock(&pte_spinlock);
pmd_set(pmd, page);
return page + address;
}
pmd_set(pmd, BAD_PAGETABLE);
return NULL;
}
- FREE_PTE(page);
+ if (page) {
+ (unsigned int)(((struct page *)pte_quicklist)->pprev_hash) = 0xffff;
+ pgtable_cache_size++;
+ spin_unlock(&pte_spinlock);
+ }
}
if(srmmu_pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
@@ -692,23 +606,34 @@ static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address)
/* Real three-level page tables on SRMMU. */
static void srmmu_pmd_free(pmd_t * pmd)
{
- FREE_PMD(pmd);
+ return srmmu_pte_free((pte_t *)pmd);
}
static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address)
{
address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
if(srmmu_pgd_none(*pgd)) {
- pmd_t *page = NEW_PMD();
+ pmd_t *page = (pmd_t *)srmmu_get_pte_fast();
+
+ if (page) {
+ pgd_set(pgd, page);
+ return page + address;
+ }
+ page = (pmd_t *)srmmu_get_pte_slow();
if(srmmu_pgd_none(*pgd)) {
if(page) {
+ spin_unlock(&pte_spinlock);
pgd_set(pgd, page);
return page + address;
}
pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
return NULL;
}
- FREE_PMD(page);
+ if (page) {
+ (unsigned int)(((struct page *)pte_quicklist)->pprev_hash) = 0xffff;
+ pgtable_cache_size++;
+ spin_unlock(&pte_spinlock);
+ }
}
if(srmmu_pgd_bad(*pgd)) {
printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
@@ -720,12 +645,58 @@ static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address)
static void srmmu_pgd_free(pgd_t *pgd)
{
- FREE_PGD(pgd);
+ struct page *page = mem_map + MAP_NR(pgd);
+
+ spin_lock(&pgd_spinlock);
+ if (!page->pprev_hash) {
+ (unsigned long *)page->next_hash = pgd_quicklist;
+ pgd_quicklist = (unsigned long *)page;
+ }
+ (unsigned int)page->pprev_hash |= (1 << ((((unsigned long)pgd) >> 10) & 3));
+ pgd_cache_size++;
+ spin_unlock(&pgd_spinlock);
}
static pgd_t *srmmu_pgd_alloc(void)
{
- return NEW_PGD();
+ pgd_t *ret;
+
+ ret = srmmu_get_pgd_fast();
+ if (ret) return ret;
+ return srmmu_get_pgd_slow();
+}
+
+
+static void srmmu_set_pgdir(unsigned long address, pgd_t entry)
+{
+ struct task_struct * p;
+ struct page *page;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ if (!p->mm)
+ continue;
+ *pgd_offset(p->mm,address) = entry;
+ }
+ read_unlock(&tasklist_lock);
+ spin_lock(&pgd_spinlock);
+ address >>= SRMMU_PGDIR_SHIFT;
+ for (page = (struct page *)pgd_quicklist; page; page = page->next_hash) {
+ pgd_t *pgd = (pgd_t *)(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+ unsigned int mask = (unsigned int)page->pprev_hash;
+
+ if (mask & 1)
+ pgd[address + 0 * SRMMU_PTRS_PER_PGD] = entry;
+ if (mask & 2)
+ pgd[address + 1 * SRMMU_PTRS_PER_PGD] = entry;
+ if (mask & 4)
+ pgd[address + 2 * SRMMU_PTRS_PER_PGD] = entry;
+ if (mask & 8)
+ pgd[address + 3 * SRMMU_PTRS_PER_PGD] = entry;
+ if (mask)
+ flush_chunk((unsigned long)pgd);
+ }
+ spin_unlock(&pgd_spinlock);
}
static void srmmu_set_pte_cacheable(pte_t *ptep, pte_t pteval)
@@ -926,6 +897,19 @@ extern void tsunami_flush_tlb_mm(struct mm_struct *mm);
extern void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end);
extern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
+/* Workaround, until we find what's going on with Swift. When low on memory, it sometimes
+ * loops in fault/handle_mm_fault incl. flush_tlb_page to find out it is already in page tables/
+ * fault again on the same instruction. I really don't understand it, have checked it and contexts
+ * are right, flush_tlb_all is done as well, and it faults again... Strange. -jj
+ */
+static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
+{
+ static unsigned long last;
+
+ if (last == address) viking_hwprobe(address);
+ last = address;
+}
+
/* Swift flushes. It has the recommended SRMMU specification flushing
* facilities, so we can do things in a more fine grained fashion than we
* could on the tsunami. Let's watch out for HARDWARE BUGS...
@@ -1191,12 +1175,10 @@ static void cypress_flush_chunk(unsigned long chunk)
cypress_flush_page_to_ram(chunk);
}
-#if NOTUSED
/* Cypress is also IO cache coherent. */
static void cypress_flush_page_for_dma(unsigned long page)
{
}
-#endif
/* Cypress has unified L2 VIPT, from which both instructions and data
* are stored. It does not have an onboard icache of any sort, therefore
@@ -1282,9 +1264,8 @@ extern void viking_flush_sig_insns(struct mm_struct *mm, unsigned long addr);
extern void viking_flush_page(unsigned long page);
extern void viking_mxcc_flush_page(unsigned long page);
extern void viking_flush_chunk(unsigned long chunk);
-extern void viking_c_flush_page(unsigned long page);
-extern void viking_c_mxcc_flush_page(unsigned long page);
extern void viking_c_flush_chunk(unsigned long chunk);
+extern void viking_s_flush_chunk(unsigned long chunk);
extern void viking_mxcc_flush_chunk(unsigned long chunk);
extern void viking_flush_tlb_all(void);
extern void viking_flush_tlb_mm(struct mm_struct *mm);
@@ -1481,7 +1462,7 @@ static inline void srmmu_allocate_ptable_skeleton(unsigned long start, unsigned
* looking at the prom's page table directly which is what most
* other OS's do. Yuck... this is much better.
*/
-void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)
+__initfunc(void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end))
{
pgd_t *pgdp;
pmd_t *pmdp;
@@ -1539,21 +1520,79 @@ void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)
}
}
-/* #define DEBUG_MAP_KERNEL */
-
#ifdef DEBUG_MAP_KERNEL
#define MKTRACE(foo) prom_printf foo
#else
#define MKTRACE(foo)
#endif
-static int lots_of_ram = 0;
-static int large_pte_optimize = 1;
+static int lots_of_ram __initdata = 0;
+static int srmmu_low_pa __initdata = 0;
+static unsigned long end_of_phys_memory __initdata = 0;
+
+__initfunc(void srmmu_end_memory(unsigned long memory_size, unsigned long *end_mem_p))
+{
+ unsigned int sum = 0;
+ unsigned long last = 0xff000000;
+ long first, cur;
+ unsigned long pa;
+ unsigned long total = 0;
+ int i;
+
+ pa = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
+ pa = (pa & SRMMU_PTE_PMASK) << 4;
+ if (!sp_banks[0].base_addr && pa == PAGE_SIZE) {
+ for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+ if (sp_banks[i].base_addr + sp_banks[i].num_bytes > 0x0d000000)
+ break;
+ }
+ if (!sp_banks[i].num_bytes) {
+ srmmu_low_pa = 1;
+ end_of_phys_memory = SRMMU_PGDIR_ALIGN(sp_banks[i-1].base_addr + sp_banks[i-1].num_bytes);
+ *end_mem_p = KERNBASE + end_of_phys_memory;
+ if (sp_banks[0].num_bytes >= (6 * 1024 * 1024) || end_of_phys_memory <= 0x06000000) {
+ /* Make sure there will be enough memory for the whole mem_map (even if sparse) */
+ return;
+ }
+ }
+ }
+ for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+ pa = sp_banks[i].base_addr;
+ first = (pa & (~SRMMU_PGDIR_MASK));
+ cur = (sp_banks[i].num_bytes + first - SRMMU_PGDIR_SIZE);
+ if (cur < 0) cur = 0;
+ if (!first || last != (pa & SRMMU_PGDIR_MASK))
+ total += SRMMU_PGDIR_SIZE;
+ sum += sp_banks[i].num_bytes;
+ if (memory_size) {
+ if (sum > memory_size) {
+ sp_banks[i].num_bytes -=
+ (sum - memory_size);
+ cur = (sp_banks[i].num_bytes + first - SRMMU_PGDIR_SIZE);
+ if (cur < 0) cur = 0;
+ total += SRMMU_PGDIR_ALIGN(cur);
+ sum = memory_size;
+ sp_banks[++i].base_addr = 0xdeadbeef;
+ sp_banks[i].num_bytes = 0;
+ break;
+ }
+ }
+ total += SRMMU_PGDIR_ALIGN(cur);
+ last = (sp_banks[i].base_addr + sp_banks[i].num_bytes - 1) & SRMMU_PGDIR_MASK;
+ }
+ if (total <= 0x0d000000)
+ *end_mem_p = KERNBASE + total;
+ else {
+ *end_mem_p = 0xfd000000;
+ lots_of_ram = 1;
+ }
+ end_of_phys_memory = total;
+}
#define KERNEL_PTE(page_shifted) ((page_shifted)|SRMMU_CACHE|SRMMU_PRIV|SRMMU_VALID)
/* Create a third-level SRMMU 16MB page mapping. */
-static inline void do_large_mapping(unsigned long vaddr, unsigned long phys_base)
+__initfunc(static void do_large_mapping(unsigned long vaddr, unsigned long phys_base))
{
pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, vaddr);
unsigned long big_pte;
@@ -1563,47 +1602,6 @@ static inline void do_large_mapping(unsigned long vaddr, unsigned long phys_base
*pgdp = __pgd(big_pte);
}
-/* Create second-level SRMMU 256K medium sized page mappings. */
-static inline void do_medium_mapping(unsigned long vaddr, unsigned long vend,
- unsigned long phys_base)
-{
- pgd_t *pgdp;
- pmd_t *pmdp;
- unsigned long medium_pte;
-
- MKTRACE(("dmm[v<%08lx,%08lx>-->p<%08lx>]", vaddr, vend, phys_base));
- while(vaddr < vend) {
- pgdp = srmmu_pgd_offset(init_task.mm, vaddr);
- pmdp = srmmu_early_pmd_offset(pgdp, vaddr);
- medium_pte = KERNEL_PTE(phys_base >> 4);
- *pmdp = __pmd(medium_pte);
- phys_base += SRMMU_PMD_SIZE;
- vaddr += SRMMU_PMD_SIZE;
- }
-}
-
-/* Create a normal set of SRMMU page mappings for the virtual range
- * START to END, using physical pages beginning at PHYS_BASE.
- */
-static inline void do_small_mapping(unsigned long start, unsigned long end,
- unsigned long phys_base)
-{
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
-
- MKTRACE(("dsm[v<%08lx,%08lx>-->p<%08lx>]", start, end, phys_base));
- while(start < end) {
- pgdp = srmmu_pgd_offset(init_task.mm, start);
- pmdp = srmmu_early_pmd_offset(pgdp, start);
- ptep = srmmu_early_pte_offset(pmdp, start);
-
- *ptep = __pte(KERNEL_PTE(phys_base >> 4));
- phys_base += PAGE_SIZE;
- start += PAGE_SIZE;
- }
-}
-
/* Look in the sp_bank for the given physical page, return the
* index number the entry was found in, or -1 for not found.
*/
@@ -1625,7 +1623,7 @@ static inline int find_in_spbanks(unsigned long phys_page)
* array of char's, each member indicating if that spbank is mapped
* yet or not.
*/
-static inline int find_free_spbank(char *taken_vector)
+__initfunc(static int find_free_spbank(char *taken_vector))
{
int entry;
@@ -1635,78 +1633,28 @@ static inline int find_free_spbank(char *taken_vector)
return entry;
}
-/* Same as above, but with a given bank size limit BLIMIT. */
-static inline int find_free_spbank_limited(char *taken_vector, unsigned long limit)
-{
- int entry;
-
- for(entry = 0; sp_banks[entry].num_bytes; entry++)
- if(!taken_vector[entry] &&
- (sp_banks[entry].num_bytes < limit))
- break;
- return entry;
-}
+static unsigned long map_spbank_last_pa __initdata = 0xff000000;
/* Map sp_bank entry SP_ENTRY, starting at virtual address VBASE.
- * This routine is expected to update the srmmu_map and try as
- * hard as possible to use 16MB level-one SRMMU pte's when at all
- * possible to get short termination and faster translations.
*/
-static inline unsigned long map_spbank(unsigned long vbase, int sp_entry)
+__initfunc(static unsigned long map_spbank(unsigned long vbase, int sp_entry))
{
- unsigned long pstart = sp_banks[sp_entry].base_addr;
- unsigned long vstart = vbase;
- unsigned long vend = vbase + sp_banks[sp_entry].num_bytes;
+ unsigned long pstart = (sp_banks[sp_entry].base_addr & SRMMU_PGDIR_MASK);
+ unsigned long vstart = (vbase & SRMMU_PGDIR_MASK);
+ unsigned long vend = SRMMU_PGDIR_ALIGN(vbase + sp_banks[sp_entry].num_bytes);
static int srmmu_bank = 0;
- /* If physically not aligned on 16MB boundry, just shortcut
- * right here by mapping them with 4k normal pages, and bumping
- * the next virtual address to the next 16MB boundry. You can
- * get this with various RAM configurations due to the way in
- * which the PROM carves out it's own chunks of memory.
- */
- if(pstart & ~SRMMU_PGDIR_MASK) {
- do_small_mapping(vstart, vend, pstart);
- vstart = SRMMU_PGDIR_ALIGN(vend);
- goto finish_up;
- }
+ MKTRACE(("map_spbank %d[v<%08lx>p<%08lx>s<%08lx>]", sp_entry, vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes));
+ MKTRACE(("map_spbank2 %d[p%08lx v%08lx-%08lx]", sp_entry, pstart, vstart, vend));
while(vstart < vend) {
- unsigned long coverage, next_aligned;
- if(vstart & ~SRMMU_PMD_MASK) {
- next_aligned = SRMMU_PMD_ALIGN(vstart);
- if(next_aligned <= vend) {
- coverage = (next_aligned - vstart);
- do_small_mapping(vstart, next_aligned, pstart);
- } else {
- coverage = (vend - vstart);
- do_small_mapping(vstart, vend, pstart);
- }
- } else if(vstart & ~SRMMU_PGDIR_MASK) {
- next_aligned = SRMMU_PGDIR_ALIGN(vstart);
- if(next_aligned <= vend) {
- coverage = (next_aligned - vstart);
- do_medium_mapping(vstart, next_aligned, pstart);
- } else {
- coverage = (vend - vstart);
- do_small_mapping(vstart, vend, pstart);
- }
- } else {
- coverage = SRMMU_PGDIR_SIZE;
- if(large_pte_optimize || ((vstart+coverage)<=vend)) {
- do_large_mapping(vstart, pstart);
- } else {
- coverage = (vend - vstart);
- do_small_mapping(vstart, vend, pstart);
- }
- }
- vstart += coverage; pstart += coverage;
+ do_large_mapping(vstart, pstart);
+ vstart += SRMMU_PGDIR_SIZE; pstart += SRMMU_PGDIR_SIZE;
}
-finish_up:
srmmu_map[srmmu_bank].vbase = vbase;
srmmu_map[srmmu_bank].pbase = sp_banks[sp_entry].base_addr;
srmmu_map[srmmu_bank].size = sp_banks[sp_entry].num_bytes;
- MKTRACE(("SRMMUBANK[v<%08lx>p<%08lx>s<%08lx>]", vbase, sp_banks[sp_entry].base_addr, sp_banks[sp_entry].num_bytes));
srmmu_bank++;
+ map_spbank_last_pa = pstart - SRMMU_PGDIR_SIZE;
return vstart;
}
@@ -1721,10 +1669,10 @@ static inline void memprobe_error(char *msg)
* is part of a full bank which is at least 4MB in size and begins at
* 0xf0000000 (ie. KERNBASE).
*/
-static void map_kernel(void)
+static inline void map_kernel(void)
{
unsigned long raw_pte, physpage;
- unsigned long vaddr, tally, low_base;
+ unsigned long vaddr, low_base;
char etaken[SPARC_PHYS_BANKS];
int entry;
@@ -1735,17 +1683,7 @@ static void map_kernel(void)
low_base = KERNBASE;
- /* Step 2: Calculate 'lots_of_ram'. */
- tally = 0;
- for(entry = 0; sp_banks[entry].num_bytes; entry++)
- tally += sp_banks[entry].num_bytes;
- if(tally > (0xfd000000 - KERNBASE))
- lots_of_ram = 1;
- else
- lots_of_ram = 0;
- MKTRACE(("tally=%08lx lots_of_ram<%d>\n", tally, lots_of_ram));
-
- /* Step 3: Fill in KERNBASE base pgd. Lots of sanity checking here. */
+ /* Step 2: Fill in KERNBASE base pgd. Lots of sanity checking here. */
raw_pte = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
if((raw_pte & SRMMU_ET_MASK) != SRMMU_ET_PTE)
memprobe_error("Wheee, kernel not mapped at all by boot loader.\n");
@@ -1757,11 +1695,10 @@ static void map_kernel(void)
if(entry == -1 || (sp_banks[entry].base_addr != physpage))
memprobe_error("Kernel mapped in non-existant memory.\n");
MKTRACE(("map_kernel: map_spbank(vbase=%08x, entry<%d>)[%08lx,%08lx]\n", KERNBASE, entry, sp_banks[entry].base_addr, sp_banks[entry].num_bytes));
- if(((KERNBASE + (sp_banks[entry].num_bytes)) > 0xfd000000) ||
- ((KERNBASE + (sp_banks[entry].num_bytes)) < KERNBASE)) {
+ if (sp_banks[entry].num_bytes > 0x0d000000) {
unsigned long orig_base = sp_banks[entry].base_addr;
unsigned long orig_len = sp_banks[entry].num_bytes;
- unsigned long can_map = (0xfd000000 - KERNBASE);
+ unsigned long can_map = 0x0d000000;
/* Map a partial bank in this case, adjust the base
* and the length, but don't mark it used.
@@ -1779,7 +1716,7 @@ static void map_kernel(void)
vaddr = map_spbank(KERNBASE, entry);
etaken[entry] = 1;
- /* Step 4: Map what we can above KERNBASE. */
+ /* Step 3: Map what we can above KERNBASE. */
MKTRACE(("map_kernel: vaddr=%08lx, entering first loop\n", vaddr));
for(;;) {
unsigned long bank_size;
@@ -1790,8 +1727,14 @@ static void map_kernel(void)
MKTRACE(("<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
if(!bank_size)
break;
- if(((vaddr + bank_size) > 0xfd000000) ||
- ((vaddr + bank_size) < KERNBASE)) {
+ if (srmmu_low_pa)
+ vaddr = KERNBASE + sp_banks[entry].base_addr;
+ else if (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)) {
+ if (map_spbank_last_pa == (sp_banks[entry].base_addr & SRMMU_PGDIR_MASK))
+ vaddr -= SRMMU_PGDIR_SIZE;
+ vaddr += (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK));
+ }
+ if ((vaddr + bank_size - KERNBASE) > 0x0d000000) {
unsigned long orig_base = sp_banks[entry].base_addr;
unsigned long orig_len = sp_banks[entry].num_bytes;
unsigned long can_map = (0xfd000000 - vaddr);
@@ -1808,8 +1751,6 @@ static void map_kernel(void)
MKTRACE(("adjust[%08lx,%08lx]\n", (orig_base + can_map), (orig_len - can_map)));
break;
}
- if(!bank_size)
- break;
/* Ok, we can map this one, do it. */
MKTRACE(("map_spbank(%08lx,entry<%d>) ", vaddr, entry));
@@ -1823,22 +1764,16 @@ loop_skip:
if(!lots_of_ram)
goto check_and_return;
- /* Step 5: Map the rest (if any) right below KERNBASE. */
+ /* Step 4: Map the rest (if any) right below KERNBASE. */
MKTRACE(("map_kernel: doing low mappings... "));
- tally = 0;
- for(entry = 0; sp_banks[entry].num_bytes; entry++) {
- if(!etaken[entry])
- tally += SRMMU_PGDIR_ALIGN(sp_banks[entry].num_bytes);
- }
- if(!tally)
- memprobe_error("Whee, lots_of_ram yet no low pages to map.\n");
- low_base = (KERNBASE - tally);
- MKTRACE(("tally=%08lx low_base=%08lx\n", tally, low_base));
+ low_base = (KERNBASE - end_of_phys_memory + 0x0d000000);
+ MKTRACE(("end_of_phys_memory=%08lx low_base=%08lx\n", end_of_phys_memory, low_base));
/* Ok, now map 'em. */
MKTRACE(("map_kernel: Allocate pt skeleton (%08lx, %08x)\n",low_base,KERNBASE));
srmmu_allocate_ptable_skeleton(low_base, KERNBASE);
vaddr = low_base;
+ map_spbank_last_pa = 0xff000000;
MKTRACE(("map_kernel: vaddr=%08lx Entering second loop for low maps.\n", vaddr));
for(;;) {
unsigned long bank_size;
@@ -1848,19 +1783,22 @@ loop_skip:
MKTRACE(("map_kernel: e<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
if(!bank_size)
break;
+ if (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK)) {
+ if (map_spbank_last_pa == (sp_banks[entry].base_addr & SRMMU_PGDIR_MASK))
+ vaddr -= SRMMU_PGDIR_SIZE;
+ vaddr += (sp_banks[entry].base_addr & (~SRMMU_PGDIR_MASK));
+ }
if((vaddr + bank_size) > KERNBASE)
memprobe_error("Wheee, kernel low mapping overflow.\n");
MKTRACE(("map_spbank(%08lx, %d) ", vaddr, entry));
vaddr = map_spbank(vaddr, entry);
etaken[entry] = 1;
- tally -= SRMMU_PGDIR_ALIGN(bank_size);
- MKTRACE(("Now, vaddr=%08lx tally=%08lx\n", vaddr, tally));
+ MKTRACE(("Now, vaddr=%08lx end_of_phys_memory=%08lx\n", vaddr, end_of_phys_memory));
}
MKTRACE(("\n"));
- if(tally)
- memprobe_error("Wheee, did not map all of low mappings.\n");
+
check_and_return:
- /* Step 6: Sanity check, make sure we did it all. */
+ /* Step 5: Sanity check, make sure we did it all. */
MKTRACE(("check_and_return: "));
for(entry = 0; sp_banks[entry].num_bytes; entry++) {
MKTRACE(("e[%d]=%d ", entry, etaken[entry]));
@@ -1872,6 +1810,10 @@ check_and_return:
MKTRACE(("success\n"));
init_task.mm->mmap->vm_start = page_offset = low_base;
stack_top = page_offset - PAGE_SIZE;
+ BTFIXUPSET_SETHI(page_offset, low_base);
+ BTFIXUPSET_SETHI(stack_top, page_offset - PAGE_SIZE);
+ BTFIXUPSET_SIMM13(user_ptrs_per_pgd, page_offset / SRMMU_PGDIR_SIZE);
+
#if 1
for(entry = 0; srmmu_map[entry].size; entry++) {
printk("[%d]: v[%08lx,%08lx](%lx) p[%08lx]\n", entry,
@@ -1884,90 +1826,73 @@ check_and_return:
/* Now setup the p2v/v2p hash tables. */
for(entry = 0; entry < SRMMU_HASHSZ; entry++)
- srmmu_v2p_hash[entry] = srmmu_p2v_hash[entry] = NULL;
+ srmmu_v2p_hash[entry] = ((0xff - entry) << 24);
+ for(entry = 0; entry < SRMMU_HASHSZ; entry++)
+ srmmu_p2v_hash[entry] = 0xffffffffUL;
for(entry = 0; srmmu_map[entry].size; entry++) {
unsigned long addr;
for(addr = srmmu_map[entry].vbase;
addr < (srmmu_map[entry].vbase + srmmu_map[entry].size);
addr += (1 << 24))
- srmmu_v2p_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry];
+ srmmu_v2p_hash[srmmu_ahashfn(addr)] =
+ srmmu_map[entry].pbase - srmmu_map[entry].vbase;
for(addr = srmmu_map[entry].pbase;
addr < (srmmu_map[entry].pbase + srmmu_map[entry].size);
addr += (1 << 24))
- srmmu_p2v_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry];
+ srmmu_p2v_hash[srmmu_ahashfn(addr)] =
+ srmmu_map[entry].pbase - srmmu_map[entry].vbase;
}
- page_contig_offset = page_offset - (0xfd000000 - KERNBASE);
- phys_mem_contig = 1;
- for(entry = 0; srmmu_map[entry].size; entry++)
- if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) {
- phys_mem_contig = 0;
- break;
- }
- if (phys_mem_contig) {
- printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes\n");
- pte_page = srmmu_c_pte_page;
- pmd_page = srmmu_c_pmd_page;
- pgd_page = srmmu_c_pgd_page;
- mk_pte = srmmu_c_mk_pte;
- pte_offset = srmmu_c_pte_offset;
- pmd_offset = srmmu_c_pmd_offset;
- if (ctxd_set == srmmu_ctxd_set)
- ctxd_set = srmmu_c_ctxd_set;
- pgd_set = srmmu_c_pgd_set;
- pmd_set = srmmu_c_pmd_set;
- mmu_v2p = srmmu_c_v2p;
- mmu_p2v = srmmu_c_p2v;
- if (flush_chunk == viking_flush_chunk)
- flush_chunk = viking_c_flush_chunk;
- }
-
- if (sparc_cpu_model == sun4d) {
- int i, j = -1;
- unsigned long bank_start, bank_end;
-
- sun4d_dma_vbase = 0;
- sun4d_dma_size = IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE;
- for (i = 0; srmmu_map[i].size; i++) {
- bank_start = srmmu_map[i].vbase;
- bank_end = bank_start + srmmu_map[i].size;
- if (bank_start <= KERNBASE && bank_end > KERNBASE)
- j = i;
- else if (srmmu_map[i].size >= sun4d_dma_size) {
- sun4d_dma_vbase = srmmu_map[i].vbase;
+ BTFIXUPSET_SETHI(page_contig_offset, page_offset - (0xfd000000 - KERNBASE));
+ if (srmmu_low_pa)
+ phys_mem_contig = 0;
+ else {
+ phys_mem_contig = 1;
+ for(entry = 0; srmmu_map[entry].size; entry++)
+ if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) {
+ phys_mem_contig = 0;
break;
}
- }
- if (!sun4d_dma_vbase && j != -1) {
- if (srmmu_map[j].size >= sun4d_dma_size + 0x1000000)
- sun4d_dma_vbase = srmmu_map[j].vbase + 0x1000000;
- else {
- sun4d_dma_vbase = srmmu_map[j].vbase;
- if (srmmu_map[j].size < sun4d_dma_size)
- sun4d_dma_size = srmmu_map[j].size;
- }
- }
- sun4d_dma_base = IOUNIT_DMA_BASE - srmmu_v2p(sun4d_dma_vbase);
}
+ if (phys_mem_contig) {
+ printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes.\n");
+ BTFIXUPSET_CALL(pte_page, srmmu_c_pte_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_page, srmmu_c_pmd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_page, srmmu_c_pgd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte, srmmu_c_mk_pte, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_offset, srmmu_c_pte_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_offset, srmmu_c_pmd_offset, BTFIXUPCALL_NORM);
+ if (BTFIXUPVAL_CALL(ctxd_set) == (unsigned long)srmmu_ctxd_set)
+ BTFIXUPSET_CALL(ctxd_set, srmmu_c_ctxd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_set, srmmu_c_pgd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_set, srmmu_c_pmd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_v2p, srmmu_c_v2p, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_p2v, srmmu_c_p2v, BTFIXUPCALL_NORM);
+ if (BTFIXUPVAL_CALL(flush_chunk) == (unsigned long)viking_flush_chunk)
+ BTFIXUPSET_CALL(flush_chunk, viking_c_flush_chunk, BTFIXUPCALL_NORM);
+ } else if (srmmu_low_pa) {
+ printk ("SRMMU: Compact physical memory. Using strightforward VA<->PA translations.\n");
+ BTFIXUPSET_CALL(pte_page, srmmu_s_pte_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_page, srmmu_s_pmd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_page, srmmu_s_pgd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte, srmmu_s_mk_pte, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_offset, srmmu_s_pte_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_offset, srmmu_s_pmd_offset, BTFIXUPCALL_NORM);
+ if (BTFIXUPVAL_CALL(ctxd_set) == (unsigned long)srmmu_ctxd_set)
+ BTFIXUPSET_CALL(ctxd_set, srmmu_s_ctxd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_set, srmmu_s_pgd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_set, srmmu_s_pmd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_v2p, srmmu_s_v2p, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_p2v, srmmu_s_p2v, BTFIXUPCALL_NORM);
+ if (BTFIXUPVAL_CALL(flush_chunk) == (unsigned long)viking_flush_chunk)
+ BTFIXUPSET_CALL(flush_chunk, viking_s_flush_chunk, BTFIXUPCALL_NORM);
+ }
+ btfixup();
return; /* SUCCESS! */
}
-unsigned long srmmu_endmem_fixup(unsigned long mem_end_now)
-{
- unsigned long tally = 0;
- int i;
-
- for(i = 0; sp_banks[i].num_bytes; i++)
- tally += SRMMU_PGDIR_ALIGN(sp_banks[i].num_bytes);
- if(tally < (0x0d000000UL)) {
- return KERNBASE + tally;
- } else {
- return 0xfd000000UL;
- }
-}
-
/* Paging initialization on the Sparc Reference MMU. */
extern unsigned long free_area_init(unsigned long, unsigned long);
extern unsigned long sparc_context_init(unsigned long, int);
@@ -1975,9 +1900,9 @@ extern unsigned long sparc_context_init(unsigned long, int);
extern int physmem_mapped_contig;
extern int linux_num_cpus;
-void (*poke_srmmu)(void);
+void (*poke_srmmu)(void) __initdata = NULL;
-unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
+__initfunc(unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem))
{
unsigned long ptables_start;
int i, cpunode;
@@ -2029,7 +1954,7 @@ unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
start_mem = PAGE_ALIGN(mempool);
flush_cache_all();
- if(flush_page_for_dma == viking_flush_page) {
+ if(BTFIXUPVAL_CALL(flush_page_for_dma) == (unsigned long)viking_flush_page) {
unsigned long start = ptables_start;
unsigned long end = start_mem;
@@ -2048,37 +1973,22 @@ unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
return PAGE_ALIGN(start_mem);
}
-static char srmmuinfo[512];
-
-static char *srmmu_mmu_info(void)
+static int srmmu_mmu_info(char *buf)
{
- sprintf(srmmuinfo, "MMU type\t: %s\n"
+ return sprintf(buf,
+ "MMU type\t: %s\n"
"invall\t\t: %d\n"
"invmm\t\t: %d\n"
"invrnge\t\t: %d\n"
"invpg\t\t: %d\n"
"contexts\t: %d\n"
-#ifdef USE_CHUNK_ALLOC
- "big chunks\t: %d\n"
- "little chunks\t: %d\n"
- "chunk pages\t: %d\n"
- "garbage\t\t: %d\n"
- "garbage hits\t: %d\n"
-#endif
, srmmu_name,
module_stats.invall,
module_stats.invmm,
module_stats.invrnge,
module_stats.invpg,
num_contexts
-#ifdef USE_CHUNK_ALLOC
- , bcwater, lcwater,
- chunk_pages,
- garbage_calls,
- clct_pages
-#endif
- );
- return srmmuinfo;
+ );
}
static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
@@ -2242,7 +2152,7 @@ __initfunc(static void init_vac_layout(void))
(int)vac_cache_size, (int)vac_line_size);
}
-static void poke_hypersparc(void)
+__initfunc(static void poke_hypersparc(void))
{
volatile unsigned long clear;
unsigned long mreg = srmmu_get_mmureg();
@@ -2271,35 +2181,38 @@ __initfunc(static void init_hypersparc(void))
init_vac_layout();
- set_pte = srmmu_set_pte_nocache_hyper;
- flush_cache_all = hypersparc_flush_cache_all;
- flush_cache_mm = hypersparc_flush_cache_mm;
- flush_cache_range = hypersparc_flush_cache_range;
- flush_cache_page = hypersparc_flush_cache_page;
-
- flush_tlb_all = hypersparc_flush_tlb_all;
- flush_tlb_mm = hypersparc_flush_tlb_mm;
- flush_tlb_range = hypersparc_flush_tlb_range;
- flush_tlb_page = hypersparc_flush_tlb_page;
-
- flush_page_to_ram = hypersparc_flush_page_to_ram;
- flush_sig_insns = hypersparc_flush_sig_insns;
- flush_page_for_dma = NULL /* hypersparc_flush_page_for_dma */;
-
- flush_chunk = hypersparc_flush_chunk; /* local flush _only_ */
-
- ctxd_set = hypersparc_ctxd_set;
- switch_to_context = hypersparc_switch_to_context;
- init_new_context = hypersparc_init_new_context;
- destroy_context = hypersparc_destroy_context;
- update_mmu_cache = srmmu_vac_update_mmu_cache;
- sparc_update_rootmmu_dir = hypersparc_update_rootmmu_dir;
+ BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_hyper, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_all, hypersparc_flush_cache_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_sig_insns, hypersparc_flush_sig_insns, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_for_dma, hypersparc_flush_page_for_dma, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(flush_chunk, hypersparc_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
+
+ BTFIXUPSET_CALL(ctxd_set, hypersparc_ctxd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(switch_to_context, hypersparc_switch_to_context, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(init_new_context, hypersparc_init_new_context, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(destroy_context, hypersparc_destroy_context, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(sparc_update_rootmmu_dir, hypersparc_update_rootmmu_dir, BTFIXUPCALL_NORM);
poke_srmmu = poke_hypersparc;
hypersparc_setup_blockops();
}
-static void poke_cypress(void)
+__initfunc(static void poke_cypress(void))
{
unsigned long mreg = srmmu_get_mmureg();
unsigned long faddr, tagval;
@@ -2342,25 +2255,28 @@ __initfunc(static void init_cypress_common(void))
{
init_vac_layout();
- set_pte = srmmu_set_pte_nocache_cypress;
- flush_cache_all = cypress_flush_cache_all;
- flush_cache_mm = cypress_flush_cache_mm;
- flush_cache_range = cypress_flush_cache_range;
- flush_cache_page = cypress_flush_cache_page;
+ BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_cypress, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_all, cypress_flush_cache_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_mm, cypress_flush_cache_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, cypress_flush_cache_range, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, cypress_flush_cache_page, BTFIXUPCALL_NORM);
- flush_tlb_all = cypress_flush_tlb_all;
- flush_tlb_mm = cypress_flush_tlb_mm;
- flush_tlb_page = cypress_flush_tlb_page;
- flush_tlb_range = cypress_flush_tlb_range;
+ BTFIXUPSET_CALL(flush_tlb_all, cypress_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, cypress_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, cypress_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, cypress_flush_tlb_range, BTFIXUPCALL_NORM);
- flush_chunk = cypress_flush_chunk; /* local flush _only_ */
+ BTFIXUPSET_CALL(flush_chunk, cypress_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
- flush_page_to_ram = cypress_flush_page_to_ram;
- flush_sig_insns = cypress_flush_sig_insns;
- flush_page_for_dma = NULL /* cypress_flush_page_for_dma */;
- sparc_update_rootmmu_dir = cypress_update_rootmmu_dir;
+ BTFIXUPSET_CALL(flush_page_to_ram, cypress_flush_page_to_ram, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_sig_insns, cypress_flush_sig_insns, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_page_for_dma, cypress_flush_page_for_dma, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(sparc_update_rootmmu_dir, cypress_update_rootmmu_dir, BTFIXUPCALL_NORM);
- update_mmu_cache = srmmu_vac_update_mmu_cache;
+ BTFIXUPSET_CALL(update_mmu_cache, srmmu_vac_update_mmu_cache, BTFIXUPCALL_NORM);
poke_srmmu = poke_cypress;
}
@@ -2388,7 +2304,7 @@ __initfunc(static void init_cypress_605(unsigned long mrev))
init_cypress_common();
}
-static void poke_swift(void)
+__initfunc(static void poke_swift(void))
{
unsigned long mreg = srmmu_get_mmureg();
@@ -2456,21 +2372,23 @@ __initfunc(static void init_swift(void))
break;
};
- flush_cache_all = swift_flush_cache_all;
- flush_cache_mm = swift_flush_cache_mm;
- flush_cache_page = swift_flush_cache_page;
- flush_cache_range = swift_flush_cache_range;
+ BTFIXUPSET_CALL(flush_cache_all, swift_flush_cache_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_mm, swift_flush_cache_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, swift_flush_cache_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, swift_flush_cache_range, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
- flush_chunk = swift_flush_chunk; /* local flush _only_ */
+ BTFIXUPSET_CALL(flush_tlb_all, swift_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, swift_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, swift_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, swift_flush_tlb_range, BTFIXUPCALL_NORM);
- flush_tlb_all = swift_flush_tlb_all;
- flush_tlb_mm = swift_flush_tlb_mm;
- flush_tlb_page = swift_flush_tlb_page;
- flush_tlb_range = swift_flush_tlb_range;
+ BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_sig_insns, swift_flush_sig_insns, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_for_dma, swift_flush_page_for_dma, BTFIXUPCALL_NORM);
- flush_page_to_ram = swift_flush_page_to_ram;
- flush_sig_insns = swift_flush_sig_insns;
- flush_page_for_dma = swift_flush_page_for_dma;
+ BTFIXUPSET_CALL(update_mmu_cache, swift_update_mmu_cache, BTFIXUPCALL_NORM);
/* Are you now convinced that the Swift is one of the
* biggest VLSI abortions of all time? Bravo Fujitsu!
@@ -2484,8 +2402,9 @@ __initfunc(static void init_swift(void))
/* turbosparc.S */
extern void turbosparc_flush_cache_all(void);
extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+extern void turbosparc_flush_page_for_dma(unsigned long page);
-static void poke_turbosparc(void)
+__initfunc(static void poke_turbosparc(void))
{
unsigned long mreg = srmmu_get_mmureg();
unsigned long ccreg;
@@ -2529,31 +2448,31 @@ __initfunc(static void init_turbosparc(void))
srmmu_name = "Fujitsu TurboSparc";
srmmu_modtype = TurboSparc;
- flush_cache_all = turbosparc_flush_cache_all;
- flush_cache_mm = hypersparc_flush_cache_mm;
- flush_cache_page = hypersparc_flush_cache_page;
- flush_cache_range = hypersparc_flush_cache_range;
+ BTFIXUPSET_CALL(flush_cache_all, turbosparc_flush_cache_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_mm, hypersparc_flush_cache_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, hypersparc_flush_cache_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, hypersparc_flush_cache_range, BTFIXUPCALL_NORM);
- flush_tlb_all = hypersparc_flush_tlb_all;
- flush_tlb_mm = hypersparc_flush_tlb_mm;
- flush_tlb_page = hypersparc_flush_tlb_page;
- flush_tlb_range = hypersparc_flush_tlb_range;
+ BTFIXUPSET_CALL(flush_tlb_all, hypersparc_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, hypersparc_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, hypersparc_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, hypersparc_flush_tlb_range, BTFIXUPCALL_NORM);
#ifdef TURBOSPARC_WRITEBACK
- flush_page_to_ram = hypersparc_flush_page_to_ram;
- flush_chunk = hypersparc_flush_chunk;
+ BTFIXUPSET_CALL(flush_page_to_ram, hypersparc_flush_page_to_ram, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_chunk, hypersparc_flush_chunk, BTFIXUPCALL_NORM);
#else
- flush_page_to_ram = swift_flush_page_to_ram;
- flush_chunk = swift_flush_chunk;
+ BTFIXUPSET_CALL(flush_page_to_ram, swift_flush_page_to_ram, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_chunk, swift_flush_chunk, BTFIXUPCALL_NOP);
#endif
- flush_sig_insns = turbosparc_flush_sig_insns;
- flush_page_for_dma = NULL /* turbosparc_flush_page_for_dma */;
+ BTFIXUPSET_CALL(flush_sig_insns, turbosparc_flush_sig_insns, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_page_for_dma, turbosparc_flush_page_for_dma, BTFIXUPCALL_NOP);
poke_srmmu = poke_turbosparc;
}
-static void poke_tsunami(void)
+__initfunc(static void poke_tsunami(void))
{
unsigned long mreg = srmmu_get_mmureg();
@@ -2574,26 +2493,26 @@ __initfunc(static void init_tsunami(void))
srmmu_name = "TI Tsunami";
srmmu_modtype = Tsunami;
- flush_cache_all = tsunami_flush_cache_all;
- flush_cache_mm = tsunami_flush_cache_mm;
- flush_cache_page = tsunami_flush_cache_page;
- flush_cache_range = tsunami_flush_cache_range;
+ BTFIXUPSET_CALL(flush_cache_all, tsunami_flush_cache_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_mm, tsunami_flush_cache_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, tsunami_flush_cache_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, tsunami_flush_cache_range, BTFIXUPCALL_NORM);
- flush_chunk = tsunami_flush_chunk; /* local flush _only_ */
+ BTFIXUPSET_CALL(flush_chunk, tsunami_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
- flush_tlb_all = tsunami_flush_tlb_all;
- flush_tlb_mm = tsunami_flush_tlb_mm;
- flush_tlb_page = tsunami_flush_tlb_page;
- flush_tlb_range = tsunami_flush_tlb_range;
+ BTFIXUPSET_CALL(flush_tlb_all, tsunami_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, tsunami_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, tsunami_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, tsunami_flush_tlb_range, BTFIXUPCALL_NORM);
- flush_page_to_ram = tsunami_flush_page_to_ram;
- flush_sig_insns = tsunami_flush_sig_insns;
- flush_page_for_dma = tsunami_flush_page_for_dma;
+ BTFIXUPSET_CALL(flush_page_to_ram, tsunami_flush_page_to_ram, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_sig_insns, tsunami_flush_sig_insns, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_for_dma, tsunami_flush_page_for_dma, BTFIXUPCALL_NORM);
poke_srmmu = poke_tsunami;
}
-static void poke_viking(void)
+__initfunc(static void poke_viking(void))
{
unsigned long mreg = srmmu_get_mmureg();
static int smp_catch = 0;
@@ -2637,13 +2556,14 @@ static void poke_viking(void)
#ifdef __SMP__
/* Avoid unnecessary cross calls. */
- flush_cache_all = local_flush_cache_all;
- flush_cache_mm = local_flush_cache_mm;
- flush_cache_range = local_flush_cache_range;
- flush_cache_page = local_flush_cache_page;
- flush_page_to_ram = local_flush_page_to_ram;
- flush_sig_insns = local_flush_sig_insns;
- flush_page_for_dma = local_flush_page_for_dma;
+ BTFIXUPCOPY_CALL(flush_cache_all, local_flush_cache_all);
+ BTFIXUPCOPY_CALL(flush_cache_mm, local_flush_cache_mm);
+ BTFIXUPCOPY_CALL(flush_cache_range, local_flush_cache_range);
+ BTFIXUPCOPY_CALL(flush_cache_page, local_flush_cache_page);
+ BTFIXUPCOPY_CALL(flush_page_to_ram, local_flush_page_to_ram);
+ BTFIXUPCOPY_CALL(flush_sig_insns, local_flush_sig_insns);
+ BTFIXUPCOPY_CALL(flush_page_for_dma, local_flush_page_for_dma);
+ btfixup();
#endif
}
@@ -2664,10 +2584,13 @@ __initfunc(static void init_viking(void))
msi_set_sync();
- set_pte = srmmu_set_pte_nocache_viking;
- sparc_update_rootmmu_dir = viking_update_rootmmu_dir;
+ BTFIXUPSET_CALL(set_pte, srmmu_set_pte_nocache_viking, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(sparc_update_rootmmu_dir, viking_update_rootmmu_dir, BTFIXUPCALL_NORM);
- flush_chunk = viking_flush_chunk; /* local flush _only_ */
+ BTFIXUPSET_CALL(flush_chunk, viking_flush_chunk, BTFIXUPCALL_NORM); /* local flush _only_ */
/* We need this to make sure old viking takes no hits
* on it's cache for dma snoops to workaround the
@@ -2675,7 +2598,7 @@ __initfunc(static void init_viking(void))
* This is only necessary because of the new way in
* which we use the IOMMU.
*/
- flush_page_for_dma = viking_flush_page;
+ BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page, BTFIXUPCALL_NORM);
/* Also, this is so far the only chip which actually uses
the page argument to flush_page_for_dma */
flush_page_for_dma_global = 0;
@@ -2683,24 +2606,25 @@ __initfunc(static void init_viking(void))
srmmu_name = "TI Viking/MXCC";
viking_mxcc_present = 1;
- flush_chunk = viking_mxcc_flush_chunk; /* local flush _only_ */
+ BTFIXUPSET_CALL(flush_chunk, viking_mxcc_flush_chunk, BTFIXUPCALL_NOP); /* local flush _only_ */
/* MXCC vikings lack the DMA snooping bug. */
- flush_page_for_dma = NULL /* viking_flush_page_for_dma */;
+ BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP);
}
- flush_cache_all = viking_flush_cache_all;
- flush_cache_mm = viking_flush_cache_mm;
- flush_cache_page = viking_flush_cache_page;
- flush_cache_range = viking_flush_cache_range;
+ /* flush_cache_* are nops */
+ BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP);
- flush_tlb_all = viking_flush_tlb_all;
- flush_tlb_mm = viking_flush_tlb_mm;
- flush_tlb_page = viking_flush_tlb_page;
- flush_tlb_range = viking_flush_tlb_range;
+ BTFIXUPSET_CALL(flush_tlb_all, viking_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, viking_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, viking_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, viking_flush_tlb_range, BTFIXUPCALL_NORM);
- flush_page_to_ram = viking_flush_page_to_ram;
- flush_sig_insns = viking_flush_sig_insns;
+ BTFIXUPSET_CALL(flush_page_to_ram, viking_flush_page_to_ram, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(flush_sig_insns, viking_flush_sig_insns, BTFIXUPCALL_NOP);
poke_srmmu = poke_viking;
}
@@ -2798,6 +2722,67 @@ __initfunc(static void get_srmmu_type(void))
srmmu_is_bad();
}
+/* Low and high watermarks for page table cache.
+ The system should try to have pgt_water[0] <= cache elements <= pgt_water[1]
+ */
+extern int pgt_cache_water[2];
+
+void srmmu_check_pgt_cache(void)
+{
+ struct page *page, *page2;
+
+ if (pgtable_cache_size > pgt_cache_water[0]) {
+ spin_lock(&pte_spinlock);
+ for (page2 = NULL, page = (struct page *)pte_quicklist; page;) {
+ if ((unsigned int)page->pprev_hash == 0xffff) {
+ if (page2)
+ page2->next_hash = page->next_hash;
+ else
+ (struct page *)pte_quicklist = page->next_hash;
+ page->next_hash = NULL;
+ page->pprev_hash = NULL;
+ pgtable_cache_size -= 16;
+ free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+ if (page2)
+ page = page2->next_hash;
+ else
+ page = (struct page *)pte_quicklist;
+ if (pgtable_cache_size <= pgt_cache_water[1])
+ break;
+ continue;
+ }
+ page2 = page;
+ page = page->next_hash;
+ }
+ spin_unlock(&pte_spinlock);
+ }
+ if (pgd_cache_size > pgt_cache_water[0] / 4) {
+ spin_lock(&pgd_spinlock);
+ for (page2 = NULL, page = (struct page *)pgd_quicklist; page;) {
+ if ((unsigned int)page->pprev_hash == 0xf) {
+ if (page2)
+ page2->next_hash = page->next_hash;
+ else
+ (struct page *)pgd_quicklist = page->next_hash;
+ page->next_hash = NULL;
+ page->pprev_hash = NULL;
+ pgd_cache_size -= 4;
+ free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT));
+ if (page2)
+ page = page2->next_hash;
+ else
+ page = (struct page *)pgd_quicklist;
+ if (pgd_cache_size <= pgt_cache_water[1] / 4)
+ break;
+ continue;
+ }
+ page2 = page;
+ page = page->next_hash;
+ }
+ spin_unlock(&pgd_spinlock);
+ }
+}
+
extern unsigned long spwin_mmu_patchme, fwin_mmu_patchme,
tsetup_mmu_patchme, rtrap_mmu_patchme;
@@ -2810,7 +2795,7 @@ extern unsigned long srmmu_fault;
iaddr = &(insn); \
daddr = &(dest); \
*iaddr = SPARC_BRANCH((unsigned long) daddr, (unsigned long) iaddr); \
- } while(0);
+ } while(0);
__initfunc(static void patch_window_trap_handlers(void))
{
@@ -2829,7 +2814,7 @@ __initfunc(static void patch_window_trap_handlers(void))
/* Local cross-calls. */
static void smp_flush_page_for_dma(unsigned long page)
{
- xc1((smpfunc_t) local_flush_page_for_dma, page);
+ xc1((smpfunc_t) BTFIXUP_CALL(local_flush_page_for_dma), page);
}
#endif
@@ -2839,98 +2824,107 @@ __initfunc(void ld_mmu_srmmu(void))
{
extern void ld_mmu_iommu(void);
extern void ld_mmu_iounit(void);
+ extern void ___xchg32_sun4md(void);
/* First the constants */
- pmd_shift = SRMMU_PMD_SHIFT;
- pmd_size = SRMMU_PMD_SIZE;
- pmd_mask = SRMMU_PMD_MASK;
- pgdir_shift = SRMMU_PGDIR_SHIFT;
- pgdir_size = SRMMU_PGDIR_SIZE;
- pgdir_mask = SRMMU_PGDIR_MASK;
-
- ptrs_per_pte = SRMMU_PTRS_PER_PTE;
- ptrs_per_pmd = SRMMU_PTRS_PER_PMD;
- ptrs_per_pgd = SRMMU_PTRS_PER_PGD;
-
- page_none = SRMMU_PAGE_NONE;
- page_shared = SRMMU_PAGE_SHARED;
- page_copy = SRMMU_PAGE_COPY;
- page_readonly = SRMMU_PAGE_RDONLY;
- page_kernel = SRMMU_PAGE_KERNEL;
+ BTFIXUPSET_SIMM13(pmd_shift, SRMMU_PMD_SHIFT);
+ BTFIXUPSET_SETHI(pmd_size, SRMMU_PMD_SIZE);
+ BTFIXUPSET_SETHI(pmd_mask, SRMMU_PMD_MASK);
+ BTFIXUPSET_SIMM13(pgdir_shift, SRMMU_PGDIR_SHIFT);
+ BTFIXUPSET_SETHI(pgdir_size, SRMMU_PGDIR_SIZE);
+ BTFIXUPSET_SETHI(pgdir_mask, SRMMU_PGDIR_MASK);
+
+ BTFIXUPSET_SIMM13(ptrs_per_pte, SRMMU_PTRS_PER_PTE);
+ BTFIXUPSET_SIMM13(ptrs_per_pmd, SRMMU_PTRS_PER_PMD);
+ BTFIXUPSET_SIMM13(ptrs_per_pgd, SRMMU_PTRS_PER_PGD);
+
+ BTFIXUPSET_INT(page_none, pgprot_val(SRMMU_PAGE_NONE));
+ BTFIXUPSET_INT(page_shared, pgprot_val(SRMMU_PAGE_SHARED));
+ BTFIXUPSET_INT(page_copy, pgprot_val(SRMMU_PAGE_COPY));
+ BTFIXUPSET_INT(page_readonly, pgprot_val(SRMMU_PAGE_RDONLY));
+ BTFIXUPSET_INT(page_kernel, pgprot_val(SRMMU_PAGE_KERNEL));
pg_iobits = SRMMU_VALID | SRMMU_WRITE | SRMMU_REF;
-
+
/* Functions */
- set_pte = srmmu_set_pte_cacheable;
- init_new_context = srmmu_init_new_context;
- switch_to_context = srmmu_switch_to_context;
- pmd_align = srmmu_pmd_align;
- pgdir_align = srmmu_pgdir_align;
- vmalloc_start = srmmu_vmalloc_start;
-
- pte_page = srmmu_pte_page;
- pmd_page = srmmu_pmd_page;
- pgd_page = srmmu_pgd_page;
-
- sparc_update_rootmmu_dir = srmmu_update_rootmmu_dir;
-
- pte_none = srmmu_pte_none;
- pte_present = srmmu_pte_present;
- pte_clear = srmmu_pte_clear;
-
- pmd_none = srmmu_pmd_none;
- pmd_bad = srmmu_pmd_bad;
- pmd_present = srmmu_pmd_present;
- pmd_clear = srmmu_pmd_clear;
-
- pgd_none = srmmu_pgd_none;
- pgd_bad = srmmu_pgd_bad;
- pgd_present = srmmu_pgd_present;
- pgd_clear = srmmu_pgd_clear;
-
- mk_pte = srmmu_mk_pte;
- mk_pte_phys = srmmu_mk_pte_phys;
- pgd_set = srmmu_pgd_set;
- mk_pte_io = srmmu_mk_pte_io;
- pte_modify = srmmu_pte_modify;
- pgd_offset = srmmu_pgd_offset;
- pmd_offset = srmmu_pmd_offset;
- pte_offset = srmmu_pte_offset;
- pte_free_kernel = srmmu_pte_free_kernel;
- pmd_free_kernel = srmmu_pmd_free_kernel;
- pte_alloc_kernel = srmmu_pte_alloc_kernel;
- pmd_alloc_kernel = srmmu_pmd_alloc_kernel;
- pte_free = srmmu_pte_free;
- pte_alloc = srmmu_pte_alloc;
- pmd_free = srmmu_pmd_free;
- pmd_alloc = srmmu_pmd_alloc;
- pgd_free = srmmu_pgd_free;
- pgd_alloc = srmmu_pgd_alloc;
-
- pte_write = srmmu_pte_write;
- pte_dirty = srmmu_pte_dirty;
- pte_young = srmmu_pte_young;
- pte_wrprotect = srmmu_pte_wrprotect;
- pte_mkclean = srmmu_pte_mkclean;
- pte_mkold = srmmu_pte_mkold;
- pte_mkwrite = srmmu_pte_mkwrite;
- pte_mkdirty = srmmu_pte_mkdirty;
- pte_mkyoung = srmmu_pte_mkyoung;
- update_mmu_cache = srmmu_update_mmu_cache;
- destroy_context = srmmu_destroy_context;
+#ifndef __SMP__
+ BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4md, BTFIXUPCALL_SWAPG1G2);
+#endif
+ BTFIXUPSET_CALL(get_pte_fast, srmmu_get_pte_fast, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(get_pgd_fast, srmmu_get_pgd_fast, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(free_pte_slow, srmmu_free_pte_slow, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(free_pgd_slow, srmmu_free_pgd_slow, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(set_pgdir, srmmu_set_pgdir, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(set_pte, srmmu_set_pte_cacheable, BTFIXUPCALL_SWAPO0O1);
+ BTFIXUPSET_CALL(init_new_context, srmmu_init_new_context, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(switch_to_context, srmmu_switch_to_context, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(pte_page, srmmu_pte_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_page, srmmu_pmd_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_page, srmmu_pgd_page, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(sparc_update_rootmmu_dir, srmmu_update_rootmmu_dir, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_SETHI(none_mask, 0xF0000000);
+
+ BTFIXUPSET_CALL(pte_present, srmmu_pte_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_clear, srmmu_pte_clear, BTFIXUPCALL_SWAPO0G0);
+
+ BTFIXUPSET_CALL(pmd_bad, srmmu_pmd_bad, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_present, srmmu_pmd_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_clear, srmmu_pmd_clear, BTFIXUPCALL_SWAPO0G0);
+
+ BTFIXUPSET_CALL(pgd_none, srmmu_pgd_none, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_bad, srmmu_pgd_bad, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_present, srmmu_pgd_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_SWAPO0G0);
+
+ BTFIXUPSET_CALL(mk_pte, srmmu_mk_pte, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte_phys, srmmu_mk_pte_phys, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte_io, srmmu_mk_pte_io, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_set, srmmu_pgd_set, BTFIXUPCALL_NORM);
- mmu_info = srmmu_mmu_info;
- mmu_v2p = srmmu_v2p;
- mmu_p2v = srmmu_p2v;
+ BTFIXUPSET_INT(pte_modify_mask, SRMMU_CHG_MASK);
+ BTFIXUPSET_CALL(pgd_offset, srmmu_pgd_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_offset, srmmu_pmd_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_offset, srmmu_pte_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_free_kernel, srmmu_pte_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_free_kernel, srmmu_pmd_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_alloc_kernel, srmmu_pte_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_alloc_kernel, srmmu_pmd_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_free, srmmu_pte_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_alloc, srmmu_pte_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_free, srmmu_pmd_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_alloc, srmmu_pmd_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_free, srmmu_pgd_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_alloc, srmmu_pgd_alloc, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_HALF(pte_writei, SRMMU_WRITE);
+ BTFIXUPSET_HALF(pte_dirtyi, SRMMU_DIRTY);
+ BTFIXUPSET_HALF(pte_youngi, SRMMU_REF);
+ BTFIXUPSET_HALF(pte_wrprotecti, SRMMU_WRITE);
+ BTFIXUPSET_HALF(pte_mkcleani, SRMMU_DIRTY);
+ BTFIXUPSET_HALF(pte_mkoldi, SRMMU_REF);
+ BTFIXUPSET_CALL(pte_mkwrite, srmmu_pte_mkwrite, BTFIXUPCALL_ORINT(SRMMU_WRITE));
+ BTFIXUPSET_CALL(pte_mkdirty, srmmu_pte_mkdirty, BTFIXUPCALL_ORINT(SRMMU_DIRTY));
+ BTFIXUPSET_CALL(pte_mkyoung, srmmu_pte_mkyoung, BTFIXUPCALL_ORINT(SRMMU_REF));
+ BTFIXUPSET_CALL(update_mmu_cache, srmmu_update_mmu_cache, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(destroy_context, srmmu_destroy_context, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mmu_info, srmmu_mmu_info, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_v2p, srmmu_v2p, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_p2v, srmmu_p2v, BTFIXUPCALL_NORM);
/* Task struct and kernel stack allocating/freeing. */
- alloc_task_struct = srmmu_alloc_task_struct;
- free_task_struct = srmmu_free_task_struct;
+ BTFIXUPSET_CALL(alloc_task_struct, srmmu_alloc_task_struct, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(free_task_struct, srmmu_free_task_struct, BTFIXUPCALL_NORM);
- quick_kernel_fault = srmmu_quick_kernel_fault;
+ BTFIXUPSET_CALL(quick_kernel_fault, srmmu_quick_kernel_fault, BTFIXUPCALL_NORM);
/* SRMMU specific. */
- ctxd_set = srmmu_ctxd_set;
- pmd_set = srmmu_pmd_set;
+ BTFIXUPSET_CALL(ctxd_set, srmmu_ctxd_set, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_set, srmmu_pmd_set, BTFIXUPCALL_NORM);
get_srmmu_type();
patch_window_trap_handlers();
@@ -2938,33 +2932,38 @@ __initfunc(void ld_mmu_srmmu(void))
#ifdef __SMP__
/* El switcheroo... */
- local_flush_cache_all = flush_cache_all;
- local_flush_cache_mm = flush_cache_mm;
- local_flush_cache_range = flush_cache_range;
- local_flush_cache_page = flush_cache_page;
- local_flush_tlb_all = flush_tlb_all;
- local_flush_tlb_mm = flush_tlb_mm;
- local_flush_tlb_range = flush_tlb_range;
- local_flush_tlb_page = flush_tlb_page;
- local_flush_page_to_ram = flush_page_to_ram;
- local_flush_sig_insns = flush_sig_insns;
- local_flush_page_for_dma = flush_page_for_dma;
-
- flush_cache_all = smp_flush_cache_all;
- flush_cache_mm = smp_flush_cache_mm;
- flush_cache_range = smp_flush_cache_range;
- flush_cache_page = smp_flush_cache_page;
- flush_tlb_all = smp_flush_tlb_all;
- flush_tlb_mm = smp_flush_tlb_mm;
- flush_tlb_range = smp_flush_tlb_range;
- flush_tlb_page = smp_flush_tlb_page;
- flush_page_to_ram = smp_flush_page_to_ram;
- flush_sig_insns = smp_flush_sig_insns;
- if (flush_page_for_dma)
- flush_page_for_dma = smp_flush_page_for_dma;
+ BTFIXUPCOPY_CALL(local_flush_cache_all, flush_cache_all);
+ BTFIXUPCOPY_CALL(local_flush_cache_mm, flush_cache_mm);
+ BTFIXUPCOPY_CALL(local_flush_cache_range, flush_cache_range);
+ BTFIXUPCOPY_CALL(local_flush_cache_page, flush_cache_page);
+ BTFIXUPCOPY_CALL(local_flush_tlb_all, flush_tlb_all);
+ BTFIXUPCOPY_CALL(local_flush_tlb_mm, flush_tlb_mm);
+ BTFIXUPCOPY_CALL(local_flush_tlb_range, flush_tlb_range);
+ BTFIXUPCOPY_CALL(local_flush_tlb_page, flush_tlb_page);
+ BTFIXUPCOPY_CALL(local_flush_page_to_ram, flush_page_to_ram);
+ BTFIXUPCOPY_CALL(local_flush_sig_insns, flush_sig_insns);
+ BTFIXUPCOPY_CALL(local_flush_page_for_dma, flush_page_for_dma);
+
+ BTFIXUPSET_CALL(flush_cache_all, smp_flush_cache_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_mm, smp_flush_cache_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, smp_flush_cache_range, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, smp_flush_cache_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_all, smp_flush_tlb_all, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, smp_flush_tlb_mm, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, smp_flush_tlb_range, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, smp_flush_tlb_page, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM);
#endif
if (sparc_cpu_model == sun4d)
ld_mmu_iounit();
else
ld_mmu_iommu();
+#ifdef __SMP__
+ if (sparc_cpu_model == sun4d)
+ sun4d_init_smp();
+ else
+ sun4m_init_smp();
+#endif
}
diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c
index c70753fa4..d247e1f2d 100644
--- a/arch/sparc/mm/sun4c.c
+++ b/arch/sparc/mm/sun4c.c
@@ -1,11 +1,14 @@
-/* $Id: sun4c.c,v 1.149 1997/07/20 05:59:38 davem Exp $
+/* $Id: sun4c.c,v 1.163 1998/03/11 04:08:21 tdyas Exp $
* sun4c.c: Doing in software what should be done in hardware.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1996 Andrew Tridgell (Andrew.Tridgell@anu.edu.au)
+ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
@@ -22,6 +25,7 @@
#include <asm/oplib.h>
#include <asm/openprom.h>
#include <asm/mmu_context.h>
+#include <asm/sun4paddr.h>
/* TODO: Make it such that interrupt handlers cannot dick with
* the user segment lists, most of the cli/sti pairs can
@@ -59,11 +63,15 @@ extern int num_segmaps, num_contexts;
: "g4", "cc"); \
} while(0);
-/* That's it, we prom_halt() if the cache size is something other than 65536.
+#ifdef CONFIG_SUN4
+#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes
+#else
+/* That's it, we prom_halt() on sun4c if the cache size is something other than 65536.
* So let's save some cycles and just use that everywhere except for that bootup
* sanity check.
*/
-#define SUN4C_VAC_SIZE 65536
+#define SUN4C_VAC_SIZE 65536
+#endif
#define SUN4C_KERNEL_BUCKETS 32
@@ -427,22 +435,76 @@ static inline void sun4c_init_clean_mmu(unsigned long kernel_end)
sun4c_set_context(savectx);
}
-void sun4c_probe_vac(void)
+__initfunc(void sun4c_probe_vac(void))
{
sun4c_disable_vac();
- if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
- (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
- /* PROM on SS1 lacks this info, to be super safe we
- * hard code it here since this arch is cast in stone.
- */
- sun4c_vacinfo.num_bytes = 65536;
- sun4c_vacinfo.linesize = 16;
+
+ if (ARCH_SUN4) {
+ switch(idprom->id_machtype) {
+
+ case (SM_SUN4|SM_4_110):
+ sun4c_vacinfo.type = NONE;
+ sun4c_vacinfo.num_bytes = 0;
+ sun4c_vacinfo.linesize = 0;
+ sun4c_vacinfo.do_hwflushes = 0;
+ prom_printf("No VAC. Get some bucks and buy a real computer.");
+ prom_halt();
+ break;
+
+ case (SM_SUN4|SM_4_260):
+ sun4c_vacinfo.type = WRITE_BACK;
+ sun4c_vacinfo.num_bytes = 128 * 1024;
+ sun4c_vacinfo.linesize = 16;
+ sun4c_vacinfo.do_hwflushes = 0;
+ break;
+
+ case (SM_SUN4|SM_4_330):
+ sun4c_vacinfo.type = WRITE_THROUGH;
+ sun4c_vacinfo.num_bytes = 128 * 1024;
+ sun4c_vacinfo.linesize = 16;
+ sun4c_vacinfo.do_hwflushes = 0;
+ break;
+
+ case (SM_SUN4|SM_4_470):
+ sun4c_vacinfo.type = WRITE_BACK;
+ sun4c_vacinfo.num_bytes = 128 * 1024;
+ sun4c_vacinfo.linesize = 32;
+ sun4c_vacinfo.do_hwflushes = 0;
+ break;
+
+ default:
+ prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype);
+ prom_halt();
+ }
} else {
- sun4c_vacinfo.num_bytes = prom_getintdefault(prom_root_node,
- "vac-size", 65536);
- sun4c_vacinfo.linesize = prom_getintdefault(prom_root_node,
- "vac-linesize", 16);
+ sun4c_vacinfo.type = WRITE_THROUGH;
+
+ if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+ (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+ /* PROM on SS1 lacks this info, to be super safe we
+ * hard code it here since this arch is cast in stone.
+ */
+ sun4c_vacinfo.num_bytes = 65536;
+ sun4c_vacinfo.linesize = 16;
+ } else {
+ sun4c_vacinfo.num_bytes =
+ prom_getintdefault(prom_root_node, "vac-size", 65536);
+ sun4c_vacinfo.linesize =
+ prom_getintdefault(prom_root_node, "vac-linesize", 16);
+ }
+ sun4c_vacinfo.do_hwflushes =
+ prom_getintdefault(prom_root_node, "vac-hwflush", 0);
+
+ if(sun4c_vacinfo.do_hwflushes == 0)
+ sun4c_vacinfo.do_hwflushes =
+ prom_getintdefault(prom_root_node, "vac_hwflush", 0);
+
+ if (sun4c_vacinfo.num_bytes != 65536) {
+ prom_printf("WEIRD Sun4C VAC cache size, tell davem");
+ prom_halt();
+ }
}
+
sun4c_vacinfo.num_lines =
(sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize);
switch(sun4c_vacinfo.linesize) {
@@ -458,17 +520,6 @@ void sun4c_probe_vac(void)
prom_halt();
};
- sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node,
- "vac-hwflush", 0);
- if(sun4c_vacinfo.do_hwflushes == 0)
- sun4c_vacinfo.do_hwflushes = prom_getintdefault(prom_root_node,
- "vac_hwflush", 0);
-
- if(sun4c_vacinfo.num_bytes != 65536) {
- prom_printf("WEIRD Sun4C VAC cache size, tell davem");
- prom_halt();
- }
-
sun4c_flush_all();
sun4c_enable_vac();
}
@@ -476,6 +527,7 @@ void sun4c_probe_vac(void)
/* Patch instructions for the low level kernel fault handler. */
extern unsigned long invalid_segment_patch1, invalid_segment_patch1_ff;
extern unsigned long invalid_segment_patch2, invalid_segment_patch2_ff;
+extern unsigned long invalid_segment_patch1_1ff, invalid_segment_patch2_1ff;
extern unsigned long num_context_patch1, num_context_patch1_16;
extern unsigned long num_context_patch2, num_context_patch2_16;
extern unsigned long vac_linesize_patch, vac_linesize_patch_32;
@@ -502,6 +554,12 @@ static void patch_kernel_fault_handler(void)
PATCH_INSN(invalid_segment_patch2_ff,
invalid_segment_patch2);
break;
+ case 512:
+ PATCH_INSN(invalid_segment_patch1_1ff,
+ invalid_segment_patch1);
+ PATCH_INSN(invalid_segment_patch2_1ff,
+ invalid_segment_patch2);
+ break;
default:
prom_printf("Unhandled number of segmaps: %d\n",
num_segmaps);
@@ -541,38 +599,80 @@ static void patch_kernel_fault_handler(void)
}
}
-static void sun4c_probe_mmu(void)
+__initfunc(static void sun4c_probe_mmu(void))
{
- if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
- (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
- /* Hardcode these just to be safe, PROM on SS1 does
- * not have this info available in the root node.
- */
- num_segmaps = 128;
- num_contexts = 8;
+ if (ARCH_SUN4) {
+ switch(idprom->id_machtype) {
+ case (SM_SUN4|SM_4_110):
+ prom_printf("No support for 4100 yet\n");
+ prom_halt();
+ num_segmaps = 256;
+ num_contexts = 8;
+ break;
+
+ case (SM_SUN4|SM_4_260):
+ prom_printf("No support for 4200 yet\n");
+ prom_halt();
+ num_segmaps = 512;
+ num_contexts = 16;
+ break;
+
+ case (SM_SUN4|SM_4_330):
+ num_segmaps = 256;
+ num_contexts = 16;
+ break;
+
+ case (SM_SUN4|SM_4_470):
+ prom_printf("No support for 4400 yet\n");
+ prom_halt();
+ num_segmaps = 1024;
+ num_contexts = 64;
+ break;
+ default:
+ prom_printf("Invalid SUN4 model\n");
+ prom_halt();
+ }
} else {
- num_segmaps = prom_getintdefault(prom_root_node, "mmu-npmg", 128);
- num_contexts = prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
+ if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+ (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+ /* Hardcode these just to be safe, PROM on SS1 does
+ * not have this info available in the root node.
+ */
+ num_segmaps = 128;
+ num_contexts = 8;
+ } else {
+ num_segmaps =
+ prom_getintdefault(prom_root_node, "mmu-npmg", 128);
+ num_contexts =
+ prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
+ }
}
patch_kernel_fault_handler();
}
volatile unsigned long *sun4c_memerr_reg = 0;
-void sun4c_probe_memerr_reg(void)
+__initfunc(void sun4c_probe_memerr_reg(void))
{
int node;
struct linux_prom_registers regs[1];
- node = prom_getchild(prom_root_node);
- node = prom_searchsiblings(prom_root_node, "memory-error");
- if (!node)
- return;
- prom_getproperty(node, "reg", (char *)regs, sizeof(regs));
- sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0,
- regs[0].reg_size,
- "memory parity error",
- regs[0].which_io, 0);
+ if (ARCH_SUN4) {
+ sun4c_memerr_reg = sparc_alloc_io(SUN4_MEMREG_PHYSADDR, 0,
+ PAGE_SIZE,
+ "memory parity error",
+ 0x0, 0);
+ } else {
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(prom_root_node, "memory-error");
+ if (!node)
+ return;
+ prom_getproperty(node, "reg", (char *)regs, sizeof(regs));
+ sun4c_memerr_reg = sparc_alloc_io(regs[0].phys_addr, 0,
+ regs[0].reg_size,
+ "memory parity error",
+ regs[0].which_io, 0);
+ }
}
static inline void sun4c_init_ss2_cache_bug(void)
@@ -581,6 +681,7 @@ static inline void sun4c_init_ss2_cache_bug(void)
if((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
(idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) ||
+ (idprom->id_machtype == (SM_SUN4 | SM_4_330)) ||
(idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) {
/* Whee.. */
printk("SS2 cache bug detected, uncaching trap table page\n");
@@ -626,13 +727,14 @@ struct sun4c_mmu_entry {
unsigned char pseg;
unsigned char locked;
};
-static struct sun4c_mmu_entry mmu_entry_pool[256];
+
+static struct sun4c_mmu_entry mmu_entry_pool[SUN4C_MAX_SEGMAPS];
__initfunc(static void sun4c_init_mmu_entry_pool(void))
{
int i;
- for(i=0; i < 256; i++) {
+ for(i=0; i < SUN4C_MAX_SEGMAPS; i++) {
mmu_entry_pool[i].pseg = i;
mmu_entry_pool[i].next = 0;
mmu_entry_pool[i].prev = 0;
@@ -703,7 +805,8 @@ struct sun4c_mmu_ring {
struct sun4c_mmu_entry ringhd;
int num_entries;
};
-static struct sun4c_mmu_ring sun4c_context_ring[16]; /* used user entries */
+
+static struct sun4c_mmu_ring sun4c_context_ring[SUN4C_MAX_CONTEXTS]; /* used user entries */
static struct sun4c_mmu_ring sun4c_ufree_ring; /* free user entries */
struct sun4c_mmu_ring sun4c_kernel_ring; /* used kernel entries */
struct sun4c_mmu_ring sun4c_kfree_ring; /* free kernel entries */
@@ -711,7 +814,7 @@ struct sun4c_mmu_ring sun4c_kfree_ring; /* free kernel entries */
static inline void sun4c_init_rings(unsigned long *mempool)
{
int i;
- for(i=0; i<16; i++) {
+ for(i=0; i<SUN4C_MAX_CONTEXTS; i++) {
sun4c_context_ring[i].ringhd.next =
sun4c_context_ring[i].ringhd.prev =
&sun4c_context_ring[i].ringhd;
@@ -1120,7 +1223,7 @@ static int sun4c_lowbucket_avail;
#define BUCKET_PTE(page) \
((((page) - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(SUN4C_PAGE_KERNEL))
#define BUCKET_PTE_PAGE(pte) \
- (PAGE_OFFSET + (((pte) & 0xffff) << PAGE_SHIFT))
+ (PAGE_OFFSET + (((pte) & SUN4C_PFN_MASK) << PAGE_SHIFT))
static inline void get_locked_segment(unsigned long addr)
{
@@ -1180,12 +1283,18 @@ static inline void garbage_collect(int entry)
free_locked_segment(BUCKET_ADDR(entry));
}
+#ifdef CONFIG_SUN4
+#define TASK_STRUCT_ORDER 0
+#else
+#define TASK_STRUCT_ORDER 1
+#endif
+
static struct task_struct *sun4c_alloc_task_struct(void)
{
unsigned long addr, pages;
int entry;
- pages = __get_free_pages(GFP_KERNEL, 1);
+ pages = __get_free_pages(GFP_KERNEL, TASK_STRUCT_ORDER);
if(!pages)
return (struct task_struct *) 0;
@@ -1193,7 +1302,7 @@ static struct task_struct *sun4c_alloc_task_struct(void)
if(sun4c_bucket[entry] == BUCKET_EMPTY)
break;
if(entry == NR_TASKS) {
- free_pages(pages, 1);
+ free_pages(pages, TASK_STRUCT_ORDER);
return (struct task_struct *) 0;
}
if(entry >= sun4c_lowbucket_avail)
@@ -1204,8 +1313,9 @@ static struct task_struct *sun4c_alloc_task_struct(void)
if(sun4c_get_segmap(addr) == invalid_segment)
get_locked_segment(addr);
sun4c_put_pte(addr, BUCKET_PTE(pages));
+#ifndef CONFIG_SUN4
sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE));
-
+#endif
return (struct task_struct *) addr;
}
@@ -1217,15 +1327,18 @@ static void sun4c_free_task_struct_hw(struct task_struct *tsk)
/* We are deleting a mapping, so the flush here is mandatory. */
sun4c_flush_page_hw(tsaddr);
+#ifndef CONFIG_SUN4
sun4c_flush_page_hw(tsaddr + PAGE_SIZE);
-
+#endif
sun4c_put_pte(tsaddr, 0);
+#ifndef CONFIG_SUN4
sun4c_put_pte(tsaddr + PAGE_SIZE, 0);
+#endif
sun4c_bucket[entry] = BUCKET_EMPTY;
if(entry < sun4c_lowbucket_avail)
sun4c_lowbucket_avail = entry;
- free_pages(pages, 1);
+ free_pages(pages, TASK_STRUCT_ORDER);
garbage_collect(entry);
}
@@ -1237,15 +1350,18 @@ static void sun4c_free_task_struct_sw(struct task_struct *tsk)
/* We are deleting a mapping, so the flush here is mandatory. */
sun4c_flush_page_sw(tsaddr);
+#ifndef CONFIG_SUN4
sun4c_flush_page_sw(tsaddr + PAGE_SIZE);
-
+#endif
sun4c_put_pte(tsaddr, 0);
+#ifndef CONFIG_SUN4
sun4c_put_pte(tsaddr + PAGE_SIZE, 0);
+#endif
sun4c_bucket[entry] = BUCKET_EMPTY;
if(entry < sun4c_lowbucket_avail)
sun4c_lowbucket_avail = entry;
- free_pages(pages, 1);
+ free_pages(pages, TASK_STRUCT_ORDER);
garbage_collect(entry);
}
@@ -1253,9 +1369,8 @@ __initfunc(static void sun4c_init_buckets(void))
{
int entry;
- if(sizeof(union task_union) != (PAGE_SIZE << 1)) {
- prom_printf("task union not 2 pages!\n");
- prom_halt();
+ if(sizeof(union task_union) != (PAGE_SIZE << TASK_STRUCT_ORDER)) {
+ prom_printf("task union not %d page(s)!\n", 1 << TASK_STRUCT_ORDER);
}
for(entry = 0; entry < NR_TASKS; entry++)
sun4c_bucket[entry] = BUCKET_EMPTY;
@@ -1949,12 +2064,17 @@ static void sun4c_set_pte(pte_t *ptep, pte_t pte)
*ptep = pte;
}
+static void sun4c_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
+{
+}
+
+
void sun4c_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
int bus_type, int rdonly)
{
unsigned long page_entry;
- page_entry = ((physaddr >> PAGE_SHIFT) & 0xffff);
+ page_entry = ((physaddr >> PAGE_SHIFT) & SUN4C_PFN_MASK);
page_entry |= ((pg_iobits | _SUN4C_PAGE_PRIV) & ~(_SUN4C_PAGE_PRESENT));
if(rdonly)
page_entry &= ~_SUN4C_WRITEABLE;
@@ -2092,21 +2212,17 @@ static void sun4c_destroy_context_sw(struct mm_struct *mm)
}
}
-#if KGPROF_PROFILING
-static char s4cinfo[10240];
-#else
-static char s4cinfo[512];
-#endif
-
-static char *sun4c_mmu_info(void)
+static int sun4c_mmu_info(char *buf)
{
int used_user_entries, i;
+ int len;
used_user_entries = 0;
for(i=0; i < num_contexts; i++)
used_user_entries += sun4c_context_ring[i].num_entries;
- sprintf(s4cinfo, "vacsize\t\t: %d bytes\n"
+ len = sprintf(buf,
+ "vacsize\t\t: %d bytes\n"
"vachwflush\t: %s\n"
"vaclinesize\t: %d bytes\n"
"mmuctxs\t\t: %d\n"
@@ -2135,29 +2251,31 @@ static char *sun4c_mmu_info(void)
#if KGPROF_PROFILING
{
- char *p = s4cinfo + strlen(s4cinfo);
int i,j;
- sprintf(p,"kgprof profiling:\n"); p += strlen(p);
+ len += sprintf(buf + len,"kgprof profiling:\n");
for (i=0;i<KGPROF_SIZE && kgprof_counters[i].addr[0];i++) {
- sprintf(p,"%5d ",kgprof_counters[i].count); p += strlen(p);
+ len += sprintf(buf + len,"%5d ",kgprof_counters[i].count);
for (j=0;j<KGPROF_DEPTH;j++) {
- sprintf(p,"%08x ",kgprof_counters[i].addr[j]);
- p += strlen(p);
+ len += sprintf(buf + len,"%08x ",kgprof_counters[i].addr[j]);
}
- sprintf(p,"\n"); p += strlen(p);
+ len += sprintf(buf + len,"\n");
}
}
#endif
- return s4cinfo;
+ return len;
}
/* Nothing below here should touch the mmu hardware nor the mmu_entry
* data structures.
*/
+#if 0 /* Not used due to BTFIXUPs */
static unsigned int sun4c_pmd_align(unsigned int addr) { return SUN4C_PMD_ALIGN(addr); }
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_ALIGN(addr); }
+#endif
/* First the functions which the mid-level code uses to directly
* manipulate the software page tables. Some defines since we are
@@ -2170,12 +2288,17 @@ static unsigned int sun4c_pgdir_align(unsigned int addr) { return SUN4C_PGDIR_AL
#define PGD_DIRTY 0x040
#define PGD_TABLE (PGD_PRESENT | PGD_RW | PGD_USER | PGD_ACCESSED | PGD_DIRTY)
+#if 0 /* Not used due to BTFIXUPs */
static unsigned long sun4c_vmalloc_start(void)
{
return SUN4C_VMALLOC_START;
}
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static int sun4c_pte_none(pte_t pte) { return !pte_val(pte); }
+#endif
+
static int sun4c_pte_present(pte_t pte)
{
return ((pte_val(pte) & (_SUN4C_PAGE_PRESENT | _SUN4C_PAGE_PRIV)) != 0);
@@ -2204,35 +2327,47 @@ static void sun4c_pgd_clear(pgd_t * pgdp) { }
* The following only work if pte_present() is true.
* Undefined behaviour if not..
*/
+#if 0 /* Not used due to BTFIXUPs */
static int sun4c_pte_write(pte_t pte)
{
return pte_val(pte) & _SUN4C_PAGE_WRITE;
}
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static int sun4c_pte_dirty(pte_t pte)
{
return pte_val(pte) & _SUN4C_PAGE_MODIFIED;
}
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static int sun4c_pte_young(pte_t pte)
{
return pte_val(pte) & _SUN4C_PAGE_ACCESSED;
}
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static pte_t sun4c_pte_wrprotect(pte_t pte)
{
return __pte(pte_val(pte) & ~(_SUN4C_PAGE_WRITE | _SUN4C_PAGE_SILENT_WRITE));
}
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static pte_t sun4c_pte_mkclean(pte_t pte)
{
return __pte(pte_val(pte) & ~(_SUN4C_PAGE_MODIFIED | _SUN4C_PAGE_SILENT_WRITE));
}
+#endif
+#if 0 /* Not used due to BTFIXUPs */
static pte_t sun4c_pte_mkold(pte_t pte)
{
return __pte(pte_val(pte) & ~(_SUN4C_PAGE_ACCESSED | _SUN4C_PAGE_SILENT_READ));
}
+#endif
static pte_t sun4c_pte_mkwrite(pte_t pte)
{
@@ -2277,22 +2412,29 @@ static pte_t sun4c_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
return __pte(((page - PAGE_OFFSET) >> PAGE_SHIFT) | pgprot_val(pgprot));
}
+#if 0 /* Not used due to BTFIXUPs */
static pte_t sun4c_pte_modify(pte_t pte, pgprot_t newprot)
{
return __pte((pte_val(pte) & _SUN4C_PAGE_CHG_MASK) |
pgprot_val(newprot));
}
+#endif
static unsigned long sun4c_pte_page(pte_t pte)
{
- return (PAGE_OFFSET + ((pte_val(pte) & 0xffff) << (PAGE_SHIFT)));
+ return (PAGE_OFFSET + ((pte_val(pte) & SUN4C_PFN_MASK) << (PAGE_SHIFT)));
}
-static unsigned long sun4c_pmd_page(pmd_t pmd)
+static inline unsigned long sun4c_pmd_page(pmd_t pmd)
{
return (pmd_val(pmd) & PAGE_MASK);
}
+static unsigned long sun4c_pgd_page(pgd_t pgd)
+{
+ return 0;
+}
+
/* to find an entry in a page-table-directory */
pgd_t *sun4c_pgd_offset(struct mm_struct * mm, unsigned long address)
{
@@ -2351,6 +2493,16 @@ static pte_t *sun4c_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
return (pte_t *) sun4c_pmd_page(*pmd) + address;
}
+static void sun4c_free_pte_slow(pte_t *pte)
+{
+ free_page((unsigned long)pte);
+}
+
+static void sun4c_free_pgd_slow(pgd_t *pgd)
+{
+ free_page((unsigned long)pgd);
+}
+
/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
* inside the pgd, so has no extra memory associated with it.
@@ -2364,16 +2516,73 @@ static pmd_t *sun4c_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
return (pmd_t *) pgd;
}
+extern __inline__ pgd_t *sun4c_get_pgd_fast(void)
+{
+ unsigned long *ret;
+
+ if((ret = pgd_quicklist) != NULL) {
+ pgd_quicklist = (unsigned long *)(*ret);
+ ret[0] = ret[1];
+ pgtable_cache_size--;
+ } else {
+ pgd_t *init;
+
+ ret = (unsigned long *)__get_free_page(GFP_KERNEL);
+ memset (ret, 0, (KERNBASE / SUN4C_PGDIR_SIZE) * sizeof(pgd_t));
+ init = pgd_offset(&init_mm, 0);
+ memcpy (((pgd_t *)ret) + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ }
+ return (pgd_t *)ret;
+}
+
+static void sun4c_set_pgdir(unsigned long address, pgd_t entry)
+{
+ /* Nothing to do */
+}
+
+extern __inline__ void sun4c_free_pgd_fast(pgd_t *pgd)
+{
+ *(unsigned long *)pgd = (unsigned long) pgd_quicklist;
+ pgd_quicklist = (unsigned long *) pgd;
+ pgtable_cache_size++;
+}
+
+extern __inline__ pte_t *sun4c_get_pte_fast(void)
+{
+ unsigned long *ret;
+
+ if((ret = (unsigned long *)pte_quicklist) != NULL) {
+ pte_quicklist = (unsigned long *)(*ret);
+ ret[0] = ret[1];
+ pgtable_cache_size--;
+ }
+ return (pte_t *)ret;
+}
+
+extern __inline__ void sun4c_free_pte_fast(pte_t *pte)
+{
+ *(unsigned long *)pte = (unsigned long) pte_quicklist;
+ pte_quicklist = (unsigned long *) pte;
+ pgtable_cache_size++;
+}
+
static void sun4c_pte_free(pte_t *pte)
{
- free_page((unsigned long) pte);
+ sun4c_free_pte_fast(pte);
}
static pte_t *sun4c_pte_alloc(pmd_t * pmd, unsigned long address)
{
address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1);
if (sun4c_pmd_none(*pmd)) {
- pte_t *page = (pte_t *) get_free_page(GFP_KERNEL);
+ pte_t *page = (pte_t *) sun4c_get_pte_fast();
+
+ if (page) {
+ *pmd = __pmd(PGD_TABLE | (unsigned long) page);
+ return page + address;
+ }
+ page = (pte_t *) get_free_page(GFP_KERNEL);
if (sun4c_pmd_none(*pmd)) {
if (page) {
*pmd = __pmd(PGD_TABLE | (unsigned long) page);
@@ -2392,13 +2601,17 @@ static pte_t *sun4c_pte_alloc(pmd_t * pmd, unsigned long address)
return (pte_t *) sun4c_pmd_page(*pmd) + address;
}
+static pte_t *sun4c_pte_get(void)
+{
+ return sun4c_get_pte_fast();
+}
+
/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
* inside the pgd, so has no extra memory associated with it.
*/
static void sun4c_pmd_free(pmd_t * pmd)
{
- *pmd = __pmd(0);
}
static pmd_t *sun4c_pmd_alloc(pgd_t * pgd, unsigned long address)
@@ -2408,12 +2621,12 @@ static pmd_t *sun4c_pmd_alloc(pgd_t * pgd, unsigned long address)
static void sun4c_pgd_free(pgd_t *pgd)
{
- free_page((unsigned long) pgd);
+ sun4c_free_pgd_fast(pgd);
}
static pgd_t *sun4c_pgd_alloc(void)
{
- return (pgd_t *) get_free_page(GFP_KERNEL);
+ return sun4c_get_pgd_fast();
}
/* There are really two cases of aliases to watch out for, and these
@@ -2435,12 +2648,13 @@ static pgd_t *sun4c_pgd_alloc(void)
*/
static void sun4c_vac_alias_fixup(struct vm_area_struct *vma, unsigned long address, pte_t pte)
{
- struct dentry *dentry;
+ struct dentry *dentry = NULL;
struct inode *inode = NULL;
pgd_t *pgdp;
pte_t *ptep;
- dentry = vma->vm_dentry;
+ if (vma->vm_file)
+ dentry = vma->vm_file->f_dentry;
if(dentry)
inode = dentry->d_inode;
if(inode) {
@@ -2556,134 +2770,147 @@ __initfunc(unsigned long sun4c_paging_init(unsigned long start_mem, unsigned lon
/* Load up routines and constants for sun4c mmu */
__initfunc(void ld_mmu_sun4c(void))
{
+ extern void ___xchg32_sun4c(void);
+
printk("Loading sun4c MMU routines\n");
/* First the constants */
- pmd_shift = SUN4C_PMD_SHIFT;
- pmd_size = SUN4C_PMD_SIZE;
- pmd_mask = SUN4C_PMD_MASK;
- pgdir_shift = SUN4C_PGDIR_SHIFT;
- pgdir_size = SUN4C_PGDIR_SIZE;
- pgdir_mask = SUN4C_PGDIR_MASK;
-
- ptrs_per_pte = SUN4C_PTRS_PER_PTE;
- ptrs_per_pmd = SUN4C_PTRS_PER_PMD;
- ptrs_per_pgd = SUN4C_PTRS_PER_PGD;
-
- page_none = SUN4C_PAGE_NONE;
- page_shared = SUN4C_PAGE_SHARED;
- page_copy = SUN4C_PAGE_COPY;
- page_readonly = SUN4C_PAGE_READONLY;
- page_kernel = SUN4C_PAGE_KERNEL;
+ BTFIXUPSET_SIMM13(pmd_shift, SUN4C_PMD_SHIFT);
+ BTFIXUPSET_SETHI(pmd_size, SUN4C_PMD_SIZE);
+ BTFIXUPSET_SETHI(pmd_mask, SUN4C_PMD_MASK);
+ BTFIXUPSET_SIMM13(pgdir_shift, SUN4C_PGDIR_SHIFT);
+ BTFIXUPSET_SETHI(pgdir_size, SUN4C_PGDIR_SIZE);
+ BTFIXUPSET_SETHI(pgdir_mask, SUN4C_PGDIR_MASK);
+
+ BTFIXUPSET_SIMM13(ptrs_per_pte, SUN4C_PTRS_PER_PTE);
+ BTFIXUPSET_SIMM13(ptrs_per_pmd, SUN4C_PTRS_PER_PMD);
+ BTFIXUPSET_SIMM13(ptrs_per_pgd, SUN4C_PTRS_PER_PGD);
+ BTFIXUPSET_SIMM13(user_ptrs_per_pgd, KERNBASE / SUN4C_PGDIR_SIZE);
+
+ BTFIXUPSET_INT(page_none, pgprot_val(SUN4C_PAGE_NONE));
+ BTFIXUPSET_INT(page_shared, pgprot_val(SUN4C_PAGE_SHARED));
+ BTFIXUPSET_INT(page_copy, pgprot_val(SUN4C_PAGE_COPY));
+ BTFIXUPSET_INT(page_readonly, pgprot_val(SUN4C_PAGE_READONLY));
+ BTFIXUPSET_INT(page_kernel, pgprot_val(SUN4C_PAGE_KERNEL));
pg_iobits = _SUN4C_PAGE_PRESENT | _SUN4C_READABLE | _SUN4C_WRITEABLE |
_SUN4C_PAGE_IO | _SUN4C_PAGE_NOCACHE;
/* Functions */
- flush_cache_all = sun4c_flush_cache_all;
+#ifndef __SMP__
+ BTFIXUPSET_CALL(___xchg32, ___xchg32_sun4c, BTFIXUPCALL_NORM);
+#endif
+ BTFIXUPSET_CALL(get_pte_fast, sun4c_pte_get, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(get_pgd_fast, sun4c_pgd_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(free_pte_slow, sun4c_free_pte_slow, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(free_pgd_slow, sun4c_free_pgd_slow, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(set_pgdir, sun4c_set_pgdir, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(flush_cache_all, sun4c_flush_cache_all, BTFIXUPCALL_NORM);
if(sun4c_vacinfo.do_hwflushes) {
- flush_cache_mm = sun4c_flush_cache_mm_hw;
- flush_cache_range = sun4c_flush_cache_range_hw;
- flush_cache_page = sun4c_flush_cache_page_hw;
- flush_page_to_ram = sun4c_flush_page_to_ram_hw;
- flush_tlb_mm = sun4c_flush_tlb_mm_hw;
- flush_tlb_range = sun4c_flush_tlb_range_hw;
- flush_tlb_page = sun4c_flush_tlb_page_hw;
- free_task_struct = sun4c_free_task_struct_hw;
- switch_to_context = sun4c_switch_to_context_hw;
- destroy_context = sun4c_destroy_context_hw;
- init_new_context = sun4c_init_new_context_hw;
+ BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_to_ram, sun4c_flush_page_to_ram_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(free_task_struct, sun4c_free_task_struct_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(switch_to_context, sun4c_switch_to_context_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context_hw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(init_new_context, sun4c_init_new_context_hw, BTFIXUPCALL_NORM);
} else {
- flush_cache_mm = sun4c_flush_cache_mm_sw;
- flush_cache_range = sun4c_flush_cache_range_sw;
- flush_cache_page = sun4c_flush_cache_page_sw;
- flush_page_to_ram = sun4c_flush_page_to_ram_sw;
- flush_tlb_mm = sun4c_flush_tlb_mm_sw;
- flush_tlb_range = sun4c_flush_tlb_range_sw;
- flush_tlb_page = sun4c_flush_tlb_page_sw;
- free_task_struct = sun4c_free_task_struct_sw;
- switch_to_context = sun4c_switch_to_context_sw;
- destroy_context = sun4c_destroy_context_sw;
- init_new_context = sun4c_init_new_context_sw;
- }
-
- flush_tlb_all = sun4c_flush_tlb_all;
-
- flush_sig_insns = sun4c_flush_sig_insns;
-
- set_pte = sun4c_set_pte;
- pmd_align = sun4c_pmd_align;
- pgdir_align = sun4c_pgdir_align;
- vmalloc_start = sun4c_vmalloc_start;
-
- pte_page = sun4c_pte_page;
- pmd_page = sun4c_pmd_page;
-
- sparc_update_rootmmu_dir = sun4c_update_rootmmu_dir;
-
- pte_none = sun4c_pte_none;
- pte_present = sun4c_pte_present;
- pte_clear = sun4c_pte_clear;
-
- pmd_none = sun4c_pmd_none;
- pmd_bad = sun4c_pmd_bad;
- pmd_present = sun4c_pmd_present;
- pmd_clear = sun4c_pmd_clear;
-
- pgd_none = sun4c_pgd_none;
- pgd_bad = sun4c_pgd_bad;
- pgd_present = sun4c_pgd_present;
- pgd_clear = sun4c_pgd_clear;
-
- mk_pte = sun4c_mk_pte;
- mk_pte_phys = sun4c_mk_pte_phys;
- mk_pte_io = sun4c_mk_pte_io;
- pte_modify = sun4c_pte_modify;
- pgd_offset = sun4c_pgd_offset;
- pmd_offset = sun4c_pmd_offset;
- pte_offset = sun4c_pte_offset;
- pte_free_kernel = sun4c_pte_free_kernel;
- pmd_free_kernel = sun4c_pmd_free_kernel;
- pte_alloc_kernel = sun4c_pte_alloc_kernel;
- pmd_alloc_kernel = sun4c_pmd_alloc_kernel;
- pte_free = sun4c_pte_free;
- pte_alloc = sun4c_pte_alloc;
- pmd_free = sun4c_pmd_free;
- pmd_alloc = sun4c_pmd_alloc;
- pgd_free = sun4c_pgd_free;
- pgd_alloc = sun4c_pgd_alloc;
-
- pte_write = sun4c_pte_write;
- pte_dirty = sun4c_pte_dirty;
- pte_young = sun4c_pte_young;
- pte_wrprotect = sun4c_pte_wrprotect;
- pte_mkclean = sun4c_pte_mkclean;
- pte_mkold = sun4c_pte_mkold;
- pte_mkwrite = sun4c_pte_mkwrite;
- pte_mkdirty = sun4c_pte_mkdirty;
- pte_mkyoung = sun4c_pte_mkyoung;
- update_mmu_cache = sun4c_update_mmu_cache;
-
- mmu_lockarea = sun4c_lockarea;
- mmu_unlockarea = sun4c_unlockarea;
-
- mmu_get_scsi_one = sun4c_get_scsi_one;
- mmu_get_scsi_sgl = sun4c_get_scsi_sgl;
- mmu_release_scsi_one = sun4c_release_scsi_one;
- mmu_release_scsi_sgl = sun4c_release_scsi_sgl;
-
- mmu_map_dma_area = sun4c_map_dma_area;
-
- mmu_v2p = sun4c_v2p;
- mmu_p2v = sun4c_p2v;
+ BTFIXUPSET_CALL(flush_cache_mm, sun4c_flush_cache_mm_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_range, sun4c_flush_cache_range_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_cache_page, sun4c_flush_cache_page_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_page_to_ram, sun4c_flush_page_to_ram_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_mm, sun4c_flush_tlb_mm_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_range, sun4c_flush_tlb_range_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(flush_tlb_page, sun4c_flush_tlb_page_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(free_task_struct, sun4c_free_task_struct_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(switch_to_context, sun4c_switch_to_context_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(destroy_context, sun4c_destroy_context_sw, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(init_new_context, sun4c_init_new_context_sw, BTFIXUPCALL_NORM);
+ }
+
+ BTFIXUPSET_CALL(flush_tlb_all, sun4c_flush_tlb_all, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(flush_sig_insns, sun4c_flush_sig_insns, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(set_pte, sun4c_set_pte, BTFIXUPCALL_STO1O0);
+
+ BTFIXUPSET_CALL(pte_page, sun4c_pte_page, BTFIXUPCALL_NORM);
+#if PAGE_SHIFT <= 12
+ BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_ANDNINT(PAGE_SIZE - 1));
+#else
+ BTFIXUPSET_CALL(pmd_page, sun4c_pmd_page, BTFIXUPCALL_NORM);
+#endif
+
+ BTFIXUPSET_CALL(sparc_update_rootmmu_dir, sun4c_update_rootmmu_dir, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(pte_present, sun4c_pte_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_clear, sun4c_pte_clear, BTFIXUPCALL_STG0O0);
+
+ BTFIXUPSET_CALL(pmd_bad, sun4c_pmd_bad, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_present, sun4c_pmd_present, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_clear, sun4c_pmd_clear, BTFIXUPCALL_STG0O0);
+
+ BTFIXUPSET_CALL(pgd_none, sun4c_pgd_none, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(pgd_bad, sun4c_pgd_bad, BTFIXUPCALL_RETINT(0));
+ BTFIXUPSET_CALL(pgd_present, sun4c_pgd_present, BTFIXUPCALL_RETINT(1));
+ BTFIXUPSET_CALL(pgd_clear, sun4c_pgd_clear, BTFIXUPCALL_NOP);
+
+ BTFIXUPSET_CALL(mk_pte, sun4c_mk_pte, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte_phys, sun4c_mk_pte_phys, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mk_pte_io, sun4c_mk_pte_io, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_INT(pte_modify_mask, _SUN4C_PAGE_CHG_MASK);
+ BTFIXUPSET_CALL(pgd_offset, sun4c_pgd_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_offset, sun4c_pmd_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_offset, sun4c_pte_offset, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_free_kernel, sun4c_pte_free_kernel, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_free_kernel, sun4c_pmd_free_kernel, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(pte_alloc_kernel, sun4c_pte_alloc_kernel, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_alloc_kernel, sun4c_pmd_alloc_kernel, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(pte_free, sun4c_pte_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_alloc, sun4c_pte_alloc, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pmd_free, sun4c_pmd_free, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(pmd_alloc, sun4c_pmd_alloc, BTFIXUPCALL_RETO0);
+ BTFIXUPSET_CALL(pgd_free, sun4c_pgd_free, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pgd_alloc, sun4c_pgd_alloc, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_HALF(pte_writei, _SUN4C_PAGE_WRITE);
+ BTFIXUPSET_HALF(pte_dirtyi, _SUN4C_PAGE_MODIFIED);
+ BTFIXUPSET_HALF(pte_youngi, _SUN4C_PAGE_ACCESSED);
+ BTFIXUPSET_HALF(pte_wrprotecti, _SUN4C_PAGE_WRITE|_SUN4C_PAGE_SILENT_WRITE);
+ BTFIXUPSET_HALF(pte_mkcleani, _SUN4C_PAGE_MODIFIED|_SUN4C_PAGE_SILENT_WRITE);
+ BTFIXUPSET_HALF(pte_mkoldi, _SUN4C_PAGE_ACCESSED|_SUN4C_PAGE_SILENT_READ);
+ BTFIXUPSET_CALL(pte_mkwrite, sun4c_pte_mkwrite, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_mkdirty, sun4c_pte_mkdirty, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(pte_mkyoung, sun4c_pte_mkyoung, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(update_mmu_cache, sun4c_update_mmu_cache, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mmu_lockarea, sun4c_lockarea, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_unlockarea, sun4c_unlockarea, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mmu_get_scsi_one, sun4c_get_scsi_one, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_get_scsi_sgl, sun4c_get_scsi_sgl, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_release_scsi_one, sun4c_release_scsi_one, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_release_scsi_sgl, sun4c_release_scsi_sgl, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM);
+
+ BTFIXUPSET_CALL(mmu_v2p, sun4c_v2p, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_p2v, sun4c_p2v, BTFIXUPCALL_NORM);
/* Task struct and kernel stack allocating/freeing. */
- alloc_task_struct = sun4c_alloc_task_struct;
+ BTFIXUPSET_CALL(alloc_task_struct, sun4c_alloc_task_struct, BTFIXUPCALL_NORM);
- quick_kernel_fault = sun4c_quick_kernel_fault;
- mmu_info = sun4c_mmu_info;
+ BTFIXUPSET_CALL(quick_kernel_fault, sun4c_quick_kernel_fault, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(mmu_info, sun4c_mmu_info, BTFIXUPCALL_NORM);
/* These should _never_ get called with two level tables. */
- pgd_set = 0;
- pgd_page = 0;
+ BTFIXUPSET_CALL(pgd_set, sun4c_pgd_set, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(pgd_page, sun4c_pgd_page, BTFIXUPCALL_RETO0);
}
diff --git a/arch/sparc/mm/turbosparc.S b/arch/sparc/mm/turbosparc.S
index 415f09056..df580a85c 100644
--- a/arch/sparc/mm/turbosparc.S
+++ b/arch/sparc/mm/turbosparc.S
@@ -1,4 +1,4 @@
-/* $Id: turbosparc.S,v 1.2 1998/03/16 08:40:31 ralf Exp $
+/* $Id: turbosparc.S,v 1.3 1998/05/04 12:41:29 ralf Exp $
* turbosparc.S: High speed TurboSparc mmu/cache operations.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -27,6 +27,7 @@
.globl turbosparc_flush_cache_all
.globl turbosparc_flush_sig_insns
+ .globl turbosparc_flush_page_for_dma
turbosparc_flush_cache_all:
WINDOW_FLUSH(%g4, %g5)
@@ -42,5 +43,6 @@ turbosparc_flush_cache_all:
sta %g0, [%g0] ASI_M_IC_FLCLEAR
turbosparc_flush_sig_insns:
+turbosparc_flush_page_for_dma:
retl
nop
diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S
index b05b7b416..c65f72007 100644
--- a/arch/sparc/mm/viking.S
+++ b/arch/sparc/mm/viking.S
@@ -1,8 +1,8 @@
-/* $Id: viking.S,v 1.6 1997/11/27 15:42:32 jj Exp $
+/* $Id: viking.S,v 1.11 1998/02/20 18:07:50 jj Exp $
* viking.S: High speed Viking cache/mmu operations
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <asm/ptrace.h>
@@ -13,6 +13,7 @@
#include <asm/pgtsrmmu.h>
#include <asm/viking.h>
#include <asm/cprefix.h>
+#include <asm/btfixup.h>
#define WINDOW_FLUSH(tmp1, tmp2) \
mov 0, tmp1; \
@@ -37,40 +38,33 @@
.globl viking_flush_tlb_all, viking_flush_tlb_mm
.globl viking_flush_tlb_range, viking_flush_tlb_page
- .globl viking_c_mxcc_flush_page
- .globl viking_c_flush_page, viking_c_flush_chunk
+ .globl viking_c_flush_chunk, viking_s_flush_chunk
+
+viking_s_flush_chunk:
+ sethi %hi(KERNBASE), %g2
+ ba 2f
+ sub %o0, %g2, %g3
-viking_c_flush_page:
viking_c_flush_chunk:
sethi %hi(KERNBASE), %g2
cmp %o0, %g2
bgeu 2f
sub %o0, %g2, %g3
- sethi %hi(C_LABEL(page_contig_offset)), %g2
- ld [%g2 + %lo(C_LABEL(page_contig_offset))], %g2
+ sethi BTFIXUP_SETHI(page_contig_offset), %g2
ba 2f
sub %o0, %g2, %g3
viking_flush_page:
viking_flush_chunk:
sethi %hi(C_LABEL(srmmu_v2p_hash)), %g2
- or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
srl %o0, 24, %o1
+ or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
sll %o1, 2, %o1
-
ld [%g2 + %o1], %g3
- cmp %g3, 0
- bne 1f
- and %o0, PAGE_MASK, %o0
-
- retl
- nop
-
-1:
- ld [%g3], %o1
- sub %o0, %o1, %g2
- ld [%g3 + 4], %o0
- add %g2, %o0, %g3
+ and %o0, PAGE_MASK, %o0
+ cmp %g3, -1
+ be 9f
+ add %o0, %g3, %g3
2: srl %g3, 12, %g1 ! ppage >> 12
clr %o1 ! set counter, 0 - 127
@@ -124,41 +118,22 @@ viking_flush_chunk:
ble 5b
clr %o2
- retl
+9: retl
nop
-viking_c_mxcc_flush_page:
- sethi %hi(KERNBASE), %g2
- cmp %o0, %g2
- bgeu 2f
- sub %o0, %g2, %g3
- sethi %hi(C_LABEL(page_contig_offset)), %g2
- ld [%g2 + %lo(C_LABEL(page_contig_offset))], %g2
- ba 2f
- sub %o0, %g2, %g3
-
viking_mxcc_flush_page:
sethi %hi(C_LABEL(srmmu_v2p_hash)), %g2
- or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
srl %o0, 24, %o1
+ or %g2, %lo(C_LABEL(srmmu_v2p_hash)), %g2
sll %o1, 2, %o1
-
ld [%g2 + %o1], %g3
- cmp %g3, 0
- bne 1f
- and %o0, PAGE_MASK, %o0
-
- retl
- nop
-
-1:
- ld [%g3], %o1
- sub %o0, %o1, %g2
- ld [%g3 + 4], %o0
- add %g2, %o0, %g3
+ and %o0, PAGE_MASK, %o0
+ cmp %g3, -1
+ be 9f
+ add %o0, %g3, %g3
2: sub %g3, -PAGE_SIZE, %g3 ! ppage + PAGE_SIZE
- mov 0x10, %g2 ! set cacheable bit
sethi %hi(MXCC_SRCSTREAM), %o3 ! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM)
+ mov 0x10, %g2 ! set cacheable bit
or %o3, %lo(MXCC_SRCSTREAM), %o2
or %o3, %lo(MXCC_DESSTREAM), %o3
sub %g3, MXCC_STREAM_SIZE, %g3
@@ -169,7 +144,7 @@ viking_mxcc_flush_page:
bne 6b
sub %g3, MXCC_STREAM_SIZE, %g3
- retl
+9: retl
nop
viking_mxcc_flush_chunk:
@@ -212,13 +187,12 @@ viking_flush_tlb_range:
cmp %o3, -1
be 2f
#endif
- srl %o1, SRMMU_PGDIR_SHIFT, %o1
+ sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
sta %o3, [%g1] ASI_M_MMUREGS
- sll %o1, SRMMU_PGDIR_SHIFT, %o1
- sethi %hi(1 << SRMMU_PGDIR_SHIFT), %o4
+ and %o1, %o4, %o1
add %o1, 0x200, %o1
sta %g0, [%o1] ASI_M_FLUSH_PROBE
-1: add %o1, %o4, %o1
+1: sub %o1, %o4, %o1
cmp %o1, %o2
blu,a 1b
sta %g0, [%o1] ASI_M_FLUSH_PROBE
diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile
index 9c820a006..917aa9ad7 100644
--- a/arch/sparc/prom/Makefile
+++ b/arch/sparc/prom/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $
+# $Id: Makefile,v 1.6 1998/01/30 10:58:59 jj Exp $
# Makefile for the Sun Boot PROM interface library under
# Linux.
#
@@ -9,7 +9,11 @@
# Note 2! The CFLAGS definitions are now in the main makefile...
OBJS = bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \
- palloc.o ranges.o segment.o tree.o console.o printf.o
+ palloc.o ranges.o segment.o console.o printf.o tree.o
+
+ifeq ($(CONFIG_SUN4),y)
+OBJS += sun4prom.o
+endif
all: promlib.a
diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c
index e7bd9b06d..10a603455 100644
--- a/arch/sparc/prom/bootstr.c
+++ b/arch/sparc/prom/bootstr.c
@@ -1,4 +1,4 @@
-/* $Id: bootstr.c,v 1.14 1997/06/19 16:28:49 jj Exp $
+/* $Id: bootstr.c,v 1.17 1998/02/09 13:26:21 jj Exp $
* bootstr.c: Boot string/argument acquisition from the PROM.
*
* Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -7,12 +7,15 @@
#include <linux/config.h>
#include <linux/string.h>
#include <asm/oplib.h>
+#include <asm/sun4prom.h>
#include <linux/init.h>
#define BARG_LEN 256
-static char barg_buf[BARG_LEN] __initdata = { 0 };
+static char barg_buf[BARG_LEN] = { 0 };
static char fetched __initdata = 0;
+extern linux_sun4_romvec *sun4_romvec;
+
__initfunc(char *
prom_getbootargs(void))
{
@@ -26,6 +29,7 @@ prom_getbootargs(void))
switch(prom_vers) {
case PROM_V0:
+ case PROM_SUN4:
cp = barg_buf;
/* Start from 1 and go over fd(0,0,0)kernel */
for(iter = 1; iter < 8; iter++) {
diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c
index 4c999477b..3bbc7ade0 100644
--- a/arch/sparc/prom/console.c
+++ b/arch/sparc/prom/console.c
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.14 1997/05/14 20:44:58 davem Exp $
+/* $Id: console.c,v 1.17 1998/03/09 14:04:21 jj Exp $
* console.c: Routines that deal with sending and receiving IO
* to/from the current console device using the PROM.
*
@@ -10,12 +10,12 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/openprom.h>
+#include <asm/sun4prom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <linux/string.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
/* Non blocking get character from console input device, returns -1
* if no input was taken. This can be used for polling.
@@ -30,6 +30,7 @@ prom_nbgetchar(void)
save_flags(flags); cli();
switch(prom_vers) {
case PROM_V0:
+ case PROM_SUN4:
i = (*(romvec->pv_nbgetchar))();
break;
case PROM_V2:
@@ -45,9 +46,7 @@ prom_nbgetchar(void)
i = -1;
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return i; /* Ugh, we could spin forever on unsupported proms ;( */
}
@@ -65,6 +64,7 @@ prom_nbputchar(char c)
save_flags(flags); cli();
switch(prom_vers) {
case PROM_V0:
+ case PROM_SUN4:
i = (*(romvec->pv_nbputchar))(c);
break;
case PROM_V2:
@@ -89,9 +89,7 @@ prom_nbputchar(char c)
i = -1;
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return i; /* Ugh, we could spin forever on unsupported proms ;( */
}
@@ -125,6 +123,7 @@ prom_query_input_device()
switch(prom_vers) {
case PROM_V0:
case PROM_V2:
+ case PROM_SUN4:
default:
switch(*romvec->pv_stdin) {
case PROMDEV_KBD: return PROMDEV_IKBD;
@@ -136,9 +135,7 @@ prom_query_input_device()
case PROM_V3:
save_flags(flags); cli();
st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
if(prom_node_has_property(st_p, "keyboard"))
return PROMDEV_IKBD;
@@ -173,6 +170,7 @@ prom_query_output_device()
switch(prom_vers) {
case PROM_V0:
+ case PROM_SUN4:
switch(*romvec->pv_stdin) {
case PROMDEV_SCREEN: return PROMDEV_OSCREEN;
case PROMDEV_TTYA: return PROMDEV_OTTYA;
@@ -183,9 +181,7 @@ prom_query_output_device()
case PROM_V3:
save_flags(flags); cli();
st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
if (propl >= 0 && propl == sizeof("display") &&
diff --git a/arch/sparc/prom/devmap.c b/arch/sparc/prom/devmap.c
index cd99ac3d6..463b07527 100644
--- a/arch/sparc/prom/devmap.c
+++ b/arch/sparc/prom/devmap.c
@@ -1,4 +1,4 @@
-/* $Id: devmap.c,v 1.5 1997/05/14 20:44:59 davem Exp $
+/* $Id: devmap.c,v 1.6 1998/03/09 14:04:23 jj Exp $
* promdevmap.c: Map device/IO areas to virtual addresses.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -11,8 +11,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
/* Just like the routines in palloc.c, these should not be used
* by the kernel at all. Bootloader facility mainly. And again,
@@ -35,9 +34,7 @@ prom_mapio(char *vhint, int ios, unsigned int paddr, unsigned int num_bytes)
else
ret = (*(romvec->pv_v2devops.v2_dumb_mmap))(vhint, ios, paddr,
num_bytes);
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return ret;
}
@@ -51,9 +48,7 @@ prom_unmapio(char *vaddr, unsigned int num_bytes)
if(num_bytes == 0x0) return;
save_flags(flags); cli();
(*(romvec->pv_v2devops.v2_dumb_munmap))(vaddr, num_bytes);
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return;
}
diff --git a/arch/sparc/prom/devops.c b/arch/sparc/prom/devops.c
index f7feb0815..c273b9922 100644
--- a/arch/sparc/prom/devops.c
+++ b/arch/sparc/prom/devops.c
@@ -1,4 +1,4 @@
-/* $Id: devops.c,v 1.10 1997/05/14 20:44:59 davem Exp $
+/* $Id: devops.c,v 1.11 1998/03/09 14:04:24 jj Exp $
* devops.c: Device operations using the PROM.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -10,8 +10,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
/* Open the device described by the string 'dstr'. Returns the handle
* to that device used for subsequent operations on that device.
@@ -37,9 +36,7 @@ prom_devopen(char *dstr)
handle = -1;
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return handle;
@@ -63,9 +60,7 @@ prom_devclose(int dhandle)
default:
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return 0;
}
@@ -90,9 +85,7 @@ prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo)
default:
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return;
diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init.c
index 6f691464a..2c70dd95a 100644
--- a/arch/sparc/prom/init.c
+++ b/arch/sparc/prom/init.c
@@ -1,8 +1,9 @@
-/* $Id: init.c,v 1.11 1997/03/18 17:58:24 jj Exp $
+/* $Id: init.c,v 1.12 1998/01/30 10:59:02 jj Exp $
* init.c: Initialize internal variables used by the PROM
* library functions.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
@@ -11,10 +12,12 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
+#include <asm/sun4prom.h>
struct linux_romvec *romvec;
enum prom_major_version prom_vers;
unsigned int prom_rev, prom_prev;
+linux_sun4_romvec *sun4_romvec;
/* The root node of the prom device tree. */
int prom_root_node;
@@ -34,11 +37,14 @@ extern void prom_ranges_init(void);
__initfunc(void prom_init(struct linux_romvec *rp))
{
+#ifdef CONFIG_SUN4
+ extern struct linux_romvec *sun4_prom_init(void);
+ rp = sun4_prom_init();
+#endif
#if CONFIG_AP1000
extern struct linux_romvec *ap_prom_init(void);
rp = ap_prom_init();
#endif
-
romvec = rp;
switch(romvec->pv_romvers) {
@@ -51,6 +57,9 @@ __initfunc(void prom_init(struct linux_romvec *rp))
case 3:
prom_vers = PROM_V3;
break;
+ case 40:
+ prom_vers = PROM_SUN4;
+ break;
case 42: /* why not :-) */
prom_vers = PROM_AP1000;
break;
@@ -83,8 +92,11 @@ __initfunc(void prom_init(struct linux_romvec *rp))
prom_ranges_init();
+#ifndef CONFIG_SUN4
+ /* SUN4 prints this in sun4_prom_init */
printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
romvec->pv_romvers, prom_rev);
+#endif
/* Initialization successful. */
return;
diff --git a/arch/sparc/prom/memory.c b/arch/sparc/prom/memory.c
index b53bd17ea..af5019eb8 100644
--- a/arch/sparc/prom/memory.c
+++ b/arch/sparc/prom/memory.c
@@ -1,8 +1,9 @@
-/* $Id: memory.c,v 1.12 1997/05/27 06:45:57 davem Exp $
+/* $Id: memory.c,v 1.13 1998/01/30 10:59:03 jj Exp $
* memory.c: Prom routine for acquiring various bits of information
* about RAM on the machine, both virtual and physical.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
*/
#include <linux/config.h>
@@ -10,6 +11,7 @@
#include <linux/init.h>
#include <asm/openprom.h>
+#include <asm/sun4prom.h>
#include <asm/oplib.h>
/* This routine, for consistency, returns the ram parameters in the
@@ -177,6 +179,21 @@ __initfunc(void prom_meminit(void))
prom_sortmemlist(prom_phys_avail);
break;
+ case PROM_SUN4:
+#ifdef CONFIG_SUN4
+ /* how simple :) */
+ prom_phys_total[0].start_adr = 0x0;
+ prom_phys_total[0].num_bytes = *(sun4_romvec->memorysize);
+ prom_phys_total[0].theres_more = 0x0;
+ prom_prom_taken[0].start_adr = 0x0;
+ prom_prom_taken[0].num_bytes = 0x0;
+ prom_prom_taken[0].theres_more = 0x0;
+ prom_phys_avail[0].start_adr = 0x0;
+ prom_phys_avail[0].num_bytes = *(sun4_romvec->memoryavail);
+ prom_phys_avail[0].theres_more = 0x0;
+#endif
+ break;
+
case PROM_AP1000:
#if CONFIG_AP1000
/* really simple memory map */
@@ -189,9 +206,6 @@ __initfunc(void prom_meminit(void))
prom_phys_avail[0].start_adr = 0x00000000;
prom_phys_avail[0].num_bytes = prom_phys_total[0].num_bytes;
prom_phys_avail[0].theres_more = 0x0;
- prom_sortmemlist(prom_phys_total);
- prom_sortmemlist(prom_prom_taken);
- prom_sortmemlist(prom_phys_avail);
#endif
default:
break;
diff --git a/arch/sparc/prom/misc.c b/arch/sparc/prom/misc.c
index fede033dd..d2ec600e1 100644
--- a/arch/sparc/prom/misc.c
+++ b/arch/sparc/prom/misc.c
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.15 1997/05/14 20:45:00 davem Exp $
+/* $Id: misc.c,v 1.16 1998/03/09 14:04:25 jj Exp $
* misc.c: Miscellaneous prom functions that don't belong
* anywhere else.
*
@@ -13,8 +13,7 @@
#include <asm/oplib.h>
#include <asm/auxio.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
/* Reset and reboot the machine with the command 'bcommand'. */
void
@@ -24,9 +23,7 @@ prom_reboot(char *bcommand)
save_flags(flags); cli();
(*(romvec->pv_reboot))(bcommand);
/* Never get here. */
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
}
@@ -42,9 +39,7 @@ prom_feval(char *fstring)
(*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
else
(*(romvec->pv_fortheval.v2_eval))(fstring);
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
}
@@ -74,9 +69,7 @@ prom_cmdline(void)
install_obp_ticker();
save_flags(flags); cli();
(*(romvec->pv_abort))();
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
install_linux_ticker();
#ifdef CONFIG_SUN_AUXIO
@@ -99,9 +92,7 @@ again:
save_flags(flags); cli();
(*(romvec->pv_halt))();
/* Never get here. */
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
goto again; /* PROM is out to get me -DaveM */
}
diff --git a/arch/sparc/prom/mp.c b/arch/sparc/prom/mp.c
index 8f07f9d40..2346e3564 100644
--- a/arch/sparc/prom/mp.c
+++ b/arch/sparc/prom/mp.c
@@ -1,4 +1,4 @@
-/* $Id: mp.c,v 1.9 1997/05/14 20:45:01 davem Exp $
+/* $Id: mp.c,v 1.10 1998/03/09 14:04:26 jj Exp $
* mp.c: OpenBoot Prom Multiprocessor support routines. Don't call
* these on a UP or else you will halt and catch fire. ;)
*
@@ -12,8 +12,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
/* Start cpu with prom-tree node 'cpunode' using context described
* by 'ctable_reg' in context 'ctx' at program counter 'pc'.
@@ -38,9 +37,7 @@ prom_startcpu(int cpunode, struct linux_prom_registers *ctable_reg, int ctx, cha
ret = (*(romvec->v3_cpustart))(cpunode, (int) ctable_reg, ctx, pc);
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return ret;
@@ -67,9 +64,7 @@ prom_stopcpu(int cpunode)
ret = (*(romvec->v3_cpustop))(cpunode);
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return ret;
@@ -96,9 +91,7 @@ prom_idlecpu(int cpunode)
ret = (*(romvec->v3_cpuidle))(cpunode);
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return ret;
@@ -125,9 +118,7 @@ prom_restartcpu(int cpunode)
ret = (*(romvec->v3_cpuresume))(cpunode);
break;
};
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return ret;
diff --git a/arch/sparc/prom/ranges.c b/arch/sparc/prom/ranges.c
index 7f7b1da54..b4fd3989e 100644
--- a/arch/sparc/prom/ranges.c
+++ b/arch/sparc/prom/ranges.c
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.10 1997/12/19 12:37:18 jj Exp $
+/* $Id: ranges.c,v 1.11 1998/01/30 10:59:05 jj Exp $
* ranges.c: Handle ranges in newer proms for obio/sbus.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -68,7 +68,7 @@ prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs)
void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs,
int nregs, struct linux_sbus_device *sdev)
{
- if(sbus->num_sbus_ranges) {
+ if(sbus && sbus->num_sbus_ranges) {
if(sdev && (sdev->ranges_applied == 0)) {
sdev->ranges_applied = 1;
prom_adjust_regs(regs, nregs, sbus->sbus_ranges,
diff --git a/arch/sparc/prom/segment.c b/arch/sparc/prom/segment.c
index 96b543727..62b3f8542 100644
--- a/arch/sparc/prom/segment.c
+++ b/arch/sparc/prom/segment.c
@@ -1,4 +1,4 @@
-/* $Id: segment.c,v 1.5 1997/05/14 20:45:02 davem Exp $
+/* $Id: segment.c,v 1.6 1998/03/09 14:04:27 jj Exp $
* segment.c: Prom routine to map segments in other contexts before
* a standalone is completely mapped. This is for sun4 and
* sun4c architectures only.
@@ -12,8 +12,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
+extern void restore_current(void);
/* Set physical segment 'segment' at virtual address 'vaddr' in
* context 'ctx'.
@@ -24,9 +23,7 @@ prom_putsegment(int ctx, unsigned long vaddr, int segment)
unsigned long flags;
save_flags(flags); cli();
(*(romvec->pv_setctxt))(ctx, (char *) vaddr, segment);
- __asm__ __volatile__("ld [%0], %%g6\n\t" : :
- "r" (&current_set[hard_smp_processor_id()]) :
- "memory");
+ restore_current();
restore_flags(flags);
return;
}
diff --git a/arch/sparc/prom/sun4prom.c b/arch/sparc/prom/sun4prom.c
new file mode 100644
index 000000000..ce15ebb43
--- /dev/null
+++ b/arch/sparc/prom/sun4prom.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1996 The Australian National University.
+ * Copyright (C) 1996 Fujitsu Laboratories Limited
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * This software may be distributed under the terms of the Gnu
+ * Public License version 2 or later
+ *
+ * fake a really simple Sun prom for the SUN4
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/sun4prom.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <linux/init.h>
+
+static struct linux_romvec sun4romvec;
+static struct idprom sun4_idprom;
+
+struct property {
+ char *name;
+ char *value;
+ int length;
+};
+
+struct node {
+ int level;
+ struct property *properties;
+};
+
+struct property null_properties = { NULL, NULL, -1 };
+
+struct property root_properties[] = {
+ {"device_type", "cpu", 4},
+ {"idprom", (char *)&sun4_idprom, sizeof(struct idprom)},
+ {NULL, NULL, -1}
+};
+
+struct node nodes[] = {
+ { 0, &null_properties },
+ { 0, root_properties },
+ { -1,&null_properties }
+};
+
+
+static int no_nextnode(int node)
+{
+ if (nodes[node].level == nodes[node+1].level)
+ return node+1;
+ return -1;
+}
+
+static int no_child(int node)
+{
+ if (nodes[node].level == nodes[node+1].level-1)
+ return node+1;
+ return -1;
+}
+
+static struct property *find_property(int node,char *name)
+{
+ struct property *prop = &nodes[node].properties[0];
+ while (prop && prop->name) {
+ if (strcmp(prop->name,name) == 0) return prop;
+ prop++;
+ }
+ return NULL;
+}
+
+static int no_proplen(int node,char *name)
+{
+ struct property *prop = find_property(node,name);
+ if (prop) return prop->length;
+ return -1;
+}
+
+static int no_getprop(int node,char *name,char *value)
+{
+ struct property *prop = find_property(node,name);
+ if (prop) {
+ memcpy(value,prop->value,prop->length);
+ return 1;
+ }
+ return -1;
+}
+
+static int no_setprop(int node,char *name,char *value,int len)
+{
+ return -1;
+}
+
+static char *no_nextprop(int node,char *name)
+{
+ struct property *prop = find_property(node,name);
+ if (prop) return prop[1].name;
+ return NULL;
+}
+
+static struct linux_nodeops sun4_nodeops = {
+ no_nextnode,
+ no_child,
+ no_proplen,
+ no_getprop,
+ no_setprop,
+ no_nextprop
+};
+
+static int synch_hook;
+
+__initfunc(struct linux_romvec *sun4_prom_init(void))
+{
+ int i;
+ unsigned char x;
+ char *p;
+
+ p = (char *)&sun4_idprom;
+ for (i = 0; i < sizeof(sun4_idprom); i++) {
+ __asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) :
+ "r" (AC_IDPROM + i), "i" (ASI_CONTROL));
+ *p++ = x;
+ }
+
+ memset(&sun4romvec,0,sizeof(sun4romvec));
+
+ sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR;
+
+ sun4romvec.pv_romvers = 40;
+ sun4romvec.pv_nodeops = &sun4_nodeops;
+ sun4romvec.pv_reboot = sun4_romvec->reboot;
+ sun4romvec.pv_abort = sun4_romvec->abortentry;
+ sun4romvec.pv_halt = sun4_romvec->exittomon;
+ sun4romvec.pv_synchook = (void (**)(void))&synch_hook;
+ sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap;
+ sun4romvec.pv_v0bootargs = sun4_romvec->bootParam;
+ sun4romvec.pv_nbgetchar = sun4_romvec->mayget;
+ sun4romvec.pv_nbputchar = sun4_romvec->mayput;
+ sun4romvec.pv_stdin = sun4_romvec->insource;
+ sun4romvec.pv_stdout = sun4_romvec->outsink;
+
+ /*
+ * We turn on the LEDs to let folks without monitors or
+ * terminals know we booted. Nothing too fancy now. They
+ * are all on, except for LED 5, which blinks. When we
+ * have more time, we can teach the penguin to say "By your
+ * command" or "Activating turbo boost, Michael". :-)
+ */
+ sun4_romvec->setLEDs(0x0);
+
+ printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n",
+ sun4_romvec->monid,
+ sun4_romvec->romvecversion);
+
+ return &sun4romvec;
+}
diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c
index 616180e81..1256aacec 100644
--- a/arch/sparc/prom/tree.c
+++ b/arch/sparc/prom/tree.c
@@ -1,4 +1,4 @@
-/* $Id: tree.c,v 1.22 1997/09/25 02:19:22 davem Exp $
+/* $Id: tree.c,v 1.24 1998/03/09 14:04:29 jj Exp $
* tree.c: Basic device tree traversal/scanning for the Linux
* prom library.
*
@@ -15,13 +15,7 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-/* XXX Let's get rid of this thing if we can... */
-extern struct task_struct *current_set[NR_CPUS];
-
-/* Macro to restore "current" to the g6 register. */
-#define restore_current() __asm__ __volatile__("ld [%0], %%g6\n\t" : : \
- "r" (&current_set[hard_smp_processor_id()]) : \
- "memory")
+extern void restore_current(void);
static char promlib_buf[128];
@@ -95,12 +89,11 @@ int prom_getproplen(int node, char *prop)
int ret;
unsigned long flags;
- save_flags(flags); cli();
-
if((!node) || (!prop))
- ret = -1;
- else
- ret = prom_nodeops->no_proplen(node, prop);
+ return -1;
+
+ save_flags(flags); cli();
+ ret = prom_nodeops->no_proplen(node, prop);
restore_current();
restore_flags(flags);
return ret;
@@ -115,15 +108,12 @@ int prom_getproperty(int node, char *prop, char *buffer, int bufsize)
int plen, ret;
unsigned long flags;
- save_flags(flags); cli();
-
plen = prom_getproplen(node, prop);
if((plen > bufsize) || (plen == 0) || (plen == -1))
- ret = -1;
- else {
- /* Ok, things seem all right. */
- ret = prom_nodeops->no_getprop(node, prop, buffer);
- }
+ return -1;
+ /* Ok, things seem all right. */
+ save_flags(flags); cli();
+ ret = prom_nodeops->no_getprop(node, prop, buffer);
restore_current();
restore_flags(flags);
return ret;
diff --git a/arch/sparc/vmlinux.lds b/arch/sparc/vmlinux.lds
index 8141f5755..cbfc9fb3c 100644
--- a/arch/sparc/vmlinux.lds
+++ b/arch/sparc/vmlinux.lds
@@ -32,6 +32,7 @@ SECTIONS
. = ALIGN(4096);
__init_begin = .;
.text.init : { *(.text.init) }
+ __init_text_end = .;
.data.init : { *(.data.init) }
. = ALIGN(4096);
__init_end = .;
diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile
index 9e8b0b862..e004359c3 100644
--- a/arch/sparc64/Makefile
+++ b/arch/sparc64/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.24 1997/10/02 16:31:16 jj Exp $
+# $Id: Makefile,v 1.25 1998/04/06 16:10:31 jj Exp $
# sparc64/Makefile
#
# Makefile for the architecture dependent flags and dependencies on the
@@ -38,9 +38,7 @@ ifneq ($(CONFIG_SOLARIS_EMUL),n)
SUBDIRS += arch/sparc64/solaris
endif
-ifneq ($(CONFIG_MATHEMU),n)
- SUBDIRS += arch/sparc64/math-emu
-endif
+SUBDIRS += arch/sparc64/math-emu
CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES)
@@ -48,9 +46,7 @@ ifeq ($(CONFIG_SOLARIS_EMUL),y)
CORE_FILES += arch/sparc64/solaris/solaris.o
endif
-ifeq ($(CONFIG_MATHEMU),y)
- CORE_FILES += arch/sparc64/math-emu/math-emu.o
-endif
+CORE_FILES += arch/sparc64/math-emu/math-emu.o
LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \
$(TOPDIR)/arch/sparc64/lib/lib.a
diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in
index 4461cdea0..8cd86f9db 100644
--- a/arch/sparc64/config.in
+++ b/arch/sparc64/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.36 1998/01/10 19:04:30 ecd Exp $
+# $Id: config.in,v 1.44 1998/04/06 16:10:35 jj Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -67,9 +67,6 @@ fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL
fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'Kernel Quad FPU (long double) and subnormal float/double emulation' CONFIG_MATHEMU
-fi
endmenu
if [ "$CONFIG_PCI" = "y" ]; then
@@ -84,6 +81,7 @@ if [ "$CONFIG_PCI" = "y" ]; then
bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
fi
fi
+ tristate 'SUNW,envctrl support' CONFIG_ENVCTRL
fi
mainmenu_option next_comment
@@ -109,14 +107,16 @@ tristate 'Network block device support' CONFIG_BLK_DEV_NBD
if [ "$CONFIG_PCI" = "y" ]; then
tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
- if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
- define_bool CONFIG_IDE_CHIPSETS y
- define_bool CONFIG_BLK_DEV_NS87415_AX y
+ define_bool CONFIG_BLK_DEV_IDEPCI y
+ define_bool CONFIG_BLK_DEV_IDEDMA y
+ define_bool CONFIG_BLK_DEV_NS87415 y
+ define_bool CONFIG_BLK_DEV_CMD646 y
fi
fi
@@ -218,17 +218,6 @@ if [ "$CONFIG_NET" = "y" ]; then
endmenu
fi
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
- define_bool CONFIG_CDROM y
-else
- if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
- define_bool CONFIG_CDROM m
- else
- define_bool CONFIG_CDROM n
- fi
-fi
-
source fs/Config.in
source fs/nls/Config.in
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
index f3c2e2ca4..0a76f100f 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc64/defconfig
@@ -25,12 +25,12 @@ CONFIG_SBUSCHAR=y
CONFIG_SUN_MOUSE=y
CONFIG_SERIAL=y
CONFIG_SUN_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
CONFIG_SUN_KEYBOARD=y
CONFIG_SUN_CONSOLE=y
CONFIG_SUN_AUXIO=y
CONFIG_SUN_IO=y
CONFIG_PCI=y
-CONFIG_PCI_OLD_PROC=y
#
# SBUS Frame Buffer support
@@ -52,10 +52,11 @@ SUN_FB_CREATOR=y
CONFIG_SUN_OPENPROMIO=m
CONFIG_SUN_MOSTEK_RTC=y
CONFIG_SAB82532=y
-# CONFIG_OBP_FLASH is not set
+CONFIG_OBP_FLASH=m
# CONFIG_SUN_BPP is not set
# CONFIG_SUN_VIDEOPIX is not set
CONFIG_SUN_OPENPROMFS=m
+CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
@@ -67,11 +68,12 @@ CONFIG_BINFMT_AOUT32=y
CONFIG_BINFMT_MISC=m
CONFIG_BINFMT_JAVA=m
CONFIG_SOLARIS_EMUL=m
-CONFIG_MATHEMU=m
CONFIG_PARPORT=y
CONFIG_PARPORT_AX=y
+# CONFIG_PARPORT_OTHER is not set
CONFIG_PRINTER=y
CONFIG_PRINTER_READBACK=y
+CONFIG_ENVCTRL=y
#
# Floppy, IDE, and other block devices
@@ -92,8 +94,10 @@ CONFIG_BLK_DEV_IDECD=y
CONFIG_BLK_DEV_IDETAPE=m
CONFIG_BLK_DEV_IDEFLOPPY=m
# CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_IDE_CHIPSETS=y
-CONFIG_BLK_DEV_NS87415_AX=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_NS87415=y
+CONFIG_BLK_DEV_CMD646=y
#
# Networking options
@@ -101,18 +105,18 @@ CONFIG_BLK_DEV_NS87415_AX=y
CONFIG_PACKET=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
-# CONFIG_NET_ALIAS is not set
+CONFIG_NET_ALIAS=y
+# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
+CONFIG_IP_ALIAS=y
# CONFIG_SYN_COOKIES is not set
#
@@ -123,30 +127,34 @@ CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
CONFIG_IPV6=m
# CONFIG_IPV6_EUI64 is not set
-# CONFIG_IPV6_NO_PB is not set
#
#
#
CONFIG_IPX=m
+
+#
+# IPX options
+#
# CONFIG_IPX_INTERN is not set
CONFIG_ATALK=m
-# CONFIG_AX25 is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
# CONFIG_LLC is not set
# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=y
CONFIG_NET_SCH_CSZ=y
-CONFIG_NET_SCH_HFQ=y
CONFIG_NET_SCH_RED=y
CONFIG_NET_SCH_SFQ=y
CONFIG_NET_SCH_TBF=y
CONFIG_NET_SCH_PFIFO=y
CONFIG_NET_SCH_PRIO=y
+# CONFIG_NET_PROFILE is not set
#
# SCSI support
@@ -187,6 +195,21 @@ CONFIG_SCSI_NCR53C8XX_SYNC=10
# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
#
+# Fibre Channel support
+#
+CONFIG_FC4=m
+
+#
+# FC4 drivers
+#
+CONFIG_FC4_SOC=m
+
+#
+# FC4 targets
+#
+CONFIG_SCSI_PLUTO=m
+
+#
# Network device support
#
CONFIG_NETDEVICES=y
@@ -205,7 +228,6 @@ CONFIG_HAPPYMEAL=y
CONFIG_SUNQE=m
CONFIG_MYRI_SBUS=m
CONFIG_DE4X5=y
-CONFIG_CDROM=y
#
# Filesystems
@@ -228,21 +250,31 @@ CONFIG_LOCKD=y
CONFIG_SMB_FS=m
CONFIG_SMB_WIN95=y
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_MOUNT_SUBDIR is not set
CONFIG_HPFS_FS=m
+# CONFIG_NTFS_FS is not set
CONFIG_SYSV_FS=m
CONFIG_AFFS_FS=m
+# CONFIG_HFS_FS is not set
CONFIG_ROMFS_FS=m
CONFIG_AUTOFS_FS=m
CONFIG_AMIGA_PARTITION=y
CONFIG_UFS_FS=m
CONFIG_BSD_DISKLABEL=y
CONFIG_SMD_DISKLABEL=y
+CONFIG_SOLARIS_X86_PARTITION=y
+# CONFIG_ADFS_FS is not set
# CONFIG_MAC_PARTITION is not set
+CONFIG_NLS=y
#
# Native Language Support
#
-CONFIG_NLS=y
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 7889c84a4..1c3f459bc 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.35 1997/09/20 21:48:58 davem Exp $
+# $Id: Makefile,v 1.36 1998/02/01 11:15:55 ecd Exp $
# Makefile for the linux kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
@@ -61,13 +61,24 @@ head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \
binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c
check_asm: dummy
+ @echo "/* Automatically generated. Do not edit. */" > asm_offsets.h
+ @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h
+ @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#ifndef __SMP__" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
@echo "#include <linux/sched.h>" > tmp.c
$(CC) -E tmp.c -o tmp.i
- @echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c
+ @echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/sched.h>" >> check_asm.c
+ @echo 'struct task_struct _task;' >> check_asm.c
+ @echo 'struct mm_struct _mm;' >> check_asm.c
+ @echo 'struct thread_struct _thread;' >> check_asm.c
+ @echo 'int main(void) {' >> check_asm.c
$(SH) ./check_asm.sh task tmp.i check_asm.c
$(SH) ./check_asm.sh mm tmp.i check_asm.c
$(SH) ./check_asm.sh thread tmp.i check_asm.c
- @echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c
+ @echo 'return 0; }' >> check_asm.c
@rm -f tmp.[ci]
#$(CC) -o check_asm check_asm.c
# <hack> Until we can do this natively, a hack has to take place
@@ -75,9 +86,46 @@ check_asm: dummy
$(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s
@rm -f check_asm.s
# </hack>
- ./check_asm > asm_offsets.h
- @if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi
+ ./check_asm >> asm_offsets.h
@rm -f check_asm check_asm.c
+ @echo "" >> asm_offsets.h
+ @echo "#else /* __SMP__ */" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#include <linux/sched.h>" > tmp.c
+ $(CC) -D__SMP__ -E tmp.c -o tmp.i
+ @echo "/* Automatically generated. Do not edit. */" > check_asm.c
+ @echo "#include <linux/sched.h>" >> check_asm.c
+ @echo 'struct task_struct _task;' >> check_asm.c
+ @echo 'struct mm_struct _mm;' >> check_asm.c
+ @echo 'struct thread_struct _thread;' >> check_asm.c
+ @echo 'int main(void) {' >> check_asm.c
+ $(SH) ./check_asm.sh task tmp.i check_asm.c
+ $(SH) ./check_asm.sh mm tmp.i check_asm.c
+ $(SH) ./check_asm.sh thread tmp.i check_asm.c
+ @echo 'return 0; }' >> check_asm.c
+ @rm -f tmp.[ci]
+ #$(CC) -D__SMP__ -o check_asm check_asm.c
+ # <hack> Until we can do this natively, a hack has to take place
+ $(CC) -D__SMP__ -mmedlow -ffixed-g4 -S -o check_asm.s check_asm.c
+ $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s
+ @rm -f check_asm.s
+ # </hack>
+ ./check_asm >> asm_offsets.h
+ @rm -f check_asm check_asm.c
+ @echo "" >> asm_offsets.h
+ @echo "#endif /* __SMP__ */" >> asm_offsets.h
+ @echo "" >> asm_offsets.h
+ @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h
+ @if test -r $(HPATH)/asm/asm_offsets.h; then \
+ if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then \
+ echo $(HPATH)/asm/asm_offsets.h is unchanged; \
+ rm -f asm_offsets.h; \
+ else \
+ mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+ fi; \
+ else \
+ mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; \
+ fi
include $(TOPDIR)/Rules.make
diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c
index 55ccbc203..b0dd675b0 100644
--- a/arch/sparc64/kernel/binfmt_aout32.c
+++ b/arch/sparc64/kernel/binfmt_aout32.c
@@ -20,6 +20,7 @@
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
+#include <linux/file.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/malloc.h>
@@ -257,7 +258,7 @@ static inline int do_load_aout32_binary(struct linux_binprm * bprm,
unsigned long p = bprm->p;
unsigned long fd_offset;
unsigned long rlim;
-int retval;
+ int retval;
ex = *((struct exec *) bprm->buf); /* exec-header */
if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
@@ -326,10 +327,10 @@ int retval;
printk(KERN_NOTICE "executable not page aligned\n");
fd = open_dentry(bprm->dentry, O_RDONLY);
-
if (fd < 0)
return fd;
- file = current->files->fd[fd];
+ file = fcheck(fd);
+
if (!file->f_op || !file->f_op->mmap) {
sys_close(fd);
do_mmap(NULL, 0, ex.a_text+ex.a_data,
@@ -397,6 +398,7 @@ load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
return retval;
}
+/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */
static inline int
do_load_aout32_library(int fd)
{
@@ -409,7 +411,7 @@ do_load_aout32_library(int fd)
unsigned int start_addr;
unsigned long error;
- file = current->files->fd[fd];
+ file = fcheck(fd);
if (!file || !file->f_op)
return -EACCES;
diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c
index 817a8ecd3..a54e89f2d 100644
--- a/arch/sparc64/kernel/central.c
+++ b/arch/sparc64/kernel/central.c
@@ -1,4 +1,4 @@
-/* $Id: central.c,v 1.4 1997/08/19 14:17:49 jj Exp $
+/* $Id: central.c,v 1.5 1998/02/12 15:57:59 jj Exp $
* central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -35,22 +35,18 @@ unsigned long central_probe(unsigned long memory_start)
printk("no central found.\n");
return memory_start;
}
- prom_printf("CENTRAL: found central PROM node.\n");
printk("found central PROM node.\n");
/* Ok we got one, grab some memory for software state. */
memory_start = long_align(memory_start);
central_bus = (struct linux_central *) (memory_start);
- prom_printf("CENTRAL: central_bus[%p] ", central_bus);
memory_start += sizeof(struct linux_central);
memory_start = long_align(memory_start);
fhc = (struct linux_fhc *)(memory_start);
memory_start += sizeof(struct linux_fhc);
memory_start = long_align(memory_start);
- prom_printf("fhc[%p] ", fhc);
-
/* First init central. */
central_bus->child = fhc;
central_bus->prom_node = cnode;
@@ -58,7 +54,6 @@ unsigned long central_probe(unsigned long memory_start)
prom_getstring(cnode, "name", namebuf, sizeof(namebuf));
strcpy(central_bus->prom_name, namebuf);
- prom_printf("init_central_ranges ");
prom_central_ranges_init(cnode, central_bus);
/* And then central's FHC. */
@@ -73,27 +68,15 @@ unsigned long central_probe(unsigned long memory_start)
prom_getstring(fnode, "name", namebuf, sizeof(namebuf));
strcpy(fhc->prom_name, namebuf);
- prom_printf("cnode[%x] fnode[%x] init_fhc_ranges\n", cnode, fnode);
prom_fhc_ranges_init(fnode, fhc);
- /* Finally, map in FHC register set. (From the prtconf dumps
- * I have seen on Ex000 boxes only the central ranges need to
- * be applied to the fhc internal register set) -DaveM
- */
- err = prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs));
- if(err == -1) {
+ /* Finally, map in FHC register set. */
+ if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) {
prom_printf("CENTRAL: fatal error, cannot get fhc regs.\n");
prom_halt();
}
prom_apply_central_ranges(central_bus, &fpregs[0], 6);
- prom_printf("CENTRAL: FHC_REGS[(%08x,%08x) (%08x,%08x) "
- "(%08x,%08x) (%08x,%08x) (%08x,%08x) (%08x,%08x)]\n",
- fpregs[0].which_io, fpregs[0].phys_addr,
- fpregs[1].which_io, fpregs[1].phys_addr,
- fpregs[2].which_io, fpregs[2].phys_addr,
- fpregs[3].which_io, fpregs[3].phys_addr,
- fpregs[4].which_io, fpregs[4].phys_addr,
- fpregs[5].which_io, fpregs[5].phys_addr);
+
fhc->fhc_regs.pregs = (struct fhc_internal_regs *)
__va((((unsigned long)fpregs[0].which_io)<<32) |
(((unsigned long)fpregs[0].phys_addr)));
@@ -112,14 +95,8 @@ unsigned long central_probe(unsigned long memory_start)
fhc->fhc_regs.tregs = (struct fhc_tod_regs *)
__va((((unsigned long)fpregs[5].which_io)<<32) |
(((unsigned long)fpregs[5].phys_addr)));
- prom_printf("CENTRAL: FHC_REGS[%p %p %p %p %p %p]\n",
- fhc->fhc_regs.pregs, fhc->fhc_regs.ireg,
- fhc->fhc_regs.ffregs, fhc->fhc_regs.sregs,
- fhc->fhc_regs.uregs, fhc->fhc_regs.tregs);
- prom_printf("CENTRAL: reading FHC_ID register... ");
err = fhc->fhc_regs.pregs->fhc_id;
- prom_printf("VALUE[%x]\n", err);
printk("FHC Version[%x] PartID[%x] Manufacturer[%x]\n",
((err & FHC_ID_VERS) >> 28),
((err & FHC_ID_PARTID) >> 12),
diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c
index d009f39d8..86efc4bb7 100644
--- a/arch/sparc64/kernel/cpu.c
+++ b/arch/sparc64/kernel/cpu.c
@@ -32,7 +32,8 @@ struct cpu_fp_info linux_sparc_fpu[] = {
{ 0x17, 0x10, 0, "UltraSparc I integrated FPU"},
{ 0x22, 0x10, 0, "UltraSparc II integrated FPU"},
{ 0x17, 0x11, 0, "UltraSparc II integrated FPU"},
- { 0x17, 0x12, 0, "UltraSparc III integrated FPU"},
+ { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"},
+ { 0x17, 0x13, 0, "UltraSparc III integrated FPU"},
};
#define NSPARCFPU (sizeof(linux_sparc_fpu)/sizeof(struct cpu_fp_info))
@@ -41,7 +42,8 @@ struct cpu_iu_info linux_sparc_chips[] = {
{ 0x17, 0x10, "TI UltraSparc I (SpitFire)"},
{ 0x22, 0x10, "TI UltraSparc II (BlackBird)"},
{ 0x17, 0x11, "TI UltraSparc II (BlackBird)"},
- { 0x17, 0x12, "TI UltraSparc III (Cheetah)"}, /* A guess... */
+ { 0x17, 0x12, "TI UltraSparc IIi"},
+ { 0x17, 0x13, "TI UltraSparc III (Cheetah)"}, /* A guess... */
};
#define NSPARCCHIPS (sizeof(linux_sparc_chips)/sizeof(struct cpu_iu_info))
diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c
index 24ca3ff10..8d3aca325 100644
--- a/arch/sparc64/kernel/devices.c
+++ b/arch/sparc64/kernel/devices.c
@@ -13,7 +13,8 @@
#include <asm/system.h>
#include <asm/smp.h>
-struct prom_cpuinfo linux_cpus[NR_CPUS];
+struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } };
+unsigned prom_cpu_nodes[NR_CPUS];
int linux_num_cpus = 0;
extern void cpu_probe(void);
@@ -64,6 +65,8 @@ device_scan(unsigned long mem_start))
prom_node_cpu = cpu_nds[0];
linux_num_cpus = cpu_ctr;
+
+ prom_cpu_nodes[0] = prom_node_cpu;
cpu_probe();
return central_probe(mem_start);
diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc64/kernel/dtlb_miss.S
index 4d71d967c..e5606cf33 100644
--- a/arch/sparc64/kernel/dtlb_miss.S
+++ b/arch/sparc64/kernel/dtlb_miss.S
@@ -1,9 +1,9 @@
-/* $Id: dtlb_miss.S,v 1.14 1997/10/14 01:48:28 davem Exp $
+/* $Id: dtlb_miss.S,v 1.15 1998/01/14 17:14:44 jj Exp $
* dtlb_miss.S: Data TLB miss code, this is included directly
* into the trap table.
*
* Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/* The basic algorithm is:
@@ -36,22 +36,22 @@
#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
#define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
-#define KERN_LOWBITS_IO ((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS)
+#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W)
/* ICACHE line 1 */
/*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET
- /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset
- /*0x08*/ srlx %g1, 48, %g5 ! Shift down CONTEXT bits
- /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset
- /*0x10*/ sllx %g1, 2, %g4 ! Position PMD offset
- /*0x14*/ brz,pn %g5, 3f ! Context 0 == kernel
- /*0x18*/ and %g4, %g2, %g4 ! Mask PMD offset
+ /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset
+ /*0x08*/ andcc %g1, %g2, %g0 ! Test CONTEXT bits
+ /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset
+ /*0x18*/ and %g1, 0xffe, %g4 ! Mask PMD offset
+ /*0x14*/ be,pn %xcc, 3f ! Context 0 == kernel
+ /*0x10*/ add %g4, %g4, %g4 ! Position PMD offset
/*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset
/* ICACHE line 2 */
- /*0x20*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD
+ /*0x20*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD
/*0x24*/ srlx %g1, 1, %g1 ! PTE offset
-2:/*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD
+2:/*0x28*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD
/*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE
/*0x30*/ brgez,pn %g5, sparc64_dtlb_refbit_catch ! Valid set?
/*0x34*/ nop ! delay
@@ -61,22 +61,22 @@
3: /* ICACHE line 3 */
/*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET
/*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables
- /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET
- /*0x4c*/ andcc %g3, 0x400, %g0 ! Slick trick...
- /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up
- /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO
- /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page?
- /*0x5c*/ xor %g1, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO...
+ /*0x48*/ or %g2, (KERN_LOWBITS), %g1 ! Construct PTE ^ PAGE_OFFSET
+ /*0x4c*/ andcc %g3, 0x100, %g0 ! Slick trick...
+ /*0x50*/ bne,a,pn %icc, 5f ! Is it an IO page?
+ /*0x54*/ or %g2, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO...
+5:/*0x58*/ xor %g1, %g5, %g1 ! Slick trick II...
+ /*0x5c*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load
/* ICACHE line 4 */
-5:/*0x60*/ xor %g1, %g5, %g1 ! Slick trick II...
- /*0x64*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load
- /*0x68*/ retry ! Trap return
-4:/*0x6c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset
- /*0x70*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD
- /*0x74*/ ba,pt %xcc, 2b ! Go back up top
- /*0x78*/ srlx %g1, 1, %g1
- /*0x7c*/ nop
+ /*0x60*/ retry ! Trap return
+ /*0x64*/ nop
+ /*0x68*/ nop
+ /*0x6c*/ nop
+4:/*0x70*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset
+ /*0x74*/ lduwa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD
+ /*0x78*/ ba,pt %xcc, 2b ! Go back up top
+ /*0x7c*/ srlx %g1, 1, %g1
#undef KERN_HIGHBITS
#undef KERN_LOWBITS
diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc64/kernel/dtlb_prot.S
index 55e86c887..86cbfdc52 100644
--- a/arch/sparc64/kernel/dtlb_prot.S
+++ b/arch/sparc64/kernel/dtlb_prot.S
@@ -1,9 +1,9 @@
-/* $Id: dtlb_prot.S,v 1.14 1997/08/03 09:07:00 davem Exp $
+/* $Id: dtlb_prot.S,v 1.15 1998/01/14 17:14:46 jj Exp $
* dtlb_prot.S: Data TLB protection code, this is included directly
* into the trap table.
*
* Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/* We know kernel never takes protection trap,
@@ -15,12 +15,12 @@
/* ICACHE line 1 */
/*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET
- /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset
- /*0x08*/ sllx %g1, 2, %g4 ! Position PMD offset
- /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset
- /*0x10*/ and %g4, %g2, %g4 ! Mask PMD offset
- /*0x14*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD
- /*0x18*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD
+ /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset
+ /*0x08*/ and %g1, 0xffe, %g4 ! Mask PMD offset
+ /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset
+ /*0x10*/ add %g4, %g4, %g4 ! Position PMD offset
+ /*0x14*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load PGD
+ /*0x18*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g4 ! Load PMD
/*0x1c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset
/* ICACHE line 2 */
@@ -34,10 +34,10 @@
/*0x3c*/ ldxa [%g5] ASI_DMMU, %g4 ! From MMU
/* ICACHE line 3 */
- /*0x40*/ add %g2, 7, %g5 ! Compute mask
- /*0x44*/ andn %g4, %g5, %g4 ! Mask page
- /*0x48*/ mov TLB_SFSR, %g5 ! read SFSR
- /*0x4c*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for
+ /*0x40*/ mov TLB_SFSR, %g5 ! read SFSR
+ /*0x44*/ srlx %g4, 13, %g4 ! Prepare...
+ /*0x48*/ ldxa [%g5] ASI_DMMU, %g5 ! from DMMU for
+ /*0x4c*/ sllx %g4, 13, %g4 ! ...and mask page
/*0x50*/ and %g5, 0x10, %g5 ! context bit
/*0x54*/ or %g4, %g5, %g4 ! for prot trap
1:/*0x58*/ stxa %g0, [%g4] ASI_DMMU_DEMAP ! TLB flush page
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
index 02faf4e3c..954cfd4bc 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc64/kernel/ebus.c
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.17 1998/01/10 18:26:13 ecd Exp $
+/* $Id: ebus.c,v 1.23 1998/03/29 16:27:24 ecd Exp $
* ebus.c: PCI to EBus bridge device.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -29,14 +29,12 @@
struct linux_ebus *ebus_chain = 0;
extern void prom_ebus_ranges_init(struct linux_ebus *);
+extern void prom_ebus_intmap_init(struct linux_ebus *);
extern unsigned long pci_console_init(unsigned long memory_start);
#ifdef CONFIG_SUN_OPENPROMIO
extern int openprom_init(void);
#endif
-#ifdef CONFIG_SUN_MOSTEK_RTC
-extern int rtc_init(void);
-#endif
#ifdef CONFIG_SPARCAUDIO
extern int sparcaudio_init(void);
#endif
@@ -46,6 +44,9 @@ extern void auxio_probe(void);
#ifdef CONFIG_OBP_FLASH
extern int flash_init(void);
#endif
+#ifdef CONFIG_ENVCTRL
+extern int envctrl_init(void);
+#endif
extern unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
unsigned int full_ino);
@@ -62,7 +63,35 @@ ebus_alloc(unsigned long *memory_start, size_t size)
return mem;
}
-__initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev))
+__initfunc(void 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;
+
+ 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;
+ }
+ }
+
+ prom_printf("ebus: IRQ [%08x.%08x.%08x] not found in interrupt-map\n",
+ reg->which_io, reg->phys_addr, *interrupt);
+ prom_halt();
+}
+
+__initfunc(void fill_ebus_child(int node, struct linux_prom_registers *preg,
+ struct linux_ebus_child *dev))
{
int regs[PROMREG_MAX];
int irqs[PROMREG_MAX];
@@ -90,8 +119,10 @@ __initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev))
dev->num_irqs = 0;
} else {
dev->num_irqs = len / sizeof(irqs[0]);
- for (i = 0; i < dev->num_irqs; i++)
+ for (i = 0; i < dev->num_irqs; i++) {
+ ebus_intmap_match(dev->bus, preg, &irqs[i]);
dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]);
+ }
}
#ifdef DEBUG_FILL_EBUS_DEV
@@ -108,7 +139,8 @@ __initfunc(void fill_ebus_child(int node, struct linux_ebus_child *dev))
#endif
}
-__initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *dev,
+__initfunc(unsigned long fill_ebus_device(int node,
+ struct linux_ebus_device *dev,
unsigned long memory_start))
{
struct linux_prom_registers regs[PROMREG_MAX];
@@ -142,8 +174,10 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de
dev->num_irqs = 0;
} else {
dev->num_irqs = len / sizeof(irqs[0]);
- for (i = 0; i < dev->num_irqs; i++)
+ for (i = 0; i < dev->num_irqs; i++) {
+ ebus_intmap_match(dev->bus, &regs[0], &irqs[i]);
dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]);
+ }
}
#ifdef DEBUG_FILL_EBUS_DEV
@@ -166,7 +200,7 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de
child->next = 0;
child->parent = dev;
child->bus = dev->bus;
- fill_ebus_child(node, child);
+ fill_ebus_child(node, &regs[0], child);
while ((node = prom_getsibling(node))) {
child->next = (struct linux_ebus_child *)
@@ -176,13 +210,16 @@ __initfunc(unsigned long fill_ebus_device(int node, struct linux_ebus_device *de
child->next = 0;
child->parent = dev;
child->bus = dev->bus;
- fill_ebus_child(node, child);
+ fill_ebus_child(node, &regs[0], child);
}
}
return memory_start;
}
+extern void sun4u_start_timers(void);
+extern void clock_probe(void);
+
__initfunc(unsigned long ebus_init(unsigned long memory_start,
unsigned long memory_end))
{
@@ -199,14 +236,10 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start,
int reg, rng, nreg;
int num_ebus = 0;
- if (!pcibios_present())
+ if (!pci_present())
return memory_start;
- for (pdev = pci_devices; pdev; pdev = pdev->next) {
- if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
- (pdev->device == PCI_DEVICE_ID_SUN_EBUS))
- break;
- }
+ pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, 0);
if (!pdev) {
printk("ebus: No EBus's found.\n");
#ifdef PROM_DEBUG
@@ -236,11 +269,9 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start,
ebus->parent = pbm = cookie->pbm;
/* Enable BUS Master. */
- pcibios_read_config_word(pdev->bus->number, pdev->devfn,
- PCI_COMMAND, &pci_command);
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
pci_command |= PCI_COMMAND_MASTER;
- pcibios_write_config_word(pdev->bus->number, pdev->devfn,
- PCI_COMMAND, pci_command);
+ pci_write_config_word(pdev, PCI_COMMAND, pci_command);
len = prom_getproperty(ebusnd, "reg", (void *)regs,
sizeof(regs));
@@ -285,6 +316,7 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start,
#endif
prom_ebus_ranges_init(ebus);
+ prom_ebus_intmap_init(ebus);
nd = prom_getchild(ebusnd);
if (!nd)
@@ -312,11 +344,8 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start,
}
next_ebus:
- for (pdev = pdev->next; pdev; pdev = pdev->next) {
- if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
- (pdev->device == PCI_DEVICE_ID_SUN_EBUS))
- break;
- }
+ pdev = pci_find_device(PCI_VENDOR_ID_SUN,
+ PCI_DEVICE_ID_SUN_EBUS, pdev);
if (!pdev)
break;
@@ -335,9 +364,6 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start,
#ifdef CONFIG_SUN_OPENPROMIO
openprom_init();
#endif
-#ifdef CONFIG_SUN_MOSTEK_RTC
- rtc_init();
-#endif
#ifdef CONFIG_SPARCAUDIO
sparcaudio_init();
#endif
@@ -345,20 +371,15 @@ __initfunc(unsigned long ebus_init(unsigned long memory_start,
bpp_init();
#endif
#ifdef CONFIG_SUN_AUXIO
- if (sparc_cpu_model == sun4u)
- auxio_probe();
+ auxio_probe();
+#endif
+#ifdef CONFIG_ENVCTRL
+ envctrl_init();
#endif
#ifdef CONFIG_OBP_FLASH
flash_init();
#endif
-#ifdef __sparc_v9__
- if (sparc_cpu_model == sun4u) {
- extern void sun4u_start_timers(void);
- extern void clock_probe(void);
-
- sun4u_start_timers();
- clock_probe();
- }
-#endif
+ sun4u_start_timers();
+ clock_probe();
return memory_start;
}
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
index 43f950b25..c0531f30a 100644
--- a/arch/sparc64/kernel/head.S
+++ b/arch/sparc64/kernel/head.S
@@ -1,9 +1,9 @@
-/* $Id: head.S,v 1.46 1997/08/08 08:33:30 jj Exp $
+/* $Id: head.S,v 1.49 1998/03/03 12:31:17 jj 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 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
*/
@@ -26,7 +26,7 @@
/* 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 bootup_kernel_stack, which is from
+ * 0x0000.0000.0040.6000 and empty_bad_page, which is from
* 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000.
*/
@@ -326,6 +326,8 @@ sun4u_init:
nop
/* Not reached... */
+/* IMPORTANT NOTE: Whenever making changes here, check
+ * trampoline.S as well. -jj */
.globl setup_tba
setup_tba:
save %sp, -160, %sp
@@ -346,9 +348,11 @@ setup_tba:
/* Set up MMU globals */
wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate
- /* PGD/PMD offset mask, used by TLB miss handlers. */
- sethi %hi(0x1ff8), %g2
- or %g2, %lo(0x1ff8), %g2
+ /* Set KERN_HIGHBITS used by dTLB miss handler. */
+#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
+ sethi %uhi(KERN_HIGHBITS), %g2
+ sllx %g2, 32, %g2
+#undef KERN_HIGHBITS
/* Kernel PGDIR used by TLB miss handlers. */
mov %i0, %g6
@@ -391,7 +395,8 @@ sparc64_boot_end:
.skip 0x2000 + _start - sparc64_boot_end
bootup_user_stack_end:
-bootup_kernel_stack:
+ .globl empty_bad_page
+empty_bad_page:
.skip 0x2000
! 0x0000000000408000
diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c
index 1829daeea..86b6c3dd6 100644
--- a/arch/sparc64/kernel/init_task.c
+++ b/arch/sparc64/kernel/init_task.c
@@ -6,7 +6,7 @@
static struct vm_area_struct init_mmap = INIT_MMAP;
static struct fs_struct init_fs = INIT_FS;
-static struct files * init_fd_array[NR_OPEN] = { NULL, };
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS;
struct mm_struct init_mm = INIT_MM;
diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c
index 64465663d..17e904f25 100644
--- a/arch/sparc64/kernel/ioctl32.c
+++ b/arch/sparc64/kernel/ioctl32.c
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.26 1997/12/15 15:11:02 jj Exp $
+/* $Id: ioctl32.c,v 1.35 1998/04/10 02:01:46 davem Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -23,9 +23,11 @@
#include <linux/netlink.h>
#include <linux/vt.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/fd.h>
#include <linux/if_ppp.h>
#include <linux/mtio.h>
+#include <linux/cdrom.h>
#include <scsi/scsi.h>
/* Ugly hack. */
@@ -64,6 +66,30 @@ static int w_long(unsigned int fd, unsigned int cmd, u32 arg)
return err;
}
+struct timeval32 {
+ int tv_sec;
+ int tv_usec;
+};
+
+static int do_siocgstamp(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ struct timeval32 *up = (struct timeval32 *)A(arg);
+ struct timeval ktv;
+ mm_segment_t old_fs = get_fs();
+ int err;
+
+ set_fs(KERNEL_DS);
+ err = sys_ioctl(fd, cmd, (unsigned long)&ktv);
+ set_fs(old_fs);
+ if(!err) {
+ if(!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __put_user(ktv.tv_sec, &up->tv_sec) ||
+ __put_user(ktv.tv_usec, &up->tv_usec))
+ err = -EFAULT;
+ }
+ return err;
+}
+
struct ifmap32 {
u32 mem_start;
u32 mem_end;
@@ -948,6 +974,90 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
return 0;
}
+struct cdrom_read32 {
+ int cdread_lba;
+ __kernel_caddr_t32 cdread_bufaddr;
+ int cdread_buflen;
+};
+
+struct cdrom_read_audio32 {
+ union cdrom_addr addr;
+ u_char addr_format;
+ int nframes;
+ __kernel_caddr_t32 buf;
+};
+
+static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ mm_segment_t old_fs = get_fs();
+ struct cdrom_read cdread;
+ struct cdrom_read_audio cdreadaudio;
+ __kernel_caddr_t32 addr;
+ char *data = 0;
+ void *karg;
+ int err = 0;
+
+ switch(cmd) {
+ case CDROMREADMODE2:
+ case CDROMREADMODE1:
+ case CDROMREADRAW:
+ case CDROMREADCOOKED:
+ karg = &cdread;
+ if (__get_user(cdread.cdread_lba, &((struct cdrom_read32 *)A(arg))->cdread_lba) ||
+ __get_user(addr, &((struct cdrom_read32 *)A(arg))->cdread_bufaddr) ||
+ __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)A(arg))->cdread_buflen))
+ return -EFAULT;
+ data = kmalloc(cdread.cdread_buflen, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ cdread.cdread_bufaddr = data;
+ break;
+ case CDROMREADAUDIO:
+ karg = &cdreadaudio;
+ if (copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)A(arg))->addr, sizeof(cdreadaudio.addr)) ||
+ __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)A(arg))->addr_format) ||
+ __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)A(arg))->nframes) ||
+ __get_user(addr, &((struct cdrom_read_audio32 *)A(arg))->buf))
+ return -EFAULT;
+ data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ cdreadaudio.buf = data;
+ break;
+ default:
+ printk("cdrom_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
+ (int)fd, (unsigned int)cmd, (unsigned int)arg);
+ return -EINVAL;
+ }
+ set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, cmd, (unsigned long)karg);
+ set_fs (old_fs);
+ if (err) {
+ if (data) kfree(data);
+ return err;
+ }
+ switch (cmd) {
+ case CDROMREADMODE2:
+ case CDROMREADMODE1:
+ case CDROMREADRAW:
+ case CDROMREADCOOKED:
+ if (copy_to_user((char *)A(addr), data, cdread.cdread_buflen)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ break;
+ case CDROMREADAUDIO:
+ if (copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ break;
+ default:
+ break;
+ }
+ if (data) kfree(data);
+ return 0;
+}
asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
{
@@ -955,10 +1065,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
int error = -EBADF;
lock_kernel();
- if(fd >= NR_OPEN)
- goto out;
-
- filp = current->files->fd[fd];
+ filp = fcheck(fd);
if(!filp)
goto out;
@@ -966,7 +1073,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
error = sys_ioctl (fd, cmd, (unsigned long)arg);
goto out;
}
- error = -EFAULT;
switch (cmd) {
case SIOCGIFCONF:
error = dev_ifconf(fd, arg);
@@ -1014,6 +1120,11 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
error = -EINVAL;
goto out;
+ case SIOCGSTAMP:
+ /* Sorry, timeval in the kernel is different now. */
+ error = do_siocgstamp(fd, cmd, arg);
+ goto out;
+
case HDIO_GETGEO:
error = hdio_getgeo(fd, arg);
goto out;
@@ -1066,6 +1177,15 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
error = mt_ioctl_trans(fd, cmd, arg);
goto out;
+ case CDROMREADMODE2:
+ case CDROMREADMODE1:
+ case CDROMREADRAW:
+ case CDROMREADCOOKED:
+ case CDROMREADAUDIO:
+ case CDROMREADALL:
+ error = cdrom_ioctl_trans(fd, cmd, arg);
+ goto out;
+
/* List here exlicitly which ioctl's are known to have
* compatable types passed or none at all...
*/
@@ -1170,6 +1290,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
/* 0x09 */
case REGISTER_DEV:
+ case REGISTER_DEV_NEW:
case START_MD:
case STOP_MD:
@@ -1219,6 +1340,7 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case SCSI_IOCTL_TAGGED_ENABLE:
case SCSI_IOCTL_TAGGED_DISABLE:
case SCSI_IOCTL_GET_BUS_NUMBER:
+ case SCSI_IOCTL_SEND_COMMAND:
/* Big V */
case VT_SETMODE:
@@ -1267,7 +1389,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case FIOGETOWN:
case SIOCGPGRP:
case SIOCATMARK:
- case SIOCGSTAMP:
case SIOCSIFLINK:
case SIOCSIFENCAP:
case SIOCGIFENCAP:
@@ -1305,6 +1426,36 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case PPPIOCSNPMODE:
case PPPIOCGDEBUG:
case PPPIOCSDEBUG:
+
+ /* CDROM stuff */
+ case CDROMPAUSE:
+ case CDROMRESUME:
+ case CDROMPLAYMSF:
+ case CDROMPLAYTRKIND:
+ case CDROMREADTOCHDR:
+ case CDROMREADTOCENTRY:
+ case CDROMSTOP:
+ case CDROMSTART:
+ case CDROMEJECT:
+ case CDROMVOLCTRL:
+ case CDROMSUBCHNL:
+ case CDROMEJECT_SW:
+ case CDROMMULTISESSION:
+ case CDROM_GET_MCN:
+ case CDROMRESET:
+ case CDROMVOLREAD:
+ case CDROMSEEK:
+ case CDROMPLAYBLK:
+ case CDROMCLOSETRAY:
+ case CDROM_SET_OPTIONS:
+ case CDROM_CLEAR_OPTIONS:
+ case CDROM_SELECT_SPEED:
+ case CDROM_SELECT_DISC:
+ case CDROM_MEDIA_CHANGED:
+ case CDROM_DRIVE_STATUS:
+ case CDROM_DISC_STATUS:
+ case CDROM_CHANGER_NSLOTS:
+
error = sys_ioctl (fd, cmd, (unsigned long)arg);
goto out;
@@ -1312,7 +1463,6 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
(int)fd, (unsigned int)cmd, (unsigned int)arg);
error = -EINVAL;
- goto out;
break;
}
out:
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index a84fe8eaa..176079643 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -1,7 +1,8 @@
-/* $Id: irq.c,v 1.47 1998/01/10 18:26:17 ecd Exp $
+/* $Id: irq.c,v 1.52 1998/03/19 00:22:54 ecd Exp $
* irq.c: UltraSparc IRQ handling/init/registry.
*
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
*/
#include <linux/config.h>
@@ -98,12 +99,22 @@ int get_irq_list(char *buf)
{
int i, len = 0;
struct irqaction *action;
+#ifdef __SMP__
+ int j;
+#endif
for(i = 0; i < (NR_IRQS + 1); i++) {
if(!(action = *(i + irq_action)))
continue;
- len += sprintf(buf + len, "%2d: %8d %c %s",
- i, kstat.interrupts[i],
+ len += sprintf(buf + len, "%3d: ", i);
+#ifndef __SMP__
+ len += sprintf(buf + len, "%10u ", kstat_irqs(i));
+#else
+ for (j = 0; j < smp_num_cpus; j++)
+ len += sprintf(buf + len, "%10u ",
+ kstat.irqs[cpu_logical_map(j)][i]);
+#endif
+ len += sprintf(buf + len, "%c %s",
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
for(action = action->next; action; action = action->next) {
@@ -113,19 +124,6 @@ int get_irq_list(char *buf)
}
len += sprintf(buf + len, "\n");
}
-#if 0
-#ifdef CONFIG_PCI
- {
- struct linux_psycho *p;
- for (p = psycho_root; p; p = p->next)
- len += sprintf(buf + len,
- "ISTAT[%d]: PCI[%016lx] OBIO[%016lx]\n",
- p->index,
- p->psycho_regs->pci_istate,
- p->psycho_regs->obio_istate);
- }
-#endif
-#endif
return len;
}
@@ -197,8 +195,7 @@ static unsigned int *sysio_irq_to_imap(unsigned int irq)
unsigned long offset;
struct sysio_regs *sregs;
- if((irq == 14) ||
- (irq >= NUM_SYSIO_OFFSETS) ||
+ if((irq >= NUM_SYSIO_OFFSETS) ||
((offset = sysio_irq_offsets[irq]) == ((unsigned long)-1)))
return NULL;
sregs = SBus_chain->iommu->sysio_regs;
@@ -224,8 +221,8 @@ static unsigned int *sysio_imap_to_iclr(unsigned int *imap)
unsigned char psycho_ino_to_pil[] = {
7, 5, 5, 2, /* PCI A slot 0 Int A, B, C, D */
7, 5, 5, 2, /* PCI A slot 1 Int A, B, C, D */
- 0, 0, 0, 0,
- 0, 0, 0, 0,
+ 7, 5, 5, 2, /* PCI A slot 2 Int A, B, C, D */
+ 7, 5, 5, 2, /* PCI A slot 3 Int A, B, C, D */
6, 4, 3, 1, /* PCI B slot 0 Int A, B, C, D */
6, 4, 3, 1, /* PCI B slot 1 Int A, B, C, D */
6, 4, 3, 1, /* PCI B slot 2 Int A, B, C, D */
@@ -255,13 +252,13 @@ unsigned char psycho_ino_to_pil[] = {
*/
#define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x)))
-#define psycho_imap_offset(ino) \
- ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \
+#define psycho_imap_offset(ino) \
+ ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \
(psycho_offset(imap_a_slot0) + (((ino) & 0x3c) << 1)))
-#define psycho_iclr_offset(ino) \
- ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \
- (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f) << 3)))
+#define psycho_iclr_offset(ino) \
+ ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \
+ (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f)<<3)))
#endif
@@ -529,7 +526,7 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
unsigned long flags;
unsigned int *imap, *iclr;
void *bus_id = NULL;
- int ivindex, ivindex_fixup, cpu_irq = -1, pending;
+ int ivindex = -1, ivindex_fixup, cpu_irq = -1, pending = 0;
if(!handler)
return -EINVAL;
@@ -537,43 +534,47 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
imap = iclr = NULL;
ivindex_fixup = 0;
+
+ if (irq == 0) {
+ cpu_irq = irq;
+ irqflags &= ~(SA_IMAP_MASKED);
+ } else {
+ irqflags |= SA_IMAP_MASKED;
#ifdef CONFIG_PCI
- if(PCI_IRQ_P(irq)) {
- pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
- } else
+ if(PCI_IRQ_P(irq)) {
+ pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
+ } else
#endif
- if(irqflags & SA_DCOOKIE) {
- if(!dev_id) {
- printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n");
- panic("Bogus irq registry.");
- }
- dcookie = dev_id;
- dev_id = dcookie->real_dev_id;
- cpu_irq = dcookie->pil;
- imap = dcookie->imap;
- iclr = dcookie->iclr;
- bus_id = dcookie->bus_cookie;
- get_irq_translations(&cpu_irq, &ivindex_fixup, &imap,
- &iclr, bus_id, irqflags, irq);
- } else {
- /* XXX NOTE: This code is maintained for compatability until I can
- * XXX verify that all drivers sparc64 will use are updated
- * XXX to use the new IRQ registry dcookie interface. -DaveM
- */
- if(irq == 14)
- cpu_irq = irq;
- else
+ if(irqflags & SA_DCOOKIE) {
+ if(!dev_id) {
+ printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n");
+ panic("Bogus irq registry.");
+ }
+ dcookie = dev_id;
+ dev_id = dcookie->real_dev_id;
+ cpu_irq = dcookie->pil;
+ imap = dcookie->imap;
+ iclr = dcookie->iclr;
+ bus_id = dcookie->bus_cookie;
+ get_irq_translations(&cpu_irq, &ivindex_fixup, &imap,
+ &iclr, bus_id, irqflags, irq);
+ } else {
+ /* XXX NOTE: This code is maintained for compatability until I can
+ * XXX verify that all drivers sparc64 will use are updated
+ * XXX to use the new IRQ registry dcookie interface. -DaveM
+ */
cpu_irq = sysio_ino_to_pil[irq];
- imap = sysio_irq_to_imap(irq);
- if(!imap) {
- printk("request_irq: BAD, null imap for old style "
- "irq registry IRQ[%x].\n", irq);
- panic("Bad IRQ registery...");
+ imap = sysio_irq_to_imap(irq);
+ if(!imap) {
+ printk("request_irq: BAD, null imap for old style "
+ "irq registry IRQ[%x].\n", irq);
+ panic("Bad IRQ registery...");
+ }
+ iclr = sysio_imap_to_iclr(imap);
}
- iclr = sysio_imap_to_iclr(imap);
+ ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
+ ivindex += ivindex_fixup;
}
- ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
- ivindex += ivindex_fixup;
action = *(cpu_irq + irq_action);
if(action) {
@@ -612,26 +613,28 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
return -ENOMEM;
}
- bucket = add_ino_hash(ivindex, imap, iclr, irqflags);
- if(!bucket) {
- kfree(action);
- restore_flags(flags);
- return -ENOMEM;
- }
+ if (irqflags & SA_IMAP_MASKED) {
+ bucket = add_ino_hash(ivindex, imap, iclr, irqflags);
+ if(!bucket) {
+ kfree(action);
+ restore_flags(flags);
+ return -ENOMEM;
+ }
- pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0);
- ivector_to_mask[ivindex] = (1 << cpu_irq);
- if(pending)
- ivector_to_mask[ivindex] |= 0x80000000;
+ pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0);
+ ivector_to_mask[ivindex] = (1 << cpu_irq);
+ if(pending)
+ ivector_to_mask[ivindex] |= 0x80000000;
- if(dcookie) {
- dcookie->ret_ino = ivindex;
- dcookie->ret_pil = cpu_irq;
+ if(dcookie) {
+ dcookie->ret_ino = ivindex;
+ dcookie->ret_pil = cpu_irq;
+ }
}
action->mask = (unsigned long) bucket;
action->handler = handler;
- action->flags = irqflags | SA_IMAP_MASKED;
+ action->flags = irqflags;
action->name = name;
action->next = NULL;
action->dev_id = dev_id;
@@ -664,7 +667,7 @@ void free_irq(unsigned int irq, void *dev_id)
unsigned int cpu_irq;
int ivindex = -1;
- if(irq == 14) {
+ if(irq == 0) {
cpu_irq = irq;
} else {
#ifdef CONFIG_PCI
@@ -951,34 +954,43 @@ void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs)
void handler_irq(int irq, struct pt_regs *regs)
{
struct ino_bucket *bucket = NULL;
- struct irqaction *action;
+ struct irqaction *action, *act;
int cpu = smp_processor_id();
+#ifndef __SMP__
+ /*
+ * Check for TICK_INT on level 14 softint.
+ */
+ if ((irq == 14) && get_softint() & (1UL << 0))
+ irq = 0;
+#endif
clear_softint(1 << irq);
irq_enter(cpu, irq);
action = *(irq + irq_action);
- kstat.interrupts[irq]++;
+ kstat.irqs[cpu][irq]++;
if(!action) {
unexpected_irq(irq, 0, regs);
} else {
+ act = action;
do {
- unsigned long *swmask = NULL;
-
- if(action->flags & SA_IMAP_MASKED) {
- bucket = (struct ino_bucket *)action->mask;
-
- swmask = &ivector_to_mask[bucket->ino];
- if(!(*swmask & 0x80000000))
+ if(act->flags & SA_IMAP_MASKED) {
+ bucket = (struct ino_bucket *)act->mask;
+ if(!(ivector_to_mask[bucket->ino] & 0x80000000))
continue;
}
-
- action->handler(irq, action->dev_id, regs);
- if(swmask) {
- *swmask &= ~(0x80000000);
+ act->handler(irq, act->dev_id, regs);
+ } while((act = act->next) != NULL);
+ act = action;
+ do {
+ if(act->flags & SA_IMAP_MASKED) {
+ bucket = (struct ino_bucket *)act->mask;
+ if(!(ivector_to_mask[bucket->ino] & 0x80000000))
+ continue;
+ ivector_to_mask[bucket->ino] &= ~(0x80000000);
*(bucket->iclr) = SYSIO_ICLR_IDLE;
}
- } while((action = action->next) != NULL);
+ } while((act = act->next) != NULL);
}
irq_exit(cpu, irq);
}
@@ -993,6 +1005,7 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs)
int cpu = smp_processor_id();
irq_enter(cpu, irq);
+ kstat.irqs[cpu][irq]++;
bucket = (struct ino_bucket *)action->mask;
floppy_interrupt(irq, dev_cookie, regs);
ivector_to_mask[bucket->ino] &= ~(0x80000000);
@@ -1036,13 +1049,19 @@ int request_fast_irq(unsigned int irq,
unsigned long flags;
unsigned int *imap, *iclr;
void *bus_id = NULL;
- int ivindex, ivindex_fixup, cpu_irq = -1;
+ int ivindex = -1, ivindex_fixup, cpu_irq = -1;
if(!handler)
return -EINVAL;
imap = iclr = NULL;
ivindex_fixup = 0;
+
+ if ((irq == 0) || (irq == 14)) {
+ printk("request_fast_irq: Trying to register shared IRQ 0 or 14.\n");
+ return -EBUSY;
+ }
+
#ifdef CONFIG_PCI
if(PCI_IRQ_P(irq)) {
pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
@@ -1066,10 +1085,7 @@ int request_fast_irq(unsigned int irq,
* XXX verify that all drivers sparc64 will use are updated
* XXX to use the new IRQ registry dcookie interface. -DaveM
*/
- if(irq == 14)
- cpu_irq = irq;
- else
- cpu_irq = sysio_ino_to_pil[irq];
+ cpu_irq = sysio_ino_to_pil[irq];
imap = sysio_irq_to_imap(irq);
if(!imap) {
printk("request_irq: BAD, null imap for old style "
@@ -1153,85 +1169,100 @@ int probe_irq_off(unsigned long mask)
return 0;
}
-struct sun5_timer *linux_timers = NULL;
-
-/* This is gets the master level10 timer going. */
-void init_timers(void (*cfunc)(int, void *, struct pt_regs *))
+/* This is gets the master TICK_INT timer going. */
+void init_timers(void (*cfunc)(int, void *, struct pt_regs *),
+ unsigned long *clock)
{
- struct linux_prom64_registers pregs[3];
- struct devid_cookie dcookie;
- unsigned int *imap, *iclr;
- u32 pirqs[2];
+ unsigned long flags;
+ unsigned long timer_tick_offset;
int node, err;
- node = prom_finddevice("/counter-timer");
- if(node == 0 || node == -1) {
- prom_printf("init_timers: Cannot find counter-timer PROM node.\n");
- prom_halt();
- }
- err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
- if(err == -1) {
- prom_printf("init_timers: Cannot obtain 'reg' for counter-timer.\n");
- prom_halt();
- }
- err = prom_getproperty(node, "interrupts", (char *)&pirqs[0], sizeof(pirqs));
- if(err == -1) {
- prom_printf("init_timers: Cannot obtain 'interrupts' "
- "for counter-timer.\n");
- prom_halt();
- }
- linux_timers = (struct sun5_timer *) __va(pregs[0].phys_addr);
- iclr = (((unsigned int *)__va(pregs[1].phys_addr))+1);
- imap = (((unsigned int *)__va(pregs[2].phys_addr))+1);
-
- /* Shut it up first. */
- linux_timers->limit0 = 0;
+ node = linux_cpus[0].prom_node;
+ *clock = prom_getint(node, "clock-frequency");
+ timer_tick_offset = *clock / HZ;
/* Register IRQ handler. */
- dcookie.real_dev_id = NULL;
- dcookie.imap = imap;
- dcookie.iclr = iclr;
- dcookie.pil = 10;
- dcookie.bus_cookie = NULL;
-
- err = request_irq(pirqs[0], cfunc,
- (SA_DCOOKIE | SA_INTERRUPT | SA_STATIC_ALLOC),
- "timer", &dcookie);
+ err = request_irq(0, cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC),
+ "timer", NULL);
if(err) {
- prom_printf("Serious problem, cannot register timer interrupt\n");
+ prom_printf("Serious problem, cannot register TICK_INT\n");
prom_halt();
- } else {
- unsigned long flags;
+ }
- save_and_cli(flags);
+ save_and_cli(flags);
- /* Set things up so user can access tick register for profiling
- * purposes.
- */
- __asm__ __volatile__("
- sethi %%hi(0x80000000), %%g1
- sllx %%g1, 32, %%g1
- rd %%tick, %%g2
- add %%g2, 6, %%g2
- andn %%g2, %%g1, %%g2
- wrpr %%g2, 0, %%tick
-" : /* no outputs */
- : /* no inputs */
- : "g1", "g2");
-
- linux_timers->limit0 =
- (SUN5_LIMIT_ENABLE | SUN5_LIMIT_ZRESTART | SUN5_LIMIT_TOZERO |
- (SUN5_HZ_TO_LIMIT(HZ) & SUN5_LIMIT_CMASK));
+ /* Set things up so user can access tick register for profiling
+ * purposes.
+ */
+ __asm__ __volatile__("
+ sethi %%hi(0x80000000), %%g1
+ sllx %%g1, 32, %%g1
+ rd %%tick, %%g2
+ add %%g2, 6, %%g2
+ andn %%g2, %%g1, %%g2
+ wrpr %%g2, 0, %%tick
+" : /* no outputs */
+ : /* no inputs */
+ : "g1", "g2");
- restore_flags(flags);
- }
+ __asm__ __volatile__("
+ rd %%tick, %%g1
+ add %%g1, %0, %%g1
+ wr %%g1, 0x0, %%tick_cmpr"
+ : /* no outputs */
+ : "r" (timer_tick_offset)
+ : "g1");
+ restore_flags(flags);
sti();
}
-struct sun5_timer *prom_timers;
+#ifdef __SMP__
+/* Called from smp_commence, when we know how many cpus are in the system
+ * and can have device IRQ's directed at them.
+ */
+void distribute_irqs(void)
+{
+ unsigned long flags;
+ int cpu, level;
+
+ printk("SMP: redistributing interrupts...\n");
+ save_and_cli(flags);
+ cpu = 0;
+ for(level = 0; level < NR_IRQS; level++) {
+ struct irqaction *p = irq_action[level];
+
+ while(p) {
+ if(p->flags & SA_IMAP_MASKED) {
+ struct ino_bucket *bucket = (struct ino_bucket *)p->mask;
+ unsigned int *imap = bucket->imap;
+ unsigned int val;
+ unsigned long tid = __cpu_logical_map[cpu] << 9;
+
+ val = *imap;
+ *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
+ printk("SMP: Redirecting IGN[%x] INO[%x] "
+ "to cpu %d [%s]\n",
+ (val & SYSIO_IMAP_IGN) >> 6,
+ (val & SYSIO_IMAP_INO), cpu,
+ p->name);
+
+ cpu++;
+ if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1)
+ cpu = 0;
+ }
+ p = p->next;
+ }
+ }
+ restore_flags(flags);
+ irqs_have_been_distributed = 1;
+}
+#endif
+
+
+struct sun5_timer *prom_timers;
static u64 prom_limit0, prom_limit1;
static void map_prom_timers(void)
@@ -1245,9 +1276,8 @@ static void map_prom_timers(void)
/* Assume if node is not present, PROM uses different tick mechanism
* which we should not care about.
*/
- if(tnode == 0) {
+ if(tnode == 0 || tnode == -1) {
prom_timers = (struct sun5_timer *) 0;
- prom_printf("AIEEE, no timers\n");
return;
}
@@ -1300,52 +1330,6 @@ void enable_prom_timer(void)
prom_timers->count0 = 0;
}
-#ifdef __SMP__
-/* Called from smp_commence, when we know how many cpus are in the system
- * and can have device IRQ's directed at them.
- */
-void distribute_irqs(void)
-{
- unsigned long flags;
- int cpu, level;
-
- printk("SMP: redistributing interrupts...\n");
- save_and_cli(flags);
- cpu = 0;
- for(level = 0; level < NR_IRQS; level++) {
- struct irqaction *p = irq_action[level];
-
- while(p) {
- if(p->flags & SA_IMAP_MASKED) {
- struct ino_bucket *bucket = (struct ino_bucket *)p->mask;
- unsigned int *imap = bucket->imap;
- unsigned int val;
- unsigned long tid = linux_cpus[cpu].mid << 9;
-
- val = *imap;
- *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
-
- printk("SMP: Redirecting IGN[%x] INO[%x] "
- "to cpu %d [%s]\n",
- (val & SYSIO_IMAP_IGN) >> 6,
- (val & SYSIO_IMAP_INO), cpu,
- p->name);
-
- cpu += 1;
- while(!(cpu_present_map & (1UL << cpu))) {
- cpu += 1;
- if(cpu >= smp_num_cpus)
- cpu = 0;
- }
- }
- p = p->next;
- }
- }
- restore_flags(flags);
- irqs_have_been_distributed = 1;
-}
-#endif
-
__initfunc(void init_IRQ(void))
{
int i;
diff --git a/arch/sparc64/kernel/itlb_miss.S b/arch/sparc64/kernel/itlb_miss.S
index 9317587a7..94e3f44f6 100644
--- a/arch/sparc64/kernel/itlb_miss.S
+++ b/arch/sparc64/kernel/itlb_miss.S
@@ -1,42 +1,42 @@
-/* $Id: itlb_miss.S,v 1.11 1997/10/14 01:48:25 davem Exp $
+/* $Id: itlb_miss.S,v 1.12 1998/01/14 17:14:47 jj Exp $
* itlb_miss.S: Instruction TLB miss code, this is included directly
* into the trap table.
*
* Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/* Gratuitous comment. */
/* ICACHE line 1 */
/*0x00*/ ldxa [%g0] ASI_IMMU, %g1 ! Get TAG_TARGET
- /*0x04*/ srlx %g1, 8, %g3 ! Position PGD offset
- /*0x08*/ srlx %g1, 48, %g5 ! Shift down CONTEXT bits
- /*0x0c*/ and %g3, %g2, %g3 ! Mask PGD offset
- /*0x10*/ sllx %g1, 2, %g4 ! Position PMD offset
+ /*0x04*/ srlx %g1, 10, %g3 ! Position PGD offset
+ /*0x08*/ andcc %g1, %g2, %g0 ! Test CONTEXT bits
+ /*0x0c*/ and %g3, 0xffc, %g3 ! Mask PGD offset
+ /*0x10*/ and %g1, 0xffe, %g4 ! Mask PMD offset
/*0x14*/ ldxa [%g0] ASI_IMMU_TSB_8KB_PTR, %g1 ! For PTE offset
- /*0x18*/ brz,pn %g5, 3f ! Context 0 == kernel
- /*0x1c*/ and %g4, %g2, %g4 ! Mask PMD offset
+ /*0x18*/ be,pn %xcc, 3f ! Context 0 == kernel
+ /*0x1c*/ add %g4, %g4, %g4 ! Position PMD offset
/* ICACHE line 2 */
- /*0x20*/ ldxa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load user PGD
+ /*0x20*/ lduwa [%g7 + %g3] ASI_PHYS_USE_EC, %g5 ! Load user PGD
/*0x24*/ srlx %g1, 1, %g1 ! PTE offset
- /*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD
-2:/*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE
+ /*0x28*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD
+ /*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE
/*0x30*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set?
/*0x34*/ nop ! delay
/*0x38*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load
/*0x3c*/ retry ! Trap return
3: /* ICACHE line 3 */
- /*0x40*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD
+ /*0x40*/ lduwa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD
/*0x44*/ srlx %g1, 1, %g1 ! PTE offset
- /*0x48*/ ba,pt %xcc, 2b ! Continue above
- /*0x4c*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD
- /*0x50*/ nop
- /*0x54*/ nop
- /*0x58*/ nop
- /*0x5c*/ nop
+ /*0x48*/ lduwa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD
+ /*0x4c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE
+ /*0x50*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set?
+ /*0x54*/ nop ! delay
+ /*0x58*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load
+ /*0x5c*/ retry ! Trap return
/* ICACHE line 4 */
/*0x60*/ nop
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c
index 4e5ead982..c0058afd9 100644
--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc64/kernel/process.c
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.50 1998/01/09 16:39:33 jj Exp $
+/* $Id: process.c,v 1.52 1998/03/29 12:57:53 ecd Exp $
* arch/sparc64/kernel/process.c
*
* Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -41,9 +41,6 @@
/* #define VERBOSE_SHOWREGS */
-#define PGTCACHE_HIGH_WATER 50
-#define PGTCACHE_LOW_WATER 25
-
#ifndef __SMP__
/*
@@ -58,16 +55,7 @@ asmlinkage int sys_idle(void)
current->priority = -100;
current->counter = -100;
for (;;) {
- if(pgtable_cache_size > PGTCACHE_LOW_WATER) {
- do {
- if(pgd_quicklist)
- free_page((unsigned long) get_pgd_fast());
- if(pmd_quicklist)
- free_page((unsigned long) get_pmd_fast());
- if(pte_quicklist)
- free_page((unsigned long) get_pte_fast());
- } while(pgtable_cache_size > PGTCACHE_HIGH_WATER);
- }
+ check_pgt_cache();
run_task_queue(&tq_scheduler);
schedule();
}
@@ -83,16 +71,7 @@ asmlinkage int cpu_idle(void)
{
current->priority = -100;
while(1) {
- if(pgtable_cache_size > PGTCACHE_LOW_WATER) {
- do {
- if(pgd_quicklist)
- free_page((unsigned long) get_pgd_fast());
- if(pmd_quicklist)
- free_page((unsigned long) get_pmd_fast());
- if(pte_quicklist)
- free_page((unsigned long) get_pte_fast());
- } while(pgtable_cache_size > PGTCACHE_HIGH_WATER);
- }
+ check_pgt_cache();
if(tq_scheduler) {
lock_kernel();
run_task_queue(&tq_scheduler);
@@ -592,6 +571,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
p->tss.flags |= SPARC_FLAG_KTHREAD;
p->tss.current_ds = KERNEL_DS;
p->tss.ctx = 0;
+ __asm__ __volatile__("flushw");
+ memcpy((void *)(p->tss.ksp + STACK_BIAS),
+ (void *)(regs->u_regs[UREG_FP] + STACK_BIAS),
+ sizeof(struct reg_window));
p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p;
} else {
if(current->tss.flags & SPARC_FLAG_32BIT) {
diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c
index b3b403e33..78a69e8df 100644
--- a/arch/sparc64/kernel/psycho.c
+++ b/arch/sparc64/kernel/psycho.c
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $
+/* $Id: psycho.c,v 1.50 1998/04/10 12:29:47 ecd Exp $
* psycho.c: Ultra/AX U2P PCI controller support.
*
* Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -8,6 +8,7 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/init.h>
#include <asm/ebus.h>
#include <asm/sbus.h> /* for sanity check... */
@@ -15,6 +16,7 @@
#undef PROM_DEBUG
#undef FIXUP_REGS_DEBUG
#undef FIXUP_IRQ_DEBUG
+#undef FIXUP_VMA_DEBUG
#ifdef PROM_DEBUG
#define dprintf prom_printf
@@ -22,6 +24,9 @@
#define dprintf printk
#endif
+unsigned long pci_dvma_offset = 0x00000000UL;
+unsigned long pci_dvma_mask = 0xffffffffUL;
+
#ifndef CONFIG_PCI
int pcibios_present(void)
@@ -51,12 +56,12 @@ asmlinkage int sys_pciconfig_write(unsigned long bus,
#include <linux/smp.h>
#include <linux/smp_lock.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/oplib.h>
#include <asm/pbm.h>
+#include <asm/apb.h>
#include <asm/uaccess.h>
struct linux_psycho *psycho_root = NULL;
@@ -95,8 +100,9 @@ static inline unsigned long long_align(unsigned long addr)
~(sizeof(unsigned long) - 1));
}
-static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
- unsigned long memory_start)
+__initfunc(static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
+ int tsbsize,
+ unsigned long memory_start))
{
unsigned long tsbbase = PAGE_ALIGN(memory_start);
unsigned long control, i;
@@ -114,10 +120,10 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
control &= ~(IOMMU_CTRL_DENAB);
psycho->psycho_regs->iommu_control = control;
- memory_start = (tsbbase + ((32 * 1024) * 8));
+ memory_start = (tsbbase + ((tsbsize * 1024) * 8));
iopte = (unsigned long *)tsbbase;
- for(i = 0; i < (32 * 1024); i++) {
+ for(i = 0; i < (tsbsize * 1024); i++) {
*iopte = (IOPTE_VALID | IOPTE_64K |
IOPTE_CACHE | IOPTE_WRITE);
*iopte |= (i << 16);
@@ -128,15 +134,215 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
control = psycho->psycho_regs->iommu_control;
control &= ~(IOMMU_CTRL_TSBSZ);
- control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+ control |= (IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+ switch(tsbsize) {
+ case 8:
+ pci_dvma_mask = 0x1fffffffUL;
+ control |= IOMMU_TSBSZ_8K;
+ break;
+ case 16:
+ pci_dvma_mask = 0x3fffffffUL;
+ control |= IOMMU_TSBSZ_16K;
+ break;
+ case 32:
+ pci_dvma_mask = 0x7fffffffUL;
+ control |= IOMMU_TSBSZ_32K;
+ break;
+ default:
+ prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
+ prom_halt();
+ break;
+ }
psycho->psycho_regs->iommu_control = control;
return memory_start;
}
extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
+extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm);
+
+/*
+ * Poor man's PCI...
+ */
+__initfunc(unsigned long sabre_init(int pnode, unsigned long memory_start))
+{
+ struct linux_prom64_registers pr_regs[2];
+ struct linux_psycho *sabre;
+ unsigned long ctrl;
+ int tsbsize, node, err;
+ u32 busrange[2];
+ u32 vdma[2];
+ u32 portid;
+ int bus;
+
+ sabre = (struct linux_psycho *)memory_start;
+ memory_start = long_align(memory_start + sizeof(struct linux_psycho));
+
+ portid = prom_getintdefault(pnode, "upa-portid", 0xff);
+
+ memset(sabre, 0, sizeof(*sabre));
+
+ sabre->next = psycho_root;
+ psycho_root = sabre;
+
+ sabre->upa_portid = portid;
+ sabre->index = linux_num_psycho++;
+
+ /*
+ * 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.
+ */
+ sabre->psycho_regs =
+ sparc_alloc_io((pr_regs[0].phys_addr & 0xffffffff),
+ NULL, sizeof(struct psycho_regs),
+ "SABRE Registers",
+ (pr_regs[0].phys_addr >> 32), 0);
+ if(sabre->psycho_regs == NULL) {
+ prom_printf("SABRE: Error, cannot map SABRE main registers.\n");
+ prom_halt();
+ }
+
+ printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+#ifdef PROM_DEBUG
+ dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+#endif
+
+ ctrl = sabre->psycho_regs->pci_a_control;
+ ctrl = (1UL << 36) | (1UL << 34) | (1UL << 21) | (1UL << 8) | 0x0fUL;
+ sabre->psycho_regs->pci_a_control = ctrl;
+
+ /* Now map in PCI config space for entire SABRE. */
+ sabre->pci_config_space =
+ sparc_alloc_io(((pr_regs[0].phys_addr & 0xffffffff)
+ + 0x01000000),
+ NULL, 0x01000000,
+ "PCI Config Space",
+ (pr_regs[0].phys_addr >> 32), 0);
+ if(sabre->pci_config_space == NULL) {
+ prom_printf("SABRE: Error, cannot map PCI config space.\n");
+ prom_halt();
+ }
+
+ /* Report some more info. */
+ printk("SABRE: PCI config space at %p\n", sabre->pci_config_space);
+#ifdef PROM_DEBUG
+ dprintf("SABRE: PCI config space at %p\n", sabre->pci_config_space);
+#endif
+
+ 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();
+ }
+
+ switch(vdma[1]) {
+ case 0x20000000:
+ tsbsize = 8;
+ break;
+ case 0x40000000:
+ tsbsize = 16;
+ break;
+ case 0x80000000:
+ tsbsize = 32;
+ break;
+ default:
+ prom_printf("SABRE: strange virtual-dma size.\n");
+ prom_halt();
+ }
+
+ memory_start = psycho_iommu_init(sabre, tsbsize, memory_start);
+ pci_dvma_offset = vdma[0];
+
+ printk("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+#ifdef PROM_DEBUG
+ dprintf("SABRE: DVMA at %08x [%08x]\n", vdma[0], vdma[1]);
+#endif
+
+ err = prom_getproperty(pnode, "bus-range",
+ (char *)&busrange[0], sizeof(busrange));
+ if(err == 0 || err == -1) {
+ prom_printf("SIMBA: Error, cannot get PCI bus-range "
+ " from PROM.\n");
+ prom_halt();
+ }
+
+ sabre->pci_first_busno = busrange[0];
+ sabre->pci_last_busno = busrange[1];
+ sabre->pci_bus = &pci_root;
+
+ /*
+ * Handle config space reads through any Simba on APB.
+ */
+ for (bus = sabre->pci_first_busno; bus <= sabre->pci_last_busno; bus++)
+ bus2pbm[bus] = &sabre->pbm_A;
+
+ /*
+ * Look for APB underneath.
+ */
+ node = prom_getchild(pnode);
+ while ((node = prom_searchsiblings(node, "pci"))) {
+ struct linux_pbm_info *pbm;
+ char namebuf[128];
+
+ 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("SIMBA: Error, cannot get PCI bus-range "
+ " from PROM.\n");
+ prom_halt();
+ }
+
+ if (busrange[0] == 1)
+ pbm = &sabre->pbm_B;
+ else
+ pbm = &sabre->pbm_A;
+
+ pbm->parent = sabre;
+ pbm->IO_assignments = NULL;
+ pbm->MEM_assignments = NULL;
+ pbm->prom_node = node;
+
+ prom_getstring(node, "name", namebuf, sizeof(namebuf));
+ strcpy(pbm->prom_name, namebuf);
+
+ /* Now the ranges. */
+ prom_pbm_ranges_init(pnode, pbm);
+ prom_pbm_intmap_init(node, pbm);
+
+ pbm->pci_first_busno = busrange[0];
+ pbm->pci_last_busno = busrange[1];
+ memset(&pbm->pci_bus, 0, sizeof(struct pci_bus));
+
+ for (bus = pbm->pci_first_busno;
+ bus <= pbm->pci_last_busno; bus++)
+ bus2pbm[bus] = pbm;
-unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
+ next_pci:
+ node = prom_getsibling(node);
+ if (!node)
+ break;
+ }
+
+ return memory_start;
+}
+
+__initfunc(unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end))
{
struct linux_prom64_registers pr_regs[3];
struct linux_psycho *psycho;
@@ -144,9 +350,9 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
u32 portid;
int node;
- printk("PSYCHO: Probing for controllers.\n");
+ printk("PCI: Probing for controllers.\n");
#ifdef PROM_DEBUG
- dprintf("PSYCHO: Probing for controllers.\n");
+ dprintf("PCI: Probing for controllers.\n");
#endif
memory_start = long_align(memory_start);
@@ -157,6 +363,12 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
u32 busrange[2];
int err, is_pbm_a;
+ err = prom_getproperty(node, "model", namebuf, sizeof(namebuf));
+ if ((err > 0) && !strncmp(namebuf, "SUNW,sabre", err)) {
+ memory_start = sabre_init(node, memory_start);
+ goto next_pci;
+ }
+
psycho = (struct linux_psycho *)memory_start;
portid = prom_getintdefault(node, "upa-portid", 0xff);
@@ -200,34 +412,30 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
* Third REG in property is base of entire PSYCHO
* register space.
*/
- psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
- NULL, sizeof(struct psycho_regs),
- "PSYCHO Registers",
- (pr_regs[2].phys_addr >> 32), 0);
+ psycho->psycho_regs =
+ sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff),
+ NULL, sizeof(struct psycho_regs),
+ "PSYCHO Registers",
+ (pr_regs[2].phys_addr >> 32), 0);
if(psycho->psycho_regs == NULL) {
prom_printf("PSYCHO: Error, cannot map PSYCHO "
"main registers.\n");
prom_halt();
}
- printk("PSYCHO: Found controller, main regs at %p\n",
+ printk("PCI: Found PSYCHO, main regs at %p\n",
psycho->psycho_regs);
#ifdef PROM_DEBUG
- dprintf("PSYCHO: Found controller, main regs at %p\n",
+ dprintf("PCI: Found PSYCHO, main regs at %p\n",
psycho->psycho_regs);
#endif
psycho->psycho_regs->irq_retry = 0xff;
-#if 0
- psycho->psycho_regs->ecc_control |= 1;
- psycho->psycho_regs->sbuf_a_control = 0;
- psycho->psycho_regs->sbuf_b_control = 0;
-#endif
-
/* Now map in PCI config space for entire PSYCHO. */
psycho->pci_config_space =
- sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000),
+ sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)
+ + 0x01000000),
NULL, 0x01000000,
"PCI Config Space",
(pr_regs[2].phys_addr >> 32), 0);
@@ -244,7 +452,8 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
psycho->pci_config_space);
#endif
- memory_start = psycho_iommu_init(psycho, memory_start);
+ memory_start = psycho_iommu_init(psycho, 32, memory_start);
+ pci_dvma_offset = 0x80000000UL;
is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
@@ -268,6 +477,7 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
/* Now the ranges. */
prom_pbm_ranges_init(node, pbm);
+ prom_pbm_intmap_init(node, pbm);
/* Finally grab the pci bus root array for this pbm after
* having found the bus range existing under it.
@@ -282,6 +492,7 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
pbm->pci_last_busno = busrange[1];
memset(&pbm->pci_bus, 0, sizeof(struct pci_bus));
+ next_pci:
node = prom_getsibling(node);
if(!node)
break;
@@ -308,54 +519,18 @@ int pcibios_present(void)
return psycho_root != NULL;
}
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus,
- unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->vendor == vendor && dev->device == device_id) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-int pcibios_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->class == class_code) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm,
unsigned long start,
- int io)
+ unsigned int offset, int io)
{
struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
- while(vp) {
- if(vp->end > start)
+ while (vp) {
+ if (offset && (vp->offset != offset))
+ goto next;
+ if (vp->end >= start)
break;
+ next:
vp = vp->next;
}
return vp;
@@ -391,7 +566,7 @@ static inline void pci_add_vma(struct linux_pbm_info *pbm, struct pci_vma *new,
/* Check for programming errors. */
if(vp &&
((vp->start >= new->start && vp->start < new->end) ||
- ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) {
+ (vp->end >= new->start && vp->end < new->end))) {
prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n",
io ? "IO" : "MEM");
prom_printf("pci_add_vma: vp[%016lx:%016lx] "
@@ -414,7 +589,7 @@ static inline void pci_init_alloc_fini(void)
pci_alloc_arena = NULL;
}
-static void *pci_init_alloc(int size)
+__initfunc(static void *pci_init_alloc(int size))
{
unsigned long start = long_align(*pci_alloc_arena);
void *mp = (void *)start;
@@ -439,8 +614,8 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void)
}
-static void
-pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
+__initfunc(static void
+pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus))
{
unsigned int devfn, l, class;
unsigned char hdr_type = 0;
@@ -487,7 +662,7 @@ pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
}
}
-static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
+__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus))
{
unsigned int nbus;
@@ -513,7 +688,79 @@ static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
}
-static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
+__initfunc(static void apb_init(struct linux_psycho *sabre))
+{
+ struct pci_dev *pdev;
+ unsigned short stmp;
+ unsigned int itmp;
+
+ for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) {
+ if (pdev->vendor == PCI_VENDOR_ID_SUN &&
+ pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
+
+ pci_read_config_word(pdev, PCI_COMMAND, &stmp);
+ stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_IO;
+ pci_write_config_word(pdev, PCI_COMMAND, stmp);
+
+ pci_write_config_word(pdev, PCI_STATUS, 0xffff);
+ pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
+
+ pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &stmp);
+ stmp = PCI_BRIDGE_CTL_MASTER_ABORT |
+ PCI_BRIDGE_CTL_SERR |
+ PCI_BRIDGE_CTL_PARITY;
+ pci_write_config_word(pdev, PCI_BRIDGE_CONTROL, stmp);
+
+ pci_read_config_dword(pdev, APB_PCI_CONTROL_HIGH, &itmp);
+ itmp = APB_PCI_CTL_HIGH_SERR |
+ APB_PCI_CTL_HIGH_ARBITER_EN;
+ pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp);
+
+ pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp);
+ itmp = APB_PCI_CTL_LOW_ARB_PARK |
+ APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
+ pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp);
+
+ /*
+ * Setup Registers for Guaranteed Completion.
+ */
+ pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0);
+ pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0);
+ pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80);
+ pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0);
+ pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80);
+ pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0);
+ }
+ }
+}
+
+__initfunc(static void sabre_probe(struct linux_psycho *sabre,
+ unsigned long *mstart))
+{
+ struct pci_bus *pbus = sabre->pci_bus;
+ static unsigned char busno = 0;
+
+ pbus->number = pbus->secondary = busno;
+ pbus->sysdata = sabre;
+
+ pbus->subordinate = pci_scan_bus(pbus, mstart);
+ busno = pbus->subordinate + 1;
+
+ for(pbus = pbus->children; pbus; pbus = pbus->next) {
+ if (pbus->number == sabre->pbm_A.pci_first_busno)
+ memcpy(&sabre->pbm_A.pci_bus, pbus, sizeof(*pbus));
+ if (pbus->number == sabre->pbm_B.pci_first_busno)
+ memcpy(&sabre->pbm_B.pci_bus, pbus, sizeof(*pbus));
+ }
+
+ apb_init(sabre);
+}
+
+
+__initfunc(static void pbm_probe(struct linux_pbm_info *pbm,
+ unsigned long *mstart))
{
static struct pci_bus *pchain = NULL;
struct pci_bus *pbus = &pbm->pci_bus;
@@ -552,9 +799,9 @@ static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
}
}
-static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
- struct pci_dev *pdev,
- int node)
+__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev,
+ int node))
{
struct linux_prom_pci_registers pregs[PROMREG_MAX];
int err;
@@ -583,7 +830,7 @@ static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
return 0;
}
-static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)
+__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev))
{
struct pcidev_cookie *pcp;
int node = prom_getchild(pbm->prom_node);
@@ -597,24 +844,37 @@ static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)
pdev->sysdata = pcp;
}
-static void fill_in_pbm_cookies(struct linux_pbm_info *pbm)
+__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus,
+ struct linux_pbm_info *pbm))
{
- struct pci_bus *pbtmp, *pbus = &pbm->pci_bus;
struct pci_dev *pdev;
- for(pbtmp = pbus->children; pbtmp; pbtmp = pbtmp->children)
- pbtmp->sysdata = pbm;
+ pbus->sysdata = pbm;
+
+ for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
+ pdev_cookie_fillin(pbm, pdev);
+
+ for(pbus = pbus->children; pbus; pbus = pbus->next)
+ fill_in_pbm_cookies(pbus, pbm);
+}
+
+__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre))
+{
+ struct pci_bus *pbus = sabre->pci_bus;
- for( ; pbus; pbus = pbus->children)
- for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
- pdev_cookie_fillin(pbm, pdev);
+ for(pbus = pbus->children; pbus; pbus = pbus->next) {
+ if (pbus->number == sabre->pbm_A.pci_first_busno)
+ pdev_cookie_fillin(&sabre->pbm_A, pbus->self);
+ else if (pbus->number == sabre->pbm_B.pci_first_busno)
+ pdev_cookie_fillin(&sabre->pbm_B, pbus->self);
+ }
}
/* Walk PROM device tree under PBM, looking for 'assigned-address'
* properties, and recording them in pci_vma's linked in via
* PBM->assignments.
*/
-static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
+__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs))
{
struct linux_prom_ebus_ranges erng[PROMREG_MAX];
int err, iter;
@@ -632,18 +892,26 @@ static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *are
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;
}
return err;
}
-static void assignment_process(struct linux_pbm_info *pbm, int node)
+__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node))
{
struct linux_prom_pci_registers aregs[PROMREG_MAX];
char pname[256];
int err, iter, numa;
err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname));
- if(strncmp(pname, "ebus", 4) == 0) {
+ if (err > 0)
+ pname[err] = 0;
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%s: %s\n", __FUNCTION__, err > 0 ? pname : "???");
+#endif
+ if(strcmp(pname, "ebus") == 0) {
numa = gimme_ebus_assignments(node, &aregs[0]);
} else {
err = prom_getproperty(node, "assigned-addresses",
@@ -653,7 +921,7 @@ static void assignment_process(struct linux_pbm_info *pbm, int node)
if(err == 0 || err == -1)
return;
- numa = (err / sizeof(struct linux_prom_pci_ranges));
+ numa = (err / sizeof(struct linux_prom_pci_registers));
}
for(iter = 0; iter < numa; iter++) {
@@ -667,8 +935,6 @@ static void assignment_process(struct linux_pbm_info *pbm, int node)
io = (space == 1);
breg = (ap->phys_hi & 0xff);
- if(breg == PCI_ROM_ADDRESS)
- continue;
vp = pci_vma_alloc();
@@ -677,21 +943,20 @@ static void assignment_process(struct linux_pbm_info *pbm, int node)
* XXX either due to it's layout so...
*/
vp->start = ap->phys_lo;
- vp->end = vp->start + ap->size_lo;
- vp->base_reg = breg;
-
- /* Sanity */
- if(io && (vp->end & ~(0xffff))) {
- prom_printf("assignment_process: Out of range PCI I/O "
- "[%08lx:%08lx]\n", vp->start, vp->end);
- prom_halt();
- }
+ vp->end = vp->start + ap->size_lo - 1;
+ vp->offset = (ap->phys_hi & 0xffffff);
pci_add_vma(pbm, vp, io);
+
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%s: BaseReg %02x", pname, breg);
+ dprintf(" %s vma [%08x,%08x]\n",
+ io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end);
+#endif
}
}
-static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
+__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node))
{
while(node) {
int child = prom_getchild(node);
@@ -704,17 +969,112 @@ static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
}
}
-static void record_assignments(struct linux_pbm_info *pbm)
+static inline void record_assignments(struct linux_pbm_info *pbm)
{
+ struct pci_vma *vp;
+
+ if (pbm->parent->pci_bus) {
+ /*
+ * Disallow anything that is not in our IO/MEM map on SIMBA.
+ */
+ struct pci_bus *pbus = pbm->parent->pci_bus;
+ struct pci_dev *pdev;
+ unsigned char map;
+ int bit;
+
+ for (pdev = pbus->devices; pdev; pdev = pdev->sibling) {
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ if (!pcp) {
+ prom_printf("record_assignments: "
+ "no pcidev_cookie for pdev %02x\n",
+ pdev->devfn);
+ prom_halt();
+ }
+ if (pcp->pbm == pbm)
+ break;
+ }
+
+ if (!pdev) {
+ prom_printf("record_assignments: no pdev for PBM\n");
+ prom_halt();
+ }
+
+ pci_read_config_byte(pdev, APB_IO_ADDRESS_MAP, &map);
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%s: IO %02x\n", __FUNCTION__, map);
+#endif
+ for (bit = 0; bit < 8; bit++) {
+ if (!(map & (1 << bit))) {
+ vp = pci_vma_alloc();
+ vp->start = (bit << 21);
+ vp->end = vp->start + (1 << 21) - 1;
+ vp->offset = 0;
+ pci_add_vma(pbm, vp, 1);
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%s: IO prealloc vma [%08x,%08x]\n",
+ __FUNCTION__, vp->start, vp->end);
+#endif
+ }
+ }
+ pci_read_config_byte(pdev, APB_MEM_ADDRESS_MAP, &map);
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%s: MEM %02x\n", __FUNCTION__, map);
+#endif
+ for (bit = 0; bit < 8; bit++) {
+ if (!(map & (1 << bit))) {
+ vp = pci_vma_alloc();
+ vp->start = (bit << 29);
+ vp->end = vp->start + (1 << 29) - 1;
+ vp->offset = 0;
+ pci_add_vma(pbm, vp, 0);
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%s: MEM prealloc vma [%08x,%08x]\n",
+ __FUNCTION__, vp->start, vp->end);
+#endif
+ }
+ }
+ }
+
assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node));
+
+ /*
+ * Protect ISA IO space from being used.
+ */
+ vp = pci_find_vma(pbm, 0, 0, 1);
+ if (!vp || 0x400 <= vp->start) {
+ vp = pci_vma_alloc();
+ vp->start = 0;
+ vp->end = vp->start + 0x400 - 1;
+ vp->offset = 0;
+ pci_add_vma(pbm, vp, 1);
+ }
+
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("PROM IO assignments for PBM %s:\n",
+ pbm == &pbm->parent->pbm_A ? "A" : "B");
+ vp = pbm->IO_assignments;
+ while (vp) {
+ dprintf(" [%08x,%08x] (%s)\n", vp->start, vp->end,
+ vp->offset ? "Register" : "Unmapped");
+ vp = vp->next;
+ }
+ dprintf("PROM MEM assignments for PBM %s:\n",
+ pbm == &pbm->parent->pbm_A ? "A" : "B");
+ vp = pbm->MEM_assignments;
+ while (vp) {
+ dprintf(" [%08x,%08x] (%s)\n", vp->start, vp->end,
+ vp->offset ? "Register" : "Unmapped");
+ vp = vp->next;
+ }
+#endif
}
-static void fixup_regs(struct pci_dev *pdev,
- struct linux_pbm_info *pbm,
- struct linux_prom_pci_registers *pregs,
- int nregs,
- struct linux_prom_pci_registers *assigned,
- int numaa)
+__initfunc(static void fixup_regs(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ struct linux_prom_pci_registers *pregs,
+ int nregs,
+ struct linux_prom_pci_registers *assigned,
+ int numaa))
{
int preg, rng;
int IO_seen = 0;
@@ -724,34 +1084,39 @@ static void fixup_regs(struct pci_dev *pdev,
struct linux_prom_pci_registers *ap = NULL;
int bustype = (pregs[preg].phys_hi >> 24) & 0x3;
int bsreg, brindex;
+ unsigned int rtmp;
u64 pci_addr;
if(bustype == 0) {
/* Config space cookie, nothing to do. */
if(preg != 0)
- printk("%s: strange, config space not 0\n",
- __FUNCTION__);
+ printk("%s %02x.%02x [%04x,%04x]: "
+ "strange, config space not 0\n",
+ __FUNCTION__,
+ pdev->bus->number, pdev->devfn,
+ pdev->vendor, pdev->device);
continue;
} else if(bustype == 3) {
/* XXX add support for this... */
- printk("%s: Warning, ignoring 64-bit PCI memory space, "
+ printk("%s %02x.%02x [%04x,%04x]: "
+ "Warning, ignoring 64-bit PCI memory space, "
"tell Eddie C. Dost (ecd@skynet.be).\n",
- __FUNCTION__);
+ __FUNCTION__,
+ pdev->bus->number, pdev->devfn,
+ pdev->vendor, pdev->device);
continue;
}
- bsreg = (pregs[preg].phys_hi & 0xff);
- /* We can safely ignore these. */
- if(bsreg == PCI_ROM_ADDRESS)
- continue;
+ bsreg = (pregs[preg].phys_hi & 0xff);
/* Sanity */
if((bsreg < PCI_BASE_ADDRESS_0) ||
- (bsreg > (PCI_BASE_ADDRESS_5 + 4)) ||
+ ((bsreg > (PCI_BASE_ADDRESS_5 + 4)) && (bsreg != PCI_ROM_ADDRESS)) ||
(bsreg & 3)) {
- printk("%s: [%04x:%04x]: "
+ printk("%s %02x.%02x [%04x:%04x]: "
"Warning, ignoring bogus basereg [%x]\n",
- __FUNCTION__, pdev->vendor, pdev->device, bsreg);
+ __FUNCTION__, pdev->bus->number, pdev->devfn,
+ pdev->vendor, pdev->device, bsreg);
printk(" PROM reg: %08x.%08x.%08x %08x.%08x\n",
pregs[preg].phys_hi, pregs[preg].phys_mid,
pregs[preg].phys_lo, pregs[preg].size_hi,
@@ -798,7 +1163,16 @@ static void fixup_regs(struct pci_dev *pdev,
/* AIEEE */
prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n");
}
- pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
+ if (bsreg == PCI_ROM_ADDRESS) {
+ pdev->rom_address = (unsigned long)__va(pci_addr);
+ pdev->rom_address |= 1;
+ /*
+ * Enable access to the ROM.
+ */
+ pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp);
+ pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1);
+ } else
+ pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
/* Preserve I/O space bit. */
if(bustype == 0x1) {
@@ -811,15 +1185,18 @@ static void fixup_regs(struct pci_dev *pdev,
/* Now handle assignments PROM did not take care of. */
if(nregs) {
+ unsigned int rtmp, ridx;
+ unsigned int offset, base;
+ struct pci_vma *vp;
+ u64 pci_addr;
int breg;
for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) {
- unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
- unsigned int base = (unsigned int)pdev->base_address[ridx];
- struct pci_vma *vp;
- u64 pci_addr;
int io;
+ ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
+ base = (unsigned int)pdev->base_address[ridx];
+
if(pdev->base_address[ridx] > PAGE_OFFSET)
continue;
@@ -827,19 +1204,14 @@ static void fixup_regs(struct pci_dev *pdev,
base &= ~((io ?
PCI_BASE_ADDRESS_IO_MASK :
PCI_BASE_ADDRESS_MEM_MASK));
- vp = pci_find_vma(pbm, base, io);
+ offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
+ vp = pci_find_vma(pbm, base, offset, io);
if(!vp || vp->start > base) {
unsigned int size, new_base;
- pcibios_read_config_dword(pdev->bus->number,
- pdev->devfn,
- breg, &rtmp);
- pcibios_write_config_dword(pdev->bus->number,
- pdev->devfn,
- breg, 0xffffffff);
- pcibios_read_config_dword(pdev->bus->number,
- pdev->devfn,
- breg, &size);
+ pci_read_config_dword(pdev, breg, &rtmp);
+ pci_write_config_dword(pdev, breg, 0xffffffff);
+ pci_read_config_dword(pdev, breg, &size);
if(io)
size &= ~1;
size = (~(size) + 1);
@@ -847,7 +1219,8 @@ static void fixup_regs(struct pci_dev *pdev,
continue;
new_base = 0;
- for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) {
+ for(vp = pci_find_vma(pbm, new_base, 0, io); ;
+ vp = vp->next) {
if(!vp || new_base + size <= vp->start)
break;
new_base = (vp->end + (size - 1)) & ~(size-1);
@@ -859,26 +1232,27 @@ static void fixup_regs(struct pci_dev *pdev,
}
vp = pci_vma_alloc();
vp->start = new_base;
- vp->end = vp->start + size;
- vp->base_reg = breg;
-
- /* Sanity */
- if(io && vp->end & ~(0xffff)) {
- prom_printf("PCI: Out of range PCI I/O "
- "[%08lx:%08lx] during fixup\n",
- vp->start, vp->end);
- prom_halt();
- }
+ vp->end = vp->start + size - 1;
+ vp->offset = offset;
+
pci_add_vma(pbm, vp, io);
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%02x.%02x.%x: BaseReg %02x",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn),
+ breg);
+ dprintf(" %s vma [%08x,%08x]\n",
+ io ? "I/O" : breg == PCI_ROM_ADDRESS ? "ROM" : "MEM", vp->start, vp->end);
+#endif
rtmp = new_base;
+ pci_read_config_dword(pdev, breg, &base);
if(io)
- rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK);
+ rtmp |= (base & ~PCI_BASE_ADDRESS_IO_MASK);
else
- rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK);
- pcibios_write_config_dword(pdev->bus->number,
- pdev->devfn,
- breg, rtmp);
+ rtmp |= (base & ~PCI_BASE_ADDRESS_MEM_MASK);
+ pci_write_config_dword(pdev, breg, rtmp);
/* Apply PBM ranges and update pci_dev. */
pci_addr = new_base;
@@ -912,13 +1286,93 @@ static void fixup_regs(struct pci_dev *pdev,
}
}
}
+
+ /*
+ * Handle PCI_ROM_ADDRESS.
+ */
+ breg = PCI_ROM_ADDRESS;
+ base = (unsigned int)pdev->rom_address;
+
+ if(pdev->rom_address > PAGE_OFFSET)
+ goto rom_address_done;
+
+ base &= PCI_ROM_ADDRESS_MASK;
+ offset = (pdev->bus->number << 16) | (pdev->devfn << 8) | breg;
+ vp = pci_find_vma(pbm, base, offset, 0);
+ if(!vp || vp->start > base) {
+ unsigned int size, new_base;
+
+ pci_read_config_dword(pdev, breg, &rtmp);
+ pci_write_config_dword(pdev, breg, 0xffffffff);
+ pci_read_config_dword(pdev, breg, &size);
+ size &= ~1;
+ size = (~(size) + 1);
+ if(!size)
+ goto rom_address_done;
+
+ new_base = 0;
+ for(vp = pci_find_vma(pbm, new_base, 0, 0); ; vp = vp->next) {
+ if(!vp || new_base + size <= vp->start)
+ break;
+ new_base = (vp->end + (size - 1)) & ~(size-1);
+ }
+ if(vp && (new_base + size > vp->start)) {
+ prom_printf("PCI: Impossible full MEM space.\n");
+ prom_halt();
+ }
+ vp = pci_vma_alloc();
+ vp->start = new_base;
+ vp->end = vp->start + size - 1;
+ vp->offset = offset;
+
+ pci_add_vma(pbm, vp, 0);
+
+#ifdef FIXUP_VMA_DEBUG
+ dprintf("%02x.%02x.%x: BaseReg %02x",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn),
+ breg);
+ dprintf(" %s vma [%08x,%08x]\n",
+ "ROM", vp->start, vp->end);
+#endif
+
+ rtmp = new_base;
+ pci_read_config_dword(pdev, breg, &base);
+ rtmp |= (base & ~PCI_ROM_ADDRESS_MASK);
+ pci_write_config_dword(pdev, breg, rtmp);
+
+ /* Apply PBM ranges and update pci_dev. */
+ pci_addr = new_base;
+ for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+ struct linux_prom_pci_ranges *rp;
+ int rspace;
+
+ rp = &pbm->pbm_ranges[rng];
+ rspace = (rp->child_phys_hi >> 24) & 3;
+ if(rspace != 2)
+ continue;
+ pci_addr += ((u64)rp->parent_phys_lo);
+ pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+ break;
+ }
+ if(rng == pbm->num_pbm_ranges) {
+ /* AIEEE */
+ prom_printf("fixup_doit: YIEEE, cannot find "
+ "PBM ranges\n");
+ }
+ pdev->rom_address = (unsigned long)__va(pci_addr);
+
+ pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK);
+ MEM_seen = 1;
+ }
+ rom_address_done:
+
}
if(IO_seen || MEM_seen) {
unsigned int l;
- pcibios_read_config_dword(pdev->bus->number,
- pdev->devfn,
- PCI_COMMAND, &l);
+ pci_read_config_dword(pdev, PCI_COMMAND, &l);
#ifdef FIXUP_REGS_DEBUG
dprintf("[");
#endif
@@ -937,9 +1391,7 @@ static void fixup_regs(struct pci_dev *pdev,
#ifdef FIXUP_REGS_DEBUG
dprintf("]");
#endif
- pcibios_write_config_dword(pdev->bus->number,
- pdev->devfn,
- PCI_COMMAND, l);
+ pci_write_config_dword(pdev, PCI_COMMAND, l);
}
#ifdef FIXUP_REGS_DEBUG
@@ -955,7 +1407,7 @@ static void fixup_regs(struct pci_dev *pdev,
#define imap_offset(__member) \
((unsigned long)(&(((struct psycho_regs *)0)->__member)))
-static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
{
unsigned int bus, slot;
@@ -963,17 +1415,20 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
slot = (ino & 0x0c) >> 2;
if(bus == 0) {
- /* Perform a sanity check, we might as well.
- * PBM A only has 2 PCI slots.
- */
- if(slot > 1) {
- prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot);
- prom_halt();
- }
- if(slot == 0)
+ switch(slot) {
+ case 0:
return imap_offset(imap_a_slot0);
- else
+ case 1:
return imap_offset(imap_a_slot1);
+ case 2:
+ return imap_offset(imap_a_slot2);
+ case 3:
+ return imap_offset(imap_a_slot3);
+ default:
+ prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
+ bus, slot);
+ prom_halt();
+ }
} else {
switch(slot) {
case 0:
@@ -988,13 +1443,12 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
bus, slot);
prom_halt();
- return 0; /* Make gcc happy */
- };
+ }
}
}
/* Exported for EBUS probing layer. */
-unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)
+__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino))
{
unsigned long imap_off, ign, ino;
@@ -1089,9 +1543,62 @@ unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino)
return pci_irq_encode(imap_off, pbm->parent->index, ign, ino);
}
-static void fixup_irq(struct pci_dev *pdev,
- struct linux_pbm_info *pbm,
- int node)
+__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev,
+ struct linux_prom_pci_registers *preg,
+ unsigned int *interrupt))
+{
+ struct linux_prom_pci_registers ppreg;
+ unsigned int hi, mid, lo, irq;
+ int i;
+
+ if (!pbm->num_pbm_intmap)
+ return 0;
+
+ /*
+ * Underneath a bridge, use register of parent bridge.
+ */
+ if (pdev->bus->number != pbm->pci_first_busno) {
+ struct pcidev_cookie *pcp = pdev->bus->self->sysdata;
+ int node;
+
+ if (!pcp)
+ goto out;
+
+ node = pcp->prom_node;
+
+ i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg));
+ if(i == 0 || i == -1)
+ goto out;
+
+ preg = &ppreg;
+ }
+
+ hi = preg->phys_hi & pbm->pbm_intmask.phys_hi;
+ mid = preg->phys_mid & pbm->pbm_intmask.phys_mid;
+ lo = preg->phys_lo & pbm->pbm_intmask.phys_lo;
+ irq = *interrupt & pbm->pbm_intmask.interrupt;
+ for (i = 0; i < pbm->num_pbm_intmap; i++) {
+ if ((pbm->pbm_intmap[i].phys_hi == hi) &&
+ (pbm->pbm_intmap[i].phys_mid == mid) &&
+ (pbm->pbm_intmap[i].phys_lo == lo) &&
+ (pbm->pbm_intmap[i].interrupt == irq)) {
+ *interrupt = pbm->pbm_intmap[i].cinterrupt;
+ return *interrupt;
+ }
+ }
+
+out:
+ prom_printf("pbm_intmap_match: IRQ [%08x.%08x.%08x.%08x] "
+ "not found in interrupt-map\n", preg->phys_hi,
+ preg->phys_mid, preg->phys_lo, *interrupt);
+ prom_halt();
+}
+
+__initfunc(static void fixup_irq(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ struct linux_prom_pci_registers *preg,
+ int node))
{
unsigned int prom_irq, portid = pbm->parent->upa_portid;
unsigned char pci_irq_line = pdev->irq;
@@ -1102,13 +1609,24 @@ static void fixup_irq(struct pci_dev *pdev,
#endif
err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq));
if(err == 0 || err == -1) {
- prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n",
- pdev->vendor, pdev->device);
- prom_halt();
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("No interrupts property.\n");
+#endif
+ pdev->irq = 0;
+ return;
}
+ /* See if we find a matching interrupt-map entry. */
+ if (pbm_intmap_match(pbm, pdev, preg, &prom_irq)) {
+ pdev->irq = psycho_irq_build(pbm,
+ (pbm->parent->upa_portid << 6)
+ | prom_irq);
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("interrupt-map specified prom_irq[%x] pdev->irq[%x]",
+ prom_irq, pdev->irq);
+#endif
/* See if fully specified already (ie. for onboard devices like hme) */
- if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
+ } else if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
pdev->irq = psycho_irq_build(pbm, prom_irq);
#ifdef FIXUP_IRQ_DEBUG
dprintf("fully specified prom_irq[%x] pdev->irq[%x]",
@@ -1147,7 +1665,7 @@ static void fixup_irq(struct pci_dev *pdev,
slot = (pdev->bus->self->devfn >> 3) - 2;
/* Use low slot number bits of child as IRQ line. */
- line = (line + ((pdev->devfn >> 3) - 4)) % 4;
+ line = (pdev->devfn >> 3) & 0x03;
}
slot = (slot << 2);
@@ -1159,16 +1677,10 @@ static void fixup_irq(struct pci_dev *pdev,
do {
unsigned char iline, ipin;
- (void)pcibios_read_config_byte(pdev->bus->number,
- pdev->devfn,
- PCI_INTERRUPT_PIN,
- &ipin);
- (void)pcibios_read_config_byte(pdev->bus->number,
- pdev->devfn,
- PCI_INTERRUPT_LINE,
- &iline);
- dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] "
- "iline[%x] ipin[%x] prom_irq[%x]",
+ pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &ipin);
+ pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &iline);
+ dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] "
+ "irq[%x] iline[%x] ipin[%x] prom_irq[%x]",
portid, bus>>4, slot>>2, line, pdev->irq,
iline, ipin, prom_irq);
} while(0);
@@ -1178,21 +1690,19 @@ static void fixup_irq(struct pci_dev *pdev,
/*
* Write the INO to config space PCI_INTERRUPT_LINE.
*/
- (void)pcibios_write_config_byte(pdev->bus->number,
- pdev->devfn,
- PCI_INTERRUPT_LINE,
- pdev->irq & PCI_IRQ_INO);
+ pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
+ pdev->irq & PCI_IRQ_INO);
#ifdef FIXUP_IRQ_DEBUG
dprintf("\n");
#endif
}
-static void fixup_doit(struct pci_dev *pdev,
- struct linux_pbm_info *pbm,
- struct linux_prom_pci_registers *pregs,
- int nregs,
- int node)
+__initfunc(static void fixup_doit(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ struct linux_prom_pci_registers *pregs,
+ int nregs,
+ int node))
{
struct linux_prom_pci_registers assigned[PROMREG_MAX];
int numaa, err;
@@ -1209,12 +1719,12 @@ static void fixup_doit(struct pci_dev *pdev,
fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa);
/* Next, fixup interrupt numbers. */
- fixup_irq(pdev, pbm, node);
+ fixup_irq(pdev, pbm, &pregs[0], node);
}
-static void fixup_pci_dev(struct pci_dev *pdev,
- struct pci_bus *pbus,
- struct linux_pbm_info *pbm)
+__initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
+ struct pci_bus *pbus,
+ struct linux_pbm_info *pbm))
{
struct linux_prom_pci_registers pregs[PROMREG_MAX];
struct pcidev_cookie *pcp = pdev->sysdata;
@@ -1225,18 +1735,12 @@ static void fixup_pci_dev(struct pci_dev *pdev,
unsigned short cmd;
/* First, enable bus mastering. */
- pcibios_read_config_word(pdev->bus->number,
- pdev->devfn,
- PCI_COMMAND, &cmd);
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MASTER;
- pcibios_write_config_word(pdev->bus->number,
- pdev->devfn,
- PCI_COMMAND, cmd);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
/* Now, set cache line size to 64-bytes. */
- pcibios_write_config_byte(pdev->bus->number,
- pdev->devfn,
- PCI_CACHE_LINE_SIZE, 64);
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64);
}
/* Ignore if this is one of the PBM's, EBUS, or a
@@ -1246,8 +1750,17 @@ static void fixup_pci_dev(struct pci_dev *pdev,
if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) ||
(pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) ||
(pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
- (pcp == NULL))
+ (pcp == NULL)) {
+ /*
+ * Prevent access to PCI_ROM_ADDRESS, in case present
+ * as we don't fixup the address.
+ */
+ if (pdev->rom_address) {
+ pci_write_config_dword(pdev, PCI_ROM_ADDRESS, 0);
+ pdev->rom_address = 0;
+ }
return;
+ }
node = pcp->prom_node;
@@ -1260,20 +1773,30 @@ static void fixup_pci_dev(struct pci_dev *pdev,
nregs = (err / sizeof(pregs[0]));
fixup_doit(pdev, pbm, &pregs[0], nregs, node);
+
+ /* Enable bus mastering on IDE interfaces. */
+ if ((pdev->class >> 8 == PCI_CLASS_STORAGE_IDE)
+ && (pdev->class & 0x80)) {
+ unsigned short cmd;
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ }
}
-static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
+__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm))
{
struct pci_dev *pdev;
for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
fixup_pci_dev(pdev, pbus, pbm);
- for(pbus = pbus->children; pbus; pbus = pbus->children)
+ for(pbus = pbus->children; pbus; pbus = pbus->next)
fixup_pci_bus(pbus, pbm);
}
-static void fixup_addr_irq(struct linux_pbm_info *pbm)
+__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm))
{
struct pci_bus *pbus = &pbm->pci_bus;
@@ -1286,14 +1809,16 @@ static void fixup_addr_irq(struct linux_pbm_info *pbm)
/* Walk all PCI devices probes, fixing up base registers and IRQ registers.
* We use OBP for most of this work.
*/
-static void psycho_final_fixup(struct linux_psycho *psycho)
+__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho))
{
/* Second, fixup base address registers and IRQ lines... */
- fixup_addr_irq(&psycho->pbm_A);
- fixup_addr_irq(&psycho->pbm_B);
+ if (psycho->pbm_A.parent)
+ fixup_addr_irq(&psycho->pbm_A);
+ if (psycho->pbm_B.parent)
+ fixup_addr_irq(&psycho->pbm_B);
}
-unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)
+__initfunc(unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end))
{
struct linux_psycho *psycho;
@@ -1312,11 +1837,16 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end
*/
for (psycho = psycho_root; psycho; psycho = psycho->next) {
- /* Probe busses under PBM B. */
- pbm_probe(&psycho->pbm_B, &memory_start);
-
- /* Probe busses under PBM A. */
- pbm_probe(&psycho->pbm_A, &memory_start);
+ /* Probe bus on builtin PCI. */
+ if (psycho->pci_bus)
+ sabre_probe(psycho, &memory_start);
+ else {
+ /* Probe busses under PBM B. */
+ pbm_probe(&psycho->pbm_B, &memory_start);
+
+ /* Probe busses under PBM A. */
+ pbm_probe(&psycho->pbm_A, &memory_start);
+ }
}
pci_init_alloc_init(&memory_start);
@@ -1327,8 +1857,13 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end
* a pci_dev cookie (PBM+PROM_NODE, for pci_dev's).
*/
for (psycho = psycho_root; psycho; psycho = psycho->next) {
- fill_in_pbm_cookies(&psycho->pbm_A);
- fill_in_pbm_cookies(&psycho->pbm_B);
+ if (psycho->pci_bus)
+ sabre_cookie_fillin(psycho);
+
+ fill_in_pbm_cookies(&psycho->pbm_A.pci_bus,
+ &psycho->pbm_A);
+ fill_in_pbm_cookies(&psycho->pbm_B.pci_bus,
+ &psycho->pbm_B);
/* See what OBP has taken care of already. */
record_assignments(&psycho->pbm_A);
@@ -1373,11 +1908,157 @@ pci_mkaddr(struct linux_pbm_info *pbm, unsigned char bus,
static inline int
out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn)
{
- return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) ||
- ((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) ||
+ return ((pbm->parent == 0) ||
+ ((pbm == &pbm->parent->pbm_B) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 4) ||
+ ((pbm == &pbm->parent->pbm_A) && (bus == pbm->pci_first_busno) && PCI_SLOT(devfn) > 6) ||
(pci_probe_enable == 0));
}
+static inline int
+sabre_out_of_range(unsigned char devfn)
+{
+ return ((PCI_SLOT(devfn) == 0) && (PCI_FUNC(devfn) > 0)) ||
+ ((PCI_SLOT(devfn) == 1) && (PCI_FUNC(devfn) > 1)) ||
+ (PCI_SLOT(devfn) > 1);
+}
+
+static int
+sabre_read_config_byte(struct linux_pbm_info *pbm,
+ unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned char *value)
+{
+ if (bus)
+ return pbm_read_config_byte(pbm, bus, devfn, where, value);
+
+ if (sabre_out_of_range(devfn)) {
+ *value = 0xff;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (where < 8) {
+ unsigned short tmp;
+
+ pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp);
+ if (where & 1)
+ *value = tmp >> 8;
+ else
+ *value = tmp & 0xff;
+ return PCIBIOS_SUCCESSFUL;
+ } else
+ return pbm_read_config_byte(pbm, bus, devfn, where, value);
+}
+
+static int
+sabre_read_config_word(struct linux_pbm_info *pbm,
+ unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned short *value)
+{
+ if (bus)
+ return pbm_read_config_word(pbm, bus, devfn, where, value);
+
+ if (sabre_out_of_range(devfn)) {
+ *value = 0xffff;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (where < 8)
+ return pbm_read_config_word(pbm, bus, devfn, where, value);
+ else {
+ unsigned char tmp;
+
+ pbm_read_config_byte(pbm, bus, devfn, where, &tmp);
+ *value = tmp;
+ pbm_read_config_byte(pbm, bus, devfn, where + 1, &tmp);
+ *value |= tmp << 8;
+ return PCIBIOS_SUCCESSFUL;
+ }
+}
+
+static int
+sabre_read_config_dword(struct linux_pbm_info *pbm,
+ unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned int *value)
+{
+ unsigned short tmp;
+
+ if (bus)
+ return pbm_read_config_dword(pbm, bus, devfn, where, value);
+
+ if (sabre_out_of_range(devfn)) {
+ *value = 0xffffffff;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ sabre_read_config_word(pbm, bus, devfn, where, &tmp);
+ *value = tmp;
+ sabre_read_config_word(pbm, bus, devfn, where + 2, &tmp);
+ *value |= tmp << 16;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+sabre_write_config_byte(struct linux_pbm_info *pbm,
+ unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned char value)
+{
+ if (bus)
+ return pbm_write_config_byte(pbm, bus, devfn, where, value);
+
+ if (sabre_out_of_range(devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ if (where < 8) {
+ unsigned short tmp;
+
+ pbm_read_config_word(pbm, bus, devfn, where & ~1, &tmp);
+ if (where & 1) {
+ value &= 0x00ff;
+ value |= tmp << 8;
+ } else {
+ value &= 0xff00;
+ value |= tmp;
+ }
+ return pbm_write_config_word(pbm, bus, devfn, where & ~1, tmp);
+ } else
+ return pbm_write_config_byte(pbm, bus, devfn, where, value);
+}
+
+static int
+sabre_write_config_word(struct linux_pbm_info *pbm,
+ unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned short value)
+{
+ if (bus)
+ return pbm_write_config_word(pbm, bus, devfn, where, value);
+
+ if (sabre_out_of_range(devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ if (where < 8)
+ return pbm_write_config_word(pbm, bus, devfn, where, value);
+ else {
+ pbm_write_config_byte(pbm, bus, devfn, where, value & 0xff);
+ pbm_write_config_byte(pbm, bus, devfn, where + 1, value >> 8);
+ return PCIBIOS_SUCCESSFUL;
+ }
+}
+
+static int
+sabre_write_config_dword(struct linux_pbm_info *pbm,
+ unsigned char bus, unsigned char devfn,
+ unsigned char where, unsigned int value)
+{
+ if (bus)
+ return pbm_write_config_dword(pbm, bus, devfn, where, value);
+
+ if (sabre_out_of_range(devfn))
+ return PCIBIOS_SUCCESSFUL;
+
+ sabre_write_config_word(pbm, bus, devfn, where, value & 0xffff);
+ sabre_write_config_word(pbm, bus, devfn, where + 2, value >> 16);
+ return PCIBIOS_SUCCESSFUL;
+}
+
static int
pbm_read_config_byte(struct linux_pbm_info *pbm,
unsigned char bus, unsigned char devfn,
@@ -1574,36 +2255,60 @@ pbm_write_config_dword(struct linux_pbm_info *pbm,
int pcibios_read_config_byte (unsigned char bus, unsigned char devfn,
unsigned char where, unsigned char *value)
{
- return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value);
+ struct linux_pbm_info *pbm = bus2pbm[bus];
+
+ if (pbm && pbm->parent && pbm->parent->pci_bus)
+ return sabre_read_config_byte(pbm, bus, devfn, where, value);
+ return pbm_read_config_byte(pbm, bus, devfn, where, value);
}
int pcibios_read_config_word (unsigned char bus, unsigned char devfn,
unsigned char where, unsigned short *value)
{
- return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value);
+ struct linux_pbm_info *pbm = bus2pbm[bus];
+
+ if (pbm && pbm->parent && pbm->parent->pci_bus)
+ return sabre_read_config_word(pbm, bus, devfn, where, value);
+ return pbm_read_config_word(pbm, bus, devfn, where, value);
}
int pcibios_read_config_dword (unsigned char bus, unsigned char devfn,
unsigned char where, unsigned int *value)
{
- return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value);
+ struct linux_pbm_info *pbm = bus2pbm[bus];
+
+ if (pbm && pbm->parent && pbm->parent->pci_bus)
+ return sabre_read_config_dword(pbm, bus, devfn, where, value);
+ return pbm_read_config_dword(pbm, bus, devfn, where, value);
}
int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
unsigned char where, unsigned char value)
{
- return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value);
+ struct linux_pbm_info *pbm = bus2pbm[bus];
+
+ if (pbm && pbm->parent && pbm->parent->pci_bus)
+ return sabre_write_config_byte(pbm, bus, devfn, where, value);
+ return pbm_write_config_byte(pbm, bus, devfn, where, value);
}
int pcibios_write_config_word (unsigned char bus, unsigned char devfn,
unsigned char where, unsigned short value)
{
+ struct linux_pbm_info *pbm = bus2pbm[bus];
+
+ if (pbm && pbm->parent && pbm->parent->pci_bus)
+ return sabre_write_config_word(pbm, bus, devfn, where, value);
return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value);
}
int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
unsigned char where, unsigned int value)
{
+ struct linux_pbm_info *pbm = bus2pbm[bus];
+
+ if (pbm && pbm->parent && pbm->parent->pci_bus)
+ return sabre_write_config_dword(pbm, bus, devfn, where, value);
return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value);
}
@@ -1692,4 +2397,9 @@ asmlinkage int sys_pciconfig_write(unsigned long bus,
return err;
}
+__initfunc(char *pcibios_setup(char *str))
+{
+ return str;
+}
+
#endif
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c
index 932addbb4..cdc8f47de 100644
--- a/arch/sparc64/kernel/setup.c
+++ b/arch/sparc64/kernel/setup.c
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.18 1997/12/18 02:43:00 ecd Exp $
+/* $Id: setup.c,v 1.20 1998/02/24 17:02:39 jj Exp $
* linux/arch/sparc64/kernel/setup.c
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
@@ -55,11 +55,6 @@ struct screen_info screen_info = {
unsigned int phys_bytes_of_ram, end_of_phys_memory;
-unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
-{
- return memory_start;
-}
-
/* Typing sync at the prom prompt calls the function pointed to by
* the sync callback which I set to the following function.
* This should sync all filesystems and return, for now it just
@@ -413,14 +408,17 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
extern char *sparc_cpu_type[];
extern char *sparc_fpu_type[];
-extern char *smp_info(void);
-extern char *mmu_info(void);
+extern int smp_info(char *);
+extern int smp_bogo(char *);
+extern int mmu_info(char *);
int get_cpuinfo(char *buffer)
{
int cpuid=smp_processor_id();
+ int len;
- return sprintf(buffer, "cpu\t\t: %s\n"
+ len = sprintf(buffer,
+ "cpu\t\t: %s\n"
"fpu\t\t: %s\n"
"promlib\t\t: Version 3 Revision %d\n"
"prom\t\t: %d.%d.%d\n"
@@ -429,33 +427,22 @@ int get_cpuinfo(char *buffer)
"ncpus active\t: %d\n"
#ifndef __SMP__
"BogoMips\t: %lu.%02lu\n"
-#else
- "Cpu0Bogo\t: %lu.%02lu\n"
- "Cpu1Bogo\t: %lu.%02lu\n"
- "Cpu2Bogo\t: %lu.%02lu\n"
- "Cpu3Bogo\t: %lu.%02lu\n"
-#endif
- "%s"
-#ifdef __SMP__
- "%s"
#endif
,
sparc_cpu_type[cpuid],
sparc_fpu_type[cpuid],
prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff,
- linux_num_cpus, smp_num_cpus,
+ linux_num_cpus, smp_num_cpus
#ifndef __SMP__
- loops_per_sec/500000, (loops_per_sec/5000) % 100,
-#else
- cpu_data[0].udelay_val/500000, (cpu_data[0].udelay_val/5000)%100,
- cpu_data[1].udelay_val/500000, (cpu_data[1].udelay_val/5000)%100,
- cpu_data[2].udelay_val/500000, (cpu_data[2].udelay_val/5000)%100,
- cpu_data[3].udelay_val/500000, (cpu_data[3].udelay_val/5000)%100,
+ , loops_per_sec/500000, (loops_per_sec/5000) % 100
#endif
- mmu_info()
+ );
#ifdef __SMP__
- , smp_info()
+ len += smp_bogo(buffer + len);
#endif
- );
-
+ len += mmu_info(buffer + len);
+#ifdef __SMP__
+ len += smp_info(buffer + len);
+#endif
+ return len;
}
diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c
index 8ca15c80b..269ff413d 100644
--- a/arch/sparc64/kernel/signal32.c
+++ b/arch/sparc64/kernel/signal32.c
@@ -1,4 +1,4 @@
-/* $Id: signal32.c,v 1.34 1997/12/15 15:04:49 jj Exp $
+/* $Id: signal32.c,v 1.35 1998/04/01 07:00:43 davem Exp $
* arch/sparc64/kernel/signal32.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
@@ -1023,6 +1023,7 @@ static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka,
spin_lock_irq(&current->sigmask_lock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
sigaddset(&current->blocked,signr);
+ recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
}
}
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 932534c05..ca2aa4360 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
+#include <linux/init.h>
#include <asm/head.h>
#include <asm/ptrace.h>
@@ -25,12 +26,14 @@
#include <asm/hardirq.h>
#include <asm/softirq.h>
#include <asm/uaccess.h>
+#include <asm/timer.h>
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
extern int linux_num_cpus;
extern void calibrate_delay(void);
+extern unsigned prom_cpu_nodes[];
volatile int smp_processors_ready = 0;
unsigned long cpu_present_map = 0;
@@ -39,35 +42,46 @@ int smp_threads_ready = 0;
struct cpuinfo_sparc cpu_data[NR_CPUS] __attribute__ ((aligned (64)));
-static unsigned char boot_cpu_id = 0;
+static unsigned char boot_cpu_id __initdata = 0;
static int smp_activated = 0;
volatile int cpu_number_map[NR_CPUS];
-volatile int cpu_logical_map[NR_CPUS];
+volatile int __cpu_logical_map[NR_CPUS];
struct klock_info klock_info = { KLOCK_CLEAR, 0 };
-void smp_setup(char *str, int *ints)
+__initfunc(void smp_setup(char *str, int *ints))
{
/* XXX implement me XXX */
}
-static char smp_buf[512];
+int smp_info(char *buf)
+{
+ int len = 7, i;
+
+ strcpy(buf, "State:\n");
+ for (i = 0; i < NR_CPUS; i++)
+ if(cpu_present_map & (1UL << i))
+ len += sprintf(buf + len,
+ "CPU%d:\t\t%s\n",
+ i, klock_info.akp == i ? "akp" : "online");
+ return len;
+}
-char *smp_info(void)
+int smp_bogo(char *buf)
{
- /* XXX not SMP safe and need to support up to 64 penguins */
- sprintf(smp_buf,
-" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
-"State: %s\t\t%s\t\t%s\t\t%s\n",
-(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
-(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
-(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
-(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
- return smp_buf;
+ int len = 0, i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ if(cpu_present_map & (1UL << i))
+ len += sprintf(buf + len,
+ "Cpu%dBogo\t: %lu.%02lu\n",
+ i, cpu_data[i].udelay_val / 500000,
+ (cpu_data[i].udelay_val / 5000) % 100);
+ return len;
}
-void smp_store_cpu_info(int id)
+__initfunc(void smp_store_cpu_info(int id))
{
cpu_data[id].udelay_val = loops_per_sec;
cpu_data[id].irq_count = 0;
@@ -80,7 +94,7 @@ void smp_store_cpu_info(int id)
extern void distribute_irqs(void);
-void smp_commence(void)
+__initfunc(void smp_commence(void))
{
distribute_irqs();
}
@@ -92,7 +106,7 @@ static volatile unsigned long callin_flag = 0;
extern void inherit_locked_prom_mappings(int save_p);
extern void cpu_probe(void);
-void smp_callin(void)
+__initfunc(void smp_callin(void))
{
int cpuid = hard_smp_processor_id();
@@ -156,22 +170,24 @@ extern struct prom_cpuinfo linux_cpus[NR_CPUS];
extern unsigned long smp_trampoline;
-void smp_boot_cpus(void)
+__initfunc(void smp_boot_cpus(void))
{
int cpucount = 0, i;
printk("Entering UltraSMPenguin Mode...\n");
+ boot_cpu_id = hard_smp_processor_id();
smp_tickoffset_init();
__sti();
cpu_present_map = 0;
for(i = 0; i < linux_num_cpus; i++)
- cpu_present_map |= (1UL << i);
+ cpu_present_map |= (1UL << linux_cpus[i].mid);
for(i = 0; i < NR_CPUS; i++) {
cpu_number_map[i] = -1;
- cpu_logical_map[i] = -1;
+ __cpu_logical_map[i] = -1;
}
cpu_number_map[boot_cpu_id] = 0;
- cpu_logical_map[0] = boot_cpu_id;
+ prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node;
+ __cpu_logical_map[0] = boot_cpu_id;
klock_info.akp = boot_cpu_id;
current->processor = boot_cpu_id;
smp_store_cpu_info(boot_cpu_id);
@@ -188,13 +204,18 @@ void smp_boot_cpus(void)
unsigned long entry = (unsigned long)(&smp_trampoline);
struct task_struct *p;
int timeout;
+ int no;
+ extern unsigned long phys_base;
- entry -= KERNBASE;
+ entry += phys_base - KERNBASE;
kernel_thread(start_secondary, NULL, CLONE_PID);
p = task[++cpucount];
p->processor = i;
callin_flag = 0;
- prom_startcpu(linux_cpus[i].prom_node,
+ for (no = 0; no < linux_num_cpus; no++)
+ if (linux_cpus[no].mid == i)
+ break;
+ prom_startcpu(linux_cpus[no].prom_node,
entry, ((unsigned long)p));
for(timeout = 0; timeout < 5000000; timeout++) {
if(callin_flag)
@@ -202,8 +223,9 @@ void smp_boot_cpus(void)
udelay(100);
}
if(callin_flag) {
- cpu_number_map[i] = i;
- cpu_logical_map[i] = i;
+ cpu_number_map[i] = cpucount;
+ prom_cpu_nodes[i] = linux_cpus[no].prom_node;
+ __cpu_logical_map[cpucount] = i;
} else {
cpucount--;
printk("Processor %d is stuck.\n", i);
@@ -248,9 +270,9 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait)
/* #define XCALL_DEBUG */
-static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, int cpu)
+static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
{
- u64 result, target = (((unsigned long)linux_cpus[cpu].mid) << 14) | 0x70;
+ u64 result, target = (cpu << 14) | 0x70;
int stuck;
#ifdef XCALL_DEBUG
@@ -307,12 +329,15 @@ void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2)
if(smp_processors_ready) {
unsigned long mask = (cpu_present_map & ~(1UL<<smp_processor_id()));
u64 pstate, data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff));
- int i, ncpus = smp_num_cpus;
+ int i, ncpus = smp_num_cpus - 1;
__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
- for(i = 0; i < ncpus; i++) {
- if(mask & (1UL << i))
+ for(i = 0; i < NR_CPUS; i++) {
+ if(mask & (1UL << i)) {
xcall_deliver(data0, data1, data2, pstate, i);
+ ncpus--;
+ }
+ if (!ncpus) break;
}
/* NOTE: Caller runs local copy on master. */
}
@@ -489,13 +514,14 @@ static inline void sparc64_do_profile(unsigned long pc)
#endif
}
-static unsigned long real_tick_offset, current_tick_offset;
+static unsigned long current_tick_offset;
#define prof_multiplier(__cpu) cpu_data[(__cpu)].multiplier
#define prof_counter(__cpu) cpu_data[(__cpu)].counter
extern void update_one_process(struct task_struct *p, unsigned long ticks,
- unsigned long user, unsigned long system);
+ unsigned long user, unsigned long system,
+ int cpu);
void smp_percpu_timer_interrupt(struct pt_regs *regs)
{
@@ -503,32 +529,62 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs)
int cpu = smp_processor_id();
int user = user_mode(regs);
+ /*
+ * Check for level 14 softint.
+ */
+ if (!(get_softint() & (1UL << 0))) {
+ extern void handler_irq(int, struct pt_regs *);
+
+ handler_irq(14, regs);
+ return;
+ }
+
clear_softint((1UL << 0));
do {
if(!user)
sparc64_do_profile(regs->tpc);
if(!--prof_counter(cpu)) {
+
+ if (cpu == boot_cpu_id) {
+ extern void irq_enter(int, int);
+ extern void irq_exit(int, int);
+
+ irq_enter(cpu, 0);
+ kstat.irqs[cpu][0]++;
+
+ timer_tick_interrupt(regs);
+
+ irq_exit(cpu, 0);
+ }
+
if(current->pid) {
- unsigned int *inc_me;
+ unsigned int *inc, *inc2;
- update_one_process(current, 1, user, !user);
+ update_one_process(current, 1, user, !user, cpu);
if(--current->counter < 0) {
current->counter = 0;
need_resched = 1;
}
if(user) {
- if(current->priority < DEF_PRIORITY)
- inc_me = &kstat.cpu_nice;
- else
- inc_me = &kstat.cpu_user;
+ if(current->priority < DEF_PRIORITY) {
+ inc = &kstat.cpu_nice;
+ inc2 = &kstat.per_cpu_nice[cpu];
+ } else {
+ inc = &kstat.cpu_user;
+ inc2 = &kstat.per_cpu_user[cpu];
+ }
} else {
- inc_me = &kstat.cpu_system;
+ inc = &kstat.cpu_system;
+ inc2 = &kstat.per_cpu_system[cpu];
}
- atomic_inc((atomic_t *)inc_me);
+ atomic_inc((atomic_t *)inc);
+ atomic_inc((atomic_t *)inc2);
}
+
prof_counter(cpu) = prof_multiplier(cpu);
}
+
__asm__ __volatile__("rd %%tick_cmpr, %0\n\t"
"add %0, %2, %0\n\t"
"wr %0, 0x0, %%tick_cmpr\n\t"
@@ -538,12 +594,55 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs)
} while (tick >= compare);
}
-static void smp_setup_percpu_timer(void)
+__initfunc(static void smp_setup_percpu_timer(void))
{
int cpu = smp_processor_id();
prof_counter(cpu) = prof_multiplier(cpu) = 1;
+ if (cpu == boot_cpu_id) {
+ extern unsigned long tl0_itick;
+ extern unsigned long tl0_smp_itick;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ /*
+ * Steal TICK_INT interrupts from timer_interrupt().
+ */
+ __asm__ __volatile__("
+ .globl tl0_smp_itick
+ b,pt %%xcc, 1f
+ nop
+
+ tl0_smp_itick:
+ rdpr %%pil, %%g2
+ wrpr %%g0, 15, %%pil
+ b,pt %%xcc, etrap_irq
+ rd %%pc, %%g7
+ call smp_percpu_timer_interrupt
+ add %%sp, %0, %%o0
+ b,pt %%xcc, rtrap
+ clr %%l6
+
+ 1:"
+ : /* no outputs */
+ : "i" (STACK_BIAS + REGWIN_SZ));
+
+ memcpy(&tl0_itick, &tl0_smp_itick, 8 * 4);
+
+ __asm__ __volatile__("
+ membar #StoreStore
+ flush %0 + 0x00
+ flush %0 + 0x08
+ flush %0 + 0x10
+ flush %0 + 0x18"
+ : /* no outputs */
+ : "r" (&tl0_itick));
+
+ restore_flags(flags);
+ }
+
__asm__ __volatile__("rd %%tick, %%g1\n\t"
"add %%g1, %0, %%g1\n\t"
"wr %%g1, 0x0, %%tick_cmpr"
@@ -552,22 +651,17 @@ static void smp_setup_percpu_timer(void)
: "g1");
}
-static void smp_tickoffset_init(void)
+__initfunc(static void smp_tickoffset_init(void))
{
- int node;
-
- node = linux_cpus[0].prom_node;
- real_tick_offset = prom_getint(node, "clock-frequency");
- real_tick_offset = real_tick_offset / HZ;
- current_tick_offset = real_tick_offset;
+ current_tick_offset = timer_tick_offset;
}
-int setup_profiling_timer(unsigned int multiplier)
+__initfunc(int setup_profiling_timer(unsigned int multiplier))
{
unsigned long flags;
int i;
- if((!multiplier) || (real_tick_offset / multiplier) < 1000)
+ if((!multiplier) || (timer_tick_offset / multiplier) < 1000)
return -EINVAL;
save_and_cli(flags);
@@ -575,7 +669,7 @@ int setup_profiling_timer(unsigned int multiplier)
if(cpu_present_map & (1UL << i))
prof_multiplier(i) = multiplier;
}
- current_tick_offset = (real_tick_offset / multiplier);
+ current_tick_offset = (timer_tick_offset / multiplier);
restore_flags(flags);
return 0;
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index b776ea06e..0eb16d7bb 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.27 1997/11/19 07:57:46 jj Exp $
+/* $Id: sparc64_ksyms.c,v 1.33 1998/04/06 16:09:40 jj Exp $
* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -50,6 +50,7 @@ struct poll {
short revents;
};
+extern unsigned prom_cpu_nodes[NR_CPUS];
extern void die_if_kernel(char *str, struct pt_regs *regs);
extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long);
@@ -112,6 +113,8 @@ EXPORT_SYMBOL_PRIVATE(global_restore_flags);
#else
EXPORT_SYMBOL(local_irq_count);
#endif
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
EXPORT_SYMBOL_PRIVATE(_lock_kernel);
EXPORT_SYMBOL_PRIVATE(_unlock_kernel);
@@ -134,7 +137,9 @@ EXPORT_SYMBOL(dma_chain);
#endif
#if CONFIG_PCI
EXPORT_SYMBOL(ebus_chain);
-EXPORT_SYMBOL(pci_devices);
+EXPORT_SYMBOL(pci_dvma_offset);
+EXPORT_SYMBOL(pci_dvma_mask);
+EXPORT_SYMBOL(empty_zero_page);
#endif
/* Solaris/SunOS binary compatibility */
@@ -171,7 +176,6 @@ EXPORT_SYMBOL(__prom_getsibling);
/* sparc library symbols */
EXPORT_SYMBOL(bcopy);
-EXPORT_SYMBOL(memscan);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strnlen);
EXPORT_SYMBOL(strcpy);
@@ -179,7 +183,6 @@ EXPORT_SYMBOL(strncpy);
EXPORT_SYMBOL(strcat);
EXPORT_SYMBOL(strncat);
EXPORT_SYMBOL(strcmp);
-EXPORT_SYMBOL(strncmp);
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strrchr);
EXPORT_SYMBOL(strpbrk);
@@ -201,15 +204,11 @@ EXPORT_SYMBOL(sys_sigsuspend);
EXPORT_SYMBOL(sys_getppid);
EXPORT_SYMBOL(svr4_getcontext);
EXPORT_SYMBOL(svr4_setcontext);
-EXPORT_SYMBOL(linux_cpus);
+EXPORT_SYMBOL(prom_cpu_nodes);
EXPORT_SYMBOL(sys_ioctl);
EXPORT_SYMBOL(sys32_ioctl);
#endif
-#ifdef CONFIG_MATHEMU_MODULE
-EXPORT_SYMBOL(handle_mathemu);
-#endif
-
/* Special internal versions of library functions. */
EXPORT_SYMBOL(__memcpy);
EXPORT_SYMBOL(__memset);
diff --git a/arch/sparc64/kernel/sunos_ioctl32.c b/arch/sparc64/kernel/sunos_ioctl32.c
index d94a7c6d6..a0bfb47ef 100644
--- a/arch/sparc64/kernel/sunos_ioctl32.c
+++ b/arch/sparc64/kernel/sunos_ioctl32.c
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl32.c,v 1.5 1997/09/18 10:37:57 rth Exp $
+/* $Id: sunos_ioctl32.c,v 1.9 1998/03/29 10:10:53 davem Exp $
* sunos_ioctl32.c: SunOS ioctl compatability on sparc64.
*
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -18,6 +18,7 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -88,15 +89,12 @@ extern asmlinkage int sys_setsid(void);
asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg)
{
- struct file *filp;
int ret = -EBADF;
lock_kernel();
if(fd >= SUNOS_NR_OPEN)
goto out;
-
- filp = current->files->fd[fd];
- if(!filp)
+ if(!fcheck(fd))
goto out;
if(cmd == TIOCSETD) {
@@ -168,7 +166,7 @@ asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg)
ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
goto out;
case _IOW('i', 24, struct ifreq32):
- ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
+ ret = sys32_ioctl(fd, SIOCSIFBRDADDR, arg);
goto out;
case _IOWR('i', 25, struct ifreq32):
ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg);
diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S
index 6fb6f739b..44f17ca01 100644
--- a/arch/sparc64/kernel/sys32.S
+++ b/arch/sparc64/kernel/sys32.S
@@ -1,4 +1,4 @@
-/* $Id: sys32.S,v 1.4 1997/09/09 17:13:29 jj Exp $
+/* $Id: sys32.S,v 1.5 1998/03/24 05:57:56 ecd Exp $
* sys32.S: I-cache tricks for 32-bit compatability layer simple
* conversions.
*
@@ -24,7 +24,7 @@ sys32_mmap:
.align 32
.globl sys32_lseek
- .globl sys32_chmod, sys32_chown, sys32_mknod
+ .globl sys32_chmod, sys32_chown, sys32_lchown, sys32_mknod
sys32_lseek:
sra %o1, 0, %o1
mov %o7, %g1
@@ -46,6 +46,15 @@ sys32_chown:
srl %o2, 16, %o2
call sys_chown
mov %g1, %o7
+sys32_lchown:
+ sll %o1, 16, %o1
+ mov %o7, %g1
+ sll %o2, 16, %o2
+ srl %o0, 0, %o0
+ srl %o1, 16, %o1
+ srl %o2, 16, %o2
+ call sys_lchown
+ mov %g1, %o7
sys32_mknod:
sll %o2, 16, %o2
mov %o7, %g1
diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c
index e0e69abd9..b5198074d 100644
--- a/arch/sparc64/kernel/sys_sparc.c
+++ b/arch/sparc64/kernel/sys_sparc.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.9 1997/12/11 15:15:44 jj Exp $
+/* $Id: sys_sparc.c,v 1.13 1998/03/29 10:10:52 davem Exp $
* linux/arch/sparc64/kernel/sys_sparc.c
*
* This file contains various random system calls that
@@ -10,6 +10,7 @@
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/mm.h>
#include <linux/sem.h>
#include <linux/msg.h>
@@ -40,7 +41,7 @@ asmlinkage unsigned long sparc_brk(unsigned long brk)
unsigned long ret;
lock_kernel();
- if(brk >= 0x80000000000ULL) { /* VM hole */
+ if(brk >= 0x80000000000UL) { /* VM hole */
ret = current->mm->brk;
goto out;
}
@@ -128,6 +129,16 @@ asmlinkage int sys_ipc (unsigned call, int first, int second, unsigned long thir
if (call <= SHMCTL)
switch (call) {
case SHMAT:
+ if (first >= 0) {
+ extern struct shmid_ds *shm_segs[];
+ struct shmid_ds *shp = shm_segs[(unsigned int) first % SHMMNI];
+ if (shp == IPC_UNUSED || shp == IPC_NOID) {
+ err = -ENOMEM;
+ if ((unsigned long)ptr >= 0x80000000000UL - shp->shm_segsz &&
+ (unsigned long)ptr < 0xfffff80000000000UL)
+ goto out; /* Somebody is trying to fool us */
+ }
+ }
err = sys_shmat (first, (char *) ptr, second, (ulong *) third);
goto out;
case SHMDT:
@@ -162,29 +173,39 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
lock_kernel();
if (!(flags & MAP_ANONYMOUS)) {
- if (fd >= NR_OPEN || !(file = current->files->fd[fd])){
+ file = fget(fd);
+ if (!file)
goto out;
- }
}
retval = -ENOMEM;
+ len = PAGE_ALIGN(len);
if(!(flags & MAP_FIXED) && !addr) {
addr = get_unmapped_area(addr, len);
- if(!addr){
- goto out;
- }
+ if(!addr)
+ goto out_putf;
}
- /* See asm-sparc64/uaccess.h */
retval = -EINVAL;
- if((len > (TASK_SIZE - PAGE_SIZE)) || (addr > (TASK_SIZE-len-PAGE_SIZE)))
- goto out;
-
- if(addr >= 0x80000000000ULL) {
- retval = current->mm->brk;
- goto out;
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ if (len > 0xf0000000UL || addr > 0xf0000000UL - len)
+ goto out_putf;
+ } else {
+ if (len >= 0x80000000000UL ||
+ (addr < 0x80000000000UL &&
+ addr > 0x80000000000UL-len))
+ goto out_putf;
+ if (addr >= 0x80000000000ULL && addr < 0xfffff80000000000UL) {
+ /* VM hole */
+ retval = current->mm->brk;
+ goto out_putf;
+ }
}
retval = do_mmap(file, addr, len, prot, flags, off);
+
+out_putf:
+ if (file)
+ fput(file);
out:
unlock_kernel();
return retval;
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index 66993ebcb..2844a4bf2 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -1,7 +1,7 @@
-/* $Id: sys_sparc32.c,v 1.71 1997/12/11 15:15:11 jj Exp $
+/* $Id: sys_sparc32.c,v 1.77 1998/03/29 10:10:50 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
*
* These routines maintain argument size conversion between 32bit and 64bit
@@ -10,11 +10,12 @@
#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/signal.h>
#include <linux/utime.h>
#include <linux/resource.h>
-#include <linux/sched.h>
#include <linux/times.h>
#include <linux/utime.h>
#include <linux/utsname.h>
@@ -41,6 +42,7 @@
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/personality.h>
+#include <linux/stat.h>
#include <asm/types.h>
#include <asm/ipc.h>
@@ -59,7 +61,6 @@
extern char * getname_quicklist;
extern int getname_quickcount;
extern struct semaphore getname_quicklock;
-extern int kerneld_msqid;
/* Tuning: increase locality by reusing same pages again...
* if getname_quicklist becomes too long on low memory machines, either a limit
@@ -324,19 +325,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u
if (!p) err = -ENOMEM;
else {
err = 0;
- if (first == kerneld_msqid) {
- *(int *)p->mtext = 0;
- if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
- __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) ||
- __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4))
- err = -EFAULT;
- else
- second += 4;
- } else {
- if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
- __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
- err = -EFAULT;
- }
+ if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+ __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
+ err = -EFAULT;
if (!err) {
mm_segment_t old_fs = get_fs();
set_fs (KERNEL_DS);
@@ -379,18 +370,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u
if (err < 0)
goto out;
- if (first == kerneld_msqid) {
- if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
- __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) ||
- __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8))
- err = -EFAULT;
- else
- err -= 4;
- } else {
- if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
- __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err))
- err = -EFAULT;
- }
+ if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+ __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err))
+ err = -EFAULT;
kfree (p);
goto out;
}
@@ -939,14 +921,12 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count)
{
int error = -EBADF;
struct file * file;
+ struct inode * inode;
struct readdir_callback32 buf;
lock_kernel();
- if(fd >= NR_OPEN)
- goto out;
-
- file = current->files->fd[fd];
- if(!file)
+ file = fget(fd);
+ if (!file)
goto out;
buf.count = 0;
@@ -954,12 +934,18 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count)
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
-
+ goto out_putf;
+
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
error = file->f_op->readdir(file, &buf, fillonedir);
+ up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
error = buf.count;
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -1006,16 +992,14 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in
asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count)
{
struct file * file;
+ struct inode * inode;
struct linux_dirent32 * lastdirent;
struct getdents_callback32 buf;
int error = -EBADF;
lock_kernel();
- if(fd >= NR_OPEN)
- goto out;
-
- file = current->files->fd[fd];
- if(!file)
+ file = fget(fd);
+ if (!file)
goto out;
buf.current_dir = (struct linux_dirent32 *) A(dirent);
@@ -1025,17 +1009,22 @@ asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count)
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
error = file->f_op->readdir(file, &buf, filldir);
+ up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
lastdirent = buf.previous;
error = buf.error;
if(lastdirent) {
put_user(file->f_pos, &lastdirent->d_off);
error = count - buf.count;
}
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -1258,6 +1247,27 @@ asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf)
return ret;
}
+extern asmlinkage int sys_xstat(int ver, char *filename, struct stat64 * statbuf);
+
+asmlinkage int sys32_xstat(int ver, u32 file, u32 statbuf)
+{
+ switch (ver & __XSTAT_VER_MASK) {
+ case __XSTAT_VER_1:
+ switch (ver & __XSTAT_VER_TYPEMASK) {
+ case __XSTAT_VER_XSTAT:
+ return sys32_newstat(file, statbuf);
+ case __XSTAT_VER_LXSTAT:
+ return sys32_newlstat(file, statbuf);
+ case __XSTAT_VER_FXSTAT:
+ return sys32_newfstat(file, statbuf);
+ }
+ return -EINVAL;
+ case __XSTAT_VER_2:
+ return sys_xstat(ver, (char *)A(file), (struct stat64 *)A(statbuf));
+ }
+ return -EINVAL;
+}
+
extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2);
asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2)
@@ -2222,18 +2232,19 @@ asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags)
char address[MAX_SOCK_ADDR];
struct iovec iov[UIO_FASTIOV];
unsigned char ctl[sizeof(struct cmsghdr) + 20];
- struct msghdr kern_msg;
- int err;
- int total_len;
unsigned char *ctl_buf = ctl;
+ struct msghdr kern_msg;
+ int err, total_len;
if(msghdr_from_user32_to_kern(&kern_msg, (struct msghdr32 *)A(user_msg)))
return -EFAULT;
if(kern_msg.msg_iovlen > UIO_MAXIOV)
return -EINVAL;
- total_len = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
- if(total_len < 0)
- return total_len;
+ err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
+ if (err < 0)
+ goto out;
+ total_len = err;
+
if(kern_msg.msg_controllen) {
struct cmsghdr32 *ucmsg = (struct cmsghdr32 *)kern_msg.msg_control;
unsigned long *kcmsg;
@@ -2241,41 +2252,40 @@ asmlinkage int sys32_sendmsg(int fd, u32 user_msg, unsigned user_flags)
if(kern_msg.msg_controllen > sizeof(ctl) &&
kern_msg.msg_controllen <= 256) {
+ err = -ENOBUFS;
ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL);
- if(!ctl_buf) {
- if(kern_msg.msg_iov != iov)
- kfree(kern_msg.msg_iov);
- return -ENOBUFS;
- }
+ if(!ctl_buf)
+ goto out_freeiov;
}
__get_user(cmlen, &ucmsg->cmsg_len);
kcmsg = (unsigned long *) ctl_buf;
*kcmsg++ = (unsigned long)cmlen;
+ err = -EFAULT;
if(copy_from_user(kcmsg, &ucmsg->cmsg_level,
- kern_msg.msg_controllen - sizeof(__kernel_size_t32))) {
- if(ctl_buf != ctl)
- kfree_s(ctl_buf, kern_msg.msg_controllen);
- if(kern_msg.msg_iov != iov)
- kfree(kern_msg.msg_iov);
- return -EFAULT;
- }
+ kern_msg.msg_controllen - sizeof(__kernel_size_t32)))
+ goto out_freectl;
kern_msg.msg_control = ctl_buf;
}
kern_msg.msg_flags = user_flags;
lock_kernel();
- if(current->files->fd[fd]->f_flags & O_NONBLOCK)
- kern_msg.msg_flags |= MSG_DONTWAIT;
- if((sock = sockfd_lookup(fd, &err)) != NULL) {
+ sock = sockfd_lookup(fd, &err);
+ if (sock != NULL) {
+ if (sock->file->f_flags & O_NONBLOCK)
+ kern_msg.msg_flags |= MSG_DONTWAIT;
err = sock_sendmsg(sock, &kern_msg, total_len);
sockfd_put(sock);
}
unlock_kernel();
+out_freectl:
+ /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
if(ctl_buf != ctl)
- kfree_s(ctl_buf, kern_msg.msg_controllen);
+ kfree(ctl_buf);
+out_freeiov:
if(kern_msg.msg_iov != iov)
kfree(kern_msg.msg_iov);
+out:
return err;
}
@@ -2299,17 +2309,18 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags)
uaddr = kern_msg.msg_name;
uaddr_len = &((struct msghdr32 *)A(user_msg))->msg_namelen;
err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE);
- if(err < 0)
- return err;
+ if (err < 0)
+ goto out;
total_len = err;
cmsg_ptr = (unsigned long) kern_msg.msg_control;
kern_msg.msg_flags = 0;
lock_kernel();
- if(current->files->fd[fd]->f_flags & O_NONBLOCK)
- user_flags |= MSG_DONTWAIT;
- if((sock = sockfd_lookup(fd, &err)) != NULL) {
+ sock = sockfd_lookup(fd, &err);
+ if (sock != NULL) {
+ if (sock->file->f_flags & O_NONBLOCK)
+ user_flags |= MSG_DONTWAIT;
err = sock_recvmsg(sock, &kern_msg, total_len, user_flags);
if(err >= 0)
len = err;
@@ -2317,8 +2328,6 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags)
}
unlock_kernel();
- if(kern_msg.msg_iov != iov)
- kfree(kern_msg.msg_iov);
if(uaddr != NULL && err >= 0)
err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
if(err >= 0) {
@@ -2330,6 +2339,10 @@ asmlinkage int sys32_recvmsg(int fd, u32 user_msg, unsigned int user_flags)
&((struct msghdr32 *)A(user_msg))->msg_controllen);
}
}
+
+ if(kern_msg.msg_iov != iov)
+ kfree(kern_msg.msg_iov);
+out:
if(err < 0)
return err;
return len;
@@ -2838,25 +2851,25 @@ asmlinkage int sys32_get_kernel_syms(u32 table)
#else /* CONFIG_MODULES */
asmlinkage unsigned long
-sys_create_module(const char *name_user, size_t size)
+sys32_create_module(const char *name_user, size_t size)
{
return -ENOSYS;
}
asmlinkage int
-sys_init_module(const char *name_user, struct module *mod_user)
+sys32_init_module(const char *name_user, struct module *mod_user)
{
return -ENOSYS;
}
asmlinkage int
-sys_delete_module(const char *name_user)
+sys32_delete_module(const char *name_user)
{
return -ENOSYS;
}
asmlinkage int
-sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize,
size_t *ret)
{
/* Let the program know about the new interface. Not that
@@ -2868,7 +2881,7 @@ sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,
}
asmlinkage int
-sys_get_kernel_syms(struct kernel_sym *table)
+sys32_get_kernel_syms(struct kernel_sym *table)
{
return -ENOSYS;
}
@@ -3327,3 +3340,19 @@ asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, u32 ubuf,
{
return sys_pwrite(fd, (char *) A(ubuf), count, pos);
}
+
+
+extern asmlinkage int sys_personality(unsigned long);
+
+asmlinkage int sys32_personality(unsigned long personality)
+{
+ int ret;
+ lock_kernel();
+ if (current->personality == PER_LINUX32 && personality == PER_LINUX)
+ personality = PER_LINUX32;
+ ret = sys_personality(personality);
+ unlock_kernel();
+ if (ret == PER_LINUX32)
+ ret = PER_LINUX;
+ return ret;
+}
diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c
index 4af388b99..ad7bac534 100644
--- a/arch/sparc64/kernel/sys_sunos32.c
+++ b/arch/sparc64/kernel/sys_sunos32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.7 1997/12/11 15:15:19 jj Exp $
+/* $Id: sys_sunos32.c,v 1.11 1998/03/29 10:10:55 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -16,6 +16,7 @@
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/resource.h>
#include <linux/ipc.h>
#include <linux/shm.h>
@@ -70,23 +71,30 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of
flags &= ~MAP_NORESERVE;
}
retval = -EBADF;
- if(!(flags & MAP_ANONYMOUS))
- if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ if(!(flags & MAP_ANONYMOUS)) {
+ if(fd >= SUNOS_NR_OPEN)
goto out;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ if (file->f_dentry && file->f_dentry->d_inode) {
+ struct inode * inode = file->f_dentry->d_inode;
+ if(MAJOR(inode->i_rdev) == MEM_MAJOR &&
+ MINOR(inode->i_rdev) == 5) {
+ flags |= MAP_ANONYMOUS;
+ fput(file);
+ file = NULL;
+ }
+ }
+ }
+
retval = -ENOMEM;
if(!(flags & MAP_FIXED) && !addr) {
unsigned long attempt = get_unmapped_area(addr, len);
if(!attempt || (attempt >= 0xf0000000UL))
- goto out;
+ goto out_putf;
addr = (u32) attempt;
}
- if(file->f_dentry && file->f_dentry->d_inode) {
- if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR &&
- MINOR(file->f_dentry->d_inode->i_rdev) == 5) {
- flags |= MAP_ANONYMOUS;
- file = 0;
- }
- }
if(!(flags & MAP_FIXED))
addr = 0;
ret_type = flags & _MAP_NEW;
@@ -98,6 +106,9 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of
(unsigned long) off);
if(!ret_type)
retval = ((retval < 0xf0000000) ? 0 : retval);
+out_putf:
+ if (file)
+ fput(file);
out:
unlock_kernel();
return (u32) retval;
@@ -372,6 +383,7 @@ static int sunos_filldir(void * __buf, const char * name, int namlen,
asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt)
{
struct file * file;
+ struct inode * inode;
struct sunos_dirent * lastdirent;
struct sunos_dirent_callback buf;
int error = -EBADF;
@@ -381,32 +393,39 @@ asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt)
if(fd >= SUNOS_NR_OPEN)
goto out;
- file = current->files->fd[fd];
+ file = fget(fd);
if(!file)
goto out;
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
error = -EINVAL;
if(cnt < (sizeof(struct sunos_dirent) + 255))
- goto out;
+ goto out_putf;
buf.curr = (struct sunos_dirent *) dirent;
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
error = file->f_op->readdir(file, &buf, sunos_filldir);
+ up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
+
lastdirent = buf.previous;
error = buf.error;
if (lastdirent) {
put_user(file->f_pos, &lastdirent->d_off);
error = cnt - buf.count;
}
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -454,43 +473,51 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen,
asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent,
int cnt, u32 u_basep)
{
+ void *dirent = (void *) A(u_dirent);
+ unsigned int *basep = (unsigned int *)A(u_basep);
struct file * file;
+ struct inode * inode;
struct sunos_direntry * lastdirent;
- struct sunos_direntry_callback buf;
int error = -EBADF;
- void *dirent = (void *) A(u_dirent);
- unsigned int *basep = (unsigned int *)A(u_basep);
+ struct sunos_direntry_callback buf;
lock_kernel();
if(fd >= SUNOS_NR_OPEN)
goto out;
- file = current->files->fd[fd];
+ file = fget(fd);
if(!file)
goto out;
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
error = -EINVAL;
if(cnt < (sizeof(struct sunos_direntry) + 255))
- goto out;
+ goto out_putf;
buf.curr = (struct sunos_direntry *) dirent;
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
error = file->f_op->readdir(file, &buf, sunos_filldirentry);
+ up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
+
lastdirent = buf.previous;
error = buf.error;
if (lastdirent) {
put_user(file->f_pos, basep);
error = cnt - buf.count;
}
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -622,14 +649,28 @@ asmlinkage int sunos_pathconf(u32 u_path, int name)
extern asmlinkage int
sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp);
-asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp)
+struct timeval32
+{
+ int tv_sec, tv_usec;
+};
+
+asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x)
{
int ret;
/* SunOS binaries expect that select won't change the tvp contents */
lock_kernel();
current->personality |= STICKY_TIMEOUTS;
- ret = sys32_select (width, inp, outp, exp, tvp);
+ ret = sys32_select (width, inp, outp, exp, tvp_x);
+ if (ret == -EINTR && tvp_x) {
+ struct timeval32 *tvp = (struct timeval32 *)A(tvp_x);
+ time_t sec, usec;
+
+ __get_user(sec, &tvp->tv_sec);
+ __get_user(usec, &tvp->tv_usec);
+ if (sec == 0 && usec == 0)
+ ret = 0;
+ }
unlock_kernel();
return ret;
}
@@ -1297,8 +1338,11 @@ asmlinkage int sunos_open(u32 filename, int flags, int mode)
static inline int check_nonblock(int ret, int fd)
{
- if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY))
- return -SUNOS_EWOULDBLOCK;
+ if (ret == -EAGAIN) {
+ struct file * file = fcheck(fd);
+ if (file && (file->f_flags & O_NDELAY))
+ ret = -SUNOS_EWOULDBLOCK;
+ }
return ret;
}
@@ -1370,12 +1414,42 @@ asmlinkage int sunos_send(int fd, u32 buff, int len, unsigned flags)
return ret;
}
+extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
+ char *optval, int optlen);
+
+asmlinkage int sunos_socket(int family, int type, int protocol)
+{
+ int ret, one = 1;
+
+ lock_kernel();
+ ret = sys_socket(family, type, protocol);
+ if (ret < 0)
+ goto out;
+
+ sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *)&one, sizeof(one));
+out:
+ unlock_kernel();
+ return ret;
+}
+
asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen)
{
- int ret;
+ int ret, one = 1;
lock_kernel();
- ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), (int *)A(addrlen)), fd);
+ while (1) {
+ ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa),
+ (int *)A(addrlen)), fd);
+ if (ret != -ENETUNREACH && ret != -EHOSTUNREACH)
+ break;
+ }
+ if (ret < 0)
+ goto out;
+
+ sys_setsockopt(ret, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *)&one, sizeof(one));
+out:
unlock_kernel();
return ret;
}
diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S
index 48ae0ecdf..6389681dd 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc64/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.37 1997/12/24 17:27:31 ecd Exp $
+/* $Id: systbls.S,v 1.41 1998/03/24 05:57:57 ecd Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
@@ -19,8 +19,8 @@
sys_call_table32:
/*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write
/*5*/ .word sys_open, sys_close, sys32_wait4, sys_creat, sys_link
-/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys32_mknod
-/*15*/ .word sys32_chmod, sys32_chown, sparc_brk, sys_nis_syscall, sys32_lseek
+/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_xstat, sys32_mknod
+/*15*/ .word sys32_chmod, sys32_lchown, sparc_brk, sys_xmknod, sys32_lseek
/*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys32_pause
/*30*/ .word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
@@ -55,7 +55,7 @@ sys_call_table32:
.word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module
.word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname
-/*190*/ .word sys32_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*190*/ .word sys32_init_module, sys32_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask
/*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir
.word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
@@ -78,8 +78,8 @@ sys_call_table64:
sys_call_table:
/*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write
/*5*/ .word sys_open, sys_close, sys_wait4, sys_creat, sys_link
-/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod
-/*15*/ .word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek
+/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_xstat, sys_mknod
+/*15*/ .word sys_chmod, sys_lchown, sparc_brk, sys_xmknod, sys_lseek
/*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid
/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_nis_syscall
/*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
@@ -117,7 +117,7 @@ sys_call_table:
/*190*/ .word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask
/*200*/ .word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall
- .word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall
+ .word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
/*210*/ .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
.word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
/*220*/ .word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid
@@ -139,7 +139,7 @@ sunos_sys_table:
.word sys_close, sunos_wait4, sys_creat
.word sys_link, sys_unlink, sunos_execv
.word sys_chdir, sunos_nosys, sys32_mknod
- .word sys32_chmod, sys32_chown, sunos_brk
+ .word sys32_chmod, sys32_lchown, sunos_brk
.word sunos_nosys, sys32_lseek, sunos_getpid
.word sunos_nosys, sunos_nosys, sunos_nosys
.word sunos_getuid, sunos_nosys, sys_ptrace
@@ -166,7 +166,7 @@ sunos_sys_table:
.word sys32_getitimer, sys_gethostname, sys_sethostname
.word sunos_getdtablesize, sys_dup2, sunos_nop
.word sys32_fcntl, sunos_select, sunos_nop
- .word sys_fsync, sys_setpriority, sys_socket
+ .word sys_fsync, sys_setpriority, sunos_socket
.word sys_connect, sunos_accept
/*100*/ .word sys_getpriority, sunos_send, sunos_recv
.word sunos_nosys, sys_bind, sunos_setsockopt
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index 8b0152231..debb08888 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc64/kernel/time.c
@@ -1,7 +1,8 @@
-/* $Id: time.c,v 1.12 1997/08/22 20:12:13 davem Exp $
+/* $Id: time.c,v 1.13 1998/03/15 17:23:47 ecd Exp $
* time.c: UltraSparc timer and TOD clock support.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
*
* Based largely on code which is:
*
@@ -42,29 +43,64 @@ static int set_rtc_mmss(unsigned long);
* NOTE: On SUN5 systems the ticker interrupt comes in using 2
* interrupts, one at level14 and one with softint bit 0.
*/
-extern struct sun5_timer *linux_timers;
+unsigned long timer_tick_offset;
+static unsigned long timer_tick_compare;
+static unsigned long timer_ticks_per_usec;
-static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static __inline__ void timer_check_rtc(void)
{
/* last time the cmos clock got updated */
static long last_rtc_update=0;
- __asm__ __volatile__("ldx [%0], %%g0"
- : /* no outputs */
- : "r" (&((linux_timers)->limit0)));
-
- do_timer(regs);
-
/* Determine when to update the Mostek clock. */
if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_usec > 500000 - (tick >> 1) &&
- xtime.tv_usec < 500000 + (tick >> 1))
- if (set_rtc_mmss(xtime.tv_sec) == 0)
- last_rtc_update = xtime.tv_sec;
- else
- last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ xtime.tv_usec < 500000 + (tick >> 1)) {
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600;
+ /* do it again in 60 s */
+ }
+}
+
+static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ unsigned long ticks;
+
+ do {
+ do_timer(regs);
+
+ __asm__ __volatile__("
+ rd %%tick_cmpr, %0
+ add %0, %2, %0
+ wr %0, 0, %%tick_cmpr
+ rd %%tick, %1"
+ : "=&r" (timer_tick_compare), "=r" (ticks)
+ : "r" (timer_tick_offset));
+ } while (ticks >= timer_tick_compare);
+
+ timer_check_rtc();
}
+#ifdef __SMP__
+void timer_tick_interrupt(struct pt_regs *regs)
+{
+ do_timer(regs);
+
+ /*
+ * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR.
+ */
+ __asm__ __volatile__("
+ rd %%tick_cmpr, %0
+ add %0, %1, %0"
+ : "=&r" (timer_tick_compare)
+ : "r" (timer_tick_offset));
+
+ timer_check_rtc();
+}
+#endif
+
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
@@ -318,29 +354,32 @@ __initfunc(void time_init(void))
*/
}
-extern void init_timers(void (*func)(int, void *, struct pt_regs *));
+extern void init_timers(void (*func)(int, void *, struct pt_regs *),
+ unsigned long *);
__initfunc(void sun4u_start_timers(void))
{
- init_timers(timer_interrupt);
+ unsigned long clock;
+
+ init_timers(timer_interrupt, &clock);
+ timer_tick_offset = clock / HZ;
+ timer_ticks_per_usec = clock / 1000000;
}
static __inline__ unsigned long do_gettimeoffset(void)
{
- unsigned long offset = 0;
- unsigned int count;
-
- /* XXX -DaveM */
-#if 0
- count = (*master_l10_counter >> 10) & 0x1fffff;
-#else
- count = 0;
-#endif
+ unsigned long ticks;
- if(test_bit(TIMER_BH, &bh_active))
- offset = 1000000;
-
- return offset + count;
+ __asm__ __volatile__("
+ rd %%tick, %%g1
+ add %1, %%g1, %0
+ sub %0, %2, %0
+"
+ : "=r" (ticks)
+ : "r" (timer_tick_offset), "r" (timer_tick_compare)
+ : "g1", "g2");
+
+ return ticks / timer_ticks_per_usec;
}
void do_gettimeofday(struct timeval *tv)
@@ -353,13 +392,16 @@ void do_gettimeofday(struct timeval *tv)
* nucleus atomic quad 128-bit loads.
*/
__asm__ __volatile__("
- sethi %hi(linux_timers), %o1
+ sethi %hi(timer_tick_offset), %g3
sethi %hi(xtime), %g2
- ldx [%o1 + %lo(linux_timers)], %g3
+ sethi %hi(timer_tick_compare), %g1
+ ldx [%g3 + %lo(timer_tick_offset)], %g3
or %g2, %lo(xtime), %g2
+ or %g1, %lo(timer_tick_compare), %g1
1: ldda [%g2] 0x24, %o4
membar #LoadLoad | #MemIssue
- ldx [%g3], %o1
+ rd %tick, %o1
+ ldx [%g1], %g7
membar #LoadLoad | #MemIssue
ldda [%g2] 0x24, %o2
membar #LoadLoad
@@ -367,24 +409,28 @@ void do_gettimeofday(struct timeval *tv)
xor %o5, %o3, %o3
orcc %o2, %o3, %g0
bne,pn %xcc, 1b
- cmp %o1, 0
- bge,pt %icc, 1f
- sethi %hi(tick), %o3
- ldx [%o3 + %lo(tick)], %o3
- sethi %hi(0x1fffff), %o2
- or %o2, %lo(0x1fffff), %o2
- add %o5, %o3, %o5
- and %o1, %o2, %o1
-1: add %o5, %o1, %o5
- sethi %hi(1000000), %o2
+ sethi %hi(lost_ticks), %o2
+ sethi %hi(timer_ticks_per_usec), %o3
+ ldx [%o2 + %lo(lost_ticks)], %o2
+ add %g3, %o1, %o1
+ ldx [%o3 + %lo(timer_ticks_per_usec)], %o3
+ sub %o1, %g7, %o1
+ brz,pt %o2, 1f
+ udivx %o1, %o3, %o1
+ sethi %hi(10000), %g2
+ or %g2, %lo(10000), %g2
+ add %o1, %g2, %o1
+1: sethi %hi(1000000), %o2
+ srlx %o5, 32, %o5
or %o2, %lo(1000000), %o2
+ add %o5, %o1, %o5
cmp %o5, %o2
bl,a,pn %xcc, 1f
stx %o4, [%o0 + 0x0]
add %o4, 0x1, %o4
sub %o5, %o2, %o5
stx %o4, [%o0 + 0x0]
-1: stx %o5, [%o0 + 0x8]");
+1: st %o5, [%o0 + 0x8]");
}
void do_settimeofday(struct timeval *tv)
@@ -401,6 +447,7 @@ void do_settimeofday(struct timeval *tv)
time_state = TIME_BAD;
time_maxerror = 0x70000000;
time_esterror = 0x70000000;
+
sti();
}
diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S
index b5ca851ac..8604b3301 100644
--- a/arch/sparc64/kernel/trampoline.S
+++ b/arch/sparc64/kernel/trampoline.S
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.2 1997/07/28 02:57:32 davem Exp $
+/* $Id: trampoline.S,v 1.3 1998/02/22 21:06:11 jj Exp $
* trampoline.S: Jump start slave processors on sparc64.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -172,8 +172,10 @@ bounce:
mov %o2, %g6
wrpr %o1, (PSTATE_MG | PSTATE_IE), %pstate
- sethi %hi(0x1ff8), %g2
- or %g2, %lo(0x1ff8), %g2
+#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
+ sethi %uhi(KERN_HIGHBITS), %g2
+ sllx %g2, 32, %g2
+#undef KERN_HIGHBITS
ldx [%o2 + AOFF_task_mm], %g6
ldx [%g6 + AOFF_mm_pgd], %g6
clr %g7
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
index 6e1d30990..b255c7623 100644
--- a/arch/sparc64/kernel/traps.c
+++ b/arch/sparc64/kernel/traps.c
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.44 1998/01/09 16:39:35 jj Exp $
+/* $Id: traps.c,v 1.49 1998/04/06 16:09:38 jj Exp $
* arch/sparc64/kernel/traps.c
*
* Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -191,7 +191,7 @@ void bad_trap (struct pt_regs *regs, long lvl)
die_if_kernel ("Kernel bad trap", regs);
current->tss.sig_desc = SUBSIG_BADTRAP(lvl - 0x100);
current->tss.sig_address = regs->tpc;
- send_sig(SIGILL, current, 1);
+ force_sig(SIGILL, current);
unlock_kernel ();
}
@@ -225,7 +225,9 @@ void data_access_exception (struct pt_regs *regs)
return;
}
}
- send_sig(SIGSEGV, current, 1);
+ lock_kernel();
+ force_sig(SIGSEGV, current);
+ unlock_kernel();
}
#ifdef CONFIG_PCI
@@ -235,16 +237,35 @@ extern volatile int pci_poke_in_progress;
extern volatile int pci_poke_faulted;
#endif
+/* When access exceptions happen, we must do this. */
+static __inline__ void clean_and_reenable_l1_caches(void)
+{
+ unsigned long va;
+
+ /* Clean 'em. */
+ for(va = 0; va < (PAGE_SIZE << 1); va += 32) {
+ spitfire_put_icache_tag(va, 0x0);
+ spitfire_put_dcache_tag(va, 0x0);
+ }
+
+ /* Re-enable. */
+ __asm__ __volatile__("flush %%g6\n\t"
+ "membar #Sync\n\t"
+ "stxa %0, [%%g0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC |
+ LSU_CONTROL_IM | LSU_CONTROL_DM),
+ "i" (ASI_LSU_CONTROL)
+ : "memory");
+}
+
void do_dae(struct pt_regs *regs)
{
#ifdef CONFIG_PCI
-#ifdef DEBUG_PCI_POKES
- prom_printf(" (POKE ");
-#endif
if(pci_poke_in_progress) {
- unsigned long va;
#ifdef DEBUG_PCI_POKES
- prom_printf("tpc[%016lx] tnpc[%016lx] ",
+ prom_printf(" (POKE tpc[%016lx] tnpc[%016lx] ",
regs->tpc, regs->tnpc);
#endif
pci_poke_faulted = 1;
@@ -255,39 +276,30 @@ void do_dae(struct pt_regs *regs)
prom_printf("PCI) ");
/* prom_halt(); */
#endif
- /* Re-enable I/D caches, Ultra turned them off. */
- for(va = 0; va < (PAGE_SIZE << 1); va += 32) {
- spitfire_put_icache_tag(va, 0x0);
- spitfire_put_dcache_tag(va, 0x0);
- }
- __asm__ __volatile__("flush %%g6\n\t"
- "membar #Sync\n\t"
- "stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC |
- LSU_CONTROL_IM | LSU_CONTROL_DM),
- "i" (ASI_LSU_CONTROL)
- : "memory");
+ clean_and_reenable_l1_caches();
return;
}
-#ifdef DEBUG_PCI_POKES
- prom_printf("USER) ");
- prom_printf("tpc[%016lx] tnpc[%016lx]\n");
- prom_halt();
#endif
-#endif
- send_sig(SIGSEGV, current, 1);
+ clean_and_reenable_l1_caches();
+ lock_kernel();
+ force_sig(SIGSEGV, current);
+ unlock_kernel();
}
void instruction_access_exception (struct pt_regs *regs)
{
- send_sig(SIGSEGV, current, 1);
+ clean_and_reenable_l1_caches();
+
+ lock_kernel();
+ force_sig(SIGSEGV, current);
+ unlock_kernel();
}
void do_iae(struct pt_regs *regs)
{
- send_sig(SIGSEGV, current, 1);
+ lock_kernel();
+ force_sig(SIGSEGV, current);
+ unlock_kernel();
}
void do_fpe_common(struct pt_regs *regs)
@@ -312,11 +324,7 @@ void do_fpieee(struct pt_regs *regs)
do_fpe_common(regs);
}
-#ifdef CONFIG_MATHEMU_MODULE
-volatile int (*handle_mathemu)(struct pt_regs *, struct fpustate *) = NULL;
-#else
extern int do_mathemu(struct pt_regs *, struct fpustate *);
-#endif
void do_fpother(struct pt_regs *regs)
{
@@ -326,18 +334,7 @@ void do_fpother(struct pt_regs *regs)
switch ((f->fsr & 0x1c000)) {
case (2 << 14): /* unfinished_FPop */
case (3 << 14): /* unimplemented_FPop */
-#ifdef CONFIG_MATHEMU_MODULE
-#ifdef CONFIG_KMOD
- if (!handle_mathemu)
- request_module("math-emu");
-#endif
- if (handle_mathemu)
- ret = handle_mathemu(regs, f);
-#else
-#ifdef CONFIG_MATHEMU
ret = do_mathemu(regs, f);
-#endif
-#endif
break;
}
if (ret) return;
@@ -576,27 +573,33 @@ void cache_flush_trap(struct pt_regs *regs)
#else
#error SMP not supported on sparc64 yet
#endif
+
+#if 0
+/* Broken */
int size = prom_getintdefault(node, "ecache-size", 512*1024);
int i, j;
- unsigned long addr, page_nr;
+ unsigned long addr;
+ struct page *page, *end;
regs->tpc = regs->tnpc;
regs->tnpc = regs->tnpc + 4;
if (!suser()) return;
size >>= PAGE_SHIFT;
addr = PAGE_OFFSET - PAGE_SIZE;
+ page = mem_map - 1;
+ end = mem_map + max_mapnr;
for (i = 0; i < size; i++) {
do {
addr += PAGE_SIZE;
- page_nr = MAP_NR(addr);
- if (page_nr >= max_mapnr) {
+ page++;
+ if (page >= end)
return;
- }
- } while (!PageReserved (mem_map + page_nr));
+ } while (!PageReserved(page));
/* E-Cache line size is 64B. Let us pollute it :)) */
for (j = 0; j < PAGE_SIZE; j += 64)
__asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1");
}
+#endif
}
#endif
diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S
index b22cf82f7..3d17fb3cb 100644
--- a/arch/sparc64/kernel/ttable.S
+++ b/arch/sparc64/kernel/ttable.S
@@ -1,4 +1,4 @@
-/* $Id: ttable.S,v 1.22 1997/10/16 07:07:46 jj Exp $
+/* $Id: ttable.S,v 1.23 1998/03/15 17:23:48 ecd Exp $
* ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -6,7 +6,7 @@
#include <linux/config.h>
- .globl sparc64_ttable_tl0, sparc64_ttable_tl1
+ .globl sparc64_ttable_tl0, tl0_itick, sparc64_ttable_tl1,
sparc64_ttable_tl0:
tl0_resv000: BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3)
@@ -45,7 +45,7 @@ tl0_irq7: TRAP_IRQ(handler_irq, 7) TRAP_IRQ(handler_irq, 8)
tl0_irq9: TRAP_IRQ(handler_irq, 9) TRAP_IRQ(handler_irq, 10)
tl0_irq11: TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12)
tl0_irq13: TRAP_IRQ(handler_irq, 13)
-tl0_itick: TRAP_TICK
+tl0_itick: TRAP_IRQ(handler_irq, 14)
tl0_irq15: TRAP_IRQ(handler_irq, 15)
tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55)
tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b)
diff --git a/arch/sparc64/lib/VIScsumcopy.S b/arch/sparc64/lib/VIScsumcopy.S
index efd2bfcd5..fff41bab2 100644
--- a/arch/sparc64/lib/VIScsumcopy.S
+++ b/arch/sparc64/lib/VIScsumcopy.S
@@ -1,4 +1,4 @@
-/* $Id: VIScsumcopy.S,v 1.2 1997/08/19 15:25:22 jj Exp $
+/* $Id: VIScsumcopy.S,v 1.4 1998/04/01 08:29:52 davem Exp $
* VIScsumcopy.S: High bandwidth IP checksumming with simultaneous
* copying utilizing the UltraSparc Visual Instruction Set.
*
@@ -393,22 +393,22 @@ vis0s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */
add %src, 128, %src /* IEU0 Group */
ldda [%src-128] %asi, %f0 /* Load Group */
ldda [%src-64] %asi, %f16 /* Load Group */
- fmovd %f48, %f62 /* FPA Group */
- faligndata %f0, %f2, %f48 /* FPA Group */
- fcmpgt32 %f32, %f2, %x1 /* FPM Group */
+ fmovd %f48, %f62 /* FPA Group f0 available */
+ faligndata %f0, %f2, %f48 /* FPA Group f2 available */
+ fcmpgt32 %f32, %f2, %x1 /* FPM Group f4 available */
fpadd32 %f0, %f62, %f0 /* FPA */
- fcmpgt32 %f32, %f4, %x2 /* FPM Group */
+ fcmpgt32 %f32, %f4, %x2 /* FPM Group f6 available */
faligndata %f2, %f4, %f50 /* FPA */
- fcmpgt32 %f62, %f0, %x3 /* FPM Group */
+ fcmpgt32 %f62, %f0, %x3 /* FPM Group f8 available */
faligndata %f4, %f6, %f52 /* FPA */
- fcmpgt32 %f32, %f6, %x4 /* FPM Group */
+ fcmpgt32 %f32, %f6, %x4 /* FPM Group f10 available */
inc %x1 /* IEU0 */
faligndata %f6, %f8, %f54 /* FPA */
- fcmpgt32 %f32, %f8, %x5 /* FPM Group */
+ fcmpgt32 %f32, %f8, %x5 /* FPM Group f12 available */
srl %x1, 1, %x1 /* IEU0 */
inc %x2 /* IEU1 */
faligndata %f8, %f10, %f56 /* FPA */
- fcmpgt32 %f32, %f10, %x6 /* FPM Group */
+ fcmpgt32 %f32, %f10, %x6 /* FPM Group f14 available */
srl %x2, 1, %x2 /* IEU0 */
add %sum, %x1, %sum /* IEU1 */
faligndata %f10, %f12, %f58 /* FPA */
@@ -451,6 +451,7 @@ vis1s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */
add %src, 128 - 8, %src /* IEU0 Group */
ldda [%src-128] %asi, %f0 /* Load Group */
ldda [%src-64] %asi, %f16 /* Load Group */
+ fmovd %f0, %f58 /* FPA Group */
fmovd %f48, %f0 /* FPA Group */
fcmpgt32 %f32, %f2, %x2 /* FPM Group */
faligndata %f2, %f4, %f48 /* FPA */
@@ -503,9 +504,10 @@ vis2s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */
add %src, 128 - 16, %src /* IEU0 Group */
ldda [%src-128] %asi, %f0 /* Load Group */
ldda [%src-64] %asi, %f16 /* Load Group */
+ fmovd %f0, %f56 /* FPA Group */
fmovd %f48, %f0 /* FPA Group */
sub %dst, 64, %dst /* IEU0 */
- fzero %f2 /* FPA Group */
+ fpsub32 %f2, %f2, %f2 /* FPA Group */
fcmpgt32 %f32, %f4, %x3 /* FPM Group */
faligndata %f4, %f6, %f48 /* FPA */
fcmpgt32 %f32, %f6, %x4 /* FPM Group */
@@ -552,10 +554,11 @@ vis3s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */
add %src, 128 - 24, %src /* IEU0 Group */
ldda [%src-128] %asi, %f0 /* Load Group */
ldda [%src-64] %asi, %f16 /* Load Group */
+ fmovd %f0, %f54 /* FPA Group */
fmovd %f48, %f0 /* FPA Group */
sub %dst, 64, %dst /* IEU0 */
- fzero %f2 /* FPA Group */
- fzero %f4 /* FPA Group */
+ fpsub32 %f2, %f2, %f2 /* FPA Group */
+ fpsub32 %f4, %f4, %f4 /* FPA Group */
fcmpgt32 %f32, %f6, %x4 /* FPM Group */
faligndata %f6, %f8, %f48 /* FPA */
fcmpgt32 %f32, %f8, %x5 /* FPM Group */
@@ -597,11 +600,12 @@ vis4s: wr %g2, ASI_BLK_XOR, %asi /* LSU Group */
add %src, 128 - 32, %src /* IEU0 Group */
ldda [%src-128] %asi, %f0 /* Load Group */
ldda [%src-64] %asi, %f16 /* Load Group */
+ fmovd %f0, %f52 /* FPA Group */
fmovd %f48, %f0 /* FPA Group */
sub %dst, 64, %dst /* IEU0 */
- fzero %f2 /* FPA Group */
- fzero %f4 /* FPA Group */
- fzero %f6 /* FPA Group */
+ fpsub32 %f2, %f2, %f2 /* FPA Group */
+ fpsub32 %f4, %f4, %f4 /* FPA Group */
+ fpsub32 %f6, %f6, %f6 /* FPA Group */
clr %x4 /* IEU0 */
fcmpgt32 %f32, %f8, %x5 /* FPM Group */
faligndata %f8, %f10, %f48 /* FPA */
@@ -697,9 +701,9 @@ vis6s: add %src, 128 - 48, %src /* IEU0 Group */
clr %x6 /* IEU0 */
fcmpgt32 %f32, %f12, %x7 /* FPM Group */
sub %dst, 64, %dst /* IEU0 */
- faligndata %f12, %f14, %f48 /* FPA */
fcmpgt32 %f32, %f14, %x8 /* FPM Group */
- fmovd %f14, %f50 /* FPA */
+ faligndata %f12, %f14, %f48 /* FPA */
+ fmovd %f14, %f50 /* FPA Group */
vis6: DO_THE_TRICK( f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30,
,f52,f54,f56,f58,f60,f62,f48,f50,f50,
,LDBLK(f32), ,,,,,,STBLK,,
diff --git a/arch/sparc64/math-emu/Makefile b/arch/sparc64/math-emu/Makefile
index ea816d98e..8f695b1e2 100644
--- a/arch/sparc64/math-emu/Makefile
+++ b/arch/sparc64/math-emu/Makefile
@@ -1,5 +1,5 @@
#
-# Makefile for the FPU Quad (long double) instruction emulation.
+# Makefile for the FPU instruction emulation.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
@@ -16,18 +16,10 @@ O_OBJS := math.o fabsq.o faddq.o fdivq.o fdmulq.o fitoq.o \
fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o \
fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o
-ifeq ($(CONFIG_MATHEMU),m)
-M_OBJS := $(O_TARGET)
-endif
-
.S.s:
$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
.S.o:
$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
-ifneq ($(CONFIG_MATHEMU),y)
-do_it_all:
-endif
-
include $(TOPDIR)/Rules.make
diff --git a/arch/sparc64/math-emu/double.h b/arch/sparc64/math-emu/double.h
index b68d76790..6aff6fdd5 100644
--- a/arch/sparc64/math-emu/double.h
+++ b/arch/sparc64/math-emu/double.h
@@ -3,7 +3,7 @@
*/
#if _FP_W_TYPE_SIZE < 32
-#error "Here's a nickle kid. Go buy yourself a real computer."
+#error "Here's a nickel kid. Go buy yourself a real computer."
#endif
#if _FP_W_TYPE_SIZE < 64
diff --git a/arch/sparc64/math-emu/fabsq.c b/arch/sparc64/math-emu/fabsq.c
index e6aa497c8..e01b02046 100644
--- a/arch/sparc64/math-emu/fabsq.c
+++ b/arch/sparc64/math-emu/fabsq.c
@@ -1,18 +1,5 @@
-#include "soft-fp.h"
-#include "quad.h"
-
int FABSQ(unsigned long *rd, unsigned long *rs2)
{
-/*
- FP_DECL_Q(A); FP_DECL_Q(R);
-
- __FP_UNPACK_Q(A, rs2);
- _FP_FRAC_COPY_2(R, A);
- R_c = A_c;
- R_e = A_e;
- R_s = 0;
- __FP_PACK_Q(rd, R);
- */
rd[0] = rs2[0] & 0x7fffffffffffffffUL;
rd[1] = rs2[1];
return 1;
diff --git a/arch/sparc64/math-emu/fcmpeq.c b/arch/sparc64/math-emu/fcmpeq.c
index cb37bc0db..e74b1b06b 100644
--- a/arch/sparc64/math-emu/fcmpeq.c
+++ b/arch/sparc64/math-emu/fcmpeq.c
@@ -11,11 +11,8 @@ int FCMPEQ(void *rd, void *rs2, void *rs1)
rd = (void *)(((long)rd)&~3);
__FP_UNPACK_Q(A, rs1);
__FP_UNPACK_Q(B, rs2);
- FP_CMP_Q(ret, A, B, 3);
- switch (ret) {
- case 1: ret = 2; break;
- case -1: ret = 1; break;
- }
+ FP_CMP_Q(ret, B, A, 3);
+ if (ret == -1) ret = 2;
fsr = *(unsigned long *)rd;
switch (fccno) {
case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
diff --git a/arch/sparc64/math-emu/fcmpq.c b/arch/sparc64/math-emu/fcmpq.c
index 81dadf47a..9effefb1f 100644
--- a/arch/sparc64/math-emu/fcmpq.c
+++ b/arch/sparc64/math-emu/fcmpq.c
@@ -11,11 +11,8 @@ int FCMPQ(void *rd, void *rs2, void *rs1)
rd = (void *)(((long)rd)&~3);
__FP_UNPACK_Q(A, rs1);
__FP_UNPACK_Q(B, rs2);
- FP_CMP_Q(ret, A, B, 3);
- switch (ret) {
- case 1: ret = 2; break;
- case -1: ret = 1; break;
- }
+ FP_CMP_Q(ret, B, A, 3);
+ if (ret == -1) ret = 2;
fsr = *(unsigned long *)rd;
switch (fccno) {
case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
diff --git a/arch/sparc64/math-emu/fnegq.c b/arch/sparc64/math-emu/fnegq.c
index dcdea3202..2251e3308 100644
--- a/arch/sparc64/math-emu/fnegq.c
+++ b/arch/sparc64/math-emu/fnegq.c
@@ -1,18 +1,7 @@
-#include "soft-fp.h"
-#include "quad.h"
-
int FNEGQ(unsigned long *rd, unsigned long *rs2)
{
-/*
- FP_DECL_Q(A); FP_DECL_Q(R);
-
- __FP_UNPACK_Q(A, rs2);
- FP_NEG_Q(R, A);
- __FP_PACK_Q(rd, R);
- */
rd[0] = rs2[0] ^ 0x8000000000000000UL;
rd[1] = rs2[1];
return 1;
}
-
diff --git a/arch/sparc64/math-emu/math.c b/arch/sparc64/math-emu/math.c
index 58ed21062..e0380720f 100644
--- a/arch/sparc64/math-emu/math.c
+++ b/arch/sparc64/math-emu/math.c
@@ -1,4 +1,4 @@
-/* $Id: math.c,v 1.3 1997/10/15 07:28:55 jj Exp $
+/* $Id: math.c,v 1.4 1998/04/06 16:09:57 jj Exp $
* arch/sparc64/math-emu/math.c
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -7,7 +7,6 @@
* of glibc and has appropriate copyrights in it.
*/
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
@@ -70,7 +69,6 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
if(tstate & TSTATE_PRIV)
die_if_kernel("FPQuad from kernel", regs);
- MOD_INC_USE_COUNT;
if(current->tss.flags & SPARC_FLAG_32BIT)
pc = (u32)pc;
if (get_user(insn, (u32 *)pc) != -EFAULT) {
@@ -182,28 +180,7 @@ int do_mathemu(struct pt_regs *regs, struct fpustate *f)
func(rd, rs2, rs1);
regs->tpc = regs->tnpc;
regs->tnpc += 4;
- MOD_DEC_USE_COUNT;
return 1;
}
-err: MOD_DEC_USE_COUNT;
- return 0;
+err: return 0;
}
-
-#ifdef MODULE
-
-MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz), Richard Henderson (rth@cygnus.com)");
-MODULE_DESCRIPTION("FPU emulation module");
-
-extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
-
-int init_module(void)
-{
- handle_mathemu = do_mathemu;
- return 0;
-}
-
-void cleanup_module(void)
-{
- handle_mathemu = NULL;
-}
-#endif
diff --git a/arch/sparc64/math-emu/op-2.h b/arch/sparc64/math-emu/op-2.h
index 879b6004f..5999cfc3b 100644
--- a/arch/sparc64/math-emu/op-2.h
+++ b/arch/sparc64/math-emu/op-2.h
@@ -207,6 +207,12 @@
R##_f1 = _FP_FRAC_WORD_4(_z,1); \
} while (0)
+/* This next macro appears to be totally broken. Fortunately nowhere
+ * seems to use it :-> The problem is that we define _z[4] but
+ * then use it in _FP_FRAC_SRS_4, which will attempt to access
+ * _z_f[n] which will cause an error. The fix probably involves
+ * declaring it with _FP_FRAC_DECL_4, see previous macro. -- PMM 02/1998
+ */
#define _FP_MUL_MEAT_2_gmp(fs, R, X, Y) \
do { \
_FP_W_TYPE _x[2], _y[2], _z[4]; \
@@ -226,6 +232,11 @@
/*
* Division algorithms:
+ * This seems to be giving me difficulties -- PMM
+ * Look, NetBSD seems to be able to comment algorithms. Can't you?
+ * I've thrown printks at the problem.
+ * This now appears to work, but I still don't really know why.
+ * Also, I don't think the result is properly normalised...
*/
#define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y) \
@@ -236,10 +247,17 @@
_FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0; \
_FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0; \
_FP_W_TYPE _rmem[2], _qmem[2]; \
- \
+ /* I think this check is to ensure that the result is normalised. \
+ * Assuming X,Y normalised (ie in [1.0,2.0)) X/Y will be in \
+ * [0.5,2.0). Furthermore, it will be less than 1.0 iff X < Y. \
+ * In this case we tweak things. (this is based on comments in \
+ * the NetBSD FPU emulation code. ) \
+ * We know X,Y are normalised because we ensure this as part of \
+ * the unpacking process. -- PMM \
+ */ \
if (_FP_FRAC_GT_2(X, Y)) \
{ \
- R##_e++; \
+/* R##_e++; */ \
_n_f3 = X##_f1 >> 1; \
_n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1; \
_n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1); \
@@ -247,14 +265,15 @@
} \
else \
{ \
+ R##_e--; \
_n_f3 = X##_f1; \
_n_f2 = X##_f0; \
_n_f1 = _n_f0 = 0; \
} \
\
/* Normalize, i.e. make the most significant bit of the \
- denominator set. */ \
- _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs - 1); \
+ denominator set. CHANGED: - 1 to nothing -- PMM */ \
+ _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs /* -1 */); \
\
/* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 \
primitive snagged from libgcc2.c. */ \
@@ -295,6 +314,11 @@
\
R##_f1 = _q_f1; \
R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0); \
+ /* adjust so answer is normalized again. I'm not sure what the \
+ * final sz param should be. In practice it's never used since \
+ * N is 1 which is always going to be < _FP_W_TYPE_SIZE... \
+ */ \
+ /* _FP_FRAC_SRS_2(R,1,_FP_WFRACBITS_##fs); */ \
} while (0)
@@ -406,3 +430,4 @@
D##_f1 = 0; \
_FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \
} while (0)
+
diff --git a/arch/sparc64/math-emu/op-4.h b/arch/sparc64/math-emu/op-4.h
index 2f917a847..5f7099271 100644
--- a/arch/sparc64/math-emu/op-4.h
+++ b/arch/sparc64/math-emu/op-4.h
@@ -1,11 +1,41 @@
/*
* Basic four-word fraction declaration and manipulation.
+ *
+ * When adding quadword support for 32 bit machines, we need
+ * to be a little careful as double multiply uses some of these
+ * macros: (in op-2.h)
+ * _FP_MUL_MEAT_2_wide() uses _FP_FRAC_DECL_4, _FP_FRAC_WORD_4,
+ * _FP_FRAC_ADD_4, _FP_FRAC_SRS_4
+ * _FP_MUL_MEAT_2_gmp() uses _FP_FRAC_SRS_4 (and should use
+ * _FP_FRAC_DECL_4: it appears to be broken and is not used
+ * anywhere anyway. )
+ *
+ * I've now fixed all the macros that were here from the sparc64 code.
+ * [*none* of the shift macros were correct!] -- PMM 02/1998
+ *
+ * The only quadword stuff that remains to be coded is:
+ * 1) the conversion to/from ints, which requires
+ * that we check (in op-common.h) that the following do the right thing
+ * for quadwords: _FP_TO_INT(Q,4,r,X,rsz,rsg), _FP_FROM_INT(Q,4,X,r,rs,rt)
+ * 2) multiply, divide and sqrt, which require:
+ * _FP_MUL_MEAT_4_*(R,X,Y), _FP_DIV_MEAT_4_*(R,X,Y), _FP_SQRT_MEAT_4(R,S,T,X,q),
+ * This also needs _FP_MUL_MEAT_Q and _FP_DIV_MEAT_Q to be defined to
+ * some suitable _FP_MUL_MEAT_4_* macros in sfp-machine.h.
+ * [we're free to choose whatever FP_MUL_MEAT_4_* macros we need for
+ * these; they are used nowhere else. ]
*/
#define _FP_FRAC_DECL_4(X) _FP_W_TYPE X##_f[4]
#define _FP_FRAC_COPY_4(D,S) \
(D##_f[0] = S##_f[0], D##_f[1] = S##_f[1], \
D##_f[2] = S##_f[2], D##_f[3] = S##_f[3])
+/* The _FP_FRAC_SET_n(X,I) macro is intended for use with another
+ * macro such as _FP_ZEROFRAC_n which returns n comma separated values.
+ * The result is that we get an expansion of __FP_FRAC_SET_n(X,I0,I1,I2,I3)
+ * which just assigns the In values to the array X##_f[].
+ * This is why the number of parameters doesn't appear to match
+ * at first glance... -- PMM
+ */
#define _FP_FRAC_SET_4(X,I) __FP_FRAC_SET_4(X, I)
#define _FP_FRAC_HIGH_4(X) (X##_f[3])
#define _FP_FRAC_LOW_4(X) (X##_f[0])
@@ -19,26 +49,32 @@
_down = _FP_W_TYPE_SIZE - _up; \
for (_i = 3; _i > _skip; --_i) \
X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down; \
- X##_f[_i] <<= _up; \
+/* bugfixed: was X##_f[_i] <<= _up; -- PMM 02/1998 */ \
+ X##_f[_i] = X##_f[0] << _up; \
for (--_i; _i >= 0; --_i) \
X##_f[_i] = 0; \
} while (0)
+/* This one was broken too */
#define _FP_FRAC_SRL_4(X,N) \
do { \
_FP_I_TYPE _up, _down, _skip, _i; \
_skip = (N) / _FP_W_TYPE_SIZE; \
_down = (N) % _FP_W_TYPE_SIZE; \
_up = _FP_W_TYPE_SIZE - _down; \
- for (_i = 0; _i < 4-_skip; ++_i) \
+ for (_i = 0; _i < 3-_skip; ++_i) \
X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \
- X##_f[_i] >>= _down; \
+ X##_f[_i] = X##_f[3] >> _down; \
for (++_i; _i < 4; ++_i) \
X##_f[_i] = 0; \
} while (0)
-/* Right shift with sticky-lsb. */
+/* Right shift with sticky-lsb.
+ * What this actually means is that we do a standard right-shift,
+ * but that if any of the bits that fall off the right hand side
+ * were one then we always set the LSbit.
+ */
#define _FP_FRAC_SRS_4(X,N,size) \
do { \
_FP_I_TYPE _up, _down, _skip, _i; \
@@ -48,13 +84,15 @@
_up = _FP_W_TYPE_SIZE - _down; \
for (_s = _i = 0; _i < _skip; ++_i) \
_s |= X##_f[_i]; \
- _s = X##_f[_i] << _up; \
- X##_f[0] = X##_f[_skip] >> _down | X##_f[_skip+1] << _up | (_s != 0); \
- for (_i = 1; _i < 4-_skip; ++_i) \
+ _s |= X##_f[_i] << _up; \
+/* s is now != 0 if we want to set the LSbit */ \
+ for (_i = 0; _i < 3-_skip; ++_i) \
X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \
- X##_f[_i] >>= _down; \
+ X##_f[_i] = X##_f[3] >> _down; \
for (++_i; _i < 4; ++_i) \
X##_f[_i] = 0; \
+ /* don't fix the LSB until the very end when we're sure f[0] is stable */ \
+ X##_f[0] |= (_s != 0); \
} while (0)
#define _FP_FRAC_ADD_4(R,X,Y) \
@@ -62,6 +100,92 @@
X##_f[3], X##_f[2], X##_f[1], X##_f[0], \
Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
+#define _FP_FRAC_SUB_4(R,X,Y) \
+ __FP_FRAC_SUB_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \
+ X##_f[3], X##_f[2], X##_f[1], X##_f[0], \
+ Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
+
+#define _FP_FRAC_ADDI_4(X,I) \
+ __FP_FRAC_ADDI_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0], I)
+
+#define _FP_ZEROFRAC_4 0,0,0,0
+#define _FP_MINFRAC_4 0,0,0,1
+
+#define _FP_FRAC_ZEROP_4(X) ((X##_f[0] | X##_f[1] | X##_f[2] | X##_f[3]) == 0)
+#define _FP_FRAC_NEGP_4(X) ((_FP_WS_TYPE)X##_f[3] < 0)
+#define _FP_FRAC_OVERP_4(fs,X) (X##_f[0] & _FP_OVERFLOW_##fs)
+
+#define _FP_FRAC_EQ_4(X,Y) \
+ (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1] \
+ && X##_f[2] == Y##_f[2] && X##_f[3] == Y##_f[3])
+
+#define _FP_FRAC_GT_4(X,Y) \
+ (X##_f[3] > Y##_f[3] || \
+ (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \
+ (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \
+ (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0]) \
+ )) \
+ )) \
+ )
+
+#define _FP_FRAC_GE_4(X,Y) \
+ (X##_f[3] > Y##_f[3] || \
+ (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] || \
+ (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] || \
+ (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0]) \
+ )) \
+ )) \
+ )
+
+
+#define _FP_FRAC_CLZ_4(R,X) \
+ do { \
+ if (X##_f[3]) \
+ { \
+ __FP_CLZ(R,X##_f[3]); \
+ } \
+ else if (X##_f[2]) \
+ { \
+ __FP_CLZ(R,X##_f[2]); \
+ R += _FP_W_TYPE_SIZE; \
+ } \
+ else if (X##_f[1]) \
+ { \
+ __FP_CLZ(R,X##_f[2]); \
+ R += _FP_W_TYPE_SIZE*2; \
+ } \
+ else \
+ { \
+ __FP_CLZ(R,X##_f[0]); \
+ R += _FP_W_TYPE_SIZE*3; \
+ } \
+ } while(0)
+
+
+#define _FP_UNPACK_RAW_4(fs, X, val) \
+ do { \
+ union _FP_UNION_##fs _flo; _flo.flt = (val); \
+ X##_f[0] = _flo.bits.frac0; \
+ X##_f[1] = _flo.bits.frac1; \
+ X##_f[2] = _flo.bits.frac2; \
+ X##_f[3] = _flo.bits.frac3; \
+ X##_e = _flo.bits.exp; \
+ X##_s = _flo.bits.sign; \
+ } while (0)
+
+#define _FP_PACK_RAW_4(fs, val, X) \
+ do { \
+ union _FP_UNION_##fs _flo; \
+ _flo.bits.frac0 = X##_f[0]; \
+ _flo.bits.frac1 = X##_f[1]; \
+ _flo.bits.frac2 = X##_f[2]; \
+ _flo.bits.frac3 = X##_f[3]; \
+ _flo.bits.exp = X##_e; \
+ _flo.bits.sign = X##_s; \
+ (val) = _flo.flt; \
+ } while (0)
+
+
/*
* Internals
*/
@@ -76,3 +200,98 @@
r2 = x2 + y2 + (r1 < x1), \
r3 = x3 + y3 + (r2 < x2))
#endif
+
+#ifndef __FP_FRAC_SUB_4
+#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \
+ (r0 = x0 - y0, \
+ r1 = x1 - y1 - (r0 > x0), \
+ r2 = x2 - y2 - (r1 > x1), \
+ r3 = x3 - y3 - (r2 > x2))
+#endif
+
+#ifndef __FP_FRAC_ADDI_4
+/* I always wanted to be a lisp programmer :-> */
+#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i) \
+ (x3 += ((x2 += ((x1 += ((x0 += i) < x0)) < x1) < x2)))
+#endif
+
+/* Convert FP values between word sizes. This appears to be more
+ * complicated than I'd have expected it to be, so these might be
+ * wrong... These macros are in any case somewhat bogus because they
+ * use information about what various FRAC_n variables look like
+ * internally [eg, that 2 word vars are X_f0 and x_f1]. But so do
+ * the ones in op-2.h and op-1.h.
+ */
+#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S) \
+ do { \
+ _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \
+ _FP_WFRACBITS_##sfs); \
+ D##_f = S##_f[0]; \
+ } while (0)
+
+#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S) \
+ do { \
+ _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \
+ _FP_WFRACBITS_##sfs); \
+ D##_f0 = S##_f[0]; \
+ D##_f1 = S##_f[1]; \
+ } while (0)
+
+/* Assembly/disassembly for converting to/from integral types.
+ * No shifting or overflow handled here.
+ */
+/* Put the FP value X into r, which is an integer of size rsize. */
+#define _FP_FRAC_ASSEMBLE_4(r, X, rsize) \
+ do { \
+ if (rsize <= _FP_W_TYPE_SIZE) \
+ r = X##_f[0]; \
+ else if (rsize <= 2*_FP_W_TYPE_SIZE) \
+ { \
+ r = X##_f[1]; \
+ r <<= _FP_W_TYPE_SIZE; \
+ r += X##_f[0]; \
+ } \
+ else \
+ { \
+ /* I'm feeling lazy so we deal with int == 3words (implausible)*/ \
+ /* and int == 4words as a single case. */ \
+ r = X##_f[3]; \
+ r <<= _FP_W_TYPE_SIZE; \
+ r += X##_f[2]; \
+ r <<= _FP_W_TYPE_SIZE; \
+ r += X##_f[1]; \
+ r <<= _FP_W_TYPE_SIZE; \
+ r += X##_f[0]; \
+ } \
+ } while (0)
+
+/* "No disassemble Number Five!" */
+/* move an integer of size rsize into X's fractional part. We rely on
+ * the _f[] array consisting of words of size _FP_W_TYPE_SIZE to avoid
+ * having to mask the values we store into it.
+ */
+#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize) \
+ do { \
+ X##_f[0] = r; \
+ X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \
+ X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \
+ X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \
+ } while (0);
+
+#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S) \
+ do { \
+ D##_f[0] = S##_f; \
+ D##_f[1] = D##_f[2] = D##_f[3] = 0; \
+ _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \
+ } while (0)
+
+#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S) \
+ do { \
+ D##_f[0] = S##_f0; \
+ D##_f[1] = S##_f1; \
+ D##_f[2] = D##_f[3] = 0; \
+ _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \
+ } while (0)
+
+/* FIXME! This has to be written */
+#define _FP_SQRT_MEAT_4(R, S, T, X, q)
diff --git a/arch/sparc64/math-emu/op-common.h b/arch/sparc64/math-emu/op-common.h
index 8123e4c46..d4ce104f6 100644
--- a/arch/sparc64/math-emu/op-common.h
+++ b/arch/sparc64/math-emu/op-common.h
@@ -1,3 +1,4 @@
+
#define _FP_DECL(wc, X) \
_FP_I_TYPE X##_c, X##_s, X##_e; \
_FP_FRAC_DECL_##wc(X)
@@ -507,6 +508,29 @@ do { \
* Convert from FP to integer
*/
+/* "When a NaN, infinity, large positive argument >= 2147483648.0, or
+ * large negative argument <= -2147483649.0 is converted to an integer,
+ * the invalid_current bit...should be set and fp_exception_IEEE_754 should
+ * be raised. If the floating point invalid trap is disabled, no trap occurs
+ * and a numerical result is generated: if the sign bit of the operand
+ * is 0, the result is 2147483647; if the sign bit of the operand is 1,
+ * the result is -2147483648."
+ * Similarly for conversion to extended ints, except that the boundaries
+ * are >= 2^63, <= -(2^63 + 1), and the results are 2^63 + 1 for s=0 and
+ * -2^63 for s=1.
+ * -- SPARC Architecture Manual V9, Appendix B, which specifies how
+ * SPARCs resolve implementation dependencies in the IEEE-754 spec.
+ * I don't believe that the code below follows this. I'm not even sure
+ * it's right!
+ * It doesn't cope with needing to convert to an n bit integer when there
+ * is no n bit integer type. Fortunately gcc provides long long so this
+ * isn't a problem for sparc32.
+ * I have, however, fixed its NaN handling to conform as above.
+ * -- PMM 02/1998
+ * NB: rsigned is not 'is r declared signed?' but 'should the value stored
+ * in r be signed or unsigned?'. r is always(?) declared unsigned.
+ * Comments below are mine, BTW -- PMM
+ */
#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \
do { \
switch (X##_c) \
@@ -514,13 +538,14 @@ do { \
case FP_CLS_NORMAL: \
if (X##_e < 0) \
{ \
- case FP_CLS_NAN: \
+ /* case FP_CLS_NAN: see above! */ \
case FP_CLS_ZERO: \
r = 0; \
} \
else if (X##_e >= rsize - (rsigned != 0)) \
- { \
- case FP_CLS_INF: \
+ { /* overflow */ \
+ case FP_CLS_NAN: \
+ case FP_CLS_INF: \
if (rsigned) \
{ \
r = 1; \
@@ -604,6 +629,23 @@ do { \
/* Count leading zeros in a word. */
#ifndef __FP_CLZ
+#if _FP_W_TYPE_SIZE < 64
+/* this is just to shut the compiler up about shifts > word length -- PMM 02/1998 */
+#define __FP_CLZ(r, x) \
+ do { \
+ _FP_W_TYPE _t = (x); \
+ r = _FP_W_TYPE_SIZE - 1; \
+ if (_t > 0xffff) r -= 16; \
+ if (_t > 0xffff) _t >>= 16; \
+ if (_t > 0xff) r -= 8; \
+ if (_t > 0xff) _t >>= 8; \
+ if (_t & 0xf0) r -= 4; \
+ if (_t & 0xf0) _t >>= 4; \
+ if (_t & 0xc) r -= 2; \
+ if (_t & 0xc) _t >>= 2; \
+ if (_t & 0x2) r -= 1; \
+ } while (0)
+#else /* not _FP_W_TYPE_SIZE < 64 */
#define __FP_CLZ(r, x) \
do { \
_FP_W_TYPE _t = (x); \
@@ -620,9 +662,11 @@ do { \
if (_t & 0xc) _t >>= 2; \
if (_t & 0x2) r -= 1; \
} while (0)
-#endif
+#endif /* not _FP_W_TYPE_SIZE < 64 */
+#endif /* ndef __FP_CLZ */
#define _FP_DIV_HELP_imm(q, r, n, d) \
do { \
q = n / d, r = n % d; \
} while (0)
+
diff --git a/arch/sparc64/math-emu/quad.h b/arch/sparc64/math-emu/quad.h
index dfc3b4eea..48fcc798c 100644
--- a/arch/sparc64/math-emu/quad.h
+++ b/arch/sparc64/math-emu/quad.h
@@ -1,12 +1,17 @@
/*
* Definitions for IEEE Quad Precision
*/
-
-#if _FP_W_TYPE_SIZE < 64
-#error "Only stud muffins allowed, schmuck."
+#if _FP_W_TYPE_SIZE < 32
+/* It appears to be traditional to abuse 16bitters in these header files... */
+#error "Here's a nickel, kid. Go buy yourself a real computer."
#endif
+#if _FP_W_TYPE_SIZE < 64
+/* This is all terribly experimental and I don't know if it'll work properly -- PMM 02/1998 */
+#define _FP_FRACTBITS_Q (4*_FP_W_TYPE_SIZE)
+#else
#define _FP_FRACTBITS_Q (2*_FP_W_TYPE_SIZE)
+#endif
#define _FP_FRACBITS_Q 113
#define _FP_FRACXBITS_Q (_FP_FRACTBITS_Q - _FP_FRACBITS_Q)
@@ -23,6 +28,66 @@
#define _FP_OVERFLOW_Q \
((_FP_W_TYPE)1 << (_FP_WFRACBITS_Q % _FP_W_TYPE_SIZE))
+#if _FP_W_TYPE_SIZE < 64
+
+union _FP_UNION_Q
+{
+ long double flt;
+ struct
+ {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ unsigned sign : 1;
+ unsigned exp : _FP_EXPBITS_Q;
+ unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3);
+ unsigned long frac2 : _FP_W_TYPE_SIZE;
+ unsigned long frac1 : _FP_W_TYPE_SIZE;
+ unsigned long frac0 : _FP_W_TYPE_SIZE;
+#else
+ unsigned long frac0 : _FP_W_TYPE_SIZE;
+ unsigned long frac1 : _FP_W_TYPE_SIZE;
+ unsigned long frac2 : _FP_W_TYPE_SIZE;
+ unsigned long frac3 : _FP_FRACBITS_Q - (_FP_IMPLBIT_Q != 0)-(_FP_W_TYPE_SIZE * 3);
+ unsigned exp : _FP_EXPBITS_Q;
+ unsigned sign : 1;
+#endif /* not bigendian */
+ } bits __attribute__((packed));
+};
+
+
+#define FP_DECL_Q(X) _FP_DECL(4,X)
+#define FP_UNPACK_RAW_Q(X,val) _FP_UNPACK_RAW_4(Q,X,val)
+#define FP_PACK_RAW_Q(val,X) _FP_PACK_RAW_4(Q,val,X)
+
+#define FP_UNPACK_Q(X,val) \
+ do { \
+ _FP_UNPACK_RAW_4(Q,X,val); \
+ _FP_UNPACK_CANONICAL(Q,4,X); \
+ } while (0)
+
+#define FP_PACK_Q(val,X) \
+ do { \
+ _FP_PACK_CANONICAL(Q,4,X); \
+ _FP_PACK_RAW_4(Q,val,X); \
+ } while (0)
+
+#define FP_NEG_Q(R,X) _FP_NEG(Q,4,R,X)
+#define FP_ADD_Q(R,X,Y) _FP_ADD(Q,4,R,X,Y)
+/* single.h and double.h define FP_SUB_t this way too. However, _FP_SUB is
+ * never defined in op-common.h! Fortunately nobody seems to use the FP_SUB_t
+ * macros: I suggest a combination of FP_NEG and FP_ADD :-> -- PMM 02/1998
+ */
+#define FP_SUB_Q(R,X,Y) _FP_SUB(Q,4,R,X,Y)
+#define FP_MUL_Q(R,X,Y) _FP_MUL(Q,4,R,X,Y)
+#define FP_DIV_Q(R,X,Y) _FP_DIV(Q,4,R,X,Y)
+#define FP_SQRT_Q(R,X) _FP_SQRT(Q,4,R,X)
+
+#define FP_CMP_Q(r,X,Y,un) _FP_CMP(Q,4,r,X,Y,un)
+#define FP_CMP_EQ_Q(r,X,Y) _FP_CMP_EQ(Q,4,r,X,Y)
+
+#define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,4,r,X,rsz,rsg)
+#define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,4,X,r,rs,rt)
+
+#else /* not _FP_W_TYPE_SIZE < 64 */
union _FP_UNION_Q
{
long double flt /* __attribute__((mode(TF))) */ ;
@@ -69,3 +134,5 @@ union _FP_UNION_Q
#define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,2,r,X,rsz,rsg)
#define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,2,X,r,rs,rt)
+
+#endif /* not _FP_W_TYPE_SIZE < 64 */
diff --git a/arch/sparc64/math-emu/single.h b/arch/sparc64/math-emu/single.h
index fa7f386cd..f19d99451 100644
--- a/arch/sparc64/math-emu/single.h
+++ b/arch/sparc64/math-emu/single.h
@@ -3,7 +3,7 @@
*/
#if _FP_W_TYPE_SIZE < 32
-#error "Here's a nickle kid. Go buy yourself a real computer."
+#error "Here's a nickel kid. Go buy yourself a real computer."
#endif
#define _FP_FRACBITS_S 24
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
index 6bc52f3eb..21389e397 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc64/mm/fault.c
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.20 1997/08/04 16:16:51 davem Exp $
+/* $Id: fault.c,v 1.21 1998/03/25 10:43:20 jj Exp $
* arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -209,7 +209,7 @@ bad_area:
} else {
current->tss.sig_address = address;
current->tss.sig_desc = SUBSIG_NOMAPPING;
- send_sig(SIGSEGV, current, 1);
+ force_sig(SIGSEGV, current);
goto out;
}
unhandled_fault (address, current, regs);
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index b564dc0dc..e7d863997 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -1,8 +1,8 @@
-/* $Id: init.c,v 1.60 1998/01/10 18:19:51 ecd Exp $
+/* $Id: init.c,v 1.71 1998/03/27 07:00:08 davem Exp $
* arch/sparc64/mm/init.c
*
* Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
@@ -22,13 +22,20 @@
#include <asm/mmu_context.h>
#include <asm/vaddrs.h>
+/* Turn this off if you suspect some place in some physical memory hole
+ might get into page tables (something would be broken very much). */
+
+#define FREE_UNUSED_MEM_MAP
+
extern void show_net_buffers(void);
extern unsigned long device_scan(unsigned long);
struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
/* Ugly, but necessary... -DaveM */
-unsigned long phys_base, null_pmd_table, null_pte_table;
+unsigned long phys_base;
+unsigned int null_pte_table;
+unsigned long two_null_pmd_table, two_null_pte_table;
extern unsigned long empty_null_pmd_table;
extern unsigned long empty_null_pte_table;
@@ -42,12 +49,12 @@ extern void __bfill64(void *, unsigned long *);
static __inline__ void __init_pmd(pmd_t *pmdp)
{
- __bfill64((void *)pmdp, &null_pte_table);
+ __bfill64((void *)pmdp, &two_null_pte_table);
}
static __inline__ void __init_pgd(pgd_t *pgdp)
{
- __bfill64((void *)pgdp, &null_pmd_table);
+ __bfill64((void *)pgdp, &two_null_pmd_table);
}
/*
@@ -88,26 +95,36 @@ pte_t __bad_page(void)
void show_mem(void)
{
- int i,free = 0,total = 0,reserved = 0;
- int shared = 0;
+ int free = 0,total = 0,reserved = 0;
+ int shared = 0, cached = 0;
+ struct page *page, *end;
printk("\nMem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
- i = max_mapnr;
- while (i-- > 0) {
+ for (page = mem_map, end = mem_map + max_mapnr;
+ page < end; page++) {
+ if (PageSkip(page)) {
+ if (page->next_hash < page)
+ break;
+ page = page->next_hash;
+ }
total++;
- if (PageReserved(mem_map + i))
+ if (PageReserved(page))
reserved++;
- else if (!atomic_read(&mem_map[i].count))
+ else if (PageSwapCache(page))
+ cached++;
+ else if (!atomic_read(&page->count))
free++;
else
- shared += atomic_read(&mem_map[i].count) - 1;
+ shared += atomic_read(&page->count) - 1;
}
printk("%d pages of RAM\n",total);
printk("%d free pages\n",free);
printk("%d reserved pages\n",reserved);
printk("%d pages shared\n",shared);
+ printk("%d pages swap cached\n",cached);
+ printk("%ld pages in page table cache\n",pgtable_cache_size);
show_buffers();
#ifdef CONFIG_NET
show_net_buffers();
@@ -409,14 +426,10 @@ void mmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus
spin_unlock_irqrestore(&iommu->iommu_lock, flags);
}
-static char sfmmuinfo[512];
-
-char *mmu_info(void)
+int mmu_info(char *buf)
{
/* We'll do the rest later to make it nice... -DaveM */
- sprintf(sfmmuinfo, "MMU Type\t: Spitfire\n");
-
- return sfmmuinfo;
+ return sprintf(buf, "MMU Type\t: Spitfire\n");
}
static unsigned long mempool;
@@ -633,10 +646,7 @@ void get_new_mmu_context(struct mm_struct *mm, unsigned long *ctx)
}
#ifndef __SMP__
-unsigned long *pgd_quicklist = NULL;
-unsigned long *pmd_quicklist = NULL;
-unsigned long *pte_quicklist = NULL;
-unsigned long pgtable_cache_size = 0;
+struct pgtable_cache_struct pgt_quicklists;
#endif
pgd_t *get_pgd_slow(void)
@@ -653,7 +663,7 @@ pmd_t *get_pmd_slow(pgd_t *pgd, unsigned long offset)
{
pmd_t *pmd;
- pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
+ pmd = (pmd_t *) __get_free_page(GFP_DMA|GFP_KERNEL);
if(pmd) {
__init_pmd(pmd);
pgd_set(pgd, pmd);
@@ -666,9 +676,9 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
{
pte_t *pte;
- pte = (pte_t *) __get_free_page(GFP_KERNEL);
+ pte = (pte_t *) __get_free_page(GFP_DMA|GFP_KERNEL);
if(pte) {
- clear_page((unsigned long)pte);
+ memset((void *)pte, 0, PTE_TABLE_SIZE);
pmd_set(pmd, pte);
return pte + offset;
}
@@ -737,6 +747,7 @@ void sparc_ultra_unmapioaddr(unsigned long virt_addr)
pte_clear(ptep);
}
+#if NOTUSED
void sparc_ultra_dump_itlb(void)
{
int slot;
@@ -766,6 +777,7 @@ void sparc_ultra_dump_dtlb(void)
slot+2, spitfire_get_dtlb_tag(slot+2), spitfire_get_dtlb_data(slot+2));
}
}
+#endif
/* paging_init() sets up the page tables */
@@ -808,23 +820,30 @@ paging_init(unsigned long start_mem, unsigned long end_mem))
/* Now set kernel pgd to upper alias so physical page computations
* work.
*/
- init_mm.pgd += ((shift) / (sizeof(pgd_t *)));
+ init_mm.pgd += ((shift) / (sizeof(pgd_t)));
/* The funny offsets are to make page table operations much quicker and
* requite less state, see pgtable.h for gory details.
+ * pgtable.h assumes null_pmd_table is null_pte_table - PAGE_SIZE, lets
+ * check it now.
*/
- null_pmd_table=__pa(((unsigned long)&empty_null_pmd_table)+shift);
null_pte_table=__pa(((unsigned long)&empty_null_pte_table)+shift);
+ if (null_pmd_table != __pa(((unsigned long)&empty_null_pmd_table)+shift)) {
+ prom_printf("null_p{md|te}_table broken.\n");
+ prom_halt();
+ }
+ two_null_pmd_table = (((unsigned long)null_pmd_table) << 32) | null_pmd_table;
+ two_null_pte_table = (((unsigned long)null_pte_table) << 32) | null_pte_table;
pmdp = (pmd_t *) &empty_null_pmd_table;
- for(i = 0; i < 1024; i++)
+ for(i = 0; i < PTRS_PER_PMD; i++)
pmd_val(pmdp[i]) = null_pte_table;
- memset((void *) &empty_null_pte_table, 0, PAGE_SIZE);
+ memset((void *) &empty_null_pte_table, 0, PTE_TABLE_SIZE);
/* Now can init the kernel/bad page tables. */
- __bfill64((void *)swapper_pg_dir, &null_pmd_table);
- __bfill64((void *)&empty_bad_pmd_table, &null_pte_table);
+ __bfill64((void *)swapper_pg_dir, &two_null_pmd_table);
+ __bfill64((void *)&empty_bad_pmd_table, &two_null_pte_table);
/* We use mempool to create page tables, therefore adjust it up
* such that __pa() macros etc. work.
@@ -867,21 +886,34 @@ paging_init(unsigned long start_mem, unsigned long end_mem))
__initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
{
- unsigned long addr, tmp2 = 0;
-
- for(addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
- if(addr >= PAGE_OFFSET && addr < start_mem)
- addr = start_mem;
- for(tmp2=0; sp_banks[tmp2].num_bytes != 0; tmp2++) {
- unsigned long phys_addr = __pa(addr);
- unsigned long base = sp_banks[tmp2].base_addr;
- unsigned long limit = base + sp_banks[tmp2].num_bytes;
-
- if((phys_addr >= base) && (phys_addr < limit) &&
- ((phys_addr + PAGE_SIZE) < limit))
- mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
- if (phys_addr >= 0xf0000000)
- mem_map[MAP_NR(addr)].flags &= ~(1<<PG_DMA);
+ unsigned long tmp = 0, paddr, endaddr;
+ unsigned long end = __pa(end_mem);
+
+ for (paddr = __pa(start_mem); paddr < end; ) {
+ for (; sp_banks[tmp].num_bytes != 0; tmp++)
+ if (sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes > paddr)
+ break;
+ if (!sp_banks[tmp].num_bytes) {
+ mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip);
+ mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
+ return;
+ }
+
+ if (sp_banks[tmp].base_addr > paddr) {
+ /* Making a one or two pages PG_skip holes is not necessary */
+ if (sp_banks[tmp].base_addr - paddr > 2 * PAGE_SIZE) {
+ mem_map[paddr>>PAGE_SHIFT].flags |= (1<<PG_skip);
+ mem_map[paddr>>PAGE_SHIFT].next_hash = mem_map + (sp_banks[tmp].base_addr >> PAGE_SHIFT);
+ }
+ paddr = sp_banks[tmp].base_addr;
+ }
+
+ endaddr = sp_banks[tmp].base_addr + sp_banks[tmp].num_bytes;
+ while (paddr < endaddr) {
+ mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_reserved);
+ if (paddr >= 0xf0000000)
+ mem_map[paddr>>PAGE_SHIFT].flags &= ~(1<<PG_DMA);
+ paddr += PAGE_SIZE;
}
}
}
@@ -891,31 +923,65 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
int codepages = 0;
int datapages = 0;
int initpages = 0;
- unsigned long tmp2, addr;
+ unsigned long addr;
unsigned long alias_base = phys_base + PAGE_OFFSET - (long)(&empty_zero_page);
+ struct page *page, *end;
end_mem &= PAGE_MASK;
max_mapnr = MAP_NR(end_mem);
high_memory = (void *) end_mem;
start_mem = PAGE_ALIGN(start_mem);
- num_physpages = (start_mem - PAGE_OFFSET) >> PAGE_SHIFT;
+ num_physpages = 0;
+
+ if (phys_base) {
+ mem_map[0].flags |= (1<<PG_skip) | (1<<PG_reserved);
+ mem_map[0].next_hash = mem_map + (phys_base >> PAGE_SHIFT);
+ }
- addr = PAGE_OFFSET;
+ addr = PAGE_OFFSET + phys_base;
while(addr < start_mem) {
#ifdef CONFIG_BLK_DEV_INITRD
- if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) {
+ if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
- num_physpages--;
- } else
+ else
#endif
mem_map[MAP_NR(addr)].flags |= (1<<PG_reserved);
addr += PAGE_SIZE;
}
taint_real_pages(start_mem, end_mem);
+
+#ifdef FREE_UNUSED_MEM_MAP
+ end = mem_map + max_mapnr;
+ for (page = mem_map; page < end; page++) {
+ if (PageSkip(page)) {
+ unsigned long low, high;
+
+ low = PAGE_ALIGN((unsigned long)(page+1));
+ if (page->next_hash < page)
+ high = ((unsigned long)end) & PAGE_MASK;
+ else
+ high = ((unsigned long)page->next_hash) & PAGE_MASK;
+ while (low < high) {
+ mem_map[MAP_NR(low)].flags &= ~(1<<PG_reserved);
+ low += PAGE_SIZE;
+ }
+ }
+ }
+#endif
+
for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
- if(PageReserved(mem_map + MAP_NR(addr))) {
+ if (PageSkip(mem_map + MAP_NR(addr))) {
+ unsigned long next = mem_map[MAP_NR(addr)].next_hash - mem_map;
+
+ next = (next << PAGE_SHIFT) + PAGE_OFFSET;
+ if (next < addr || next >= end_mem)
+ break;
+ addr = next;
+ }
+ num_physpages++;
+ if (PageReserved(mem_map + MAP_NR(addr))) {
if ((addr < ((unsigned long) &etext) + alias_base) && (addr >= alias_base))
codepages++;
else if((addr >= ((unsigned long)&__init_begin) + alias_base)
@@ -926,7 +992,6 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
continue;
}
atomic_set(&mem_map[MAP_NR(addr)].count, 1);
- num_physpages++;
#ifdef CONFIG_BLK_DEV_INITRD
if (!initrd_start ||
(addr < initrd_start || addr >= initrd_end))
@@ -934,18 +999,16 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
free_page(addr);
}
- tmp2 = nr_free_pages << PAGE_SHIFT;
-
- printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n",
- tmp2 >> 10,
+ printk("Memory: %uk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n",
+ nr_free_pages << (PAGE_SHIFT-10),
codepages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10),
initpages << (PAGE_SHIFT-10),
PAGE_OFFSET, end_mem);
freepages.low = nr_free_pages >> 7;
- if(freepages.low < 16)
- freepages.low = 16;
+ if(freepages.low < 48)
+ freepages.low = 48;
freepages.low = freepages.low + (freepages.low >> 1);
freepages.high = freepages.low + freepages.low;
}
@@ -967,20 +1030,25 @@ void free_initmem (void)
void si_meminfo(struct sysinfo *val)
{
- int i;
+ struct page *page, *end;
- i = MAP_NR(high_memory);
val->totalram = 0;
val->sharedram = 0;
val->freeram = nr_free_pages << PAGE_SHIFT;
val->bufferram = buffermem;
- while (i-- > 0) {
- if (PageReserved(mem_map + i))
+ for (page = mem_map, end = mem_map + max_mapnr;
+ page < end; page++) {
+ if (PageSkip(page)) {
+ if (page->next_hash < page)
+ break;
+ page = page->next_hash;
+ }
+ if (PageReserved(page))
continue;
val->totalram++;
- if (!atomic_read(&mem_map[i].count))
+ if (!atomic_read(&page->count))
continue;
- val->sharedram += atomic_read(&mem_map[i].count) - 1;
+ val->sharedram += atomic_read(&page->count) - 1;
}
val->totalram <<= PAGE_SHIFT;
val->sharedram <<= PAGE_SHIFT;
diff --git a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c
index e6b4b2223..303b98996 100644
--- a/arch/sparc64/mm/modutil.c
+++ b/arch/sparc64/mm/modutil.c
@@ -1,7 +1,7 @@
-/* $Id: modutil.c,v 1.1 1997/06/27 14:53:35 jj Exp $
+/* $Id: modutil.c,v 1.3 1998/01/16 16:35:02 jj Exp $
* arch/sparc64/mm/modutil.c
*
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Based upon code written by Linus Torvalds and others.
*/
@@ -21,7 +21,7 @@ void module_unmap (void * addr)
if (!addr)
return;
if ((PAGE_SIZE-1) & (unsigned long) addr) {
- printk("Trying to vfree() bad address (%p)\n", addr);
+ printk("Trying to unmap module with bad address (%p)\n", addr);
return;
}
for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) {
@@ -35,6 +35,32 @@ void module_unmap (void * addr)
printk("Trying to unmap nonexistent module vm area (%p)\n", addr);
}
+void module_shrink(void * addr, unsigned long size)
+{
+ struct vm_struct *tmp;
+
+ if (!addr)
+ return;
+ if ((PAGE_SIZE-1) & (unsigned long) addr) {
+ printk("Trying to shrink module with bad address (%p)\n", addr);
+ return;
+ }
+ size = PAGE_ALIGN(size);
+ if (!size)
+ module_unmap(addr);
+ for (tmp = modvmlist; tmp; tmp = tmp->next) {
+ if (tmp->addr == addr) {
+ if (size > tmp->size - PAGE_SIZE) {
+ printk("Trying to expand module with module_shrink()\n");
+ return;
+ }
+ vmfree_area_pages(VMALLOC_VMADDR(tmp->addr)+size, tmp->size-size);
+ return;
+ }
+ }
+ printk("Trying to shrink nonexistent module vm area (%p)\n", addr);
+}
+
void * module_map (unsigned long size)
{
void * addr;
diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c
index 7ef17159d..6d53b8be2 100644
--- a/arch/sparc64/prom/bootstr.c
+++ b/arch/sparc64/prom/bootstr.c
@@ -1,8 +1,8 @@
-/* $Id: bootstr.c,v 1.4 1997/06/17 13:25:35 jj Exp $
+/* $Id: bootstr.c,v 1.5 1998/01/23 08:51:39 jj Exp $
* bootstr.c: Boot string/argument acquisition from the PROM.
*
* Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright(C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright(C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/string.h>
@@ -10,9 +10,9 @@
#include <asm/oplib.h>
#define BARG_LEN 256
-int bootstr_len __initdata = BARG_LEN;
-static int bootstr_valid __initdata = 0;
-static char bootstr_buf[BARG_LEN] __initdata = { 0 };
+int bootstr_len = BARG_LEN;
+static int bootstr_valid = 0;
+static char bootstr_buf[BARG_LEN] = { 0 };
__initfunc(char *
prom_getbootargs(void))
diff --git a/arch/sparc64/prom/init.c b/arch/sparc64/prom/init.c
index 7dcef7642..4c1bd1e00 100644
--- a/arch/sparc64/prom/init.c
+++ b/arch/sparc64/prom/init.c
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.7 1997/03/24 17:43:59 jj Exp $
+/* $Id: init.c,v 1.8 1998/03/15 10:14:44 ecd Exp $
* init.c: Initialize internal variables used by the PROM
* library functions.
*
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
+#include <linux/ctype.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
@@ -32,11 +33,13 @@ extern void prom_cif_init(void *, void *);
__initfunc(void prom_init(void *cif_handler, void *cif_stack))
{
- char buffer[80];
+ char buffer[80], *p;
+ int ints[3];
int node;
-
+ int i = 0;
+
prom_vers = PROM_P1275;
-
+
prom_cif_init(cif_handler, cif_stack);
prom_root_node = prom_getsibling(0);
@@ -46,34 +49,45 @@ __initfunc(void prom_init(void *cif_handler, void *cif_stack))
prom_chosen_node = prom_finddevice("/chosen");
if (!prom_chosen_node || prom_chosen_node == -1)
prom_halt();
-
+
prom_stdin = prom_getint (prom_chosen_node, "stdin");
prom_stdout = prom_getint (prom_chosen_node, "stdout");
node = prom_finddevice("/openprom");
if (!node || node == -1)
prom_halt();
-
+
prom_getstring (node, "version", buffer, sizeof (buffer));
-
+
prom_printf ("\n");
-
- if (strncmp (buffer, "OBP ", 4) || buffer[5] != '.' || buffer[7] != '.') {
- prom_printf ("Strange OBP version `%s'.\n", buffer);
- prom_halt ();
+
+ if (strncmp (buffer, "OBP ", 4))
+ goto strange_version;
+
+ /* Version field is expected to be 'OBP xx.yy.zz date...' */
+
+ p = buffer + 4;
+ while (p && isdigit(*p) && i < 3) {
+ ints[i++] = simple_strtoul(p, NULL, 0);
+ if ((p = strchr(p, '.')) != NULL)
+ p++;
}
- /* Version field is expected to be 'OBP x.y.z date...' */
-
- prom_rev = buffer[6] - '0';
- prom_prev = ((buffer[4] - '0') << 16) |
- ((buffer[6] - '0') << 8) |
- (buffer[8] - '0');
-
+ if (i != 3)
+ goto strange_version;
+
+ prom_rev = ints[1];
+ prom_prev = (ints[0] << 16) | (ints[1] << 8) | ints[2];
+
printk ("PROMLIB: Sun IEEE Boot Prom %s\n", buffer + 4);
-
+
prom_meminit();
prom_ranges_init();
/* Initialization successful. */
+ return;
+
+strange_version:
+ prom_printf ("Strange OBP version `%s'.\n", buffer);
+ prom_halt ();
}
diff --git a/arch/sparc64/prom/ranges.c b/arch/sparc64/prom/ranges.c
index 83f860d45..7b889bac1 100644
--- a/arch/sparc64/prom/ranges.c
+++ b/arch/sparc64/prom/ranges.c
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.8 1997/08/17 22:39:45 ecd Exp $
+/* $Id: ranges.c,v 1.10 1998/03/24 05:54:29 ecd Exp $
* ranges.c: Handle ranges in newer proms for obio/sbus.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -140,17 +140,61 @@ __initfunc(void prom_ebus_ranges_init(struct linux_ebus *ebus))
ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges));
}
+__initfunc(void prom_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();
+ }
+}
+
__initfunc(void prom_pbm_ranges_init(int pnode, struct linux_pbm_info *pbm))
{
int success;
pbm->num_pbm_ranges = 0;
- success = prom_getproperty(pbm->prom_node, "ranges",
+ success = prom_getproperty(pnode, "ranges",
(char *)&pbm->pbm_ranges,
sizeof(pbm->pbm_ranges));
if(success != -1)
pbm->num_pbm_ranges = (success/sizeof(struct linux_prom_pci_ranges));
}
+
+__initfunc(void prom_pbm_intmap_init(int pnode, struct linux_pbm_info *pbm))
+{
+ int success;
+
+ pbm->num_pbm_intmap = 0;
+ success = prom_getproperty(pnode, "interrupt-map",
+ (char *)pbm->pbm_intmap,
+ sizeof(pbm->pbm_intmap));
+ if (success == -1)
+ return;
+
+ pbm->num_pbm_intmap = (success/sizeof(struct linux_prom_pci_intmap));
+
+ success = prom_getproperty(pnode, "interrupt-map-mask",
+ (char *)&pbm->pbm_intmask,
+ sizeof(pbm->pbm_intmask));
+ if (success == -1) {
+ prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__);
+ prom_halt();
+ }
+}
#endif
void
diff --git a/arch/sparc64/solaris/Makefile b/arch/sparc64/solaris/Makefile
index 056909b6f..691601a3e 100644
--- a/arch/sparc64/solaris/Makefile
+++ b/arch/sparc64/solaris/Makefile
@@ -8,7 +8,7 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := solaris.o
-O_OBJS := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o
+O_OBJS := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o timod.o
ifeq ($(CONFIG_SOLARIS_EMUL),m)
M_OBJS := $(O_TARGET)
CPPFLAGS = $(MODFLAGS)
diff --git a/arch/sparc64/solaris/conv.h b/arch/sparc64/solaris/conv.h
index c806d3dc1..fa716f595 100644
--- a/arch/sparc64/solaris/conv.h
+++ b/arch/sparc64/solaris/conv.h
@@ -1,10 +1,11 @@
-/* $Id: conv.h,v 1.2 1997/09/03 12:29:13 jj Exp $
+/* $Id: conv.h,v 1.3 1998/03/26 08:46:13 jj Exp $
* conv.h: Utility macros for Solaris emulation
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/* #define DEBUG_SOLARIS */
+#define DEBUG_SOLARIS_KMALLOC
#ifndef __ASSEMBLY__
@@ -25,4 +26,12 @@ extern unsigned sunos_sys_table[];
#define SYS(name) ((long)sys_call_table[__NR_##name])
#define SUNOS(x) ((long)sunos_sys_table[x])
+#ifdef DEBUG_SOLARIS
+#define SOLD(s) printk("%s,%d,%s(): %s\n",__FILE__,__LINE__,__FUNCTION__,(s))
+#define SOLDD(s) printk("solaris: "); printk s
+#else
+#define SOLD(s)
+#define SOLDD(s)
+#endif
+
#endif /* __ASSEMBLY__ */
diff --git a/arch/sparc64/solaris/entry64.S b/arch/sparc64/solaris/entry64.S
index 414506522..53d825e6d 100644
--- a/arch/sparc64/solaris/entry64.S
+++ b/arch/sparc64/solaris/entry64.S
@@ -1,7 +1,7 @@
-/* $Id: entry64.S,v 1.4 1997/09/09 17:13:50 jj Exp $
+/* $Id: entry64.S,v 1.5 1998/03/26 08:46:15 jj Exp $
* entry64.S: Solaris syscall emulation entry point.
*
- * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
*/
@@ -29,8 +29,11 @@ solaris_syscall_trace:
mov %i4, %o4
srl %i1, 0, %o1
mov %i5, %o5
- b,pt %xcc, 2f
+ andcc %l3, 1, %g0
+ be,pt %icc, 2f
srl %i2, 0, %o2
+ b,pt %xcc, 2f
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
solaris_sucks:
/* Solaris is a big system which needs to be able to do all the things
@@ -59,9 +62,9 @@ solaris_reg:
mov %i4, %o4
linux_syscall_for_solaris:
- sll %l7, 2, %l4
+ sll %l3, 2, %l4
ba,pt %xcc, 10f
- lduw [%l6 + %l4], %l7
+ lduw [%l6 + %l4], %l3
/* Solaris system calls enter here... */
.align 32
@@ -78,18 +81,18 @@ solaris_sparc_syscall:
cmp %l0, 1
bne,pn %icc, solaris_reg
1: srl %i0, 0, %o0
- lduw [%l7 + %l4], %l7
+ lduw [%l7 + %l4], %l3
srl %i1, 0, %o1
ldx [%g6 + AOFF_task_flags], %l5
- cmp %l7, NR_SYSCALLS
+ cmp %l3, NR_SYSCALLS
bleu,a,pn %xcc, linux_syscall_for_solaris
sethi %hi(sys_call_table32), %l6
- andcc %l7, 1, %g0
+ andcc %l3, 1, %g0
bne,a,pn %icc, 10f
add %sp, STACK_BIAS + REGWIN_SZ, %o0
10: srl %i2, 0, %o2
mov %i5, %o5
- andn %l7, 3, %l7
+ andn %l3, 3, %l7
andcc %l5, 0x20, %g0
bne,pn %icc, solaris_syscall_trace
mov %i0, %l5
@@ -110,12 +113,20 @@ ret_from_solaris:
andn %g3, %g2, %g3
stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]
bne,pn %icc, solaris_syscall_trace2
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
- add %l1, 0x4, %l2 !npc = npc+4
- stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
- clr %l6
+ ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1
+ andcc %l1, 1, %g0
+ bne,pn %icc, 2f
+ clr %l6
+ add %l1, 0x4, %l2
+ stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ! pc = npc
call rtrap
- stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+ stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc+4
+
+ /* When tnpc & 1, this comes from setcontext and we don't want to advance pc */
+2: andn %l1, 3, %l1
+ call rtrap
+ stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc&~3
+
1:
/* System call failure, set Carry condition code.
* Also, get abs(errno) to return to the process.
@@ -134,15 +145,20 @@ ret_from_solaris:
mov 1, %l6
stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]
bne,pn %icc, solaris_syscall_trace2
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
- add %l1, 0x4, %l2 !npc = npc+4
-
- stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
+ ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1
+ andcc %l1, 1, %g0
+ bne,pn %icc, 2b
+ add %l1, 0x4, %l2
+ stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] ! pc = npc
call rtrap
- stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+ stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] !npc = npc+4
+
solaris_syscall_trace2:
call syscall_trace
add %l1, 0x4, %l2 /* npc = npc+4 */
+ andcc %l1, 1, %g0
+ bne,pn %icc, 2b
+ nop
stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
call rtrap
stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c
index 4d1448809..39e69d242 100644
--- a/arch/sparc64/solaris/fs.c
+++ b/arch/sparc64/solaris/fs.c
@@ -1,11 +1,13 @@
-/* $Id: fs.c,v 1.6 1997/10/13 03:54:05 davem Exp $
+/* $Id: fs.c,v 1.8 1998/03/29 10:11:02 davem Exp $
* fs.c: fs related syscall emulation for Solaris
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/types.h>
+#include <linux/sched.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/stat.h>
#include <linux/smp_lock.h>
#include <linux/limits.h>
@@ -339,9 +341,12 @@ asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf)
int error;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- error = -EBADF;
- else if (!(dentry = file->f_dentry))
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ if (!(dentry = file->f_dentry))
error = -ENOENT;
else if (!(inode = dentry->d_inode))
error = -ENOENT;
@@ -351,6 +356,8 @@ asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf)
error = -ENOSYS;
else
error = report_statvfs(inode, buf);
+ fput(file);
+out:
unlock_kernel();
return error;
}
@@ -516,7 +523,7 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
newattrs.ia_mode &= ~S_ISGID;
newattrs.ia_valid |= ATTR_MODE;
}
- DQUOT_TRANSFER(inode, newattrs);
+ DQUOT_TRANSFER(dentry, newattrs);
out:
return error;
}
@@ -563,13 +570,15 @@ asmlinkage int solaris_pread(int fd, u32 buf, u32 nbyte, s32 offset)
lock_kernel();
retval = -EBADF;
- if (fd >= NR_OPEN ||
- !(file = current->files->fd[fd]))
+ file = fget(fd);
+ if (!file)
goto bad;
+
temp = file->f_pos;
if (temp != offset) {
retval = sys_lseek(fd, offset, 0);
- if (retval < 0) goto bad;
+ if (retval < 0)
+ goto out_putf;
}
retval = sys_read(fd, (char *)A(buf), nbyte);
if (file->f_pos != temp) {
@@ -578,6 +587,9 @@ asmlinkage int solaris_pread(int fd, u32 buf, u32 nbyte, s32 offset)
else
sys_lseek(fd, temp, 0);
}
+
+out_putf:
+ fput(file);
bad:
unlock_kernel();
return retval;
@@ -595,13 +607,15 @@ asmlinkage int solaris_pwrite(int fd, u32 buf, u32 nbyte, s32 offset)
lock_kernel();
retval = -EBADF;
- if (fd >= NR_OPEN ||
- !(file = current->files->fd[fd]))
+ file = fget(fd);
+ if (!file)
goto bad;
+
temp = file->f_pos;
if (temp != offset) {
retval = sys_lseek(fd, offset, 0);
- if (retval < 0) goto bad;
+ if (retval < 0)
+ goto out_putf;
}
retval = sys_write(fd, (char *)A(buf), nbyte);
if (file->f_pos != temp) {
@@ -610,6 +624,9 @@ asmlinkage int solaris_pwrite(int fd, u32 buf, u32 nbyte, s32 offset)
else
sys_lseek(fd, temp, 0);
}
+
+out_putf:
+ fput(file);
bad:
unlock_kernel();
return retval;
diff --git a/arch/sparc64/solaris/ioctl.c b/arch/sparc64/solaris/ioctl.c
index c5ef7b99f..242d1e914 100644
--- a/arch/sparc64/solaris/ioctl.c
+++ b/arch/sparc64/solaris/ioctl.c
@@ -1,7 +1,8 @@
-/* $Id: ioctl.c,v 1.4 1997/09/18 10:38:24 rth Exp $
+/* $Id: ioctl.c,v 1.10 1998/03/29 10:11:00 davem Exp $
* ioctl.c: Solaris ioctl emulation.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
*
* Streams & timod emulation based on code
* Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
@@ -15,14 +16,16 @@
#include <linux/smp_lock.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
#include <asm/termios.h>
#include "conv.h"
+#include "socksys.h"
-extern char * getname32(u32 filename);
+extern char *getname32(u32 filename);
#define putname32 putname
extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd,
@@ -31,6 +34,11 @@ extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd,
u32 arg);
asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+extern int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len,
+ char *data_buf, int data_len, int flags);
+extern int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, int *ctl_len,
+ char *data_buf, int data_maxlen, int *data_len, int *flags);
+
/* termio* stuff {{{ */
struct solaris_termios {
@@ -231,31 +239,303 @@ struct strioctl {
u32 data;
};
+struct solaris_si_sockparams {
+ int sp_family;
+ int sp_type;
+ int sp_protocol;
+};
+
+struct solaris_o_si_udata {
+ int tidusize;
+ int addrsize;
+ int optsize;
+ int etsdusize;
+ int servtype;
+ int so_state;
+ int so_options;
+ int tsdusize;
+};
+
+struct solaris_si_udata {
+ int tidusize;
+ int addrsize;
+ int optsize;
+ int etsdusize;
+ int servtype;
+ int so_state;
+ int so_options;
+ int tsdusize;
+ struct solaris_si_sockparams sockparams;
+};
+
+#define SOLARIS_MODULE_TIMOD 0
+#define SOLARIS_MODULE_SOCKMOD 1
+#define SOLARIS_MODULE_MAX 2
+
+static struct module_info {
+ const char *name;
+ /* can be expanded further if needed */
+} module_table[ SOLARIS_MODULE_MAX + 1 ] = {
+ /* the ordering here must match the module numbers above! */
+ { "timod" },
+ { "sockmod" },
+ { NULL }
+};
+
+static inline int solaris_sockmod(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ struct inode *ino;
+ /* I wonder which of these tests are superfluous... --patrik */
+ if (! current->files->fd[fd] ||
+ ! current->files->fd[fd]->f_dentry ||
+ ! (ino = current->files->fd[fd]->f_dentry->d_inode) ||
+ ! ino->i_sock)
+ return TBADF;
+
+ switch (cmd & 0xff) {
+ case 109: /* SI_SOCKPARAMS */
+ {
+ struct solaris_si_sockparams si;
+ if (copy_from_user (&si, (struct solaris_si_sockparams *) A(arg), sizeof(si)))
+ return (EFAULT << 8) | TSYSERR;
+
+ /* Should we modify socket ino->socket_i.ops and type? */
+ return 0;
+ }
+ case 110: /* SI_GETUDATA */
+ {
+ int etsdusize, servtype;
+ switch (ino->u.socket_i.type) {
+ case SOCK_STREAM:
+ etsdusize = 1;
+ servtype = 2;
+ break;
+ default:
+ etsdusize = -2;
+ servtype = 3;
+ break;
+ }
+ if (put_user(16384, &((struct solaris_si_udata *)A(arg))->tidusize) ||
+ __put_user(sizeof(struct sockaddr), &((struct solaris_si_udata *)A(arg))->addrsize) ||
+ __put_user(-1, &((struct solaris_si_udata *)A(arg))->optsize) ||
+ __put_user(etsdusize, &((struct solaris_si_udata *)A(arg))->etsdusize) ||
+ __put_user(servtype, &((struct solaris_si_udata *)A(arg))->servtype) ||
+ __put_user(0, &((struct solaris_si_udata *)A(arg))->so_state) ||
+ __put_user(0, &((struct solaris_si_udata *)A(arg))->so_options) ||
+ __put_user(16384, &((struct solaris_si_udata *)A(arg))->tsdusize) ||
+ __put_user(ino->u.socket_i.ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_family) ||
+ __put_user(ino->u.socket_i.type, &((struct solaris_si_udata *)A(arg))->sockparams.sp_type) ||
+ __put_user(ino->u.socket_i.ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_protocol))
+ return (EFAULT << 8) | TSYSERR;
+ return 0;
+ }
+ case 101: /* O_SI_GETUDATA */
+ {
+ int etsdusize, servtype;
+ switch (ino->u.socket_i.type) {
+ case SOCK_STREAM:
+ etsdusize = 1;
+ servtype = 2;
+ break;
+ default:
+ etsdusize = -2;
+ servtype = 3;
+ break;
+ }
+ if (put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tidusize) ||
+ __put_user(sizeof(struct sockaddr), &((struct solaris_o_si_udata *)A(arg))->addrsize) ||
+ __put_user(-1, &((struct solaris_o_si_udata *)A(arg))->optsize) ||
+ __put_user(etsdusize, &((struct solaris_o_si_udata *)A(arg))->etsdusize) ||
+ __put_user(servtype, &((struct solaris_o_si_udata *)A(arg))->servtype) ||
+ __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_state) ||
+ __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_options) ||
+ __put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tsdusize))
+ return (EFAULT << 8) | TSYSERR;
+ return 0;
+ }
+ case 102: /* SI_SHUTDOWN */
+ case 103: /* SI_LISTEN */
+ case 104: /* SI_SETMYNAME */
+ case 105: /* SI_SETPEERNAME */
+ case 106: /* SI_GETINTRANSIT */
+ case 107: /* SI_TCL_LINK */
+ case 108: /* SI_TCL_UNLINK */
+ }
+ return TNOTSUPPORT;
+}
+
+static inline int solaris_timod(unsigned int fd, unsigned int cmd, u32 arg,
+ int len, int *len_p)
+{
+ struct file *filp;
+ struct inode *ino;
+ int ret;
+
+ filp = current->files->fd[fd];
+ if (! filp ||
+ ! (ino = filp->f_dentry->d_inode) ||
+ ! ino->i_sock)
+ return TBADF;
+
+ switch (cmd & 0xff) {
+ case 141: /* TI_OPTMGMT */
+ {
+ int i;
+ u32 prim;
+ SOLD("TI_OPMGMT entry");
+ ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0);
+ SOLD("timod_putmsg() returned");
+ if (ret)
+ return (-ret << 8) | TSYSERR;
+ i = MSG_HIPRI;
+ SOLD("calling timod_getmsg()");
+ ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+ SOLD("timod_getmsg() returned");
+ if (ret)
+ return (-ret << 8) | TSYSERR;
+ SOLD("ret ok");
+ if (get_user(prim, (u32 *)A(arg)))
+ return (EFAULT << 8) | TSYSERR;
+ SOLD("got prim");
+ if (prim == T_ERROR_ACK) {
+ u32 tmp, tmp2;
+ SOLD("prim is T_ERROR_ACK");
+ if (get_user(tmp, (u32 *)A(arg)+3) ||
+ get_user(tmp2, (u32 *)A(arg)+2))
+ return (EFAULT << 8) | TSYSERR;
+ return (tmp2 << 8) | tmp;
+ }
+ SOLD("TI_OPMGMT return 0");
+ return 0;
+ }
+ case 142: /* TI_BIND */
+ {
+ int i;
+ u32 prim;
+ SOLD("TI_BIND entry");
+ ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0);
+ SOLD("timod_putmsg() returned");
+ if (ret)
+ return (-ret << 8) | TSYSERR;
+ len = 1024; /* Solaris allows arbitrary return size */
+ i = MSG_HIPRI;
+ SOLD("calling timod_getmsg()");
+ ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+ SOLD("timod_getmsg() returned");
+ if (ret)
+ return (-ret << 8) | TSYSERR;
+ SOLD("ret ok");
+ if (get_user(prim, (u32 *)A(arg)))
+ return (EFAULT << 8) | TSYSERR;
+ SOLD("got prim");
+ if (prim == T_ERROR_ACK) {
+ u32 tmp, tmp2;
+ SOLD("prim is T_ERROR_ACK");
+ if (get_user(tmp, (u32 *)A(arg)+3) ||
+ get_user(tmp2, (u32 *)A(arg)+2))
+ return (EFAULT << 8) | TSYSERR;
+ return (tmp2 << 8) | tmp;
+ }
+ SOLD("no ERROR_ACK requested");
+ if (prim != T_OK_ACK)
+ return TBADSEQ;
+ SOLD("OK_ACK requested");
+ i = MSG_HIPRI;
+ SOLD("calling timod_getmsg()");
+ ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+ SOLD("timod_getmsg() returned");
+ if (ret)
+ return (-ret << 8) | TSYSERR;
+ SOLD("TI_BIND return ok");
+ return 0;
+ }
+ case 140: /* TI_GETINFO */
+ case 143: /* TI_UNBIND */
+ case 144: /* TI_GETMYNAME */
+ case 145: /* TI_GETPEERNAME */
+ case 146: /* TI_SETMYNAME */
+ case 147: /* TI_SETPEERNAME */
+ }
+ return TNOTSUPPORT;
+}
+
static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg)
{
char *p;
int ret;
mm_segment_t old_fs;
struct strioctl si;
-
+ struct inode *ino;
+ struct file *filp;
+ struct sol_socket_struct *sock;
+ struct module_info *mi;
+
+ filp = current->files->fd[fd];
+ if (! filp ||
+ ! (ino = filp->f_dentry->d_inode) ||
+ ! ino->i_sock)
+ return -EBADF;
+ sock = filp->private_data;
+ if (! sock) {
+ printk("solaris_S: NULL private_data\n");
+ return -EBADF;
+ }
+ if (sock->magic != SOLARIS_SOCKET_MAGIC) {
+ printk("solaris_S: invalid magic\n");
+ return -EBADF;
+ }
+
+
switch (cmd & 0xff) {
case 1: /* I_NREAD */
return -ENOSYS;
case 2: /* I_PUSH */
+ {
p = getname32 (arg);
if (IS_ERR (p))
return PTR_ERR(p);
+ ret = -EINVAL;
+ for (mi = module_table; mi->name; mi++) {
+ if (strcmp(mi->name, p) == 0) {
+ sol_module m;
+ if (sock->modcount >= MAX_NR_STREAM_MODULES) {
+ ret = -ENXIO;
+ break;
+ }
+ m = (sol_module) (mi - module_table);
+ sock->module[sock->modcount++] = m;
+ ret = 0;
+ break;
+ }
+ }
putname32 (p);
- return 0;
+ return ret;
+ }
case 3: /* I_POP */
+ if (sock->modcount <= 0) return -EINVAL;
+ sock->modcount--;
return 0;
+ case 4: /* I_LOOK */
+ {
+ const char *p;
+ if (sock->modcount <= 0) return -EINVAL;
+ p = module_table[(unsigned)sock->module[sock->modcount]].name;
+ if (copy_to_user ((char *)A(arg), p, strlen(p)))
+ return -EFAULT;
+ return 0;
+ }
case 5: /* I_FLUSH */
return 0;
case 8: /* I_STR */
- if (copy_from_user (&si, (struct strioctl *)A(arg), sizeof(struct strioctl)))
+ if (copy_from_user(&si, (struct strioctl *)A(arg), sizeof(struct strioctl)))
return -EFAULT;
+ /* We ignore what module is actually at the top of stack. */
switch ((si.cmd >> 8) & 0xff) {
+ case 'I':
+ return solaris_sockmod(fd, si.cmd, si.data);
case 'T':
+ return solaris_timod(fd, si.cmd, si.data, si.len,
+ &((struct strioctl*)A(arg))->len);
default:
return solaris_ioctl(fd, si.cmd, si.data);
}
@@ -269,12 +549,25 @@ static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg)
if (ret == current->pid) return 0x3ff;
else return -EINVAL;
case 11: /* I_FIND */
+ {
+ int i;
p = getname32 (arg);
if (IS_ERR (p))
return PTR_ERR(p);
- ret = !strcmp(p, "timod");
+ ret = 0;
+ for (i = 0; i < sock->modcount; i++) {
+ unsigned m = sock->module[i];
+ if (strcmp(module_table[m].name, p) == 0) {
+ ret = 1;
+ break;
+ }
+ }
putname32 (p);
return ret;
+ }
+ case 19: /* I_SWROPT */
+ case 32: /* I_SETCLTIME */
+ return 0; /* Lie */
}
return -ENOSYS;
}
@@ -287,7 +580,8 @@ static inline int solaris_s(unsigned int fd, unsigned int cmd, u32 arg)
return 0; /* We don't support them */
case 1: /* SIOCGHIWAT */
case 3: /* SIOCGLOWAT */
- put_user_ret (0, (u32 *)A(arg), -EFAULT);
+ if (put_user (0, (u32 *)A(arg)))
+ return -EFAULT;
return 0; /* Lie */
case 7: /* SIOCATMARK */
return sys_ioctl(fd, SIOCATMARK, arg);
@@ -368,8 +662,10 @@ static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg)
ret = sys_socketcall(((cmd & 0xff) == 52) ? SYS_GETSOCKNAME : SYS_GETPEERNAME,
args);
set_fs(old_fs);
- if (ret >= 0)
- copy_to_user_ret((char *)A(arg), &uaddr, uaddr_len, -EFAULT);
+ if (ret >= 0) {
+ if (copy_to_user((char *)A(arg), &uaddr, uaddr_len))
+ return -EFAULT;
+ }
return ret;
}
#if 0
@@ -382,7 +678,8 @@ static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg)
int i = 0;
for (d = dev_base; d; d = d->next) i++;
- put_user_ret (i, (int *)A(arg), -EFAULT);
+ if (put_user (i, (int *)A(arg)))
+ return -EFAULT;
return 0;
}
}
@@ -393,14 +690,13 @@ static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg)
asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
{
- struct file * filp;
+ struct file *filp;
int error = -EBADF;
lock_kernel();
- if(fd >= NR_OPEN) goto out;
-
- filp = current->files->fd[fd];
- if(!filp) goto out;
+ filp = fcheck(fd);
+ if (!filp)
+ goto out;
error = -EFAULT;
switch ((cmd >> 8) & 0xff) {
@@ -410,6 +706,7 @@ asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case 'r': error = solaris_r(fd, cmd, arg); break;
case 's': error = solaris_s(fd, cmd, arg); break;
case 't': error = solaris_t(fd, cmd, arg); break;
+ case 'f': error = sys_ioctl(fd, cmd, arg); break;
default:
error = -ENOSYS;
break;
diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c
index 7a6fb5969..4179f54b3 100644
--- a/arch/sparc64/solaris/misc.c
+++ b/arch/sparc64/solaris/misc.c
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.6 1997/12/14 23:40:15 ecd Exp $
+/* $Id: misc.c,v 1.10 1998/04/01 05:16:06 davem Exp $
* misc.c: Miscelaneous syscall emulation for Solaris
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -106,7 +106,7 @@ static char *machine(void)
static char *platform(char *buffer)
{
- int i;
+ int i, len;
struct {
char *platform;
int id_machtype;
@@ -128,7 +128,9 @@ static char *platform(char *buffer)
};
*buffer = 0;
- prom_getproperty(prom_root_node, "name", buffer, 256);
+ len = prom_getproperty(prom_root_node, "name", buffer, 256);
+ if(len > 0)
+ buffer[len] = 0;
if (*buffer) {
char *p;
@@ -145,10 +147,13 @@ static char *platform(char *buffer)
static char *serial(char *buffer)
{
int node = prom_getchild(prom_root_node);
+ int len;
node = prom_searchsiblings(node, "options");
*buffer = 0;
- prom_getproperty(node, "system-board-serial#", buffer, 256);
+ len = prom_getproperty(node, "system-board-serial#", buffer, 256);
+ if(len > 0)
+ buffer[len] = 0;
if (!*buffer)
return "4512348717234";
else
@@ -268,6 +273,8 @@ asmlinkage int solaris_sysinfo(int cmd, u32 buf, s32 count)
#define SOLARIS_CONFIG_PHYS_PAGES 26
#define SOLARIS_CONFIG_AVPHYS_PAGES 27
+extern unsigned prom_cpu_nodes[NR_CPUS];
+
asmlinkage int solaris_sysconf(int id)
{
switch (id) {
@@ -279,9 +286,8 @@ asmlinkage int solaris_sysconf(int id)
case SOLARIS_CONFIG_XOPEN_VER: return 3;
case SOLARIS_CONFIG_CLK_TCK:
case SOLARIS_CONFIG_PROF_TCK:
- return prom_getintdefault(
- linux_cpus[smp_processor_id()].prom_node,
- "clock-frequency", 167000000);
+ return prom_getintdefault(prom_cpu_nodes[smp_processor_id()],
+ "clock-frequency", 167000000);
#ifdef __SMP__
case SOLARIS_CONFIG_NPROC_CONF: return NR_CPUS;
case SOLARIS_CONFIG_NPROC_ONLN: return smp_num_cpus;
@@ -362,6 +368,14 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid)
return -EINVAL;
}
+asmlinkage int solaris_gettimeofday(u32 tim)
+{
+ int (*sys_gettimeofday)(struct timeval *, struct timezone *) =
+ (int (*)(struct timeval *, struct timezone *))SYS(gettimeofday);
+
+ return sys_gettimeofday((struct timeval *)(u64)tim, NULL);
+}
+
asmlinkage int do_sol_unimplemented(struct pt_regs *regs)
{
printk ("Unimplemented Solaris syscall %d %08x %08x %08x %08x\n",
@@ -401,10 +415,13 @@ struct exec_domain solaris_exec_domain = {
NULL
};
+extern int init_socksys(void);
+
#ifdef MODULE
-MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz)");
+MODULE_AUTHOR("Jakub Jelinek (jj@ultra.linux.cz), Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)");
MODULE_DESCRIPTION("Solaris binary emulation module");
+EXPORT_NO_SYMBOLS;
#ifdef __sparc_v9__
extern u32 tl0_solaris[8];
@@ -416,12 +433,13 @@ extern u32 tl0_solaris[8];
extern u32 solaris_sparc_syscall[];
extern u32 solaris_syscall[];
-extern int init_socksys(void);
extern void cleanup_socksys(void);
int init_module(void)
{
int ret;
+
+ SOLDD(("Solaris module at %p\n", solaris_sparc_syscall));
register_exec_domain(&solaris_exec_domain);
if ((ret = init_socksys())) {
unregister_exec_domain(&solaris_exec_domain);
diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c
index 3a18916c9..523073a6d 100644
--- a/arch/sparc64/solaris/socksys.c
+++ b/arch/sparc64/solaris/socksys.c
@@ -1,7 +1,8 @@
-/* $Id: socksys.c,v 1.2 1997/09/08 11:29:38 jj Exp $
+/* $Id: socksys.c,v 1.7 1998/03/29 10:11:04 davem Exp $
* socksys.c: /dev/inet/ stuff for Solaris emulation.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997, 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
* Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
*/
@@ -12,14 +13,17 @@
#include <linux/smp_lock.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/file.h>
+#include <linux/malloc.h>
#include <asm/uaccess.h>
#include <asm/termios.h>
#include "conv.h"
+#include "socksys.h"
extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg);
@@ -30,6 +34,20 @@ IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_RAW,
0, 0, 0, 0, 0, 0,
};
+#ifndef DEBUG_SOLARIS_KMALLOC
+
+#define mykmalloc kmalloc
+#define mykfree kfree
+
+#else
+
+extern void * mykmalloc(size_t s, int gfp);
+extern void mykfree(void *);
+
+#endif
+
+static unsigned int (*sock_poll)(struct file *, poll_table *);
+
static struct file_operations socksys_file_ops = {
NULL, /* lseek */
NULL, /* read */
@@ -48,6 +66,7 @@ static int socksys_open(struct inode * inode, struct file * filp)
struct dentry *dentry;
int (*sys_socket)(int,int,int) =
(int (*)(int,int,int))SUNOS(97);
+ struct sol_socket_struct * sock;
family = ((MINOR(inode->i_rdev) >> 4) & 0xf);
switch (family) {
@@ -68,30 +87,78 @@ static int socksys_open(struct inode * inode, struct file * filp)
protocol = 0;
break;
}
+
fd = sys_socket(family, type, protocol);
- if (fd < 0) return fd;
+ if (fd < 0)
+ return fd;
+ /*
+ * N.B. The following operations are not legal!
+ * Try instead:
+ * d_delete(filp->f_dentry), then d_instantiate with sock inode
+ */
dentry = filp->f_dentry;
- filp->f_dentry = current->files->fd[fd]->f_dentry;
+ filp->f_dentry = dget(fcheck(fd)->f_dentry);
filp->f_dentry->d_inode->i_rdev = inode->i_rdev;
filp->f_dentry->d_inode->i_flock = inode->i_flock;
filp->f_dentry->d_inode->u.socket_i.file = filp;
filp->f_op = &socksys_file_ops;
+ sock = (struct sol_socket_struct*)
+ mykmalloc(sizeof(struct sol_socket_struct), GFP_KERNEL);
+ if (!sock) return -ENOMEM;
+ SOLDD(("sock=%016lx(%016lx)\n", sock, filp));
+ sock->magic = SOLARIS_SOCKET_MAGIC;
+ sock->modcount = 0;
+ sock->state = TS_UNBND;
+ sock->offset = 0;
+ sock->pfirst = sock->plast = NULL;
+ filp->private_data = sock;
+ SOLDD(("filp->private_data %016lx\n", filp->private_data));
+
+ sys_close(fd);
dput(dentry);
- FD_CLR(fd, &current->files->close_on_exec);
- FD_CLR(fd, &current->files->open_fds);
- put_filp(current->files->fd[fd]);
- current->files->fd[fd] = NULL;
return 0;
}
static int socksys_release(struct inode * inode, struct file * filp)
{
+ struct sol_socket_struct * sock;
+ struct T_primsg *it;
+
+ /* XXX: check this */
+ sock = (struct sol_socket_struct *)filp->private_data;
+ SOLDD(("sock release %016lx(%016lx)\n", sock, filp));
+ it = sock->pfirst;
+ while (it) {
+ struct T_primsg *next = it->next;
+
+ SOLDD(("socksys_release %016lx->%016lx\n", it, next));
+ mykfree((char*)it);
+ it = next;
+ }
+ filp->private_data = NULL;
+ SOLDD(("socksys_release %016lx\n", sock));
+ mykfree((char*)sock);
return 0;
}
static unsigned int socksys_poll(struct file * filp, poll_table * wait)
{
- return 0;
+ struct inode *ino;
+ unsigned int mask = 0;
+
+ ino=filp->f_dentry->d_inode;
+ if (ino && ino->i_sock) {
+ struct sol_socket_struct *sock;
+ sock = (struct sol_socket_struct*)filp->private_data;
+ if (sock && sock->pfirst) {
+ mask |= POLLIN | POLLRDNORM;
+ if (sock->pfirst->pri == MSG_HIPRI)
+ mask |= POLLPRI;
+ }
+ }
+ if (sock_poll)
+ mask |= (*sock_poll)(filp, wait);
+ return mask;
}
static struct file_operations socksys_fops = {
@@ -110,6 +177,7 @@ __initfunc(int
init_socksys(void))
{
int ret;
+ struct file * file;
int (*sys_socket)(int,int,int) =
(int (*)(int,int,int))SUNOS(97);
int (*sys_close)(unsigned int) =
@@ -125,8 +193,11 @@ init_socksys(void))
printk ("Couldn't create socket\n");
return ret;
}
- socksys_file_ops = *current->files->fd[ret]->f_op;
+ file = fcheck(ret);
+ /* N.B. Is this valid? Suppose the f_ops are in a module ... */
+ socksys_file_ops = *file->f_op;
sys_close(ret);
+ sock_poll = socksys_file_ops.poll;
socksys_file_ops.poll = socksys_poll;
socksys_file_ops.release = socksys_release;
return 0;
diff --git a/arch/sparc64/solaris/socksys.h b/arch/sparc64/solaris/socksys.h
new file mode 100644
index 000000000..5d1b78ec1
--- /dev/null
+++ b/arch/sparc64/solaris/socksys.h
@@ -0,0 +1,208 @@
+/* $Id: socksys.h,v 1.2 1998/03/26 08:46:07 jj Exp $
+ * socksys.h: Definitions for STREAMS modules emulation code.
+ *
+ * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
+ */
+
+#define MSG_HIPRI 0x01
+#define MSG_ANY 0x02
+#define MSG_BAND 0x04
+
+#define MORECTL 1
+#define MOREDATA 2
+
+#define TBADADDR 1
+#define TBADOPT 2
+#define TACCES 3
+#define TBADF 4
+#define TNOADDR 5
+#define TOUTSTATE 6
+#define TBADSEQ 7
+#define TSYSERR 8
+#define TLOOK 9
+#define TBADDATA 10
+#define TBUFOVFLW 11
+#define TFLOW 12
+#define TNODATA 13
+#define TNODIS 14
+#define TNOUDERR 15
+#define TBADFLAG 16
+#define TNOREL 17
+#define TNOTSUPPORT 18
+#define TSTATECHNG 19
+
+#define T_CONN_REQ 0
+#define T_CONN_RES 1
+#define T_DISCON_REQ 2
+#define T_DATA_REQ 3
+#define T_EXDATA_REQ 4
+#define T_INFO_REQ 5
+#define T_BIND_REQ 6
+#define T_UNBIND_REQ 7
+#define T_UNITDATA_REQ 8
+#define T_OPTMGMT_REQ 9
+#define T_ORDREL_REQ 10
+
+#define T_CONN_IND 11
+#define T_CONN_CON 12
+#define T_DISCON_IND 13
+#define T_DATA_IND 14
+#define T_EXDATA_IND 15
+#define T_INFO_ACK 16
+#define T_BIND_ACK 17
+#define T_ERROR_ACK 18
+#define T_OK_ACK 19
+#define T_UNITDATA_IND 20
+#define T_UDERROR_IND 21
+#define T_OPTMGMT_ACK 22
+#define T_ORDREL_IND 23
+
+#define T_NEGOTIATE 0x0004
+#define T_FAILURE 0x0040
+
+#define TS_UNBND 0 /* unbound */
+#define TS_WACK_BREQ 1 /* waiting for T_BIND_REQ ack */
+#define TS_WACK_UREQ 2 /* waiting for T_UNBIND_REQ ack */
+#define TS_IDLE 3 /* idle */
+#define TS_WACK_OPTREQ 4 /* waiting for T_OPTMGMT_REQ ack */
+#define TS_WACK_CREQ 5 /* waiting for T_CONN_REQ ack */
+#define TS_WCON_CREQ 6 /* waiting for T_CONN_REQ confirmation */
+#define TS_WRES_CIND 7 /* waiting for T_CONN_IND */
+#define TS_WACK_CRES 8 /* waiting for T_CONN_RES ack */
+#define TS_DATA_XFER 9 /* data transfer */
+#define TS_WIND_ORDREL 10 /* releasing read but not write */
+#define TS_WREQ_ORDREL 11 /* wait to release write but not read */
+#define TS_WACK_DREQ6 12 /* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ7 13 /* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ9 14 /* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ10 15 /* waiting for T_DISCON_REQ ack */
+#define TS_WACK_DREQ11 16 /* waiting for T_DISCON_REQ ack */
+#define TS_NOSTATES 17
+
+struct T_conn_req {
+ s32 PRIM_type;
+ s32 DEST_length;
+ s32 DEST_offset;
+ s32 OPT_length;
+ s32 OPT_offset;
+};
+
+struct T_bind_req {
+ s32 PRIM_type;
+ s32 ADDR_length;
+ s32 ADDR_offset;
+ u32 CONIND_number;
+};
+
+struct T_unitdata_req {
+ s32 PRIM_type;
+ s32 DEST_length;
+ s32 DEST_offset;
+ s32 OPT_length;
+ s32 OPT_offset;
+};
+
+struct T_optmgmt_req {
+ s32 PRIM_type;
+ s32 OPT_length;
+ s32 OPT_offset;
+ s32 MGMT_flags;
+};
+
+struct T_bind_ack {
+ s32 PRIM_type;
+ s32 ADDR_length;
+ s32 ADDR_offset;
+ u32 CONIND_number;
+};
+
+struct T_error_ack {
+ s32 PRIM_type;
+ s32 ERROR_prim;
+ s32 TLI_error;
+ s32 UNIX_error;
+};
+
+struct T_ok_ack {
+ s32 PRIM_type;
+ s32 CORRECT_prim;
+};
+
+struct T_conn_ind {
+ s32 PRIM_type;
+ s32 SRC_length;
+ s32 SRC_offset;
+ s32 OPT_length;
+ s32 OPT_offset;
+ s32 SEQ_number;
+};
+
+struct T_conn_con {
+ s32 PRIM_type;
+ s32 RES_length;
+ s32 RES_offset;
+ s32 OPT_length;
+ s32 OPT_offset;
+};
+
+struct T_discon_ind {
+ s32 PRIM_type;
+ s32 DISCON_reason;
+ s32 SEQ_number;
+};
+
+struct T_unitdata_ind {
+ s32 PRIM_type;
+ s32 SRC_length;
+ s32 SRC_offset;
+ s32 OPT_length;
+ s32 OPT_offset;
+};
+
+struct T_optmgmt_ack {
+ s32 PRIM_type;
+ s32 OPT_length;
+ s32 OPT_offset;
+ s32 MGMT_flags;
+};
+
+struct opthdr {
+ s32 level;
+ s32 name;
+ s32 len;
+ char value[0];
+};
+
+struct T_primsg {
+ struct T_primsg *next;
+ unsigned char pri;
+ unsigned char band;
+ int length;
+ s32 type;
+};
+
+struct strbuf {
+ s32 maxlen;
+ s32 len;
+ u32 buf;
+} ;
+
+/* Constants used by STREAMS modules emulation code */
+
+typedef char sol_module;
+
+#define MAX_NR_STREAM_MODULES 16
+
+/* Private data structure assigned to sockets. */
+
+struct sol_socket_struct {
+ int magic;
+ int modcount;
+ sol_module module[MAX_NR_STREAM_MODULES];
+ long state;
+ int offset;
+ struct T_primsg *pfirst, *plast;
+};
+
+#define SOLARIS_SOCKET_MAGIC 0xADDED
+
diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S
index c425fb721..bb18807f1 100644
--- a/arch/sparc64/solaris/systbl.S
+++ b/arch/sparc64/solaris/systbl.S
@@ -1,4 +1,4 @@
-/* $Id: systbl.S,v 1.5 1997/09/04 15:46:24 jj Exp $
+/* $Id: systbl.S,v 1.6 1998/03/26 08:46:08 jj Exp $
* systbl.S: System call entry point table for Solaris compatibility.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -14,11 +14,9 @@
#define REGS(name) name+1
/* Hack till all be implemented */
-#define solaris_getmsg solaris_unimplemented
#define solaris_getpmsg solaris_unimplemented
#define solaris_hrtsys solaris_unimplemented
#define solaris_msgsys solaris_unimplemented
-#define solaris_putmsg solaris_unimplemented
#define solaris_putpmsg solaris_unimplemented
#define solaris_semsys solaris_unimplemented
@@ -115,8 +113,8 @@ solaris_sys_table:
.word solaris_unimplemented /* libattach 82 */
.word solaris_unimplemented /* libdetach 83 */
.word CHAIN(sysfs) /* sysfs dxx 84 */
- .word REGS(solaris_getmsg) /* getmsg dxxx 85 */
- .word REGS(solaris_putmsg) /* putmsg dxxd 86 */
+ .word solaris_getmsg /* getmsg dxxx 85 */
+ .word solaris_putmsg /* putmsg dxxd 86 */
.word CHAIN(poll) /* poll xdd 87 */
.word solaris_lstat /* lstat sp 88 */
.word CHAIN(symlink) /* symlink ss 89 */
@@ -186,7 +184,7 @@ solaris_sys_table:
.word solaris_unimplemented /* fchroot d 153 */
.word solaris_unimplemented /* lvlproc dx 154 */
.word solaris_unimplemented /* ? 155 */
- .word CHAIN(gettimeofday) /* gettimeofday xx 156 */
+ .word solaris_gettimeofday /* gettimeofday x 156 */
.word CHAIN(getitimer) /* getitimer dx 157 */
.word CHAIN(setitimer) /* setitimer dxx 158 */
.word solaris_unimplemented /* lwp-xxx 159 */
diff --git a/arch/sparc64/solaris/timod.c b/arch/sparc64/solaris/timod.c
new file mode 100644
index 000000000..7673702d2
--- /dev/null
+++ b/arch/sparc64/solaris/timod.c
@@ -0,0 +1,979 @@
+/* $Id: timod.c,v 1.1 1998/03/26 08:46:18 jj Exp $
+ * timod.c: timod emulation.
+ *
+ * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
+ *
+ * Streams & timod emulation based on code
+ * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+
+#include <asm/uaccess.h>
+#include <asm/termios.h>
+
+#include "conv.h"
+#include "socksys.h"
+
+extern char *getname32(u32 filename);
+#define putname32 putname
+
+extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd,
+ unsigned long arg);
+extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd,
+ u32 arg);
+asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+
+#ifdef __SMP__
+spinlock_t timod_pagelock = SPIN_LOCK_UNLOCKED;
+#endif
+static char * page = NULL ;
+
+#ifndef DEBUG_SOLARIS_KMALLOC
+
+#define mykmalloc kmalloc
+#define mykfree kfree
+
+#else
+
+void * mykmalloc(size_t s, int gfp)
+{
+ static char * page;
+ static size_t free = 0;
+ void * r;
+ s = ((s + 63) & ~63);
+ if( s > PAGE_SIZE ) {
+ SOLD("too big size, calling real kmalloc");
+ return kmalloc(s, gfp);
+ }
+ if( s > free ) {
+ /* we are wasting memory, but we don't care */
+ page = (char *)__get_free_page(gfp);
+ free = PAGE_SIZE;
+ }
+ r = page;
+ page += s;
+ free -= s;
+ return r;
+}
+
+void mykfree(void *p)
+{
+}
+
+#endif
+
+#ifndef DEBUG_SOLARIS
+
+#define BUF_SIZE PAGE_SIZE
+#define PUT_MAGIC(a,m)
+#define CHECK_MAGIC(a,m)
+#define BUF_OFFSET 0
+#define MKCTL_TRAILER 0
+
+#else
+
+#define BUF_SIZE (PAGE_SIZE-2*sizeof(u64))
+#define BUFPAGE_MAGIC 0xBADC0DEDDEADBABEL
+#define MKCTL_MAGIC 0xDEADBABEBADC0DEDL
+#define PUT_MAGIC(a,m) do{(*(u64*)(a))=(m);}while(0)
+#define CHECK_MAGIC(a,m) do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\
+ __FILE__,__LINE__,__FUNCTION__,(m),(a));}while(0)
+#define BUF_OFFSET sizeof(u64)
+#define MKCTL_TRAILER sizeof(u64)
+
+#endif
+
+static char *getpage( void )
+{
+ char *r;
+ SOLD("getting page");
+ spin_lock(&timod_pagelock);
+ if (page) {
+ r = page;
+ page = NULL;
+ spin_unlock(&timod_pagelock);
+ SOLD("got cached");
+ return r + BUF_OFFSET;
+ }
+ spin_unlock(&timod_pagelock);
+ SOLD("getting new");
+ r = (char *)__get_free_page(GFP_KERNEL);
+ PUT_MAGIC(r,BUFPAGE_MAGIC);
+ PUT_MAGIC(r+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC);
+ return r + BUF_OFFSET;
+}
+
+static void putpage(char *p)
+{
+ SOLD("putting page");
+ p = p - BUF_OFFSET;
+ CHECK_MAGIC(p,BUFPAGE_MAGIC);
+ CHECK_MAGIC(p+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC);
+ spin_lock(&timod_pagelock);
+ if (page) {
+ spin_unlock(&timod_pagelock);
+ free_page((unsigned long)p);
+ SOLD("freed it");
+ } else {
+ page = p;
+ spin_unlock(&timod_pagelock);
+ SOLD("cached it");
+ }
+}
+
+static struct T_primsg *timod_mkctl(int size)
+{
+ struct T_primsg *it;
+
+ SOLD("creating primsg");
+ it = (struct T_primsg *)mykmalloc(size+sizeof(*it)-sizeof(s32)+2*MKCTL_TRAILER, GFP_KERNEL);
+ if (it) {
+ SOLD("got it");
+ it->pri = MSG_HIPRI;
+ it->length = size;
+ PUT_MAGIC((char*)((u64)(((char *)&it->type)+size+7)&~7),MKCTL_MAGIC);
+ }
+ return it;
+}
+
+static void timod_wake_socket(unsigned int fd)
+{
+ struct socket *sock;
+
+ SOLD("wakeing socket");
+ sock = &current->files->fd[fd]->f_dentry->d_inode->u.socket_i;
+ wake_up_interruptible(&sock->wait);
+ if (sock->fasync_list && !(sock->flags & SO_WAITDATA))
+ kill_fasync(sock->fasync_list, SIGIO);
+ SOLD("done");
+}
+
+static void timod_queue(unsigned int fd, struct T_primsg *it)
+{
+ struct sol_socket_struct *sock;
+
+ SOLD("queuing primsg");
+ sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data;
+ it->next = sock->pfirst;
+ sock->pfirst = it;
+ if (!sock->plast)
+ sock->plast = it;
+ timod_wake_socket(fd);
+ SOLD("done");
+}
+
+static void timod_queue_end(unsigned int fd, struct T_primsg *it)
+{
+ struct sol_socket_struct *sock;
+
+ SOLD("queuing primsg at end");
+ sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data;
+ it->next = NULL;
+ if (sock->plast)
+ sock->plast->next = it;
+ else
+ sock->pfirst = it;
+ sock->plast = it;
+ SOLD("done");
+}
+
+static void timod_error(unsigned int fd, int prim, int terr, int uerr)
+{
+ struct T_primsg *it;
+
+ SOLD("making error");
+ it = timod_mkctl(sizeof(struct T_error_ack));
+ if (it) {
+ struct T_error_ack *err = (struct T_error_ack *)&it->type;
+
+ SOLD("got it");
+ err->PRIM_type = T_ERROR_ACK;
+ err->ERROR_prim = prim;
+ err->TLI_error = terr;
+ err->UNIX_error = uerr; /* FIXME: convert this */
+ timod_queue(fd, it);
+ }
+ SOLD("done");
+}
+
+static void timod_ok(unsigned int fd, int prim)
+{
+ struct T_primsg *it;
+ struct T_ok_ack *ok;
+
+ SOLD("creating ok ack");
+ it = timod_mkctl(sizeof(*ok));
+ if (it) {
+ SOLD("got it");
+ ok = (struct T_ok_ack *)&it->type;
+ ok->PRIM_type = T_OK_ACK;
+ ok->CORRECT_prim = prim;
+ timod_queue(fd, it);
+ }
+ SOLD("done");
+}
+
+static int timod_optmgmt(unsigned int fd, int flag, char *opt_buf, int opt_len, int do_ret)
+{
+ int error, failed;
+ int ret_space, ret_len;
+ long args[5];
+ char *ret_pos,*ret_buf;
+ int (*sys_socketcall)(int, unsigned long *) =
+ (int (*)(int, unsigned long *))SYS(socketcall);
+ mm_segment_t old_fs = get_fs();
+
+ SOLD("entry");
+ SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret));
+ if (!do_ret && (!opt_buf || opt_len <= 0))
+ return 0;
+ SOLD("getting page");
+ ret_pos = ret_buf = getpage();
+ ret_space = BUF_SIZE;
+ ret_len = 0;
+
+ error = failed = 0;
+ SOLD("looping");
+ while(opt_len >= sizeof(struct opthdr)) {
+ struct opthdr *opt;
+ int orig_opt_len;
+ SOLD("loop start");
+ opt = (struct opthdr *)ret_pos;
+ if (ret_space < sizeof(struct opthdr)) {
+ failed = TSYSERR;
+ break;
+ }
+ SOLD("getting opthdr");
+ if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) ||
+ opt->len > opt_len) {
+ failed = TBADOPT;
+ break;
+ }
+ SOLD("got opthdr");
+ if (flag == T_NEGOTIATE) {
+ char *buf;
+
+ SOLD("handling T_NEGOTIATE");
+ buf = ret_pos + sizeof(struct opthdr);
+ if (ret_space < opt->len + sizeof(struct opthdr) ||
+ copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) {
+ failed = TSYSERR;
+ break;
+ }
+ SOLD("got optdata");
+ args[0] = fd;
+ args[1] = opt->level;
+ args[2] = opt->name;
+ args[3] = (long)buf;
+ args[4] = opt->len;
+ SOLD("calling SETSOCKOPT");
+ set_fs(KERNEL_DS);
+ error = sys_socketcall(SYS_SETSOCKOPT, args);
+ set_fs(old_fs);
+ if (error) {
+ failed = TBADOPT;
+ break;
+ }
+ SOLD("SETSOCKOPT ok");
+ }
+ orig_opt_len = opt->len;
+ opt->len = ret_space - sizeof(struct opthdr);
+ if (opt->len < 0) {
+ failed = TSYSERR;
+ break;
+ }
+ args[0] = fd;
+ args[1] = opt->level;
+ args[2] = opt->name;
+ args[3] = (long)(ret_pos+sizeof(struct opthdr));
+ args[4] = (long)&opt->len;
+ SOLD("calling GETSOCKOPT");
+ set_fs(KERNEL_DS);
+ error = sys_socketcall(SYS_GETSOCKOPT, args);
+ set_fs(old_fs);;
+ if (error) {
+ failed = TBADOPT;
+ break;
+ }
+ SOLD("GETSOCKOPT ok");
+ ret_space -= sizeof(struct opthdr) + opt->len;
+ ret_len += sizeof(struct opthdr) + opt->len;
+ ret_pos += sizeof(struct opthdr) + opt->len;
+ opt_len -= sizeof(struct opthdr) + orig_opt_len;
+ opt_buf += sizeof(struct opthdr) + orig_opt_len;
+ SOLD("loop end");
+ }
+ SOLD("loop done");
+ if (do_ret) {
+ SOLD("generating ret msg");
+ if (failed)
+ timod_error(fd, T_OPTMGMT_REQ, failed, -error);
+ else {
+ struct T_primsg *it;
+ it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len);
+ if (it) {
+ struct T_optmgmt_ack *ack =
+ (struct T_optmgmt_ack *)&it->type;
+ SOLD("got primsg");
+ ack->PRIM_type = T_OPTMGMT_ACK;
+ ack->OPT_length = ret_len;
+ ack->OPT_offset = sizeof(struct T_optmgmt_ack);
+ ack->MGMT_flags = (failed ? T_FAILURE : flag);
+ memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack),
+ ret_buf, ret_len);
+ timod_queue(fd, it);
+ }
+ }
+ }
+ SOLDD(("put_page %p\n", ret_buf));
+ putpage(ret_buf);
+ SOLD("done");
+ return 0;
+}
+
+int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len,
+ char *data_buf, int data_len, int flags)
+{
+ int ret, error, terror;
+ char *buf;
+ struct file *filp;
+ struct inode *ino;
+ struct sol_socket_struct *sock;
+ mm_segment_t old_fs = get_fs();
+ long args[6];
+ int (*sys_socketcall)(int, unsigned long *) =
+ (int (*)(int, unsigned long *))SYS(socketcall);
+ int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int) =
+ (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int))SYS(sendto);
+ filp = current->files->fd[fd];
+ ino = filp->f_dentry->d_inode;
+ sock = (struct sol_socket_struct *)filp->private_data;
+ SOLD("entry");
+ if (get_user(ret, (int *)A(ctl_buf)))
+ return -EFAULT;
+ switch (ret) {
+ case T_BIND_REQ:
+ {
+ struct T_bind_req req;
+
+ SOLDD(("bind %016lx(%016lx)\n", sock, filp));
+ SOLD("T_BIND_REQ");
+ if (sock->state != TS_UNBND) {
+ timod_error(fd, T_BIND_REQ, TOUTSTATE, 0);
+ return 0;
+ }
+ SOLD("state ok");
+ if (copy_from_user(&req, ctl_buf, sizeof(req))) {
+ timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
+ return 0;
+ }
+ SOLD("got ctl req");
+ if (req.ADDR_offset && req.ADDR_length) {
+ if (req.ADDR_length > BUF_SIZE) {
+ timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
+ return 0;
+ }
+ SOLD("req size ok");
+ buf = getpage();
+ if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) {
+ timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
+ putpage(buf);
+ return 0;
+ }
+ SOLD("got ctl data");
+ args[0] = fd;
+ args[1] = (long)buf;
+ args[2] = req.ADDR_length;
+ SOLD("calling BIND");
+ set_fs(KERNEL_DS);
+ error = sys_socketcall(SYS_BIND, args);
+ set_fs(old_fs);
+ putpage(buf);
+ SOLD("BIND returned");
+ } else
+ error = 0;
+ if (!error) {
+ struct T_primsg *it;
+ if (req.CONIND_number) {
+ args[0] = fd;
+ args[1] = req.CONIND_number;
+ SOLD("calling LISTEN");
+ set_fs(KERNEL_DS);
+ error = sys_socketcall(SYS_LISTEN, args);
+ set_fs(old_fs);
+ SOLD("LISTEN done");
+ }
+ it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr));
+ if (it) {
+ struct T_bind_ack *ack;
+
+ ack = (struct T_bind_ack *)&it->type;
+ ack->PRIM_type = T_BIND_ACK;
+ ack->ADDR_offset = sizeof(*ack);
+ ack->ADDR_length = sizeof(struct sockaddr);
+ ack->CONIND_number = req.CONIND_number;
+ args[0] = fd;
+ args[1] = (long)(ack+sizeof(*ack));
+ args[2] = (long)&ack->ADDR_length;
+ set_fs(KERNEL_DS);
+ sys_socketcall(SYS_GETSOCKNAME,args);
+ set_fs(old_fs);
+ sock->state = TS_IDLE;
+ timod_ok(fd, T_BIND_REQ);
+ timod_queue_end(fd, it);
+ SOLD("BIND done");
+ return 0;
+ }
+ }
+ SOLD("some error");
+ switch (error) {
+ case -EINVAL:
+ terror = TOUTSTATE;
+ error = 0;
+ break;
+ case -EACCES:
+ terror = TACCES;
+ error = 0;
+ break;
+ case -EADDRNOTAVAIL:
+ case -EADDRINUSE:
+ terror = TNOADDR;
+ error = 0;
+ break;
+ default:
+ terror = TSYSERR;
+ break;
+ }
+ timod_error(fd, T_BIND_REQ, terror, -error);
+ SOLD("BIND done");
+ return 0;
+ }
+ case T_CONN_REQ:
+ {
+ struct T_conn_req req;
+ unsigned short oldflags;
+ struct T_primsg *it;
+ SOLD("T_CONN_REQ");
+ if (sock->state != TS_UNBND && sock->state != TS_IDLE) {
+ timod_error(fd, T_CONN_REQ, TOUTSTATE, 0);
+ return 0;
+ }
+ SOLD("state ok");
+ if (copy_from_user(&req, ctl_buf, sizeof(req))) {
+ timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+ return 0;
+ }
+ SOLD("got ctl req");
+ if (ctl_len > BUF_SIZE) {
+ timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+ return 0;
+ }
+ SOLD("req size ok");
+ buf = getpage();
+ if (copy_from_user(buf, ctl_buf, ctl_len)) {
+ timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+ putpage(buf);
+ return 0;
+ }
+#ifdef DEBUG_SOLARIS
+ {
+ char * ptr = buf;
+ int len = ctl_len;
+ printk("returned data (%d bytes): ",len);
+ while( len-- ) {
+ if (!(len & 7))
+ printk(" ");
+ printk("%02x",(unsigned char)*ptr++);
+ }
+ printk("\n");
+ }
+#endif
+ SOLD("got ctl data");
+ args[0] = fd;
+ args[1] = (long)buf+req.DEST_offset;
+ args[2] = req.DEST_length;
+ oldflags = filp->f_flags;
+ filp->f_flags &= ~O_NONBLOCK;
+ SOLD("calling CONNECT");
+ set_fs(KERNEL_DS);
+ error = sys_socketcall(SYS_CONNECT, args);
+ set_fs(old_fs);
+ filp->f_flags = oldflags;
+ SOLD("CONNECT done");
+ if (!error) {
+ struct T_conn_con *con;
+ SOLD("no error");
+ it = timod_mkctl(ctl_len);
+ if (!it) {
+ putpage(buf);
+ return -ENOMEM;
+ }
+ con = (struct T_conn_con *)&it->type;
+#ifdef DEBUG_SOLARIS
+ {
+ char * ptr = buf;
+ int len = ctl_len;
+ printk("returned data (%d bytes): ",len);
+ while( len-- ) {
+ if (!(len & 7))
+ printk(" ");
+ printk("%02x",(unsigned char)*ptr++);
+ }
+ printk("\n");
+ }
+#endif
+ memcpy(con, buf, ctl_len);
+ SOLD("copied ctl_buf");
+ con->PRIM_type = T_CONN_CON;
+ sock->state = TS_DATA_XFER;
+ } else {
+ struct T_discon_ind *dis;
+ SOLD("some error");
+ it = timod_mkctl(sizeof(*dis));
+ if (!it) {
+ putpage(buf);
+ return -ENOMEM;
+ }
+ SOLD("got primsg");
+ dis = (struct T_discon_ind *)&it->type;
+ dis->PRIM_type = T_DISCON_IND;
+ dis->DISCON_reason = -error; /* FIXME: convert this as in iABI_errors() */
+ dis->SEQ_number = 0;
+ }
+ putpage(buf);
+ timod_ok(fd, T_CONN_REQ);
+ it->pri = 0;
+ timod_queue_end(fd, it);
+ SOLD("CONNECT done");
+ return 0;
+ }
+ case T_OPTMGMT_REQ:
+ {
+ struct T_optmgmt_req req;
+ SOLD("OPTMGMT_REQ");
+ if (copy_from_user(&req, ctl_buf, sizeof(req)))
+ return -EFAULT;
+ SOLD("got req");
+ return timod_optmgmt(fd, req.MGMT_flags,
+ req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL,
+ req.OPT_length, 1);
+ }
+ case T_UNITDATA_REQ:
+ {
+ struct T_unitdata_req req;
+
+ int err;
+ SOLD("T_UNITDATA_REQ");
+ if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) {
+ timod_error(fd, T_CONN_REQ, TOUTSTATE, 0);
+ return 0;
+ }
+ SOLD("state ok");
+ if (copy_from_user(&req, ctl_buf, sizeof(req))) {
+ timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
+ return 0;
+ }
+ SOLD("got ctl req");
+#ifdef DEBUG_SOLARIS
+ {
+ char * ptr = ctl_buf+req.DEST_offset;
+ int len = req.DEST_length;
+ printk("socket address (%d bytes): ",len);
+ while( len-- ) {
+ char c;
+ if (get_user(c,ptr))
+ printk("??");
+ else
+ printk("%02x",(unsigned char)c);
+ ptr++;
+ }
+ printk("\n");
+ }
+#endif
+ err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr*)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length);
+ if (err == data_len)
+ return 0;
+ if(err >= 0) {
+ printk("timod: sendto failed to send all the data\n");
+ return 0;
+ }
+ timod_error(fd, T_CONN_REQ, TSYSERR, -err);
+ return 0;
+ }
+ default:
+ printk("timod_putmsg: unsuported command %u.\n", ret);
+ break;
+ }
+ return -EINVAL;
+}
+
+/* copied directly from fs/select.c */
+
+static void free_wait(poll_table * p)
+{
+ struct poll_table_entry * entry = p->entry + p->nr;
+
+ SOLD("entry");
+ while (p->nr > 0) {
+ p->nr--;
+ entry--;
+ remove_wait_queue(entry->wait_address,&entry->wait);
+ }
+ SOLD("done");
+}
+
+
+int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, s32 *ctl_len,
+ char *data_buf, int data_maxlen, s32 *data_len, int *flags_p)
+{
+ int error;
+ int oldflags;
+ struct file *filp;
+ struct inode *ino;
+ struct sol_socket_struct *sock;
+ struct T_unitdata_ind udi;
+ mm_segment_t old_fs = get_fs();
+ long args[6];
+ char *tmpbuf;
+ int tmplen;
+ int (*sys_socketcall)(int, unsigned long *) =
+ (int (*)(int, unsigned long *))SYS(socketcall);
+ int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *);
+
+ SOLD("entry");
+ SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p));
+ filp = current->files->fd[fd];
+ ino = filp->f_dentry->d_inode;
+ sock = (struct sol_socket_struct *)filp->private_data;
+ SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL));
+ if ( ctl_maxlen > 0 && !sock->pfirst && ino->u.socket_i.type == SOCK_STREAM
+ && sock->state == TS_IDLE) {
+ SOLD("calling LISTEN");
+ args[0] = fd;
+ args[1] = -1;
+ set_fs(KERNEL_DS);
+ sys_socketcall(SYS_LISTEN, args);
+ set_fs(old_fs);
+ SOLD("LISTEN done");
+ }
+ if (!(filp->f_flags & O_NONBLOCK)) {
+ poll_table wait_table, *wait;
+ struct poll_table_entry *entry;
+ SOLD("getting poll_table");
+ entry = (struct poll_table_entry *)__get_free_page(GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+ SOLD("got one");
+ wait_table.nr = 0;
+ wait_table.entry = entry;
+ wait = &wait_table;
+ for(;;) {
+ SOLD("loop");
+ current->state = TASK_INTERRUPTIBLE;
+ /* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */
+ /* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */
+ /* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */
+ /* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */
+ /* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */
+ /* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */
+ if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI))
+ break;
+ SOLD("cond 1 passed");
+ if (
+ #if 1
+ *flags_p != MSG_HIPRI &&
+ #endif
+ ((filp->f_op->poll(filp, wait) & POLLIN) ||
+ (filp->f_op->poll(filp, NULL) & POLLIN) ||
+ signal_pending(current))
+ ) {
+ break;
+ }
+ if( *flags_p == MSG_HIPRI ) {
+ SOLD("avoiding lockup");
+ break ;
+ }
+ SOLD("scheduling");
+ schedule();
+ }
+ SOLD("loop done");
+ current->state = TASK_RUNNING;
+ free_wait(&wait_table);
+ free_page((unsigned long)entry);
+ if (signal_pending(current)) {
+ SOLD("signal pending");
+ return -EINTR;
+ }
+ }
+ if (ctl_maxlen >= 0 && sock->pfirst) {
+ struct T_primsg *it = sock->pfirst;
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+ int l = min(ctl_maxlen, it->length);
+ CHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC);
+ SOLD("purting ctl data");
+ if(copy_to_user(ctl_buf,
+ (char*)&it->type + sock->offset, l))
+ return -EFAULT;
+ SOLD("pur it");
+ if(put_user(l, ctl_len))
+ return -EFAULT;
+ SOLD("set ctl_len");
+ *flags_p = it->pri;
+ it->length -= l;
+ if (it->length) {
+ SOLD("more ctl");
+ sock->offset += l;
+ return MORECTL;
+ } else {
+ SOLD("removing message");
+ sock->pfirst = it->next;
+ if (!sock->pfirst)
+ sock->plast = NULL;
+ SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst));
+ mykfree(it);
+ sock->offset = 0;
+ SOLD("ctl done");
+ return 0;
+ }
+ }
+ *flags_p = 0;
+ if (ctl_maxlen >= 0) {
+ SOLD("ACCEPT perhaps?");
+ if (ino->u.socket_i.type == SOCK_STREAM && sock->state == TS_IDLE) {
+ struct T_conn_ind ind;
+ char *buf = getpage();
+ int len = BUF_SIZE;
+
+ SOLD("trying ACCEPT");
+ if (put_user(ctl_maxlen - sizeof(ind), ctl_len))
+ return -EFAULT;
+ args[0] = fd;
+ args[1] = (long)buf;
+ args[2] = (long)&len;
+ oldflags = filp->f_flags;
+ filp->f_flags |= O_NONBLOCK;
+ SOLD("calling ACCEPT");
+ set_fs(KERNEL_DS);
+ error = sys_socketcall(SYS_ACCEPT, args);
+ set_fs(old_fs);
+ filp->f_flags = oldflags;
+ if (error < 0) {
+ SOLD("some error");
+ putpage(buf);
+ return error;
+ }
+ if (error) {
+ SOLD("connect");
+ putpage(buf);
+ if (sizeof(ind) > ctl_maxlen) {
+ SOLD("generating CONN_IND");
+ ind.PRIM_type = T_CONN_IND;
+ ind.SRC_length = len;
+ ind.SRC_offset = sizeof(ind);
+ ind.OPT_length = ind.OPT_offset = 0;
+ ind.SEQ_number = error;
+ if(copy_to_user(ctl_buf, &ind, sizeof(ind))||
+ put_user(sizeof(ind)+ind.SRC_length,ctl_len))
+ return -EFAULT;
+ SOLD("CONN_IND created");
+ }
+ if (data_maxlen >= 0)
+ put_user(0, data_len);
+ SOLD("CONN_IND done");
+ return 0;
+ }
+ if (len>ctl_maxlen) {
+ SOLD("data don't fit");
+ putpage(buf);
+ return -EFAULT; /* XXX - is this ok ? */
+ }
+ if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){
+ SOLD("can't copy data");
+ putpage(buf);
+ return -EFAULT;
+ }
+ SOLD("ACCEPT done");
+ putpage(buf);
+ }
+ }
+ SOLD("checking data req");
+ if (data_maxlen <= 0) {
+ if (data_maxlen == 0)
+ put_user(0, data_len);
+ if (ctl_maxlen >= 0)
+ put_user(0, ctl_len);
+ return -EAGAIN;
+ }
+ SOLD("wants data");
+ if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) {
+ SOLD("udi fits");
+ tmpbuf = ctl_buf + sizeof(udi);
+ tmplen = ctl_maxlen - sizeof(udi);
+ } else {
+ SOLD("udi does not fit");
+ tmpbuf = NULL;
+ tmplen = 0;
+ }
+ if (put_user(tmplen, ctl_len))
+ return -EFAULT;
+ SOLD("set ctl_len");
+ oldflags = filp->f_flags;
+ filp->f_flags |= O_NONBLOCK;
+ SOLD("calling recvfrom");
+ sys_recvfrom = (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom);
+ error = sys_recvfrom(fd, data_buf, min(0,data_maxlen), 0, (struct sockaddr*)tmpbuf, ctl_len);
+ filp->f_flags = oldflags;
+ if (error < 0)
+ return error;
+ SOLD("error >= 0" ) ;
+ if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) {
+ SOLD("generating udi");
+ udi.PRIM_type = T_UNITDATA_IND;
+ get_user(udi.SRC_length, ctl_len);
+ udi.SRC_offset = sizeof(udi);
+ udi.OPT_length = udi.OPT_offset = 0;
+ copy_to_user(ctl_buf, &udi, sizeof(udi));
+ put_user(sizeof(udi)+udi.SRC_length, ctl_len);
+ SOLD("udi done");
+ } else
+ put_user(0, ctl_len);
+ put_user(error, data_len);
+ SOLD("done");
+ return 0;
+}
+
+asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3)
+{
+ struct file *filp;
+ struct inode *ino;
+ struct strbuf *ctlptr, *datptr;
+ struct strbuf ctl, dat;
+ int *flgptr;
+ int flags;
+ int error = -EBADF;
+
+ SOLD("entry");
+ lock_kernel();
+ if(fd >= NR_OPEN) goto out;
+
+ filp = current->files->fd[fd];
+ if(!filp) goto out;
+
+ ino = filp->f_dentry->d_inode;
+ if (!ino) goto out;
+
+ if (!ino->i_sock)
+ goto out;
+
+ ctlptr = (struct strbuf *)A(arg1);
+ datptr = (struct strbuf *)A(arg2);
+ flgptr = (int *)A(arg3);
+
+ error = -EFAULT;
+
+ if (ctlptr) {
+ if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) ||
+ put_user(-1,&ctlptr->len))
+ goto out;
+ } else
+ ctl.maxlen = -1;
+
+ if (datptr) {
+ if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) ||
+ put_user(-1,&datptr->len))
+ goto out;
+ } else
+ dat.maxlen = -1;
+
+ if (get_user(flags,flgptr))
+ goto out;
+
+ switch (flags) {
+ case 0:
+ case MSG_HIPRI:
+ case MSG_ANY:
+ case MSG_BAND:
+ break;
+ default:
+ error = -EINVAL;
+ goto out;
+ }
+
+ error = timod_getmsg(fd,(char*)A(ctl.buf),ctl.maxlen,&ctlptr->len,
+ (char*)A(dat.buf),dat.maxlen,&datptr->len,&flags);
+
+ if (!error && put_user(flags,flgptr))
+ error = -EFAULT;
+out:
+ unlock_kernel();
+ SOLD("done");
+ return error;
+}
+
+asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3)
+{
+ struct file *filp;
+ struct inode *ino;
+ struct strbuf *ctlptr, *datptr;
+ struct strbuf ctl, dat;
+ int flags = (int) arg3;
+ int error = -EBADF;
+
+ SOLD("entry");
+ lock_kernel();
+ if(fd >= NR_OPEN) goto out;
+
+ filp = current->files->fd[fd];
+ if(!filp) goto out;
+
+ ino = filp->f_dentry->d_inode;
+ if (!ino) goto out;
+
+ if (!ino->i_sock &&
+ (MAJOR(ino->i_rdev) != 30 || MINOR(ino->i_rdev) != 1))
+ goto out;
+
+ ctlptr = (struct strbuf *)A(arg1);
+ datptr = (struct strbuf *)A(arg2);
+
+ error = -EFAULT;
+
+ if (ctlptr) {
+ if (copy_from_user(&ctl,ctlptr,sizeof(ctl)))
+ goto out;
+ if (ctl.len < 0 && flags) {
+ error = -EINVAL;
+ goto out;
+ }
+ } else {
+ ctl.len = 0;
+ ctl.buf = 0;
+ }
+
+ if (datptr) {
+ if (copy_from_user(&dat,datptr,sizeof(dat)))
+ goto out;
+ } else {
+ dat.len = 0;
+ dat.buf = 0;
+ }
+
+ error = timod_putmsg(fd,(char*)A(ctl.buf),ctl.len,
+ (char*)A(dat.buf),dat.len,flags);
+out:
+ unlock_kernel();
+ SOLD("done");
+ return error;
+}
diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds
index 661acc098..d2c08f0fe 100644
--- a/arch/sparc64/vmlinux.lds
+++ b/arch/sparc64/vmlinux.lds
@@ -52,12 +52,9 @@ SECTIONS
. += 8192;
empty_bad_pte_table = .;
. += 8192;
- empty_bad_page = .;
- . += 8192;
. += 0x40;
empty_null_pmd_table = .;
. += 8192;
- . += 0x40;
empty_null_pte_table = .;
. += 8192;
}