From 743eee7ce010ee9086a51009d44b0533327c2383 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:18:54 -0500 Subject: [PATCH] Import 2.2.8pre6 --- CREDITS | 4 + Documentation/Configure.help | 29 +- Documentation/mtrr.txt | 17 + Documentation/networking/ip-sysctl.txt | 2 +- arch/alpha/config.in | 9 +- arch/alpha/kernel/entry.S | 546 +++++---- arch/alpha/kernel/osf_sys.c | 29 +- arch/alpha/kernel/process.c | 31 +- arch/alpha/kernel/smp.c | 25 +- arch/alpha/kernel/traps.c | 66 +- arch/alpha/math-emu/fp-emul.c | 91 +- arch/alpha/math-emu/ieee-math.c | 39 + arch/alpha/math-emu/ieee-math.h | 2 + arch/i386/kernel/i386_ksyms.c | 2 +- arch/i386/kernel/irq.c | 6 +- arch/i386/kernel/irq.h | 3 +- arch/i386/kernel/mtrr.c | 1072 +++++++++++------ arch/i386/kernel/setup.c | 24 +- arch/i386/kernel/smp.c | 98 +- drivers/Makefile | 6 +- drivers/char/lp.c | 14 +- drivers/char/mem.c | 8 +- drivers/char/planb.c | 1 - drivers/misc/parport_ieee1284.c | 13 +- drivers/misc/parport_pc.c | 83 +- drivers/net/ethertap.c | 1 + drivers/usb/Config.in | 4 + drivers/usb/Makefile | 15 +- drivers/usb/README.ohci_hcd | 112 ++ drivers/usb/ohci-hcd.c | 1488 ++++++++++++++++++++++++ drivers/usb/ohci-hcd.h | 402 +++++++ drivers/usb/ohci-root-hub.c | 604 ++++++++++ drivers/usb/ohci-root-hub.h | 71 ++ drivers/usb/usb.h | 2 +- drivers/video/vesafb.c | 2 - fs/nfs/dir.c | 25 +- fs/proc/array.c | 5 +- fs/super.c | 1 + include/asm-alpha/fpu.h | 12 + include/asm-alpha/pgtable.h | 13 - include/asm-alpha/system.h | 39 +- include/asm-arm/arch-ebsa285/irq.h | 1 + include/asm-arm/arch-ebsa285/memory.h | 2 + include/asm-i386/bugs.h | 23 + include/asm-i386/mtrr.h | 11 +- include/asm-i386/processor.h | 6 + include/linux/mc146818rtc.h | 149 +++ include/linux/parport_pc.h | 20 +- include/linux/smp.h | 19 +- include/net/tcp.h | 17 + kernel/sched.c | 3 +- net/core/iovec.c | 9 +- net/core/sock.c | 2 +- net/ipv4/devinet.c | 3 +- net/ipv4/route.c | 15 +- net/ipv4/tcp_input.c | 5 +- net/ipv4/tcp_ipv4.c | 4 +- net/ipv4/tcp_output.c | 31 +- net/ipv4/tcp_timer.c | 53 +- net/ipv4/udp.c | 4 +- net/ipv6/udp.c | 6 +- net/unix/af_unix.c | 9 +- 62 files changed, 4495 insertions(+), 913 deletions(-) create mode 100644 drivers/usb/README.ohci_hcd create mode 100644 drivers/usb/ohci-hcd.c create mode 100644 drivers/usb/ohci-hcd.h create mode 100644 drivers/usb/ohci-root-hub.c create mode 100644 drivers/usb/ohci-root-hub.h create mode 100644 include/linux/mc146818rtc.h diff --git a/CREDITS b/CREDITS index d3d62c49b25a..afaa94d0109b 100644 --- a/CREDITS +++ b/CREDITS @@ -256,6 +256,10 @@ W: http://math-www.uni-paderborn.de/~axel/ D: Configuration help text support D: Linux CD and Support Giveaway List +N: Zoltan Boszormenyi +E: zboszor@mol.hu +D: MTRR emulation with Cyrix style ARR registers + N: John Boyd E: boyd@cis.ohio-state.edu D: Co-author of wd7000 SCSI driver diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 4a5bb8af2ed6..90c4851fb729 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -8666,17 +8666,24 @@ CONFIG_FT_FDC_MAX_RATE MTRR control and configuration CONFIG_MTRR - On Intel Pentium Pro and Pentium II systems the Memory Type Range - Registers (MTRRs) may be used to control processor access to memory - ranges. This is most useful when you have a video (VGA) card on a - PCI or AGP bus. Enabling write-combining allows bus write transfers - to be combined into a larger transfer before bursting over the - PCI/AGP bus. This can increase performance of image write operations - 2.5 times or more. This option creates a /proc/mtrr file which may - be used to manipulate your MTRRs. Typically the X server should use - this. This should have a reasonably generic interface so that - similar control registers on other processors can be easily - supported. + On Intel P6 family processors (Pentium Pro, Pentium II and later) + the Memory Type Range Registers (MTRRs) may be used to control + processor access to memory ranges. This is most useful when you have + a video (VGA) card on a PCI or AGP bus. Enabling write-combining + allows bus write transfers to be combined into a larger transfer + before bursting over the PCI/AGP bus. This can increase performance + of image write operations 2.5 times or more. This option creates a + /proc/mtrr file which may be used to manipulate your + MTRRs. Typically the X server should use this. This should have a + reasonably generic interface so that similar control registers on + other processors can be easily supported. + + The Cyrix 6x86, 6x86MX and M II processors have Address Range + Registers (ARRs) which provide a similar functionality to MTRRs. For + these, the ARRs are used to emulate the MTRRs. + + The AMD K6-2 (stepping 8 and above) and K6-3 processors have two + MTRRs. These are supported. Saying Y here also fixes a problem with buggy SMP BIOSes which only set the MTRRs for the boot CPU and not the secondary CPUs. This can diff --git a/Documentation/mtrr.txt b/Documentation/mtrr.txt index 4b6264bcdfc1..af58d63d7854 100644 --- a/Documentation/mtrr.txt +++ b/Documentation/mtrr.txt @@ -62,6 +62,23 @@ in other words the X server will manipulate /proc/mtrr using the ioctl() interface, so users won't have to do anything. If you use a commercial X server, lobby your vendor to add support for MTRRs. =============================================================================== +Creating overlapping MTRRs: + +%echo "base=0xfb000000 size=0x1000000 type=write-combining" >/proc/mtrr +%echo "base=0xfb000000 size=0x1000 type=uncachable" >/proc/mtrr + +And the results: cat /proc/mtrr +reg00: base=0x00000000 ( 0MB), size= 64MB: write-back, count=1 +reg01: base=0xfb000000 (4016MB), size= 16MB: write-combining, count=1 +reg02: base=0xfb000000 (4016MB), size= 4kB: uncachable, count=1 + +Some cards (especially Voodoo Graphics boards) need this 4 kB area +excluded from the beginning of the region because it is used for +registers. + +NOTE: You can only create type=uncachable region, if the first +region that you created is type=write-combining. +=============================================================================== Removing MTRRs from the shell: % echo "disable=2" >! /proc/mtrr =============================================================================== diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 20d319029840..8def0e7fc2b7 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -211,4 +211,4 @@ kuznet@ms2.inr.ac.ru Updated by: Andi Kleen ak@muc.de -$Id: ip-sysctl.txt,v 1.8 1999/01/02 16:37:06 davem Exp $ +$Id: ip-sysctl.txt,v 1.9 1999/05/08 02:58:44 davem Exp $ diff --git a/arch/alpha/config.in b/arch/alpha/config.in index dfaacab354db..d632cdbb9919 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -54,12 +54,10 @@ unset CONFIG_PCI CONFIG_ALPHA_EISA unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS CONFIG_ALPHA_POLARIS unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA -unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION if [ "$CONFIG_ALPHA_GENERIC" = "y" ] then define_bool CONFIG_PCI y - define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y fi if [ "$CONFIG_ALPHA_BOOK1" = "y" ] then @@ -108,7 +106,7 @@ then then define_bool CONFIG_ALPHA_EV5 y else - define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_EV4 y fi define_bool CONFIG_ALPHA_T2 y fi @@ -141,11 +139,6 @@ if [ "$CONFIG_ALPHA_JENSEN" = "y" ] then define_bool CONFIG_ALPHA_EV4 y fi -if [ "$CONFIG_ALPHA_EV4" = "y" ] -then - # EV45 and older do not support all rounding modes in hw: - define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y -fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \ diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index 91d079d041d5..b4e71bf56cd6 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -6,8 +6,6 @@ #include -#define halt .long PAL_halt -#define rti .long PAL_rti #define SIGCHLD 20 #define NR_SYSCALLS 371 @@ -98,7 +96,7 @@ call_pal PAL_swpipl; \ stq $21,HAE_CACHE($19); \ stq $21,0($20); \ - bis $0,$0,$16; \ + mov $0,$16; \ call_pal PAL_swpipl; \ ldq $0,0($30); \ ldq $1,8($30); \ @@ -127,8 +125,8 @@ entInt: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entInt .end entInt @@ -170,8 +168,8 @@ entMM: entArith: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entArith .end entArith @@ -181,8 +179,8 @@ entArith: entIF: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entIF .end entIF @@ -192,8 +190,8 @@ entIF: entDbg: SAVE_ALL lda $8,0x3fff - bic $30,$8,$8 lda $26,ret_from_sys_call + bic $30,$8,$8 jsr $31,do_entDbg .end entDbg @@ -212,18 +210,18 @@ entDbg: kernel_clone: .frame $30, 0, $26 .prologue 0 - subq $30,6*8,$30 - stq $31,0($30) - stq $26,8($30) - stq $29,16($30) - stq $16,24($30) - stq $17,32($30) - stq $18,40($30) - bis $31,2,$0 /* Register v0: syscall nr for fork() */ + subq $30,6*8,$30 + stq $31,0($30) + stq $26,8($30) + stq $29,16($30) + stq $16,24($30) + stq $17,32($30) + stq $18,40($30) + bis $31,2,$0 /* Register v0: syscall nr for fork() */ SAVE_ALL - bsr $26,sys_clone - stq $0,0($30) - br $31,ret_from_sys_call + bsr $26,sys_clone + stq $0,0($30) + br ret_from_sys_call .end kernel_clone /* @@ -233,32 +231,32 @@ kernel_clone: .globl __kernel_thread .ent __kernel_thread __kernel_thread: - ldgp $29,0($27) /* we can be called from a module */ + ldgp $29,0($27) /* we can be called from a module */ .frame $30, 4*8, $26 - subq $30,4*8,$30 - stq $10,16($30) - stq $9,8($30) - stq $26,0($30) + subq $30,4*8,$30 + stq $10,16($30) + stq $9,8($30) + stq $26,0($30) .prologue 1 - bis $17,$17,$9 /* save fn */ - bis $18,$18,$10 /* save arg */ - bsr $26,kernel_clone - bne $20,1f /* $20 is non-zero in child */ - ldq $26,0($30) - ldq $9,8($30) - ldq $10,16($30) - addq $30,4*8,$30 - ret $31,($26),1 + mov $17,$9 /* save fn */ + mov $18,$10 /* save arg */ + bsr $26,kernel_clone + bne $20,1f /* $20 is non-zero in child */ + ldq $26,0($30) + ldq $9,8($30) + ldq $10,16($30) + addq $30,4*8,$30 + ret $31,($26),1 /* this is in child: look out as we don't have any stack here.. */ -1: bis $9,$9,$27 /* get fn */ - lda $8,0x3fff - bis $10,$10,$16 /* get arg */ - bic $30,$8,$8 /* get current */ - jsr $26,($27) +1: mov $9,$27 /* get fn */ + lda $8,0x3fff + mov $10,$16 /* get arg */ + bic $30,$8,$8 /* get current */ + jsr $26,($27) ldgp $29,0($26) - bis $0,$0,$16 - jsr $26,sys_exit - call_pal PAL_halt + mov $0,$16 + mov $31,$26 + jsr $31,sys_exit .end __kernel_thread /* @@ -286,202 +284,199 @@ __kernel_execve: .align 3 .ent do_switch_stack do_switch_stack: - lda $30,-SWITCH_STACK_SIZE($30) - stq $9,0($30) - stq $10,8($30) - stq $11,16($30) - stq $12,24($30) - stq $13,32($30) - stq $14,40($30) - stq $15,48($30) - stq $26,56($30) - stt $f0,64($30) - stt $f1,72($30) - stt $f2,80($30) - stt $f3,88($30) - stt $f4,96($30) - stt $f5,104($30) - stt $f6,112($30) - stt $f7,120($30) - stt $f8,128($30) - stt $f9,136($30) - stt $f10,144($30) - stt $f11,152($30) - stt $f12,160($30) - stt $f13,168($30) - stt $f14,176($30) - stt $f15,184($30) - stt $f16,192($30) - stt $f17,200($30) - stt $f18,208($30) - stt $f19,216($30) - stt $f20,224($30) - stt $f21,232($30) - stt $f22,240($30) - stt $f23,248($30) - stt $f24,256($30) - stt $f25,264($30) - stt $f26,272($30) - stt $f27,280($30) - mf_fpcr $f0 # get fpcr - stt $f28,288($30) - stt $f29,296($30) - stt $f30,304($30) - stt $f0,312($30) # save fpcr in slot of $f31 - ldt $f0,64($30) # dont let "do_switch_stack" change fp state. - ret $31,($1),1 + lda $30,-SWITCH_STACK_SIZE($30) + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + stq $26,56($30) + stt $f0,64($30) + stt $f1,72($30) + stt $f2,80($30) + stt $f3,88($30) + stt $f4,96($30) + stt $f5,104($30) + stt $f6,112($30) + stt $f7,120($30) + stt $f8,128($30) + stt $f9,136($30) + stt $f10,144($30) + stt $f11,152($30) + stt $f12,160($30) + stt $f13,168($30) + stt $f14,176($30) + stt $f15,184($30) + stt $f16,192($30) + stt $f17,200($30) + stt $f18,208($30) + stt $f19,216($30) + stt $f20,224($30) + stt $f21,232($30) + stt $f22,240($30) + stt $f23,248($30) + stt $f24,256($30) + stt $f25,264($30) + stt $f26,272($30) + stt $f27,280($30) + mf_fpcr $f0 # get fpcr + stt $f28,288($30) + stt $f29,296($30) + stt $f30,304($30) + stt $f0,312($30) # save fpcr in slot of $f31 + ldt $f0,64($30) # dont let "do_switch_stack" change fp state. + ret $31,($1),1 .end do_switch_stack .align 3 .ent undo_switch_stack undo_switch_stack: - ldq $9,0($30) - ldq $10,8($30) - ldq $11,16($30) - ldq $12,24($30) - ldq $13,32($30) - ldq $14,40($30) - ldq $15,48($30) - ldq $26,56($30) - ldt $f30,312($30) # get saved fpcr - ldt $f0,64($30) - ldt $f1,72($30) - ldt $f2,80($30) - ldt $f3,88($30) - mt_fpcr $f30 # install saved fpcr - ldt $f4,96($30) - ldt $f5,104($30) - ldt $f6,112($30) - ldt $f7,120($30) - ldt $f8,128($30) - ldt $f9,136($30) - ldt $f10,144($30) - ldt $f11,152($30) - ldt $f12,160($30) - ldt $f13,168($30) - ldt $f14,176($30) - ldt $f15,184($30) - ldt $f16,192($30) - ldt $f17,200($30) - ldt $f18,208($30) - ldt $f19,216($30) - ldt $f20,224($30) - ldt $f21,232($30) - ldt $f22,240($30) - ldt $f23,248($30) - ldt $f24,256($30) - ldt $f25,264($30) - ldt $f26,272($30) - ldt $f27,280($30) - ldt $f28,288($30) - ldt $f29,296($30) - ldt $f30,304($30) - lda $30,SWITCH_STACK_SIZE($30) - ret $31,($1),1 + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + ldq $26,56($30) + ldt $f30,312($30) # get saved fpcr + ldt $f0,64($30) + ldt $f1,72($30) + ldt $f2,80($30) + ldt $f3,88($30) + mt_fpcr $f30 # install saved fpcr + ldt $f4,96($30) + ldt $f5,104($30) + ldt $f6,112($30) + ldt $f7,120($30) + ldt $f8,128($30) + ldt $f9,136($30) + ldt $f10,144($30) + ldt $f11,152($30) + ldt $f12,160($30) + ldt $f13,168($30) + ldt $f14,176($30) + ldt $f15,184($30) + ldt $f16,192($30) + ldt $f17,200($30) + ldt $f18,208($30) + ldt $f19,216($30) + ldt $f20,224($30) + ldt $f21,232($30) + ldt $f22,240($30) + ldt $f23,248($30) + ldt $f24,256($30) + ldt $f25,264($30) + ldt $f26,272($30) + ldt $f27,280($30) + ldt $f28,288($30) + ldt $f29,296($30) + ldt $f30,304($30) + lda $30,SWITCH_STACK_SIZE($30) + ret $31,($1),1 .end undo_switch_stack .align 3 .globl entUna .ent entUna entUna: - lda $30,-256($30) - stq $0,0($30) - ldq $0,256($30) /* get PS */ - stq $1,8($30) - stq $2,16($30) - stq $3,24($30) - and $0,8,$0 /* user mode? */ - stq $4,32($30) - bne $0,entUnaUser /* yup -> do user-level unaligned fault */ - stq $5,40($30) - stq $6,48($30) - stq $7,56($30) - stq $8,64($30) - stq $9,72($30) - stq $10,80($30) - stq $11,88($30) - stq $12,96($30) - stq $13,104($30) - stq $14,112($30) - stq $15,120($30) + lda $30,-256($30) + stq $0,0($30) + ldq $0,256($30) /* get PS */ + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + and $0,8,$0 /* user mode? */ + stq $4,32($30) + bne $0,entUnaUser /* yup -> do user-level unaligned fault */ + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $9,72($30) + stq $10,80($30) + stq $11,88($30) + stq $12,96($30) + stq $13,104($30) + stq $14,112($30) + stq $15,120($30) /* 16-18 PAL-saved */ - stq $19,152($30) - stq $20,160($30) - stq $21,168($30) - stq $22,176($30) - stq $23,184($30) - stq $24,192($30) - stq $25,200($30) - stq $26,208($30) - stq $27,216($30) - stq $28,224($30) - stq $29,232($30) - stq $30,240($30) - stq $31,248($30) - lda $8,0x3fff - bic $30,$8,$8 - jsr $26,do_entUna - ldq $0,0($30) - ldq $1,8($30) - ldq $2,16($30) - ldq $3,24($30) - ldq $4,32($30) - ldq $5,40($30) - ldq $6,48($30) - ldq $7,56($30) - ldq $8,64($30) - ldq $9,72($30) - ldq $10,80($30) - ldq $11,88($30) - ldq $12,96($30) - ldq $13,104($30) - ldq $14,112($30) - ldq $15,120($30) + stq $19,152($30) + stq $20,160($30) + stq $21,168($30) + stq $22,176($30) + stq $23,184($30) + stq $24,192($30) + stq $25,200($30) + stq $26,208($30) + stq $27,216($30) + stq $28,224($30) + stq $29,232($30) + lda $8,0x3fff + stq $31,248($30) + bic $30,$8,$8 + jsr $26,do_entUna + ldq $0,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $9,72($30) + ldq $10,80($30) + ldq $11,88($30) + ldq $12,96($30) + ldq $13,104($30) + ldq $14,112($30) + ldq $15,120($30) /* 16-18 PAL-saved */ - ldq $19,152($30) - ldq $20,160($30) - ldq $21,168($30) - ldq $22,176($30) - ldq $23,184($30) - ldq $24,192($30) - ldq $25,200($30) - ldq $26,208($30) - ldq $27,216($30) - ldq $28,224($30) - ldq $29,232($30) - ldq $30,240($30) - lda $30,256($30) - rti + ldq $19,152($30) + ldq $20,160($30) + ldq $21,168($30) + ldq $22,176($30) + ldq $23,184($30) + ldq $24,192($30) + ldq $25,200($30) + ldq $26,208($30) + ldq $27,216($30) + ldq $28,224($30) + ldq $29,232($30) + lda $30,256($30) + call_pal PAL_rti .end entUna .align 3 .ent entUnaUser entUnaUser: - ldq $0,0($30) /* restore original $0 */ - lda $30,256($30) /* pop entUna's stack frame */ - SAVE_ALL /* setup normal kernel stack */ - lda $30,-56($30) - stq $9,0($30) - stq $10,8($30) - stq $11,16($30) - stq $12,24($30) - stq $13,32($30) - stq $14,40($30) - stq $15,48($30) - lda $8,0x3fff - addq $30,56,$19 - bic $30,$8,$8 - jsr $26,do_entUnaUser - ldq $9,0($30) - ldq $10,8($30) - ldq $11,16($30) - ldq $12,24($30) - ldq $13,32($30) - ldq $14,40($30) - ldq $15,48($30) - lda $30,56($30) - br $31,ret_from_sys_call - + ldq $0,0($30) /* restore original $0 */ + lda $30,256($30) /* pop entUna's stack frame */ + SAVE_ALL /* setup normal kernel stack */ + lda $30,-56($30) + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + lda $8,0x3fff + addq $30,56,$19 + bic $30,$8,$8 + jsr $26,do_entUnaUser + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + lda $30,56($30) + br ret_from_sys_call .end entUnaUser /* @@ -491,36 +486,36 @@ entUnaUser: .globl sys_fork .ent sys_fork sys_fork: - bsr $1,do_switch_stack - bis $31,SIGCHLD,$16 - bis $31,$31,$17 - bis $30,$30,$18 - jsr $26,alpha_clone - bsr $1,undo_switch_stack - ret $31,($26),1 + bsr $1,do_switch_stack + bis $31,SIGCHLD,$16 + mov $31,$17 + mov $30,$18 + jsr $26,alpha_clone + bsr $1,undo_switch_stack + ret $31,($26),1 .end sys_fork .align 3 .globl sys_clone .ent sys_clone sys_clone: - bsr $1,do_switch_stack + bsr $1,do_switch_stack /* arg1 and arg2 come from the user */ - bis $30,$30,$18 - jsr $26,alpha_clone - bsr $1,undo_switch_stack - ret $31,($26),1 + mov $30,$18 + jsr $26,alpha_clone + bsr $1,undo_switch_stack + ret $31,($26),1 .end sys_clone .align 3 .globl sys_vfork .ent sys_vfork sys_vfork: - bsr $1,do_switch_stack - bis $30,$30,$16 - jsr $26,alpha_vfork - bsr $1,undo_switch_stack - ret $31,($26),1 + bsr $1,do_switch_stack + mov $30,$16 + jsr $26,alpha_vfork + bsr $1,undo_switch_stack + ret $31,($26),1 .end sys_vfork .align 3 @@ -528,12 +523,12 @@ sys_vfork: .ent alpha_switch_to alpha_switch_to: .prologue 0 - bsr $1,do_switch_stack + bsr $1,do_switch_stack call_pal PAL_swpctx - lda $16,-2($31) - call_pal PAL_tbi - bsr $1,undo_switch_stack - ret $31,($26),1 + unop + bsr $1,undo_switch_stack + mov $17,$0 + ret $31,($26),1 .end alpha_switch_to /* @@ -592,7 +587,7 @@ ret_from_reschedule: bne $5,signal_return restore_all: RESTORE_ALL - rti + call_pal PAL_rti /* PTRACE syscall handler */ @@ -620,7 +615,7 @@ strace: s8addq $0,$2,$2 beq $1,1f ldq $27,0($2) -1: jsr $26,($27),alpha_ni_syscall +1: jsr $26,($27),sys_gettimeofday ldgp $29,0($26) /* check return.. */ @@ -646,15 +641,15 @@ strace_error: stq $1,72($30) /* a3 for return */ bsr $1,do_switch_stack - bis $19,$19,$9 /* save old syscall number */ - bis $20,$20,$10 /* save old a3 */ + mov $19,$9 /* save old syscall number */ + mov $20,$10 /* save old a3 */ jsr $26,syscall_trace - bis $9,$9,$19 - bis $10,$10,$20 + mov $9,$19 + mov $10,$20 bsr $1,undo_switch_stack - bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */ - br $31,ret_from_sys_call + mov $31,$26 /* tell "ret_from_sys_call" we can restart */ + br ret_from_sys_call .align 3 handle_bottom_half: @@ -665,7 +660,7 @@ handle_bottom_half: ldq $19,0($30) ldq $20,8($30) addq $30,16,$30 - br $31,ret_from_handle_bh + br ret_from_handle_bh .align 3 syscall_error: @@ -683,38 +678,35 @@ syscall_error: subq $31,$0,$0 /* with error in v0 */ addq $31,1,$1 /* set a3 for errno return */ stq $0,0($30) - bis $31,$31,$26 /* tell "ret_from_sys_call" we can restart */ + mov $31,$26 /* tell "ret_from_sys_call" we can restart */ stq $1,72($30) /* a3 for return */ - br $31,ret_from_sys_call + br ret_from_sys_call ret_success: stq $0,0($30) stq $31,72($30) /* a3=0 => no error */ - br $31,ret_from_sys_call + br ret_from_sys_call .align 3 signal_return: - bis $30,$30,$17 + mov $30,$17 br $1,do_switch_stack - bis $30,$30,$18 - bis $31,$31,$16 + mov $30,$18 + mov $31,$16 jsr $26,do_signal bsr $1,undo_switch_stack - br $31,restore_all + br restore_all .end entSys #ifdef __SMP__ - .globl ret_from_smpfork + .globl ret_from_smp_fork .align 3 -.ent ret_from_smpfork -ret_from_smpfork: - .set at - mb /* Make the changed data visible before the freed lock. */ - stq $31,scheduler_lock +.ent ret_from_smp_fork +ret_from_smp_fork: lda $26,ret_from_sys_call + mov $17,$16 jsr $31,schedule_tail - .set noat -.end ret_from_smpfork +.end ret_from_smp_fork #endif /* __SMP__ */ .align 3 @@ -727,51 +719,51 @@ reschedule: ldq $19,0($30) ldq $20,8($30) addq $30,16,$30 - br $31,ret_from_reschedule + br ret_from_reschedule .end reschedule .align 3 .ent sys_sigreturn sys_sigreturn: - bis $30,$30,$17 + mov $30,$17 + lda $18,-SWITCH_STACK_SIZE($30) lda $30,-SWITCH_STACK_SIZE($30) - bis $30,$30,$18 jsr $26,do_sigreturn br $1,undo_switch_stack - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_sigreturn .align 3 .ent sys_rt_sigreturn sys_rt_sigreturn: - bis $30,$30,$17 + mov $30,$17 + lda $18,-SWITCH_STACK_SIZE($30) lda $30,-SWITCH_STACK_SIZE($30) - bis $30,$30,$18 jsr $26,do_rt_sigreturn br $1,undo_switch_stack - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_rt_sigreturn .align 3 .ent sys_sigsuspend sys_sigsuspend: - bis $30,$30,$17 + mov $30,$17 br $1,do_switch_stack - bis $30,$30,$18 + mov $30,$18 jsr $26,do_sigsuspend lda $30,SWITCH_STACK_SIZE($30) - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_sigsuspend .align 3 .ent sys_rt_sigsuspend sys_rt_sigsuspend: - bis $30,$30,$18 + mov $30,$18 br $1,do_switch_stack - bis $30,$30,$19 + mov $30,$19 jsr $26,do_rt_sigsuspend lda $30,SWITCH_STACK_SIZE($30) - br $31,ret_from_sys_call + br ret_from_sys_call .end sys_rt_sigsuspend .data diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index aeb27211d078..38bd43947c08 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -885,7 +885,21 @@ asmlinkage unsigned long osf_getsysinfo(unsigned long op, void *buffer, case GSI_IEEE_FP_CONTROL: /* Return current software fp control & status bits. */ /* Note that DU doesn't verify available space here. */ - w = current->tss.flags & IEEE_SW_MASK; + + /* EV6 implements most of the bits in hardware. If + UNDZ is not set, UNFD is maintained in software. */ + if (implver() == IMPLVER_EV6) { + unsigned long fpcr = rdfpcr(); + w = ieee_fpcr_to_swcr(fpcr); + if (!(fpcr & FPCR_UNDZ)) { + w &= ~IEEE_TRAP_ENABLE_UNF; + w |= current->tss.flags & IEEE_TRAP_ENABLE_UNF; + } + } else { + /* Otherwise we are forced to do everything in sw. */ + w = current->tss.flags & IEEE_SW_MASK; + } + if (put_user(w, (unsigned long *) buffer)) return -EFAULT; return 0; @@ -935,7 +949,7 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, { switch (op) { case SSI_IEEE_FP_CONTROL: { - unsigned long swcr, fpcr; + unsigned long swcr, fpcr, undz; /* * Alpha Architecture Handbook 4.7.7.3: @@ -950,11 +964,12 @@ asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, current->tss.flags &= ~IEEE_SW_MASK; current->tss.flags |= swcr & IEEE_SW_MASK; - /* Update the real fpcr. For exceptions that are disabled in - software but have not been seen, enable the exception in - hardware so that we can update our software status mask. */ - fpcr = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK); - fpcr |= ieee_swcr_to_fpcr(swcr | (~swcr & IEEE_STATUS_MASK)>>16); + /* Update the real fpcr. Keep UNFD off if not UNDZ. */ + fpcr = rdfpcr(); + undz = (fpcr & FPCR_UNDZ); + fpcr &= ~(FPCR_MASK | FPCR_DYN_MASK | FPCR_UNDZ); + fpcr |= ieee_swcr_to_fpcr(swcr); + fpcr &= ~(undz << 1); wrfpcr(fpcr); return 0; diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 55e211a2345f..93d8db60276a 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -237,10 +237,13 @@ void exit_thread(void) void flush_thread(void) { - /* Arrange for each exec'ed process to start off with a - clean slate with respect to the FPU. */ + /* Arrange for each exec'ed process to start off with a clean slate + with respect to the FPU. This is all exceptions disabled. Note + that EV6 defines UNFD valid only with UNDZ, which we don't want + for IEEE conformance -- so that disabled bit remains in software. */ + current->tss.flags &= ~IEEE_SW_MASK; - wrfpcr(FPCR_DYN_NORMAL); + wrfpcr(FPCR_DYN_NORMAL | FPCR_INVD | FPCR_DZED | FPCR_OVFD | FPCR_INED); } void release_thread(struct task_struct *dead_task) @@ -270,8 +273,6 @@ int alpha_vfork(struct switch_stack * swstack) (struct pt_regs *) (swstack+1)); } -extern void ret_from_sys_call(void); -extern void ret_from_smpfork(void); /* * Copy an alpha thread.. * @@ -282,9 +283,13 @@ extern void ret_from_smpfork(void); * Use the passed "regs" pointer to determine how much space we need * for a kernel fork(). */ + int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { + extern void ret_from_sys_call(void); + extern void ret_from_smp_fork(void); + struct pt_regs * childregs; struct switch_stack * childstack, *stack; unsigned long stack_offset; @@ -292,18 +297,18 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, stack_offset = PAGE_SIZE - sizeof(struct pt_regs); if (!(regs->ps & 8)) stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; - childregs = (struct pt_regs *) (stack_offset + PAGE_SIZE + (unsigned long)p); + childregs = (struct pt_regs *) (stack_offset + PAGE_SIZE + (long)p); *childregs = *regs; childregs->r0 = 0; childregs->r19 = 0; - childregs->r20 = 1; /* OSF/1 has some strange fork() semantics.. */ + childregs->r20 = 1; /* OSF/1 has some strange fork() semantics. */ regs->r20 = 0; stack = ((struct switch_stack *) regs) - 1; childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; #ifdef __SMP__ - childstack->r26 = (unsigned long) ret_from_smpfork; + childstack->r26 = (unsigned long) ret_from_smp_fork; #else childstack->r26 = (unsigned long) ret_from_sys_call; #endif @@ -328,10 +333,12 @@ void dump_thread(struct pt_regs * pt, struct user * dump) dump->start_code = current->mm->start_code; dump->start_data = current->mm->start_data; dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); - dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT; - dump->u_dsize = (current->mm->brk + (PAGE_SIZE - 1) - dump->start_data) >> PAGE_SHIFT; - dump->u_ssize = - (current->mm->start_stack - dump->start_stack + PAGE_SIZE - 1) >> PAGE_SHIFT; + dump->u_tsize = ((current->mm->end_code - dump->start_code) + >> PAGE_SHIFT); + dump->u_dsize = ((current->mm->brk + PAGE_SIZE-1 - dump->start_data) + >> PAGE_SHIFT); + dump->u_ssize = (current->mm->start_stack - dump->start_stack + + PAGE_SIZE-1) >> PAGE_SHIFT; /* * We store the registers in an order/format that is diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index b905a66b73ca..aa1eaf363e48 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -37,7 +37,18 @@ #define DBGS(args) #endif -struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; +struct ipi_msg_flush_tb_struct { + volatile unsigned int flush_tb_mask; + union { + struct mm_struct * flush_mm; + struct vm_area_struct * flush_vma; + } p; + unsigned long flush_addr; + unsigned long flush_end; +}; + +static struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; +static spinlock_t flush_tb_lock = SPIN_LOCK_UNLOCKED; struct cpuinfo_alpha cpu_data[NR_CPUS]; @@ -786,7 +797,7 @@ flush_tlb_all(void) unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); long timeout = 1000000; - spin_lock_own(&kernel_flag, "flush_tlb_all"); + spin_lock(&flush_tb_lock); ipi_msg_flush_tb.flush_tb_mask = to_whom; send_ipi_message(to_whom, IPI_TLB_ALL); @@ -803,6 +814,8 @@ flush_tlb_all(void) ipi_msg_flush_tb.flush_tb_mask); ipi_msg_flush_tb.flush_tb_mask = 0; } + + spin_unlock(&flush_tb_lock); } void @@ -811,7 +824,7 @@ flush_tlb_mm(struct mm_struct *mm) unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); long timeout = 1000000; - spin_lock_own(&kernel_flag, "flush_tlb_mm"); + spin_lock(&flush_tb_lock); ipi_msg_flush_tb.flush_tb_mask = to_whom; ipi_msg_flush_tb.p.flush_mm = mm; @@ -833,6 +846,8 @@ flush_tlb_mm(struct mm_struct *mm) ipi_msg_flush_tb.flush_tb_mask); ipi_msg_flush_tb.flush_tb_mask = 0; } + + spin_unlock(&flush_tb_lock); } void @@ -843,7 +858,7 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) struct mm_struct * mm = vma->vm_mm; int timeout = 1000000; - spin_lock_own(&kernel_flag, "flush_tlb_page"); + spin_lock(&flush_tb_lock); ipi_msg_flush_tb.flush_tb_mask = to_whom; ipi_msg_flush_tb.p.flush_vma = vma; @@ -866,6 +881,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) ipi_msg_flush_tb.flush_tb_mask); ipi_msg_flush_tb.flush_tb_mask = 0; } + + spin_unlock(&flush_tb_lock); } void diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index e8e9fbafd157..2548f0914588 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -125,13 +125,16 @@ do_entArith(unsigned long summary, unsigned long write_mask, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { - if ((summary & 1)) { - /* - * Software-completion summary bit is set, so try to - * emulate the instruction. - */ - if (alpha_fp_emul_imprecise(®s, write_mask)) { - return; /* emulation was successful */ + if (summary & 1) { + /* Software-completion summary bit is set, so try to + emulate the instruction. */ + if (implver() == IMPLVER_EV6) { + /* Whee! EV6 has precice exceptions. */ + if (alpha_fp_emul(regs.pc - 4)) + return; + } else { + if (alpha_fp_emul_imprecise(®s, write_mask)) + return; } } @@ -141,7 +144,7 @@ do_entArith(unsigned long summary, unsigned long write_mask, current->comm, regs.pc, summary, write_mask); #endif die_if_kernel("Arithmetic fault", ®s, 0, 0); - force_sig(SIGFPE, current); + send_sig(SIGFPE, current, 1); unlock_kernel(); } @@ -150,14 +153,17 @@ do_entIF(unsigned long type, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { - lock_kernel(); die_if_kernel("Instruction fault", ®s, type, 0); switch (type) { case 0: /* breakpoint */ if (ptrace_cancel_bpt(current)) { regs.pc -= 4; /* make pc point to former bpt */ } - force_sig(SIGTRAP, current); + send_sig(SIGTRAP, current, 1); + break; + + case 1: /* bugcheck */ + send_sig(SIGTRAP, current, 1); break; case 2: /* gentrap */ @@ -171,14 +177,13 @@ do_entIF(unsigned long type, unsigned long a1, switch ((long) regs.r16) { case GEN_INTOVF: case GEN_INTDIV: case GEN_FLTOVF: case GEN_FLTDIV: case GEN_FLTUND: case GEN_FLTINV: - case GEN_FLTINE: - force_sig(SIGFPE, current); + case GEN_FLTINE: case GEN_ROPRAND: + send_sig(SIGFPE, current, 1); break; case GEN_DECOVF: case GEN_DECDIV: case GEN_DECINV: - case GEN_ROPRAND: case GEN_ASSERTERR: case GEN_NULPTRERR: case GEN_STKOVF: @@ -193,42 +198,29 @@ do_entIF(unsigned long type, unsigned long a1, case GEN_SUBRNG5: case GEN_SUBRNG6: case GEN_SUBRNG7: - force_sig(SIGILL, current); + send_sig(SIGTRAP, current, 1); break; } break; - case 1: /* bugcheck */ case 3: /* FEN fault */ - force_sig(SIGILL, current); + send_sig(SIGILL, current, 1); break; case 4: /* opDEC */ -#ifdef CONFIG_ALPHA_NEED_ROUNDING_EMULATION - { - unsigned int opcode; - - /* get opcode of faulting instruction: */ - get_user(opcode, (__u32*)(regs.pc - 4)); - opcode >>= 26; - if (opcode == 0x16) { - /* - * It's a FLTI instruction, emulate it - * (we don't do no stinkin' VAX fp...) - */ - if (!alpha_fp_emul(regs.pc - 4)) - force_sig(SIGFPE, current); - break; - } + if (implver() == IMPLVER_EV4) { + /* EV4 does not implement anything except normal + rounding. Everything else will come here as + an illegal instruction. Emulate them. */ + if (alpha_fp_emul(regs.pc - 4)) + return; } -#endif - force_sig(SIGILL, current); + send_sig(SIGILL, current, 1); break; default: panic("do_entIF: unexpected instruction-fault type"); } - unlock_kernel(); } /* There is an ifdef in the PALcode in MILO that enables a @@ -877,14 +869,14 @@ do_entUnaUser(void * va, unsigned long opcode, give_sigsegv: regs->pc -= 4; /* make pc point to faulting insn */ lock_kernel(); - force_sig(SIGSEGV, current); + send_sig(SIGSEGV, current, 1); unlock_kernel(); return; give_sigbus: regs->pc -= 4; lock_kernel(); - force_sig(SIGBUS, current); + send_sig(SIGBUS, current, 1); unlock_kernel(); return; } diff --git a/arch/alpha/math-emu/fp-emul.c b/arch/alpha/math-emu/fp-emul.c index 97f4bc3267f2..122ec85ac8cc 100644 --- a/arch/alpha/math-emu/fp-emul.c +++ b/arch/alpha/math-emu/fp-emul.c @@ -13,6 +13,7 @@ #define OPC_INTL 0x11 #define OPC_INTS 0x12 #define OPC_INTM 0x13 +#define OPC_FLTC 0x14 #define OPC_FLTV 0x15 #define OPC_FLTI 0x16 #define OPC_FLTL 0x17 @@ -21,33 +22,37 @@ #define OPC_JSR 0x1a +#define OP_FUN(OP,FUN) ((OP << 26) | (FUN << 5)) + /* - * "Base" function codes for the FLTI-class instructions. These - * instructions all have opcode 0x16. Note that in most cases these - * actually correspond to the "chopped" form of the instruction. Not - * to worry---we extract the qualifier bits separately and deal with - * them separately. Notice that base function code 0x2c is used for - * both CVTTS and CVTST. The other bits in the function code are used - * to distinguish the two. + * "Base" function codes for the FLTI-class instructions. + * Note that in most cases these actually correspond to the "chopped" + * form of the instruction. Not to worry---we extract the qualifier + * bits separately and deal with them separately. Notice that base + * function code 0x2c is used for both CVTTS and CVTST. The other bits + * in the function code are used to distinguish the two. */ -#define FLTI_FUNC_ADDS 0x000 -#define FLTI_FUNC_ADDT 0x020 -#define FLTI_FUNC_CMPTEQ 0x025 -#define FLTI_FUNC_CMPTLT 0x026 -#define FLTI_FUNC_CMPTLE 0x027 -#define FLTI_FUNC_CMPTUN 0x024 -#define FLTI_FUNC_CVTTS_or_CVTST 0x02c -#define FLTI_FUNC_CVTTQ 0x02f -#define FLTI_FUNC_CVTQS 0x03c -#define FLTI_FUNC_CVTQT 0x03e -#define FLTI_FUNC_DIVS 0x003 -#define FLTI_FUNC_DIVT 0x023 -#define FLTI_FUNC_MULS 0x002 -#define FLTI_FUNC_MULT 0x022 -#define FLTI_FUNC_SUBS 0x001 -#define FLTI_FUNC_SUBT 0x021 - -#define FLTI_FUNC_CVTQL 0x030 /* opcode 0x17 */ +#define FLTI_FUNC_ADDS OP_FUN(OPC_FLTI, 0x000) +#define FLTI_FUNC_ADDT OP_FUN(OPC_FLTI, 0x020) +#define FLTI_FUNC_CMPTEQ OP_FUN(OPC_FLTI, 0x025) +#define FLTI_FUNC_CMPTLT OP_FUN(OPC_FLTI, 0x026) +#define FLTI_FUNC_CMPTLE OP_FUN(OPC_FLTI, 0x027) +#define FLTI_FUNC_CMPTUN OP_FUN(OPC_FLTI, 0x024) +#define FLTI_FUNC_CVTTS_or_CVTST OP_FUN(OPC_FLTI, 0x02c) +#define FLTI_FUNC_CVTTQ OP_FUN(OPC_FLTI, 0x02f) +#define FLTI_FUNC_CVTQS OP_FUN(OPC_FLTI, 0x03c) +#define FLTI_FUNC_CVTQT OP_FUN(OPC_FLTI, 0x03e) +#define FLTI_FUNC_DIVS OP_FUN(OPC_FLTI, 0x003) +#define FLTI_FUNC_DIVT OP_FUN(OPC_FLTI, 0x023) +#define FLTI_FUNC_MULS OP_FUN(OPC_FLTI, 0x002) +#define FLTI_FUNC_MULT OP_FUN(OPC_FLTI, 0x022) +#define FLTI_FUNC_SUBS OP_FUN(OPC_FLTI, 0x001) +#define FLTI_FUNC_SUBT OP_FUN(OPC_FLTI, 0x021) + +#define FLTC_FUNC_SQRTS OP_FUN(OPC_FLTC, 0x00B) +#define FLTC_FUNC_SQRTT OP_FUN(OPC_FLTC, 0x02B) + +#define FLTL_FUNC_CVTQL OP_FUN(OPC_FLTL, 0x030) #define MISC_TRAPB 0x0000 #define MISC_EXCB 0x0400 @@ -101,7 +106,7 @@ void cleanup_module(void) long alpha_fp_emul (unsigned long pc) { - unsigned long opcode, fa, fb, fc, func, mode; + unsigned long op_fun, fa, fb, fc, func, mode; unsigned long fpcw = current->tss.flags; unsigned long va, vb, vc, res, fpcr; __u32 insn; @@ -110,10 +115,11 @@ alpha_fp_emul (unsigned long pc) get_user(insn, (__u32*)pc); fc = (insn >> 0) & 0x1f; /* destination register */ - func = (insn >> 5) & 0x7ff; fb = (insn >> 16) & 0x1f; fa = (insn >> 21) & 0x1f; - opcode = insn >> 26; + func = (insn >> 5) & 0x7ff; + mode = (insn >> 5) & 0xc0; + op_fun = insn & OP_FUN(0x3f, 0x3f); va = alpha_read_fp_reg(fa); vb = alpha_read_fp_reg(fb); @@ -123,7 +129,6 @@ alpha_fp_emul (unsigned long pc) * Try the operation in software. First, obtain the rounding * mode... */ - mode = func & 0xc0; if (mode == 0xc0) { /* dynamic---get rounding mode from fpcr: */ mode = ((fpcr & FPCR_DYN_MASK) >> FPCR_DYN_SHIFT) << ROUND_SHIFT; @@ -135,8 +140,7 @@ alpha_fp_emul (unsigned long pc) something_is_wrong(); } - /* least 6 bits contain operation code: */ - switch (func & 0x3f) { + switch (op_fun) { case FLTI_FUNC_CMPTEQ: res = ieee_CMPTEQ(va, vb, &vc); break; @@ -153,7 +157,7 @@ alpha_fp_emul (unsigned long pc) res = ieee_CMPTUN(va, vb, &vc); break; - case FLTI_FUNC_CVTQL: + case FLTL_FUNC_CVTQL: /* * Notice: We can get here only due to an integer * overflow. Such overflows are reported as invalid @@ -222,6 +226,14 @@ alpha_fp_emul (unsigned long pc) res = ieee_CVTTQ(mode, vb, &vc); break; + case FLTC_FUNC_SQRTS: + res = ieee_SQRTS(mode, vb, &vc); + break; + + case FLTC_FUNC_SQRTT: + res = ieee_SQRTT(mode, vb, &vc); + break; + default: printk("alpha_fp_emul: unexpected function code %#lx at %#lx\n", func & 0x3f, pc); @@ -247,7 +259,7 @@ alpha_fp_emul (unsigned long pc) /* Update hardware control register */ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); - fpcr |= ieee_swcr_to_fpcr(fpcw | (~fpcw&IEEE_STATUS_MASK)>>16); + fpcr |= ieee_swcr_to_fpcr(fpcw); wrfpcr(fpcr); /* Do we generate a signal? */ @@ -319,6 +331,7 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) write_mask &= ~(1UL << rc); break; + case OPC_FLTC: case OPC_FLTV: case OPC_FLTI: case OPC_FLTL: @@ -326,13 +339,11 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) break; } if (!write_mask) { - if ((opcode == OPC_FLTI || opcode == OPC_FLTL) - && alpha_fp_emul(trigger_pc)) - { - /* re-execute insns in trap-shadow: */ - regs->pc = trigger_pc + 4; - MOD_DEC_USE_COUNT; - return 1; + if (alpha_fp_emul(trigger_pc)) { + /* re-execute insns in trap-shadow: */ + regs->pc = trigger_pc + 4; + MOD_DEC_USE_COUNT; + return 1; } break; } diff --git a/arch/alpha/math-emu/ieee-math.c b/arch/alpha/math-emu/ieee-math.c index 4208b742c92d..224fdd2b7122 100644 --- a/arch/alpha/math-emu/ieee-math.c +++ b/arch/alpha/math-emu/ieee-math.c @@ -22,6 +22,7 @@ * functions are used on exceptional numbers only (well, assuming you * don't turn on the "trap on inexact"...). */ +#include #include "ieee-math.h" #define STICKY_S 0x20000000 /* both in longword 0 of fraction */ @@ -1340,3 +1341,41 @@ ieee_DIVT (int f, unsigned long a, unsigned long b, unsigned long *c) op_c.e -= 9; /* remove excess exp from original shift */ return round_t_ieee(f, &op_c, c); } + +/* + * Sqrt a = b, where a and b are ieee s-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_SQRTS (int f, unsigned long a, unsigned long *b) +{ + fpclass_t a_type; + EXTENDED op_a, op_b; + + *b = IEEE_QNaN; + a_type = extend_ieee(a, &op_a, SINGLE); + if (op_a.s == 0) { + /* FIXME -- handle positive denormals. */ + send_sig(SIGFPE, current, 1); + } + return FPCR_INV; +} + +/* + * Sqrt a = b, where a and b are ieee t-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_SQRTT (int f, unsigned long a, unsigned long *b) +{ + fpclass_t a_type; + EXTENDED op_a, op_b; + + *b = IEEE_QNaN; + a_type = extend_ieee(a, &op_a, DOUBLE); + if (op_a.s == 0) { + /* FIXME -- handle positive denormals. */ + send_sig(SIGFPE, current, 1); + } + return FPCR_INV; +} diff --git a/arch/alpha/math-emu/ieee-math.h b/arch/alpha/math-emu/ieee-math.h index 35ce1afb7668..1b3cf26940af 100644 --- a/arch/alpha/math-emu/ieee-math.h +++ b/arch/alpha/math-emu/ieee-math.h @@ -48,5 +48,7 @@ extern unsigned long ieee_DIVS (int rm, unsigned long a, unsigned long b, unsigned long *c); extern unsigned long ieee_DIVT (int rm, unsigned long a, unsigned long b, unsigned long *c); +extern unsigned long ieee_SQRTS (int rm, unsigned long a, unsigned long *b); +extern unsigned long ieee_SQRTT (int rm, unsigned long a, unsigned long *b); #endif /* __ieee_math_h__ */ diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index b316d8670f86..f0d5d337829a 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -92,7 +92,7 @@ EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); -EXPORT_SYMBOL(mtrr_hook); +EXPORT_SYMBOL(smp_call_function); #endif #ifdef CONFIG_MCA diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 6170e0ce1b1d..ea218fe45214 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -322,7 +322,7 @@ BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_SMP_INTERRUPT(reschedule_interrupt) BUILD_SMP_INTERRUPT(invalidate_interrupt) BUILD_SMP_INTERRUPT(stop_cpu_interrupt) -BUILD_SMP_INTERRUPT(mtrr_interrupt) +BUILD_SMP_INTERRUPT(call_function_interrupt) BUILD_SMP_INTERRUPT(spurious_interrupt) /* @@ -1094,8 +1094,8 @@ __initfunc(void init_IRQ(void)) /* self generated IPI for local APIC timer */ set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); - /* IPI for MTRR control */ - set_intr_gate(MTRR_CHANGE_VECTOR, mtrr_interrupt); + /* IPI for generic function call */ + set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); /* IPI vector for APIC spurious interrupts */ set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 533ab9c9a759..6a19d9884a7d 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -65,7 +65,7 @@ typedef struct { #define INVALIDATE_TLB_VECTOR 0x31 #define STOP_CPU_VECTOR 0x40 #define LOCAL_TIMER_VECTOR 0x41 -#define MTRR_CHANGE_VECTOR 0x50 +#define CALL_FUNCTION_VECTOR 0x50 /* * First APIC vector available to drivers: (vectors 0x51-0xfe) @@ -99,7 +99,6 @@ extern void disable_8259A_irq(unsigned int irq); extern int i8259A_irq_pending(unsigned int irq); extern void ack_APIC_irq(void); extern void FASTCALL(send_IPI_self(int vector)); -extern void smp_send_mtrr(void); extern void init_VISWS_APIC_irqs(void); extern void setup_IO_APIC(void); extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn); diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 8dd5b1bb86a8..0d71d8bb50fb 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -132,11 +132,70 @@ Fixed harmless compiler warning in include/asm-i386/mtrr.h Fixed version numbering and history for v1.23 -> v1.24. v1.26 - - v1.26ac Alan Cox - Added some K6-II/III support. This needs back merging with - Richard's current code before it goes to Linus really. - + 19990118 Richard Gooch + PLACEHOLDER. + v1.27 + 19990123 Richard Gooch + Changed locking to spin with reschedule. + Made use of new . + v1.28 + 19990201 Zoltan Boszormenyi + Extended the driver to be able to use Cyrix style ARRs. + 19990204 Richard Gooch + Restructured Cyrix support. + v1.29 + 19990204 Zoltan Boszormenyi + Refined ARR support: enable MAPEN in set_mtrr_prepare() + and disable MAPEN in set_mtrr_done(). + 19990205 Richard Gooch + Minor cleanups. + v1.30 + 19990208 Zoltan Boszormenyi + Protect plain 6x86s (and other processors without the + Page Global Enable feature) against accessing CR4 in + set_mtrr_prepare() and set_mtrr_done(). + 19990210 Richard Gooch + Turned and into function pointers. + v1.31 + 19990212 Zoltan Boszormenyi + Major rewrite of cyrix_arr_init(): do not touch ARRs, + leave them as the BIOS have set them up. + Enable usage of all 8 ARRs. + Avoid multiplications by 3 everywhere and other + code clean ups/speed ups. + 19990213 Zoltan Boszormenyi + Set up other Cyrix processors identical to the boot cpu. + Since Cyrix don't support Intel APIC, this is l'art pour l'art. + Weigh ARRs by size: + If size <= 32M is given, set up ARR# we were given. + If size > 32M is given, set up ARR7 only if it is free, + fail otherwise. + 19990214 Zoltan Boszormenyi + Also check for size >= 256K if we are to set up ARR7, + mtrr_add() returns the value it gets from set_mtrr() + 19990218 Zoltan Boszormenyi + Remove Cyrix "coma bug" workaround from here. + Moved to linux/arch/i386/kernel/setup.c and + linux/include/asm-i386/bugs.h + 19990228 Richard Gooch + Added #ifdef CONFIG_DEVFS_FS + Added MTRRIOC_KILL_ENTRY ioctl(2) + Trap for counter underflow in . + Trap for 4 MiB aligned regions for PPro, stepping <= 7. + 19990301 Richard Gooch + Created hook. + 19990305 Richard Gooch + Temporarily disable AMD support now MTRR capability flag is set. + v1.32 + 19990308 Zoltan Boszormenyi + Adjust my changes (19990212-19990218) to Richard Gooch's + latest changes. (19990228-19990305) + v1.33 + 19990309 Richard Gooch + Fixed typo in message. + 19990310 Richard Gooch + Support K6-II/III based on Alan Cox's patches. + v1.34 */ #include #include @@ -173,7 +232,7 @@ #include #include "irq.h" -#define MTRR_VERSION "1.26 (19981001)" +#define MTRR_VERSION "1.34 (19990310)" #define TRUE 1 #define FALSE 0 @@ -203,7 +262,7 @@ # define MTRR_CHANGE_MASK_DEFTYPE 0x04 #endif -/* In the intel processor's MTRR interface, the MTRR type is always held in +/* In the Intel processor's MTRR interface, the MTRR type is always held in an 8 bit field: */ typedef u8 mtrr_type; @@ -213,9 +272,12 @@ typedef u8 mtrr_type; #ifdef __SMP__ # define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type) #else -# define set_mtrr(reg,base,size,type) set_mtrr_up (reg, base, size, type,TRUE) +# define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \ + TRUE) #endif +#define spin_lock_reschedule(lock) while (!spin_trylock(lock)) schedule (); + #ifndef CONFIG_PROC_FS # define compute_ascii() while (0) #endif @@ -231,7 +293,6 @@ static spinlock_t main_lock = SPIN_LOCK_UNLOCKED; #ifdef CONFIG_PROC_FS static void compute_ascii (void); #endif -static int k6_has_ranges(void); struct set_mtrr_context @@ -240,36 +301,30 @@ struct set_mtrr_context unsigned long deftype_lo; unsigned long deftype_hi; unsigned long cr4val; + unsigned long ccr3; }; -/* - * No point continually digging through complex CPU conditionals.. - */ - -static int mtrr_flags; - -#define MTRR_PRESENT 1 -#define MTRR_WRCOMB 2 -/* Put the processor into a state where MTRRs can be safely set. */ -static void set_mtrr_prepare(struct set_mtrr_context *ctxt) +/* Put the processor into a state where MTRRs can be safely set */ +static void set_mtrr_prepare (struct set_mtrr_context *ctxt) { unsigned long tmp; - /* disable interrupts locally */ + /* Disable interrupts locally */ __save_flags (ctxt->flags); __cli (); - if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - return; - - /* save value of CR4 and clear Page Global Enable (bit 7) */ - asm volatile ("movl %%cr4, %0\n\t" - "movl %0, %1\n\t" - "andb $0x7f, %b1\n\t" - "movl %1, %%cr4\n\t" - : "=r" (ctxt->cr4val), "=q" (tmp) : : "memory"); - /* disable and flush caches. Note that wbinvd flushes the TLBs as - a side-effect. */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return; + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) + asm volatile ("movl %%cr4, %0\n\t" + "movl %0, %1\n\t" + "andb $0x7f, %b1\n\t" + "movl %1, %%cr4\n\t" + : "=r" (ctxt->cr4val), "=q" (tmp) : : "memory"); + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ asm volatile ("movl %%cr0, %0\n\t" "orl $0x40000000, %0\n\t" "wbinvd\n\t" @@ -277,110 +332,108 @@ static void set_mtrr_prepare(struct set_mtrr_context *ctxt) "wbinvd\n\t" : "=r" (tmp) : : "memory"); - /* disable MTRRs, and set the default type to uncached. */ - rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); - wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + /* Disable MTRRs, and set the default type to uncached */ + rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); + break; + case X86_VENDOR_CYRIX: + tmp = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (tmp & 0x0f) | 0x10); + ctxt->ccr3 = tmp; + break; + } } /* End Function set_mtrr_prepare */ - -/* Restore the processor after a set_mtrr_prepare */ -static void set_mtrr_done(struct set_mtrr_context *ctxt) +/* Restore the processor after a set_mtrr_prepare */ +static void set_mtrr_done (struct set_mtrr_context *ctxt) { unsigned long tmp; - if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - return; - - /* flush caches and TLBs */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + { + __restore_flags (ctxt->flags); + return; + } + + /* Flush caches and TLBs */ asm volatile ("wbinvd" : : : "memory" ); - /* restore MTRRdefType */ - wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + /* Restore MTRRdefType */ + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + break; + case X86_VENDOR_CYRIX: + setCx86 (CX86_CCR3, ctxt->ccr3); + break; + } - /* enable caches */ + /* Enable caches */ asm volatile ("movl %%cr0, %0\n\t" "andl $0xbfffffff, %0\n\t" "movl %0, %%cr0\n\t" : "=r" (tmp) : : "memory"); - /* restore value of CR4 */ - asm volatile ("movl %0, %%cr4" - : : "r" (ctxt->cr4val) : "memory"); + /* Restore value of CR4 */ + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) + asm volatile ("movl %0, %%cr4" + : : "r" (ctxt->cr4val) : "memory"); - /* re-enable interrupts locally (if enabled previously) */ + /* Re-enable interrupts locally (if enabled previously) */ __restore_flags (ctxt->flags); } /* End Function set_mtrr_done */ - -/* this function returns the number of variable MTRRs */ +/* This function returns the number of variable MTRRs */ static unsigned int get_num_var_ranges (void) { unsigned long config, dummy; - - if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - return 2; - rdmsr(MTRRcap_MSR, config, dummy); - return (config & 0xff); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & 0xff); + /*break;*/ + case X86_VENDOR_CYRIX: + /* Cyrix have 8 ARRs */ + return 8; + /*break;*/ + case X86_VENDOR_AMD: + return 2; + /*break;*/ + } + return 0; } /* End Function get_num_var_ranges */ - -/* non-zero if we have the write-combining memory type. */ +/* Returns non-zero if we have the write-combining memory type */ static int have_wrcomb (void) { unsigned long config, dummy; - rdmsr(MTRRcap_MSR, config, dummy); - return (config & (1<<10)); -} - + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + rdmsr (MTRRcap_MSR, config, dummy); + return (config & (1<<10)); + /*break;*/ + case X86_VENDOR_CYRIX: + case X86_VENDOR_AMD: + return 1; + /*break;*/ + } + return 0; +} /* End Function have_wrcomb */ -static void get_mtrr (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type) +static void intel_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) { unsigned long dummy, mask_lo, base_lo; - if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - { - unsigned long low, high; - rdmsr(0xC0000085, low, high); - /* Upper dword is region 1, lower is region 0 */ - if(reg==1) - low=high; - /* The base masks off on the right alignment */ - *base=low&0xFFFE0000; - *type=0; - if(low&1) - *type=MTRR_TYPE_UNCACHABLE; - if(low&2) - *type=MTRR_TYPE_WRCOMB; - if(!(low&3)) - { - *size=0; - return; - } - - /* - * This needs a little explaining. The size is stored as an - * inverted mask of bits of 128K granularity 15 bits long offset - * 2 bits - * - * So to get a size we do invert the mask and add 1 to the lowest - * mask bit (4 as its 2 bits in). This gives us a size we then shift - * to turn into 128K blocks - * - * eg 111 1111 1111 1100 is 512K - * - * invert 000 0000 0000 0011 - * +1 000 0000 0000 0100 - * *128K ... - */ - - low=(~low)&0x1FFFC; - *size = (low+4)<<15; - return; - } - rdmsr(MTRRphysMask_MSR(reg), mask_lo, dummy); + rdmsr (MTRRphysMask_MSR(reg), mask_lo, dummy); if ((mask_lo & 0x800) == 0) { /* Invalid (i.e. free) range. */ *base = 0; @@ -404,11 +457,104 @@ static void get_mtrr (unsigned int reg, unsigned long *base, *base = (base_lo & 0xfffff000UL); *type = (base_lo & 0xff); -} /* End Function get_mtrr */ +} /* End Function intel_get_mtrr */ +static void cyrix_get_arr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long flags; + unsigned char arr, ccr3, rcr, shift; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* Save flags and disable interrupts */ + __save_flags (flags); __cli (); + + ccr3 = getCx86 (CX86_CCR3); + setCx86 (CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ((unsigned char *) base)[3] = getCx86 (arr); + ((unsigned char *) base)[2] = getCx86 (arr+1); + ((unsigned char *) base)[1] = getCx86 (arr+2); + rcr = getCx86(CX86_RCR_BASE + reg); + setCx86 (CX86_CCR3, ccr3); /* disable MAPEN */ -static void set_mtrr_up (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type, int do_safe) + /* Enable interrupts if it was enabled previously */ + __restore_flags (flags); + + shift = ((unsigned char *) base)[1] & 0x0f; + *base &= 0xfffff000UL; + + /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 + * Note: shift==0xf means 4G, this is unsupported. + */ + if (shift) + *size = (reg < 7 ? 0x800UL : 0x20000UL) << shift; + else + *size = 0; + + /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ + if (reg < 7) { + switch (rcr) { + case 1: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRBACK; break; + case 9: *type = MTRR_TYPE_WRCOMB; break; + case 24: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } else { + switch (rcr) { + case 0: *type = MTRR_TYPE_UNCACHABLE; break; + case 8: *type = MTRR_TYPE_WRCOMB; break; + case 9: *type = MTRR_TYPE_WRBACK; break; + case 25: + default: *type = MTRR_TYPE_WRTHROUGH; break; + } + } +} /* End Function cyrix_get_arr */ + +static void amd_get_mtrr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + unsigned long low, high; + + rdmsr (0xC0000085, low, high); + /* Upper dword is region 1, lower is region 0 */ + if (reg == 1) low = high; + /* The base masks off on the right alignment */ + *base = low & 0xFFFE0000; + *type = 0; + if (low & 1) *type = MTRR_TYPE_UNCACHABLE; + if (low & 2) *type = MTRR_TYPE_WRCOMB; + if ( !(low & 3) ) + { + *size = 0; + return; + } + /* + * This needs a little explaining. The size is stored as an + * inverted mask of bits of 128K granularity 15 bits long offset + * 2 bits + * + * So to get a size we do invert the mask and add 1 to the lowest + * mask bit (4 as its 2 bits in). This gives us a size we then shift + * to turn into 128K blocks + * + * eg 111 1111 1111 1100 is 512K + * + * invert 000 0000 0000 0011 + * +1 000 0000 0000 0100 + * *128K ... + */ + low = (~low) & 0x1FFFC; + *size = (low + 4) << 15; + return; +} /* End Function amd_get_mtrr */ + +static void (*get_mtrr) (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) = NULL; + +static void intel_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) /* [SUMMARY] Set variable MTRR register on the local CPU. The register to set. The base address of the region. @@ -416,65 +562,110 @@ static void set_mtrr_up (unsigned int reg, unsigned long base, The type of the region. If TRUE, do the change safely. If FALSE, safety measures should be done externally. + [RETURNS] Nothing. */ { struct set_mtrr_context ctxt; if (do_safe) set_mtrr_prepare (&ctxt); - - if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) + if (size == 0) { - if (size == 0) - { - /* The invalid bit is kept in the mask, so we simply clear the - relevant mask register to disable a range. */ - wrmsr (MTRRphysMask_MSR (reg), 0, 0); - } - else - { - wrmsr (MTRRphysBase_MSR (reg), base | type, 0); - wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0); - } + /* The invalid bit is kept in the mask, so we simply clear the + relevant mask register to disable a range. */ + wrmsr (MTRRphysMask_MSR (reg), 0, 0); } - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + else { - u32 low, high; - unsigned long flags; - /* - * Low is MTRR0 , High MTRR 1 - */ - rdmsr(0xC0000085, low, high); - /* - * Blank to disable - */ - if(size==0) - *(reg?&high:&low)=0; - else - /* Set the register to the base (already shifted for us), the - type (off by one) and an inverted bitmask of the size - - The size is the only odd bit. We are fed say 512K - We invert this and we get 111 1111 1111 1011 but - if you subtract one and invert you get the desired - 111 1111 1111 1100 mask - */ - - *(reg?&high:&low)=(((~(size-1))>>15)&0x0001FFFC)|base|(type+1); - - /* - * The writeback rule is quite specific. See the manual. Its - * disable local interrupts, write back the cache, set the mtrr - */ - - save_flags(flags); - __cli(); - __asm__ __volatile__("wbinvd" : : : "memory"); - wrmsr(0xC0000085, low, high); - restore_flags(flags); + wrmsr (MTRRphysBase_MSR (reg), base | type, 0); + wrmsr (MTRRphysMask_MSR (reg), ~(size - 1) | 0x800, 0); } if (do_safe) set_mtrr_done (&ctxt); -} /* End Function set_mtrr_up */ +} /* End Function intel_set_mtrr_up */ + +static void cyrix_set_arr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +{ + struct set_mtrr_context ctxt; + unsigned char arr, arr_type, arr_size; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ + size >>= (reg < 7 ? 12 : 18); + size &= 0x7fff; /* make sure arr_size <= 14 */ + for(arr_size = 0; size; arr_size++, size >>= 1); + + if (reg<7) { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; + case MTRR_TYPE_WRCOMB: arr_type = 9; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; + default: arr_type = 8; break; + } + } else { + switch (type) { + case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; + case MTRR_TYPE_WRCOMB: arr_type = 8; break; + case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; + default: arr_type = 9; break; + } + } + + if (do_safe) set_mtrr_prepare (&ctxt); + setCx86(arr, ((unsigned char *) &base)[3]); + setCx86(arr+1, ((unsigned char *) &base)[2]); + setCx86(arr+2, (((unsigned char *) &base)[1]) | arr_size); + setCx86(CX86_RCR_BASE + reg, arr_type); + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function cyrix_set_arr_up */ + +static void amd_set_mtrr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, int do_safe) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + u32 low, high; + struct set_mtrr_context ctxt; + + if (do_safe) set_mtrr_prepare (&ctxt); + /* + * Low is MTRR0 , High MTRR 1 + */ + rdmsr (0xC0000085, low, high); + /* + * Blank to disable + */ + if (size == 0) + *(reg ? &high : &low) = 0; + else + /* Set the register to the base (already shifted for us), the + type (off by one) and an inverted bitmask of the size + + The size is the only odd bit. We are fed say 512K + We invert this and we get 111 1111 1111 1011 but + if you subtract one and invert you get the desired + 111 1111 1111 1100 mask + */ + *(reg ? &high : &low)=(((~(size-1))>>15)&0x0001FFFC)|base|(type+1); + /* + * The writeback rule is quite specific. See the manual. Its + * disable local interrupts, write back the cache, set the mtrr + */ + __asm__ __volatile__ ("wbinvd" : : : "memory"); + wrmsr (0xC0000085, low, high); + if (do_safe) set_mtrr_done (&ctxt); +} /* End Function amd_set_mtrr_up */ +static void (*set_mtrr_up) (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, + int do_safe) = NULL; #ifdef __SMP__ @@ -487,7 +678,7 @@ struct mtrr_var_range }; -/* Get the MSR pair relating to a var range. */ +/* Get the MSR pair relating to a var range */ __initfunc(static void get_mtrr_var_range (unsigned int index, struct mtrr_var_range *vr)) { @@ -496,8 +687,8 @@ __initfunc(static void get_mtrr_var_range (unsigned int index, } /* End Function get_mtrr_var_range */ -/* Set the MSR pair relating to a var range. Returns TRUE if - changes are made. */ +/* Set the MSR pair relating to a var range. Returns TRUE if + changes are made */ __initfunc(static int set_mtrr_var_range_testing (unsigned int index, struct mtrr_var_range *vr)) { @@ -521,8 +712,7 @@ __initfunc(static int set_mtrr_var_range_testing (unsigned int index, } return changed; -} - +} /* End Function set_mtrr_var_range_testing */ __initfunc(static void get_fixed_ranges(mtrr_type *frs)) { @@ -536,8 +726,7 @@ __initfunc(static void get_fixed_ranges(mtrr_type *frs)) for (i = 0; i < 8; i++) rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); -} - +} /* End Function get_fixed_ranges */ __initfunc(static int set_fixed_ranges_testing(mtrr_type *frs)) { @@ -567,10 +756,8 @@ __initfunc(static int set_fixed_ranges_testing(mtrr_type *frs)) changed = TRUE; } } - return changed; -} - +} /* End Function set_fixed_ranges_testing */ struct mtrr_state { @@ -582,7 +769,7 @@ struct mtrr_state }; -/* Grab all of the MTRR state for this CPU into *state. */ +/* Grab all of the MTRR state for this CPU into *state */ __initfunc(static void get_mtrr_state(struct mtrr_state *state)) { unsigned int nvrs, i; @@ -591,28 +778,22 @@ __initfunc(static void get_mtrr_state(struct mtrr_state *state)) nvrs = state->num_var_ranges = get_num_var_ranges(); vrs = state->var_ranges - = kmalloc(nvrs * sizeof(struct mtrr_var_range), GFP_KERNEL); + = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); if (vrs == NULL) nvrs = state->num_var_ranges = 0; for (i = 0; i < nvrs; i++) - get_mtrr_var_range(i, &vrs[i]); + get_mtrr_var_range (i, &vrs[i]); - if(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) - { - get_fixed_ranges(state->fixed_ranges); + get_fixed_ranges (state->fixed_ranges); - rdmsr(MTRRdefType_MSR, lo, dummy); - state->def_type = (lo & 0xff); - state->enabled = (lo & 0xc00) >> 10; - return; - } - state->def_type = 0; - state->enabled = 1; + rdmsr (MTRRdefType_MSR, lo, dummy); + state->def_type = (lo & 0xff); + state->enabled = (lo & 0xc00) >> 10; } /* End Function get_mtrr_state */ -/* Free resources associated with a struct mtrr_state */ +/* Free resources associated with a struct mtrr_state */ __initfunc(static void finalize_mtrr_state(struct mtrr_state *state)) { if (state->var_ranges) kfree (state->var_ranges); @@ -631,18 +812,15 @@ __initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, unsigned int i; unsigned long change_mask = 0; - if(boot_cpu_data.x86_vendor == X86_VENDOR_AMD) - return 0UL; - for (i = 0; i < state->num_var_ranges; i++) - if (set_mtrr_var_range_testing(i, &state->var_ranges[i])) + if ( set_mtrr_var_range_testing (i, &state->var_ranges[i]) ) change_mask |= MTRR_CHANGE_MASK_VARIABLE; - if (set_fixed_ranges_testing(state->fixed_ranges)) + if ( set_fixed_ranges_testing(state->fixed_ranges) ) change_mask |= MTRR_CHANGE_MASK_FIXED; - /* set_mtrr_restore restores the old value of MTRRdefType, - so to set it we fiddle with the saved value. */ + /* Set_mtrr_restore restores the old value of MTRRdefType, + so to set it we fiddle with the saved value */ if ((ctxt->deftype_lo & 0xff) != state->def_type || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled) { @@ -655,76 +833,63 @@ __initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, static atomic_t undone_count; -static void (*handler_func) (struct set_mtrr_context *ctxt, void *info); -static void *handler_info; static volatile int wait_barrier_execute = FALSE; static volatile int wait_barrier_cache_enable = FALSE; -static void sync_handler (void) +struct set_mtrr_data +{ + unsigned long smp_base; + unsigned long smp_size; + unsigned int smp_reg; + mtrr_type smp_type; +}; + +static void ipi_handler (void *info) /* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. [RETURNS] Nothing. */ { + struct set_mtrr_data *data = info; struct set_mtrr_context ctxt; set_mtrr_prepare (&ctxt); - /* Notify master CPU that I'm at the barrier and then wait */ + /* Notify master that I've flushed and disabled my cache */ atomic_dec (&undone_count); while (wait_barrier_execute) barrier (); /* The master has cleared me to execute */ - (*handler_func) (&ctxt, handler_info); + (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, + data->smp_type, FALSE); /* Notify master CPU that I've executed the function */ atomic_dec (&undone_count); /* Wait for master to clear me to enable cache and return */ while (wait_barrier_cache_enable) barrier (); set_mtrr_done (&ctxt); -} /* End Function sync_handler */ - -static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt, - void *info), - void *info, int local) -/* [SUMMARY] Execute a function on all CPUs, with caches flushed and disabled. - [PURPOSE] This function will synchronise all CPUs, flush and disable caches - on all CPUs, then call a specified function. When the specified function - finishes on all CPUs, caches are enabled on all CPUs. - The function to execute. - An arbitrary information pointer which is passed to <>. - If TRUE <> is executed locally. - [RETURNS] Nothing. -*/ +} /* End Function ipi_handler */ + +static void set_mtrr_smp (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) { - unsigned long timeout; + struct set_mtrr_data data; struct set_mtrr_context ctxt; - mtrr_hook = sync_handler; - handler_func = handler; - handler_info = info; + data.smp_reg = reg; + data.smp_base = base; + data.smp_size = size; + data.smp_type = type; wait_barrier_execute = TRUE; wait_barrier_cache_enable = TRUE; - /* Send a message to all other CPUs and wait for them to enter the - barrier */ atomic_set (&undone_count, smp_num_cpus - 1); - smp_send_mtrr(); - /* Wait for it to be done */ - timeout = jiffies + JIFFIE_TIMEOUT; - while ( (atomic_read (&undone_count) > 0) && - time_before(jiffies, timeout) ) - barrier (); - if (atomic_read (&undone_count) > 0) - { + /* Flush and disable the local CPU's cache and start the ball rolling on + other CPUs */ + set_mtrr_prepare (&ctxt); + if (smp_call_function (ipi_handler, &data, 1, 0) != 0) panic ("mtrr: timed out waiting for other CPUs\n"); - } - mtrr_hook = NULL; - /* All other CPUs should be waiting for the barrier, with their caches - already flushed and disabled. Prepare for function completion - notification */ + /* Wait for all other CPUs to flush and disable their caches */ + while (atomic_read (&undone_count) > 0) barrier (); + /* Set up for completion wait and then release other CPUs to change MTRRs*/ atomic_set (&undone_count, smp_num_cpus - 1); - /* Flush and disable the local CPU's cache and release the barier, which - should cause the other CPUs to execute the function. Also execute it - locally if required */ - set_mtrr_prepare (&ctxt); wait_barrier_execute = FALSE; - if (local) (*handler) (&ctxt, info); + (*set_mtrr_up) (reg, base, size, type, FALSE); /* Now wait for other CPUs to complete the function */ while (atomic_read (&undone_count) > 0) barrier (); /* Now all CPUs should have finished the function. Release the barrier to @@ -732,41 +897,10 @@ static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt, then enable the local cache and return */ wait_barrier_cache_enable = FALSE; set_mtrr_done (&ctxt); - handler_func = NULL; - handler_info = NULL; -} /* End Function do_all_cpus */ - - -struct set_mtrr_data -{ - unsigned long smp_base; - unsigned long smp_size; - unsigned int smp_reg; - mtrr_type smp_type; -}; - -static void set_mtrr_handler (struct set_mtrr_context *ctxt, void *info) -{ - struct set_mtrr_data *data = info; - - set_mtrr_up (data->smp_reg, data->smp_base, data->smp_size, data->smp_type, - FALSE); -} /* End Function set_mtrr_handler */ - -static void set_mtrr_smp (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type) -{ - struct set_mtrr_data data; - - data.smp_reg = reg; - data.smp_base = base; - data.smp_size = size; - data.smp_type = type; - do_all_cpus (set_mtrr_handler, &data, TRUE); } /* End Function set_mtrr_smp */ -/* Some BIOS's are fucked and don't set all MTRRs the same! */ +/* Some BIOS's are fucked and don't set all MTRRs the same! */ __initfunc(static void mtrr_state_warn (unsigned long mask)) { if (!mask) return; @@ -809,6 +943,58 @@ static void init_table (void) #endif } /* End Function init_table */ +static int generic_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = get_num_var_ranges (); + for (i = 0; i < max; ++i) + { + (*get_mtrr) (i, &lbase, &lsize, <ype); + if (lsize < 1) return i; + } + return -ENOSPC; +} /* End Function generic_get_free_region */ + +static int cyrix_get_free_region (unsigned long base, unsigned long size) +/* [SUMMARY] Get a free ARR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i; + mtrr_type ltype; + unsigned long lbase, lsize; + + /* If we are to set up a region >32M then look at ARR7 immediately */ + if (size > 0x2000000UL) { + cyrix_get_arr (7, &lbase, &lsize, <ype); + if (lsize < 1) return 7; + /* else try ARR0-ARR6 first */ + } else { + for (i = 0; i < 7; i++) + { + cyrix_get_arr (i, &lbase, &lsize, <ype); + if (lsize < 1) return i; + } + /* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ + cyrix_get_arr (i, &lbase, &lsize, <ype); + if ((lsize < 1) && (size >= 0x40000)) return i; + } + return -ENOSPC; +} /* End Function cyrix_get_free_region */ + +static int (*get_free_region) (unsigned long base, + unsigned long size) = generic_get_free_region; + int mtrr_add (unsigned long base, unsigned long size, unsigned int type, char increment) /* [SUMMARY] Add an MTRR entry. @@ -826,11 +1012,44 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, mtrr_type ltype; unsigned long lbase, lsize, last; - if (!(mtrr_flags&MTRR_PRESENT)) - return -ENODEV; - - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ + if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) && + (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) ) + { + printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base); + return -EINVAL; + } + /* Fall through */ + case X86_VENDOR_CYRIX: + if ( (base & 0xfff) || (size & 0xfff) ) + { + printk ("mtrr: size and base must be multiples of 4 kiB\n"); + printk ("mtrr: size: %lx base: %lx\n", size, base); + return -EINVAL; + } + if (base + size < 0x100000) + { + printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n", + base, size); + return -EINVAL; + } + /* Check upper bits of base and last are equal and lower bits are 0 + for base and 1 for last */ + last = base + size - 1; + for (lbase = base; !(lbase & 1) && (last & 1); + lbase = lbase >> 1, last = last >> 1); + if (lbase != last) + { + printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n", + base, size); + return -EINVAL; + } + break; + case X86_VENDOR_AMD: /* Apply the K6 block alignment and size rules In order o Uncached or gathering only @@ -838,54 +1057,32 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, o Power of 2 block o base suitably aligned to the power */ - if(type > 1 || size < (1<<17) || (size & ~(size-1))-size || (base&(size-1))) - return -EINVAL; + if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) || + (size & ~(size-1))-size || (base & (size-1))) + return -EINVAL; + break; + default: + return -EINVAL; + /*break;*/ } - - if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) + if (type >= MTRR_NUM_TYPES) { - if ( (base & 0xfff) || (size & 0xfff) ) - { - printk ("mtrr: size and base must be multiples of 4kB\n"); - printk ("mtrr: size: %lx base: %lx\n", size, base); - return -EINVAL; - } - if (base + size < 0x100000) - { - printk ("mtrr: cannot set region below 1 MByte (0x%lx,0x%lx)\n", - base, size); - return -EINVAL; - } - /* Check upper bits of base and last are equal and lower bits are 0 for - base and 1 for last */ - last = base + size - 1; - for (lbase = base; !(lbase & 1) && (last & 1); - lbase = lbase >> 1, last = last >> 1); - if (lbase != last) - { - printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n", - base, size); - return -EINVAL; - } - if (type >= MTRR_NUM_TYPES) - { - printk ("mtrr: type: %u illegal\n", type); - return -EINVAL; - } - /* If the type is WC, check that this processor supports it */ - if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) - { - printk ("mtrr: your processor doesn't support write-combining\n"); - return -EINVAL; - } + printk ("mtrr: type: %u illegal\n", type); + return -EINVAL; + } + /* If the type is WC, check that this processor supports it */ + if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) + { + printk ("mtrr: your processor doesn't support write-combining\n"); + return -ENOSYS; } increment = increment ? 1 : 0; max = get_num_var_ranges (); /* Search for existing MTRR */ - spin_lock (&main_lock); + spin_lock_reschedule (&main_lock); for (i = 0; i < max; ++i) { - get_mtrr (i, &lbase, &lsize, <ype); + (*get_mtrr) (i, &lbase, &lsize, <ype); if (base >= lbase + lsize) continue; if ( (base < lbase) && (base + size <= lbase) ) continue; /* At this point we know there is some kind of overlap/enclosure */ @@ -911,19 +1108,18 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, return i; } /* Search for an empty MTRR */ - for (i = 0; i < max; ++i) + i = (*get_free_region) (base, size); + if (i < 0) { - get_mtrr (i, &lbase, &lsize, <ype); - if (lsize > 0) continue; - set_mtrr (i, base, size, type); - usage_table[i] = 1; - compute_ascii (); spin_unlock (&main_lock); + printk ("mtrr: no more MTRRs available\n"); return i; } + set_mtrr (i, base, size, type); + usage_table[i] = 1; + compute_ascii (); spin_unlock (&main_lock); - printk ("mtrr: no more MTRRs available\n"); - return -ENOSPC; + return i; } /* End Function mtrr_add */ int mtrr_del (int reg, unsigned long base, unsigned long size) @@ -941,15 +1137,15 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) mtrr_type ltype; unsigned long lbase, lsize; - if ( !(mtrr_flags&MTRR_PRESENT) ) return -ENODEV; + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; max = get_num_var_ranges (); - spin_lock (&main_lock); + spin_lock_reschedule (&main_lock); if (reg < 0) { /* Search for existing MTRR */ for (i = 0; i < max; ++i) { - get_mtrr (i, &lbase, &lsize, <ype); + (*get_mtrr) (i, &lbase, &lsize, <ype); if ( (lbase == base) && (lsize == size) ) { reg = i; @@ -969,7 +1165,7 @@ int mtrr_del (int reg, unsigned long base, unsigned long size) printk ("mtrr: register: %d too big\n", reg); return -EINVAL; } - get_mtrr (reg, &lbase, &lsize, <ype); + (*get_mtrr) (reg, &lbase, &lsize, <ype); if (lsize < 1) { spin_unlock (&main_lock); @@ -1020,7 +1216,9 @@ static int mtrr_file_del (unsigned long base, unsigned long size, reg = mtrr_del (-1, base, size); if (reg < 0) return reg; - if (fcount != NULL) --fcount[reg]; + if (fcount == NULL) return reg; + if (fcount[reg] < 1) return -EINVAL; + --fcount[reg]; return reg; } /* End Function mtrr_file_del */ @@ -1126,11 +1324,18 @@ static int mtrr_ioctl (struct inode *inode, struct file *file, err = mtrr_file_del (sentry.base, sentry.size, file); if (err < 0) return err; break; + case MTRRIOC_KILL_ENTRY: + if ( !suser () ) return -EPERM; + if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) + return -EFAULT; + err = mtrr_del (-1, sentry.base, sentry.size); + if (err < 0) return err; + break; case MTRRIOC_GET_ENTRY: if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) return -EFAULT; if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; - get_mtrr (gentry.regnum, &gentry.base, &gentry.size, &type); + (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); gentry.type = type; if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) return -EFAULT; @@ -1222,7 +1427,7 @@ static void compute_ascii (void) max = get_num_var_ranges (); for (i = 0; i < max; i++) { - get_mtrr (i, &base, &size, &type); + (*get_mtrr) (i, &base, &size, &type); if (size < 1) usage_table[i] = 0; else { @@ -1255,28 +1460,165 @@ EXPORT_SYMBOL(mtrr_del); #ifdef __SMP__ +typedef struct { + unsigned long base; + unsigned long size; + mtrr_type type; +} arr_state_t; + +arr_state_t arr_state[8] __initdata = { + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, + {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} +}; + +unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; + +__initfunc(static void cyrix_arr_init_secondary(void)) +{ + struct set_mtrr_context ctxt; + int i; + + set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + + /* the CCRs are not contiguous */ + for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]); + for( ; i<7; i++) setCx86(CX86_CCR4 + i, ccr_state[i]); + for(i=0; i<8; i++) + cyrix_set_arr_up(i, + arr_state[i].base, arr_state[i].size, arr_state[i].type, FALSE); + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ +} /* End Function cyrix_arr_init_secondary */ + +#endif + +/* + * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection + * with the SMM (System Management Mode) mode. So we need the following: + * Check whether SMI_LOCK (CCR3 bit 0) is set + * if it is set, write a warning message: ARR3 cannot be changed! + * (it cannot be changed until the next processor reset) + * if it is reset, then we can change it, set all the needed bits: + * - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) + * - disable access to SMM memory (CCR1 bit 2 reset) + * - disable SMM mode (CCR1 bit 1 reset) + * - disable write protection of ARR3 (CCR6 bit 1 reset) + * - (maybe) disable ARR3 + * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) + */ +__initfunc(static void cyrix_arr_init(void)) +{ + struct set_mtrr_context ctxt; + unsigned char ccr[7]; + int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; +#ifdef __SMP__ + int i; +#endif + + set_mtrr_prepare (&ctxt); /* flush cache and enable MAPEN */ + + /* Save all CCRs locally */ + ccr[0] = getCx86 (CX86_CCR0); + ccr[1] = getCx86 (CX86_CCR1); + ccr[2] = getCx86 (CX86_CCR2); + ccr[3] = ctxt.ccr3; + ccr[4] = getCx86 (CX86_CCR4); + ccr[5] = getCx86 (CX86_CCR5); + ccr[6] = getCx86 (CX86_CCR6); + + if (ccr[3] & 1) + ccrc[3] = 1; + else { + /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and + * access to SMM memory through ARR3 (bit 7). + */ +/* + if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } + if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } + if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } +*/ + if (ccr[6] & 0x02) { + ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3. */ + setCx86 (CX86_CCR6, ccr[6]); + } + /* Disable ARR3. */ + /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ + } + /* If we changed CCR1 in memory, change it in the processor, too. */ + if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]); + + /* Enable ARR usage by the processor */ + if (!(ccr[5] & 0x20)) { + ccr[5] |= 0x20; ccrc[5] = 1; + setCx86 (CX86_CCR5, ccr[5]); + } + +#ifdef __SMP__ + for(i=0; i<7; i++) ccr_state[i] = ccr[i]; + for(i=0; i<8; i++) + cyrix_get_arr(i, + &arr_state[i].base, &arr_state[i].size, &arr_state[i].type); +#endif + + set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ + + if ( ccrc[5] ) printk ("mtrr: ARR usage was not enabled, enabled manually\n"); + if ( ccrc[3] ) printk ("mtrr: ARR3 cannot be changed\n"); +/* + if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n"); + if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n"); + if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n"); +*/ + if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n"); +} /* End Function cyrix_arr_init */ + +__initfunc(static void mtrr_setup (void)) +{ + printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + get_mtrr = intel_get_mtrr; + set_mtrr_up = intel_set_mtrr_up; + break; + case X86_VENDOR_CYRIX: + printk ("mtrr: Using Cyrix style ARRs\n"); + get_mtrr = cyrix_get_arr; + set_mtrr_up = cyrix_set_arr_up; + get_free_region = cyrix_get_free_region; + break; + case X86_VENDOR_AMD: + get_mtrr = amd_get_mtrr; + set_mtrr_up = amd_set_mtrr_up; + break; + } +} /* End Function mtrr_setup */ + +#ifdef __SMP__ + static volatile unsigned long smp_changes_mask __initdata = 0; static struct mtrr_state smp_mtrr_state __initdata = {0, 0}; __initfunc(void mtrr_init_boot_cpu (void)) { - printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); - if (boot_cpu_data.x86_capability & X86_FEATURE_MTRR) - { - get_mtrr_state (&smp_mtrr_state); - } - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && k6_has_ranges()) + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; + mtrr_setup (); + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: get_mtrr_state (&smp_mtrr_state); + break; + case X86_VENDOR_CYRIX: + cyrix_arr_init (); + break; } } /* End Function mtrr_init_boot_cpu */ -__initfunc(void mtrr_init_secondary_cpu (void)) +__initfunc(static void intel_mtrr_init_secondary_cpu (void)) { unsigned long mask, count; struct set_mtrr_context ctxt; - if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; /* Note that this is not ideal, since the cache is only flushed/disabled for this CPU while the MTRRs are changed, but changing this requires more invasive changes to the way the kernel boots */ @@ -1289,53 +1631,57 @@ __initfunc(void mtrr_init_secondary_cpu (void)) if (mask & 0x01) set_bit (count, &smp_changes_mask); mask >>= 1; } -} /* End Function mtrr_init_secondary_cpu */ +} /* End Function intel_mtrr_init_secondary_cpu */ -#endif /* __SMP__ */ - -/* - * The extended memory handling is available on the K6-III and the - * K6-II stepping 8 and higher only. - */ - -static int k6_has_ranges(void) +__initfunc(void mtrr_init_secondary_cpu (void)) { - if(boot_cpu_data.x86 !=5) - return 0; - if(boot_cpu_data.x86_model == 9 || - (boot_cpu_data.x86_model == 8 && - boot_cpu_data.x86_mask >= 8)) - return 1; - return 0; -} + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + intel_mtrr_init_secondary_cpu (); + break; + case X86_VENDOR_CYRIX: + /* This is _completely theoretical_! + * I assume here that one day Cyrix will support Intel APIC. + * In reality on non-Intel CPUs we won't even get to this routine. + * Hopefully no one will plug two Cyrix processors in a dual P5 board. + * :-) + */ + cyrix_arr_init_secondary (); + break; + default: + printk ("mtrr: SMP support incomplete for this vendor\n"); + break; + } +} /* End Function mtrr_init_secondary_cpu */ +#endif /* __SMP__ */ __initfunc(int mtrr_init(void)) { - if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD && k6_has_ranges()) + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0; +# ifdef __SMP__ + switch (boot_cpu_data.x86_vendor) { -# ifdef CONFIG_PROC_FS - proc_register (&proc_root, &proc_root_mtrr); -# endif - mtrr_flags|=MTRR_PRESENT|MTRR_WRCOMB; - init_table(); - return 0; + case X86_VENDOR_INTEL: + finalize_mtrr_state (&smp_mtrr_state); + mtrr_state_warn (smp_changes_mask); + break; } - if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) - return 0; -# ifndef __SMP__ - printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); -# endif - -# ifdef __SMP__ - finalize_mtrr_state (&smp_mtrr_state); - mtrr_state_warn (smp_changes_mask); -# endif /* __SMP__ */ +# else /* __SMP__ */ + mtrr_setup (); + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_CYRIX: + cyrix_arr_init (); + break; + } +# endif /* !__SMP__ */ # ifdef CONFIG_PROC_FS proc_register (&proc_root, &proc_root_mtrr); # endif - mtrr_flags|=MTRR_PRESENT; init_table (); return 0; } /* End Function mtrr_init */ diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 41cf2c9e1002..af6df1065f31 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -5,6 +5,10 @@ * * Enhanced CPU type detection by Mike Jagdis, Patrick St. Jean * and Martin Mares, November 1997. + * + * Force Cyrix 6x86(MX) and M II processors to report MTRR capability + * and fix against Cyrix "coma bug" by + * Zoltan Boszormenyi February 1999. */ /* @@ -395,6 +399,14 @@ __initfunc(static int get_model_name(struct cpuinfo_x86 *c)) cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); c->x86_model_id[48] = 0; + /* Set MTRR capability flag if appropriate */ + if(boot_cpu_data.x86 !=5) + return 1; + if((boot_cpu_data.x86_model == 9) || + ((boot_cpu_data.x86_model == 8) && + (boot_cpu_data.x86_mask >= 8))) + c->x86_capability |= X86_FEATURE_MTRR; + return 1; } @@ -574,6 +586,10 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) (c->x86_model)++; } else /* 686 */ p = Cx86_cb+1; + /* Emulate MTRRs using Cyrix's ARRs. */ + c->x86_capability |= X86_FEATURE_MTRR; + /* 6x86's contain this bug */ + c->coma_bug = 1; break; case 4: /* MediaGX/GXm */ @@ -598,11 +614,14 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) case 5: /* 6x86MX/M II */ if (dir1 > 7) dir0_msn++; /* M II */ + else c->coma_bug = 1; /* 6x86MX, it has the bug. */ tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0; Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7]; p = Cx86_cb+tmp; if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20)) (c->x86_model)++; + /* Emulate MTRRs using Cyrix's ARRs. */ + c->x86_capability |= X86_FEATURE_MTRR; break; case 0xf: /* Cyrix 486 without DEVID registers */ @@ -856,7 +875,7 @@ int get_cpuinfo(char * buffer) int sep_bug; static char *x86_cap_flags[] = { "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce", - "cx8", "9", "10", "sep", "12", "pge", "14", "cmov", + "cx8", "9", "10", "sep", "mtrr", "pge", "14", "cmov", "16", "17", "psn", "19", "20", "21", "22", "mmx", "24", "kni", "26", "27", "28", "29", "30", "31" }; @@ -904,7 +923,6 @@ int get_cpuinfo(char * buffer) } else if (c->x86_vendor == X86_VENDOR_INTEL) { x86_cap_flags[6] = "pae"; x86_cap_flags[9] = "apic"; - x86_cap_flags[12] = "mtrr"; x86_cap_flags[14] = "mca"; x86_cap_flags[16] = "pat"; x86_cap_flags[17] = "pse36"; @@ -923,6 +941,7 @@ int get_cpuinfo(char * buffer) "hlt_bug\t\t: %s\n" "sep_bug\t\t: %s\n" "f00f_bug\t: %s\n" + "coma_bug\t: %s\n" "fpu\t\t: %s\n" "fpu_exception\t: %s\n" "cpuid level\t: %d\n" @@ -932,6 +951,7 @@ int get_cpuinfo(char * buffer) c->hlt_works_ok ? "no" : "yes", sep_bug ? "yes" : "no", c->f00f_bug ? "yes" : "no", + c->coma_bug ? "yes" : "no", c->hard_math ? "yes" : "no", (c->hard_math && ignore_irq13) ? "yes" : "no", c->cpuid_level, diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 155d66f69010..4d13635aed5c 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -44,6 +44,8 @@ #include "irq.h" +#define JIFFIE_TIMEOUT 100 + extern void update_one_process( struct task_struct *p, unsigned long ticks, unsigned long user, unsigned long system, int cpu); @@ -1651,15 +1653,84 @@ void smp_send_stop(void) send_IPI_allbutself(STOP_CPU_VECTOR); } +/* Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +struct smp_call_function_struct { + void (*func) (void *info); + void *info; + atomic_t unstarted_count; + atomic_t unfinished_count; + int wait; +}; +static volatile struct smp_call_function_struct *smp_call_function_data = NULL; + /* - * this function sends an 'reload MTRR state' IPI to all other CPUs - * in the system. it goes straight through, completion processing - * is done on the mttr.c level. + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. */ -void smp_send_mtrr(void) +int smp_call_function (void (*func) (void *info), void *info, int retry, + int wait) +/* [SUMMARY] Run a function on all other CPUs. + The function to run. This must be fast and non-blocking. + An arbitrary pointer to pass to the function. + If true, keep retrying until ready. + If true, wait until function has completed on other CPUs. + [RETURNS] 0 on success, else a negative status code. Does not return until + remote CPUs are nearly ready to execute <> or are or have executed. +*/ { - send_IPI_allbutself(MTRR_CHANGE_VECTOR); + unsigned long timeout; + struct smp_call_function_struct data; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + + if (retry) { + while (1) { + if (smp_call_function_data) { + schedule (); /* Give a mate a go */ + continue; + } + spin_lock (&lock); + if (smp_call_function_data) { + spin_unlock (&lock); /* Bad luck */ + continue; + } + /* Mine, all mine! */ + break; + } + } + else { + if (smp_call_function_data) return -EBUSY; + spin_lock (&lock); + if (smp_call_function_data) { + spin_unlock (&lock); + return -EBUSY; + } + } + smp_call_function_data = &data; + spin_unlock (&lock); + data.func = func; + data.info = info; + atomic_set (&data.unstarted_count, smp_num_cpus - 1); + data.wait = wait; + if (wait) atomic_set (&data.unfinished_count, smp_num_cpus - 1); + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself (CALL_FUNCTION_VECTOR); + /* Wait for response */ + timeout = jiffies + JIFFIE_TIMEOUT; + while ( (atomic_read (&data.unstarted_count) > 0) && + time_before (jiffies, timeout) ) + barrier (); + if (atomic_read (&data.unstarted_count) > 0) { + smp_call_function_data = NULL; + return -ETIMEDOUT; + } + if (wait) + while (atomic_read (&data.unfinished_count) > 0) + barrier (); + smp_call_function_data = NULL; + return 0; } /* @@ -1798,12 +1869,19 @@ asmlinkage void smp_stop_cpu_interrupt(void) stop_this_cpu(); } -void (*mtrr_hook) (void) = NULL; - -asmlinkage void smp_mtrr_interrupt(void) +asmlinkage void smp_call_function_interrupt(void) { - ack_APIC_irq(); - if (mtrr_hook) (*mtrr_hook)(); + void (*func) (void *info) = smp_call_function_data->func; + void *info = smp_call_function_data->info; + int wait = smp_call_function_data->wait; + + ack_APIC_irq (); + /* Notify initiating CPU that I've grabbed the data and am about to + execute the function */ + atomic_dec (&smp_call_function_data->unstarted_count); + /* At this point the structure may be out of scope unless wait==1 */ + (*func) (info); + if (wait) atomic_dec (&smp_call_function_data->unfinished_count); } /* diff --git a/drivers/Makefile b/drivers/Makefile index eef043f28559..43b3f1ad4264 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -47,7 +47,11 @@ endif ifeq ($(CONFIG_USB),y) SUB_DIRS += usb MOD_SUB_DIRS += usb -endif +else + ifeq ($(CONFIG_USB),m) + MOD_SUB_DIRS += usb + endif +endif # If CONFIG_SCSI is set, the core of SCSI support will be added to the kernel, # but some of the low-level things may also be modules. diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 34aa31d7dba3..996c15019520 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -202,9 +202,7 @@ struct lp_struct lp_table[LP_NO] = /* Test if the printer is not acking the strobe */ #define LP_NO_ACKING(status) ((status) & LP_PACK) /* Test if the printer has error conditions */ -#define LP_NO_ERROR(status) \ - (((status) & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \ - (LP_PSELECD|LP_PERRORP)) +#define LP_NO_ERROR(status) ((status) & LP_PERRORP) #undef LP_DEBUG #undef LP_READ_DEBUG @@ -424,7 +422,10 @@ static int lp_check_status(int minor) { unsigned int last = lp_table[minor].last_error; unsigned char status = r_str(minor); - if ((status & LP_POUTPA)) { + if (status & LP_PERRORP) + /* No error. */ + last = 0; + else if ((status & LP_POUTPA)) { if (last != LP_POUTPA) { last = LP_POUTPA; printk(KERN_INFO "lp%d out of paper\n", minor); @@ -434,13 +435,12 @@ static int lp_check_status(int minor) last = LP_PSELECD; printk(KERN_INFO "lp%d off-line\n", minor); } - } else if (!(status & LP_PERRORP)) { + } else { if (last != LP_PERRORP) { last = LP_PERRORP; - printk(KERN_INFO "lp%d on fire!\n", minor); + printk(KERN_INFO "lp%d on fire\n", minor); } } - else last = 0; lp_table[minor].last_error = last; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index c267027f87af..47ae0706f6aa 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -57,7 +57,10 @@ int uhci_init(void); #ifdef CONFIG_USB_OHCI int ohci_init(void); #endif - +#ifdef CONFIG_USB_OHCI_HCD +int ohci_hcd_init(void); +#endif + static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, const char * buf, size_t count, loff_t *ppos) { @@ -612,6 +615,9 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_USB_OHCI ohci_init(); #endif +#ifdef CONFIG_USB_OHCI_HCD + ohci_hcd_init(); +#endif #endif #if defined (CONFIG_FB) fbmem_init(); diff --git a/drivers/char/planb.c b/drivers/char/planb.c index 8fd09da3107f..0f2d24e8bbe4 100644 --- a/drivers/char/planb.c +++ b/drivers/char/planb.c @@ -29,7 +29,6 @@ /* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */ -#include #include #include #include diff --git a/drivers/misc/parport_ieee1284.c b/drivers/misc/parport_ieee1284.c index f37a2e8663b1..91f6c262b939 100644 --- a/drivers/misc/parport_ieee1284.c +++ b/drivers/misc/parport_ieee1284.c @@ -48,25 +48,22 @@ int parport_wait_peripheral(struct parport *port, unsigned char mask, int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) { /* make sure it's a valid state, set nStrobe & nAutoFeed high */ - parport_write_control(port, (parport_read_control(port) \ - & ~1 ) & ~2); + parport_frob_control (port, (1|2), 0); udelay(1); parport_write_data(port, mode); udelay(400); /* nSelectIn high, nAutoFd low */ - parport_write_control(port, (parport_read_control(port) & ~8) | 2); + parport_frob_control(port, (2|8), 2); if (parport_wait_peripheral(port, 0x78, 0x38)) { - parport_write_control(port, - (parport_read_control(port) & ~2) | 8); + parport_frob_control(port, (2|8), 8); return 0; } /* nStrobe low */ - parport_write_control(port, parport_read_control(port) | 1); + parport_frob_control (port, 1, 1); udelay(1); /* Strobe wait */ /* nStrobe high, nAutoFeed low, last step before transferring * reverse data */ - parport_write_control(port, (parport_read_control(port) \ - & ~1) & ~2); + parport_frob_control (port, (1|2), 0); udelay(1); /* Data available? */ parport_wait_peripheral (port, PARPORT_STATUS_ACK, PARPORT_STATUS_ACK); diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c index c12d5017776c..8279c075b06e 100644 --- a/drivers/misc/parport_pc.c +++ b/drivers/misc/parport_pc.c @@ -53,6 +53,8 @@ than PARPORT_MAX (in ). */ #define PARPORT_PC_MAX_PORTS 8 +static int user_specified = 0; + static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { parport_generic_irq(irq, (struct parport *) dev_id, regs); @@ -103,19 +105,24 @@ unsigned char parport_pc_read_data(struct parport *p) void parport_pc_write_control(struct parport *p, unsigned char d) { + struct parport_pc_private *priv = p->private_data; + priv->ctr = d;/* update soft copy */ outb(d, p->base+CONTROL); } unsigned char parport_pc_read_control(struct parport *p) { - return inb(p->base+CONTROL); + struct parport_pc_private *priv = p->private_data; + return priv->ctr; } unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask, unsigned char val) { - unsigned char old = inb(p->base+CONTROL); - outb(((old & ~mask) ^ val), p->base+CONTROL); - return old; + struct parport_pc_private *priv = p->private_data; + unsigned char ctr = priv->ctr; + ctr = (ctr & ~mask) ^ val; + outb (ctr, p->base+CONTROL); + return priv->ctr = ctr; /* update soft copy */ } void parport_pc_write_status(struct parport *p, unsigned char d) @@ -345,6 +352,8 @@ int parport_pc_epp_clear_timeout(struct parport *pb) */ static int parport_SPP_supported(struct parport *pb) { + unsigned char r, w; + /* * first clear an eventually pending EPP timeout * I (sailer@ife.ee.ethz.ch) have an SMSC chipset @@ -354,14 +363,54 @@ static int parport_SPP_supported(struct parport *pb) parport_pc_epp_clear_timeout(pb); /* Do a simple read-write test to make sure the port exists. */ - parport_pc_write_control(pb, 0xc); - parport_pc_write_data(pb, 0xaa); - if (parport_pc_read_data(pb) != 0xaa) return 0; - - parport_pc_write_data(pb, 0x55); - if (parport_pc_read_data(pb) != 0x55) return 0; + w = 0xc; + parport_pc_write_control(pb, w); + + /* Can we read from the control register? Some ports don't + * allow reads, so read_control just returns a software + * copy. Some ports _do_ allow reads, so bypass the software + * copy here. In addition, some bits aren't writable. */ + r = inb (pb->base+CONTROL); + if ((r & 0x3f) == w) { + w = 0xe; + parport_pc_write_control (pb, w); + r = inb (pb->base+CONTROL); + parport_pc_write_control (pb, 0xc); + if ((r & 0x3f) == w) + return PARPORT_MODE_PCSPP; + } - return PARPORT_MODE_PCSPP; + if (user_specified) + /* That didn't work, but the user thinks there's a + * port here. */ + printk (KERN_DEBUG "0x%lx: CTR: wrote 0x%02x, read 0x%02x\n", + pb->base, w, r); + + /* Try the data register. The data lines aren't tri-stated at + * this stage, so we expect back what we wrote. */ + w = 0xaa; + parport_pc_write_data (pb, w); + r = parport_pc_read_data (pb); + if (r == w) { + w = 0x55; + parport_pc_write_data (pb, w); + r = parport_pc_read_data (pb); + if (r == w) + return PARPORT_MODE_PCSPP; + } + + if (user_specified) + /* Didn't work with 0xaa, but the user is convinced + * this is the place. */ + printk (KERN_DEBUG "0x%lx: DATA: wrote 0x%02x, read 0x%02x\n", + pb->base, w, r); + + /* It's possible that we can't read the control register or + the data register. In that case just believe the user. */ + if (user_specified) + return PARPORT_MODE_PCSPP; + + return 0; } /* Check for ECP @@ -712,6 +761,15 @@ static int probe_one_port(unsigned long int base, int irq, int dma) if (check_region(base, 3)) return 0; if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops))) return 0; + p->private_data = kmalloc (sizeof (struct parport_pc_private), + GFP_KERNEL); + if (!p->private_data) { + /* Not enough memory. */ + printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base); + parport_unregister_port (p); + return 0; + } + ((struct parport_pc_private *) (p->private_data))->ctr = 0xc; if (p->base != 0x3bc) { if (!check_region(base+0x400,3)) { p->modes |= parport_ECR_present(p); @@ -725,6 +783,7 @@ static int probe_one_port(unsigned long int base, int irq, int dma) } if (!parport_SPP_supported(p)) { /* No port. */ + kfree (p->private_data); parport_unregister_port (p); return 0; } @@ -787,6 +846,7 @@ int parport_pc_init(int *io, int *irq, int *dma) int count = 0, i = 0; if (io && *io) { /* Only probe the ports we were given. */ + user_specified = 1; do { count += probe_one_port(*(io++), *(irq++), *(dma++)); } while (*io && (++i < PARPORT_PC_MAX_PORTS)); @@ -829,6 +889,7 @@ void cleanup_module(void) if (!(p->flags & PARPORT_FLAG_COMA)) parport_quiesce(p); parport_proc_unregister(p); + kfree (p->private_data); parport_unregister_port(p); } p = tmp; diff --git a/drivers/net/ethertap.c b/drivers/net/ethertap.c index 46b7babd4d33..7e98d50e23cb 100644 --- a/drivers/net/ethertap.c +++ b/drivers/net/ethertap.c @@ -98,6 +98,7 @@ __initfunc(int ethertap_probe(struct device *dev)) ether_setup(dev); + dev->hard_header_len = 16; dev->tx_queue_len = 0; dev->flags|=IFF_NOARP; tap_map[dev->base_addr]=dev; diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 669478cd0a6a..632e3d08a5a8 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -16,6 +16,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ ! "$CONFIG_USB" = "n" ]; then bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI + bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD + if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then + bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB + fi bool 'USB mouse support' CONFIG_USB_MOUSE bool 'USB keyboard support' CONFIG_USB_KBD diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 0d3eadacad07..6c6e7b091cc0 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -59,7 +59,17 @@ ifeq ($(CONFIG_USB_OHCI),y) endif endif - +ifeq ($(CONFIG_USB_OHCI_HCD),y) + ifeq ($(CONFIG_USB), y) + L_OBJS += ohci-hcd.o ohci-root-hub.o + else + ifeq ($(CONFIG_USB),m) + USBO_OBJS += ohci-hcd.o ohci-root-hub.o + M_OBJS += usb-ohci-hcd.o + MIX_OBJS += $(USBX_OBJS) + endif + endif +endif include $(TOPDIR)/Rules.make keymap.o: keymap.c @@ -73,3 +83,6 @@ usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS) usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS) $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS) +usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) + $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) + diff --git a/drivers/usb/README.ohci_hcd b/drivers/usb/README.ohci_hcd new file mode 100644 index 000000000000..53bd35c955b3 --- /dev/null +++ b/drivers/usb/README.ohci_hcd @@ -0,0 +1,112 @@ + +The OHCI HCD layer is a simple but nearly complete implementation of what the +USB people would call a HCD for the OHCI. + (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) +It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree). +The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers. + +- Roman Weissgaerber + + + * v2.1 1999/05/09 ep_addr correction, code cleanup + * v0.2.0 1999/05/04 + * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers) + * virtual root hub is now an option, + * memory allocation based on kmalloc and kfree now, simple Bus error handling, + * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion + * + * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff + * from Greg Smith (ohci.c): better reset ohci-controller handling, hub + * + * v0.1.0 1999/04/27 initial release + +to remove the module try: +killall root-hub +: +rmmod usb-ohci-hcd + +Features: +- virtual root hub, all basic hub descriptors and commands (state: complete) + this is an option now (v0.2.0) + #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB) + default is without. + (at the moment: the Virtual Root Hub option is not recommended) + + files: ohci-root-hub.c, ohci-root-hub.h + + +- Endpoint Descriptor (ED) handling more static approach + (EDs should be allocated in parallel to the SET CONFIGURATION command and they live + as long as the function (device) is alive or another configuration is choosen. + In the HCD layer the EDs has to be allocated manually either by calling a subroutine + or by sending a USB root hub vendor specific command to the virtual root hub. + At the alternate linux usb stack EDs will be added (allocated) at their first use. + + files: ohci-hcd.c ohci-hcd.h + routines: (do not use for drivers, use the top layer alternate usb commands instead) + + int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1, + int interval, int load, f_handler handler, int ep_size, int speed) + adds an endpoint, (if the endpoint already exists some parameters will be updated) + + int usb_ohci_rm_ep(struct usb_ohci_ed *ed, struct ohci * ohci) + removes an endpoint and all pending TDs of that EP + + usb_ohci_rm_function( struct ohci * ohci, union ep_addr_ ep_addr) + removes all Endpoints of a function (device) + +- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers + The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has + to take care of buffer allocation. + files: ohci-hcd.c ohci-hcd.h + + There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL): + + int ohci_trans_req(struct ohci * ohci, int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) + + CTRL: ctrl, ctrl_len ... cmd buffer + data, data_len ... data buffer (in or out) + INT, BULK: ctrl = NULL, ctrl_len=0, + data, data_len ... data buffer (in or out) + ISO: tbd + + There is no buffer reinsertion done by the internal HCD function. + (The interface layer does this for a INT-pipe on request.) + If you want a transfer then you have to + provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED + you can send as many as you like. They should come back by the callback f_handler in + the same order (for each endpoint, not globally) If an error occurs all + queued transfers of an endpoint will return unsent. They will be marked with an error status. + + e.g double-buffering for int transfers: + + ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0) + ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0) + + and when a data0 packet returns by the callback f_handler requeue it: + ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0) + and when a data1 packet returns by the callback f_handler requeue it: + ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0) + + lw0, lw1 are private fields for upper layers for ids or fine grained handlers. + The alternate usb uses them for dev_id and usb_device_irq handler. + + +- Done list handling: returns the requests (callback f_handler in ED) and does + some error handling, root-hub request dequeuing + (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0)) + +ep_addr union or int is for addressing devices&endpoints: +__u8 ep_addr.bep.ep ... bit 3..0 endpoint address + bit 4 0 + bit 6,5 type: eg. 10 CTRL, 11 BULK, 01 INT, 00 ISO + bit 7 direction 1 IN, 0 OUT + +__u8 ep_addr.bep.fa ... bit 6..0 function address + bit 7 0 + +(__u8 ep_addr.bep.hc ... host controller nr) not used +(__u8 ep_addr.bep.host ... host nr) not used + + + diff --git a/drivers/usb/ohci-hcd.c b/drivers/usb/ohci-hcd.c new file mode 100644 index 000000000000..9ee863fcd911 --- /dev/null +++ b/drivers/usb/ohci-hcd.c @@ -0,0 +1,1488 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * + * The OHCI HCD layer is a simple but nearly complete implementation of what the + * USB people would call a HCD for the OHCI. + * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) + * The layer on top of it, is for interfacing to the alternate-usb device-drivers. + * + * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ] + * [ Open Host Controller Interface driver for USB. ] + * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] + * [ (C) Copyright 1999 Gregory P. Smith ] + * [ $Log: ohci.c,v $ ] + * [ Revision 1.1 1999/04/05 08:32:30 greg ] + * + * + * v2.1 1999/05/09 ep_addr correction, code clean up + * v2.0 1999/05/04 + * virtual root hub is now an option, + * memory allocation based on kmalloc and kfree now, Bus error handling, + * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion + * + * from Linus Torvalds (uhci.c) (APM not tested; hub, usb_device, bus and related stuff) + * from Greg Smith (ohci.c) (reset controller handling, hub) + * + * v1.0 1999/04/27 initial release + * ohci-hcd.c + */ + +/* #define OHCI_DBG */ /* printk some debug information */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "usb.h" +#include "ohci-hcd.h" +#include "inits.h" + + + +#ifdef CONFIG_APM +#include +static int handle_apm_event(apm_event_t event); +static int apm_resume = 0; +#endif + + + +static struct wait_queue *control_wakeup; +static struct wait_queue *root_hub = NULL; + +static __u8 cc_to_status[16] = { /* mapping of the OHCI CC to the UHCI status codes; first guess */ +/* Activ, Stalled, Data Buffer Err, Babble Detected : NAK recvd, CRC/Timeout, Bitstuff, reservd */ +/* No Error */ 0x00, +/* CRC Error */ 0x04, +/* Bit Stuff */ 0x02, +/* Data Togg */ 0x40, +/* Stall */ 0x40, +/* DevNotResp */ 0x04, +/* PIDCheck */ 0x04, +/* UnExpPID */ 0x40, +/* DataOver */ 0x20, +/* DataUnder */ 0x20, +/* reservd */ 0x40, +/* reservd */ 0x40, +/* BufferOver */ 0x20, +/* BuffUnder */ 0x20, +/* Not Access */ 0x80, +/* Not Access */ 0x80 + }; + + +/******** + **** Interface functions + ***********************************************/ + +static int sohci_int_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) +{ + + struct ohci * ohci = ohci_in; + usb_device_irq handler=(void *) lw0; + void *dev_id = (void *) lw1; + int ret; + + OHCI_DEBUG({ int i; printk("USB HC IRQ <<<: %x: data(%d):", ep_addr, data_len);) + OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) + OHCI_DEBUG( printk(" ret_status: %x\n", status); }) + + ret = handler(cc_to_status[status & 0xf], data, dev_id); + if(ret == 0) return 0; /* 0 .. do not requeue */ + if(status > 0) return -1; /* error occured do not requeue ? */ + ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */ + return 0; +} + +static int sohci_ctrl_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw) +{ + *(int * )lw0 = status; + wake_up(&control_wakeup); + + OHCI_DEBUG( { int i; printk("USB HC CTRL<<<: %x: ctrl(%d):", ep_addr, ctrl_len);) + OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) ctrl)[i]);) + OHCI_DEBUG( printk(" data(%d):", data_len);) + OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) + OHCI_DEBUG( printk(" ret_status: %x\n", status); }) + return 0; +} + +static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) +{ + struct ohci * ohci = usb_dev->bus->hcpriv; + union ep_addr_ ep_addr; + + ep_addr.iep = 0; + ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */ + | (pipe & 0x80) /* direction */ + | (1 << 5); /* type = int*/ + ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */ + + OHCI_DEBUG( printk("USB HC IRQ >>>: %x: every %d ms\n", ep_addr.iep, period);) + + usb_ohci_add_ep(ohci, ep_addr.iep, period, 1, sohci_int_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01); + + ohci_trans_req(ohci, ep_addr.iep, 0, NULL, ((struct ohci_device *) usb_dev->hcpriv)->data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); + return 0; +} + + +static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) +{ + struct wait_queue wait = { current, NULL }; + struct ohci * ohci = usb_dev->bus->hcpriv; + int status; + union ep_addr_ ep_addr; + + ep_addr.iep = 0; + ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */ + | (pipe & 0x80) /* direction */ + | (1 << 6); /* type = ctrl*/ + ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */ + + status = 0xf; /* CC not Accessed */ + OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: %x: ctrl(%d):", ep_addr.iep, 8);) + OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) cmd)[i]);) + OHCI_DEBUG( printk(" data(%d):", len);) + OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) + OHCI_DEBUG( printk("\n"); }) + + usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_ctrl_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01); + + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&control_wakeup, &wait); + + ohci_trans_req(ohci, ep_addr.iep, 8, cmd, data, len, (__OHCI_BAG) &status, 0); + + schedule_timeout(HZ/10); + + remove_wait_queue(&control_wakeup, &wait); + + OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);) + + return cc_to_status[status & 0x0f]; +} + + +static int sohci_usb_deallocate(struct usb_device *usb_dev) { + struct ohci_device *dev = usb_to_ohci(usb_dev); + union ep_addr_ ep_addr; + + ep_addr.iep = 0; + + OHCI_DEBUG(printk("USB HC dealloc %x\n", usb_dev->devnum);) + + /* wait_ms(20); */ + + if(usb_dev->devnum >=0) { + ep_addr.bep.fa = usb_dev->devnum; + usb_ohci_rm_function(((struct ohci_device *)usb_dev->hcpriv)->ohci, ep_addr.iep); + } + + USB_FREE(dev); + USB_FREE(usb_dev); + + return 0; +} + +static struct usb_device *sohci_usb_allocate(struct usb_device *parent) { + + struct usb_device *usb_dev; + struct ohci_device *dev; + + + USB_ALLOC(usb_dev, sizeof(*usb_dev)); + if (!usb_dev) + return NULL; + + memset(usb_dev, 0, sizeof(*usb_dev)); + + USB_ALLOC(dev, sizeof(*dev)); + if (!dev) { + USB_FREE(usb_dev); + return NULL; + } + + /* Initialize "dev" */ + memset(dev, 0, sizeof(*dev)); + + usb_dev->hcpriv = dev; + dev->usb = usb_dev; + + usb_dev->parent = parent; + + if (parent) { + usb_dev->bus = parent->bus; + dev->ohci = usb_to_ohci(parent)->ohci; + } + return usb_dev; +} + +struct usb_operations sohci_device_operations = { + sohci_usb_allocate, + sohci_usb_deallocate, + sohci_control_msg, + sohci_request_irq, +}; + + +/****** + *** ED handling functions + ************************************/ + + + +/* + * search for the right place to insert an interrupt ed into the int tree + * do some load ballancing + * */ + +static int usb_ohci_int_ballance(struct ohci * ohci, int interval, int load) { + + int i,j; + + j = 0; /* search for the least loaded interrupt endpoint branch of all 32 branches */ + for(i=0; i< 32; i++) if(ohci->ohci_int_load[j] > ohci->ohci_int_load[i]) j=i; + + if(interval < 1) interval = 1; + if(interval > 32) interval = 32; + for(i= 0; ((interval >> i) > 1 ); interval &= (0xfffe << i++ )); /* interval = 2^int(ld(interval)) */ + + + for(i=j%interval; i< 32; i+=interval) ohci->ohci_int_load[i] += load; + j = interval + (j % interval); + + OHCI_DEBUG(printk("USB HC new int ed on pos : %x \n",j);) + + return j; +} + +/* get the ed from the endpoint / device adress */ + +struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in) { + +union ep_addr_ ep_addr; +struct usb_ohci_ed *tmp; +unsigned int mask; + +mask = 0; +ep_addr.iep = ep_addr_in; + +#ifdef VROOTHUB + if(ep_addr.bep.fa == ohci->root_hub_funct_addr) { + if((ep_addr.bep.ep & 0x0f) == 0) + return &ohci->ed_rh_ep0; /* root hub ep0 */ + else + return &ohci->ed_rh_epi; /* root hub int ep */ + } +#endif + + tmp = ohci->ed_func_ep0[ep_addr.bep.fa]; + mask = ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); + ep_addr.bep.ep &= mask; /* mask out direction of ctrl ep */ + + while (tmp != NULL) { + if (tmp->ep_addr.iep == ep_addr.iep) + return tmp; + tmp = tmp->ed_list; + } + return NULL; +} + +spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; +/* add a new endpoint ep_addr */ +struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr_in, int interval, int load, f_handler handler, int ep_size, int speed) { + + struct usb_ohci_ed * ed; + struct usb_ohci_td * td; + union ep_addr_ ep_addr; + + + int int_junk; + + struct usb_ohci_ed *tmp; + + ep_addr.iep = ep_addr_in ; + ep_addr.bep.ep &= ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); /* mask out direction of ctrl ep */ + + spin_lock(&usb_ed_lock); + + tmp = ohci_find_ep(ohci, ep_addr.iep); + if (tmp != NULL) { + +#ifdef VROOTHUB + if(ep_addr.bep.fa == ohci->root_hub_funct_addr) { + if((ep_addr.bep.ep & 0x0f) != 0) { /* root hub int ep */ + ohci->ed_rh_epi.handler = handler; + ohci_init_rh_int_timer(ohci, interval); + } + else { /* root hub ep0 */ + ohci->ed_rh_ep0.handler = handler; + } + } + + else +#endif + + { + tmp->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7) + + | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0) + | (speed << 13) + | ep_size <<16; + + tmp->handler = handler; + } + spin_unlock(&usb_ed_lock); + return tmp; /* ed already in use */ + } + + + OHCI_ALLOC(td, sizeof(td)); /* dummy td; end of td list for ed */ + OHCI_ALLOC(ed, sizeof(ed)); + td->prev_td = NULL; + + ed->hw.tail_td = virt_to_bus(&td->hw); + ed->hw.head_td = ed->hw.tail_td; + ed->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7) + /* | ((ep_addr.bep.port & 0x80)? 0x1000 : 0x0800 ) */ + | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0) + | (speed << 13) + | ep_size <<16; + + ed->handler = handler; + + switch((ep_addr.bep.ep >> 5) & 0x03) { + case CTRL: + ed->hw.next_ed = 0; + if(ohci->ed_controltail == NULL) { + writel(virt_to_bus(&ed->hw), &ohci->regs->ed_controlhead); + } + else { + ohci->ed_controltail->hw.next_ed = virt_to_bus(&ed->hw); + } + ed->ed_prev = ohci->ed_controltail; + ohci->ed_controltail = ed; + break; + case BULK: + ed->hw.next_ed = 0; + if(ohci->ed_bulktail == NULL) { + writel(virt_to_bus(&ed->hw), &ohci->regs->ed_bulkhead); + } + else { + ohci->ed_bulktail->hw.next_ed = virt_to_bus(&ed->hw); + } + ed->ed_prev = ohci->ed_bulktail; + ohci->ed_bulktail = ed; + break; + case INT: + int_junk = usb_ohci_int_ballance(ohci, interval, load); + ed->hw.next_ed = ohci->hc_area->ed[int_junk].next_ed; + ohci->hc_area->ed[int_junk].next_ed = virt_to_bus(&ed->hw); + ed->ed_prev = (struct usb_ohci_ed *) &ohci->hc_area->ed[int_junk]; + break; + case ISO: + ed->hw.next_ed = 0; + ohci->ed_isotail->hw.next_ed = virt_to_bus(&ed->hw); + ed->ed_prev = ohci->ed_isotail; + ohci->ed_isotail = ed; + break; + } + ed->ep_addr = ep_addr; + + /* Add it to the "hash"-table of known endpoint descriptors */ + + ed->ed_list = ohci->ed_func_ep0[ed->ep_addr.bep.fa]; + ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed; + + spin_unlock(&usb_ed_lock); + + OHCI_DEBUG(printk("USB HC new ed %x: %x :", ep_addr.iep, (unsigned int ) ed); ) + OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; ) + return 0; +} + +/***** + * Request the removal of an endpoint + * + * put the ep on the rm_list and request a stop of the bulk or ctrl list + * real removal is done at the next start of frame hardware interrupt + */ +int usb_ohci_rm_ep(struct ohci * ohci, struct usb_ohci_ed *ed) +{ + unsigned int flags; + struct usb_ohci_ed *tmp; + + OHCI_DEBUG(printk("USB HC remove ed %x: %x :\n", ed->ep_addr.iep, (unsigned int ) ed); ) + + spin_lock_irqsave(&usb_ed_lock, flags); + + tmp = ohci->ed_func_ep0[ed->ep_addr.bep.fa]; + if (tmp == NULL) { + spin_unlock_irqrestore(&usb_ed_lock, flags); + return 0; + } + + if(tmp == ed) { + ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed->ed_list; + } + else { + while (tmp->ed_list != ed) { + if (tmp->ed_list == NULL) { + spin_unlock_irqrestore(&usb_ed_lock, flags); + return 0; + } + tmp = tmp->ed_list; + } + tmp->ed_list = ed->ed_list; + } + ed->ed_list = ohci->ed_rm_list; + ohci->ed_rm_list = ed; + ed->hw.info |= OHCI_ED_SKIP; + + switch((ed->ep_addr.bep.ep >> 5) & 0x03) { + case CTRL: + writel_mask(~(0x01<<4), &ohci->regs->control); /* stop CTRL list */ + break; + case BULK: + writel_mask(~(0x01<<5), &ohci->regs->control); /* stop BULK list */ + break; + } + + + writel( OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */ + + spin_unlock_irqrestore(&usb_ed_lock, flags); + + return 1; +} + +/* we have requested to stop the bulk or CTRL list, + * now we can remove the eds on the rm_list */ + +static int ohci_rm_eds(struct ohci * ohci) { + + unsigned int flags; + struct usb_ohci_ed *ed; + struct usb_ohci_ed *ed_tmp; + struct usb_ohci_td *td; + __u32 td_hw_tmp; + __u32 td_hw; + + spin_lock_irqsave(&usb_ed_lock, flags); + + ed = ohci->ed_rm_list; + + while (ed != NULL) { + + switch((ed->ep_addr.bep.ep >> 5) & 0x03) { + case CTRL: + if(ed->ed_prev == NULL) { + writel(ed->hw.next_ed, &ohci->regs->ed_controlhead); + } + else { + ed->ed_prev->hw.next_ed = ed->hw.next_ed; + } + if(ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } + break; + case BULK: + if(ed->ed_prev == NULL) { + writel(ed->hw.next_ed, &ohci->regs->ed_bulkhead); + } + else { + ed->ed_prev->hw.next_ed = ed->hw.next_ed; + } + if(ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } + break; + case INT: + ed->ed_prev->hw.next_ed = ed->hw.next_ed; + break; + case ISO: + ed->ed_prev->hw.next_ed = ed->hw.next_ed; + if(ohci->ed_isotail == ed) { + ohci->ed_isotail = ed->ed_prev; + } + break; + } + + if(ed->hw.next_ed != 0) ((struct usb_ohci_ed *) bus_to_virt(ed->hw.next_ed))->ed_prev = ed->ed_prev; + + +/* tds directly connected to ed */ + + td_hw = ed->hw.head_td & 0xfffffff0; + while(td_hw != 0) { + td = bus_to_virt(td_hw); + td_hw_tmp = td_hw; + td_hw = td->hw.next_td; + OHCI_FREE(td); /* free pending tds */ + if(td_hw_tmp == ed->hw.tail_td) break; + + } + + /* mark TDs on the hc done list (if there are any) */ + td_hw = readl(&ohci->regs->donehead) & 0xfffffff0; + while(td_hw != 0) { + td = bus_to_virt(td_hw); + td_hw = td->hw.next_td; + if(td->ep == ed) td->ep = 0; + } + + /* mark TDs on the hcca done list (if there are any) */ + td_hw = ohci->hc_area->hcca.done_head & 0xfffffff0 ; + + while(td_hw != 0) { + td = bus_to_virt(td_hw); + td_hw = td->hw.next_td; + if(td->ep == ed) td->ep = 0; + } + + ed_tmp = ed; + ed = ed->ed_list; + OHCI_FREE(ed_tmp); /* free ed */ + } + writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */ + writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */ + writel_set((0x01<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */ + + spin_unlock_irqrestore(&usb_ed_lock, flags); + + ohci->ed_rm_list = NULL; + OHCI_DEBUG(printk("USB HC after rm ed control: %4x intrstat: %4x intrenable: %4x\n", readl(&ohci->regs->control),readl(&ohci->regs->intrstatus),readl(&ohci->regs->intrenable));) + + + return 0; +} + +/* remove all endpoints of a function (device) */ +int usb_ohci_rm_function( struct ohci * ohci, unsigned int ep_addr_in) +{ + struct usb_ohci_ed *ed; + struct usb_ohci_ed *tmp; + union ep_addr_ ep_addr; + + + + ep_addr.iep = ep_addr_in; + + for(ed = ohci->ed_func_ep0[ep_addr.bep.fa]; ed != NULL;) { + tmp = ed; + ed = ed->ed_list; + usb_ohci_rm_ep(ohci, tmp); + } + + + + return 1; +} + + + + + +/****** + *** TD handling functions + ************************************/ + + +#define FILL_TD(TD_PT, HANDLER, INFO, DATA, LEN, LW0, LW1) \ + td_pt = (TD_PT); \ + td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); \ + td_pt1->ep = usb_ep; \ + td_pt1->handler = (HANDLER); \ + td_pt1->buffer_start = (DATA); \ + td_pt1->lw0 = (LW0); \ + td_pt1->lw1 = (LW1); \ + td_pt1->hw.info = (INFO); \ + td_pt1->hw.cur_buf = virt_to_bus(DATA); \ + td_pt1->hw.buf_end = td_pt1->hw.cur_buf + (LEN) - 1; \ + td_pt1->hw.next_td = virt_to_bus(td_pt); \ + usb_ep->hw.tail_td = virt_to_bus(td_pt); \ + td_pt->prev_td = td_pt1; \ + td_pt->hw.next_td = 0 + +spinlock_t usb_req_lock = SPIN_LOCK_UNLOCKED; + +int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) { + + int ed_type; + unsigned int flags; + struct usb_ohci_td *td_pt; + struct usb_ohci_td *td_pt1; + struct usb_ohci_td *td_pt_a1, *td_pt_a2, *td_pt_a3; + struct usb_ohci_ed *usb_ep; + f_handler handler; + + + td_pt_a1 =NULL; + td_pt_a2 =NULL; + td_pt_a3 =NULL; + + usb_ep = ohci_find_ep(ohci, ep_addr); + if(usb_ep == NULL ) return -1; /* not known ep */ + + handler = usb_ep->handler; + +#ifdef VROOTHUB + if(usb_ep == &ohci->ed_rh_ep0) { /* root hub ep 0 control endpoint */ + root_hub_control_msg(ohci, 8, ctrl, data, data_len, lw0, lw1, handler); + return 0; + } + + if(usb_ep == &ohci->ed_rh_epi) { /* root hub interrupt endpoint */ + + root_hub_int_req(ohci, 8, ctrl, data, data_len, lw0, lw1, handler); + return 0; + } +#endif + /* struct usb_ohci_ed * usb_ep = usb_ohci_add_ep(pipe, ohci, interval, 1); */ + + ed_type = ((((union ep_addr_)ep_addr).bep.ep >> 5) & 0x07); + + switch(ed_type) { + case BULK_IN: + case BULK_OUT: + case INT_IN: + case INT_OUT: + OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1)); + break; + + case CTRL_IN: + case CTRL_OUT: + OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1)); + OHCI_ALLOC(td_pt_a3, sizeof(td_pt_a3)); + if(data_len > 0) { + OHCI_ALLOC(td_pt_a2, sizeof(td_pt_a2)); + } + break; + + case ISO_IN: + case ISO_OUT: + + } + + spin_lock_irqsave(&usb_req_lock, flags); + + switch(ed_type) { + case BULK_IN: + FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 ); + writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case BULK_OUT: + FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 ); + writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case INT_IN: + FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 ); + break; + + case INT_OUT: + FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 ); + break; + + case CTRL_IN: + FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 ); + if(data_len > 0) { + FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_IN | TD_T_DATA1, data, data_len, 0, 0 ); + } + FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_OUT | TD_T_DATA1, NULL, 0, lw0, lw1 ); + writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + + case CTRL_OUT: + FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 ); + if(data_len > 0) { + FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1, data, data_len, 0, 0 ); + } + FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_IN | TD_T_DATA1, NULL, 0, lw0, lw1 ); + writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + + case ISO_IN: + case ISO_OUT: + break; + } + + + + + td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); + + + if(td_pt_a3 != NULL) td_pt_a3->prev_td = NULL; + else if (td_pt_a2 != NULL) td_pt_a2->prev_td = NULL; + else if (td_pt_a1 != NULL) td_pt_a1->prev_td = NULL; + + spin_unlock_irqrestore(&usb_req_lock, flags); + return 0; +} + + +/****** + *** Done List handling functions + ************************************/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static struct usb_ohci_td * ohci_reverse_done_list(struct ohci * ohci) { + + __u32 td_list_hc; + struct usb_ohci_td * td_list = NULL; + struct usb_ohci_td * td_rev = NULL; + + td_list_hc = ohci->hc_area->hcca.done_head & 0xfffffff0; + ohci->hc_area->hcca.done_head = 0; + + while(td_list_hc) { + + td_list = (struct usb_ohci_td *) bus_to_virt(td_list_hc); + td_list->next_dl_td = td_rev; + + td_rev = td_list; + td_list_hc = td_list->hw.next_td & 0xfffffff0; + } + return td_list; +} + +/* all done requests are replied here */ +static int usb_ohci_done_list(struct ohci * ohci) { + + struct usb_ohci_td * td = NULL; + struct usb_ohci_td * td_list; + struct usb_ohci_td * td_list_next = NULL; + struct usb_ohci_td * td_err = NULL; + __u32 td_hw; + + + td_list = ohci_reverse_done_list(ohci); + + while(td_list) { + td_list_next = td_list->next_dl_td; + td = td_list; + + if(td->ep == NULL) { /* removed ep */ + OHCI_FREE(td_list); + break; + } + + /* the HC halts an ED if an error occurs; put all pendings TDs of an halted ED on the + * done list; they are marked with an 0xf CC_error code + */ + + if(TD_CC_GET(td_list->hw.info) != TD_CC_NOERROR) { /* on error move all pending tds of an ed into the done list */ + printk("******* USB BUS error %x @ep %x\n", TD_CC_GET(td_list->hw.info), td_list->ep->ep_addr.iep); + td_err= td_list; + td_hw = td_list->ep->hw.head_td & 0xfffffff0; + while(td_hw != 0) { + if(td_hw == td_list->ep->hw.tail_td) break; + td = bus_to_virt(td_hw); + td_err->next_dl_td = td; + td_err= td; + td_hw = td->hw.next_td; + } + td_list->ep->hw.head_td = td_list->ep->hw.tail_td; + td->next_dl_td = td_list_next; + td_list_next = td_list->next_dl_td; + + } + /* send the reply */ + if(td_list->handler != NULL) { + if(td_list->prev_td == NULL) { + td_list->handler((void *) ohci, + td_list->ep->ep_addr.iep, + 0, + NULL, + td_list->buffer_start, + td_list->hw.buf_end-virt_to_bus(td_list->buffer_start)+1, + TD_CC_GET(td_list->hw.info), + td_list->lw0, + td_list->lw1); + OHCI_FREE(td_list); + } + else { + if(td_list->prev_td->prev_td == NULL) { /* cntrl 2 Transactions dataless */ + td_list->handler((void *) ohci, + td_list->ep->ep_addr.iep, + td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1, + td_list->prev_td->buffer_start, + NULL, + 0, + (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info), + td_list->lw0, + td_list->lw1); + OHCI_FREE(td_list->prev_td); + OHCI_FREE(td_list); + } + else { /* cntrl 3 Transactions */ + td_list->handler((void *) ohci, + td_list->ep->ep_addr.iep, + td_list->prev_td->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->prev_td->buffer_start)+1, + td_list->prev_td->prev_td->buffer_start, + td_list->prev_td->buffer_start, + td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1, + (TD_CC_GET(td_list->prev_td->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->prev_td->hw.info) + : (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info), + td_list->lw0, + td_list->lw1); + OHCI_FREE(td_list->prev_td->prev_td); + OHCI_FREE(td_list->prev_td); + OHCI_FREE(td_list); + + } + } + + } + td_list = td_list_next; + } + return 0; +} + + + +/****** + *** HC functions + ************************************/ + + + +void reset_hc(struct ohci *ohci) { + int retries = 5; + int timeout = 30; + int fminterval; + + if(readl(&ohci->regs->control) & 0x100) { /* SMM owns the HC */ + writel(0x08, &ohci->regs->cmdstatus); /* request ownership */ + printk("USB HC TakeOver from SMM\n"); + do { + wait_ms(100); + if(--retries) { + printk("USB HC TakeOver timed out!\n"); + break; + } + } + while(readl(&ohci->regs->control) & 0x100); + } + + writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */ + OHCI_DEBUG(printk("USB HC reset_hc: %x ; retries: %d\n", readl(&ohci->regs->control), 5-retries);) + fminterval = readl(&ohci->regs->fminterval) & 0x3fff; + writel(1, &ohci->regs->cmdstatus); /* HC Reset */ + while ((readl(&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */ + if (--timeout == 0) { + printk("USB HC reset timed out!\n"); + return; + } + udelay(1); + } + /* set the timing */ + fminterval |= (((fminterval -210) * 6)/7)<<16; + writel(fminterval, &ohci->regs->fminterval); + writel(((fminterval&0x3fff)*9)/10, &ohci->regs->periodicstart); +} + + +/* + * Reset and start an OHCI controller + */ +void start_hc(struct ohci *ohci) +{ + /* int fminterval; */ + unsigned int mask; + /* fminterval = readl(&ohci->regs->fminterval) & 0x3fff; + reset_hc(ohci); */ + + + writel(virt_to_bus(&ohci->hc_area->hcca), &ohci->regs->hcca); /* a reset clears this */ + + /* Choose the interrupts we care about now, others later on demand */ + mask = OHCI_INTR_MIE | OHCI_INTR_WDH; + /* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF| + OHCI_INTR_FNO */ + + + + writel((0x00), &ohci->regs->control); /* USB Reset BUS */ + wait_ms(10); + + writel((0x97), &ohci->regs->control); /* USB Operational */ + + writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */ + wait_ms(50); + + OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); ) + OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); ) + OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); ) + OHCI_DEBUG(printk("USB HC roothubstatu: %x \n", readl( &(ohci->regs->roothub.status) )); ) + OHCI_DEBUG(printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); ) + OHCI_DEBUG(printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); ) + + /* control_wakeup = NULL; */ + writel(mask, &ohci->regs->intrenable); + writel(mask, &ohci->regs->intrstatus); + +#ifdef VROOTHUB + { + + struct usb_device * usb_dev; + struct ohci_device *dev; + + usb_dev = sohci_usb_allocate(ohci->root_hub->usb); + dev = usb_dev->hcpriv; + + dev->ohci = ohci; + + usb_connect(usb_dev); + + ohci->root_hub->usb->children[0] = usb_dev; + + usb_new_device(usb_dev); + } +#endif + + + +} + + + + +static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) +{ + struct ohci *ohci = __ohci; + struct ohci_regs *regs = ohci->regs; + + int ints; + + + if((ohci->hc_area->hcca.done_head != 0) && !(ohci->hc_area->hcca.done_head & 0x01)) { + ints = OHCI_INTR_WDH; + } + else { + if((ints = (readl(®s->intrstatus) & readl(®s->intrenable))) == 0) + return; + } + + ohci->intrstatus |= ints; + OHCI_DEBUG(printk("USB HC interrupt: %x (%x) \n", ints, readl(&ohci->regs->intrstatus));) + + /* ints &= ~(OHCI_INTR_WDH); WH Bit will be set by done list subroutine */ + /* if(ints & OHCI_INTR_FNO) { + writel(OHCI_INTR_FNO, ®s->intrstatus); + if (waitqueue_active(&ohci_tasks)) wake_up(&ohci_tasks); + } */ + + if(ints & OHCI_INTR_WDH) { + writel(OHCI_INTR_WDH, ®s->intrdisable); + ohci->intrstatus &= (~OHCI_INTR_WDH); + usb_ohci_done_list(ohci); /* prepare out channel list */ + writel(OHCI_INTR_WDH, &ohci->regs->intrstatus); + writel(OHCI_INTR_WDH, &ohci->regs->intrenable); + + } + + if(ints & OHCI_INTR_SF) { + writel(OHCI_INTR_SF, ®s->intrdisable); + writel(OHCI_INTR_SF, &ohci->regs->intrstatus); + ohci->intrstatus &= (~OHCI_INTR_SF); + if(ohci->ed_rm_list != NULL) { + ohci_rm_eds(ohci); + } + } +#ifndef VROOTHUB + if(ints & OHCI_INTR_RHSC) { + writel(OHCI_INTR_RHSC, ®s->intrdisable); + writel(OHCI_INTR_RHSC, &ohci->regs->intrstatus); + wake_up(&root_hub); + + } + #endif + + writel(OHCI_INTR_MIE, ®s->intrenable); + +} + +#ifndef VROOTHUB +/* + * This gets called if the connect status on the root + * hub (and the root hub only) changes. + */ +static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr) +{ + struct usb_device *usb_dev; + struct ohci_device *dev; + OHCI_DEBUG(printk("uhci_connect_change: called for %d stat %x\n", port_nr,readl(&ohci->regs->roothub.portstatus[port_nr]) );) + + /* + * Even if the status says we're connected, + * the fact that the status bits changed may + * that we got disconnected and then reconnected. + * + * So start off by getting rid of any old devices.. + */ + usb_disconnect(&ohci->root_hub->usb->children[port_nr]); + + if(!(readl(&ohci->regs->roothub.portstatus[port_nr]) & RH_PS_CCS)) { + writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[port_nr]); + return; /* nothing connected */ + } + /* + * Ok, we got a new connection. Allocate a device to it, + * and find out what it wants to do.. + */ + usb_dev = sohci_usb_allocate(ohci->root_hub->usb); + dev = usb_dev->hcpriv; + dev->ohci = ohci; + usb_connect(dev->usb); + ohci->root_hub->usb->children[port_nr] = usb_dev; + wait_ms(200); /* wait for powerup */ + /* reset port/device */ + writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[port_nr]); /* reset port */ + while(!(readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_PRSC)) wait_ms(10); /* reset active ? */ + writel(RH_PS_PES, &ohci->regs->roothub.portstatus[port_nr]); /* enable port */ + wait_ms(10); + /* Get speed information */ + usb_dev->slow = (readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_LSDA) ? 1 : 0; + + /* + * Ok, all the stuff specific to the root hub has been done. + * The rest is generic for any new USB attach, regardless of + * hub type. + */ + usb_new_device(usb_dev); +} +#endif + + + +/* + * Allocate the resources required for running an OHCI controller. + * Host controller interrupts must not be running while calling this + * function. + * + * The mem_base parameter must be the usable -virtual- address of the + * host controller's memory mapped I/O registers. + * + * This is where OHCI triumphs over UHCI, because good is dumb. + * Note how much simpler this function is than in uhci.c. + * + * OHCI hardware takes care of most of the scheduling of different + * transfer types with the correct prioritization for us. + */ + + +static struct ohci *alloc_ohci(void* mem_base) +{ + int i,j; + struct ohci *ohci; + struct ohci_hc_area *hc_area; + struct usb_bus *bus; + struct ohci_device *dev; + struct usb_device *usb; + + /* + * Here we allocate some dummy EDs as well as the + * OHCI host controller communications area. The HCCA is just + * a nice pool of memory with pointers to endpoint descriptors + * for the different interrupts. + * + * The first page of memory contains the HCCA and ohci structure + */ + hc_area = (struct ohci_hc_area *) __get_free_pages(GFP_KERNEL, 1); + if (!hc_area) + return NULL; + memset(hc_area, 0, sizeof(*hc_area)); + ohci = &hc_area->ohci; + ohci->irq = -1; + ohci->regs = mem_base; + + ohci->hc_area = hc_area; + /* Tell the controller where the HCCA is */ + writel(virt_to_bus(&hc_area->hcca), &ohci->regs->hcca); + + + /* + * Initialize the ED polling "tree", full tree; + * dummy eds ed[i] (hc should skip them) + * i == 0 is the end of the iso list; + * 1 is the 1ms node, + * 2,3 2ms nodes, + * 4,5,6,7 4ms nodes, + * 8 ... 15 8ms nodes, + * 16 ... 31 16ms nodes, + * 32 ... 63 32ms nodes + * Sequenzes: + * 32-16- 8-4-2-1-0 + * 33-17- 9-5-3-1-0 + * 34-18-10-6-2-1-0 + * 35-19-11-7-3-1-0 + * 36-20-12-4-2-1-0 + * 37-21-13-5-3-1-0 + * 38-22-14-6-2-1-0 + * 39-23-15-7-3-1-0 + * 40-24- 8-4-2-1-0 + * 41-25- 9-5-3-1-0 + * 42-26-10-6-2-1-0 + * : : + * 63-31-15-7-3-1-0 + */ + hc_area->ed[ED_ISO].info |= OHCI_ED_SKIP; /* place holder, so skip it */ + hc_area->ed[ED_ISO].next_ed = 0x0000; /* end of iso list */ + + hc_area->ed[1].next_ed = virt_to_bus(&(hc_area->ed[ED_ISO])); + hc_area->ed[1].info |= OHCI_ED_SKIP; /* place holder, skip it */ + + j=1; + for (i = 2; i < (NUM_INTS * 2); i++) { + if (i >= NUM_INTS) + hc_area->hcca.int_table[i - NUM_INTS] = virt_to_bus(&(hc_area->ed[i])); + + if( i == j*4) j *= 2; + hc_area->ed[i].next_ed = virt_to_bus(&(hc_area->ed[j+ i%j])); + hc_area->ed[i].info |= OHCI_ED_SKIP; /* place holder, skip it */ + } + + + /* + * for load ballancing of the interrupt branches + */ + for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; + + /* + * Store the end of control and bulk list eds. So, we know where we can add + * elements to these lists. + */ + ohci->ed_isotail = (struct usb_ohci_ed *) &(hc_area->ed[ED_ISO]); + ohci->ed_controltail = NULL; + ohci->ed_bulktail = NULL; + + /* + * Tell the controller where the control and bulk lists are + * The lists are empty now. + */ + writel(0, &ohci->regs->ed_controlhead); + writel(0, &ohci->regs->ed_bulkhead); + + + USB_ALLOC(bus, sizeof(*bus)); + if (!bus) + return NULL; + + memset(bus, 0, sizeof(*bus)); + + ohci->bus = bus; + bus->hcpriv = (void *) ohci; + bus->op = &sohci_device_operations; + + + usb = sohci_usb_allocate(NULL); + if (!usb) + return NULL; + + dev = ohci->root_hub = usb_to_ohci(usb); + + usb->bus = bus; + /* bus->root_hub = ohci_to_usb(ohci->root_hub); */ + dev->ohci = ohci; + + /* Initialize the root hub */ + + usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; + usb_init_root_hub(usb); + + return ohci; +} + + +/* + * De-allocate all resources.. + */ + +static void release_ohci(struct ohci *ohci) +{ + int i; + union ep_addr_ ep_addr; + ep_addr.iep = 0; + + OHCI_DEBUG(printk("USB HC release ohci \n");) + + if (ohci->irq >= 0) { + free_irq(ohci->irq, ohci); + ohci->irq = -1; + } + + /* stop hc */ + writel(OHCI_USB_SUSPEND, &ohci->regs->control); + + /* deallocate all EDs and TDs */ + for(i=0; i < 128; i ++) { + ep_addr.bep.fa = i; + usb_ohci_rm_function(ohci, ep_addr.iep); + } + ohci_rm_eds(ohci); /* remove eds */ + + /* disconnect all devices */ + if(ohci->root_hub) + for(i = 0; i < ohci->root_hub->usb->maxchild; i++) + usb_disconnect(ohci->root_hub->usb->children + i); + + USB_FREE(ohci->root_hub->usb); + USB_FREE(ohci->root_hub); + USB_FREE(ohci->bus); + + /* unmap the IO address space */ + iounmap(ohci->regs); + + + free_pages((unsigned int) ohci->hc_area, 1); + +} + + +void cleanup_drivers(void); + +static int ohci_roothub_thread(void * __ohci) +{ + struct ohci *ohci = (struct ohci *)__ohci; + lock_kernel(); + + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources.. + */ + printk("ohci_roothub_thread at %p\n", &ohci_roothub_thread); + exit_mm(current); + exit_files(current); + exit_fs(current); + + + strcpy(current->comm, "root-hub"); + + + start_hc(ohci); + writel( 0x10000, &ohci->regs->roothub.status); + wait_ms(50); /* root hub power on */ + do { +#ifdef CONFIG_APM + if (apm_resume) { + apm_resume = 0; + start_hc(ohci); + continue; + } +#endif + + OHCI_DEBUG(printk("USB RH tasks: int: %x\n", ohci->intrstatus); ) +#ifndef VROOTHUB + /* if (ohci->intrstatus & OHCI_INTR_RHSC) */ + { + int port_nr; + for(port_nr=0; port_nr< ohci->root_hub->usb->maxchild; port_nr++) + if(readl(&ohci->regs->roothub.portstatus[port_nr]) & (RH_PS_CSC | RH_PS_PRSC)) { + ohci_connect_change(ohci, port_nr); + writel(0xffff0000, &ohci->regs->roothub.portstatus[port_nr]); + } + ohci->intrstatus &= ~(OHCI_INTR_RHSC); + writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); + } +#endif + + interruptible_sleep_on(&root_hub); + + } while (!signal_pending(current)); + +#ifdef VROOTHUB + ohci_del_rh_int_timer(ohci); +#endif + + + cleanup_drivers(); + /* reset_hc(ohci); */ + + release_ohci(ohci); + MOD_DEC_USE_COUNT; + + printk("ohci_control_thread exiting\n"); + + return 0; +} + + + + +/* + * Increment the module usage count, start the control thread and + * return success. + */ +static int found_ohci(int irq, void* mem_base) +{ + int retval; + struct ohci *ohci; + OHCI_DEBUG(printk("USB HC found ohci: irq= %d membase= %x \n", irq, (int)mem_base);) + /* Allocate the running OHCI structures */ + ohci = alloc_ohci(mem_base); + if (!ohci) { + return -ENOMEM; + } + + reset_hc(ohci); + + retval = -EBUSY; + if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) { + int pid; + + MOD_INC_USE_COUNT; + ohci->irq = irq; + + pid = kernel_thread(ohci_roothub_thread, ohci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + if (pid >= 0) + return 0; + + + MOD_DEC_USE_COUNT; + retval = pid; + } + + release_ohci(ohci); + return retval; +} + +static int start_ohci(struct pci_dev *dev) +{ + unsigned int mem_base = dev->base_address[0]; + + /* If its OHCI, its memory */ + if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) + return -ENODEV; + + /* Get the memory address and map it for IO */ + mem_base &= PCI_BASE_ADDRESS_MEM_MASK; + + /* + * FIXME ioremap_nocache isn't implemented on all CPUs (such + * as the Alpha) [?] What should I use instead... + * + * The iounmap() is done on in release_ohci. + */ + mem_base = (unsigned int) ioremap_nocache(mem_base, 4096); + + if (!mem_base) { + printk("Error mapping OHCI memory\n"); + return -EFAULT; + } + + return found_ohci(dev->irq, (void *) mem_base); +} + + + +#ifdef CONFIG_APM +static int handle_apm_event(apm_event_t event) +{ + static int down = 0; + + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (down) { + printk(KERN_DEBUG "ohci: received extra suspend event\n"); + break; + } + down = 1; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (!down) { + printk(KERN_DEBUG "ohci: received bogus resume event\n"); + break; + } + down = 0; + if (waitqueue_active(&root_hub)) { + apm_resume = 1; + wake_up(&root_hub); + } + break; + } + return 0; +} +#endif + + + int usb_mouse_init(void); +#ifdef MODULE + +void cleanup_module(void) +{ +#ifdef CONFIG_APM + apm_unregister_callback(&handle_apm_event); +#endif +} + +#define ohci_hcd_init init_module + +#endif + +#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 +#define PCI_CLASS_SERIAL_USB_OHCI_PG 0x10 + + +int ohci_hcd_init(void) +{ + int retval; + struct pci_dev *dev = NULL; + + retval = -ENODEV; + + dev = NULL; + while((dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev))) { /* OHCI */ + retval = start_ohci(dev); + if (retval < 0) break; + + +#ifdef CONFIG_USB_MOUSE + usb_mouse_init(); +#endif +#ifdef CONFIG_USB_KBD + usb_kbd_init(); +#endif + hub_init(); +#ifdef CONFIG_USB_AUDIO + usb_audio_init(); +#endif +#ifdef CONFIG_APM + apm_register_callback(&handle_apm_event); +#endif + + return 0; + } + return retval; +} + +void cleanup_drivers(void) +{ + hub_cleanup(); +#ifdef CONFIG_USB_MOUSE + usb_mouse_cleanup(); +#endif +} + diff --git a/drivers/usb/ohci-hcd.h b/drivers/usb/ohci-hcd.h new file mode 100644 index 000000000000..cbcac83d3bc5 --- /dev/null +++ b/drivers/usb/ohci-hcd.h @@ -0,0 +1,402 @@ + /* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * + * The OHCI HCD layer is a simple but nearly complete implementation of what the + * USB people would call a HCD for the OHCI. + * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) + * The layer on top of it, is for interfacing to the alternate-usb device-drivers. + * + * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ] + * [ Open Host Controller Interface driver for USB. ] + * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] + * [ (C) Copyright 1999 Gregory P. Smith ] + * [ $Log: ohci.c,v $ ] + * [ Revision 1.1 1999/04/05 08:32:30 greg ] + * + * + * v2.1 1999/05/09 ep_addr correction, code clean up + * v2.0 1999/05/04 + * v1.0 1999/04/27 + * ohci-hcd.h + */ + +#ifdef CONFIG_USB_OHCI_VROOTHUB +#define VROOTHUB +#endif +/* enables virtual root hub + * (root hub will be managed by the hub controller + * hub.c of the alternate usb driver) + * last time I did more testing without virtual root hub + * -> the virtual root hub could be more unstable now */ + + + +#ifdef OHCI_DBG +#define OHCI_DEBUG(X) X +#else +#define OHCI_DEBUG(X) +#endif + +/* for readl writel functions */ +#include +#include + +/* for ED and TD structures */ + +typedef void * __OHCI_BAG; +typedef int (*f_handler )(void * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void *data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1); + + + +struct ep_address { + __u8 ep; /* bit 7: IN/-OUT, 6,5: type 10..CTRL 00..ISO 11..BULK 10..INT, 3..0: ep nr */ + __u8 fa; /* function address */ + __u8 hc; + __u8 host; +}; + +union ep_addr_ { + unsigned int iep; + struct ep_address bep; +}; + +/* + * ED and TD descriptors has to be 16-byte aligned + */ +struct ohci_hw_ed { + __u32 info; + __u32 tail_td; /* TD Queue tail pointer */ + __u32 head_td; /* TD Queue head pointer */ + __u32 next_ed; /* Next ED */ +} __attribute((aligned(16))); + + +struct usb_ohci_ed { + struct ohci_hw_ed hw; + /* struct ohci * ohci; */ + f_handler handler; + union ep_addr_ ep_addr; + struct usb_ohci_ed *ed_list; + struct usb_ohci_ed *ed_prev; +} __attribute((aligned(32))); + + /* OHCI Hardware fields */ +struct ohci_hw_td { + __u32 info; + __u32 cur_buf; /* Current Buffer Pointer */ + __u32 next_td; /* Next TD Pointer */ + __u32 buf_end; /* Memory Buffer End Pointer */ +} __attribute((aligned(16))); + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x04) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + + +struct usb_ohci_td { + struct ohci_hw_td hw; + void * buffer_start; + f_handler handler; + struct usb_ohci_td *prev_td; + struct usb_ohci_ed *ep; + struct usb_ohci_td *next_dl_td; + __OHCI_BAG lw0; + __OHCI_BAG lw1; +} __attribute((aligned(32))); + + + +/* TD types */ +#define BULK 0x03 +#define INT 0x01 +#define CTRL 0x02 +#define ISO 0x00 +/* TD types with direction */ +#define BULK_IN 0x07 +#define BULK_OUT 0x03 +#define INT_IN 0x05 +#define INT_OUT 0x01 +#define CTRL_IN 0x06 +#define CTRL_OUT 0x02 +#define ISO_IN 0x04 +#define ISO_OUT 0x00 + +struct ohci_rep_td { + int cmd_len; + void * cmd; + void * data; + int data_len; + f_handler handler; + struct ohci_rep_td *next_td; + int ep_addr; + __OHCI_BAG lw0; + __OHCI_BAG lw1; + __u32 status; +} __attribute((aligned(32))); + +#define OHCI_ED_SKIP (1 << 14) +#define OHCI_ED_MPS (0x7ff << 16) +#define OHCI_ED_F_NORM (0) +#define OHCI_ED_F_ISOC (1 << 15) +#define OHCI_ED_S_LOW (1 << 13) +#define OHCI_ED_S_HIGH (0) +#define OHCI_ED_D (3 << 11) +#define OHCI_ED_D_IN (2 << 11) +#define OHCI_ED_D_OUT (1 << 11) +#define OHCI_ED_EN (0xf << 7) +#define OHCI_ED_FA (0x7f) + + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + + +#define ED_INT_1 1 +#define ED_INT_2 2 +#define ED_INT_4 4 +#define ED_INT_8 8 +#define ED_INT_16 16 +#define ED_INT_32 32 +#define ED_CONTROL 64 +#define ED_BULK 65 +#define ED_ISO 0 /* same as 1ms interrupt queue */ + + +/* + * This is the maximum number of root hub ports. I don't think we'll + * ever see more than two as that's the space available on an ATX + * motherboard's case, but it could happen. The OHCI spec allows for + * up to 15... (which is insane!) + * + * Although I suppose several "ports" could be connected directly to + * internal laptop devices such as a keyboard, mouse, camera and + * serial/parallel ports. hmm... That'd be neat. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + + +/* + * Read a MMIO register and re-write it after ANDing with (m) + */ +#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) ) + +/* + * Read a MMIO register and re-write it after ORing with (b) + */ +#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) ) + +/* + * cmdstatus register */ +#define OHCI_CLF 0x02 +#define OHCI_BLF 0x04 + +/* + * Interrupt register masks + */ +#define OHCI_INTR_SO (1) +#define OHCI_INTR_WDH (1 << 1) +#define OHCI_INTR_SF (1 << 2) +#define OHCI_INTR_RD (1 << 3) +#define OHCI_INTR_UE (1 << 4) +#define OHCI_INTR_FNO (1 << 5) +#define OHCI_INTR_RHSC (1 << 6) +#define OHCI_INTR_OC (1 << 30) +#define OHCI_INTR_MIE (1 << 31) + +/* + * Control register masks + */ +#define OHCI_USB_OPER (2 << 6) +#define OHCI_USB_SUSPEND (3 << 6) + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +struct ohci { + int irq; + struct ohci_regs *regs; /* OHCI controller's memory */ + struct ohci_hc_area *hc_area; /* hcca, int ed-tree, ohci itself .. */ + int root_hub_funct_addr; /* Address of Root Hub endpoint */ + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/ + struct usb_ohci_ed * ed_rm_list; /* list of all endpoints to be removed */ + struct usb_ohci_ed * ed_bulktail; /* last endpoint of bulk list */ + struct usb_ohci_ed * ed_controltail; /* last endpoint of control list */ + struct usb_ohci_ed * ed_isotail; /* last endpoint of iso list */ + struct ohci_device * root_hub; + struct usb_ohci_ed ed_rh_ep0; + struct usb_ohci_ed ed_rh_epi; + struct ohci_rep_td *td_rh_epi; + int intrstatus; + struct usb_ohci_ed *ed_func_ep0[128]; /* "hash"-table for ep to ed mapping */ + struct ohci_rep_td *repl_queue; /* for internal requests */ + int rh_int_interval; + int rh_int_timer; + struct usb_bus *bus; + + +}; + +/* + * Warning: This constant must not be so large as to cause the + * ohci_device structure to exceed one 4096 byte page. Or "weird + * things will happen" as the alloc_ohci() function assumes that + * its less than one page at the moment. (FIXME) + */ +#define NUM_TDS 4 /* num of preallocated transfer descriptors */ +#define NUM_EDS 80 /* num of preallocated endpoint descriptors */ + +struct ohci_hc_area { + + struct ohci_hcca hcca; /* OHCI mem. mapped IO area 256 Bytes*/ + + struct ohci_hw_ed ed[NUM_EDS]; /* Endpoint Descriptors 80 * 16 : 1280 Bytes */ + struct ohci_hw_td td[NUM_TDS]; /* Transfer Descriptors 2 * 32 : 64 Bytes */ + struct ohci ohci; + +}; +struct ohci_device { + struct usb_device *usb; + struct ohci *ohci; + unsigned long data[16]; +}; + +#define ohci_to_usb(uhci) ((ohci)->usb) +#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) + +/* Debugging code */ +/*void show_ed(struct ohci_ed *ed); +void show_td(struct ohci_td *td); +void show_status(struct ohci *ohci); */ + +/* hcd */ +int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1); +struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr, int interval, int load, f_handler handler, int ep_size, int speed); +int usb_ohci_rm_function(struct ohci * ohci, unsigned int ep_addr); +int usb_ohci_rm_ep(struct ohci * ohci, struct usb_ohci_ed *ed); +struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in); + +/* roothub */ +int ohci_del_rh_int_timer(struct ohci * ohci); +int ohci_init_rh_int_timer(struct ohci * ohci, int interval); +int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler); +int root_hub_send_irq(struct ohci * ohci, void * data, int data_len ); +int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler); +int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler); +int send_replies(struct ohci * ohci); + + + + +/* Root-Hub Register info */ + +#define RH_PS_CCS 0x00000001 +#define RH_PS_PES 0x00000002 +#define RH_PS_PSS 0x00000004 +#define RH_PS_POCI 0x00000008 +#define RH_PS_PRS 0x00000010 +#define RH_PS_PPS 0x00000100 +#define RH_PS_LSDA 0x00000200 +#define RH_PS_CSC 0x00010000 +#define RH_PS_PESC 0x00020000 +#define RH_PS_PSSC 0x00040000 +#define RH_PS_OCIC 0x00080000 +#define RH_PS_PRSC 0x00100000 + + +#ifdef OHCI_DBG +#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d\n", -- __ohci_free_cnt) +#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("OHCI ALLO: %d\n", ++ __ohci_free_cnt) +#define USB_FREE(x) kfree(x); printk("USB FREE: %d\n", -- __ohci_free1_cnt) +#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("USB ALLO: %d\n", ++ __ohci_free1_cnt) +static int __ohci_free_cnt = 0; +static int __ohci_free1_cnt = 0; +#else +#define OHCI_FREE(x) kfree(x) +#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) +#define USB_FREE(x) kfree(x) +#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) +#endif + diff --git a/drivers/usb/ohci-root-hub.c b/drivers/usb/ohci-root-hub.c new file mode 100644 index 000000000000..6d1018eff575 --- /dev/null +++ b/drivers/usb/ohci-root-hub.c @@ -0,0 +1,604 @@ +/* + * HCD (OHCI) Virtual Root Hub for USB. + * + * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at) + * + * The Root Hub is build into the HC (UHCI or OHCI) hardware. + * This piece of code lets it look like it resides on the usb + * like the other hubs. + * (for anyone who wants to do a control operation on the root hub) + * + * v2.1 1999/05/09 + * v2.0 1999/05/04 + * v1.0 1999/04/27 + * ohci-root-hub.c + * + */ + + + +#include +#include +#include +#include +#include +#include + +#include "usb.h" +#include "ohci-hcd.h" + +#ifdef VROOTHUB + +#include "ohci-root-hub.h" + + +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x00, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x40, /* __u16 ep_wMaxPacketSize; 64 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* +For OHCI we need just the 2nd Byte, so we +don't need this constant byte-array + +static __u8 root_hub_hub_des[] = +{ + 0x00, * __u8 bLength; * + 0x29, * __u8 bDescriptorType; Hub-descriptor * + 0x02, * __u8 bNbrPorts; * + 0x00, * __u16 wHubCharacteristics; * + 0x00, + 0x01, * __u8 bPwrOn2pwrGood; 2ms * + 0x00, * __u8 bHubContrCurrent; 0 mA * + 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** * + 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** * +}; +*/ + + +int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int leni, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) +{ + + __u32 stat; + __u32 rep_handler; + int req_reply=0; + union ep_addr_ ep_addr; + union ep_addr_ ep_addr_ret; + __u8 * cmd = rh_cmd; + __u8 * data = rh_data; + int i; + int len =leni; + + __u8 bmRequestType = cmd[0]; + __u8 bRequest = cmd[1]; + __u16 wValue = cmd[3] << 8 | cmd [2]; + __u16 wIndex = cmd[5] << 8 | cmd [4]; + __u16 wLength = cmd[7] << 8 | cmd [6]; +printk("USB root hub: adr: %8x cmd(%8x): ", ohci->root_hub_funct_addr, 8); +for(i=0;i<8;i++) + printk("%2x", ((char *)rh_cmd)[i]); + +printk(" ; \n"); + + ep_addr_ret.iep = 0; + ep_addr_ret.bep.fa = ohci->root_hub_funct_addr; + ep_addr_ret.bep.ep = (bmRequestType & 0x80) | 0x40; + + switch (bmRequestType | bRequest << 8) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + len = 2; + data[0] = 0x01; + data[1] = 0x00; + req_reply = RH_ACK; + break; + case RH_GET_STATUS | RH_INTERFACE: + len = 2; + data[0] = 0x00; + data[1] = 0x00; + req_reply = RH_ACK; + break; + case RH_GET_STATUS | RH_ENDPOINT: + len = 2; + data[0] = 0x00; + data[1] = 0x00; + req_reply = RH_ACK; + break; + case RH_GET_STATUS | RH_CLASS: /* HUB_STATUS */ + stat = readl(&ohci->regs->roothub.status) & 0x7fff7fff; /* bit 31 u. 15 has other meaning */ + data[0] = stat & 0xff; + data[1] = (stat >> 8) & 0xff; + data[2] = (stat >> 16) & 0xff; + data[3] = (stat >> 24) & 0xff; + len = 4; + req_reply = RH_ACK; + break; + case RH_GET_STATUS | RH_OTHER | RH_CLASS: /* PORT_STATUS */ + stat = readl(&ohci->regs->roothub.portstatus[wIndex-1]); + data[0] = stat & 0xff; + data[1] = (stat >> 8) & 0xff; + data[2] = (stat >> 16) & 0xff; + data[3] = (stat >> 24) & 0xff; + len = 4; + req_reply = RH_ACK; + printk("rh: stat %4x wIndex %4x;\n", stat , wIndex); + break; + + case RH_CLEAR_FEATURE: + switch (wValue) { + case (RH_DEVICE_REMOTE_WAKEUP): + default: + } + break; + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + len=0; + req_reply = RH_ACK; + break; + default: + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + /* case (RH_C_HUB_LOCAL_POWER): OHCI says: no switching of this one */ + case (RH_C_HUB_OVER_CURRENT): + writel(RH_PS_OCIC, &ohci->regs->roothub.status); + len=0; + req_reply = RH_ACK; + break; + default: + } + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_PORT_SUSPEND): + writel(RH_PS_POCI, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_PORT_POWER): + writel(RH_PS_LSDA, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_C_PORT_CONNECTION): + writel(RH_PS_CSC, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_C_PORT_ENABLE): + writel(RH_PS_PESC, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_C_PORT_SUSPEND): + writel(RH_PS_PSSC, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_C_PORT_OVER_CURRENT): + writel(RH_PS_OCIC, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_C_PORT_RESET): + writel(RH_PS_PRSC, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + /* + case (RH_PORT_CONNECTION): + case (RH_PORT_OVER_CURRENT): + case (RH_PORT_RESET): + case (RH_PORT_LOW_SPEED): + */ + default: + } + break; + case RH_SET_FEATURE: + switch (wValue) { + case (RH_DEVICE_REMOTE_WAKEUP): + default: + } + break; + + case RH_SET_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + default: + } + break; + + case RH_SET_FEATURE | RH_CLASS: + switch (wValue) { + /* case (RH_C_HUB_LOCAL_POWER): Root Hub has no local Power + case (RH_C_HUB_OVER_CURRENT): */ + default: + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_PORT_RESET): + if((readl(&ohci->regs->roothub.portstatus[wIndex-1]) &1) != 0) /* BUG IN HUP CODE *********/ + writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_PORT_POWER): + writel(RH_PS_PPS, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + case (RH_PORT_ENABLE): + writel(RH_PS_PES, &ohci->regs->roothub.portstatus[wIndex-1]); + len=0; + req_reply = RH_ACK; + break; + /* + case (RH_PORT_CONNECTION): + case (RH_PORT_OVER_CURRENT): + case (RH_PORT_LOW_SPEED): + case (RH_C_PORT_CONNECTION): + case (RH_C_PORT_ENABLE): + case (RH_C_PORT_SUSPEND): + case (RH_C_PORT_OVER_CURRENT): + case (RH_C_PORT_RESET): + */ + default: + } + break; + + case RH_SET_ADDRESS: + ohci->root_hub_funct_addr = wValue; + /* ohci->ed_func_ep0[wValue] = &ohci->ed_rh_ep0; + ohci->ed_func_ep0[wValue]->ep_addr.bep.fa = wValue; + ohci->ed_func_ep0[wValue]->ed_list = &ohci->ed_rh_epi; */ + ohci->ed_rh_epi.ed_list = NULL; + ohci->ed_rh_epi.ep_addr.bep.fa = wValue; + ohci->ed_rh_epi.ep_addr.bep.ep = 0xa1; /* Int in port 1 */ + ohci->ed_func_ep0[0]= NULL; + len = 0; + req_reply = RH_ACK; + break; + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min(sizeof(root_hub_dev_des), wLength); + memcpy(data, root_hub_dev_des, len); + req_reply = RH_ACK; + break; + case (0x02): /* configuration descriptor */ + len = min(sizeof(root_hub_config_des), wLength); + memcpy(data, root_hub_config_des, len); + req_reply = RH_ACK; + break; + case (0x03): /* string descriptors */ + default: + } + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + data[1] = 0x29; + stat = readl(&ohci->regs->roothub.a); + data[2] = stat & 0xff; /* number of ports */ + data[0] = (data[2] / 8) * 2 + 9; /* length of descriptor */ + if(data[0] > wLength) { + req_reply = RH_REQ_ERR; + break; + } + data[3] = (stat >> 8) & 0xff; + data[4] = (stat >> 16) & 0xff; + data[5] = (stat >> 24) & 0xff; + data[6] = 0; /* Root Hub needs no current from bus */ + stat = readl(&ohci->regs->roothub.b); + if(data[2] <= 8) { /* less than 8 Ports */ + data[7] = stat & 0xff; + data[8] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1 ?, stat >> 16 for USB Rev. 1.0 */ + } + else { + data[7] = stat & 0xff; + data[8] = (stat >> 8) & 0xff; + data[9] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 16 for USB Rev. 1.0 */ + data[10] = (stat >> 24) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 24 for USB Rev. 1.0 */ + } + len = data[0]; + req_reply = RH_ACK; + break; + + case RH_SET_DESCRIPTOR: + break; + + case RH_GET_CONFIGURATION: + len = 1; + data[0] = 0x01; + req_reply = RH_ACK; + break; + + case RH_SET_CONFIGURATION: /* start it up */ + writel( 0x10000, &ohci->regs->roothub.status); + /*writel( OHCI_INTR_RHSC, &ohci->regs->intrenable);*/ + len = 0; + req_reply = RH_ACK; + break; + /* Optional or meaningless requests + case RH_GET_STATE | RH_OTHER | RH_CLASS: + case RH_GET_INTERFACE | RH_INTERFACE: + case RH_SET_INTERFACE | RH_INTERFACE: + case RH_SYNC_FRAME | RH_ENDPOINT: + */ + + /* Vendor Requests, we are the vendor! + Will the USB-Consortium give us a Vendor Id + for a virtual hub-device :-) ? + We could use these requests for configuration purposes on the HCD Driver, not used in the altenate usb !*/ + + case RH_SET_FEATURE | RH_VENDOR: /* remove all endpoints of device wIndex = Dev << 8 */ + switch(wValue) { + case RH_REMOVE_EP: + ep_addr.iep = 0; + ep_addr.bep.ep = wIndex & 0xff; + ep_addr.bep.fa = (wIndex << 8) & 0xff00; + usb_ohci_rm_function(ohci, ep_addr.iep); + len=0; + req_reply = RH_ACK; + break; + } + break; + case RH_SET_FEATURE | RH_ENDPOINT | RH_VENDOR: /* remove endpoint wIndex = Dev << 8 | EP */ + switch(wValue) { + case RH_REMOVE_EP: + ep_addr.iep = 0; + ep_addr.bep.ep = wIndex & 0xff; + ep_addr.bep.fa = (wIndex << 8) & 0xff00; + usb_ohci_rm_ep(ohci, ohci_find_ep(ohci, ep_addr.iep)); + len=0; + req_reply = RH_ACK; + break; + } + break; + case RH_SET_EP | RH_ENDPOINT | RH_VENDOR: + ep_addr.bep.ep = data[0]; + ep_addr.bep.fa = data[1]; + ep_addr.bep.hc = data[2]; + ep_addr.bep.host = data[3]; + rep_handler = data[7] << 24 |data[6] << 16 | data[5] << 8 | data[4]; + /* struct usb_ohci_ed *usb_ohci_add_ep(union ep_addr_ ep_addr, + struct ohci * ohci, int interval, int load, int (*handler)(int, void*), int ep_size, int speed) */ + usb_ohci_add_ep(ohci, ep_addr.iep, data[8], data[9], (f_handler) rep_handler, data[11] << 8 | data[10] , data[12]); + len=0; + req_reply = RH_ACK; + break; + + default: + } + printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); + printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); + + /* if (req_reply == RH_ACK) len; */ + queue_reply(ohci, ep_addr_ret.iep, 8, rh_cmd, data, len, lw0, lw1, handler); + return 0; +} + +int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler){ + + struct ohci_rep_td *td; + struct ohci_rep_td *tmp; + union ep_addr_ ep_addr; + + td = kmalloc(sizeof(td),GFP_KERNEL); + tmp = ohci->td_rh_epi; + td->next_td = NULL; + if(tmp == NULL) { /* queue td */ + ohci->td_rh_epi = td; + } + else { + while(tmp->next_td != NULL) tmp = tmp->next_td; + tmp->next_td = td; + } + ep_addr.iep = 0; + ep_addr.bep.fa = ohci->root_hub_funct_addr; + ep_addr.bep.ep = 0xA1; /* INT IN EP endpoint 1 */ + td->cmd_len = 0; + td->cmd = NULL; + td->data = data; + td->data_len = data_len; + td->handler = handler; + td->next_td = NULL; + td->ep_addr = ep_addr.iep; + td->lw0 = lw0; + td->lw1 = lw1; + ohci_init_rh_int_timer(ohci, 255); + return 0; +} + + +/* prepare Interrupt pipe transaction data; HUP INTERRUPT ENDPOINT */ +int root_hub_send_irq(struct ohci * ohci, void * rh_data, int rh_len ) { + + int num_ports; + int i; + int ret; + int len; + + __u8 * data = rh_data; + + num_ports = readl(&ohci->regs->roothub.a) & 0xff; + data[0] = (readl(&ohci->regs->roothub.status) & 0x00030000)>0?1:0; + ret = data[0]; + + for(i=0; i < num_ports; i++) { + data[i/8] |= ((readl(&ohci->regs->roothub.portstatus[i]) & 0x001f0000)>0?1:0) << ((i+1) % 8); + ret += data[i/8]; + } + len = i/8 + 1; + + if (ret > 0) return len; + + return RH_NACK; +} + + + + + +static struct timer_list rh_int_timer; + +/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ +static void rh_int_timer_do(unsigned long ptr) { + int len; + int interval; + struct ohci * ohci = (struct ohci *) ptr; + struct ohci_rep_td *td = ohci->td_rh_epi; + + if(td != NULL) { /* if ther is a TD handle the INT request */ + + len = root_hub_send_irq(ohci, td->data, td->data_len ); + if(len > 0) { + ohci->td_rh_epi = td->next_td; + td->next_td = ohci->repl_queue; + ohci->repl_queue = td; + send_replies(ohci); + } + } + interval = ohci->rh_int_interval; + init_timer(& rh_int_timer); + rh_int_timer.function = rh_int_timer_do; + rh_int_timer.data = (unsigned long) ohci; + rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000; + add_timer(&rh_int_timer); +} + +/* Root Hub INTs are polled by this timer */ +int ohci_init_rh_int_timer(struct ohci * ohci, int interval) { + + if(!(ohci->rh_int_timer)) { + ohci->rh_int_timer = 1; + ohci->rh_int_interval = interval; + init_timer(& rh_int_timer); + rh_int_timer.function = rh_int_timer_do; + rh_int_timer.data = (unsigned long) ohci; + rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000; + add_timer(&rh_int_timer); + } + return 0; +} + +int ohci_del_rh_int_timer(struct ohci * ohci) { + del_timer(&rh_int_timer); + return 0; +} +/* for root hub replies, queue the reply, (it will be sent immediately now) */ + +int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) { + + struct ohci_rep_td *td; + int status = 0; + +printk("queue_reply ep: %x len: %x\n", ep_addr, len); +td = kmalloc(sizeof(td), GFP_KERNEL); + + if (len < 0) { status = len; len = 0;} + td->cmd_len = cmd_len; + td->cmd = cmd; + td->data = data; + td->data_len = len; + td->handler = handler; + td->next_td = ohci->repl_queue; ohci->repl_queue = td; + td->ep_addr = ep_addr; + td->lw0 = lw0; + td->lw1 = lw1; + td->status = status; + send_replies(ohci); +return 0; +} + +/* for root hub replies; send the reply */ +int send_replies(struct ohci * ohci) { + struct ohci_rep_td *td; + struct ohci_rep_td *tmp; + + td = ohci->repl_queue; ohci->repl_queue = NULL; + while ( td != NULL) { + td->handler((void *) ohci, td->ep_addr,td->cmd_len,td->cmd, td->data, td->data_len, td->status, td->lw0, td->lw1); + tmp = td; + td = td->next_td; + + kfree(tmp); + } + return 0; +} + +#endif diff --git a/drivers/usb/ohci-root-hub.h b/drivers/usb/ohci-root-hub.h new file mode 100644 index 000000000000..8f1e80998278 --- /dev/null +++ b/drivers/usb/ohci-root-hub.h @@ -0,0 +1,71 @@ +/* + * HCD (OHCI) Virtual Root Hub Protocol for USB. + * + * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at) + * + * The Root Hub is build into the HC (UHCI or OHCI) hardware. + * This piece of code lets it look like it resides on the bus + * like the other hubs. + * (for anyone who wants to do a control operation on the root hub) + * + * v1.0 1999/04/27 + * ohci-root-hub.h + * + */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +#define min(a,b) (((a)<(b))?(a):(b)) + diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index ef1ec5b4fa94..8d35e2eebb1d 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -9,7 +9,7 @@ static __inline__ void wait_ms(unsigned int ms) { current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(1 + ms / 10); + schedule_timeout(1 + ms * HZ / 1000); } diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index e6f4351dff5c..ab11028050e0 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -635,10 +635,8 @@ __initfunc(void vesafb_init(void)) video_cmap_len = 256; } request_region(0x3c0, 32, "vga+"); -#ifdef CONFIG_MTRR if (mtrr) mtrr_add((unsigned long)video_base, video_size, MTRR_TYPE_WRCOMB, 1); -#endif strcpy(fb_info.modename, "VESA VGA"); fb_info.changevar = NULL; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 36931e02a0da..ef5bc6ea9610 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -371,10 +371,19 @@ static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) /* * If it's the last lookup in a series, we use a stricter - * cache consistency check! + * cache consistency check by looking at the parent mtime. + * + * If it's been modified in the last hour, be really strict. + * (This still means that we can avoid doing unnecessary + * work on directories like /usr/share/bin etc which basically + * never change). */ - if (!(flags & LOOKUP_CONTINUE)) - timeout = 0; + if (!(flags & LOOKUP_CONTINUE)) { + long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; + + if (diff < 15*60) + timeout = 0; + } return time_after(jiffies,dentry->d_time + timeout); } @@ -389,14 +398,10 @@ static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) static inline int nfs_neg_need_reval(struct dentry *dentry) { unsigned long timeout = 30 * HZ; - struct inode *dir = dentry->d_parent->d_inode; + long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; - if (dir) { - /* Modified in the last two minutes? */ - long diff = CURRENT_TIME - dir->i_mtime; - if (diff < 2*60) - timeout = 1 * HZ; - } + if (diff < 5*60) + timeout = 1 * HZ; return time_after(jiffies, dentry->d_time + timeout); } diff --git a/fs/proc/array.c b/fs/proc/array.c index d5523befd677..4ce3222a3599 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -896,7 +896,7 @@ static int get_stat(int pid, char * buffer) return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \ -%lu %lu %lu %lu %lu %lu %lu %lu %d\n", +%lu %lu %lu %lu %lu %lu %lu %lu %d %d\n", pid, tsk->comm, state, @@ -938,7 +938,8 @@ static int get_stat(int pid, char * buffer) wchan, tsk->nswap, tsk->cnswap, - tsk->exit_signal); + tsk->exit_signal, + tsk->processor); } static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, diff --git a/fs/super.c b/fs/super.c index 08475c017068..690807a26079 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1131,6 +1131,7 @@ void __init mount_root(void) sb = get_empty_super(); /* "can't fail" */ sb->s_dev = get_unnamed_dev(); sb->s_flags = root_mountflags; + sema_init(&sb->s_vfs_rename_sem,1); vfsmnt = add_vfsmnt(sb, "/dev/root", "/"); if (vfsmnt) { if (nfs_root_mount(sb) >= 0) { diff --git a/include/asm-alpha/fpu.h b/include/asm-alpha/fpu.h index 333e5caebada..5e56e7db2304 100644 --- a/include/asm-alpha/fpu.h +++ b/include/asm-alpha/fpu.h @@ -81,6 +81,18 @@ ieee_swcr_to_fpcr(unsigned long sw) return fp; } +static inline unsigned long +ieee_fpcr_to_swcr(unsigned long fp) +{ + unsigned long sw; + sw = (fp >> 35) & IEEE_STATUS_MASK; + sw |= (~fp >> 48) & (IEEE_TRAP_ENABLE_INV + | IEEE_TRAP_ENABLE_DZE + | IEEE_TRAP_ENABLE_OVF); + sw |= (~fp >> 57) & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE); + return sw; +} + #ifdef __KERNEL__ /* The following two functions don't need trapb/excb instructions diff --git a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h index 499598a76012..308b28af9f28 100644 --- a/include/asm-alpha/pgtable.h +++ b/include/asm-alpha/pgtable.h @@ -167,19 +167,6 @@ static inline void flush_tlb_range(struct mm_struct *mm, #else /* __SMP__ */ -/* ipi_msg_flush_tb is owned by the holder of the global kernel lock. */ -struct ipi_msg_flush_tb_struct { - volatile unsigned int flush_tb_mask; - union { - struct mm_struct * flush_mm; - struct vm_area_struct * flush_vma; - } p; - unsigned long flush_addr; - unsigned long flush_end; -}; - -extern struct ipi_msg_flush_tb_struct ipi_msg_flush_tb; - extern void flush_tlb_all(void); extern void flush_tlb_mm(struct mm_struct *); extern void flush_tlb_page(struct vm_area_struct *, unsigned long); diff --git a/include/asm-alpha/system.h b/include/asm-alpha/system.h index 626495adfe10..172543fa27a5 100644 --- a/include/asm-alpha/system.h +++ b/include/asm-alpha/system.h @@ -97,12 +97,15 @@ extern void wripir(unsigned long); extern void halt(void) __attribute__((noreturn)); -#define switch_to(prev,next) do { \ - current = next; \ - alpha_switch_to((unsigned long) ¤t->tss - IDENT_ADDR); \ +#define switch_to(prev,next,last) \ +do { \ + unsigned long pcbb; \ + current = (next); \ + pcbb = virt_to_phys(¤t->tss); \ + (last) = alpha_switch_to(pcbb, (prev)); \ } while (0) -extern void alpha_switch_to(unsigned long pctxp); +extern struct task_struct* alpha_switch_to(unsigned long, struct task_struct*); #define mb() \ __asm__ __volatile__("mb": : :"memory") @@ -119,6 +122,34 @@ __asm__ __volatile__ ("call_pal %0 #imb" : : "i" (PAL_imb) : "memory") #define draina() \ __asm__ __volatile__ ("call_pal %0 #draina" : : "i" (PAL_draina) : "memory") +enum implver_enum { + IMPLVER_EV4, + IMPLVER_EV5, + IMPLVER_EV6 +}; + +#ifdef CONFIG_ALPHA_GENERIC +#define implver() \ +({ unsigned long __implver; \ + __asm__ ("implver %0" : "=r"(__implver)); \ + (enum implver_enum) __implver; }) +#else +/* Try to eliminate some dead code. */ +#ifdef CONFIG_ALPHA_EV4 +#define implver() IMPLVER_EV4 +#endif +#ifdef CONFIG_ALPHA_EV5 +#define implver() IMPLVER_EV5 +#endif +#ifdef CONFIG_ALPHA_EV6 +#define implver() IMPLVER_EV6 +#endif +#endif + +#define amask(mask) \ +({ unsigned long __amask, __input = (mask); \ + __asm__ ("amask %1,%0" : "=r"(__amask) : "rI"(__input)); \ + __amask; }) static inline unsigned long wrperfmon(unsigned long perf_fun, unsigned long arg) diff --git a/include/asm-arm/arch-ebsa285/irq.h b/include/asm-arm/arch-ebsa285/irq.h index d8f0ab21d8a0..ca1a55cdb4ae 100644 --- a/include/asm-arm/arch-ebsa285/irq.h +++ b/include/asm-arm/arch-ebsa285/irq.h @@ -10,6 +10,7 @@ * 26-Jan-1999 PJB Don't use IACK on CATS * 16-Mar-1999 RMK Added autodetect of ISA PICs */ +#include #include #include #include diff --git a/include/asm-arm/arch-ebsa285/memory.h b/include/asm-arm/arch-ebsa285/memory.h index 745750e3be21..a03cea639672 100644 --- a/include/asm-arm/arch-ebsa285/memory.h +++ b/include/asm-arm/arch-ebsa285/memory.h @@ -15,6 +15,8 @@ #ifndef __ASM_ARCH_MMU_H #define __ASM_ARCH_MMU_H +#include + #if defined(CONFIG_HOST_FOOTBRIDGE) /* diff --git a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h index d3642f4ae4e1..ebd9a4f1a73d 100644 --- a/include/asm-i386/bugs.h +++ b/include/asm-i386/bugs.h @@ -277,6 +277,7 @@ __initfunc(static void check_cx686_cpuid(void)) ((Cx86_dir0_msb == 5) || (Cx86_dir0_msb == 3))) { int eax, dummy; unsigned char ccr3, ccr4; + __u32 old_cap; cli(); ccr3 = getCx86(CX86_CCR3); @@ -288,8 +289,11 @@ __initfunc(static void check_cx686_cpuid(void)) /* we have up to level 1 available on the Cx6x86(L|MX) */ boot_cpu_data.cpuid_level = 1; + /* Need to preserve some externally computed capabilities */ + old_cap = boot_cpu_data.x86_capability & X86_FEATURE_MTRR; cpuid(1, &eax, &dummy, &dummy, &boot_cpu_data.x86_capability); + boot_cpu_data.x86_capability |= old_cap; boot_cpu_data.x86 = (eax >> 8) & 15; /* @@ -344,6 +348,24 @@ __initfunc(static void check_cyrix_cpu(void)) } } +/* + * In setup.c's cyrix_model() we have set the boot_cpu_data.coma_bug + * on certain processors that we know contain this bug and now we + * enable the workaround for it. + */ + +__initfunc(static void check_cyrix_coma(void)) +{ + if (boot_cpu_data.coma_bug) { + unsigned char ccr1; + cli(); + ccr1 = getCx86 (CX86_CCR1); + setCx86 (CX86_CCR1, ccr1 | 0x10); + sti(); + printk("Cyrix processor with \"coma bug\" found, workaround enabled\n"); + } +} + /* * Check wether we are able to run this kernel safely on SMP. * @@ -402,5 +424,6 @@ __initfunc(static void check_bugs(void)) check_popad(); check_amd_k6(); check_pentium_f00f(); + check_cyrix_coma(); system_utsname.machine[1] = '0' + boot_cpu_data.x86; } diff --git a/include/asm-i386/mtrr.h b/include/asm-i386/mtrr.h index 5b1e8470c051..e6d130c9a0ae 100644 --- a/include/asm-i386/mtrr.h +++ b/include/asm-i386/mtrr.h @@ -1,6 +1,6 @@ /* Generic MTRR (Memory Type Range Register) ioctls. - Copyright (C) 1997-1998 Richard Gooch + Copyright (C) 1997-1999 Richard Gooch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -44,10 +44,11 @@ struct mtrr_gentry }; /* These are the various ioctls */ -#define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry) -#define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry) -#define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry) +#define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry) +#define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry) +#define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry) #define MTRRIOC_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry) +#define MTRRIOC_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry) /* These are the region types */ #define MTRR_TYPE_UNCACHABLE 0 @@ -75,7 +76,7 @@ static char *mtrr_strings[MTRR_NUM_TYPES] = #ifdef __KERNEL__ /* The following functions are for use by other drivers */ -# if defined(CONFIG_MTRR) || defined(CONFIG_MTRR_MODULE) +# ifdef CONFIG_MTRR extern int mtrr_add (unsigned long base, unsigned long size, unsigned int type, char increment); extern int mtrr_del (int reg, unsigned long base, unsigned long size); diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 92cf329e8ab6..b98893662383 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -35,6 +35,7 @@ struct cpuinfo_x86 { call */ int fdiv_bug; int f00f_bug; + int coma_bug; unsigned long loops_per_sec; unsigned long *pgd_quick; unsigned long *pte_quick; @@ -119,12 +120,17 @@ extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) /* * Cyrix CPU configuration register indexes */ +#define CX86_CCR0 0xc0 +#define CX86_CCR1 0xc1 #define CX86_CCR2 0xc2 #define CX86_CCR3 0xc3 #define CX86_CCR4 0xe8 #define CX86_CCR5 0xe9 +#define CX86_CCR6 0xea #define CX86_DIR0 0xfe #define CX86_DIR1 0xff +#define CX86_ARR_BASE 0xc4 +#define CX86_RCR_BASE 0xdc /* * Cyrix CPU indexed register access macros diff --git a/include/linux/mc146818rtc.h b/include/linux/mc146818rtc.h new file mode 100644 index 000000000000..0a2efb6e930d --- /dev/null +++ b/include/linux/mc146818rtc.h @@ -0,0 +1,149 @@ +/* mc146818rtc.h - register definitions for the Real-Time-Clock / CMOS RAM + * Copyright Torsten Duwe 1993 + * derived from Data Sheet, Copyright Motorola 1984 (!). + * It was written to be part of the Linux operating system. + */ +/* permission is hereby granted to copy, modify and redistribute this code + * in terms of the GNU Library General Public License, Version 2 or later, + * at your option. + */ + +#ifndef _MC146818RTC_H +#define _MC146818RTC_H +#include + +#ifndef RTC_PORT +#define RTC_PORT(x) (0x70 + (x)) +#define RTC_ALWAYS_BCD 1 +#endif + +#define CMOS_READ(addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +inb_p(RTC_PORT(1)); \ +}) +#define CMOS_WRITE(val, addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +outb_p((val),RTC_PORT(1)); \ +}) + +/********************************************************************** + * register summary + **********************************************************************/ +#define RTC_SECONDS 0 +#define RTC_SECONDS_ALARM 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +/* RTC_*_alarm is always true if 2 MSBs are set */ +# define RTC_ALARM_DONT_CARE 0xC0 + +#define RTC_DAY_OF_WEEK 6 +#define RTC_DAY_OF_MONTH 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 + +/* control registers - Moto names + */ +#define RTC_REG_A 10 +#define RTC_REG_B 11 +#define RTC_REG_C 12 +#define RTC_REG_D 13 + +/********************************************************************** + * register details + **********************************************************************/ +#define RTC_FREQ_SELECT RTC_REG_A + +/* update-in-progress - set to "1" 244 microsecs before RTC goes off the bus, + * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete, + * totalling to a max high interval of 2.228 ms. + */ +# define RTC_UIP 0x80 +# define RTC_DIV_CTL 0x70 + /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */ +# define RTC_REF_CLCK_4MHZ 0x00 +# define RTC_REF_CLCK_1MHZ 0x10 +# define RTC_REF_CLCK_32KHZ 0x20 + /* 2 values for divider stage reset, others for "testing purposes only" */ +# define RTC_DIV_RESET1 0x60 +# define RTC_DIV_RESET2 0x70 + /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */ +# define RTC_RATE_SELECT 0x0F + +/**********************************************************************/ +#define RTC_CONTROL RTC_REG_B +# define RTC_SET 0x80 /* disable updates for clock setting */ +# define RTC_PIE 0x40 /* periodic interrupt enable */ +# define RTC_AIE 0x20 /* alarm interrupt enable */ +# define RTC_UIE 0x10 /* update-finished interrupt enable */ +# define RTC_SQWE 0x08 /* enable square-wave output */ +# define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */ +# define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */ +# define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */ + +/**********************************************************************/ +#define RTC_INTR_FLAGS RTC_REG_C +/* caution - cleared by read */ +# define RTC_IRQF 0x80 /* any of the following 3 is active */ +# define RTC_PF 0x40 +# define RTC_AF 0x20 +# define RTC_UF 0x10 + +/**********************************************************************/ +#define RTC_VALID RTC_REG_D +# define RTC_VRT 0x80 /* valid RAM and time */ +/**********************************************************************/ + +/* example: !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + * determines if the following two #defines are needed + */ +#ifndef BCD_TO_BIN +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) +#endif + +/* + * The struct used to pass data via the following ioctl. Similar to the + * struct tm in , but it needs to be here so that the kernel + * source is self contained, allowing cross-compiles, etc. etc. + */ + +struct rtc_time { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +/* + * ioctl calls that are permitted to the /dev/rtc interface, if + * CONFIG_RTC was enabled. + */ + +#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */ +#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */ +#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ +#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */ +#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */ +#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */ + +#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */ +#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */ +#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time) /* Read RTC time */ +#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time) /* Set RTC time */ +#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */ +#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */ +#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ +#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ + + +#endif /* _MC146818RTC_H */ diff --git a/include/linux/parport_pc.h b/include/linux/parport_pc.h index 65d8209e526f..a74c7a12162b 100644 --- a/include/linux/parport_pc.h +++ b/include/linux/parport_pc.h @@ -14,8 +14,15 @@ #define STATUS 0x1 #define DATA 0 +/* Private data for PC low-level driver. */ +struct parport_pc_private { + /* Contents of CTR. */ + unsigned char ctr; +}; + extern int parport_pc_epp_clear_timeout(struct parport *pb); +extern volatile unsigned char parport_pc_ctr; extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned char d) { @@ -62,19 +69,24 @@ extern __inline__ unsigned char parport_pc_read_data(struct parport *p) extern __inline__ void parport_pc_write_control(struct parport *p, unsigned char d) { + struct parport_pc_private *priv = p->private_data; + priv->ctr = d;/* update soft copy */ outb(d, p->base+CONTROL); } extern __inline__ unsigned char parport_pc_read_control(struct parport *p) { - return inb(p->base+CONTROL); + struct parport_pc_private *priv = p->private_data; + return priv->ctr; } extern __inline__ unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask, unsigned char val) { - unsigned char old = inb(p->base+CONTROL); - outb(((old & ~mask) ^ val), p->base+CONTROL); - return old; + struct parport_pc_private *priv = p->private_data; + unsigned char ctr = priv->ctr; + ctr = (ctr & ~mask) ^ val; + outb (ctr, p->base+CONTROL); + return priv->ctr = ctr; /* update soft copy */ } extern __inline__ void parport_pc_write_status(struct parport *p, unsigned char d) diff --git a/include/linux/smp.h b/include/linux/smp.h index 80ea3056ffa9..75c34e87ff39 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -41,6 +41,12 @@ extern void smp_callin(void); */ extern void smp_commence(void); +/* + * Call a function on all other processors + */ +extern int smp_call_function (void (*func) (void *info), void *info, + int retry, int wait); + /* * True once the per process idle is forked */ @@ -60,7 +66,7 @@ extern volatile int smp_msg_id; * when rebooting */ #define MSG_RESCHEDULE 0x0003 /* Reschedule request from master CPU*/ -#define MSG_MTRR_CHANGE 0x0004 /* Change MTRR */ +#define MSG_CALL_FUNCTION 0x0004 /* Call function on all other CPUs */ #else @@ -68,12 +74,13 @@ extern volatile int smp_msg_id; * These macros fold the SMP functionality into a single CPU system */ -#define smp_num_cpus 1 -#define smp_processor_id() 0 -#define hard_smp_processor_id() 0 -#define smp_threads_ready 1 +#define smp_num_cpus 1 +#define smp_processor_id() 0 +#define hard_smp_processor_id() 0 +#define smp_threads_ready 1 #define kernel_lock() -#define cpu_logical_map(cpu) 0 +#define cpu_logical_map(cpu) 0 +#define smp_call_function(func,info,retry,wait) #endif #endif diff --git a/include/net/tcp.h b/include/net/tcp.h index 94fd65b7bd44..2d91a8d43bf5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -174,6 +174,7 @@ struct tcp_tw_bucket { struct tcp_func *af_specific; struct tcp_bind_bucket *tb; struct tcp_tw_bucket *next_death; + struct tcp_tw_bucket **pprev_death; int death_slot; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct in6_addr v6_daddr; @@ -716,6 +717,22 @@ extern __inline__ int tcp_raise_window(struct sock *sk) return (new_win && (new_win > (cur_win << 1))); } +/* Recalculate snd_ssthresh, we want to set it to: + * + * one half the current congestion window, but no + * less than two segments + * + * We must take into account the current send window + * as well, however we keep track of that using different + * units so a conversion is necessary. -DaveM + */ +extern __inline__ __u32 tcp_recalc_ssthresh(struct tcp_opt *tp) +{ + __u32 snd_wnd_packets = tp->snd_wnd / tp->mss_cache; + + return max(min(snd_wnd_packets, tp->snd_cwnd) >> 1, 2); +} + /* TCP timestamps are only 32-bits, this causes a slight * complication on 64-bit systems since we store a snapshot * of jiffies in the buffer control blocks below. We decidely diff --git a/kernel/sched.c b/kernel/sched.c index f183ec610626..098c904081c1 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -672,6 +672,7 @@ void schedule_tail (struct task_struct *prev) { __schedule_tail(prev); } + /* * 'schedule()' is the scheduler function. It's a very simple and nice * scheduler: it's not perfect, but certainly works for most things. @@ -820,7 +821,7 @@ still_running_back: kstat.context_swtch++; get_mmu_context(next); switch_to(prev, next, prev); - schedule_tail(prev); + __schedule_tail(prev); same_process: diff --git a/net/core/iovec.c b/net/core/iovec.c index 8919fc5c1da6..c20f85303b8b 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -59,8 +59,15 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode) goto out; m->msg_iov=iov; - for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) + for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) { err += iov[ct].iov_len; + /* Goal is not to verify user data, but to prevent returning + negative value, which is interpreted as errno. + Overflow is still possible, but it is harmless. + */ + if (err < 0) + return -EMSGSIZE; + } out: return err; } diff --git a/net/core/sock.c b/net/core/sock.c index cf7b44c295d9..e0eb41a015a6 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -7,7 +7,7 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.79 1999/03/28 10:18:25 davem Exp $ + * Version: $Id: sock.c,v 1.80 1999/05/08 03:04:34 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 3d0e31e1d49b..c8b0fbbc80c4 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1,7 +1,7 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.27 1999/03/25 10:04:06 davem Exp $ + * Version: $Id: devinet.c,v 1.28 1999/05/08 20:00:16 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -712,6 +712,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, void ifa->ifa_mask = inet_make_mask(8); ifa->ifa_dev = in_dev; ifa->ifa_scope = RT_SCOPE_HOST; + memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); inet_insert_ifa(in_dev, ifa); } } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 52ea9b2d304f..dbde97b70fca 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,7 +5,7 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.66 1999/04/22 10:07:35 davem Exp $ + * Version: $Id: route.c,v 1.67 1999/05/08 20:00:20 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -1492,13 +1492,16 @@ int ip_route_output_slow(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int return -ENODEV; /* Wrong error code */ if (LOCAL_MCAST(daddr) || daddr == 0xFFFFFFFF) { - key.src = inet_select_addr(dev_out, 0, RT_SCOPE_LINK); + if (!key.src) + key.src = inet_select_addr(dev_out, 0, RT_SCOPE_LINK); goto make_route; } - if (MULTICAST(daddr)) - key.src = inet_select_addr(dev_out, 0, key.scope); - else if (!daddr) - key.src = inet_select_addr(dev_out, 0, RT_SCOPE_HOST); + if (!key.src) { + if (MULTICAST(daddr)) + key.src = inet_select_addr(dev_out, 0, key.scope); + else if (!daddr) + key.src = inet_select_addr(dev_out, 0, RT_SCOPE_HOST); + } } if (!key.dst) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5744752a1984..4a607a7492f9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.163 1999/04/28 16:08:05 davem Exp $ + * Version: $Id: tcp_input.c,v 1.164 1999/05/08 21:09:52 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -497,8 +497,7 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) if (tp->high_seq == 0 || after(ack, tp->high_seq)) { tp->dup_acks++; if ((tp->fackets_out > 3) || (tp->dup_acks == 3)) { - tp->snd_ssthresh = - max(min(tp->snd_wnd, tp->snd_cwnd) >> 1, 2); + tp->snd_ssthresh = tcp_recalc_ssthresh(tp); tp->snd_cwnd = (tp->snd_ssthresh + 3); tp->high_seq = tp->snd_nxt; if(!tp->fackets_out) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f676077a421f..b5070c3a753d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.174 1999/04/28 16:08:19 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.175 1999/05/08 21:09:54 davem Exp $ * * IPv4 specific functions * @@ -819,7 +819,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) switch (type) { case ICMP_SOURCE_QUENCH: #ifndef OLD_SOURCE_QUENCH /* This is deprecated */ - tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2); + tp->snd_ssthresh = tcp_recalc_ssthresh(tp); tp->snd_cwnd = tp->snd_ssthresh; tp->snd_cwnd_cnt = 0; tp->high_seq = tp->snd_nxt; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index a73db21a5e9a..9a096f0f307a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.107 1999/04/28 16:08:12 davem Exp $ + * Version: $Id: tcp_output.c,v 1.108 1999/05/08 21:48:59 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -521,24 +521,39 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int m void tcp_simple_retransmit(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - struct sk_buff *skb; - unsigned int mss = tcp_current_mss(sk); + struct sk_buff *skb, *old_next_skb; + unsigned int mss = tcp_current_mss(sk); /* Don't muck with the congestion window here. */ tp->dup_acks = 0; tp->high_seq = tp->snd_nxt; - tp->retrans_head = NULL; + tp->retrans_head = NULL; /* Input control flow will see that this was retransmitted * and not use it for RTT calculation in the absence of * the timestamp option. */ - for (skb = skb_peek(&sk->write_queue); + for (old_next_skb = skb = skb_peek(&sk->write_queue); ((skb != tp->send_head) && (skb != (struct sk_buff *)&sk->write_queue)); - skb = skb->next) - if (skb->len > mss) - tcp_retransmit_skb(sk, skb); + skb = skb->next) { + int resend_skb = 0; + + /* Our goal is to push out the packets which we + * sent already, but are being chopped up now to + * account for the PMTU information we have. + * + * As we resend the queue, packets are fragmented + * into two pieces, and when we try to send the + * second piece it may be collapsed together with + * a subsequent packet, and so on. -DaveM + */ + if (old_next_skb != skb || skb->len > mss) + resend_skb = 1; + old_next_skb = skb->next; + if (resend_skb != 0) + tcp_retransmit_skb(sk, skb); + } } static __inline__ void update_retrans_head(struct sock *sk) diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 00ac053763dc..ad6ccace95c6 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.60 1999/04/28 16:08:21 davem Exp $ + * Version: $Id: tcp_timer.c,v 1.62 1999/05/08 21:09:55 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -317,48 +317,47 @@ static void tcp_twkill(unsigned long data) void tcp_tw_schedule(struct tcp_tw_bucket *tw) { int slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1); + struct tcp_tw_bucket **tpp = &tcp_tw_death_row[slot]; + + if((tw->next_death = *tpp) != NULL) + (*tpp)->pprev_death = &tw->next_death; + *tpp = tw; + tw->pprev_death = tpp; tw->death_slot = slot; - tw->next_death = tcp_tw_death_row[slot]; - tcp_tw_death_row[slot] = tw; + tcp_inc_slow_timer(TCP_SLT_TWKILL); } /* Happens rarely if at all, no care about scalability here. */ void tcp_tw_reschedule(struct tcp_tw_bucket *tw) { - struct tcp_tw_bucket *walk; - int slot = tw->death_slot; + struct tcp_tw_bucket **tpp; + int slot; + + if(tw->next_death) + tw->next_death->pprev_death = tw->pprev_death; + *tw->pprev_death = tw->next_death; + tw->pprev_death = NULL; - walk = tcp_tw_death_row[slot]; - if(walk == tw) { - tcp_tw_death_row[slot] = tw->next_death; - } else { - while(walk->next_death != tw) - walk = walk->next_death; - walk->next_death = tw->next_death; - } slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1); + tpp = &tcp_tw_death_row[slot]; + if((tw->next_death = *tpp) != NULL) + (*tpp)->pprev_death = &tw->next_death; + *tpp = tw; + tw->pprev_death = tpp; + tw->death_slot = slot; - tw->next_death = tcp_tw_death_row[slot]; - tcp_tw_death_row[slot] = tw; /* Timer was incremented when we first entered the table. */ } /* This is for handling early-kills of TIME_WAIT sockets. */ void tcp_tw_deschedule(struct tcp_tw_bucket *tw) { - struct tcp_tw_bucket *walk; - int slot = tw->death_slot; - - walk = tcp_tw_death_row[slot]; - if(walk == tw) { - tcp_tw_death_row[slot] = tw->next_death; - } else { - while(walk->next_death != tw) - walk = walk->next_death; - walk->next_death = tw->next_death; - } + if(tw->next_death) + tw->next_death->pprev_death = tw->pprev_death; + *tw->pprev_death = tw->next_death; + tw->pprev_death = NULL; tcp_dec_slow_timer(TCP_SLT_TWKILL); } @@ -478,7 +477,7 @@ void tcp_retransmit_timer(unsigned long data) * means it must be an accurate representation of our current * sending rate _and_ the snd_wnd. */ - tp->snd_ssthresh = max(min(tp->snd_wnd, tp->snd_cwnd) >> 1, 2); + tp->snd_ssthresh = tcp_recalc_ssthresh(tp); tp->snd_cwnd_cnt = 0; tp->snd_cwnd = 1; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index de0a9187eb7f..5fcec9cf3db0 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.65 1999/03/21 05:22:49 davem Exp $ + * Version: $Id: udp.c,v 1.66 1999/05/08 20:00:25 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -955,7 +955,7 @@ csum_copy_err: * Error for blocking case is chosen to masquerade * as some normal condition. */ - return (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; + return (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; #endif } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4a239b18fe37..5b4d55f9e4c8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.39 1999/04/22 10:07:47 davem Exp $ + * $Id: udp.c,v 1.40 1999/05/08 20:00:32 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -381,7 +381,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, /* Error for blocking case is chosen to masquerade as some normal condition. */ - err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; + err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; udp_stats_in6.UdpInErrors++; goto out_free; } @@ -398,7 +398,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, /* Error for blocking case is chosen to masquerade as some normal condition. */ - err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; + err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; udp_stats_in6.UdpInErrors++; goto out_free; } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index c7f594fbba67..21614a3c6fd3 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.75 1999/03/22 05:02:45 davem Exp $ + * Version: $Id: af_unix.c,v 1.76 1999/05/08 05:54:55 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -322,7 +322,7 @@ static int unix_release_sock (unix_socket *sk) { if (sk->type==SOCK_STREAM && unix_our_peer(sk, skpair)) { - skpair->state_change(skpair); + skpair->data_ready(skpair,0); skpair->shutdown=SHUTDOWN_MASK; /* No more writes*/ } unix_unlock(skpair); /* It may now die */ @@ -1409,7 +1409,10 @@ static int unix_shutdown(struct socket *sock, int mode) if (mode&SEND_SHUTDOWN) peer_mode |= RCV_SHUTDOWN; other->shutdown |= peer_mode; - other->state_change(other); + if (peer_mode&RCV_SHUTDOWN) + other->data_ready(other,0); + else + other->state_change(other); } } return 0; -- 2.39.5