From 9b8ed78b35fcd2710b5175555fb28e641269216c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:32:28 -0500 Subject: [PATCH] Import 2.3.50pre2 --- Documentation/Configure.help | 2 +- arch/sh/Makefile | 16 +- arch/sh/boot/Makefile | 2 +- arch/sh/config.in | 121 +++- arch/sh/defconfig | 71 ++- arch/sh/kernel/Makefile | 11 +- arch/sh/kernel/cf-enabler.c | 30 + arch/sh/kernel/entry.S | 351 ++++++++--- arch/sh/kernel/fpu.c | 266 ++++++++ arch/sh/kernel/head.S | 48 +- arch/sh/kernel/irq.c | 50 +- arch/sh/kernel/irq_imask.c | 106 ++++ arch/sh/kernel/irq_onchip.c | 38 +- arch/sh/kernel/pci-sh.c | 12 + arch/sh/kernel/process.c | 157 +++-- arch/sh/kernel/semaphore.c | 161 +++++ arch/sh/kernel/setup.c | 85 +-- arch/sh/kernel/signal.c | 28 +- arch/sh/kernel/sys_sh.c | 52 +- arch/sh/kernel/time.c | 138 ++++- arch/sh/kernel/traps.c | 23 +- arch/sh/mm/cache.c | 29 +- arch/sh/mm/fault.c | 23 +- arch/sh/mm/init.c | 93 ++- arch/sh/mm/ioremap.c | 3 +- arch/sh/vmlinux.lds.S | 38 +- drivers/char/Makefile | 14 + drivers/char/sh-sci.c | 992 ++++++++++++++++++++++++++++++ drivers/char/sh-sci.h | 195 ++++++ drivers/pnp/isapnp.c | 4 +- drivers/pnp/quirks.c | 4 +- drivers/sound/sb_card.c | 93 +++ drivers/video/Config.in | 8 + drivers/video/Makefile | 11 +- drivers/video/fbcon-hga.c | 245 ++++++++ drivers/video/fbcon.c | 8 +- drivers/video/fbmem.c | 5 + drivers/video/hgafb.c | 757 +++++++++++++++++++++++ drivers/video/vesafb.c | 8 +- drivers/video/vgacon.c | 78 +++ fs/fifo.c | 1 + fs/pipe.c | 18 +- include/asm-sh/cache.h | 16 +- include/asm-sh/div64.h | 10 + include/asm-sh/dma.h | 8 +- include/asm-sh/elf.h | 5 +- include/asm-sh/hardirq.h | 6 +- include/asm-sh/hdreg.h | 2 +- include/asm-sh/highmem.h | 85 --- include/asm-sh/ide.h | 20 +- include/asm-sh/io.h | 36 +- include/asm-sh/ipcbuf.h | 29 + include/asm-sh/irq.h | 5 +- include/asm-sh/mmu_context.h | 30 +- include/asm-sh/msgbuf.h | 31 + include/asm-sh/page.h | 4 + include/asm-sh/pci.h | 143 +++++ include/asm-sh/pgalloc-2level.h | 23 + include/asm-sh/pgalloc.h | 183 ++++++ include/asm-sh/pgtable-2level.h | 28 +- include/asm-sh/pgtable.h | 293 ++------- include/asm-sh/posix_types.h | 8 + include/asm-sh/processor.h | 69 ++- include/asm-sh/resource.h | 4 +- include/asm-sh/scatterlist.h | 13 + include/asm-sh/semaphore-helper.h | 14 +- include/asm-sh/semaphore.h | 156 ++++- include/asm-sh/sembuf.h | 25 + include/asm-sh/shmbuf.h | 42 ++ include/asm-sh/siginfo.h | 2 +- include/asm-sh/softirq.h | 62 +- include/asm-sh/stat.h | 36 ++ include/asm-sh/system.h | 130 ++-- include/asm-sh/termios.h | 1 + include/asm-sh/types.h | 4 + include/asm-sh/unistd.h | 80 ++- include/asm-sh/user.h | 12 + include/linux/i2c-id.h | 1 + include/linux/linkage.h | 12 - kernel/softirq.c | 1 + net/core/netfilter.c | 1 + 81 files changed, 4983 insertions(+), 1042 deletions(-) create mode 100644 arch/sh/kernel/cf-enabler.c create mode 100644 arch/sh/kernel/fpu.c create mode 100644 arch/sh/kernel/irq_imask.c create mode 100644 arch/sh/kernel/pci-sh.c create mode 100644 drivers/char/sh-sci.c create mode 100644 drivers/char/sh-sci.h create mode 100644 drivers/video/fbcon-hga.c create mode 100644 drivers/video/hgafb.c create mode 100644 include/asm-sh/div64.h delete mode 100644 include/asm-sh/highmem.h create mode 100644 include/asm-sh/ipcbuf.h create mode 100644 include/asm-sh/msgbuf.h create mode 100644 include/asm-sh/pci.h create mode 100644 include/asm-sh/pgalloc-2level.h create mode 100644 include/asm-sh/pgalloc.h create mode 100644 include/asm-sh/scatterlist.h create mode 100644 include/asm-sh/sembuf.h create mode 100644 include/asm-sh/shmbuf.h diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 908f120edc9d..f660890446d9 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -644,7 +644,7 @@ CONFIG_IDEDMA_PCI_AUTO Various ATA, Work(s) In Progress (EXPERIMENTAL) CONFIG_IDEDMA_PCI_WIP If you enable this you will be capable of using and testing - highly developmentail projects. + highly developmental projects. It is SAFEST to say N to this question. diff --git a/arch/sh/Makefile b/arch/sh/Makefile index 31fcf5d694a5..b0b694d8380a 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1 1999/09/18 16:55:51 gniibe Exp gniibe $ +# $Id: Makefile,v 1.2 1999/12/23 12:13:53 gniibe Exp gniibe $ # # 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 @@ -15,21 +15,21 @@ # # Select the object file format to substitute into the linker script. # -tool-prefix = sh-elf +tool-prefix = sh-linux-gnu- ifdef CONFIG_LITTLE_ENDIAN CFLAGS += -ml AFLAGS += -ml # LINKFLAGS += -EL LDFLAGS := -EL - -LD =$(CROSS_COMPILE)ld $(LDFLAGS) - endif -ifdef CONFIG_CROSSCOMPILE +# ifdef CONFIG_CROSSCOMPILE CROSS_COMPILE = $(tool-prefix) -endif +# endif + +LD =$(CROSS_COMPILE)ld $(LDFLAGS) +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S MODFLAGS += @@ -51,7 +51,7 @@ endif # none has been choosen above. # LINKSCRIPT = arch/sh/vmlinux.lds -LINKFLAGS += -T $(word 1,$(LINKSCRIPT)) -e __stext +LINKFLAGS += -T $(word 1,$(LINKSCRIPT)) -e _stext ifdef LOADADDR LINKFLAGS += -Ttext $(word 1,$(LOADADDR)) diff --git a/arch/sh/boot/Makefile b/arch/sh/boot/Makefile index e2ae36bde368..8c087beb1c38 100644 --- a/arch/sh/boot/Makefile +++ b/arch/sh/boot/Makefile @@ -1,5 +1,5 @@ # -# arch/mips/boot/Makefile +# arch/sh/boot/Makefile # # This file is subject to the terms and conditions of the GNU General Public # License. See the file "COPYING" in the main directory of this archive diff --git a/arch/sh/config.in b/arch/sh/config.in index 6471e7aa8c0a..8b74eeafa019 100644 --- a/arch/sh/config.in +++ b/arch/sh/config.in @@ -4,6 +4,10 @@ # mainmenu_name "Linux/SuperH Kernel Configuration" +define_bool CONFIG_SUPERH y + +define_bool CONFIG_UID16 y + mainmenu_option next_comment comment 'Code maturity level options' bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL @@ -29,7 +33,6 @@ if [ "$CONFIG_CPU_SUBTYPE_SH7750" = "y" ]; then fi bool 'Little Endian' CONFIG_LITTLE_ENDIAN hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 -bool 'Use SH CPU internal real time clock' CONFIG_SH_CPU_RTC endmenu mainmenu_option next_comment @@ -43,63 +46,139 @@ endmenu mainmenu_option next_comment comment 'General setup' + bool 'Networking support' CONFIG_NET + +bool 'Directy Connected Compact Flash support' CONFIG_CF_ENABLER + +bool 'PCI support' CONFIG_PCI +if [ "$CONFIG_PCI" = "y" ]; then + choice ' PCI access mode' \ + "BIOS CONFIG_PCI_GOBIOS \ + Direct CONFIG_PCI_GODIRECT \ + Any CONFIG_PCI_GOANY" Any + if [ "$CONFIG_PCI_GOBIOS" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then + define_bool CONFIG_PCI_BIOS y + fi + if [ "$CONFIG_PCI_GODIRECT" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then + define_bool CONFIG_PCI_DIRECT y + fi +fi + +source drivers/pci/Config.in + +bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG + +if [ "$CONFIG_HOTPLUG" = "y" ] ; then + source drivers/pcmcia/Config.in +fi + bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL - if [ "$CONFIG_PROC_FS" = "y" ]; then choice 'Kernel core (/proc/kcore) format' \ "ELF CONFIG_KCORE_ELF \ A.OUT CONFIG_KCORE_AOUT" ELF fi - tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC -endmenu -mainmenu_option next_comment -comment 'Character devices' -define_bool CONFIG_SERIAL n -define_bool CONFIG_SERIAL_CONSOLE y -bool 'SuperH SCI support' CONFIG_SH_SCI_SERIAL -bool 'SuperH SCIF support' CONFIG_SH_SCIF_SERIAL +source drivers/parport/Config.in + endmenu -mainmenu_option next_comment -comment 'Floppy, IDE, and other block devices' +source drivers/block/Config.in -tristate 'RAM disk support' CONFIG_BLK_DEV_RAM -if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then - bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in fi -tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP -tristate 'Network block device support' CONFIG_BLK_DEV_NBD +mainmenu_option next_comment +comment 'SCSI support' + +tristate 'SCSI support' CONFIG_SCSI + +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in +fi endmenu +source drivers/ieee1394/Config.in + if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in mainmenu_option next_comment - comment 'Network device drivers' + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in + if [ "$CONFIG_ATM" = "y" ]; then + source drivers/atm/Config.in + fi + fi endmenu fi mainmenu_option next_comment +comment 'Character devices' + +bool 'Virtual terminal' CONFIG_VT +if [ "$CONFIG_VT" = "y" ]; then + bool ' Support for console on virtual terminal' CONFIG_VT_CONSOLE +fi + +tristate 'Serial support' CONFIG_SERIAL +if [ "$CONFIG_SERIAL" = "y" -o "$CONFIG_SERIAL" = "m" ]; then + choice 'Serial interface type' \ + "SCI CONFIG_SH_SCI_SERIAL \ + SCIF CONFIG_SH_SCIF_SERIAL" +fi +if [ "$CONFIG_SERIAL" = "y" ]; then + bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE +fi comment 'Unix 98 PTY support' bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 fi +if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT + if [ "$CONFIG_PRINTER" != "n" ]; then + bool ' Support for console on line printer' CONFIG_LP_CONSOLE + fi + dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT +fi endmenu +if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then + source drivers/char/pcmcia/Config.in +fi + +source drivers/misc/Config.in + source fs/Config.in +if [ "$CONFIG_VT" = "y" ]; then + mainmenu_option next_comment + comment 'Console drivers' + bool 'VGA text console' CONFIG_VGA_CONSOLE + bool 'Video mode selection support' CONFIG_VIDEO_SELECT + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'MDA text console (dual-headed) (EXPERIMENTAL)' CONFIG_MDA_CONSOLE + source drivers/video/Config.in + fi + endmenu +fi + + mainmenu_option next_comment -comment 'Watchdog' +comment 'Sound' -tristate 'Software watchdog' CONFIG_SOFT_WATCHDOG +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in +fi endmenu mainmenu_option next_comment diff --git a/arch/sh/defconfig b/arch/sh/defconfig index 37440e7c09d5..5fabcdedca5f 100644 --- a/arch/sh/defconfig +++ b/arch/sh/defconfig @@ -1,6 +1,8 @@ # # Automatically generated make config: don't edit # +CONFIG_SUPERH=y +CONFIG_UID16=y # # Code maturity level options @@ -17,7 +19,6 @@ CONFIG_CPU_SH3=y # CONFIG_CPU_SH4 is not set CONFIG_LITTLE_ENDIAN=y CONFIG_MEMORY_START=0c000000 -CONFIG_SH_CPU_RTC=y # # Loadable module support @@ -28,6 +29,9 @@ CONFIG_SH_CPU_RTC=y # General setup # # CONFIG_NET is not set +CONFIG_CF_ENABLER=y +# CONFIG_PCI is not set +# CONFIG_HOTPLUG is not set # CONFIG_SYSVIPC is not set # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_SYSCTL is not set @@ -35,39 +39,77 @@ CONFIG_KCORE_ELF=y # CONFIG_KCORE_AOUT is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set +# CONFIG_PARPORT is not set # -# Character devices +# Block devices # -# CONFIG_SERIAL is not set -CONFIG_SERIAL_CONSOLE=y -CONFIG_SH_SCI_SERIAL=y -# CONFIG_SH_SCIF_SERIAL is not set +# 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_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set # -# Floppy, IDE, and other block devices +# IDE chipset support/bugfixes # +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SH_SCI_SERIAL=y +# CONFIG_SH_SCIF_SERIAL is not set +CONFIG_SERIAL_CONSOLE=y # # Unix 98 PTY support # # CONFIG_UNIX98_PTYS is not set +# +# Misc devices +# + # # Filesystems # # CONFIG_QUOTA is not set # CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set # CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set # CONFIG_FAT_FS is not set +# CONFIG_CRAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set -# CONFIG_UDF_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set @@ -75,6 +117,7 @@ CONFIG_PROC_FS=y # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set # CONFIG_UFS_FS is not set # @@ -82,14 +125,12 @@ CONFIG_EXT2_FS=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y -# CONFIG_SGI_PARTITION is not set -# CONFIG_SUN_PARTITION is not set # CONFIG_NLS is not set # -# Watchdog +# Sound # -# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_SOUND is not set # # Kernel hacking diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 6cf0b319efe4..efa2fb1094e1 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile @@ -11,10 +11,19 @@ O_TARGET := kernel.o O_OBJS := process.o signal.o entry.o traps.o irq.o irq_onchip.o \ - ptrace.o setup.o time.o sys_sh.o semaphore.o + ptrace.o setup.o time.o sys_sh.o semaphore.o pci-sh.o \ + irq_imask.o OX_OBJS := sh_ksyms.o MX_OBJS := +ifdef CONFIG_CF_ENABLER +O_OBJS += cf-enabler.o +endif + +ifdef CONFIG_CPU_SH4 +O_OBJS += fpu.o +endif + all: kernel.o head.o init_task.o entry.o: entry.S diff --git a/arch/sh/kernel/cf-enabler.c b/arch/sh/kernel/cf-enabler.c new file mode 100644 index 000000000000..80dc511b3da7 --- /dev/null +++ b/arch/sh/kernel/cf-enabler.c @@ -0,0 +1,30 @@ +/* $Id: cf-enabler.c,v 1.2 1999/12/20 10:14:40 gniibe Exp $ + * + * linux/drivers/block/cf-enabler.c + * + * Copyright (C) 1999 Niibe Yutaka + * + * Enable the CF configuration. + */ + +#include + +#include +#include + +#define CF_CIS_BASE 0xb8000000 +/* + * 0xB8000000 : Attribute + * 0xB8001000 : Common Memory + * 0xBA000000 : I/O + */ + +int __init cf_init(void) +{ + outw(0x0042, CF_CIS_BASE+0x0200); + make_imask_irq(14); + disable_irq(14); + return 0; +} + +__initcall (cf_init); diff --git a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S index a3e5a918c2e6..77bb7938b261 100644 --- a/arch/sh/kernel/entry.S +++ b/arch/sh/kernel/entry.S @@ -1,8 +1,8 @@ -/* $Id: entry.S,v 1.19 1999/10/31 13:19:35 gniibe Exp gniibe $ +/* $Id: entry.S,v 1.55 2000/03/05 01:48:58 gniibe Exp $ * * linux/arch/sh/entry.S * - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999, 2000 Niibe Yutaka * * 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 @@ -54,7 +54,8 @@ sigpending = 8 addr_limit = 12 need_resched = 20 -PF_TRACESYS = 0x20 +PF_TRACESYS = 0x00000020 +PF_USEDFPU = 0x00100000 ENOSYS = 38 @@ -207,37 +208,10 @@ error: 1: .long SYMBOL_NAME(do_exception_error) 2: .long 0xefffffff ! BL=0 -reschedule: - mova SYMBOL_NAME(ret_from_syscall),r0 - mov.l 1f,r1 - jmp @r1 - lds r0,pr - .balign 4 -1: .long SYMBOL_NAME(schedule) - badsys: mov #-ENOSYS,r0 rts ! go to ret_from_syscall.. mov.l r0,@(R0,r15) -signal_return: - ! We can reach here from an interrupt handler, - ! so, we need to unblock interrupt. - /* STI */ - mov.l 1f,r1 - stc sr,r0 - and r1,r0 - ldc r0,sr - ! - mov r15,r4 - mov #0,r5 - mov.l 2f,r1 - mova restore_all,r0 - jmp @r1 - lds r0,pr - .balign 4 -1: .long 0xefffffff ! BL=0 -2: .long SYMBOL_NAME(do_signal) - ! ! ! @@ -274,7 +248,7 @@ system_call: ldc r2,sr ! mov.l __n_sys,r1 - cmp/ge r1,r0 + cmp/hs r1,r0 bt/s badsys mov r0,r2 ! @@ -329,6 +303,9 @@ system_call: 3: .long SYMBOL_NAME(syscall_trace) 2: .long 0xefffffff ! BL=0 1: .long TRA +__n_sys: .long NR_syscalls +__sct: .long SYMBOL_NAME(sys_call_table) +__tsk_flags: .long flags-8192 ! offset from stackbase to tsk->flags led: .long 0xa8000000 ! For my board -- gN .section .fixup,"ax" @@ -343,30 +320,57 @@ fixup_syscall_argerr: .long 8b,fixup_syscall_argerr .previous +reschedule: + mova SYMBOL_NAME(ret_from_syscall),r0 + mov.l 1f,r1 + jmp @r1 + lds r0,pr + .balign 4 +1: .long SYMBOL_NAME(schedule) ENTRY(ret_from_irq) - mov.l @(SR,r15),r0 ! get original stack + mov.l @(SR,r15),r0 ! get status register shll r0 shll r0 ! kernel space? bt restore_all ! Yes, it's from kernel, go back soon - ! XXX: Is it better to run through bottom half? - ! In such a case, we should go "ret_from_syscall" instead + ! STI + mov.l 1f, $r1 + stc $sr, $r2 + and $r1, $r2 + ldc $r2, $sr + ! bra ret_with_reschedule nop +ENTRY(ret_from_exception) + mov.l @(SR,r15),r0 ! get status register + shll r0 + shll r0 ! kernel space? + bt restore_all ! Yes, it's from kernel, go back soon + ! STI + mov.l 1f, $r1 + stc $sr, $r2 + and $r1, $r2 + ldc $r2, $sr + ! + bra ret_from_syscall + nop + .balign 4 +1: .long 0xefffffff ! BL=0 + + .balign 4 ret: add r8,r15 ! pop off the arguments mov.l r0,@(R0,r15) ! save the return value /* fall through */ ENTRY(ret_from_syscall) - mov.l __bh_mask,r0 + mov.l __softirq_state,r0 mov.l @r0,r1 - mov.l __bh_active,r0 - mov.l @r0,r2 + mov.l @(4,r0),r2 tst r2,r1 bt ret_with_reschedule -handle_bottom_half: - mov.l __dbh,r0 +handle_softirq: + mov.l __do_softirq,r0 jsr @r0 nop ret_with_reschedule: @@ -378,11 +382,44 @@ ret_with_reschedule: bf reschedule mov.l @(sigpending,r1),r0 tst #0xff,r0 - bf signal_return - ! + bt restore_all +signal_return: + mov r15,r4 + mov #0,r5 + mov.l __do_signal,r1 + mova restore_all,r0 + jmp @r1 + lds r0,pr + .balign 4 +__do_signal: + .long SYMBOL_NAME(do_signal) +__softirq_state: + .long SYMBOL_NAME(softirq_state) +__do_softirq: + .long SYMBOL_NAME(do_softirq) +__minus8192: + .long -8192 ! offset from stackbase to tsk + + .balign 4 restore_all: - add #4,r15 ! skip syscall number - mov.l @r15+,r11 ! SSR +#if defined(__SH4__) + mov.l __fpu_prepare_fd, $r1 + jsr @$r1 + stc $sr, $r4 +#endif + add #4,r15 ! Skip syscall number + mov.l @r15+,r11 ! Got SSR into R11 +#if defined(__SH4__) + mov $r11, $r12 +#endif + ! + mov.l 1f,r1 + stc sr,r0 + and r1,r0 ! Get IMASK+FD + mov.l 2f,r1 + and r1,r11 + or r0,r11 ! Inherit the IMASK+FD value of SR + ! mov.l @r15+,r10 ! original stack mov.l @r15+,r0 mov.l @r15+,r1 @@ -398,6 +435,9 @@ restore_all: ldc r14,sr ! here, change the register bank mov r10,k0 mov r11,k1 +#if defined(__SH4__) + mov $r12, $k2 +#endif mov.l @r15+,r8 mov.l @r15+,r9 mov.l @r15+,r10 @@ -410,21 +450,69 @@ restore_all: lds.l @r15+,macl lds.l @r15+,pr ldc.l @r15+,spc - mov k0,r15 ldc k1,ssr +#if defined(__SH4__) + shll $k1 + shll $k1 + bf 9f ! user mode + /* Kernel to kernel transition */ + mov.l 3f, $k1 + tst $k1, $k2 + bf 9f ! it hadn't FPU + ! Kernel to kernel and FPU was used + ! There's the case we don't get FPU now + stc $sr, $k2 + tst $k1, $k2 + bt 7f + ! We need to grab FPU here + xor $k1, $k2 + ldc $k2, $sr ! Grab FPU + mov.l __init_task_flags, $k1 + mov.l @$k1, $k2 + mov.l __PF_USEDFPU, $k1 + or $k1, $k2 + mov.l __init_task_flags, $k1 + mov.l $k2, @$k1 ! Set init_task.flags |= PF_USEDFPU + ! + ! Restoring FPU... + ! +7: fmov.s @$r15+, $fr0 + fmov.s @$r15+, $fr1 + fmov.s @$r15+, $fr2 + fmov.s @$r15+, $fr3 + fmov.s @$r15+, $fr4 + fmov.s @$r15+, $fr5 + fmov.s @$r15+, $fr6 + fmov.s @$r15+, $fr7 + fmov.s @$r15+, $fr8 + fmov.s @$r15+, $fr9 + fmov.s @$r15+, $fr10 + fmov.s @$r15+, $fr11 + fmov.s @$r15+, $fr12 + fmov.s @$r15+, $fr13 + fmov.s @$r15+, $fr14 + fmov.s @$r15+, $fr15 + lds.l @$r15+, $fpscr + lds.l @$r15+, $fpul +9: +#endif + mov k0,r15 rte nop .balign 4 -__n_sys: .long NR_syscalls -__sct: .long SYMBOL_NAME(sys_call_table) -__bh_mask: .long SYMBOL_NAME(bh_mask) -__bh_active: .long SYMBOL_NAME(bh_active) -__dbh: .long SYMBOL_NAME(do_bottom_half) __blrb_flags: .long 0x30000000 -__minus8192: .long -8192 ! offset from stackbase to tsk -__tsk_flags: .long flags-8192 ! offset from stackbase to tsk->flags - +#if defined(__SH4__) +__fpu_prepare_fd: + .long SYMBOL_NAME(fpu_prepare_fd) +__init_task_flags: + .long SYMBOL_NAME(init_task_union)+4 +__PF_USEDFPU: + .long PF_USEDFPU +#endif +1: .long 0x000080f0 ! IMASK+FD +2: .long 0xffff7f0f ! ~(IMASK+FD) +3: .long 0x00008000 ! FD=1 ! Exception Vector Base ! @@ -441,43 +529,81 @@ general_exception: bra handle_exception mov.l @k2,k2 .balign 4 -2: .long SYMBOL_NAME(ret_from_syscall) +2: .long SYMBOL_NAME(ret_from_exception) 1: .long EXPEVT ! ! .balign 1024,0,1024 tlb_miss: mov.l 1f,k2 - mov.l 3f,k3 + mov.l 4f,k3 bra handle_exception mov.l @k2,k2 ! .balign 512,0,512 interrupt: mov.l 2f,k2 - mov.l 4f,k3 + mov.l 3f,k3 bra handle_exception mov.l @k2,k2 .balign 4 1: .long EXPEVT 2: .long INTEVT -3: .long SYMBOL_NAME(ret_from_syscall) -4: .long SYMBOL_NAME(ret_from_irq) +3: .long SYMBOL_NAME(ret_from_irq) +4: .long SYMBOL_NAME(ret_from_exception) ! ! handle_exception: - ! Using k0, k1 for scratch registers (r0_bank1, and r1_bank1), + ! Using k0, k1 for scratch registers (r0_bank1, r1_bank), ! save all registers onto stack. ! stc ssr,k0 ! from kernel space? shll k0 ! Check MD bit (bit30) shll k0 - bt/s 1f ! it's from kernel to kernel transition +#if defined(__SH4__) + bf/s 8f ! it's from user to kernel transition + mov $r15, $k0 ! save original stack to k0 + /* Kernel to kernel transition */ + mov.l 2f, $k1 + stc $ssr, $k0 + tst $k1, $k0 + bf/s 9f ! FPU is not used + mov $r15, $k0 ! save original stack to k0 + ! FPU is used, save FPU + ! /* XXX: Need to save another bank of FPU if all FPU feature is used */ + ! /* Currently it's not the case for GCC (only udivsi3_i4, divsi3_i4) */ + sts.l $fpul, @-$r15 + sts.l $fpscr, @-$r15 + fmov.s $fr15, @-$r15 + fmov.s $fr14, @-$r15 + fmov.s $fr13, @-$r15 + fmov.s $fr12, @-$r15 + fmov.s $fr11, @-$r15 + fmov.s $fr10, @-$r15 + fmov.s $fr9, @-$r15 + fmov.s $fr8, @-$r15 + fmov.s $fr7, @-$r15 + fmov.s $fr6, @-$r15 + fmov.s $fr5, @-$r15 + fmov.s $fr4, @-$r15 + fmov.s $fr3, @-$r15 + fmov.s $fr2, @-$r15 + fmov.s $fr1, @-$r15 + fmov.s $fr0, @-$r15 + bra 9f + mov #0, $k1 +#else + bt/s 9f ! it's from kernel to kernel transition mov r15,k0 ! save original stack to k0 anyway - mov kernel_sp,r15 ! change to kernel stack -1: stc.l spc,@-r15 +#endif +8: /* User space to kernel */ + mov kernel_sp, $r15 ! change to kernel stack +#if defined(__SH4__) + mov.l 2f, $k1 ! let kernel release FPU +#endif +9: stc.l spc,@-r15 sts.l pr,@-r15 ! lds k3,pr ! Set the return address to pr @@ -487,9 +613,12 @@ handle_exception: stc.l gbr,@-r15 mov.l r14,@-r15 ! - mov.l 2f,k1 - stc sr,r14 ! back to normal register bank, and - and k1,r14 ! .. + stc sr,r14 ! Back to normal register bank, and +#if defined(__SH4__) + or $k1, $r14 ! may release FPU +#endif + mov.l 3f,k1 + and k1,r14 ! ... ldc r14,sr ! ...changed here. ! mov.l r13,@-r15 @@ -520,7 +649,8 @@ handle_exception: mov.l @r15,r0 ! recovering r0.. .balign 4 1: .long SYMBOL_NAME(exception_handling_table) -2: .long 0xdfffffff ! RB=0, BL=1 +2: .long 0x00008000 ! FD=1 +3: .long 0xdfffffff ! RB=0, leave BL=1 none: rts @@ -537,7 +667,11 @@ ENTRY(exception_handling_table) .long tlb_protection_violation_store .long error ! address_error_load (filled by trap_init) .long error ! address_error_store (filled by trap_init) +#if defined(__SH4__) + .long SYMBOL_NAME(do_fpu_error) +#else .long error ! fpu_exception +#endif .long error .long system_call ! Unconditional Trap .long error ! reserved_instruction (filled by trap_init) @@ -628,8 +762,8 @@ ENTRY(interrupt_table) .long error .long error .long error - .long error ! fpu - .long error ! fpu + .long SYMBOL_NAME(do_fpu_state_restore) + .long SYMBOL_NAME(do_fpu_state_restore) #endif ENTRY(sys_call_table) @@ -649,15 +783,15 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_time) .long SYMBOL_NAME(sys_mknod) .long SYMBOL_NAME(sys_chmod) /* 15 */ - .long SYMBOL_NAME(sys_lchown) + .long SYMBOL_NAME(sys_lchown16) .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ .long SYMBOL_NAME(sys_stat) .long SYMBOL_NAME(sys_lseek) .long SYMBOL_NAME(sys_getpid) /* 20 */ .long SYMBOL_NAME(sys_mount) .long SYMBOL_NAME(sys_oldumount) - .long SYMBOL_NAME(sys_setuid) - .long SYMBOL_NAME(sys_getuid) + .long SYMBOL_NAME(sys_setuid16) + .long SYMBOL_NAME(sys_getuid16) .long SYMBOL_NAME(sys_stime) /* 25 */ .long SYMBOL_NAME(sys_ptrace) .long SYMBOL_NAME(sys_alarm) @@ -679,13 +813,13 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_times) .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ .long SYMBOL_NAME(sys_brk) /* 45 */ - .long SYMBOL_NAME(sys_setgid) - .long SYMBOL_NAME(sys_getgid) + .long SYMBOL_NAME(sys_setgid16) + .long SYMBOL_NAME(sys_getgid16) .long SYMBOL_NAME(sys_signal) - .long SYMBOL_NAME(sys_geteuid) - .long SYMBOL_NAME(sys_getegid) /* 50 */ + .long SYMBOL_NAME(sys_geteuid16) + .long SYMBOL_NAME(sys_getegid16) /* 50 */ .long SYMBOL_NAME(sys_acct) - .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ + .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ .long SYMBOL_NAME(sys_ioctl) .long SYMBOL_NAME(sys_fcntl) /* 55 */ @@ -703,19 +837,19 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_sigaction) .long SYMBOL_NAME(sys_sgetmask) .long SYMBOL_NAME(sys_ssetmask) - .long SYMBOL_NAME(sys_setreuid) /* 70 */ - .long SYMBOL_NAME(sys_setregid) + .long SYMBOL_NAME(sys_setreuid16) /* 70 */ + .long SYMBOL_NAME(sys_setregid16) .long SYMBOL_NAME(sys_sigsuspend) .long SYMBOL_NAME(sys_sigpending) .long SYMBOL_NAME(sys_sethostname) .long SYMBOL_NAME(sys_setrlimit) /* 75 */ - .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_old_getrlimit) .long SYMBOL_NAME(sys_getrusage) .long SYMBOL_NAME(sys_gettimeofday) .long SYMBOL_NAME(sys_settimeofday) - .long SYMBOL_NAME(sys_getgroups) /* 80 */ - .long SYMBOL_NAME(sys_setgroups) - .long SYMBOL_NAME(sys_ni_syscall) /* old_select */ + .long SYMBOL_NAME(sys_getgroups16) /* 80 */ + .long SYMBOL_NAME(sys_setgroups16) + .long SYMBOL_NAME(sys_ni_syscall) /* sys_oldselect */ .long SYMBOL_NAME(sys_symlink) .long SYMBOL_NAME(sys_lstat) .long SYMBOL_NAME(sys_readlink) /* 85 */ @@ -723,18 +857,18 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_swapon) .long SYMBOL_NAME(sys_reboot) .long SYMBOL_NAME(old_readdir) - .long SYMBOL_NAME(sys_mmap) /* 90 */ + .long SYMBOL_NAME(old_mmap) /* 90 */ .long SYMBOL_NAME(sys_munmap) .long SYMBOL_NAME(sys_truncate) .long SYMBOL_NAME(sys_ftruncate) .long SYMBOL_NAME(sys_fchmod) - .long SYMBOL_NAME(sys_fchown) /* 95 */ + .long SYMBOL_NAME(sys_fchown16) /* 95 */ .long SYMBOL_NAME(sys_getpriority) .long SYMBOL_NAME(sys_setpriority) .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ .long SYMBOL_NAME(sys_statfs) .long SYMBOL_NAME(sys_fstatfs) /* 100 */ - .long SYMBOL_NAME(sys_ni_syscall) /* ioperm */ + .long SYMBOL_NAME(sys_ni_syscall) /* ioperm */ .long SYMBOL_NAME(sys_socketcall) .long SYMBOL_NAME(sys_syslog) .long SYMBOL_NAME(sys_setitimer) @@ -771,8 +905,8 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_sysfs) /* 135 */ .long SYMBOL_NAME(sys_personality) .long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */ - .long SYMBOL_NAME(sys_setfsuid) - .long SYMBOL_NAME(sys_setfsgid) + .long SYMBOL_NAME(sys_setfsuid16) + .long SYMBOL_NAME(sys_setfsgid16) .long SYMBOL_NAME(sys_llseek) /* 140 */ .long SYMBOL_NAME(sys_getdents) .long SYMBOL_NAME(sys_select) @@ -797,14 +931,14 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_sched_rr_get_interval) .long SYMBOL_NAME(sys_nanosleep) .long SYMBOL_NAME(sys_mremap) - .long SYMBOL_NAME(sys_setresuid) - .long SYMBOL_NAME(sys_getresuid) /* 165 */ - .long SYMBOL_NAME(sys_ni_syscall) /* vm86 */ + .long SYMBOL_NAME(sys_setresuid16) + .long SYMBOL_NAME(sys_getresuid16) /* 165 */ + .long SYMBOL_NAME(sys_ni_syscall) /* vm86 */ .long SYMBOL_NAME(sys_query_module) .long SYMBOL_NAME(sys_poll) .long SYMBOL_NAME(sys_nfsservctl) - .long SYMBOL_NAME(sys_setresgid) /* 170 */ - .long SYMBOL_NAME(sys_getresgid) + .long SYMBOL_NAME(sys_setresgid16) /* 170 */ + .long SYMBOL_NAME(sys_getresgid16) .long SYMBOL_NAME(sys_prctl) .long SYMBOL_NAME(sys_rt_sigreturn) .long SYMBOL_NAME(sys_rt_sigaction) @@ -815,15 +949,42 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_rt_sigsuspend) .long SYMBOL_NAME(sys_pread) /* 180 */ .long SYMBOL_NAME(sys_pwrite) - .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_chown16) .long SYMBOL_NAME(sys_getcwd) .long SYMBOL_NAME(sys_capget) .long SYMBOL_NAME(sys_capset) /* 185 */ .long SYMBOL_NAME(sys_sigaltstack) .long SYMBOL_NAME(sys_sendfile) - .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ - .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ .long SYMBOL_NAME(sys_vfork) /* 190 */ + .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_mmap2) + .long SYMBOL_NAME(sys_truncate64) + .long SYMBOL_NAME(sys_ftruncate64) + .long SYMBOL_NAME(sys_stat64) /* 195 */ + .long SYMBOL_NAME(sys_lstat64) + .long SYMBOL_NAME(sys_fstat64) + .long SYMBOL_NAME(sys_lchown) + .long SYMBOL_NAME(sys_getuid) + .long SYMBOL_NAME(sys_getgid) /* 200 */ + .long SYMBOL_NAME(sys_geteuid) + .long SYMBOL_NAME(sys_getegid) + .long SYMBOL_NAME(sys_setreuid) + .long SYMBOL_NAME(sys_setregid) + .long SYMBOL_NAME(sys_getgroups) /* 205 */ + .long SYMBOL_NAME(sys_setgroups) + .long SYMBOL_NAME(sys_fchown) + .long SYMBOL_NAME(sys_setresuid) + .long SYMBOL_NAME(sys_getresuid) + .long SYMBOL_NAME(sys_setresgid) /* 210 */ + .long SYMBOL_NAME(sys_getresgid) + .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_setuid) + .long SYMBOL_NAME(sys_setgid) + .long SYMBOL_NAME(sys_setfsuid) /* 215 */ + .long SYMBOL_NAME(sys_setfsgid) + .long SYMBOL_NAME(sys_pivot_root) /* * NOTE!! This doesn't have to be exact - we just have @@ -831,7 +992,7 @@ ENTRY(sys_call_table) * entries. Don't panic if you notice that this hasn't * been shrunk every time we add a new system call. */ - .rept NR_syscalls-190 + .rept NR_syscalls-217 .long SYMBOL_NAME(sys_ni_syscall) .endr diff --git a/arch/sh/kernel/fpu.c b/arch/sh/kernel/fpu.c new file mode 100644 index 000000000000..335902c1d81a --- /dev/null +++ b/arch/sh/kernel/fpu.c @@ -0,0 +1,266 @@ +/* $Id: fpu.c,v 1.27 2000/03/05 01:48:34 gniibe Exp $ + * + * linux/arch/sh/kernel/fpu.c + * + * Save/restore floating point context for signal handlers. + * + * 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) 1999, 2000 Kaz Kojima & Niibe Yutaka + * + * FIXME! These routines can be optimized in big endian case. + */ + +#include +#include +#include +#include + +void +save_fpu(struct task_struct *tsk) +{ + asm volatile("sts.l $fpul, @-%0\n\t" + "sts.l $fpscr, @-%0\n\t" + "frchg\n\t" + "fmov.s $fr15, @-%0\n\t" + "fmov.s $fr14, @-%0\n\t" + "fmov.s $fr13, @-%0\n\t" + "fmov.s $fr12, @-%0\n\t" + "fmov.s $fr11, @-%0\n\t" + "fmov.s $fr10, @-%0\n\t" + "fmov.s $fr9, @-%0\n\t" + "fmov.s $fr8, @-%0\n\t" + "fmov.s $fr7, @-%0\n\t" + "fmov.s $fr6, @-%0\n\t" + "fmov.s $fr5, @-%0\n\t" + "fmov.s $fr4, @-%0\n\t" + "fmov.s $fr3, @-%0\n\t" + "fmov.s $fr2, @-%0\n\t" + "fmov.s $fr1, @-%0\n\t" + "fmov.s $fr0, @-%0\n\t" + "frchg\n\t" + "fmov.s $fr15, @-%0\n\t" + "fmov.s $fr14, @-%0\n\t" + "fmov.s $fr13, @-%0\n\t" + "fmov.s $fr12, @-%0\n\t" + "fmov.s $fr11, @-%0\n\t" + "fmov.s $fr10, @-%0\n\t" + "fmov.s $fr9, @-%0\n\t" + "fmov.s $fr8, @-%0\n\t" + "fmov.s $fr7, @-%0\n\t" + "fmov.s $fr6, @-%0\n\t" + "fmov.s $fr5, @-%0\n\t" + "fmov.s $fr4, @-%0\n\t" + "fmov.s $fr3, @-%0\n\t" + "fmov.s $fr2, @-%0\n\t" + "fmov.s $fr1, @-%0\n\t" + "fmov.s $fr0, @-%0" + : /* no output */ + : "r" ((char *)(&tsk->thread.fpu.hard.status)) + : "memory"); + + tsk->flags &= ~PF_USEDFPU; + release_fpu(); +} + +static void +restore_fpu(struct task_struct *tsk) +{ + asm volatile("fmov.s @%0+, $fr0\n\t" + "fmov.s @%0+, $fr1\n\t" + "fmov.s @%0+, $fr2\n\t" + "fmov.s @%0+, $fr3\n\t" + "fmov.s @%0+, $fr4\n\t" + "fmov.s @%0+, $fr5\n\t" + "fmov.s @%0+, $fr6\n\t" + "fmov.s @%0+, $fr7\n\t" + "fmov.s @%0+, $fr8\n\t" + "fmov.s @%0+, $fr9\n\t" + "fmov.s @%0+, $fr10\n\t" + "fmov.s @%0+, $fr11\n\t" + "fmov.s @%0+, $fr12\n\t" + "fmov.s @%0+, $fr13\n\t" + "fmov.s @%0+, $fr14\n\t" + "fmov.s @%0+, $fr15\n\t" + "frchg\n\t" + "fmov.s @%0+, $fr0\n\t" + "fmov.s @%0+, $fr1\n\t" + "fmov.s @%0+, $fr2\n\t" + "fmov.s @%0+, $fr3\n\t" + "fmov.s @%0+, $fr4\n\t" + "fmov.s @%0+, $fr5\n\t" + "fmov.s @%0+, $fr6\n\t" + "fmov.s @%0+, $fr7\n\t" + "fmov.s @%0+, $fr8\n\t" + "fmov.s @%0+, $fr9\n\t" + "fmov.s @%0+, $fr10\n\t" + "fmov.s @%0+, $fr11\n\t" + "fmov.s @%0+, $fr12\n\t" + "fmov.s @%0+, $fr13\n\t" + "fmov.s @%0+, $fr14\n\t" + "fmov.s @%0+, $fr15\n\t" + "frchg\n\t" + "lds.l @%0+, $fpscr\n\t" + "lds.l @%0+, $fpul\n\t" + : /* no output */ + : "r" (&tsk->thread.fpu) + : "memory"); +} + +/* + * Load the FPU with signalling NANS. This bit pattern we're using + * has the property that no matter wether considered as single or as + * double precission represents signaling NANS. + */ +/* Double presision, NANS as NANS, rounding to nearest, no exceptions */ +#define FPU_DEFAULT 0x00080000 + +void fpu_init(void) +{ + asm volatile("lds %0, $fpul\n\t" + "lds %1, $fpscr\n\t" + "fsts $fpul, $fr0\n\t" + "fsts $fpul, $fr1\n\t" + "fsts $fpul, $fr2\n\t" + "fsts $fpul, $fr3\n\t" + "fsts $fpul, $fr4\n\t" + "fsts $fpul, $fr5\n\t" + "fsts $fpul, $fr6\n\t" + "fsts $fpul, $fr7\n\t" + "fsts $fpul, $fr8\n\t" + "fsts $fpul, $fr9\n\t" + "fsts $fpul, $fr10\n\t" + "fsts $fpul, $fr11\n\t" + "fsts $fpul, $fr12\n\t" + "fsts $fpul, $fr13\n\t" + "fsts $fpul, $fr14\n\t" + "fsts $fpul, $fr15\n\t" + "frchg\n\t" + "fsts $fpul, $fr0\n\t" + "fsts $fpul, $fr1\n\t" + "fsts $fpul, $fr2\n\t" + "fsts $fpul, $fr3\n\t" + "fsts $fpul, $fr4\n\t" + "fsts $fpul, $fr5\n\t" + "fsts $fpul, $fr6\n\t" + "fsts $fpul, $fr7\n\t" + "fsts $fpul, $fr8\n\t" + "fsts $fpul, $fr9\n\t" + "fsts $fpul, $fr10\n\t" + "fsts $fpul, $fr11\n\t" + "fsts $fpul, $fr12\n\t" + "fsts $fpul, $fr13\n\t" + "fsts $fpul, $fr14\n\t" + "fsts $fpul, $fr15\n\t" + "frchg" + : /* no output */ + : "r" (0), "r" (FPU_DEFAULT)); +} + +asmlinkage void +do_fpu_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, + struct pt_regs regs) +{ + struct task_struct *tsk = current; + + regs.syscall_nr = -1; + regs.pc += 2; + + grab_fpu(); + save_fpu(tsk); + tsk->thread.trap_no = 11; + tsk->thread.error_code = 0; + force_sig(SIGFPE, tsk); +} + +asmlinkage void +do_fpu_state_restore(unsigned long r4, unsigned long r5, unsigned long r6, + unsigned long r7, struct pt_regs regs) +{ + struct task_struct *tsk = current; + + regs.syscall_nr = -1; + + if (!user_mode(®s)) { + if (tsk != &init_task) { + unlazy_fpu(tsk); + } + tsk = &init_task; + if (tsk->flags & PF_USEDFPU) + BUG(); + } + + grab_fpu(); + if (tsk->used_math) { + /* Using the FPU again. */ + restore_fpu(tsk); + } else { + /* First time FPU user. */ + fpu_init(); + tsk->used_math = 1; + } + tsk->flags |= PF_USEDFPU; + release_fpu(); +} + +/* + * Change current FD flag to set FD flag back to exception + */ +asmlinkage void +fpu_prepare_fd(unsigned long sr, unsigned long r5, unsigned long r6, + unsigned long r7, struct pt_regs regs) +{ + __cli(); + if (!user_mode(®s)) { + if (init_task.flags & PF_USEDFPU) + grab_fpu(); + else { + if (!(sr & SR_FD)) { + release_fpu(); + BUG(); + } + } + return; + } + + if (sr & SR_FD) { /* Kernel doesn't grab FPU */ + if (current->flags & PF_USEDFPU) + grab_fpu(); + else { + if (init_task.flags & PF_USEDFPU) { + init_task.flags &= ~PF_USEDFPU; + BUG(); + } + } + } else { + if (init_task.flags & PF_USEDFPU) + save_fpu(&init_task); + else { + release_fpu(); + BUG(); + } + } +} + +/* Short cut for the FPU exception */ +asmlinkage void +enable_fpu_in_danger(void) +{ + struct task_struct *tsk = current; + + if (tsk != &init_task) + unlazy_fpu(tsk); + + tsk = &init_task; + if (tsk->used_math) { + /* Using the FPU again. */ + restore_fpu(tsk); + } else { + /* First time FPU user. */ + fpu_init(); + tsk->used_math = 1; + } + tsk->flags |= PF_USEDFPU; +} diff --git a/arch/sh/kernel/head.S b/arch/sh/kernel/head.S index f6378927ee9f..3f938557a1aa 100644 --- a/arch/sh/kernel/head.S +++ b/arch/sh/kernel/head.S @@ -1,8 +1,8 @@ -/* $Id: head.S,v 1.7 1999/10/27 09:41:42 gniibe Exp gniibe $ +/* $Id: head.S,v 1.16 2000/03/02 00:01:15 gniibe Exp $ * * arch/sh/kernel/head.S * - * Copyright (C) 1999 Niibe Yutaka & Kaz Kojima + * Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima * * 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 @@ -34,49 +34,37 @@ ENTRY(empty_zero_page) * Cache may or may not be initialized. * Hardware (including on-chip modules) may or may not be initialized. * - * The register R4&R5 holds the address of the parameter block, which has - * command-line data, etc. - * */ ENTRY(_stext) -#if defined(__SH4__) - ! Initialize FPSCR - /* GCC (as of 2.95.1) assumes FPU with double precision mode. */ - mov.l 7f,r0 - lds r0,fpscr -#endif ! Initialize Status Register - mov.l 1f,r0 ! MD=1, RB=0, BL=1 - ldc r0,sr + mov.l 1f, $r0 ! MD=1, RB=0, BL=1 + ldc $r0, $sr ! - mov.l 2f,r0 - mov r0,r15 ! Set initial r15 (stack pointer) - ldc r0,r4_bank ! and stack base + mov.l 2f, $r0 + mov $r0, $r15 ! Set initial r15 (stack pointer) + ldc $r0, $r4_bank ! and stack base ! ! Enable cache - mov.l 6f,r0 - jsr @r0 + mov.l 6f, $r0 + jsr @$r0 nop ! Clear BSS area - mov.l 3f,r1 - add #4,r1 - mov.l 4f,r2 - mov #0,r0 -9: cmp/hs r2,r1 + mov.l 3f, $r1 + add #4, $r1 + mov.l 4f, $r2 + mov #0, $r0 +9: cmp/hs $r2, $r1 bf/s 9b ! while (r1 < r2) - mov.l r0,@-r2 + mov.l $r0,@-$r2 ! Start kernel - mov.l 5f,r0 - jmp @r0 + mov.l 5f, $r0 + jmp @$r0 nop .balign 4 -1: .long 0x50000000 ! MD=1, RB=0, BL=1 +1: .long 0x50000000 ! MD=1, RB=0, BL=1, FD=0 2: .long SYMBOL_NAME(stack) 3: .long SYMBOL_NAME(__bss_start) 4: .long SYMBOL_NAME(_end) 5: .long SYMBOL_NAME(start_kernel) 6: .long SYMBOL_NAME(cache_init) -#if defined(__SH4__) -7: .long 0x00080000 -#endif diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index e87972c737ca..a153523895a3 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.4 1999/10/11 13:12:14 gniibe Exp $ +/* $Id: irq.c,v 1.11 2000/02/29 11:03:40 gniibe Exp $ * * linux/arch/sh/kernel/irq.c * @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,7 +49,8 @@ spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED; /* * Controller mappings for all interrupt sources: */ -irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; /* * Special irq handlers. @@ -112,9 +113,8 @@ int get_irq_list(char *buf) p += sprintf(p, " %14s", irq_desc[i].handler->typename); p += sprintf(p, " %s", action->name); - for (action=action->next; action; action = action->next) { + for (action=action->next; action; action = action->next) p += sprintf(p, ", %s", action->name); - } *p++ = '\n'; } return p - buf; @@ -248,7 +248,7 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, kstat.irqs[cpu][irq]++; desc = irq_desc + irq; spin_lock(&irq_controller_lock); - irq_desc[irq].handler->ack(irq); + desc->handler->ack(irq); /* REPLAY is when Linux resends an IRQ that was dropped earlier WAITING is used by probe to mark irqs that are being tested @@ -298,21 +298,15 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, spin_unlock(&irq_controller_lock); } desc->status &= ~IRQ_INPROGRESS; - if (!(desc->status & IRQ_DISABLED)){ - irq_desc[irq].handler->end(irq); - } + if (!(desc->status & IRQ_DISABLED)) + desc->handler->end(irq); spin_unlock(&irq_controller_lock); - /* - * This should be conditional: we should really get - * a return code from the irq handler to tell us - * whether the handler wants us to do software bottom - * half handling or not.. - */ - if (1) { - if (bh_active & bh_mask) - do_bottom_half(); - } +#if 1 + __sti(); +#endif + if (softirq_state[cpu].active&softirq_state[cpu].mask) + do_softirq(); return 1; } @@ -347,7 +341,7 @@ int request_irq(unsigned int irq, kfree(action); return retval; } - + void free_irq(unsigned int irq, void *dev_id) { struct irqaction **p; @@ -373,10 +367,6 @@ void free_irq(unsigned int irq, void *dev_id) irq_desc[irq].handler->shutdown(irq); } spin_unlock_irqrestore(&irq_controller_lock,flags); - - /* Wait to make sure it's not being used on another CPU */ - while (irq_desc[irq].status & IRQ_INPROGRESS) - barrier(); kfree(action); return; } @@ -398,6 +388,7 @@ unsigned long probe_irq_on(void) { unsigned int i; unsigned long delay; + unsigned long val; /* * first, enable any unassigned irqs @@ -421,6 +412,7 @@ unsigned long probe_irq_on(void) /* * Now filter out any obviously spurious interrupts */ + val = 0; spin_lock_irq(&irq_controller_lock); for (i=0; ishutdown(i); } + + if (i < 32) + val |= 1 << i; } spin_unlock_irq(&irq_controller_lock); - return 0x12345678; + return val; } -int probe_irq_off(unsigned long unused) +int probe_irq_off(unsigned long val) { int i, irq_found, nr_irqs; - if (unused != 0x12345678) - printk("Bad IRQ probe from %lx\n", (&unused)[-1]); - nr_irqs = 0; irq_found = 0; spin_lock_irq(&irq_controller_lock); diff --git a/arch/sh/kernel/irq_imask.c b/arch/sh/kernel/irq_imask.c new file mode 100644 index 000000000000..a3cf78b5f525 --- /dev/null +++ b/arch/sh/kernel/irq_imask.c @@ -0,0 +1,106 @@ +/* $Id: irq_imask.c,v 1.2 2000/02/11 04:57:40 gniibe Exp $ + * + * linux/arch/sh/kernel/irq_imask.c + * + * Copyright (C) 1999 Niibe Yutaka + * + * Simple interrupt handling using IMASK of SR register. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* Bitmap of IRQ masked */ +static unsigned long imask_mask = 0x7fff; +static int interrupt_priority = 0; + +static void enable_imask_irq(unsigned int irq); +static void disable_imask_irq(unsigned int irq); +static void shutdown_imask_irq(unsigned int irq); +static void mask_and_ack_imask(unsigned int); +static void end_imask_irq(unsigned int irq); + +#define IMASK_PRIORITY 15 + +static unsigned int startup_imask_irq(unsigned int irq) +{ + enable_imask_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type imask_irq_type = { + "Interrupt using IMASK of SR register", + startup_imask_irq, + shutdown_imask_irq, + enable_imask_irq, + disable_imask_irq, + mask_and_ack_imask, + end_imask_irq +}; + +void disable_imask_irq(unsigned int irq) +{ + unsigned long __dummy; + + clear_bit(irq, &imask_mask); + if (interrupt_priority < IMASK_PRIORITY - irq) + interrupt_priority = IMASK_PRIORITY - irq; + + asm volatile("stc sr,%0\n\t" + "and %1,%0\n\t" + "or %2,%0\n\t" + "ldc %0,sr" + : "=&r" (__dummy) + : "r" (0xffffff0f), "r" (interrupt_priority << 4)); +} + +static void enable_imask_irq(unsigned int irq) +{ + unsigned long __dummy; + + set_bit(irq, &imask_mask); + interrupt_priority = IMASK_PRIORITY - ffz(imask_mask); + + asm volatile("stc sr,%0\n\t" + "and %1,%0\n\t" + "or %2,%0\n\t" + "ldc %0,sr" + : "=&r" (__dummy) + : "r" (0xffffff0f), "r" (interrupt_priority << 4)); +} + +static void mask_and_ack_imask(unsigned int irq) +{ + disable_imask_irq(irq); +} + +static void end_imask_irq(unsigned int irq) +{ + enable_imask_irq(irq); +} + +static void shutdown_imask_irq(unsigned int irq) +{ + disable_imask_irq(irq); +} + +void make_imask_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &imask_irq_type; + enable_irq(irq); +} diff --git a/arch/sh/kernel/irq_onchip.c b/arch/sh/kernel/irq_onchip.c index cd28d2a596bf..10c48fd38b9f 100644 --- a/arch/sh/kernel/irq_onchip.c +++ b/arch/sh/kernel/irq_onchip.c @@ -1,4 +1,4 @@ -/* $Id: irq_onchip.c,v 1.5 1999/10/28 02:18:33 gniibe Exp $ +/* $Id: irq_onchip.c,v 1.7 2000-01-09 15:55:55+09 gniibe Exp $ * * linux/arch/sh/kernel/irq_onchip.c * @@ -143,7 +143,18 @@ static void end_onChip_irq(unsigned int irq) */ #define INTC_IRR0 0xa4000004UL -#define INTC_IPRC 0xa4000016UL +#define INTC_IRR1 0xa4000006UL +#define INTC_IRR2 0xa4000008UL + +#define INTC_ICR0 0xfffffee0 +#define INTC_ICR1 0xa4000010 +#define INTC_ICR2 0xa4000012 +#define INTC_INTER 0xa4000014 +#define INTC_IPRA 0xfffffee2 +#define INTC_IPRB 0xfffffee4 +#define INTC_IPRC 0xa4000016 +#define INTC_IPRD 0xa4000018 +#define INTC_IPRE 0xa400001a #define IRQ0_IRQ 32 #define IRQ1_IRQ 33 @@ -248,6 +259,26 @@ void __init init_IRQ(void) } #ifdef CONFIG_CPU_SUBTYPE_SH7709 + + /* + * Initialize the Interrupt Controller (INTC) + * registers to their power on values + */ + + ctrl_outb(0, INTC_IRR0); + ctrl_outb(0, INTC_IRR1); + ctrl_outb(0, INTC_IRR2); + + ctrl_outw(0, INTC_ICR0); + ctrl_outw(0, INTC_ICR1); + ctrl_outw(0, INTC_ICR2); + ctrl_outw(0, INTC_INTER); + ctrl_outw(0, INTC_IPRA); + ctrl_outw(0, INTC_IPRB); + ctrl_outw(0, INTC_IPRC); + ctrl_outw(0, INTC_IPRD); + ctrl_outw(0, INTC_IPRE); + for (i = IRQ0_IRQ; i < NR_IRQS; i++) { irq_desc[i].handler = &onChip2_irq_type; } @@ -263,8 +294,5 @@ void __init init_IRQ(void) set_ipr_data(IRQ3_IRQ, IRQ3_IRP_OFFSET, IRQ3_PRIORITY); set_ipr_data(IRQ4_IRQ, IRQ4_IRP_OFFSET, IRQ4_PRIORITY); set_ipr_data(IRQ5_IRQ, IRQ5_IRP_OFFSET, IRQ5_PRIORITY); - - ctrl_inb(INTC_IRR0); - ctrl_outb(0, INTC_IRR0); #endif /* CONFIG_CPU_SUBTYPE_SH7709 */ } diff --git a/arch/sh/kernel/pci-sh.c b/arch/sh/kernel/pci-sh.c new file mode 100644 index 000000000000..3613596f7246 --- /dev/null +++ b/arch/sh/kernel/pci-sh.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 5d2a5696ca94..2ca91cb403c7 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -1,10 +1,10 @@ -/* $Id: process.c,v 1.8 1999/10/31 13:19:16 gniibe Exp $ +/* $Id: process.c,v 1.28 2000/03/05 02:16:15 gniibe Exp $ * * linux/arch/sh/kernel/process.c * * Copyright (C) 1995 Linus Torvalds * - * SuperH version: Copyright (C) 1999 Niibe Yutaka & Kaz Kojima + * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima */ /* @@ -42,10 +42,6 @@ #include -#if defined(__SH4__) -struct task_struct *last_task_used_math = NULL; -#endif - static int hlt_counter=0; #define HARD_IDLE_TIMEOUT (HZ / 3) @@ -140,25 +136,25 @@ void free_task_struct(struct task_struct *p) */ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { /* Don't use this in BL=1(cli). Or else, CPU resets! */ - register unsigned long __sc0 __asm__ ("r0") = __NR_clone; - register unsigned long __sc4 __asm__ ("r4") = (long) flags | CLONE_VM; - register unsigned long __sc5 __asm__ ("r5") = 0; - register unsigned long __sc8 __asm__ ("r8") = (long) arg; - register unsigned long __sc9 __asm__ ("r9") = (long) fn; - __asm__ __volatile__( - "trapa #0\n\t" /* Linux/SH system call */ - "tst #0xff,r0\n\t" /* child or parent? */ + register unsigned long __sc0 __asm__ ("$r0") = __NR_clone; + register unsigned long __sc4 __asm__ ("$r4") = (long) flags | CLONE_VM; + register unsigned long __sc5 __asm__ ("$r5") = 0; + register unsigned long __sc8 __asm__ ("$r8") = (long) arg; + register unsigned long __sc9 __asm__ ("$r9") = (long) fn; + + __asm__("trapa #0\n\t" /* Linux/SH system call */ + "tst #0xff, $r0\n\t" /* child or parent? */ "bf 1f\n\t" /* parent - jump */ - "jsr @r9\n\t" /* call fn */ - " mov r8,r4\n\t" /* push argument */ - "mov r0,r4\n\t" /* return value to arg of exit */ - "mov %2,r0\n\t" /* exit */ + "jsr @$r9\n\t" /* call fn */ + " mov $r8, $r4\n\t" /* push argument */ + "mov $r0, $r4\n\t" /* return value to arg of exit */ + "mov %2, $r0\n\t" /* exit */ "trapa #0\n" "1:" - :"=z" (__sc0) - :"0" (__sc0), "i" (__NR_exit), - "r" (__sc4), "r" (__sc5), "r" (__sc8), "r" (__sc9) - :"memory"); + : "=z" (__sc0) + : "0" (__sc0), "i" (__NR_exit), + "r" (__sc4), "r" (__sc5), "r" (__sc8), "r" (__sc9) + : "memory"); return __sc0; } @@ -167,18 +163,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) */ void exit_thread(void) { -#if defined(__sh3__) - /* nothing to do ... */ -#elif defined(__SH4__) -#if 0 /* for the time being... */ - /* Forget lazy fpu state */ - if (last_task_used_math == current) { - set_status_register (SR_FD, 0); - write_system_register (fpscr, FPSCR_PR); - last_task_used_math = NULL; - } -#endif -#endif + /* Nothing to do. */ } void flush_thread(void) @@ -187,14 +172,11 @@ void flush_thread(void) /* do nothing */ /* Possibly, set clear debug registers */ #elif defined(__SH4__) -#if 0 /* for the time being... */ - /* Forget lazy fpu state */ - if (last_task_used_math == current) { - set_status_register (SR_FD, 0); - write_system_register (fpscr, FPSCR_PR); - last_task_used_math = NULL; - } -#endif + struct task_struct *tsk = current; + + /* Forget lazy FPU state */ + clear_fpu(tsk); + tsk->used_math = 0; #endif } @@ -204,18 +186,22 @@ void release_thread(struct task_struct *dead_task) } /* Fill in the fpu structure for a core dump.. */ -int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) { #if defined(__SH4__) -#if 0 /* for the time being... */ - /* We store the FPU info in the task->thread area. */ - if (! (regs->sr & SR_FD)) { - memcpy (r, ¤t->thread.fpu, sizeof (*r)); - return 1; - } -#endif -#endif + int fpvalid; + struct task_struct *tsk = current; + + fpvalid = tsk->used_math; + if (fpvalid) { + unlazy_fpu(tsk); + memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu)); + } + + return fpvalid; +#else return 0; /* Task didn't use the fpu at all. */ +#endif } asmlinkage void ret_from_fork(void); @@ -224,21 +210,17 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct *p, struct pt_regs *regs) { struct pt_regs *childregs; + struct task_struct *tsk = current; childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long) p)) - 1; - *childregs = *regs; + struct_cpy(childregs, regs); #if defined(__SH4__) -#if 0 /* for the time being... */ - if (last_task_used_math == current) { - set_status_register (SR_FD, 0); - sh4_save_fp (p); + if (tsk != &init_task) { + unlazy_fpu(tsk); + struct_cpy(&p->thread.fpu, ¤t->thread.fpu); + p->used_math = tsk->used_math; } - /* New tasks loose permission to use the fpu. This accelerates context - switching for most programs since they don't use the fpu. */ - p->thread.sr = (read_control_register (sr) &~ SR_MD) | SR_FD; - childregs->sr |= SR_FD; -#endif #endif if (user_mode(regs)) { childregs->sp = usp; @@ -246,6 +228,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childregs->sp = (unsigned long)p+2*PAGE_SIZE; } childregs->regs[0] = 0; /* Set return value for child */ + childregs->sr |= SR_FD; /* Invalidate FPU flag */ p->thread.sp = (unsigned long) childregs; p->thread.pc = (unsigned long) ret_from_fork; @@ -258,7 +241,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, */ void dump_thread(struct pt_regs * regs, struct user * dump) { -/* changed the size calculations - should hopefully work better. lbt */ dump->magic = CMAGIC; dump->start_code = current->mm->start_code; dump->start_data = current->mm->start_data; @@ -271,11 +253,7 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->regs = *regs; -#if 0 /* defined(__SH4__) */ - /* FPU */ - memcpy (&dump->regs[EF_SIZE/4], ¤t->thread.fpu, - sizeof (current->thread.fpu)); -#endif + dump->u_fpvalid = dump_fpu(regs, &dump->fpu); } /* @@ -284,11 +262,15 @@ void dump_thread(struct pt_regs * regs, struct user * dump) */ void __switch_to(struct task_struct *prev, struct task_struct *next) { +#if defined(__SH4__) + if (prev != &init_task) + unlazy_fpu(prev); +#endif /* * Restore the kernel stack onto kernel mode register * k4 (r4_bank1) */ - asm volatile("ldc %0,r4_bank" + asm volatile("ldc %0, $r4_bank" : /* no output */ :"r" ((unsigned long)next+8192)); } @@ -341,6 +323,7 @@ asmlinkage int sys_execve(char *ufilename, char **uargv, error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; + error = do_execve(filename, uargv, uenvp, ®s); if (error == 0) current->flags &= ~PF_DTRACE; @@ -349,3 +332,41 @@ out: unlock_kernel(); return error; } + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long schedule_frame; + unsigned long pc; + + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + /* + * The same comment as on the Alpha applies here, too ... + */ + pc = thread_saved_pc(&p->thread); + if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) { + schedule_frame = ((unsigned long *)(long)p->thread.sp)[1]; + return (unsigned long)((unsigned long *)schedule_frame)[1]; + } + return pc; +} + +asmlinkage void print_syscall(int x) +{ + unsigned long flags, sr; + asm("stc $sr, %0": "=r" (sr)); + save_and_cli(flags); + printk("%c: %c %c, %c: SYSCALL\n", (x&63)+32, + (current->flags&PF_USEDFPU)?'C':' ', + (init_task.flags&PF_USEDFPU)?'K':' ', (sr&SR_FD)?' ':'F'); + restore_flags(flags); +} diff --git a/arch/sh/kernel/semaphore.c b/arch/sh/kernel/semaphore.c index b9f565dd820d..c958745b59b6 100644 --- a/arch/sh/kernel/semaphore.c +++ b/arch/sh/kernel/semaphore.c @@ -8,6 +8,8 @@ */ #include +#include +#include #include /* @@ -131,3 +133,162 @@ int __down_trylock(struct semaphore * sem) { return waking_non_zero_trylock(sem); } + +/* Called when someone has done an up that transitioned from + * negative to non-negative, meaning that the lock has been + * granted to whomever owned the bias. + */ +struct rw_semaphore *rwsem_wake_readers(struct rw_semaphore *sem) +{ + if (xchg(&sem->read_bias_granted, 1)) + BUG(); + wake_up(&sem->wait); + return sem; +} + +struct rw_semaphore *rwsem_wake_writer(struct rw_semaphore *sem) +{ + if (xchg(&sem->write_bias_granted, 1)) + BUG(); + wake_up(&sem->write_bias_wait); + return sem; +} + +struct rw_semaphore * __rwsem_wake(struct rw_semaphore *sem) +{ + if (atomic_read(&sem->count) == 0) + return rwsem_wake_writer(sem); + else + return rwsem_wake_readers(sem); +} + +struct rw_semaphore *down_read_failed_biased(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&sem->wait, &wait); /* put ourselves at the head of the list */ + + for (;;) { + if (sem->read_bias_granted && xchg(&sem->read_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!sem->read_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + return sem; +} + +struct rw_semaphore *down_write_failed_biased(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); /* put ourselves at the end of the list */ + + for (;;) { + if (sem->write_bias_granted && xchg(&sem->write_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (!sem->write_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* if the lock is currently unbiased, awaken the sleepers + * FIXME: this wakes up the readers early in a bit of a + * stampede -> bad! + */ + if (atomic_read(&sem->count) >= 0) + wake_up(&sem->wait); + + return sem; +} + +/* Wait for the lock to become unbiased. Readers + * are non-exclusive. =) + */ +struct rw_semaphore *down_read_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __up_read(sem); /* this takes care of granting the lock */ + + add_wait_queue(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + return sem; +} + +/* Wait for the lock to become unbiased. Since we're + * a writer, we'll make ourselves exclusive. + */ +struct rw_semaphore *down_write_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __up_write(sem); /* this takes care of granting the lock */ + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (atomic_read(&sem->count) >= 0) + break; /* we must attempt to aquire or bias the lock */ + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + return sem; +} + +struct rw_semaphore *__down_read(struct rw_semaphore *sem, int carry) +{ + if (carry) { + int saved, new; + + do { + down_read_failed(sem); + saved = atomic_read(&sem->count); + if ((new = atomic_dec_return(&sem->count)) >= 0) + return sem; + } while (!(new < 0 && saved >=0)); + } + + return down_read_failed_biased(sem); +} + +struct rw_semaphore *__down_write(struct rw_semaphore *sem, int carry) +{ + if (carry) { + int saved, new; + + do { + down_write_failed(sem); + saved = atomic_read(&sem->count); + if ((new = atomic_sub_return(RW_LOCK_BIAS, &sem->count) ) == 0) + return sem; + } while (!(new < 0 && saved >=0)); + } + + return down_write_failed_biased(sem); +} diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index f97e66585f1a..1542835712a1 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.7 1999/10/23 01:34:50 gniibe Exp gniibe $ +/* $Id: setup.c,v 1.20 2000/03/05 02:44:41 gniibe Exp $ * * linux/arch/sh/kernel/setup.c * @@ -51,6 +51,7 @@ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ extern int rd_image_start; /* starting block # of image */ #endif +extern void fpu_init(void); extern int root_mountflags; extern int _text, _etext, _edata, _end; @@ -195,83 +196,83 @@ void __init setup_arch(char **cmdline_p) #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) #define PFN_PHYS(x) ((x) << PAGE_SHIFT) - /* - * partially used pages are not usable - thus - * we are rounding upwards: - */ - start_pfn = PFN_UP(__pa(&_end)-__MEMORY_START); - /* * Find the highest page frame number we have available */ - max_pfn = PFN_DOWN(__pa(memory_end)-__MEMORY_START); + max_pfn = PFN_DOWN(__pa(memory_end)); /* * Determine low and high memory ranges: */ max_low_pfn = max_pfn; + /* + * Partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = PFN_UP(__pa(&_end)); /* - * Initialize the boot-time allocator (with low memory only): - */ - bootmap_size = init_bootmem(start_pfn, max_low_pfn, __MEMORY_START); - - /* - * FIXME: what about high memory? + * Find a proper area for the bootmem bitmap. After this + * bootstrap step all allocations (until the page allocator + * is intact) must be done via bootmem_alloc(). */ - ram_resources[1].end = PFN_PHYS(max_low_pfn) + __MEMORY_START; + bootmap_size = init_bootmem_node(0, start_pfn, + __MEMORY_START>>PAGE_SHIFT, + max_low_pfn); /* * Register fully available low RAM pages with the bootmem allocator. */ { - unsigned long curr_pfn, last_pfn, size; + unsigned long curr_pfn, last_pfn, pages; /* * We are rounding up the start address of usable memory: */ - curr_pfn = PFN_UP(0); + curr_pfn = PFN_UP(__MEMORY_START); /* * ... and at the end of the usable range downwards: */ - last_pfn = PFN_DOWN(memory_end-__MEMORY_START); + last_pfn = PFN_DOWN(__pa(memory_end)); if (last_pfn > max_low_pfn) last_pfn = max_low_pfn; - size = last_pfn - curr_pfn; - free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + pages = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(pages)); } + /* * Reserve the kernel text and - * Reserve the bootmem bitmap itself as well. We do this in two - * steps (first step was init_bootmem()) because this catches - * the (very unlikely) case of us accidentally initializing the - * bootmem allocator with an invalid RAM area. + * Reserve the bootmem bitmap.We do this in two steps (first step + * was init_bootmem()), because this catches the (definitely buggy) + * case of us accidentally initializing the bootmem allocator with + * an invalid RAM area. */ - reserve_bootmem(PAGE_SIZE, PFN_PHYS(start_pfn) + bootmap_size); + reserve_bootmem(__MEMORY_START+PAGE_SIZE, (PFN_PHYS(start_pfn) + + bootmap_size + PAGE_SIZE-1) - __MEMORY_START); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(0, PAGE_SIZE); + reserve_bootmem(__MEMORY_START, PAGE_SIZE); #ifdef CONFIG_BLK_DEV_INITRD - if (LOADER_TYPE) { + if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { - reserve_bootmem(INITRD_START, INITRD_SIZE); - initrd_start = - INITRD_START ? INITRD_START + PAGE_OFFSET + __MEMORY_START : 0; - initrd_end = initrd_start+INITRD_SIZE; + reserve_bootmem(INITRD_START+__MEMORY_START, INITRD_SIZE); + initrd_start = + INITRD_START ? INITRD_START + PAGE_OFFSET + __MEMORY_START : 0; + initrd_end = initrd_start + INITRD_SIZE; } else { - printk("initrd extends beyond end of memory " - "(0x%08lx > 0x%08lx)\ndisabling initrd\n", - INITRD_START + INITRD_SIZE, - max_low_pfn << PAGE_SHIFT); - initrd_start = 0; - } - } + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + INITRD_START + INITRD_SIZE, + max_low_pfn << PAGE_SHIFT); + initrd_start = 0; + } + } #endif #if 0 @@ -298,6 +299,14 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif #endif + +#if defined(__SH4__) + init_task.used_math = 1; + init_task.flags |= PF_USEDFPU; + grab_fpu(); + fpu_init(); +#endif + paging_init(); } /* diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index 7c9fbbf008ba..0c24acf73d0e 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.10 1999/09/27 23:25:44 gniibe Exp $ +/* $Id: signal.c,v 1.16 2000/01/29 11:31:31 gniibe Exp gniibe $ * * linux/arch/sh/kernel/signal.c * @@ -54,7 +54,7 @@ sys_sigsuspend(old_sigset_t mask, while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s,&saveset)) + if (do_signal(®s, &saveset)) return -EINTR; } } @@ -73,7 +73,6 @@ sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, if (copy_from_user(&newset, unewset, sizeof(newset))) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); saveset = current->blocked; current->blocked = newset; @@ -188,6 +187,7 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1], &frame->extramask, @@ -195,6 +195,7 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); current->blocked = set; recalc_sigpending(current); @@ -220,6 +221,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; @@ -228,7 +230,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, current->blocked = set; recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &r0)) goto badframe; @@ -317,7 +319,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, if (ka->sa.sa_flags & SA_RESTORER) { regs->pr = (unsigned long) ka->sa.sa_restorer; } else { - /* This is ; mov #__NR_sigreturn,r0 ; trapa #0 */ + /* This is : mov #__NR_sigreturn,r0 ; trapa #0 */ #ifdef __LITTLE_ENDIAN__ unsigned long code = 0xc300e000 | (__NR_sigreturn); #else @@ -390,11 +392,11 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, if (ka->sa.sa_flags & SA_RESTORER) { regs->pr = (unsigned long) ka->sa.sa_restorer; } else { - /* This is ; mov #__NR_sigreturn,r0 ; trapa #0 */ + /* This is : mov #__NR_rt_sigreturn,r0 ; trapa #0 */ #ifdef __LITTLE_ENDIAN__ - unsigned long code = 0xc300e000 | (__NR_sigreturn); + unsigned long code = 0xc300e000 | (__NR_rt_sigreturn); #else - unsigned long code = 0xe000c300 | (__NR_sigreturn << 16); + unsigned long code = 0xe000c300 | (__NR_rt_sigreturn << 16); #endif regs->pr = (unsigned long) frame->retcode; @@ -485,6 +487,15 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) siginfo_t info; struct k_sigaction *ka; + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + if (!oldset) oldset = ¤t->blocked; @@ -580,6 +591,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) /* NOTREACHED */ } } + /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs); return 1; diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c index 1b708e5a9a6f..0b3d5fc2e8ed 100644 --- a/arch/sh/kernel/sys_sh.c +++ b/arch/sh/kernel/sys_sh.c @@ -28,7 +28,9 @@ * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way Unix traditionally does this, though. */ -asmlinkage int sys_pipe(unsigned long * fildes) +asmlinkage int sys_pipe(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs regs) { int fd[2]; int error; @@ -37,46 +39,62 @@ asmlinkage int sys_pipe(unsigned long * fildes) error = do_pipe(fd); unlock_kernel(); if (!error) { - if (copy_to_user(fildes, fd, 2*sizeof(int))) - error = -EFAULT; + regs.regs[1] = fd[1]; + return fd[0]; } return error; } -asmlinkage unsigned long -sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, - unsigned long flags, int fd, unsigned long off) +static inline long +do_mmap2(unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, int fd, unsigned long pgoff) { - int error = -EFAULT; + int error = -EBADF; struct file *file = NULL; - down(¤t->mm->mmap_sem); - lock_kernel(); + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { - error = -EBADF; file = fget(fd); if (!file) goto out; } - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - error = do_mmap(file, addr, len, prot, flags, off); - if (file) - fput(file); -out: + down(¤t->mm->mmap_sem); + lock_kernel(); + + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); unlock_kernel(); up(¤t->mm->mmap_sem); + if (file) + fput(file); +out: return error; } +asmlinkage int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + int fd, unsigned long off) +{ + if (off & ~PAGE_MASK) + return -EINVAL; + return do_mmap2(addr, len, prot, flags, fd, off>>PAGE_SHIFT); +} + +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + /* * sys_ipc() is the de-multiplexer for the SysV IPC calls.. * * This is really horribly ugly. */ -asmlinkage int sys_ipc (uint call, int first, int second, - int third, void *ptr, long fifth) +asmlinkage int sys_ipc(uint call, int first, int second, + int third, void *ptr, long fifth) { int version, ret; diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index 49a765f83bac..fad3a81458f7 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.7 1999/11/06 02:00:37 gniibe Exp $ +/* $Id: time.c,v 1.20 2000/02/28 12:42:51 gniibe Exp $ * * linux/arch/sh/kernel/time.c * @@ -8,8 +8,6 @@ * Copyright (C) 1991, 1992, 1995 Linus Torvalds */ -#include - #include #include #include @@ -43,10 +41,10 @@ #define TMU0_TCNT 0xfffffe98 /* Long access */ #define TMU0_TCR 0xfffffe9c /* Word access */ -#define INTERVAL 37500 /* (1000000*CLOCK_MHZ/HZ/2) ??? for CqREEK */ -#if 0 /* Takeshi's board */ -#define INTERVAL 83333 -#endif +#define FRQCR 0xffffff80 + +#define RTC_IRQ 22 +#define RTC_IPR_OFFSET 0 /* SH-3 RTC */ #define R64CNT 0xfffffec0 @@ -74,7 +72,10 @@ #define TMU0_TCNT 0xffd8000c /* Long access */ #define TMU0_TCR 0xffd80010 /* Word access */ -#define INTERVAL 83333 +#define FRQCR 0xffc00000 + +#define RTC_IRQ 22 +#define RTC_IPR_OFFSET 0 /* SH-4 RTC */ #define R64CNT 0xffc80000 @@ -145,11 +146,10 @@ void do_settimeofday(struct timeval *tv) static int set_rtc_time(unsigned long nowtime) { -#ifdef CONFIG_SH_CPU_RTC int retval = 0; int real_seconds, real_minutes, cmos_minutes; - ctrl_outb(2, RCR2); /* reset pre-scaler & stop RTC */ + ctrl_outb(0x02, RCR2); /* reset pre-scaler & stop RTC */ cmos_minutes = ctrl_inb(RMINCNT); BCD_TO_BIN(cmos_minutes); @@ -178,13 +178,9 @@ static int set_rtc_time(unsigned long nowtime) retval = -1; } - ctrl_outb(2, RCR2); /* start RTC */ + ctrl_outb(0x01, RCR2); /* start RTC */ return retval; -#else - /* XXX should support other clock devices? */ - return -1; -#endif } /* last time the RTC clock got updated */ @@ -197,7 +193,6 @@ static long last_rtc_update = 0; static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { do_timer(regs); - #ifdef TAKESHI { unsigned long what_is_this=0xa4000124; @@ -248,9 +243,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) * locally disabled. -arca */ write_lock(&xtime_lock); - do_timer_interrupt(irq, NULL, regs); - write_unlock(&xtime_lock); } @@ -287,11 +280,10 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon, static unsigned long get_rtc_time(void) { -#ifdef CONFIG_SH_CPU_RTC unsigned int sec, min, hr, wk, day, mon, yr, yr100; again: - ctrl_outb(1, RCR1); /* clear CF bit */ + ctrl_outb(0x01, RCR1); /* clear CF bit */ do { sec = ctrl_inb(RSECCNT); min = ctrl_inb(RMINCNT); @@ -321,7 +313,7 @@ static unsigned long get_rtc_time(void) hr > 23 || min > 59 || sec > 59) { printk(KERN_ERR "SH RTC: invalid value, resetting to 1 Jan 2000\n"); - ctrl_outb(2, RCR2); /* reset, stop */ + ctrl_outb(0x02, RCR2); /* reset, stop */ ctrl_outb(0, RSECCNT); ctrl_outb(0, RMINCNT); ctrl_outb(0, RHRCNT); @@ -333,36 +325,114 @@ static unsigned long get_rtc_time(void) #else ctrl_outb(0, RYRCNT); #endif - ctrl_outb(1, RCR2); /* start */ + ctrl_outb(0x01, RCR2); /* start */ goto again; } return mktime(yr100 * 100 + yr, mon, day, hr, min, sec); +} + +static __init unsigned int get_cpu_mhz(void) +{ + unsigned int count; + unsigned long __dummy; + + sti(); + do {} while (ctrl_inb(R64CNT) != 0); + ctrl_outb(0x11, RCR1); + asm volatile( + "1:\t" + "tst %1,%1\n\t" + "bt/s 1b\n\t" + " add #1,%0" + : "=&r"(count), "=&z" (__dummy) + : "0" (0), "1" (0)); + cli(); + /* + * SH-3: + * CPU clock = 4 stages * loop + * tst rm,rm if id ex + * bt/s 1b if id ex + * add #1,rd if id ex + * (if) pipe line stole + * tst rm,rm if id ex + * .... + * + * + * SH-4: + * CPU clock = 6 stages * loop + * I don't know why. + * .... + */ +#if defined(__SH4__) + return count*6; #else - /* XXX should support other clock devices? */ - return 0; + return count*4; #endif } +static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ctrl_outb(0x01, RCR1); + regs->regs[0] = 1; +} + static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; +static struct irqaction irq1 = { rtc_interrupt, SA_INTERRUPT, 0, "rtc", NULL, NULL}; void __init time_init(void) { + unsigned int cpu_clock, master_clock, module_clock; + unsigned short ifc, pfc; + unsigned long interval; +#if defined(__sh3__) + static int ifc_table[] = { 1, 2, 4, 1, 3, 1, 1, 1 }; + static int pfc_table[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; +#elif defined(__SH4__) + static int ifc_table[] = { 1, 2, 3, 4, 6, 8, 1, 1 }; + static int pfc_table[] = { 2, 3, 4, 6, 8, 2, 2, 2 }; +#endif + xtime.tv_sec = get_rtc_time(); xtime.tv_usec = 0; - set_ipr_data(TIMER_IRQ, TIMER_IRP_OFFSET, TIMER_PRIORITY); + set_ipr_data(TIMER_IRQ, TIMER_IPR_OFFSET, TIMER_PRIORITY); setup_irq(TIMER_IRQ, &irq0); + set_ipr_data(RTC_IRQ, RTC_IPR_OFFSET, TIMER_PRIORITY); + setup_irq(RTC_IRQ, &irq1); - /* Start TMU0 */ - ctrl_outb(TMU_TOCR_INIT,TMU_TOCR); - ctrl_outw(TMU0_TCR_INIT,TMU0_TCR); - ctrl_outl(INTERVAL,TMU0_TCOR); - ctrl_outl(INTERVAL,TMU0_TCNT); - ctrl_outb(TMU_TSTR_INIT,TMU_TSTR); + /* Check how fast it is.. */ + cpu_clock = get_cpu_mhz(); + disable_irq(RTC_IRQ); -#if 0 - /* Start RTC */ - asm volatile(""); + printk("CPU clock: %d.%02dMHz\n", + (cpu_clock / 1000000), (cpu_clock % 1000000)/10000); +#if defined(__sh3__) + { + unsigned short tmp; + tmp = (ctrl_inw(FRQCR) & 0x000c) >> 2; + tmp |= (ctrl_inw(FRQCR) & 0x4000) >> 12; + ifc = ifc_table[tmp & 0x0007]; + tmp = ctrl_inw(FRQCR) & 0x0003; + tmp |= (ctrl_inw(FRQCR) & 0x2000) >> 11; + pfc = pfc_table[ctrl_inw(FRQCR) & 0x0007]; + } +#elif defined(__SH4__) + ifc = ifc_table[(ctrl_inw(FRQCR)>> 6) & 0x0007]; + pfc = pfc_table[ctrl_inw(FRQCR) & 0x0007]; #endif + master_clock = cpu_clock * ifc; + module_clock = master_clock/pfc; + printk("Module clock: %d.%02dMHz\n", + (module_clock/1000000), (module_clock % 1000000)/10000); + interval = (module_clock/400); + + printk("Interval = %ld\n", interval); + + /* Start TMU0 */ + ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); + ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); + ctrl_outl(interval, TMU0_TCOR); + ctrl_outl(interval, TMU0_TCNT); + ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); } diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 8a9b3e1f9b87..98431cb367b8 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.3 1999/09/21 14:37:19 gniibe Exp $ +/* $Id: traps.c,v 1.5 2000/02/27 08:27:55 gniibe Exp $ * * linux/arch/sh/traps.c * @@ -26,6 +26,7 @@ #include #include #include +#include static inline void console_verbose(void) { @@ -40,7 +41,7 @@ asmlinkage void do_##name(unsigned long r4, unsigned long r5, \ { \ unsigned long error_code; \ \ - asm volatile("stc r2_bank,%0": "=r" (error_code)); \ + asm volatile("stc $r2_bank, %0": "=r" (error_code)); \ sti(); \ regs.syscall_nr = -1; \ tsk->thread.error_code = error_code; \ @@ -99,7 +100,7 @@ asmlinkage void do_exception_error (unsigned long r4, unsigned long r5, struct pt_regs regs) { long ex; - asm volatile("stc r2_bank,%0" : "=r" (ex)); + asm volatile("stc $r2_bank, %0" : "=r" (ex)); die_if_kernel("exception", ®s, ex); } @@ -117,8 +118,22 @@ void __init trap_init(void) (or P2, virtural "fixed" address space). It's definitely should not in physical address. */ - asm volatile("ldc %0,vbr" + asm volatile("ldc %0, $vbr" : /* no output */ : "r" (&vbr_base) : "memory"); } + +void dump_stack(void) +{ + unsigned long *start; + unsigned long *end; + unsigned long *p; + + asm("mov $r15, %0" : "=r" (start)); + asm("stc $r4_bank, %0" : "=r" (end)); + + printk("%08lx:%08lx\n", (unsigned long)start, (unsigned long)end); + for (p=start; p < end; p++) + printk("%08lx\n", *p); +} diff --git a/arch/sh/mm/cache.c b/arch/sh/mm/cache.c index d15c62385c87..f5c5200bed6d 100644 --- a/arch/sh/mm/cache.c +++ b/arch/sh/mm/cache.c @@ -1,4 +1,4 @@ -/* $Id: cache.c,v 1.7 1999/09/23 11:43:07 gniibe Exp $ +/* $Id: cache.c,v 1.9 2000/02/14 12:45:26 gniibe Exp $ * * linux/arch/sh/mm/cache.c * @@ -283,7 +283,30 @@ void __init cache_init(void) } #if defined(__SH4__) -/* Write back data caches, and invalidates instructiin caches */ +void flush_icache_page(struct vm_area_struct *vma, struct page *pg) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v; + + save_and_cli(flags); + jump_to_p2(__dummy); + + v = page_address(pg); + + /* Write back O Cache */ + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); + /* Invalidate I Cache */ + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* Valid=0 */ + ctrl_outl(data,addr); + + back_to_p1(__dummy); + restore_flags(flags); +} + void flush_icache_range(unsigned long start, unsigned long end) { unsigned long flags, __dummy; @@ -358,7 +381,7 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long addr) flush_cache_range(vma->vm_mm, addr, addr+PAGE_SIZE); } -void flush_page_to_ram(unsigned long page) +void __flush_page_to_ram(unsigned long page) { /* Page is in physical address */ /* XXX: for the time being... */ flush_cache_all(); diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 7f3610a3b22b..3b8e86e36fb7 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.5 1999/10/31 13:17:31 gniibe Exp $ +/* $Id: fault.c,v 1.12 2000/03/01 11:15:27 gniibe Exp $ * * linux/arch/sh/mm/fault.c * Copyright (C) 1999 Niibe Yutaka @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -211,10 +211,12 @@ no_context: printk(KERN_ALERT "Unable to handle kernel paging request"); printk(" at virtual address %08lx\n",address); printk(KERN_ALERT "pc = %08lx\n", regs->pc); - page = (unsigned long)mm->pgd; - page = ((unsigned long *) __va(page))[address >> 22]; + asm volatile("mov.l %1,%0" + : "=r" (page) + : "m" (__m(MMU_TTB))); + page = ((unsigned long *) page)[address >> 22]; printk(KERN_ALERT "*pde = %08lx\n", page); - if (page & 1) { + if (page & _PAGE_PRESENT) { page &= PAGE_MASK; address &= 0x003ff000; page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; @@ -256,6 +258,7 @@ void update_mmu_cache(struct vm_area_struct * vma, { unsigned long flags; unsigned long pteval; + unsigned long pteaddr; save_and_cli(flags); /* @@ -267,6 +270,9 @@ void update_mmu_cache(struct vm_area_struct * vma, pteval |= _PAGE_FLAGS_HARDWARE_DEFAULT; /* add default flags */ /* Set PTEL register */ ctrl_outl(pteval, MMU_PTEL); + /* Set PTEH register */ + pteaddr = (address & MMU_VPN_MASK) | (vma->vm_mm->context & MMU_CONTEXT_ASID_MASK); + ctrl_outl(pteaddr, MMU_PTEH); /* Load the TLB */ asm volatile("ldtlb": /* no output */ : /* no input */ : "memory"); @@ -277,6 +283,9 @@ static void __flush_tlb_page(struct mm_struct *mm, unsigned long page) { unsigned long addr, data, asid; unsigned long saved_asid = MMU_NO_ASID; +#if defined(__SH4__) + int i; +#endif if (mm->context == NO_CONTEXT) return; @@ -296,8 +305,6 @@ static void __flush_tlb_page(struct mm_struct *mm, unsigned long page) data = (page & 0xfffe0000) | asid; /* VALID bit is off */ ctrl_outl(data, addr); #elif defined(__SH4__) - int i; - addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT; data = page | asid; /* VALID bit is off */ ctrl_outl(data, addr); @@ -305,7 +312,7 @@ static void __flush_tlb_page(struct mm_struct *mm, unsigned long page) for (i=0; i<4; i++) { addr = MMU_ITLB_ADDRESS_ARRAY | (i<<8); data = ctrl_inl(addr); - data &= ~0x30; + data &= ~0x300; if (data == (page | asid)) { ctrl_outl(data, addr); break; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 458685f4afa9..776389cac44a 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.4 1999/10/23 01:37:02 gniibe Exp gniibe $ +/* $Id: init.c,v 1.16 2000/02/14 15:19:05 gniibe Exp $ * * linux/arch/sh/mm/init.c * @@ -24,12 +24,14 @@ #ifdef CONFIG_BLK_DEV_INITRD #include #endif +#include #include #include #include #include #include +#include #include #include @@ -77,13 +79,13 @@ static pte_t * get_bad_pte_table(void) void __handle_bad_pmd(pmd_t *pmd) { pmd_ERROR(*pmd); - pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table()); + set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table()))); } void __handle_bad_pmd_kernel(pmd_t *pmd) { pmd_ERROR(*pmd); - pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table()); + set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table()))); } pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) @@ -94,10 +96,10 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) if (pmd_none(*pmd)) { if (pte) { clear_page(pte); - pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte); + set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte))); return pte + offset; } - pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table()); + set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table()))); return NULL; } free_page((unsigned long)pte); @@ -116,10 +118,10 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) if (pmd_none(*pmd)) { if (pte) { clear_page((void *)pte); - pmd_val(*pmd) = _PAGE_TABLE + __pa(pte); + set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))); return (pte_t *)pte + offset; } - pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table()); + set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(get_bad_pte_table()))); return NULL; } free_page(pte); @@ -133,15 +135,15 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) int do_check_pgt_cache(int low, int high) { int freed = 0; - if(pgtable_cache_size > high) { + if (pgtable_cache_size > high) { do { - if(pgd_quicklist) + if (pgd_quicklist) free_pgd_slow(get_pgd_fast()), freed++; - if(pmd_quicklist) + if (pmd_quicklist) free_pmd_slow(get_pmd_fast()), freed++; - if(pte_quicklist) + if (pte_quicklist) free_pte_slow(get_pte_fast()), freed++; - } while(pgtable_cache_size > low); + } while (pgtable_cache_size > low); } return freed; } @@ -181,6 +183,10 @@ extern char __init_begin, __init_end; pgd_t swapper_pg_dir[1024]; +/* It'd be good if these lines were in the standard header file. */ +#define START_PFN (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN (NODE_DATA(0)->bdata->node_low_pfn) + /* * paging_init() sets up the page tables * @@ -204,32 +210,55 @@ void __init paging_init(void) mmu_context_cache = MMU_CONTEXT_FIRST_VERSION; set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK); - free_area_init(max_low_pfn); + { + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long max_dma, low, start_pfn; + + start_pfn = START_PFN; + max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; + low = MAX_LOW_PFN; + + if (low < max_dma) + zones_size[ZONE_DMA] = low - start_pfn; + else { + zones_size[ZONE_DMA] = max_dma - start_pfn; + zones_size[ZONE_NORMAL] = low - max_dma; + } + free_area_init_node(0, 0, zones_size, __MEMORY_START); + } } void __init mem_init(void) { - int codepages = 0; - int reservedpages = 0; - int datapages = 0; - int initpages = 0; + int codesize, reservedpages, datasize, initsize; + int tmp; - max_mapnr = num_physpages = max_low_pfn; - high_memory = (void *) ((unsigned long)__va(max_low_pfn * PAGE_SIZE)+__MEMORY_START); + max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; + high_memory = (void *)__va(MAX_LOW_PFN * PAGE_SIZE); /* clear the zero-page */ memset(empty_zero_page, 0, PAGE_SIZE); /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem(); + reservedpages = 0; + for (tmp = 0; tmp < num_physpages; tmp++) + /* + * Only count reserved RAM pages + */ + if (PageReserved(mem_map+tmp)) + reservedpages++; + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", - (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), max_mapnr << (PAGE_SHIFT-10), - codepages << (PAGE_SHIFT-10), + codesize >> 10, reservedpages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10), - initpages << (PAGE_SHIFT-10)); + datasize >> 10, + initsize >> 10); } void free_initmem(void) @@ -246,14 +275,28 @@ void free_initmem(void) printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); } +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + unsigned long p; + for (p = start; p < end; p += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(p)); + set_page_count(mem_map+MAP_NR(p), 1); + free_page(p); + totalram_pages++; + } + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +} +#endif + void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; val->sharedram = 0; - val->freeram = nr_free_pages; + val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); val->totalhigh = totalhigh_pages; - val->freehigh = nr_free_highpages; + val->freehigh = nr_free_highpages(); val->mem_unit = PAGE_SIZE; return; } diff --git a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap.c index c12b97d185b0..2dd1e6ec2aad 100644 --- a/arch/sh/mm/ioremap.c +++ b/arch/sh/mm/ioremap.c @@ -1,4 +1,4 @@ -/* $Id: ioremap.c,v 1.1 1999/09/18 16:57:48 gniibe Exp $ +/* $Id: ioremap.c,v 1.2 1999/11/25 14:00:28 gniibe Exp $ * * arch/sh/mm/ioremap.c * @@ -11,6 +11,7 @@ #include #include +#include static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, unsigned long phys_addr, unsigned long flags) diff --git a/arch/sh/vmlinux.lds.S b/arch/sh/vmlinux.lds.S index 688c4c9c4b92..53a7fff56c19 100644 --- a/arch/sh/vmlinux.lds.S +++ b/arch/sh/vmlinux.lds.S @@ -1,4 +1,4 @@ -/* $Id: vmlinux.lds.S,v 1.3 1999/10/05 12:33:48 gniibe Exp $ +/* $Id: vmlinux.lds.S,v 1.4 1999/12/23 11:37:45 gniibe Exp $ * ld script to make SuperH Linux kernel * Written by Niibe Yutaka */ @@ -13,8 +13,8 @@ ENTRY(_start) SECTIONS { . = 0x80000000 + CONFIG_MEMORY_START + 0x1000; - __text = .; /* Text and read-only data */ _text = .; /* Text and read-only data */ + text = .; /* Text and read-only data */ .text : { *(.empty_zero_page) *(.text) @@ -26,41 +26,41 @@ SECTIONS .kstrtab : { *(.kstrtab) } . = ALIGN(16); /* Exception table */ - ___start___ex_table = .; - ___ex_table : { *(__ex_table) } - ___stop___ex_table = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; - ___start___ksymtab = .; /* Kernel symbol table */ - ___ksymtab : { *(__ksymtab) } - ___stop___ksymtab = .; + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; - __etext = .; /* End of text section */ + _etext = .; /* End of text section */ .data : { /* Data */ *(.data) CONSTRUCTORS } - __edata = .; /* End of data section */ + _edata = .; /* End of data section */ . = ALIGN(8192); /* init_task */ .data.init_task : { *(.data.init_task) } /* stack */ - .stack : { _stack = .; __stack = .; } + .stack : { stack = .; _stack = .; } . = ALIGN(4096); /* Init code and data */ - ___init_begin = .; + __init_begin = .; .text.init : { *(.text.init) } .data.init : { *(.data.init) } . = ALIGN(16); - ___setup_start = .; + __setup_start = .; .setup.init : { *(.setup.init) } - ___setup_end = .; - ___initcall_start = .; + __setup_end = .; + __initcall_start = .; .initcall.init : { *(.initcall.init) } - ___initcall_end = .; + __initcall_end = .; . = ALIGN(4096); - ___init_end = .; + __init_end = .; . = ALIGN(4096); .data.page_aligned : { *(.data.idt) } @@ -69,12 +69,12 @@ SECTIONS .data.cacheline_aligned : { *(.data.cacheline_aligned) } . = ALIGN(4); - ___bss_start = .; /* BSS */ + __bss_start = .; /* BSS */ .bss : { *(.bss) } . = ALIGN(4); - __end = . ; + _end = . ; /* Stabs debugging sections. */ .stab 0 : { *(.stab) } diff --git a/drivers/char/Makefile b/drivers/char/Makefile index ddfe79a754d2..4eae5919654a 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -61,6 +61,20 @@ ifeq ($(ARCH),arm) SERIAL = endif +ifeq ($(ARCH),sh) + KEYMAP = + KEYBD = + CONSOLE = + SERIAL = + ifeq ($(CONFIG_SERIAL),y) + SERIAL = generic_serial.o sh-sci.o + else + ifeq ($(CONFIG_SERIAL),m) + SERIAL = sh-sci.o + endif + endif +endif + ifeq ($(CONFIG_DECSTATION),y) KEYBD = SERIAL = diff --git a/drivers/char/sh-sci.c b/drivers/char/sh-sci.c new file mode 100644 index 000000000000..827be953a491 --- /dev/null +++ b/drivers/char/sh-sci.c @@ -0,0 +1,992 @@ +/* $Id: sh-sci.c,v 1.32 2000-03-05 13:56:18+09 gniibe Exp $ + * + * linux/drivers/char/sh-sci.c + * + * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) + * Copyright (C) 1999, 2000 Niibe Yutaka + * + * TTY code is based on sx.c (Specialix SX driver) by: + * + * (C) 1998 R.E.Wolff@BitWizard.nl + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "generic_serial.h" +#include "sh-sci.h" + +#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB +static void gdb_detach(void); +#endif + +struct sci_port sci_ports[1]; + +/* Function prototypes */ +static void sci_disable_tx_interrupts(void *ptr); +static void sci_enable_tx_interrupts(void *ptr); +static void sci_disable_rx_interrupts(void *ptr); +static void sci_enable_rx_interrupts(void *ptr); +static int sci_get_CD(void *ptr); +static void sci_shutdown_port(void *ptr); +static void sci_set_real_termios(void *ptr); +static void sci_hungup(void *ptr); +static void sci_close(void *ptr); +static int sci_chars_in_buffer(void *ptr); +static int sci_init_drivers(void); + +static struct tty_driver sci_driver, sci_callout_driver; + +#define SCI_NPORTS 1 +static struct tty_struct *sci_table[SCI_NPORTS] = { NULL, }; +static struct termios *sci_termios[2]; /* nomal, locked */ + +int sci_refcount; +int sci_debug = 0; + +#ifdef MODULE +MODULE_PARM(sci_debug, "i"); +#endif + +static struct real_driver sci_real_driver = { + sci_disable_tx_interrupts, + sci_enable_tx_interrupts, + sci_disable_rx_interrupts, + sci_enable_rx_interrupts, + sci_get_CD, + sci_shutdown_port, + sci_set_real_termios, + sci_chars_in_buffer, + sci_close, + sci_hungup, + NULL +}; + +static void sci_setsignals(struct sci_port *port, int dtr, int rts) +{ + /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ + /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ + /* If you have signals for DTR and DCD, please implement here. */ + ; +} + +static int sci_getsignals(struct sci_port *port) +{ + /* This routine is used for geting signals of: DTR, DCD, DSR, RI, + and CTS/RTS */ + + return TIOCM_DTR|TIOCM_RTS|TIOCM_DSR; +/* + (((o_stat & OP_DTR)?TIOCM_DTR:0) | + ((o_stat & OP_RTS)?TIOCM_RTS:0) | + ((i_stat & IP_CTS)?TIOCM_CTS:0) | + ((i_stat & IP_DCD)?TIOCM_CAR:0) | + ((i_stat & IP_DSR)?TIOCM_DSR:0) | + ((i_stat & IP_RI) ?TIOCM_RNG:0) +*/ +} + +static void sci_set_baud(struct sci_port *port) +{ + int t; + + switch (port->gs.baud) { + case 0: + t = -1; + break; + case 2400: + t = BPS_2400; + break; + case 4800: + t = BPS_4800; + break; + case 9600: + t = BPS_9600; + break; + case 19200: + t = BPS_19200; + break; + case 38400: + t = BPS_38400; + break; + default: + printk(KERN_INFO "sci: unsupported baud rate: %d, use 115200 instead.\n", port->gs.baud); + case 115200: + t = BPS_115200; + break; + } + + if (t > 0) { + sci_setsignals (port, 1, -1); + ctrl_outb(t, SCBRR); + ctrl_outw(0xa400, RFCR); /* Refresh counter clear */ + while (ctrl_inw(RFCR) < WAIT_RFCR_COUNTER) + ; + } else { + sci_setsignals (port, 0, -1); + } +} + +static void sci_set_termios_cflag(struct sci_port *port) +{ + unsigned short status; + unsigned short smr_val=0; +#if defined(CONFIG_SH_SCIF_SERIAL) + unsigned short fcr_val=6; /* TFRST=1, RFRST=1 */ +#endif + + do + status = ctrl_in(SC_SR); + while (!(status & SCI_TEND)); + + port->old_cflag = port->gs.tty->termios->c_cflag; + + ctrl_out(0x00, SCSCR); /* TE=0, RE=0, CKE1=0 */ +#if defined(CONFIG_SH_SCIF_SERIAL) + ctrl_out(fcr_val, SCFCR); + fcr_val = 0; +#endif + + if ((port->gs.tty->termios->c_cflag & CSIZE) == CS7) + smr_val |= 0x40; + if (C_PARENB(port->gs.tty)) + smr_val |= 0x20; + if (C_PARODD(port->gs.tty)) + smr_val |= 0x10; + if (C_CSTOPB(port->gs.tty)) + smr_val |= 0x08; + ctrl_out(smr_val, SCSMR); + +#if defined(CONFIG_SH_SCIF_SERIAL) + if (C_CRTSCTS(port->gs.tty)) + fcr_val |= 0x08; + ctrl_out(fcr_val, SCFCR); +#endif + + sci_set_baud(port); + ctrl_out(SCSCR_INIT, SCSCR); /* TIE=0,RIE=0,TE=1,RE=1 */ +#if 0 /* defined(CONFIG_SH_SCIF_SERIAL) */ + ctrl_outw(0x0080, SCSPTR); /* Set RTS = 1 */ +#endif + sci_enable_rx_interrupts(port); +} + +static void sci_set_real_termios(void *ptr) +{ + struct sci_port *port = ptr; + + if (port->old_cflag != port->gs.tty->termios->c_cflag) + sci_set_termios_cflag(port); + + /* Tell line discipline whether we will do input cooking */ + if (I_OTHER(port->gs.tty)) + clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + else + set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + +/* Tell line discipline whether we will do output cooking. + * If OPOST is set and no other output flags are set then we can do output + * processing. Even if only *one* other flag in the O_OTHER group is set + * we do cooking in software. + */ + if (O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty)) + set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + else + clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); +} + +/* ********************************************************************** * + * the interrupt related routines * + * ********************************************************************** */ + +static void sci_transmit_chars(struct sci_port *port) +{ + int count, i; + int txroom; + unsigned long flags; + unsigned short status; + unsigned short ctrl; + unsigned char c; + + status = ctrl_in(SC_SR); + if (!(status & SCI_TD_E)) { + save_and_cli(flags); + ctrl = ctrl_in(SCSCR); + if (port->gs.xmit_cnt == 0) { + ctrl &= ~SCI_CTRL_FLAGS_TIE; + port->gs.flags &= ~GS_TX_INTEN; + } else + ctrl |= SCI_CTRL_FLAGS_TIE; + ctrl_out(ctrl, SCSCR); + restore_flags(flags); + return; + } + + while (1) { + count = port->gs.xmit_cnt; +#if defined(CONFIG_SH_SCIF_SERIAL) + txroom = 16 - (ctrl_inw(SCFDR)>>8); +#else + txroom = (ctrl_in(SC_SR)&SCI_TD_E)?1:0; +#endif + if (count > txroom) + count = txroom; + + /* Don't copy pas the end of the source buffer */ + if (count > SERIAL_XMIT_SIZE - port->gs.xmit_tail) + count = SERIAL_XMIT_SIZE - port->gs.xmit_tail; + + /* If for one reason or another, we can't copy more data, we're done! */ + if (count == 0) + break; + + for (i=0; igs.xmit_buf[port->gs.xmit_tail + i]; + ctrl_outb(c, SC_TDR); + } + ctrl_out(SCI_TD_E_CLEAR, SC_SR); + + /* Update the kernel buffer end */ + port->gs.xmit_tail = (port->gs.xmit_tail + count) & (SERIAL_XMIT_SIZE-1); + + /* This one last. (this is essential) + It would allow others to start putting more data into the buffer! */ + port->gs.xmit_cnt -= count; + } + + if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { + if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + port->gs.tty->ldisc.write_wakeup) + (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + wake_up_interruptible(&port->gs.tty->write_wait); + } + + save_and_cli(flags); + ctrl = ctrl_in(SCSCR); + if (port->gs.xmit_cnt == 0) { + ctrl &= ~SCI_CTRL_FLAGS_TIE; + port->gs.flags &= ~GS_TX_INTEN; + } else { +#if defined(CONFIG_SH_SCIF_SERIAL) + ctrl_in(SC_SR); /* Dummy read */ + ctrl_out(SCI_TD_E_CLEAR, SC_SR); +#endif + ctrl |= SCI_CTRL_FLAGS_TIE; + } + ctrl_out(ctrl, SCSCR); + restore_flags(flags); +} + +static inline void sci_receive_chars(struct sci_port *port) +{ + int i, count; + struct tty_struct *tty; + int copied=0; + unsigned short status; + + status = ctrl_in(SC_SR); + if (!(status & SCI_RD_F)) + return; + + tty = port->gs.tty; + while (1) { +#if defined(CONFIG_SH_SCIF_SERIAL) + count = ctrl_inw(SCFDR)&0x001f; +#else + count = (ctrl_in(SC_SR)&SCI_RD_F)?1:0; +#endif + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + count > TTY_FLIPBUF_SIZE) + count = TTY_FLIPBUF_SIZE - tty->flip.count; + + /* If for one reason or another, we can't copy more data, we're done! */ + if (count == 0) + break; + + for (i=0; iflip.char_buf_ptr[i] = ctrl_inb(SC_RDR); + ctrl_in(SC_SR); /* dummy read */ + ctrl_out(SCI_RDRF_CLEAR, SC_SR); + + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, count); + + /* Update the kernel buffer end */ + tty->flip.count += count; + tty->flip.char_buf_ptr += count; + tty->flip.flag_buf_ptr += count; + + copied += count; + } + + if (copied) + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tty); +} + +static void sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + struct sci_port *port = ptr; + + if (port->gs.flags & GS_ACTIVE) + if (!(port->gs.flags & SCI_RX_THROTTLE)) + sci_receive_chars(port); +} + +static void sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + struct sci_port *port = ptr; + + if (port->gs.flags & GS_ACTIVE) + if (port->gs.xmit_cnt) { + sci_transmit_chars(port); + } +} + +static void sci_er_interrupt(int irq, void *ptr, struct pt_regs *regs) +{ + /* Handle errors */ + if (ctrl_in(SC_SR) & SCI_ERRORS) + ctrl_out(SCI_ERROR_CLEAR, SC_SR); + + /* Kick the transmission */ + sci_tx_interrupt(irq, ptr, regs); +} + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the generic_serial driver * + * ********************************************************************** */ + +static void sci_disable_tx_interrupts(void *ptr) +{ + unsigned long flags; + unsigned short ctrl; + + /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ + save_and_cli(flags); + ctrl = ctrl_in(SCSCR); + ctrl &= ~SCI_CTRL_FLAGS_TIE; + ctrl_out(ctrl, SCSCR); + restore_flags(flags); +} + +static void sci_enable_tx_interrupts(void *ptr) +{ + struct sci_port *port = ptr; + + disable_irq(SCI_TXI_IRQ); + sci_transmit_chars(port); + enable_irq(SCI_TXI_IRQ); +} + +static void sci_disable_rx_interrupts(void * ptr) +{ + unsigned long flags; + unsigned short ctrl; + + /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ + save_and_cli(flags); + ctrl = ctrl_in(SCSCR); + ctrl &= ~SCI_CTRL_FLAGS_RIE; + ctrl_out(ctrl, SCSCR); + restore_flags(flags); +} + +static void sci_enable_rx_interrupts(void * ptr) +{ + unsigned long flags; + unsigned short ctrl; + + /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ + save_and_cli(flags); + ctrl = ctrl_in(SCSCR); + ctrl |= SCI_CTRL_FLAGS_RIE; + ctrl_out(ctrl, SCSCR); + restore_flags(flags); +} + +static int sci_get_CD(void * ptr) +{ + /* If you have signal for CD (Carrier Detect), please change here. */ + return 1; +} + +static int sci_chars_in_buffer(void * ptr) +{ +#if defined(CONFIG_SH_SCIF_SERIAL) + return (ctrl_inw(SCFDR) >> 8) + ((ctrl_in(SC_SR) & SCI_TEND)? 0: 1); +#else + return (ctrl_in(SC_SR) & SCI_TEND)? 0: 1; +#endif +} + +static void sci_shutdown_port(void * ptr) +{ + struct sci_port *port = ptr; + + port->gs.flags &= ~ GS_ACTIVE; + if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) + sci_setsignals(port, 0, 0); +} + +/* ********************************************************************** * + * Here are the routines that actually * + * interface with the rest of the system * + * ********************************************************************** */ + +static int sci_open(struct tty_struct * tty, struct file * filp) +{ + struct sci_port *port; + int retval, line; + + line = MINOR(tty->device) - SCI_MINOR_START; + + if ((line < 0) || (line >= SCI_NPORTS)) + return -ENODEV; + + port = &sci_ports[line]; + + tty->driver_data = port; + port->gs.tty = tty; + port->gs.count++; + + /* + * Start up serial port + */ + retval = gs_init_port(&port->gs); + if (retval) { + port->gs.count--; + return retval; + } + + port->gs.flags |= GS_ACTIVE; + sci_setsignals(port, 1,1); + + if (port->gs.count == 1) { + MOD_INC_USE_COUNT; + } + + retval = block_til_ready(port, filp); + + if (retval) { + MOD_DEC_USE_COUNT; + port->gs.count--; + return retval; + } + + if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = port->gs.normal_termios; + else + *tty->termios = port->gs.callout_termios; + sci_set_real_termios(port); + } + + sci_enable_rx_interrupts(port); + + port->gs.session = current->session; + port->gs.pgrp = current->pgrp; + + return 0; +} + +static void sci_hungup(void *ptr) +{ + MOD_DEC_USE_COUNT; +} + +static void sci_close(void *ptr) +{ + MOD_DEC_USE_COUNT; +} + +static int sci_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + int rc; + struct sci_port *port = tty->driver_data; + int ival; + + rc = 0; + switch (cmd) { + case TIOCGSOFTCAR: + rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), + (unsigned int *) arg); + break; + case TIOCSSOFTCAR: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(int))) == 0) { + get_user(ival, (unsigned int *) arg); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (ival ? CLOCAL : 0); + } + break; + case TIOCGSERIAL: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct))) == 0) + gs_getserial(&port->gs, (struct serial_struct *) arg); + break; + case TIOCSSERIAL: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct))) == 0) + rc = gs_setserial(&port->gs, + (struct serial_struct *) arg); + break; + case TIOCMGET: + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int))) == 0) { + ival = sci_getsignals(port); + put_user(ival, (unsigned int *) arg); + } + break; + case TIOCMBIS: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { + get_user(ival, (unsigned int *) arg); + sci_setsignals(port, ((ival & TIOCM_DTR) ? 1 : -1), + ((ival & TIOCM_RTS) ? 1 : -1)); + } + break; + case TIOCMBIC: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { + get_user(ival, (unsigned int *) arg); + sci_setsignals(port, ((ival & TIOCM_DTR) ? 0 : -1), + ((ival & TIOCM_RTS) ? 0 : -1)); + } + break; + case TIOCMSET: + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { + get_user(ival, (unsigned int *)arg); + sci_setsignals(port, ((ival & TIOCM_DTR) ? 1 : 0), + ((ival & TIOCM_RTS) ? 1 : 0)); + } + break; + + default: + rc = -ENOIOCTLCMD; + break; + } + + return rc; +} + +static void sci_throttle(struct tty_struct * tty) +{ + struct sci_port *port = (struct sci_port *)tty->driver_data; + + /* If the port is using any type of input flow + * control then throttle the port. + */ + if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty)) ) + port->gs.flags |= SCI_RX_THROTTLE; +} + +static void sci_unthrottle(struct tty_struct * tty) +{ + struct sci_port *port = (struct sci_port *)tty->driver_data; + + /* Always unthrottle even if flow control is not enabled on + * this port in case we disabled flow control while the port + * was throttled + */ + port->gs.flags &= ~SCI_RX_THROTTLE; + return; +} + +/* ********************************************************************** * + * Here are the initialization routines. * + * ********************************************************************** */ + +static int sci_init_drivers(void) +{ + int error; + struct sci_port *port; + + memset(&sci_driver, 0, sizeof(sci_driver)); + sci_driver.magic = TTY_DRIVER_MAGIC; + sci_driver.driver_name = "serial"; + sci_driver.name = "ttyS"; + sci_driver.major = TTY_MAJOR; + sci_driver.minor_start = SCI_MINOR_START; + sci_driver.num = 1; + sci_driver.type = TTY_DRIVER_TYPE_SERIAL; + sci_driver.subtype = SERIAL_TYPE_NORMAL; + sci_driver.init_termios = tty_std_termios; + sci_driver.init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + sci_driver.flags = TTY_DRIVER_REAL_RAW; + sci_driver.refcount = &sci_refcount; + sci_driver.table = sci_table; + sci_driver.termios = &sci_termios[0]; + sci_driver.termios_locked = &sci_termios[1]; + sci_termios[0] = sci_termios[1] = NULL; + + sci_driver.open = sci_open; + sci_driver.close = gs_close; + sci_driver.write = gs_write; + sci_driver.put_char = gs_put_char; + sci_driver.flush_chars = gs_flush_chars; + sci_driver.write_room = gs_write_room; + sci_driver.chars_in_buffer = gs_chars_in_buffer; + sci_driver.flush_buffer = gs_flush_buffer; + sci_driver.ioctl = sci_ioctl; + sci_driver.throttle = sci_throttle; + sci_driver.unthrottle = sci_unthrottle; + sci_driver.set_termios = gs_set_termios; + sci_driver.stop = gs_stop; + sci_driver.start = gs_start; + sci_driver.hangup = gs_hangup; + + sci_callout_driver = sci_driver; + sci_callout_driver.name = "cua"; + sci_callout_driver.major = TTYAUX_MAJOR; + sci_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if ((error = tty_register_driver(&sci_driver))) { + printk(KERN_ERR "sci: Couldn't register SCI driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&sci_callout_driver))) { + tty_unregister_driver(&sci_driver); + printk(KERN_ERR "sci: Couldn't register SCI callout driver, error = %d\n", + error); + return 1; + } + + port = &sci_ports[0]; + port->gs.callout_termios = tty_std_termios; + port->gs.normal_termios = tty_std_termios; + port->gs.magic = SCI_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &sci_real_driver; + init_waitqueue_head(&port->gs.open_wait); + init_waitqueue_head(&port->gs.close_wait); + port->old_cflag = 0; + + return 0; +} + +#ifdef MODULE +#define sci_init init_module +#else +#define sci_init rs_init +#endif + +int __init sci_init(void) +{ + struct sci_port *port; + int i; + + for (i=SCI_ERI_IRQ; i> 4) & 0xf]; +} + +static char lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +static void gdb_detach(void) +{ + asm volatile("trapa #0xff"); + + if (in_gdb == 1) { + in_gdb = 0; + get_char(); + put_char('\r'); + put_char('\n'); + } +} +#endif + +/* send the packet in buffer. The host get's one chance to read it. + This routine does not wait for a positive acknowledge. */ + +static void +put_string(const char *buffer, int count) +{ + int i; + const unsigned char *p = buffer; +#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB + int checksum; + +if (in_gdb) { + /* $#. */ + do { + unsigned char c; + put_char('$'); + put_char('O'); /* 'O'utput to console */ + checksum = 'O'; + + for (i=0; iindex); +} + +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + int baud = 115200; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + char *s; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch (baud) { + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch (bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch (parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* XXX: set baud, char, and parity here. */ + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +/* + * Register console. + */ + +void __init serial_console_init(void) +{ + register_console(&sercons); +} +#endif /* CONFIG_SERIAL_CONSOLE */ diff --git a/drivers/char/sh-sci.h b/drivers/char/sh-sci.h new file mode 100644 index 000000000000..893d1a1e0d14 --- /dev/null +++ b/drivers/char/sh-sci.h @@ -0,0 +1,195 @@ +/* $Id: sh-sci.h,v 1.5 2000-03-05 13:54:32+09 gniibe Exp $ + * + * linux/drivers/char/sh-sci.h + * + * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) + * Copyright (C) 1999, 2000 Niibe Yutaka + * Copyright (C) 2000 Greg Banks + * + */ + +#if defined(CONFIG_SH_SCI_SERIAL) +#if defined(__sh3__) +#define SCSMR (volatile unsigned char *)0xfffffe80 +#define SCBRR 0xfffffe82 +#define SCSCR (volatile unsigned char *)0xfffffe84 +#define SC_TDR 0xfffffe86 +#define SC_SR (volatile unsigned char *)0xfffffe88 +#define SC_RDR 0xfffffe8a +#define SCSPTR 0xffffff7c + +#define SCSCR_INIT 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ + +#elif defined(__SH4__) +Not yet. +#endif + +#define SCI_TD_E 0x80 +#define SCI_RD_F 0x40 +#define SCI_ORER 0x20 +#define SCI_FER 0x10 +#define SCI_PER 0x08 +#define SCI_TEND 0x04 + +#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) +#define SCI_TD_E_CLEAR 0x78 +#define SCI_RDRF_CLEAR 0xbc +#define SCI_ERROR_CLEAR 0xc4 + +#define SCI_CTRL_FLAGS_TIE 0x80 +#define SCI_CTRL_FLAGS_RIE 0x40 +#define SCI_CTRL_FLAGS_TE 0x20 +#define SCI_CTRL_FLAGS_RE 0x10 +/* TEIE=0x04 */ +#define SCI_CTRL_FLAGS_CKE1 0x02 +#define SCI_CTRL_FLAGS_CKE0 0x01 + +#define RFCR 0xffffff74 + +#define SCI_ERI_IRQ 23 +#define SCI_RXI_IRQ 24 +#define SCI_TXI_IRQ 25 +#define SCI_TEI_IRQ 26 +#define SCI_IRQ_END 27 + +#define SCI_IPR_OFFSET (16+4) +#endif + +#if defined(CONFIG_SH_SCIF_SERIAL) +#if defined(__sh3__) +#define SCSMR (volatile unsigned char *)0xA4000150 +#define SCBRR 0xA4000152 +#define SCSCR (volatile unsigned char *)0xA4000154 +#define SC_TDR 0xA4000156 +#define SC_SR (volatile unsigned short *)0xA4000158 +#define SC_RDR 0xA400015A +#define SCFCR (volatile unsigned char *)0xA400015C +#define SCFDR 0xA400015E +#undef SCSPTR /* Is there any register for RTS?? */ +#undef SCLSR + +#define RFCR 0xffffff74 + +#define SCSCR_INIT 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ + /* 0x33 when external clock is used */ +#define SCI_IPR_OFFSET (64+4) + +#elif defined(__SH4__) +#define SCSMR (volatile unsigned short *)0xFFE80000 +#define SCBRR 0xFFE80004 +#define SCSCR (volatile unsigned short *)0xFFE80008 +#define SC_TDR 0xFFE8000C +#define SC_SR (volatile unsigned short *)0xFFE80010 +#define SC_RDR 0xFFE80014 +#define SCFCR (volatile unsigned short *)0xFFE80018 +#define SCFDR 0xFFE8001C +#define SCSPTR 0xFFE80020 +#define SCLSR 0xFFE80024 + +#define RFCR 0xFF800028 + +#define SCSCR_INIT 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#define SCI_IPR_OFFSET (32+4) + +#endif + +#define SCI_ER 0x0080 +#define SCI_TEND 0x0040 +#define SCI_TD_E 0x0020 +#define SCI_BRK 0x0010 +#define SCI_FER 0x0008 +#define SCI_PER 0x0004 +#define SCI_RD_F 0x0002 +#define SCI_DR 0x0001 + +#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ER | SCI_BRK) +#define SCI_TD_E_CLEAR 0x00df +#define SCI_TEND_CLEAR 0x00bf +#define SCI_RDRF_CLEAR 0x00fc +#define SCI_ERROR_CLEAR 0x0063 + +#define SCI_CTRL_FLAGS_TIE 0x80 +#define SCI_CTRL_FLAGS_RIE 0x40 +#define SCI_CTRL_FLAGS_TE 0x20 +#define SCI_CTRL_FLAGS_RE 0x10 +#define SCI_CTRL_FLAGS_REIE 0x08 +#define SCI_CTRL_FLAGS_CKE1 0x02 + +#if defined(__sh3__) +#define SCI_ERI_IRQ 56 +#define SCI_RXI_IRQ 57 +#define SCI_BRI_IRQ 58 +#define SCI_TXI_IRQ 59 +#define SCI_IRQ_END 60 +#elif defined(__SH4__) +#define SCI_ERI_IRQ 40 +#define SCI_RXI_IRQ 41 +#define SCI_BRI_IRQ 42 +#define SCI_TXI_IRQ 43 +#define SCI_IRQ_END 44 +#endif +#endif + +#define SCI_PRIORITY 3 + +#define SCI_MINOR_START 64 +#define SCI_RX_THROTTLE 0x0000001 + +#define O_OTHER(tty) \ + ((O_OLCUC(tty)) ||\ + (O_ONLCR(tty)) ||\ + (O_OCRNL(tty)) ||\ + (O_ONOCR(tty)) ||\ + (O_ONLRET(tty)) ||\ + (O_OFILL(tty)) ||\ + (O_OFDEL(tty)) ||\ + (O_NLDLY(tty)) ||\ + (O_CRDLY(tty)) ||\ + (O_TABDLY(tty)) ||\ + (O_BSDLY(tty)) ||\ + (O_VTDLY(tty)) ||\ + (O_FFDLY(tty))) + +#define I_OTHER(tty) \ + ((I_INLCR(tty)) ||\ + (I_IGNCR(tty)) ||\ + (I_ICRNL(tty)) ||\ + (I_IUCLC(tty)) ||\ + (L_ISIG(tty))) + +#define SCI_MAGIC 0xbabeface + +struct sci_port { + struct gs_port gs; + unsigned int old_cflag; +}; + +#define WAIT_RFCR_COUNTER 200 + +/* + * Values for the BitRate Register (SCBRR) + * + * The values are actually divisors for a frequency which can + * be internal to the SH3 (14.7456MHz) or derived from an external + * clock source. This driver assumes the internal clock is used; + * to support using an external clock source, config options or + * possibly command-line options would need to be added. + * + * Also, to support speeds below 2400 (why?) the lower 2 bits of + * the SCSMR register would also need to be set to non-zero values. + * + * -- Greg Banks 27Feb2000 + */ + +#if defined(__sh3__) +#define BPS_2400 191 +#define BPS_4800 95 +#define BPS_9600 47 +#define BPS_19200 23 +#define BPS_38400 11 +#define BPS_115200 3 +#elif defined(__SH4__) +/* Values for SH-4 please! */ + +#define BPS_115200 8 +#endif diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c index 8f3a6e1d8349..51468e1ef126 100644 --- a/drivers/pnp/isapnp.c +++ b/drivers/pnp/isapnp.c @@ -2040,7 +2040,7 @@ static int __init isapnp_do_reserve_irq(int irq) if (isapnp_reserve_irq[i] < 0) { isapnp_reserve_irq[i] = irq; #ifdef ISAPNP_DEBUG - printk("IRQ %i is reserved now.\n", irq); + printk("isapnp: IRQ %i is reserved now.\n", irq); #endif return 0; } @@ -2056,7 +2056,7 @@ static void __init isapnp_pci_init(void) pci_for_each_dev(dev) { #ifdef ISAPNP_DEBUG - printk("PCI: reserved IRQ: %i\n", dev->irq); + printk("isapnp: PCI: reserved IRQ: %i\n", dev->irq); #endif if (dev->irq > 0) isapnp_do_reserve_irq(dev->irq); diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 838241de2334..aeca7fc3cd18 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c @@ -45,7 +45,7 @@ static void __init quirk_awe32_resources(struct pci_dev *dev) port3->min += 0x800; port3->max += 0x800; } - printk(KERN_INFO "ISAPnP: AWE32 quirk - adding two ports\n"); + printk(KERN_INFO "isapnp: AWE32 quirk - adding two ports\n"); } @@ -71,7 +71,7 @@ void isapnp_fixup_device(struct pci_dev *dev) while (isapnp_fixups[i].vendor != 0) { if ((isapnp_fixups[i].vendor == dev->vendor) && (isapnp_fixups[i].device == dev->device)) { - printk(KERN_DEBUG "PnP: Calling quirk for %02x:%02x\n", + printk(KERN_DEBUG "isapnp: Calling quirk for %02x:%02x\n", dev->bus->number, dev->devfn); isapnp_fixups[i].quirk_function(dev); } diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index 49a55288f64b..cf43655e2bcf 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -394,6 +394,97 @@ static struct pci_dev *sb_init_cmi(struct pci_bus *bus, struct pci_dev *card, st return(sb_dev); } +static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) +{ + /* + * Diamonds DT0197H + * very similar to the CMI8330 above + */ + + /* @@@0001:Soundblaster. + */ + + if((sb_dev = isapnp_find_dev(bus, + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) + { + sb_dev->prepare(sb_dev); + + if((sb_dev = activate_dev("DT0197H", "sb", sb_dev))) + { + hw_config->io_base = sb_dev->resource[0].start; + hw_config->irq = sb_dev->irq_resource[0].start; + hw_config->dma = sb_dev->dma_resource[0].start; + hw_config->dma2 = -1; + + show_base("DT0197H", "sb", &sb_dev->resource[0]); + } + + if(!sb_dev) return(NULL); + + } + else + printk(KERN_ERR "sb: DT0197H panic: sb base not found\n"); + + /* @X@0001:mpu + */ + +#ifdef CONFIG_MIDI + if((mpu_dev = isapnp_find_dev(bus, + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) + { + mpu_dev->prepare(mpu_dev); + + if((mpu_dev = activate_dev("DT0197H", "mpu", mpu_dev))) + { + show_base("DT0197H", "mpu", &mpu_dev->resource[0]); + mpu_config->io_base = mpu_dev->resource[0].start; + } + } + else + printk(KERN_ERR "sb: DT0197H panic: mpu not found\n"); +#endif + + + /* @P@:Gameport + */ + + if((jp_dev = isapnp_find_dev(bus, + ISAPNP_VENDOR('@','P','@'), ISAPNP_FUNCTION(0x0001), NULL))) + { + jp_dev->prepare(jp_dev); + + if((jp_dev = activate_dev("DT0197H", "gameport", jp_dev))) + show_base("DT0197H", "gameport", &jp_dev->resource[0]); + } + else + printk(KERN_ERR "sb: DT0197H panic: gameport not found\n"); + + /* @H@0001:OPL3 + */ + +#if defined(CONFIG_SOUND_YM3812) || defined(CONFIG_SOUND_YM3812_MODULE) + if((wss_dev = isapnp_find_dev(bus, + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), NULL))) + { + wss_dev->prepare(wss_dev); + + /* Let's disable IRQ and DMA for WSS device */ + + wss_dev->irq_resource[0].flags = 0; + wss_dev->dma_resource[0].flags = 0; + + if((wss_dev = activate_dev("DT0197H", "opl3", wss_dev))) + show_base("DT0197H", "opl3", &wss_dev->resource[0]); + } + else + printk(KERN_ERR "sb: DT0197H panic: opl3 not found\n"); +#endif + + printk(KERN_INFO "sb: DT0197H mail reports to Torsten Werner \n"); + + return(sb_dev); +} + /* Specific support for awe will be dropped when: * a) The new awe_wawe driver with PnP support will be introduced in the kernel * b) The joystick driver will support PnP - a little patch is available from me....hint, hint :-) @@ -491,12 +582,14 @@ isapnp_sb_list[] __initdata = { {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x009D), 0, &sb_init_awe, "Sound Blaster AWE 64" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x00C5), 0, &sb_init_awe, "Sound Blaster AWE 64" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x00E4), 0, &sb_init_awe, "Sound Blaster AWE 64" }, + {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), SBF_DEV, &sb_init_ess, "ESS 1688" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), SBF_DEV, &sb_init_ess, "ESS 1868" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), SBF_DEV, &sb_init_ess, "ESS 1868" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), SBF_DEV, &sb_init_ess, "ESS 1869" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), SBF_DEV, &sb_init_ess, "ESS 1878" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), SBF_DEV, &sb_init_ess, "ESS 1879" }, {ISAPNP_VENDOR('C','M','I'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_cmi, "CMI 8330 SoundPRO" }, + {ISAPNP_VENDOR('R','W','B'), ISAPNP_FUNCTION(0x1688), 0, &sb_init_diamond, "Diamond DT0197H" }, {0} }; diff --git a/drivers/video/Config.in b/drivers/video/Config.in index 850755767953..70db39ddb49f 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -85,6 +85,7 @@ if [ "$CONFIG_FB" = "y" ]; then if [ "$ARCH" = "i386" ]; then bool ' VESA VGA graphics console' CONFIG_FB_VESA tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16 + tristate ' Hercules mono graphics console (EXPERIMENTAL)' CONFIG_FB_HGA define_bool CONFIG_VIDEO_SELECT y fi if [ "$CONFIG_VISWS" = "y" ]; then @@ -331,6 +332,13 @@ if [ "$CONFIG_FB" = "y" ]; then define_tristate CONFIG_FBCON_VGA_PLANES m fi fi + if [ "$CONFIG_FB_HGA" = "y" ]; then + define_tristate CONFIG_FBCON_HGA y + else + if [ "$CONFIG_FB_HGA" = "m" ]; then + define_tristate CONFIG_FBCON_HGA m + fi + fi fi bool ' Support only 8 pixels wide fonts' CONFIG_FBCON_FONTWIDTH8_ONLY if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0f8b53c3f411..c3d442e228be 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,7 +21,7 @@ export-objs := fbmem.o fbcmap.o fbcon.o fbcon-afb.o fbcon-ilbm.o \ fbcon-iplan2p8.o fbcon-vga-planes.o fbcon-cfb16.o \ fbcon-cfb2.o fbcon-cfb24.o fbcon-cfb32.o fbcon-cfb4.o \ fbcon-cfb8.o fbcon-mac.o fbcon-mfb.o fbcon-vga8-planes.o \ - matrox/matroxfb.o cyber2000fb.o + matrox/matroxfb.o cyber2000fb.o fbcon-hga.o # Object file lists. obj-y := @@ -85,9 +85,6 @@ obj-$(CONFIG_FB_TCX) += tcxfb.o sbusfb.o obj-$(CONFIG_FB_CGFOURTEEN) += cgfourteenfb.o sbusfb.o obj-$(CONFIG_FB_P9100) += p9100fb.o sbusfb.o obj-$(CONFIG_FB_LEO) += leofb.o sbusfb.o -obj-$(CONFIG_FB_SUN3) += sun3fb.o -obj-$(CONFIG_FB_BWTWO) += bwtwofb.o -obj-$(CONFIG_FB_VIRTUAL) += vfb.o ifeq ($(CONFIG_FB_MATROX),y) SUB_DIRS += matrox @@ -109,6 +106,11 @@ else endif endif +obj-$(CONFIG_FB_SUN3) += sun3fb.o +obj-$(CONFIG_FB_BWTWO) += bwtwofb.o +obj-$(CONFIG_FB_HGA) += hgafb.o +obj-$(CONFIG_FB_VIRTUAL) += vfb.o + # Generic Low Level Drivers obj-$(CONFIG_FBCON_AFB) += fbcon-afb.o @@ -126,6 +128,7 @@ obj-$(CONFIG_FBCON_IPLAN2P16) += fbcon-iplan2p16.o obj-$(CONFIG_FBCON_MAC) += fbcon-mac.o obj-$(CONFIG_FBCON_MFB) += fbcon-mfb.o obj-$(CONFIG_FBCON_VGA) += fbcon-vga.o +obj-$(CONFIG_FBCON_HGA) += fbcon-hga.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff --git a/drivers/video/fbcon-hga.c b/drivers/video/fbcon-hga.c new file mode 100644 index 000000000000..384e0a917da8 --- /dev/null +++ b/drivers/video/fbcon-hga.c @@ -0,0 +1,245 @@ +/* + * linux/drivers/video/fbcon-hga.c -- Low level frame buffer operations for + * the Hercules graphics adaptor + * + * Created 25 Nov 1999 by Ferenc Bakonyi (fero@drama.obuda.kando.hu) + * Based on fbcon-mfb.c by Geert Uytterhoeven + * + * History: + * + * - Revision 0.1.0 (6 Dec 1999): comment changes + * - First release (25 Nov 1999) + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include